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));
+						}
+						bits += status_byte;
+						// Read pad byte
+						if (((status_byte & 0x03) == 1) || ((status_byte & 0x03) == 2)) {
+							BYTE padding = 0;
+							if(io->read_proc(&padding, sizeof(BYTE), 1, handle) != 1) {
+								throw(1);
+							}
+						}
+					}
+					break;
+				}
+			}
+		}
+		
+		{
+			// Convert to 4-bit
+			for(int y = 0; y < height; y++) {
+				const BYTE *src = (BYTE*)pixels + y * width;
+				BYTE *dst = FreeImage_GetScanLine(dib, y);
+
+				BOOL hinibble = TRUE;
+
+				for (int cols = 0; cols < width; cols++){
+					if (hinibble) {
+						dst[cols >> 1] = (src[cols] << 4);
+					} else {
+						dst[cols >> 1] |= src[cols];
+					}
+
+					hinibble = !hinibble;
+				}
+			}
+		}
+
+		free(pixels);
+
+		return TRUE;
+
+	} catch(int) {
+		if(pixels) free(pixels);
+		return FALSE;
+	}
+}
+
+/**
+Load image pixels for 8-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 
+LoadPixelDataRLE8(FreeImageIO *io, fi_handle handle, int width, int height, FIBITMAP *dib) {
+	BYTE status_byte = 0;
+	BYTE second_byte = 0;
+	int scanline = 0;
+	int bits = 0;
+
+	for (;;) {
+		if( io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+			return FALSE;
+		}
+
+		switch (status_byte) {
+			case RLE_COMMAND :
+				if(io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+					return FALSE;
+				}
+
+				switch (status_byte) {
+					case RLE_ENDOFLINE :
+						bits = 0;
+						scanline++;
+						break;
+
+					case RLE_ENDOFBITMAP :
+						return TRUE;
+
+					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) {
+							return FALSE;
+						}
+						if(io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
+							return FALSE;
+						}
+
+						// apply them
+
+						bits     += delta_x;
+						scanline += delta_y;
+
+						break;
+					}
+
+					default :
+					{
+						if(scanline >= abs(height)) {
+							return TRUE;
+						}
+
+						int count = MIN((int)status_byte, width - bits);
+
+						BYTE *sline = FreeImage_GetScanLine(dib, scanline);
+
+						if(io->read_proc((void *)(sline + bits), sizeof(BYTE) * count, 1, handle) != 1) {
+							return FALSE;
+						}
+						
+						// align run length to even number of bytes 
+
+						if ((status_byte & 1) == 1) {
+							if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+								return FALSE;
+							}
+						}
+
+						bits += status_byte;													
+
+						break;	
+					}
+				}
+
+				break;
+
+			default :
+			{
+				if(scanline >= abs(height)) {
+					return TRUE;
+				}
+
+				int count = MIN((int)status_byte, width - bits);
+
+				BYTE *sline = FreeImage_GetScanLine(dib, scanline);
+
+				if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+					return FALSE;
+				}
+
+				for (int i = 0; i < count; i++) {
+					*(sline + bits) = second_byte;
+
+					bits++;					
+				}
+
+				break;
+			}
+		}
+	}
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *
+LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset, int type) {
+	FIBITMAP *dib = NULL;
+
+	try {
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+		// load the info header
+
+		BITMAPINFOHEADER bih;
+
+		io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapInfoHeader(&bih);
+#endif
+
+		// keep some general information about the bitmap
+
+		unsigned used_colors	= bih.biClrUsed;
+		int width				= bih.biWidth;
+		int height				= bih.biHeight;		// WARNING: height can be < 0 => check each call using 'height' as a parameter
+		unsigned bit_count		= bih.biBitCount;
+		unsigned compression	= bih.biCompression;
+		unsigned pitch			= CalculatePitch(CalculateLine(width, bit_count));
+
+		switch (bit_count) {
+			case 1 :
+			case 4 :
+			case 8 :
+			{
+				if ((used_colors == 0) || (used_colors > CalculateUsedPaletteEntries(bit_count))) {
+					used_colors = CalculateUsedPaletteEntries(bit_count);
+				}
+				
+				// allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette
+
+				dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+
+				// set resolution information
+				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+				// seek to the end of the header (depending on the BMP header version)
+				// type == sizeof(BITMAPVxINFOHEADER)
+				switch(type) {
+					case 40:	// sizeof(BITMAPINFOHEADER) - all Windows versions since Windows 3.0
+						break;
+					case 52:	// sizeof(BITMAPV2INFOHEADER) (undocumented)
+					case 56:	// sizeof(BITMAPV3INFOHEADER) (undocumented)
+					case 108:	// sizeof(BITMAPV4HEADER) - all Windows versions since Windows 95/NT4 (not supported)
+					case 124:	// sizeof(BITMAPV5HEADER) - Windows 98/2000 and newer (not supported)
+						io->seek_proc(handle, (long)(type - sizeof(BITMAPINFOHEADER)), SEEK_CUR);
+						break;
+				}
+				
+				// load the palette
+
+				io->read_proc(FreeImage_GetPalette(dib), used_colors * sizeof(RGBQUAD), 1, handle);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+				RGBQUAD *pal = FreeImage_GetPalette(dib);
+				for(int i = 0; i < used_colors; i++) {
+					INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
+				}
+#endif
+
+				if(header_only) {
+					// header only mode
+					return dib;
+				}
+
+				// seek to the actual pixel data.
+				// this is needed because sometimes the palette is larger than the entries it contains predicts
+				io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+				// read the pixel data
+
+				switch (compression) {
+					case BI_RGB :
+						if( LoadPixelData(io, handle, dib, height, pitch, bit_count) ) {
+							return dib;
+						} else {
+							throw "Error encountered while decoding BMP data";
+						}
+						break;
+
+					case BI_RLE4 :
+						if( LoadPixelDataRLE4(io, handle, width, height, dib) ) {
+							return dib;
+						} else {
+							throw "Error encountered while decoding RLE4 BMP data";
+						}
+						break;
+
+					case BI_RLE8 :
+						if( LoadPixelDataRLE8(io, handle, width, height, dib) ) {
+							return dib;
+						} else {
+							throw "Error encountered while decoding RLE8 BMP data";
+						}
+						break;
+
+					default :
+						throw FI_MSG_ERROR_UNSUPPORTED_COMPRESSION;
+				}
+			}
+			break; // 1-, 4-, 8-bit
+
+			case 16 :
+			{
+				int use_bitfields = 0;
+				if (bih.biCompression == BI_BITFIELDS) use_bitfields = 3;
+				else if (bih.biCompression == BI_ALPHABITFIELDS) use_bitfields = 4;
+				else if (type == 52) use_bitfields = 3;
+				else if (type >= 56) use_bitfields = 4;
+				
+				if (use_bitfields > 0) {
+ 					DWORD bitfields[4];
+					io->read_proc(bitfields, use_bitfields * sizeof(DWORD), 1, handle);
+					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
+				} else {
+					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+				}
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;						
+				}
+
+				// set resolution information
+				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+				if(header_only) {
+					// header only mode
+					return dib;
+				}
+				
+				// seek to the actual pixel data
+				io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+				// load pixel data and swap as needed if OS is Big Endian
+				LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+				return dib;
+			}
+			break; // 16-bit
+
+			case 24 :
+			case 32 :
+			{
+				int use_bitfields = 0;
+				if (bih.biCompression == BI_BITFIELDS) use_bitfields = 3;
+				else if (bih.biCompression == BI_ALPHABITFIELDS) use_bitfields = 4;
+				else if (type == 52) use_bitfields = 3;
+				else if (type >= 56) use_bitfields = 4;
+
+ 				if (use_bitfields > 0) {
+					DWORD bitfields[4];
+					io->read_proc(bitfields, use_bitfields * sizeof(DWORD), 1, handle);
+					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
+				} else {
+					if( bit_count == 32 ) {
+						dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+					} else {
+						dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+					}
+				}
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+
+				// set resolution information
+				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+				if(header_only) {
+					// header only mode
+					return dib;
+				}
+
+				// Skip over the optional palette 
+				// A 24 or 32 bit DIB may contain a palette for faster color reduction
+				// i.e. you can have (FreeImage_GetColorsUsed(dib) > 0)
+
+				// seek to the actual pixel data
+				io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+				// read in the bitmap bits
+				// load pixel data and swap as needed if OS is Big Endian
+				LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+				// check if the bitmap contains transparency, if so enable it in the header
+
+				FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));
+
+				return dib;
+			}
+			break; // 24-, 32-bit
+		}
+	} catch(const char *message) {
+		if(dib) {
+			FreeImage_Unload(dib);
+		}
+		if(message) {
+			FreeImage_OutputMessageProc(s_format_id, message);
+		}
+	}
+
+	return NULL;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *
+LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
+	FIBITMAP *dib = NULL;
+
+	try {
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+		// load the info header
+
+		BITMAPINFOHEADER bih;
+
+		io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapInfoHeader(&bih);
+#endif
+
+		// keep some general information about the bitmap
+
+		unsigned used_colors	= bih.biClrUsed;
+		int width				= bih.biWidth;
+		int height				= bih.biHeight;		// WARNING: height can be < 0 => check each read_proc using 'height' as a parameter
+		unsigned bit_count		= bih.biBitCount;
+		unsigned compression	= bih.biCompression;
+		unsigned pitch			= CalculatePitch(CalculateLine(width, bit_count));
+		
+		switch (bit_count) {
+			case 1 :
+			case 4 :
+			case 8 :
+			{
+				if ((used_colors == 0) || (used_colors > CalculateUsedPaletteEntries(bit_count)))
+					used_colors = CalculateUsedPaletteEntries(bit_count);
+					
+				// allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette
+
+				dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+
+				// set resolution information
+				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+				
+				// load the palette
+				// note that it may contain RGB or RGBA values : we will calculate this
+				unsigned pal_size = (bitmap_bits_offset - sizeof(BITMAPFILEHEADER) - bih.biSize) / used_colors; 
+
+				io->seek_proc(handle, sizeof(BITMAPFILEHEADER) + bih.biSize, SEEK_SET);
+
+				RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+				if(pal_size == 4) {
+					for (unsigned count = 0; count < used_colors; count++) {
+						FILE_BGRA bgra;
+
+						io->read_proc(&bgra, sizeof(FILE_BGRA), 1, handle);
+						
+						pal[count].rgbRed	= bgra.r;
+						pal[count].rgbGreen = bgra.g;
+						pal[count].rgbBlue	= bgra.b;
+					} 
+				} else if(pal_size == 3) {
+					for (unsigned count = 0; count < used_colors; count++) {
+						FILE_BGR bgr;
+
+						io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
+						
+						pal[count].rgbRed	= bgr.r;
+						pal[count].rgbGreen = bgr.g;
+						pal[count].rgbBlue	= bgr.b;
+					} 
+				}
+				
+				if(header_only) {
+					// header only mode
+					return dib;
+				}
+
+				// seek to the actual pixel data.
+				// this is needed because sometimes the palette is larger than the entries it contains predicts
+
+				if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
+					io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+				}
+
+				// read the pixel data
+
+				switch (compression) {
+					case BI_RGB :
+						// load pixel data 
+						LoadPixelData(io, handle, dib, height, pitch, bit_count);						
+						return dib;
+
+					case BI_RLE4 :
+						if( LoadPixelDataRLE4(io, handle, width, height, dib) ) {
+							return dib;
+						} else {
+							throw "Error encountered while decoding RLE4 BMP data";
+						}
+						break;
+
+					case BI_RLE8 :
+						if( LoadPixelDataRLE8(io, handle, width, height, dib) ) {
+							return dib;
+						} else {
+							throw "Error encountered while decoding RLE8 BMP data";
+						}
+						break;
+
+					default :		
+						throw FI_MSG_ERROR_UNSUPPORTED_COMPRESSION;
+				}	
+			}
+
+			case 16 :
+			{
+				if (bih.biCompression == 3) {
+					DWORD bitfields[3];
+
+					io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
+				} else {
+					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+				}
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+
+				// set resolution information
+				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+				if(header_only) {
+					// header only mode
+					return dib;
+				}
+
+				if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
+					io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+				}
+
+				// load pixel data and swap as needed if OS is Big Endian
+				LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+				return dib;
+			}
+
+			case 24 :
+			case 32 :
+			{
+				if( bit_count == 32 ) {
+					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				} else {
+					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				}
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+				
+				// set resolution information
+				FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+				FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+				if(header_only) {
+					// header only mode
+					return dib;
+				}
+
+				// Skip over the optional palette 
+				// A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+				if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
+					io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+				}
+				
+				// read in the bitmap bits
+				// load pixel data and swap as needed if OS is Big Endian
+				LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+				// check if the bitmap contains transparency, if so enable it in the header
+
+				FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));
+
+				return dib;
+			}
+		}
+	} catch(const char *message) {
+		if(dib)
+			FreeImage_Unload(dib);
+
+		FreeImage_OutputMessageProc(s_format_id, message);
+	}
+
+	return NULL;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *
+LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
+	FIBITMAP *dib = NULL;
+
+	try {
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+		BITMAPINFOOS2_1X_HEADER bios2_1x;
+
+		io->read_proc(&bios2_1x, sizeof(BITMAPINFOOS2_1X_HEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapOS21XHeader(&bios2_1x);
+#endif
+		// keep some general information about the bitmap
+
+		unsigned used_colors = 0;
+		unsigned width		= bios2_1x.biWidth;
+		unsigned height		= bios2_1x.biHeight;	// WARNING: height can be < 0 => check each read_proc using 'height' as a parameter
+		unsigned bit_count	= bios2_1x.biBitCount;
+		unsigned pitch		= CalculatePitch(CalculateLine(width, bit_count));
+		
+		switch (bit_count) {
+			case 1 :
+			case 4 :
+			case 8 :
+			{
+				used_colors = CalculateUsedPaletteEntries(bit_count);
+				
+				// allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette
+
+				dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+
+				// set resolution information to default values (72 dpi in english units)
+				FreeImage_SetDotsPerMeterX(dib, 2835);
+				FreeImage_SetDotsPerMeterY(dib, 2835);
+				
+				// load the palette
+
+				RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+				for (unsigned count = 0; count < used_colors; count++) {
+					FILE_BGR bgr;
+
+					io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
+					
+					pal[count].rgbRed	= bgr.r;
+					pal[count].rgbGreen = bgr.g;
+					pal[count].rgbBlue	= bgr.b;
+				}
+				
+				if(header_only) {
+					// header only mode
+					return dib;
+				}
+
+				// Skip over the optional palette 
+				// A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+				io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+				
+				// read the pixel data
+
+				// load pixel data 
+				LoadPixelData(io, handle, dib, height, pitch, bit_count);
+						
+				return dib;
+			}
+
+			case 16 :
+			{
+				dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;						
+				}
+
+				// set resolution information to default values (72 dpi in english units)
+				FreeImage_SetDotsPerMeterX(dib, 2835);
+				FreeImage_SetDotsPerMeterY(dib, 2835);
+
+				if(header_only) {
+					// header only mode
+					return dib;
+				}
+
+				// load pixel data and swap as needed if OS is Big Endian
+				LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+				return dib;
+			}
+
+			case 24 :
+			case 32 :
+			{
+				if( bit_count == 32 ) {
+					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				} else {
+					dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				}
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;						
+				}
+
+				// set resolution information to default values (72 dpi in english units)
+				FreeImage_SetDotsPerMeterX(dib, 2835);
+				FreeImage_SetDotsPerMeterY(dib, 2835);
+
+				if(header_only) {
+					// header only mode
+					return dib;
+				}
+
+				// Skip over the optional palette 
+				// A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+				// load pixel data and swap as needed if OS is Big Endian
+				LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+				// check if the bitmap contains transparency, if so enable it in the header
+
+				FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));
+
+				return dib;
+			}
+		}
+	} catch(const char *message) {	
+		if(dib)
+			FreeImage_Unload(dib);
+
+		FreeImage_OutputMessageProc(s_format_id, message);
+	}
+
+	return NULL;
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "BMP";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Windows or OS/2 Bitmap";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "bmp";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return "^BM";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/bmp";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE bmp_signature1[] = { 0x42, 0x4D };
+	BYTE bmp_signature2[] = { 0x42, 0x41 };
+	BYTE signature[2] = { 0, 0 };
+
+	io->read_proc(signature, 1, sizeof(bmp_signature1), handle);
+
+	if (memcmp(bmp_signature1, signature, sizeof(bmp_signature1)) == 0)
+		return TRUE;
+
+	if (memcmp(bmp_signature2, signature, sizeof(bmp_signature2)) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+			(depth == 1) ||
+			(depth == 4) ||
+			(depth == 8) ||
+			(depth == 16) ||
+			(depth == 24) ||
+			(depth == 32)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	if (handle != NULL) {
+		BITMAPFILEHEADER bitmapfileheader;
+		DWORD type = 0;
+
+		// we use this offset value to make seemingly absolute seeks relative in the file
+		
+		long offset_in_file = io->tell_proc(handle);
+
+		// read the fileheader
+
+		io->read_proc(&bitmapfileheader, sizeof(BITMAPFILEHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapFileHeader(&bitmapfileheader);
+#endif
+
+		// check the signature
+
+		if((bitmapfileheader.bfType != 0x4D42) && (bitmapfileheader.bfType != 0x4142)) {
+			FreeImage_OutputMessageProc(s_format_id, FI_MSG_ERROR_MAGIC_NUMBER);
+			return NULL;
+		}
+
+		// read the first byte of the infoheader
+
+		io->read_proc(&type, sizeof(DWORD), 1, handle);
+		io->seek_proc(handle, 0 - (long)sizeof(DWORD), SEEK_CUR);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapLong(&type);
+#endif
+
+		// call the appropriate load function for the found bitmap type
+
+		switch(type) {
+			case 12:
+				// OS/2 and also all Windows versions since Windows 3.0
+				return LoadOS21XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+
+			case 64:
+				// OS/2
+				return LoadOS22XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+
+			case 40:	// BITMAPINFOHEADER - all Windows versions since Windows 3.0
+			case 52:	// BITMAPV2INFOHEADER (undocumented, partially supported)
+			case 56:	// BITMAPV3INFOHEADER (undocumented, partially supported)
+			case 108:	// BITMAPV4HEADER - all Windows versions since Windows 95/NT4 (partially supported)
+			case 124:	// BITMAPV5HEADER - Windows 98/2000 and newer (partially supported)
+				return LoadWindowsBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits, type);
+
+			default:
+				break;
+		}
+
+		FreeImage_OutputMessageProc(s_format_id, "unknown bmp subtype with id %d", type);
+	}
+
+	return NULL;
+}
+
+// ----------------------------------------------------------
+
+/**
+Encode a 8-bit source buffer into a 8-bit target buffer using a RLE compression algorithm. 
+The size of the target buffer must be equal to the size of the source buffer. 
+On return, the function will return the real size of the target buffer, which should be less that or equal to the source buffer size. 
+@param target 8-bit Target buffer
+@param source 8-bit Source buffer
+@param size Source/Target input buffer size
+@return Returns the target buffer size
+*/
+static int
+RLEEncodeLine(BYTE *target, BYTE *source, int size) {
+	BYTE buffer[256];
+	int buffer_size = 0;
+	int target_pos = 0;
+
+	for (int i = 0; i < size; ++i) {
+		if ((i < size - 1) && (source[i] == source[i + 1])) {
+			// find a solid block of same bytes
+
+			int j = i + 1;
+			int jmax = 254 + i;
+
+			while ((j < size - 1) && (j < jmax) && (source[j] == source[j + 1]))
+				++j;
+
+			// if the block is larger than 3 bytes, use it
+			// else put the data into the larger pool
+
+			if (((j - i) + 1) > 3) {
+				// don't forget to write what we already have in the buffer
+
+				switch(buffer_size) {
+					case 0 :
+						break;
+
+					case RLE_DELTA :
+						target[target_pos++] = 1;
+						target[target_pos++] = buffer[0];
+						target[target_pos++] = 1;
+						target[target_pos++] = buffer[1];
+						break;
+
+					case RLE_ENDOFBITMAP :
+						target[target_pos++] = (BYTE)buffer_size;
+						target[target_pos++] = buffer[0];
+						break;
+
+					default :
+						target[target_pos++] = RLE_COMMAND;
+						target[target_pos++] = (BYTE)buffer_size;
+						memcpy(target + target_pos, buffer, buffer_size);
+
+						// prepare for next run
+						
+						target_pos += buffer_size;
+
+						if ((buffer_size & 1) == 1)
+							target_pos++;
+
+						break;
+				}
+
+				// write the continuous data
+
+				target[target_pos++] = (BYTE)((j - i) + 1);
+				target[target_pos++] = source[i];
+
+				buffer_size = 0;
+			} else {
+				for (int k = 0; k < (j - i) + 1; ++k) {
+					buffer[buffer_size++] = source[i + k];
+
+					if (buffer_size == 254) {
+						// write what we have
+
+						target[target_pos++] = RLE_COMMAND;
+						target[target_pos++] = (BYTE)buffer_size;
+						memcpy(target + target_pos, buffer, buffer_size);
+
+						// prepare for next run
+
+						target_pos += buffer_size;
+						buffer_size = 0;
+					}
+				}
+			}
+
+			i = j;
+		} else {
+			buffer[buffer_size++] = source[i];
+		}
+
+		// write the buffer if it's full
+
+		if (buffer_size == 254) {
+			target[target_pos++] = RLE_COMMAND;
+			target[target_pos++] = (BYTE)buffer_size;
+			memcpy(target + target_pos, buffer, buffer_size);
+
+			// prepare for next run
+
+			target_pos += buffer_size;
+			buffer_size = 0;
+		}
+	}
+
+	// write the last bytes
+
+	switch(buffer_size) {
+		case 0 :
+			break;
+
+		case RLE_DELTA :
+			target[target_pos++] = 1;
+			target[target_pos++] = buffer[0];
+			target[target_pos++] = 1;
+			target[target_pos++] = buffer[1];
+			break;
+
+		case RLE_ENDOFBITMAP :
+			target[target_pos++] = (BYTE)buffer_size;
+			target[target_pos++] = buffer[0];
+			break;
+
+		default :
+			target[target_pos++] = RLE_COMMAND;
+			target[target_pos++] = (BYTE)buffer_size;
+			memcpy(target + target_pos, buffer, buffer_size);
+
+			// prepare for next run
+			
+			target_pos += buffer_size;
+
+			if ((buffer_size & 1) == 1)
+				target_pos++;
+
+			break;			
+	}
+
+	// write the END_OF_LINE marker
+
+	target[target_pos++] = RLE_COMMAND;
+	target[target_pos++] = RLE_ENDOFLINE;
+
+	// return the written size
+
+	return target_pos;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	if ((dib != NULL) && (handle != NULL)) {
+		// write the file header
+
+		BITMAPFILEHEADER bitmapfileheader;
+		bitmapfileheader.bfType = 0x4D42;
+		bitmapfileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD);
+		bitmapfileheader.bfSize = bitmapfileheader.bfOffBits + FreeImage_GetHeight(dib) * FreeImage_GetPitch(dib);
+		bitmapfileheader.bfReserved1 = 0;
+		bitmapfileheader.bfReserved2 = 0;
+
+		// take care of the bit fields data of any
+
+		bool bit_fields = (FreeImage_GetBPP(dib) == 16);
+
+		if (bit_fields) {
+			bitmapfileheader.bfSize += 3 * sizeof(DWORD);
+			bitmapfileheader.bfOffBits += 3 * sizeof(DWORD);
+		}
+
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapFileHeader(&bitmapfileheader);
+#endif
+		if (io->write_proc(&bitmapfileheader, sizeof(BITMAPFILEHEADER), 1, handle) != 1)
+			return FALSE;		
+
+		// update the bitmap info header
+
+		BITMAPINFOHEADER bih;
+		memcpy(&bih, FreeImage_GetInfoHeader(dib), sizeof(BITMAPINFOHEADER));
+
+		if (bit_fields)
+			bih.biCompression = BI_BITFIELDS;
+		else if ((bih.biBitCount == 8) && (flags & BMP_SAVE_RLE))
+			bih.biCompression = BI_RLE8;
+		else
+			bih.biCompression = BI_RGB;
+
+		// write the bitmap info header
+
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapInfoHeader(&bih);
+#endif
+		if (io->write_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle) != 1)
+			return FALSE;
+
+		// write the bit fields when we are dealing with a 16 bit BMP
+
+		if (bit_fields) {
+			DWORD d;
+
+			d = FreeImage_GetRedMask(dib);
+
+			if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1)
+				return FALSE;
+
+			d = FreeImage_GetGreenMask(dib);
+
+			if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1)
+				return FALSE;
+
+			d = FreeImage_GetBlueMask(dib);
+
+			if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1)
+				return FALSE;
+		}
+
+		// write the palette
+
+		if (FreeImage_GetPalette(dib) != NULL) {
+			RGBQUAD *pal = FreeImage_GetPalette(dib);
+			FILE_BGRA bgra;
+			for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++ ) {
+				bgra.b = pal[i].rgbBlue;
+				bgra.g = pal[i].rgbGreen;
+				bgra.r = pal[i].rgbRed;
+				bgra.a = pal[i].rgbReserved;
+				if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1)
+					return FALSE;
+			}
+		}
+
+		// write the bitmap data... if RLE compression is enable, use it
+
+		unsigned bpp = FreeImage_GetBPP(dib);
+		if ((bpp == 8) && (flags & BMP_SAVE_RLE)) {
+			BYTE *buffer = (BYTE*)malloc(FreeImage_GetPitch(dib) * 2 * sizeof(BYTE));
+
+			for (DWORD i = 0; i < FreeImage_GetHeight(dib); ++i) {
+				int size = RLEEncodeLine(buffer, FreeImage_GetScanLine(dib, i), FreeImage_GetLine(dib));
+
+				if (io->write_proc(buffer, size, 1, handle) != 1) {
+					free(buffer);
+					return FALSE;
+				}
+			}
+
+			buffer[0] = RLE_COMMAND;
+			buffer[1] = RLE_ENDOFBITMAP;
+
+			if (io->write_proc(buffer, 2, 1, handle) != 1) {
+				free(buffer);
+				return FALSE;
+			}
+
+			free(buffer);
+#ifdef FREEIMAGE_BIGENDIAN
+		} else if (bpp == 16) {
+			int padding = FreeImage_GetPitch(dib) - FreeImage_GetWidth(dib) * sizeof(WORD);
+			WORD pad = 0;
+			WORD pixel;
+			for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+				BYTE *line = FreeImage_GetScanLine(dib, y);
+				for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+					pixel = ((WORD *)line)[x];
+					SwapShort(&pixel);
+					if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1)
+						return FALSE;
+				}
+				if(padding != 0) {
+					if(io->write_proc(&pad, padding, 1, handle) != 1) {
+						return FALSE;				
+					}
+				}
+			}
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+		} else if (bpp == 24) {
+			int padding = FreeImage_GetPitch(dib) - FreeImage_GetWidth(dib) * sizeof(FILE_BGR);
+			DWORD pad = 0;
+			FILE_BGR bgr;
+			for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+				BYTE *line = FreeImage_GetScanLine(dib, y);
+				for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+					RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x;
+					bgr.b = triple->rgbtBlue;
+					bgr.g = triple->rgbtGreen;
+					bgr.r = triple->rgbtRed;
+					if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1)
+						return FALSE;
+				}
+				if(padding != 0) {
+					if(io->write_proc(&pad, padding, 1, handle) != 1) {
+						return FALSE;					
+					}
+				}
+			}
+		} else if (bpp == 32) {
+			FILE_BGRA bgra;
+			for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+				BYTE *line = FreeImage_GetScanLine(dib, y);
+				for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+					RGBQUAD *quad = ((RGBQUAD *)line)+x;
+					bgra.b = quad->rgbBlue;
+					bgra.g = quad->rgbGreen;
+					bgra.r = quad->rgbRed;
+					bgra.a = quad->rgbReserved;
+					if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1)
+						return FALSE;
+				}
+			}
+#endif
+		} else if (io->write_proc(FreeImage_GetBits(dib), FreeImage_GetHeight(dib) * FreeImage_GetPitch(dib), 1, handle) != 1) {
+			return FALSE;
+		}
+
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitBMP(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;	// not implemented yet;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginCUT.cpp b/files/Source/FreeImage/PluginCUT.cpp
new file mode 100644
index 0000000..5dcd16b
--- /dev/null
+++ b/files/Source/FreeImage/PluginCUT.cpp
@@ -0,0 +1,240 @@
+// ==========================================================
+// CUT Loader
+//
+// 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"
+
+// ----------------------------------------------------------
+//   Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagCUTHEADER {
+	WORD width;
+	WORD height;
+	LONG dummy;
+} CUTHEADER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "CUT";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Dr. Halo";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "cut";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-cut";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	FIBITMAP *dib = NULL;
+
+	if(!handle) {
+		return NULL;
+	}
+
+	try {
+		CUTHEADER header;		
+
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+		// read the cut header
+
+		if(io->read_proc(&header, 1, sizeof(CUTHEADER), handle) != sizeof(CUTHEADER)) {
+			throw FI_MSG_ERROR_PARSING;
+		}
+
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapShort((WORD *)&header.width);
+		SwapShort((WORD *)&header.height);
+#endif
+
+		if ((header.width == 0) || (header.height == 0)) {
+			return NULL;
+		}
+
+		// allocate a new bitmap
+
+		dib = FreeImage_AllocateHeader(header_only, header.width, header.height, 8);
+
+		if (dib == NULL) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		// stuff it with a palette
+
+		RGBQUAD *palette = FreeImage_GetPalette(dib);
+
+		for (int j = 0; j < 256; ++j) {
+			palette[j].rgbBlue = palette[j].rgbGreen = palette[j].rgbRed = (BYTE)j;
+		}
+		
+		if(header_only) {
+			// header only mode
+			return dib;
+		}
+
+		// unpack the RLE bitmap bits
+
+		BYTE *bits = FreeImage_GetScanLine(dib, header.height - 1);
+
+		unsigned i = 0, k = 0;
+		unsigned pitch = FreeImage_GetPitch(dib);
+		unsigned size = header.width * header.height;
+		BYTE count = 0, run = 0;
+
+		while (i < size) {
+			if(io->read_proc(&count, 1, sizeof(BYTE), handle) != 1) {
+				throw FI_MSG_ERROR_PARSING;
+			}
+
+			if (count == 0) {
+				k = 0;
+				bits -= pitch;
+
+				// paint shop pro adds two useless bytes here...
+
+				io->read_proc(&count, 1, sizeof(BYTE), handle);
+				io->read_proc(&count, 1, sizeof(BYTE), handle);
+
+				continue;
+			}
+
+			if (count & 0x80) {
+				count &= ~(0x80);
+
+				if(io->read_proc(&run, 1, sizeof(BYTE), handle) != 1) {
+					throw FI_MSG_ERROR_PARSING;
+				}
+
+				if(k + count <= header.width) {
+					memset(bits + k, run, count);
+				} else {
+					throw FI_MSG_ERROR_PARSING;
+				}
+			} else {
+				if(k + count <= header.width) {
+					if(io->read_proc(&bits[k], count, sizeof(BYTE), handle) != 1) {
+						throw FI_MSG_ERROR_PARSING;
+					}
+				} else {
+					throw FI_MSG_ERROR_PARSING;
+				}
+			}
+
+			k += count;
+			i += count;
+		}
+
+		return dib;
+
+	} catch(const char* text) {
+		if(dib) {
+			FreeImage_Unload(dib);
+		}
+		FreeImage_OutputMessageProc(s_format_id, text);
+		return NULL;
+	}
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitCUT(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginDDS.cpp b/files/Source/FreeImage/PluginDDS.cpp
new file mode 100644
index 0000000..6399423
--- /dev/null
+++ b/files/Source/FreeImage/PluginDDS.cpp
@@ -0,0 +1,655 @@
+// ==========================================================
+// DDS Loader
+//
+// Design and implementation by
+// - Volker Gärtner (volkerg@gmx.at)
+// - Sherman Wilcox
+//
+// 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"
+
+// ----------------------------------------------------------
+//   Definitions for the DDS format
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagDDPIXELFORMAT {
+	DWORD dwSize;	// size of this structure (must be 32)
+	DWORD dwFlags;	// see DDPF_*
+	DWORD dwFourCC;
+	DWORD dwRGBBitCount;	// Total number of bits for RGB formats
+	DWORD dwRBitMask;
+	DWORD dwGBitMask;
+	DWORD dwBBitMask;
+	DWORD dwRGBAlphaBitMask;
+} DDPIXELFORMAT;
+
+// DIRECTDRAW PIXELFORMAT FLAGS
+enum {
+	DDPF_ALPHAPIXELS = 0x00000001l,	// surface has alpha channel
+	DDPF_ALPHA		 = 0x00000002l,	// alpha only
+	DDPF_FOURCC		 = 0x00000004l,	// FOURCC available
+	DDPF_RGB		 = 0x00000040l	// RGB(A) bitmap
+};
+
+typedef struct tagDDCAPS2 {
+	DWORD dwCaps1;	// Zero or more of the DDSCAPS_* members
+	DWORD dwCaps2;	// Zero or more of the DDSCAPS2_* members
+	DWORD dwReserved[2];
+} DDCAPS2;
+
+// DIRECTDRAWSURFACE CAPABILITY FLAGS
+enum {
+	DDSCAPS_ALPHA	= 0x00000002l, // alpha only surface
+	DDSCAPS_COMPLEX	= 0x00000008l, // complex surface structure
+	DDSCAPS_TEXTURE	= 0x00001000l, // used as texture (should always be set)
+	DDSCAPS_MIPMAP	= 0x00400000l  // Mipmap present
+};
+
+enum {
+	DDSCAPS2_CUBEMAP			= 0x00000200L,
+	DDSCAPS2_CUBEMAP_POSITIVEX	= 0x00000400L,
+	DDSCAPS2_CUBEMAP_NEGATIVEX	= 0x00000800L,
+	DDSCAPS2_CUBEMAP_POSITIVEY	= 0x00001000L,
+	DDSCAPS2_CUBEMAP_NEGATIVEY	= 0x00002000L,
+	DDSCAPS2_CUBEMAP_POSITIVEZ	= 0x00004000L,
+	DDSCAPS2_CUBEMAP_NEGATIVEZ	= 0x00008000L,
+	DDSCAPS2_VOLUME				= 0x00200000L
+};
+
+typedef struct tagDDSURFACEDESC2 {
+	DWORD dwSize;	// size of this structure (must be 124)
+	DWORD dwFlags;	// combination of the DDSS_* flags
+	DWORD dwHeight;
+	DWORD dwWidth;
+	DWORD dwPitchOrLinearSize;
+	DWORD dwDepth;	// Depth of a volume texture
+	DWORD dwMipMapCount;
+	DWORD dwReserved1[11];
+	DDPIXELFORMAT ddpfPixelFormat;
+	DDCAPS2 ddsCaps;
+	DWORD dwReserved2;
+} DDSURFACEDESC2;
+
+enum {
+	DDSD_CAPS			= 0x00000001l,
+	DDSD_HEIGHT			= 0x00000002l,
+	DDSD_WITH			= 0x00000004l,
+	DDSD_PITCH			= 0x00000008l,
+	DDSD_ALPHABITDEPTH  = 0x00000080l,
+	DDSD_PIXELFORMAT	= 0x00001000l,
+	DDSD_MIPMAPCOUNT	= 0x00020000l,
+	DDSD_LINEARSIZE		= 0x00080000l,
+	DDSD_DEPTH			= 0x00800000l
+};
+
+typedef struct tagDDSHEADER {
+	DWORD dwMagic;			// FOURCC: "DDS "
+	DDSURFACEDESC2 surfaceDesc;
+} DDSHEADER;
+
+#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+	((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) |   \
+    ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 ))
+
+#define FOURCC_DXT1	MAKEFOURCC('D','X','T','1')
+#define FOURCC_DXT2	MAKEFOURCC('D','X','T','2')
+#define FOURCC_DXT3	MAKEFOURCC('D','X','T','3')
+#define FOURCC_DXT4	MAKEFOURCC('D','X','T','4')
+#define FOURCC_DXT5	MAKEFOURCC('D','X','T','5')
+
+// ----------------------------------------------------------
+//   Structures used by DXT textures
+// ----------------------------------------------------------
+
+typedef struct tagColor8888 {
+	BYTE b;
+	BYTE g;
+	BYTE r;
+	BYTE a;
+} Color8888;
+
+typedef struct tagColor565 {
+	WORD b : 5;
+	WORD g : 6;
+	WORD r : 5;
+} Color565;
+
+typedef struct tagDXTColBlock {
+	Color565 colors[2];
+	BYTE row[4];
+} DXTColBlock;
+
+typedef struct tagDXTAlphaBlockExplicit {
+	WORD row[4];
+} DXTAlphaBlockExplicit;
+
+typedef struct tagDXTAlphaBlock3BitLinear {
+	BYTE alpha[2];
+	BYTE data[6];
+} DXTAlphaBlock3BitLinear;
+
+typedef struct tagDXT1Block
+{
+	DXTColBlock color;
+} DXT1Block;
+
+typedef struct tagDXT3Block {		// also used by dxt2
+	DXTAlphaBlockExplicit alpha;
+	DXTColBlock color;
+} DXT3Block;
+
+typedef struct tagDXT5Block {		// also used by dxt4
+	DXTAlphaBlock3BitLinear alpha;
+	DXTColBlock color;
+} DXT5Block;
+
+#ifdef _WIN32
+#	pragma pack(pop)
+#else
+#	pragma pack()
+#endif
+
+// ----------------------------------------------------------
+//   Internal functions
+// ----------------------------------------------------------
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapHeader(DDSHEADER *header) {
+	SwapLong(&header->dwMagic);
+	SwapLong(&header->surfaceDesc.dwSize);
+	SwapLong(&header->surfaceDesc.dwFlags);
+	SwapLong(&header->surfaceDesc.dwHeight);
+	SwapLong(&header->surfaceDesc.dwWidth);
+	SwapLong(&header->surfaceDesc.dwPitchOrLinearSize);
+	SwapLong(&header->surfaceDesc.dwDepth);
+	SwapLong(&header->surfaceDesc.dwMipMapCount);
+	for(int i=0; i<11; i++) {
+		SwapLong(&header->surfaceDesc.dwReserved1[i]);
+	}
+	SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwSize);
+	SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwFlags);
+	SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwFourCC);
+	SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwRGBBitCount);
+	SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwRBitMask);
+	SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwGBitMask);
+	SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwBBitMask);
+	SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwRGBAlphaBitMask);
+	SwapLong(&header->surfaceDesc.ddsCaps.dwCaps1);
+	SwapLong(&header->surfaceDesc.ddsCaps.dwCaps2);
+	SwapLong(&header->surfaceDesc.ddsCaps.dwReserved[0]);
+	SwapLong(&header->surfaceDesc.ddsCaps.dwReserved[1]);
+	SwapLong(&header->surfaceDesc.dwReserved2);
+}
+#endif
+
+// ==========================================================
+
+// Get the 4 possible colors for a block
+//
+static void 
+GetBlockColors (const DXTColBlock &block, Color8888 colors[4], bool isDXT1) {
+	int i;
+	// expand from 565 to 888
+	for (i = 0; i < 2; i++)	{
+		colors[i].a = 0xff;
+		/*
+		colors[i].r = (BYTE)(block.colors[i].r * 0xff / 0x1f);
+		colors[i].g = (BYTE)(block.colors[i].g * 0xff / 0x3f);
+		colors[i].b = (BYTE)(block.colors[i].b * 0xff / 0x1f);
+		*/
+		colors[i].r = (BYTE)((block.colors[i].r << 3U) | (block.colors[i].r >> 2U));
+		colors[i].g = (BYTE)((block.colors[i].g << 2U) | (block.colors[i].g >> 4U));
+		colors[i].b = (BYTE)((block.colors[i].b << 3U) | (block.colors[i].b >> 2U));
+	}
+
+	WORD *wCol = (WORD *)block.colors;
+	if (wCol[0] > wCol[1] || !isDXT1) {
+		// 4 color block
+		for (i = 0; i < 2; i++)	{
+			colors[i + 2].a = 0xff;
+			colors[i + 2].r = (BYTE)((WORD (colors[0].r) * (2 - i) + WORD (colors[1].r) * (1 + i)) / 3);
+			colors[i + 2].g = (BYTE)((WORD (colors[0].g) * (2 - i) + WORD (colors[1].g) * (1 + i)) / 3);
+			colors[i + 2].b = (BYTE)((WORD (colors[0].b) * (2 - i) + WORD (colors[1].b) * (1 + i)) / 3);
+		}
+	}
+	else {
+		// 3 color block, number 4 is transparent
+		colors[2].a = 0xff;
+		colors[2].r = (BYTE)((WORD (colors[0].r) + WORD (colors[1].r)) / 2);
+		colors[2].g = (BYTE)((WORD (colors[0].g) + WORD (colors[1].g)) / 2);
+		colors[2].b = (BYTE)((WORD (colors[0].b) + WORD (colors[1].b)) / 2);
+
+		colors[3].a = 0x00;
+		colors[3].g = 0x00;
+		colors[3].b = 0x00;
+		colors[3].r = 0x00;
+	}
+}
+
+struct DXT_INFO_1 {
+	typedef DXT1Block Block;
+	enum {
+		isDXT1 = 1,
+		bytesPerBlock = 8
+	};
+};
+
+struct DXT_INFO_3 {
+	typedef DXT3Block Block;
+	enum {
+		isDXT1 = 1,
+		bytesPerBlock = 16
+	};
+};
+
+struct DXT_INFO_5 {
+	typedef DXT5Block Block;
+	enum
+	{
+		isDXT1 = 1,
+		bytesPerBlock = 16
+	};
+};
+
+template <class INFO> class DXT_BLOCKDECODER_BASE {
+protected:
+	Color8888 m_colors[4];
+	const typename INFO::Block *m_pBlock;
+	unsigned m_colorRow;
+
+public:
+	void Setup (const BYTE *pBlock) {
+		m_pBlock = (const typename INFO::Block *)pBlock;
+		GetBlockColors (m_pBlock->color, m_colors, INFO::isDXT1);
+	}
+
+	void SetY (int y) {
+		m_colorRow = m_pBlock->color.row[y];
+	}
+
+	void GetColor (int x, int y, Color8888 &color) {
+		unsigned bits = (m_colorRow >> (x * 2)) & 3;
+		color = m_colors[bits];
+	}
+};
+
+class DXT_BLOCKDECODER_1 : public DXT_BLOCKDECODER_BASE <DXT_INFO_1> {
+public:
+	typedef DXT_INFO_1 INFO;
+};
+
+class DXT_BLOCKDECODER_3 : public DXT_BLOCKDECODER_BASE <DXT_INFO_3> {
+public:
+	typedef DXT_BLOCKDECODER_BASE <DXT_INFO_3> base;
+	typedef DXT_INFO_3 INFO;
+
+protected:
+	unsigned m_alphaRow;
+
+public:
+	void SetY (int y) {
+		base::SetY (y);
+		m_alphaRow = m_pBlock->alpha.row[y];
+	}
+
+	void GetColor (int x, int y, Color8888 &color) {
+		base::GetColor (x, y, color);
+		const unsigned bits = (m_alphaRow >> (x * 4)) & 0xF;
+		color.a = (BYTE)((bits * 0xFF) / 0xF);
+	}
+};
+
+class DXT_BLOCKDECODER_5 : public DXT_BLOCKDECODER_BASE <DXT_INFO_5> {
+public:
+	typedef DXT_BLOCKDECODER_BASE <DXT_INFO_5> base;
+	typedef DXT_INFO_5 INFO;
+
+protected:
+	unsigned m_alphas[8];
+	unsigned m_alphaBits;
+	int m_offset;
+
+public:
+	void Setup (const BYTE *pBlock) {
+		base::Setup (pBlock);
+
+		const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha;
+		m_alphas[0] = block.alpha[0];
+		m_alphas[1] = block.alpha[1];
+		if (m_alphas[0] > m_alphas[1]) {
+			// 8 alpha block
+			for (int i = 0; i < 6; i++) {
+				m_alphas[i + 2] = ((6 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 3) / 7;
+			}
+		}
+		else {
+			// 6 alpha block
+			for (int i = 0; i < 4; i++) {
+				m_alphas[i + 2] = ((4 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 2) / 5;
+			}
+			m_alphas[6] = 0;
+			m_alphas[7] = 0xFF;
+		}
+
+	}
+
+	void SetY (int y) {
+		base::SetY (y);
+		int i = y / 2;
+		const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha;
+		m_alphaBits = unsigned(block.data[0 + i * 3]) | (unsigned(block.data[1 + i * 3]) << 8)
+			| (unsigned(block.data[2 + i * 3]) << 16);
+		m_offset = (y & 1) * 12;
+	}
+
+	void GetColor (int x, int y, Color8888 &color) {
+		base::GetColor (x, y, color);
+		unsigned bits = (m_alphaBits >> (x * 3 + m_offset)) & 7;
+		color.a = (BYTE)m_alphas[bits];
+	}
+};
+
+template <class DECODER> void DecodeDXTBlock (BYTE *dstData, const BYTE *srcBlock, long dstPitch, int bw, int bh) {
+	DECODER decoder;
+	decoder.Setup (srcBlock);
+	for (int y = 0; y < bh; y++) {
+		BYTE *dst = dstData - y * dstPitch;
+		decoder.SetY (y);
+		for (int x = 0; x < bw; x++) {
+			decoder.GetColor (x, y, (Color8888 &)*dst);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB 
+			INPLACESWAP(dst[FI_RGBA_RED], dst[FI_RGBA_BLUE]);
+#endif 
+			dst += 4;
+		}
+	}
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static FIBITMAP *
+LoadRGB (DDSURFACEDESC2 &desc, FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	int width = (int)desc.dwWidth & ~3;
+	int height = (int)desc.dwHeight & ~3;
+	int bpp = (int)desc.ddpfPixelFormat.dwRGBBitCount;
+	
+	// allocate a new dib
+	FIBITMAP *dib = FreeImage_Allocate (width, height, bpp, desc.ddpfPixelFormat.dwRBitMask,
+		desc.ddpfPixelFormat.dwGBitMask, desc.ddpfPixelFormat.dwBBitMask);
+	if (dib == NULL)
+		return NULL;
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+		// Calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit)
+		int bytespp = FreeImage_GetLine(dib) / FreeImage_GetWidth(dib);
+#endif
+	
+	// read the file
+	int line   = CalculateLine(width, bpp);
+	int filePitch = (desc.dwFlags & DDSD_PITCH) ? (int)desc.dwPitchOrLinearSize : line;
+	long delta = (long)filePitch - (long)line;
+	for (int i = 0; i < height; i++) {
+		BYTE *pixels = FreeImage_GetScanLine(dib, height - i - 1);
+		io->read_proc (pixels, 1, line, handle);
+		io->seek_proc (handle, delta, SEEK_CUR);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ 		for(int x = 0; x < width; x++) {
+			INPLACESWAP(pixels[FI_RGBA_RED],pixels[FI_RGBA_BLUE]);
+			pixels += bytespp;
+		}
+#endif
+	}
+	
+	// enable transparency
+	FreeImage_SetTransparent (dib, (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) ? TRUE : FALSE);
+
+	if (!(desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) && bpp == 32) {
+		// no transparency: convert to 24-bit
+		FIBITMAP *old = dib;
+		dib = FreeImage_ConvertTo24Bits (old);
+		FreeImage_Unload (old);
+	}
+	return dib;
+}
+
+template <class DECODER> static void 
+LoadDXT_Helper (FreeImageIO *io, fi_handle handle, int page, int flags, void *data, FIBITMAP *dib, int width, int height, int line) {
+	typedef typename DECODER::INFO INFO;
+	typedef typename INFO::Block Block;
+
+	Block *input_buffer = new(std::nothrow) Block[(width + 3) / 4];
+	if(!input_buffer) return;
+
+	int widthRest = (int) width & 3;
+	int heightRest = (int) height & 3;
+	int inputLine = (width + 3) / 4;
+	int y = 0;
+
+	if (height >= 4) {
+		for (; y < height; y += 4) {
+			io->read_proc (input_buffer, sizeof (typename INFO::Block), inputLine, handle);
+			// TODO: probably need some endian work here
+			BYTE *pbSrc = (BYTE *)input_buffer;
+			BYTE *pbDst = FreeImage_GetScanLine (dib, height - y - 1);
+
+			if (width >= 4) {
+				for (int x = 0; x < width; x += 4) {
+					DecodeDXTBlock <DECODER> (pbDst, pbSrc,	line, 4, 4);
+					pbSrc += INFO::bytesPerBlock;
+					pbDst += 4 * 4;
+				}
+			}
+			if (widthRest) {
+				DecodeDXTBlock <DECODER> (pbDst, pbSrc, line, widthRest, 4);
+			}
+		}
+	}
+	if (heightRest)	{
+		io->read_proc (input_buffer, sizeof (typename INFO::Block), inputLine, handle);
+		// TODO: probably need some endian work here
+		BYTE *pbSrc = (BYTE *)input_buffer;
+		BYTE *pbDst = FreeImage_GetScanLine (dib, height - y - 1);
+
+		if (width >= 4) {
+			for (int x = 0; x < width; x += 4) {
+				DecodeDXTBlock <DECODER> (pbDst, pbSrc,	line, 4, heightRest);
+				pbSrc += INFO::bytesPerBlock;
+				pbDst += 4 * 4;
+			}
+		}
+		if (widthRest) {
+			DecodeDXTBlock <DECODER> (pbDst, pbSrc,	line, widthRest, heightRest);
+		}
+
+	}
+
+	delete [] input_buffer;
+}
+
+static FIBITMAP *
+LoadDXT (int type, DDSURFACEDESC2 &desc, FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	int width = (int)desc.dwWidth & ~3;
+	int height = (int)desc.dwHeight & ~3;
+
+	// allocate a 32-bit dib
+	FIBITMAP *dib = FreeImage_Allocate (width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+	if (dib == NULL)
+		return NULL;
+
+	int bpp = FreeImage_GetBPP (dib);
+	int line = CalculateLine (width, bpp);
+	BYTE *bits = FreeImage_GetBits (dib);
+
+	// select the right decoder
+	switch (type) {
+		case 1:
+			LoadDXT_Helper <DXT_BLOCKDECODER_1> (io, handle, page, flags, data, dib, width, height, line);
+			break;
+		case 3:
+			LoadDXT_Helper <DXT_BLOCKDECODER_3> (io, handle, page, flags, data, dib, width, height, line);
+			break;
+		case 5:
+			LoadDXT_Helper <DXT_BLOCKDECODER_5> (io, handle, page, flags, data, dib, width, height, line);
+			break;
+	}
+	
+	return dib;
+}
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "DDS";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "DirectX Surface";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "dds";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-dds";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	DDSHEADER header;
+	memset(&header, 0, sizeof(header));
+	io->read_proc(&header, 1, sizeof(header), handle);
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapHeader(&header);
+#endif
+	if (header.dwMagic != MAKEFOURCC ('D','D','S',' '))
+		return FALSE;
+	if (header.surfaceDesc.dwSize != sizeof (header.surfaceDesc) ||
+		header.surfaceDesc.ddpfPixelFormat.dwSize != sizeof (header.surfaceDesc.ddpfPixelFormat))
+		return FALSE;
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	return NULL;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	DDSHEADER header;
+	FIBITMAP *dib = NULL;
+
+	memset(&header, 0, sizeof(header));
+	io->read_proc(&header, 1, sizeof(header), handle);
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapHeader(&header);
+#endif
+	if (header.surfaceDesc.ddpfPixelFormat.dwFlags & DDPF_RGB) {
+		dib = LoadRGB (header.surfaceDesc, io, handle, page, flags, data);
+	}
+	else if (header.surfaceDesc.ddpfPixelFormat.dwFlags & DDPF_FOURCC) {
+		switch (header.surfaceDesc.ddpfPixelFormat.dwFourCC) {
+			case FOURCC_DXT1:
+				dib = LoadDXT (1, header.surfaceDesc, io, handle, page, flags, data);
+				break;
+			case FOURCC_DXT3:
+				dib = LoadDXT (3, header.surfaceDesc, io, handle, page, flags, data);
+				break;
+			case FOURCC_DXT5:
+				dib = LoadDXT (5, header.surfaceDesc, io, handle, page, flags, data);
+				break;
+		}
+	}
+	return dib;
+}
+
+/*
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	return FALSE;
+}
+*/
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitDDS(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;	//Save;	// not implemented (yet?)
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/files/Source/FreeImage/PluginEXR.cpp b/files/Source/FreeImage/PluginEXR.cpp
new file mode 100644
index 0000000..9d25efb
--- /dev/null
+++ b/files/Source/FreeImage/PluginEXR.cpp
@@ -0,0 +1,772 @@
+// ==========================================================
+// EXR Loader and writer
+//
+// Design and implementation by 
+// - Hervé Drolon (drolon@infonie.fr)
+// - 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!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+#ifdef _MSC_VER
+// OpenEXR has many problems with MSVC warnings (why not just correct them ?), just ignore one of them
+#pragma warning (disable : 4800) // ImfVersion.h - 'const int' : forcing value to bool 'true' or 'false' (performance warning)
+#endif 
+#include "third_party/openexr/IlmImf/ImfIO.h"
+#include "third_party/ilmbase/Iex/Iex.h"
+#include "third_party/openexr/IlmImf/ImfOutputFile.h"
+#include "third_party/openexr/IlmImf/ImfInputFile.h"
+#include "third_party/openexr/IlmImf/ImfRgbaFile.h"
+#include "third_party/openexr/IlmImf/ImfChannelList.h"
+#include "third_party/openexr/IlmImf/ImfRgba.h"
+#include "third_party/openexr/IlmImf/ImfArray.h"
+#include "third_party/openexr/IlmImf/ImfPreviewImage.h"
+#include "third_party/ilmbase/Half/half.h"
+
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ----------------------------------------------------------
+
+/**
+FreeImage input stream wrapper
+@see Imf_2_2::IStream
+*/
+class C_IStream : public Imf::IStream {
+private:
+    FreeImageIO *_io;
+	fi_handle _handle;
+
+public:
+	C_IStream (FreeImageIO *io, fi_handle handle) : 
+	  Imf::IStream(""), _io (io), _handle(handle) {
+	}
+
+	virtual bool read (char c[/*n*/], int n) {
+		return ((unsigned)n != _io->read_proc(c, 1, n, _handle));
+	}
+
+	virtual Imath::Int64 tellg() {
+		return _io->tell_proc(_handle);
+	}
+
+	virtual void seekg(Imath::Int64 pos) {
+		_io->seek_proc(_handle, (unsigned)pos, SEEK_SET);
+	}
+
+	virtual void clear() {
+	}
+};
+
+// ----------------------------------------------------------
+
+/**
+FreeImage output stream wrapper
+@see Imf_2_2::OStream
+*/
+class C_OStream : public Imf::OStream {
+private:
+    FreeImageIO *_io;
+	fi_handle _handle;
+
+public:
+	C_OStream (FreeImageIO *io, fi_handle handle) : 
+	  Imf::OStream(""), _io (io), _handle(handle) {
+	}
+
+	virtual void write(const char c[/*n*/], int n) {
+		if((unsigned)n != _io->write_proc((void*)&c[0], 1, n, _handle)) {
+			Iex::throwErrnoExc();
+		}
+	}
+
+	virtual Imath::Int64 tellp() {
+		return _io->tell_proc(_handle);
+	}
+
+	virtual void seekp(Imath::Int64 pos) {
+		_io->seek_proc(_handle, (unsigned)pos, SEEK_SET);
+	}
+};
+
+// ----------------------------------------------------------
+
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "EXR";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "ILM OpenEXR";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "exr";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-exr";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE exr_signature[] = { 0x76, 0x2F, 0x31, 0x01 };
+	BYTE signature[] = { 0, 0, 0, 0 };
+
+	io->read_proc(signature, 1, 4, handle);
+	return (memcmp(exr_signature, signature, 4) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (
+		(type == FIT_FLOAT) ||
+		(type == FIT_RGBF)  ||
+		(type == FIT_RGBAF)
+	);
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	bool bUseRgbaInterface = false;
+	FIBITMAP *dib = NULL;	
+
+	if(!handle) {
+		return NULL;
+	}
+
+	try {
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+		// save the stream starting point
+		const long stream_start = io->tell_proc(handle);
+
+		// wrap the FreeImage IO stream
+		C_IStream istream(io, handle);
+
+		// open the file
+		Imf::InputFile file(istream);
+
+		// get file info			
+		const Imath::Box2i &dataWindow = file.header().dataWindow();
+		int width  = dataWindow.max.x - dataWindow.min.x + 1;
+		int height = dataWindow.max.y - dataWindow.min.y + 1;
+
+		//const Imf::Compression &compression = file.header().compression();
+
+		const Imf::ChannelList &channels = file.header().channels();
+
+		// check the number of components and check for a coherent format
+
+		std::string exr_color_model;
+		Imf::PixelType pixel_type = Imf::HALF;
+		FREE_IMAGE_TYPE image_type = FIT_UNKNOWN;
+		int components = 0;
+		bool bMixedComponents = false;
+
+		for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
+			components++;
+			if(components == 1) {
+				exr_color_model += i.name();
+				pixel_type = i.channel().type;
+			} else {
+				exr_color_model += "/";
+				exr_color_model += i.name();
+				if (i.channel().type != pixel_type) {
+					bMixedComponents = true;
+				}
+			}
+		}
+
+		if(bMixedComponents) {
+			bool bHandled = false;
+			// we may have a RGBZ or RGBAZ image ... 
+			if(components > 4) {
+				if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B") && channels.findChannel("A")) {
+					std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model";
+					FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+					bHandled = true;
+				}
+			}
+			else if(components > 3) {
+				if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) {
+					std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model";
+					FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+					bHandled = true;
+				}
+			}
+			if(!bHandled) {
+				THROW (Iex::InputExc, "Unable to handle mixed component types (color model = " << exr_color_model << ")");
+			} 
+		}
+
+		switch(pixel_type) {
+			case Imf::UINT:
+				THROW (Iex::InputExc, "Unsupported format: UINT");
+				break;
+			case Imf::HALF:
+			case Imf::FLOAT:
+			default:
+				break;
+		}
+
+		// check for supported image color models
+		// --------------------------------------------------------------
+
+		if((components == 1) || (components == 2)) {				
+			// if the image is gray-alpha (YA), ignore the alpha channel
+			if((components == 1) && channels.findChannel("Y")) {
+				image_type = FIT_FLOAT;
+				components = 1;
+			} else {
+				std::string msg = "Warning: loading color model " + exr_color_model + " as Y color model";
+				FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+				image_type = FIT_FLOAT;
+				// ignore the other channel
+				components = 1;
+			}
+		} else if(components == 3) {
+			if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) {
+				image_type = FIT_RGBF;
+			}
+			else if(channels.findChannel("BY") && channels.findChannel("RY") && channels.findChannel("Y")) {
+				image_type = FIT_RGBF;
+				bUseRgbaInterface = true;
+			}
+		} else if(components >= 4) {
+			if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) {
+				if(channels.findChannel("A")) {
+					if(components > 4) {
+						std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model";
+						FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+					}
+					image_type = FIT_RGBAF;
+					// ignore other layers if there is more than one alpha layer
+					components = 4;
+				} else {
+					std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model";
+					FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+
+					image_type = FIT_RGBF;
+					// ignore other channels
+					components = 3;					
+				}
+			}
+		}
+
+		if(image_type == FIT_UNKNOWN) {
+			THROW (Iex::InputExc, "Unsupported color model: " << exr_color_model);
+		}
+
+		// allocate a new dib
+		dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, 0);
+		if(!dib) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY);
+
+		// try to load the preview image
+		// --------------------------------------------------------------
+
+		if(file.header().hasPreviewImage()) {
+			const Imf::PreviewImage& preview = file.header().previewImage();
+			const unsigned thWidth = preview.width();
+			const unsigned thHeight = preview.height();
+			
+			FIBITMAP* thumbnail = FreeImage_Allocate(thWidth, thHeight, 32);
+			if(thumbnail) {
+				const Imf::PreviewRgba *src_line = preview.pixels();
+				BYTE *dst_line = FreeImage_GetScanLine(thumbnail, thHeight - 1);
+				const unsigned dstPitch = FreeImage_GetPitch(thumbnail);
+				
+				for (unsigned y = 0; y < thHeight; ++y) {
+					const Imf::PreviewRgba *src_pixel = src_line;
+					RGBQUAD* dst_pixel = (RGBQUAD*)dst_line;
+					
+					for(unsigned x = 0; x < thWidth; ++x) {
+						dst_pixel->rgbRed = src_pixel->r;
+						dst_pixel->rgbGreen = src_pixel->g;
+						dst_pixel->rgbBlue = src_pixel->b;
+						dst_pixel->rgbReserved = src_pixel->a;				
+						src_pixel++;
+						dst_pixel++;
+					}
+					src_line += thWidth;
+					dst_line -= dstPitch;
+				}
+				FreeImage_SetThumbnail(dib, thumbnail);
+				FreeImage_Unload(thumbnail);
+			}
+		}
+
+		if(header_only) {
+			// header only mode
+			return dib;
+		}
+
+		// load pixels
+		// --------------------------------------------------------------
+
+		const BYTE *bits = FreeImage_GetBits(dib);			// pointer to our pixel buffer
+		const size_t bytespp = sizeof(float) * components;	// size of our pixel in bytes
+		const unsigned pitch = FreeImage_GetPitch(dib);		// size of our yStride in bytes
+
+		Imf::PixelType pixelType = Imf::FLOAT;	// load as float data type;
+		
+		if(bUseRgbaInterface) {
+			// use the RGBA interface (used when loading RY BY Y images )
+
+			const int chunk_size = 16;
+
+			BYTE *scanline = (BYTE*)bits;
+
+			// re-open using the RGBA interface
+			io->seek_proc(handle, stream_start, SEEK_SET);
+			Imf::RgbaInputFile rgbaFile(istream);
+
+			// read the file in chunks
+			Imath::Box2i dw = dataWindow;
+			Imf::Array2D<Imf::Rgba> chunk(chunk_size, width);
+			while (dw.min.y <= dw.max.y) {
+				// read a chunk
+				rgbaFile.setFrameBuffer (&chunk[0][0] - dw.min.x - dw.min.y * width, 1, width);
+				rgbaFile.readPixels (dw.min.y, MIN(dw.min.y + chunk_size - 1, dw.max.y));
+				// fill the dib
+				const int y_max = ((dw.max.y - dw.min.y) <= chunk_size) ? (dw.max.y - dw.min.y) : chunk_size;
+				for(int y = 0; y < y_max; y++) {
+					FIRGBF *pixel = (FIRGBF*)scanline;
+					const Imf::Rgba *half_rgba = chunk[y];
+					for(int x = 0; x < width; x++) {
+						// convert from half to float
+						pixel[x].red = half_rgba[x].r;
+						pixel[x].green = half_rgba[x].g;
+						pixel[x].blue = half_rgba[x].b;
+					}
+					// next line
+					scanline += pitch;
+				}
+				// next chunk
+				dw.min.y += chunk_size;
+			}
+
+		} else {
+			// use the low level interface
+
+			// build a frame buffer (i.e. what we want on output)
+			Imf::FrameBuffer frameBuffer;
+
+			// allow dataWindow with minimal bounds different form zero
+			size_t offset = - dataWindow.min.x * bytespp - dataWindow.min.y * pitch;
+
+			if(components == 1) {
+				frameBuffer.insert ("Y",	// name
+					Imf::Slice (pixelType,	// type
+					(char*)(bits + offset), // base
+					bytespp,				// xStride
+					pitch,					// yStride
+					1, 1,					// x/y sampling
+					0.0));					// fillValue
+			} else if((components == 3) || (components == 4)) {
+				const char *channel_name[4] = { "R", "G", "B", "A" };
+
+				for(int c = 0; c < components; c++) {
+					frameBuffer.insert (
+						channel_name[c],					// name
+						Imf::Slice (pixelType,				// type
+						(char*)(bits + c * sizeof(float) + offset), // base
+						bytespp,							// xStride
+						pitch,								// yStride
+						1, 1,								// x/y sampling
+						0.0));								// fillValue
+				}
+			}
+
+			// read the file
+			file.setFrameBuffer(frameBuffer);
+			file.readPixels(dataWindow.min.y, dataWindow.max.y);
+		}
+
+		// lastly, flip dib lines
+		FreeImage_FlipVertical(dib);
+
+	}
+	catch(Iex::BaseExc & e) {
+		if(dib != NULL) {
+			FreeImage_Unload(dib);
+		}
+		FreeImage_OutputMessageProc(s_format_id, e.what());
+		return NULL;
+	}
+
+	return dib;
+}
+
+/**
+Set the preview image using the dib embedded thumbnail
+*/
+static BOOL
+SetPreviewImage(FIBITMAP *dib, Imf::Header& header) {
+	if(!FreeImage_GetThumbnail(dib)) {
+		return FALSE;
+	}
+	FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
+
+	if((FreeImage_GetImageType(thumbnail) != FIT_BITMAP) || (FreeImage_GetBPP(thumbnail) != 32)) {
+		// invalid thumbnail - ignore it
+		FreeImage_OutputMessageProc(s_format_id, FI_MSG_WARNING_INVALID_THUMBNAIL);
+	} else {
+		const unsigned thWidth = FreeImage_GetWidth(thumbnail);
+		const unsigned thHeight = FreeImage_GetHeight(thumbnail);
+		
+		Imf::PreviewImage preview(thWidth, thHeight);
+
+		// copy thumbnail to 32-bit RGBA preview image
+		
+		const BYTE* src_line = FreeImage_GetScanLine(thumbnail, thHeight - 1);
+		Imf::PreviewRgba* dst_line = preview.pixels();
+		const unsigned srcPitch = FreeImage_GetPitch(thumbnail);
+		
+		for (unsigned y = 0; y < thHeight; y++) {
+			const RGBQUAD* src_pixel = (RGBQUAD*)src_line;
+			Imf::PreviewRgba* dst_pixel = dst_line;
+			
+			for(unsigned x = 0; x < thWidth; x++) {
+				dst_pixel->r = src_pixel->rgbRed;
+				dst_pixel->g = src_pixel->rgbGreen;
+				dst_pixel->b = src_pixel->rgbBlue;
+				dst_pixel->a = src_pixel->rgbReserved;
+				
+				src_pixel++;
+				dst_pixel++;
+			}
+			
+			src_line -= srcPitch;
+			dst_line += thWidth;
+		}
+		
+		header.setPreviewImage(preview);
+	}
+
+	return TRUE;
+}
+
+/**
+Save using EXR_LC compression (works only with RGB[A]F images)
+*/
+static BOOL 
+SaveAsEXR_LC(C_OStream& ostream, FIBITMAP *dib, Imf::Header& header, int width, int height) {
+	int x, y;
+	Imf::RgbaChannels rgbaChannels;
+
+	try {
+
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+		// convert from float to half
+		Imf::Array2D<Imf::Rgba> pixels(height, width);
+		switch(image_type) {
+			case FIT_RGBF:
+				rgbaChannels = Imf::WRITE_YC;
+				for(y = 0; y < height; y++) {
+					FIRGBF *src_bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
+					for(x = 0; x < width; x++) {
+						Imf::Rgba &dst_bits = pixels[y][x];
+						dst_bits.r = src_bits[x].red;
+						dst_bits.g = src_bits[x].green;
+						dst_bits.b = src_bits[x].blue;
+					}
+				}
+				break;
+			case FIT_RGBAF:
+				rgbaChannels = Imf::WRITE_YCA;
+				for(y = 0; y < height; y++) {
+					FIRGBAF *src_bits = (FIRGBAF*)FreeImage_GetScanLine(dib, height - 1 - y);
+					for(x = 0; x < width; x++) {
+						Imf::Rgba &dst_bits = pixels[y][x];
+						dst_bits.r = src_bits[x].red;
+						dst_bits.g = src_bits[x].green;
+						dst_bits.b = src_bits[x].blue;
+						dst_bits.a = src_bits[x].alpha;
+					}
+				}
+				break;
+			default:
+				THROW (Iex::IoExc, "Bad image type");
+				break;
+		}
+
+		// write the data
+		Imf::RgbaOutputFile file(ostream, header, rgbaChannels);
+		file.setFrameBuffer (&pixels[0][0], 1, width);
+		file.writePixels (height);
+
+		return TRUE;
+
+	} catch(Iex::BaseExc & e) {
+		FreeImage_OutputMessageProc(s_format_id, e.what());
+
+		return FALSE;
+	}
+
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	const char *channel_name[4] = { "R", "G", "B", "A" };
+	BOOL bIsFlipped = FALSE;
+	half *halfData = NULL;
+
+	if(!dib || !handle) return FALSE;
+
+	try {
+		// check for EXR_LC compression and verify that the format is RGB
+		if((flags & EXR_LC) == EXR_LC) {
+			FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+			if(((image_type != FIT_RGBF) && (image_type != FIT_RGBAF)) || ((flags & EXR_FLOAT) == EXR_FLOAT)) {
+				THROW (Iex::IoExc, "EXR_LC compression is only available with RGB[A]F images");
+			}
+			if((FreeImage_GetWidth(dib) % 2) || (FreeImage_GetHeight(dib) % 2)) {
+				THROW (Iex::IoExc, "EXR_LC compression only works when the width and height are a multiple of 2");
+			}
+		}
+
+		// wrap the FreeImage IO stream
+		C_OStream ostream(io, handle);
+
+		// compression
+		Imf::Compression compress;
+		if((flags & EXR_NONE) == EXR_NONE) {
+			// no compression
+			compress = Imf::NO_COMPRESSION;
+		} else if((flags & EXR_ZIP) == EXR_ZIP) {
+			// zlib compression, in blocks of 16 scan lines
+			compress = Imf::ZIP_COMPRESSION;
+		} else if((flags & EXR_PIZ) == EXR_PIZ) {
+			// piz-based wavelet compression
+			compress = Imf::PIZ_COMPRESSION;
+		} else if((flags & EXR_PXR24) == EXR_PXR24) {
+			// lossy 24-bit float compression
+			compress = Imf::PXR24_COMPRESSION;
+		} else if((flags & EXR_B44) == EXR_B44) {
+			// lossy 44% float compression
+			compress = Imf::B44_COMPRESSION;
+		} else {
+			// default value
+			compress = Imf::PIZ_COMPRESSION;
+		}
+
+		// create the header
+		int width  = FreeImage_GetWidth(dib);
+		int height = FreeImage_GetHeight(dib);
+		int dx = 0, dy = 0;
+
+		Imath::Box2i dataWindow (Imath::V2i (0, 0), Imath::V2i (width - 1, height - 1));
+		Imath::Box2i displayWindow (Imath::V2i (-dx, -dy), Imath::V2i (width - dx - 1, height - dy - 1));
+
+		Imf::Header header = Imf::Header(displayWindow, dataWindow, 1, 
+			Imath::V2f(0,0), 1, 
+			Imf::INCREASING_Y, compress);        		
+
+		// handle thumbnail
+		SetPreviewImage(dib, header);
+		
+		// check for EXR_LC compression
+		if((flags & EXR_LC) == EXR_LC) {
+			return SaveAsEXR_LC(ostream, dib, header, width, height);
+		}
+
+		// output pixel type
+		Imf::PixelType pixelType;
+		if((flags & EXR_FLOAT) == EXR_FLOAT) {
+			pixelType = Imf::FLOAT;	// save as float data type
+		} else {
+			// default value
+			pixelType = Imf::HALF;	// save as half data type
+		}
+
+		// check the data type and number of channels
+		int components = 0;
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+		switch(image_type) {
+			case FIT_FLOAT:
+				components = 1;
+				// insert luminance channel
+				header.channels().insert ("Y", Imf::Channel(pixelType));
+				break;
+			case FIT_RGBF:
+				components = 3;
+				for(int c = 0; c < components; c++) {
+					// insert R, G and B channels
+					header.channels().insert (channel_name[c], Imf::Channel(pixelType));
+				}
+				break;
+			case FIT_RGBAF:
+				components = 4;
+				for(int c = 0; c < components; c++) {
+					// insert R, G, B and A channels
+					header.channels().insert (channel_name[c], Imf::Channel(pixelType));
+				}
+				break;
+			default:
+				THROW (Iex::ArgExc, "Cannot save: invalid data type.\nConvert the image to float before saving as OpenEXR.");
+		}
+
+		// build a frame buffer (i.e. what we have on input)
+		Imf::FrameBuffer frameBuffer;
+
+		BYTE *bits = NULL;	// pointer to our pixel buffer
+		size_t bytespp = 0;	// size of our pixel in bytes
+		size_t bytespc = 0;	// size of our pixel component in bytes
+		unsigned pitch = 0;	// size of our yStride in bytes
+
+
+		if(pixelType == Imf::HALF) {
+			// convert from float to half
+			halfData = new(std::nothrow) half[width * height * components];
+			if(!halfData) {
+				THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY);
+			}
+
+			for(int y = 0; y < height; y++) {
+				float *src_bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y);
+				half *dst_bits = halfData + y * width * components;
+				for(int x = 0; x < width; x++) {
+					for(int c = 0; c < components; c++) {
+						dst_bits[c] = src_bits[c];
+					}
+					src_bits += components;
+					dst_bits += components;
+				}
+			}
+			bits = (BYTE*)halfData;
+			bytespc = sizeof(half);
+			bytespp = sizeof(half) * components;
+			pitch = sizeof(half) * width * components;
+		} else if(pixelType == Imf::FLOAT) {
+			// invert dib scanlines
+			bIsFlipped = FreeImage_FlipVertical(dib);
+		
+			bits = FreeImage_GetBits(dib);
+			bytespc = sizeof(float);
+			bytespp = sizeof(float) * components;
+			pitch = FreeImage_GetPitch(dib);
+		}
+
+		if(image_type == FIT_FLOAT) {
+			frameBuffer.insert ("Y",	// name
+				Imf::Slice (pixelType,	// type
+				(char*)(bits),			// base
+				bytespp,				// xStride
+				pitch));				// yStride
+		} else if((image_type == FIT_RGBF) || (image_type == FIT_RGBAF)) {			
+			for(int c = 0; c < components; c++) {
+				char *channel_base = (char*)(bits) + c*bytespc;
+				frameBuffer.insert (channel_name[c],// name
+					Imf::Slice (pixelType,			// type
+					channel_base,					// base
+					bytespp,	// xStride
+					pitch));	// yStride
+			}
+		}
+
+		// write the data
+		Imf::OutputFile file (ostream, header);
+		file.setFrameBuffer (frameBuffer);
+		file.writePixels (height);
+
+		if(halfData != NULL) {
+			delete[] halfData;
+		}
+		if(bIsFlipped) {
+			// invert dib scanlines
+			FreeImage_FlipVertical(dib);
+		}
+
+		return TRUE;
+
+	} catch(Iex::BaseExc & e) {
+		if(halfData != NULL) {
+			delete[] halfData;
+		}
+		if(bIsFlipped) {
+			// invert dib scanlines
+			FreeImage_FlipVertical(dib);
+		}
+
+		FreeImage_OutputMessageProc(s_format_id, e.what());
+
+		return FALSE;
+	}	
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitEXR(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	// initialize the OpenEXR library
+	// note that this OpenEXR function produce so called "false memory leaks"
+	// see http://lists.nongnu.org/archive/html/openexr-devel/2013-11/msg00000.html
+	Imf::staticInitialize();
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginG3.cpp b/files/Source/FreeImage/PluginG3.cpp
new file mode 100644
index 0000000..b8f1fad
--- /dev/null
+++ b/files/Source/FreeImage/PluginG3.cpp
@@ -0,0 +1,432 @@
+// ==========================================================
+// G3 Fax Loader
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - 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!
+// ==========================================================
+
+#include "third_party/tiff/libtiff/tiffiop.h"
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+//   Constant/Macro declarations
+// ==========================================================
+
+#define G3_DEFAULT_WIDTH	1728
+
+#define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
+
+// ==========================================================
+//   libtiff interface 
+// ==========================================================
+
+static tmsize_t 
+_g3ReadProc(thandle_t handle, void *buf, tmsize_t size) {
+	// returns an error when reading the TIFF header
+	return 0;
+}
+
+static tmsize_t
+_g3WriteProc(thandle_t handle, void *buf, tmsize_t size) {
+	// returns ok when writing the TIFF header
+	return size;
+}
+
+static toff_t
+_g3SeekProc(thandle_t handle, toff_t off, int whence) {
+	return 0;
+}
+
+static int
+_g3CloseProc(thandle_t handle) {
+	return 0;
+}
+
+static toff_t
+_g3SizeProc(thandle_t handle) {
+	return 0;
+}
+
+static int
+_g3MapProc(thandle_t, void** base, toff_t* size) {
+	return 0;
+}
+
+static void
+_g3UnmapProc(thandle_t, void* base, toff_t size) {
+}
+
+// --------------------------------------------------------------
+
+static tmsize_t
+G3GetFileSize(FreeImageIO *io, fi_handle handle) {
+    long currentPos = io->tell_proc(handle);
+    io->seek_proc(handle, 0, SEEK_END);
+    long fileSize = io->tell_proc(handle);
+    io->seek_proc(handle, currentPos, SEEK_SET);
+    return fileSize;
+}
+
+static BOOL 
+G3ReadFile(FreeImageIO *io, fi_handle handle, uint8 *tif_rawdata, tmsize_t tif_rawdatasize) {
+	return ((tmsize_t)(io->read_proc(tif_rawdata, (unsigned)tif_rawdatasize, 1, handle) * tif_rawdatasize) == tif_rawdatasize);
+}
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static int 
+copyFaxFile(FreeImageIO *io, fi_handle handle, TIFF* tifin, uint32 xsize, int stretch, FIMEMORY *memory) {
+	BYTE *rowbuf = NULL;
+	BYTE *refbuf = NULL;
+	uint32 row;
+	uint16 badrun;
+	uint16	badfaxrun;
+	uint32	badfaxlines;
+	int ok;
+
+	try {
+
+		uint32 linesize = TIFFhowmany8(xsize);
+		rowbuf = (BYTE*) _TIFFmalloc(linesize);
+		refbuf = (BYTE*) _TIFFmalloc(linesize);
+		if (rowbuf == NULL || refbuf == NULL) {
+			throw FI_MSG_ERROR_MEMORY;
+		}
+
+		tifin->tif_rawdatasize = G3GetFileSize(io, handle);
+		tifin->tif_rawdata = (tidata_t) _TIFFmalloc(tifin->tif_rawdatasize);
+		if (tifin->tif_rawdata == NULL) {
+			throw FI_MSG_ERROR_MEMORY;
+		}
+			
+		if(!G3ReadFile(io, handle, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
+			throw "Read error at scanline 0";
+		}
+		tifin->tif_rawcp = tifin->tif_rawdata;
+		tifin->tif_rawcc = tifin->tif_rawdatasize;
+
+		(*tifin->tif_setupdecode)(tifin);
+		(*tifin->tif_predecode)(tifin, (uint16) 0);
+		tifin->tif_row = 0;
+		badfaxlines = 0;
+		badfaxrun = 0;
+
+		_TIFFmemset(refbuf, 0, linesize);
+		row = 0;
+		badrun = 0;		// current run of bad lines 
+		while (tifin->tif_rawcc > 0) {
+			ok = (*tifin->tif_decoderow)(tifin, rowbuf, linesize, 0);
+			if (!ok) {
+				badfaxlines++;
+				badrun++;
+				// regenerate line from previous good line 
+				_TIFFmemcpy(rowbuf, refbuf, linesize);
+			} else {
+				if (badrun > badfaxrun)
+					badfaxrun = badrun;
+				badrun = 0;
+				_TIFFmemcpy(refbuf, rowbuf, linesize);
+			}
+			tifin->tif_row++;
+
+			FreeImage_WriteMemory(rowbuf, linesize, 1, memory);
+			row++;
+			if (stretch) {
+				FreeImage_WriteMemory(rowbuf, linesize, 1, memory);
+				row++;
+			}
+		}
+		if (badrun > badfaxrun)
+			badfaxrun = badrun;
+
+		_TIFFfree(tifin->tif_rawdata);
+		tifin->tif_rawdata = NULL;
+
+		_TIFFfree(rowbuf);
+		_TIFFfree(refbuf);
+
+		/*
+		if (verbose) {
+			fprintf(stderr, "%d rows in input\n", rows);
+			fprintf(stderr, "%ld total bad rows\n", (long) badfaxlines);
+			fprintf(stderr, "%d max consecutive bad rows\n", badfaxrun);
+		}
+		*/
+
+	} catch(const char *message) {
+		if(rowbuf) _TIFFfree(rowbuf);
+		if(refbuf) _TIFFfree(refbuf);
+		if(tifin->tif_rawdata) {
+			_TIFFfree(tifin->tif_rawdata);
+			tifin->tif_rawdata = NULL;
+		}
+		FreeImage_OutputMessageProc(s_format_id, message);
+
+		return -1;
+	}
+
+	return (row);
+}
+
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "G3";
+}
+
+static const char * DLL_CALLCONV 
+Description() {
+	return "Raw fax format CCITT G.3";
+}
+
+static const char * DLL_CALLCONV 
+Extension() {
+	return "g3";
+}
+
+static const char * DLL_CALLCONV 
+RegExpr() {
+	return NULL; // there is now reasonable regexp for raw G3
+}
+
+static const char * DLL_CALLCONV 
+MimeType() {
+	return "image/fax-g3";
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportDepth(int depth) {
+	return	FALSE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	TIFF *faxTIFF = NULL;
+	FIBITMAP *dib = NULL;
+	FIMEMORY *memory = NULL;
+
+	//int verbose = 0;
+	int	stretch = 0;
+	int rows;
+	float resX = 204.0;
+	float resY = 196.0;
+
+	uint32 xsize = G3_DEFAULT_WIDTH;
+	int compression_in = COMPRESSION_CCITTFAX3;
+	int fillorder_in = FILLORDER_LSB2MSB;
+	uint32 group3options_in = 0;	// 1d-encoded 
+	uint32 group4options_in = 0;	// compressed 
+	int photometric_in = PHOTOMETRIC_MINISWHITE;
+
+	if(handle==NULL) return NULL;
+
+	try {
+		// set default load options
+
+		compression_in = COMPRESSION_CCITTFAX3;			// input is g3-encoded 
+		group3options_in &= ~GROUP3OPT_2DENCODING;		// input is 1d-encoded (g3 only) 
+		fillorder_in = FILLORDER_MSB2LSB;				// input has msb-to-lsb fillorder 
+
+		/*
+		Original input-related fax2tiff options
+
+		while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1) {
+			switch (c) {
+					// input-related options 
+				case '3':		// input is g3-encoded 
+					compression_in = COMPRESSION_CCITTFAX3;
+					break;
+				case '4':		// input is g4-encoded 
+					compression_in = COMPRESSION_CCITTFAX4;
+					break;
+				case 'U':		// input is uncompressed (g3 and g4) 
+					group3options_in |= GROUP3OPT_UNCOMPRESSED;
+					group4options_in |= GROUP4OPT_UNCOMPRESSED;
+					break;
+				case '1':		// input is 1d-encoded (g3 only) 
+					group3options_in &= ~GROUP3OPT_2DENCODING;
+					break;
+				case '2':		// input is 2d-encoded (g3 only) 
+					group3options_in |= GROUP3OPT_2DENCODING;
+					break;
+				case 'P':	// input has not-aligned EOL (g3 only) 
+					group3options_in &= ~GROUP3OPT_FILLBITS;
+					break;
+				case 'A':		// input has aligned EOL (g3 only) 
+					group3options_in |= GROUP3OPT_FILLBITS;
+					break;
+				case 'W':		// input has 0 mean white 
+					photometric_in = PHOTOMETRIC_MINISWHITE;
+					break;
+				case 'B':		// input has 0 mean black 
+					photometric_in = PHOTOMETRIC_MINISBLACK;
+					break;
+				case 'L':		// input has lsb-to-msb fillorder 
+					fillorder_in = FILLORDER_LSB2MSB;
+					break;
+				case 'M':		// input has msb-to-lsb fillorder 
+					fillorder_in = FILLORDER_MSB2LSB;
+					break;
+				case 'R':		// input resolution 
+					resY = (float) atof(optarg);
+					break;
+				case 'X':		// input width 
+					xsize = (uint32) atoi(optarg);
+					break;
+
+					// output-related options 
+				case 's':		// stretch image by dup'ng scanlines 
+					stretch = 1;
+					break;
+				case 'v':		// -v for info 
+					verbose++;
+					break;
+			}
+		}
+
+		*/
+
+		// open a temporary memory buffer to save decoded scanlines
+		memory = FreeImage_OpenMemory();
+		if(!memory) throw FI_MSG_ERROR_MEMORY;
+		
+		// wrap the raw fax file
+		faxTIFF = TIFFClientOpen("(FakeInput)", "w",
+			// TIFFClientOpen() fails if we don't set existing value here 
+			NULL,
+			_g3ReadProc, _g3WriteProc,
+			_g3SeekProc, _g3CloseProc,
+			_g3SizeProc, _g3MapProc,
+			_g3UnmapProc);
+
+		if (faxTIFF == NULL) {
+			throw "Can not create fake input file";
+		}
+		TIFFSetMode(faxTIFF, O_RDONLY);
+		TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize);
+		TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
+		TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1);
+		TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in);
+		TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+		TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in);
+		TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY);
+		TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+
+		// NB: this must be done after directory info is setup 
+		TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in);
+		if (compression_in == COMPRESSION_CCITTFAX3)
+			TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in);
+		else if (compression_in == COMPRESSION_CCITTFAX4)
+			TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in);
+		
+		resX = 204;
+		if (!stretch) {
+			TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY);
+		} else {
+			resY = 196;
+		}
+
+		// decode the raw fax data
+		rows = copyFaxFile(io, handle, faxTIFF, xsize, stretch, memory);
+		if(rows <= 0) throw "Error when decoding raw fax file : check the decoder options";
+
+
+		// allocate the output dib
+		dib = FreeImage_Allocate(xsize, rows, 1);
+		unsigned pitch = FreeImage_GetPitch(dib);
+		uint32 linesize = TIFFhowmany8(xsize);
+
+		// fill the bitmap structure ...
+		// ... palette
+		RGBQUAD *pal = FreeImage_GetPalette(dib);
+		if(photometric_in == PHOTOMETRIC_MINISWHITE) {
+			pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255;
+			pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 0;
+		} else {
+			pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+			pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+		}
+		// ... resolution
+		FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX/0.0254000 + 0.5));
+		FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY/0.0254000 + 0.5));
+
+		// read the decoded scanline and fill the bitmap data
+		FreeImage_SeekMemory(memory, 0, SEEK_SET);
+		BYTE *bits = FreeImage_GetScanLine(dib, rows - 1);
+		for(int k = 0; k < rows; k++) {
+			FreeImage_ReadMemory(bits, linesize, 1, memory);
+			bits -= pitch;
+		}
+
+		// free the TIFF wrapper
+		TIFFClose(faxTIFF);
+
+		// free the memory buffer
+		FreeImage_CloseMemory(memory);
+
+	} catch(const char *message) {
+		if(memory) FreeImage_CloseMemory(memory);
+		if(faxTIFF) TIFFClose(faxTIFF);
+		if(dib) FreeImage_Unload(dib);
+		FreeImage_OutputMessageProc(s_format_id, message);
+		return NULL;
+	}
+
+	return dib;
+
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitG3(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = NULL;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = NULL;
+	plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/files/Source/FreeImage/PluginGIF.cpp b/files/Source/FreeImage/PluginGIF.cpp
new file mode 100644
index 0000000..7ba0476
--- /dev/null
+++ b/files/Source/FreeImage/PluginGIF.cpp
@@ -0,0 +1,1414 @@
+// ==========================================================
+// GIF Loader and Writer
+//
+// Design and implementation by
+// - Ryan Rubley <ryan@lostreality.org>
+// - Raphaël Gaquer <raphael.gaquer@alcer.com>
+// - Aaron Shumate <aaron@shumate.us>
+//
+// 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 "../Metadata/FreeImageTag.h"
+
+// ==========================================================
+//   Metadata declarations
+// ==========================================================
+
+#define GIF_DISPOSAL_UNSPECIFIED	0
+#define GIF_DISPOSAL_LEAVE			1
+#define GIF_DISPOSAL_BACKGROUND		2
+#define GIF_DISPOSAL_PREVIOUS		3
+
+// ==========================================================
+//   Constant/Typedef declarations
+// ==========================================================
+
+
+struct GIFinfo {
+	BOOL read;
+	//only really used when reading
+	size_t global_color_table_offset;
+	int global_color_table_size;
+	BYTE background_color;
+	std::vector<size_t> application_extension_offsets;
+	std::vector<size_t> comment_extension_offsets;
+	std::vector<size_t> graphic_control_extension_offsets;
+	std::vector<size_t> image_descriptor_offsets;
+
+	GIFinfo() : read(0), global_color_table_offset(0), global_color_table_size(0), background_color(0)
+	{
+	}
+};
+
+struct PageInfo {
+	PageInfo(int d, int l, int t, int w, int h) { 
+		disposal_method = d; left = (WORD)l; top = (WORD)t; width = (WORD)w; height = (WORD)h; 
+	}
+	int disposal_method;
+	WORD left, top, width, height;
+};
+
+//GIF defines a max of 12 bits per code
+#define MAX_LZW_CODE			4096
+
+// This change is for building for SketchUp on Linux.  We don't want to use
+// StringTable because it conflicts with a StringTable elsewhere, so I typedef
+// To FreeImage_StringTable.
+#ifdef USE_THIRD_PARTY_TIFF
+#define StringTable FreeImage_StringTable
+#endif
+
+class StringTable
+{
+public:
+	StringTable();
+	~StringTable();
+	void Initialize(int minCodeSize);
+	BYTE *FillInputBuffer(int len);
+	void CompressStart(int bpp, int width);
+	int CompressEnd(BYTE *buf); //0-4 bytes
+	bool Compress(BYTE *buf, int *len);
+	bool Decompress(BYTE *buf, int *len);
+	void Done(void);
+
+protected:
+	bool m_done;
+
+	int m_minCodeSize, m_clearCode, m_endCode, m_nextCode;
+
+	int m_bpp, m_slack; //Compressor information
+
+	int m_prefix; //Compressor state variable
+	int m_codeSize, m_codeMask; //Compressor/Decompressor state variables
+	int m_oldCode; //Decompressor state variable
+	int m_partial, m_partialSize; //Compressor/Decompressor bit buffer
+
+	int firstPixelPassed; // A specific flag that indicates if the first pixel
+	                      // of the whole image had already been read
+
+	std::string m_strings[MAX_LZW_CODE]; //This is what is really the "string table" data for the Decompressor
+	int* m_strmap;
+
+	//input buffer
+	BYTE *m_buffer;
+	int m_bufferSize, m_bufferRealSize, m_bufferPos, m_bufferShift;
+
+	void ClearCompressorTable(void);
+	void ClearDecompressorTable(void);
+};
+
+#define GIF_PACKED_LSD_HAVEGCT		0x80
+#define GIF_PACKED_LSD_COLORRES		0x70
+#define GIF_PACKED_LSD_GCTSORTED	0x08
+#define GIF_PACKED_LSD_GCTSIZE		0x07
+#define GIF_PACKED_ID_HAVELCT		0x80
+#define GIF_PACKED_ID_INTERLACED	0x40
+#define GIF_PACKED_ID_LCTSORTED		0x20
+#define GIF_PACKED_ID_RESERVED		0x18
+#define GIF_PACKED_ID_LCTSIZE		0x07
+#define GIF_PACKED_GCE_RESERVED		0xE0
+#define GIF_PACKED_GCE_DISPOSAL		0x1C
+#define GIF_PACKED_GCE_WAITINPUT	0x02
+#define GIF_PACKED_GCE_HAVETRANS	0x01
+
+#define GIF_BLOCK_IMAGE_DESCRIPTOR	0x2C
+#define GIF_BLOCK_EXTENSION			0x21
+#define GIF_BLOCK_TRAILER			0x3B
+
+#define GIF_EXT_PLAINTEXT			0x01
+#define GIF_EXT_GRAPHIC_CONTROL		0xF9
+#define GIF_EXT_COMMENT				0xFE
+#define GIF_EXT_APPLICATION			0xFF
+
+#define GIF_INTERLACE_PASSES		4
+static int g_GifInterlaceOffset[GIF_INTERLACE_PASSES] = {0, 4, 2, 1};
+static int g_GifInterlaceIncrement[GIF_INTERLACE_PASSES] = {8, 8, 4, 2};
+
+// ==========================================================
+// Helpers Functions
+// ==========================================================
+
+static BOOL 
+FreeImage_SetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, WORD id, FREE_IMAGE_MDTYPE type, DWORD count, DWORD length, const void *value)
+{
+	BOOL bResult = FALSE;
+	FITAG *tag = FreeImage_CreateTag();
+	if(tag) {
+		FreeImage_SetTagKey(tag, key);
+		FreeImage_SetTagID(tag, id);
+		FreeImage_SetTagType(tag, type);
+		FreeImage_SetTagCount(tag, count);
+		FreeImage_SetTagLength(tag, length);
+		FreeImage_SetTagValue(tag, value);
+		if(model == FIMD_ANIMATION) {
+			TagLib& s = TagLib::instance();
+			// get the tag description
+			const char *description = s.getTagDescription(TagLib::ANIMATION, id);
+			FreeImage_SetTagDescription(tag, description);
+		}
+		// store the tag
+		bResult = FreeImage_SetMetadata(model, dib, key, tag);
+		FreeImage_DeleteTag(tag);
+	}
+	return bResult;
+}
+
+static BOOL 
+FreeImage_GetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FREE_IMAGE_MDTYPE type, FITAG **tag)
+{
+	if( FreeImage_GetMetadata(model, dib, key, tag) ) {
+		if( FreeImage_GetTagType(*tag) == type ) {
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+StringTable::StringTable()
+{
+	m_buffer = NULL;
+	firstPixelPassed = 0; // Still no pixel read
+	// Maximum number of entries in the map is MAX_LZW_CODE * 256 
+	// (aka 2**12 * 2**8 => a 20 bits key)
+	// This Map could be optmized to only handle MAX_LZW_CODE * 2**(m_bpp)
+	m_strmap = new(std::nothrow) int[1<<20];
+}
+
+StringTable::~StringTable()
+{
+	if( m_buffer != NULL ) {
+		delete [] m_buffer;
+	}
+	if( m_strmap != NULL ) {
+		delete [] m_strmap;
+		m_strmap = NULL;
+	}
+}
+
+void StringTable::Initialize(int minCodeSize)
+{
+	m_done = false;
+
+	m_bpp = 8;
+	m_minCodeSize = minCodeSize;
+	m_clearCode = 1 << m_minCodeSize;
+	if(m_clearCode > MAX_LZW_CODE) {
+		m_clearCode = MAX_LZW_CODE;
+	}
+	m_endCode = m_clearCode + 1;
+
+	m_partial = 0;
+	m_partialSize = 0;
+
+	m_bufferSize = 0;
+	ClearCompressorTable();
+	ClearDecompressorTable();
+}
+
+BYTE *StringTable::FillInputBuffer(int len)
+{
+	if( m_buffer == NULL ) {
+		m_buffer = new(std::nothrow) BYTE[len];
+		m_bufferRealSize = len;
+	} else if( len > m_bufferRealSize ) {
+		delete [] m_buffer;
+		m_buffer = new(std::nothrow) BYTE[len];
+		m_bufferRealSize = len;
+	}
+	m_bufferSize = len;
+	m_bufferPos = 0;
+	m_bufferShift = 8 - m_bpp;
+	return m_buffer;
+}
+
+void StringTable::CompressStart(int bpp, int width)
+{
+	m_bpp = bpp;
+	m_slack = (8 - ((width * bpp) % 8)) % 8;
+
+	m_partial |= m_clearCode << m_partialSize;
+	m_partialSize += m_codeSize;
+	ClearCompressorTable();
+}
+
+int StringTable::CompressEnd(BYTE *buf)
+{
+	int len = 0;
+
+	//output code for remaining prefix
+	m_partial |= m_prefix << m_partialSize;
+	m_partialSize += m_codeSize;
+	while( m_partialSize >= 8 ) {
+		*buf++ = (BYTE)m_partial;
+		m_partial >>= 8;
+		m_partialSize -= 8;
+		len++;
+	}
+
+	//add the end of information code and flush the entire buffer out
+	m_partial |= m_endCode << m_partialSize;
+	m_partialSize += m_codeSize;
+	while( m_partialSize > 0 ) {
+		*buf++ = (BYTE)m_partial;
+		m_partial >>= 8;
+		m_partialSize -= 8;
+		len++;
+	}
+
+	//most this can be is 4 bytes.  7 bits in m_partial to start + 12 for the
+	//last code + 12 for the end code = 31 bits total.
+	return len;
+}
+
+bool StringTable::Compress(BYTE *buf, int *len)
+{
+	if( m_bufferSize == 0 || m_done ) {
+		return false;
+	}
+
+	int mask = (1 << m_bpp) - 1;
+	BYTE *bufpos = buf;
+	while( m_bufferPos < m_bufferSize ) {
+		//get the current pixel value
+		char ch = (char)((m_buffer[m_bufferPos] >> m_bufferShift) & mask);
+
+		// The next prefix is : 
+		// <the previous LZW code (on 12 bits << 8)> | <the code of the current pixel (on 8 bits)>
+		int nextprefix = (((m_prefix)<<8)&0xFFF00) + (ch & 0x000FF);
+		if(firstPixelPassed) {
+			
+			if( m_strmap[nextprefix] > 0) {
+				m_prefix = m_strmap[nextprefix];
+			} else {
+				m_partial |= m_prefix << m_partialSize;
+				m_partialSize += m_codeSize;
+				//grab full bytes for the output buffer
+				while( m_partialSize >= 8 && bufpos - buf < *len ) {
+					*bufpos++ = (BYTE)m_partial;
+					m_partial >>= 8;
+					m_partialSize -= 8;
+				}
+
+				//add the code to the "table map"
+				m_strmap[nextprefix] = m_nextCode;
+
+				//increment the next highest valid code, increase the code size
+				if( m_nextCode == (1 << m_codeSize) ) {
+					m_codeSize++;
+				}
+				m_nextCode++;
+
+				//if we're out of codes, restart the string table
+				if( m_nextCode == MAX_LZW_CODE ) {
+					m_partial |= m_clearCode << m_partialSize;
+					m_partialSize += m_codeSize;
+					ClearCompressorTable();
+				}
+
+				// Only keep the 8 lowest bits (prevent problems with "negative chars")
+				m_prefix = ch & 0x000FF;
+			}
+
+			//increment to the next pixel
+			if( m_bufferShift > 0 && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack) ) {
+				m_bufferShift -= m_bpp;
+			} else {
+				m_bufferPos++;
+				m_bufferShift = 8 - m_bpp;
+			}
+
+			//jump out here if the output buffer is full
+			if( bufpos - buf == *len ) {
+				return true;
+			}
+		
+		} else {
+			// Specific behavior for the first pixel of the whole image
+
+			firstPixelPassed=1;
+			// Only keep the 8 lowest bits (prevent problems with "negative chars")
+			m_prefix = ch & 0x000FF;
+
+			//increment to the next pixel
+			if( m_bufferShift > 0 && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack) ) {
+				m_bufferShift -= m_bpp;
+			} else {
+				m_bufferPos++;
+				m_bufferShift = 8 - m_bpp;
+			}
+
+			//jump out here if the output buffer is full
+			if( bufpos - buf == *len ) {
+				return true;
+			}
+		}
+	}
+
+	m_bufferSize = 0;
+	*len = (int)(bufpos - buf);
+
+	return true;
+}
+
+bool StringTable::Decompress(BYTE *buf, int *len)
+{
+	if( m_bufferSize == 0 || m_done ) {
+		return false;
+	}
+
+	BYTE *bufpos = buf;
+	for( ; m_bufferPos < m_bufferSize; m_bufferPos++ ) {
+		m_partial |= (int)m_buffer[m_bufferPos] << m_partialSize;
+		m_partialSize += 8;
+		while( m_partialSize >= m_codeSize ) {
+			int code = m_partial & m_codeMask;
+			m_partial >>= m_codeSize;
+			m_partialSize -= m_codeSize;
+
+			if( code > m_nextCode || /*(m_nextCode == MAX_LZW_CODE && code != m_clearCode) || */code == m_endCode ) {
+				m_done = true;
+				*len = (int)(bufpos - buf);
+				return true;
+			}
+			if( code == m_clearCode ) {
+				ClearDecompressorTable();
+				continue;
+			}
+
+			//add new string to string table, if not the first pass since a clear code
+			if( m_oldCode != MAX_LZW_CODE && m_nextCode < MAX_LZW_CODE) {
+				m_strings[m_nextCode] = m_strings[m_oldCode] + m_strings[code == m_nextCode ? m_oldCode : code][0];
+			}
+
+			if( (int)m_strings[code].size() > *len - (bufpos - buf) ) {
+				//out of space, stuff the code back in for next time
+				m_partial <<= m_codeSize;
+				m_partialSize += m_codeSize;
+				m_partial |= code;
+				m_bufferPos++;
+				*len = (int)(bufpos - buf);
+				return true;
+			}
+
+			//output the string into the buffer
+			memcpy(bufpos, m_strings[code].data(), m_strings[code].size());
+			bufpos += m_strings[code].size();
+
+			//increment the next highest valid code, add a bit to the mask if we need to increase the code size
+			if( m_oldCode != MAX_LZW_CODE && m_nextCode < MAX_LZW_CODE ) {
+				if( ++m_nextCode < MAX_LZW_CODE ) {
+					if( (m_nextCode & m_codeMask) == 0 ) {
+						m_codeSize++;
+						m_codeMask |= m_nextCode;
+					}
+				}
+			}
+
+			m_oldCode = code;
+		}
+	}
+
+	m_bufferSize = 0;
+	*len = (int)(bufpos - buf);
+
+	return true;
+}
+
+void StringTable::Done(void)
+{
+	m_done = true;
+}
+
+void StringTable::ClearCompressorTable(void)
+{
+	if(m_strmap) {
+		memset(m_strmap, 0xFF, sizeof(unsigned int)*(1<<20));
+	}
+	m_nextCode = m_endCode + 1;
+
+	m_prefix = 0;
+	m_codeSize = m_minCodeSize + 1;
+}
+
+void StringTable::ClearDecompressorTable(void)
+{
+	for( int i = 0; i < m_clearCode; i++ ) {
+		m_strings[i].resize(1);
+		m_strings[i][0] = (char)i;
+	}
+	m_nextCode = m_endCode + 1;
+
+	m_codeSize = m_minCodeSize + 1;
+	m_codeMask = (1 << m_codeSize) - 1;
+	m_oldCode = MAX_LZW_CODE;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV 
+Format() {
+	return "GIF";
+}
+
+static const char * DLL_CALLCONV 
+Description() {
+	return "Graphics Interchange Format";
+}
+
+static const char * DLL_CALLCONV 
+Extension() {
+	return "gif";
+}
+
+static const char * DLL_CALLCONV 
+RegExpr() {
+	return "^GIF";
+}
+
+static const char * DLL_CALLCONV 
+MimeType() {
+	return "image/gif";
+}
+
+static BOOL DLL_CALLCONV 
+Validate(FreeImageIO *io, fi_handle handle) {
+	char buf[6];
+	if( io->read_proc(buf, 6, 1, handle) < 1 ) {
+		return FALSE;
+	}
+
+	BOOL bResult = FALSE;
+	if( !strncmp(buf, "GIF", 3) ) {
+		if( buf[3] >= '0' && buf[3] <= '9' && buf[4] >= '0' && buf[4] <= '9' && buf[5] >= 'a' && buf[5] <= 'z' ) {
+			bResult = TRUE;
+		}
+	}
+
+	io->seek_proc(handle, -6, SEEK_CUR);
+
+	return bResult;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportDepth(int depth) {
+	return	(depth == 1) ||
+			(depth == 4) ||
+			(depth == 8);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+// ----------------------------------------------------------
+
+static void *DLL_CALLCONV 
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	GIFinfo *info = new(std::nothrow) GIFinfo;
+	if( info == NULL ) {
+		return NULL;
+	}
+
+	// 25/02/2008 MDA:	Not safe to memset GIFinfo structure with VS 2008 (safe iterators),
+	//					perform initialization in constructor instead.
+	// memset(info, 0, sizeof(GIFinfo));
+
+	info->read = read;
+	if( read ) {
+		try {
+			//Header
+			if( !Validate(io, handle) ) {
+				throw FI_MSG_ERROR_MAGIC_NUMBER;
+			}
+			io->seek_proc(handle, 6, SEEK_CUR);
+
+			//Logical Screen Descriptor
+			io->seek_proc(handle, 4, SEEK_CUR);
+			BYTE packed;
+			if( io->read_proc(&packed, 1, 1, handle) < 1 ) {
+				throw "EOF reading Logical Screen Descriptor";
+			}
+			if( io->read_proc(&info->background_color, 1, 1, handle) < 1 ) {
+				throw "EOF reading Logical Screen Descriptor";
+			}
+			io->seek_proc(handle, 1, SEEK_CUR);
+
+			//Global Color Table
+			if( packed & GIF_PACKED_LSD_HAVEGCT ) {
+				info->global_color_table_offset = io->tell_proc(handle);
+				info->global_color_table_size = 2 << (packed & GIF_PACKED_LSD_GCTSIZE);
+				io->seek_proc(handle, 3 * info->global_color_table_size, SEEK_CUR);
+			}
+
+			//Scan through all the rest of the blocks, saving offsets
+			size_t gce_offset = 0;
+			BYTE block = 0;
+			while( block != GIF_BLOCK_TRAILER ) {
+				if( io->read_proc(&block, 1, 1, handle) < 1 ) {
+					throw "EOF reading blocks";
+				}
+				if( block == GIF_BLOCK_IMAGE_DESCRIPTOR ) {
+					info->image_descriptor_offsets.push_back(io->tell_proc(handle));
+					//GCE may be 0, meaning no GCE preceded this ID
+					info->graphic_control_extension_offsets.push_back(gce_offset);
+					gce_offset = 0;
+
+					io->seek_proc(handle, 8, SEEK_CUR);
+					if( io->read_proc(&packed, 1, 1, handle) < 1 ) {
+						throw "EOF reading Image Descriptor";
+					}
+
+					//Local Color Table
+					if( packed & GIF_PACKED_ID_HAVELCT ) {
+						io->seek_proc(handle, 3 * (2 << (packed & GIF_PACKED_ID_LCTSIZE)), SEEK_CUR);
+					}
+
+					//LZW Minimum Code Size
+					io->seek_proc(handle, 1, SEEK_CUR);
+				} else if( block == GIF_BLOCK_EXTENSION ) {
+					BYTE ext;
+					if( io->read_proc(&ext, 1, 1, handle) < 1 ) {
+						throw "EOF reading extension";
+					}
+
+					if( ext == GIF_EXT_GRAPHIC_CONTROL ) {
+						//overwrite previous offset if more than one GCE found before an ID
+						gce_offset = io->tell_proc(handle);
+					} else if( ext == GIF_EXT_COMMENT ) {
+						info->comment_extension_offsets.push_back(io->tell_proc(handle));
+					} else if( ext == GIF_EXT_APPLICATION ) {
+						info->application_extension_offsets.push_back(io->tell_proc(handle));
+					}
+				} else if( block == GIF_BLOCK_TRAILER ) {
+					continue;
+				} else {
+					throw "Invalid GIF block found";
+				}
+
+				//Data Sub-blocks
+				BYTE len;
+				if( io->read_proc(&len, 1, 1, handle) < 1 ) {
+					throw "EOF reading sub-block";
+				}
+				while( len != 0 ) {
+					io->seek_proc(handle, len, SEEK_CUR);
+					if( io->read_proc(&len, 1, 1, handle) < 1 ) {
+						throw "EOF reading sub-block";
+					}
+				}
+			}
+		} catch (const char *msg) {
+			FreeImage_OutputMessageProc(s_format_id, msg);
+			delete info;
+			return NULL;
+		}
+	} else {
+		//Header
+		io->write_proc((void *)"GIF89a", 6, 1, handle);
+	}
+
+	return info;
+}
+
+static void DLL_CALLCONV 
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+	if( data == NULL ) {
+		return;
+	}
+	GIFinfo *info = (GIFinfo *)data;
+
+	if( !info->read ) {
+		//Trailer
+		BYTE b = GIF_BLOCK_TRAILER;
+		io->write_proc(&b, 1, 1, handle);
+	}
+
+	delete info;
+}
+
+static int DLL_CALLCONV
+PageCount(FreeImageIO *io, fi_handle handle, void *data) {
+	if( data == NULL ) {
+		return 0;
+	}
+	GIFinfo *info = (GIFinfo *)data;
+
+	return (int) info->image_descriptor_offsets.size();
+}
+
+static FIBITMAP * DLL_CALLCONV 
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	if( data == NULL ) {
+		return NULL;
+	}
+	GIFinfo *info = (GIFinfo *)data;
+
+	if( page == -1 ) {
+		page = 0;
+	}
+	if( page < 0 || page >= (int)info->image_descriptor_offsets.size() ) {
+		return NULL;
+	}
+
+	FIBITMAP *dib = NULL;
+	try {
+		bool have_transparent = false, no_local_palette = false, interlaced = false;
+		int disposal_method = GIF_DISPOSAL_LEAVE, delay_time = 0, transparent_color = 0;
+		WORD left, top, width, height;
+		BYTE packed, b;
+		WORD w;
+
+		//playback pages to generate what the user would see for this frame
+		if( (flags & GIF_PLAYBACK) == GIF_PLAYBACK ) {
+			//Logical Screen Descriptor
+			io->seek_proc(handle, 6, SEEK_SET);
+			WORD logicalwidth, logicalheight;
+			io->read_proc(&logicalwidth, 2, 1, handle);
+			io->read_proc(&logicalheight, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+			SwapShort(&logicalwidth);
+			SwapShort(&logicalheight);
+#endif
+			//set the background color with 0 alpha
+			RGBQUAD background;
+			if( info->global_color_table_offset != 0 && info->background_color < info->global_color_table_size ) {
+				io->seek_proc(handle, (long)(info->global_color_table_offset + (info->background_color * 3)), SEEK_SET);
+				io->read_proc(&background.rgbRed, 1, 1, handle);
+				io->read_proc(&background.rgbGreen, 1, 1, handle);
+				io->read_proc(&background.rgbBlue, 1, 1, handle);
+			} else {
+				background.rgbRed = 0;
+				background.rgbGreen = 0;
+				background.rgbBlue = 0;
+			}
+			background.rgbReserved = 0;
+
+			//allocate entire logical area
+			dib = FreeImage_Allocate(logicalwidth, logicalheight, 32);
+			if( dib == NULL ) {
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+
+			//fill with background color to start
+			int x, y;
+			RGBQUAD *scanline;
+			for( y = 0; y < logicalheight; y++ ) {
+				scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, y);
+				for( x = 0; x < logicalwidth; x++ ) {
+					*scanline++ = background;
+				}
+			}
+
+			//cache some info about each of the pages so we can avoid decoding as many of them as possible
+			std::vector<PageInfo> pageinfo;
+			int start = page, end = page;
+			while( start >= 0 ) {
+				//Graphic Control Extension
+				io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[start] + 1), SEEK_SET);
+				io->read_proc(&packed, 1, 1, handle);
+				have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
+				disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
+				//Image Descriptor
+				io->seek_proc(handle, (long)(info->image_descriptor_offsets[start]), SEEK_SET);
+				io->read_proc(&left, 2, 1, handle);
+				io->read_proc(&top, 2, 1, handle);
+				io->read_proc(&width, 2, 1, handle);
+				io->read_proc(&height, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+				SwapShort(&left);
+				SwapShort(&top);
+				SwapShort(&width);
+				SwapShort(&height);
+#endif
+
+				pageinfo.push_back(PageInfo(disposal_method, left, top, width, height));
+
+				if( start != end ) {
+					if( left == 0 && top == 0 && width == logicalwidth && height == logicalheight ) {
+						if( disposal_method == GIF_DISPOSAL_BACKGROUND ) {
+							pageinfo.pop_back();
+							start++;
+							break;
+						} else if( disposal_method != GIF_DISPOSAL_PREVIOUS ) {
+							if( !have_transparent ) {
+								break;
+							}
+						}
+					}
+				}
+				start--;
+			}
+			if( start < 0 ) {
+				start = 0;
+			}
+
+			//draw each page into the logical area
+			delay_time = 0;
+			for( page = start; page <= end; page++ ) {
+				PageInfo &info = pageinfo[end - page];
+				//things we can skip having to decode
+				if( page != end ) {
+					if( info.disposal_method == GIF_DISPOSAL_PREVIOUS ) {
+						continue;
+					}
+					if( info.disposal_method == GIF_DISPOSAL_BACKGROUND ) {
+						for( y = 0; y < info.height; y++ ) {
+							const int scanidx = logicalheight - (y + info.top) - 1;
+							if ( scanidx < 0 ) {
+								break;  // If data is corrupt, don't calculate in invalid scanline
+							}
+							scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + info.left;
+							for( x = 0; x < info.width; x++ ) {
+								*scanline++ = background;
+							}
+						}
+						continue;
+					}
+				}
+
+				//decode page
+				FIBITMAP *pagedib = Load(io, handle, page, GIF_LOAD256, data);
+				if( pagedib != NULL ) {
+					RGBQUAD *pal = FreeImage_GetPalette(pagedib);
+					have_transparent = false;
+					if( FreeImage_IsTransparent(pagedib) ) {
+						int count = FreeImage_GetTransparencyCount(pagedib);
+						BYTE *table = FreeImage_GetTransparencyTable(pagedib);
+						for( int i = 0; i < count; i++ ) {
+							if( table[i] == 0 ) {
+								have_transparent = true;
+								transparent_color = i;
+								break;
+							}
+						}
+					}
+					//copy page data into logical buffer, with full alpha opaqueness
+					for( y = 0; y < info.height; y++ ) {
+						const int scanidx = logicalheight - (y + info.top) - 1;
+						if ( scanidx < 0 ) {
+							break;  // If data is corrupt, don't calculate in invalid scanline
+						}
+						scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + info.left;
+						BYTE *pageline = FreeImage_GetScanLine(pagedib, info.height - y - 1);
+						for( x = 0; x < info.width; x++ ) {
+							if( !have_transparent || *pageline != transparent_color ) {
+								*scanline = pal[*pageline];
+								scanline->rgbReserved = 255;
+							}
+							scanline++;
+							pageline++;
+						}
+					}
+					//copy frame time
+					if( page == end ) {
+						FITAG *tag;
+						if( FreeImage_GetMetadataEx(FIMD_ANIMATION, pagedib, "FrameTime", FIDT_LONG, &tag) ) {
+							delay_time = *(LONG *)FreeImage_GetTagValue(tag);
+						}
+					}
+					FreeImage_Unload(pagedib);
+				}
+			}
+
+			//setup frame time
+			FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
+			return dib;
+		}
+
+		//get the actual frame image data for a single frame
+
+		//Image Descriptor
+		io->seek_proc(handle, (long)info->image_descriptor_offsets[page], SEEK_SET);
+		io->read_proc(&left, 2, 1, handle);
+		io->read_proc(&top, 2, 1, handle);
+		io->read_proc(&width, 2, 1, handle);
+		io->read_proc(&height, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapShort(&left);
+		SwapShort(&top);
+		SwapShort(&width);
+		SwapShort(&height);
+#endif
+		io->read_proc(&packed, 1, 1, handle);
+		interlaced = (packed & GIF_PACKED_ID_INTERLACED) ? true : false;
+		no_local_palette = (packed & GIF_PACKED_ID_HAVELCT) ? false : true;
+
+		int bpp = 8;
+		if( (flags & GIF_LOAD256) == 0 ) {
+			if( !no_local_palette ) {
+				int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);
+				if( size <= 2 ) bpp = 1;
+				else if( size <= 16 ) bpp = 4;
+			} else if( info->global_color_table_offset != 0 ) {
+				if( info->global_color_table_size <= 2 ) bpp = 1;
+				else if( info->global_color_table_size <= 16 ) bpp = 4;
+			}
+		}
+		dib = FreeImage_Allocate(width, height, bpp);
+		if( dib == NULL ) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", ANIMTAG_FRAMELEFT, FIDT_SHORT, 1, 2, &left);
+		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", ANIMTAG_FRAMETOP, FIDT_SHORT, 1, 2, &top);
+		b = no_local_palette ? 1 : 0;
+		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", ANIMTAG_NOLOCALPALETTE, FIDT_BYTE, 1, 1, &b);
+		b = interlaced ? 1 : 0;
+		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", ANIMTAG_INTERLACED, FIDT_BYTE, 1, 1, &b);
+
+		//Palette
+		RGBQUAD *pal = FreeImage_GetPalette(dib);
+		if( !no_local_palette ) {
+			int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);
+
+			int i = 0;
+			while( i < size ) {
+				io->read_proc(&pal[i].rgbRed, 1, 1, handle);
+				io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
+				io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
+				i++;
+			}
+		} else if( info->global_color_table_offset != 0 ) {
+			long pos = io->tell_proc(handle);
+			io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET);
+
+			int i = 0;
+			while( i < info->global_color_table_size ) {
+				io->read_proc(&pal[i].rgbRed, 1, 1, handle);
+				io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
+				io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
+				i++;
+			}
+
+			io->seek_proc(handle, pos, SEEK_SET);
+		} else {
+			//its legal to have no palette, but we're going to generate *something*
+			for( int i = 0; i < 256; i++ ) {
+				pal[i].rgbRed   = (BYTE)i;
+				pal[i].rgbGreen = (BYTE)i;
+				pal[i].rgbBlue  = (BYTE)i;
+			}
+		}
+
+		//LZW Minimum Code Size
+		io->read_proc(&b, 1, 1, handle);
+		StringTable *stringtable = new(std::nothrow) StringTable;
+		stringtable->Initialize(b);
+
+		//Image Data Sub-blocks
+		int x = 0, xpos = 0, y = 0, shift = 8 - bpp, mask = (1 << bpp) - 1, interlacepass = 0;
+		BYTE *scanline = FreeImage_GetScanLine(dib, height - 1);
+		BYTE buf[4096];
+		io->read_proc(&b, 1, 1, handle);
+		while( b ) {
+			io->read_proc(stringtable->FillInputBuffer(b), b, 1, handle);
+			int size = sizeof(buf);
+			while( stringtable->Decompress(buf, &size) ) {
+				for( int i = 0; i < size; i++ ) {
+					scanline[xpos] |= (buf[i] & mask) << shift;
+					if( shift > 0 ) {
+						shift -= bpp;
+					} else {
+						xpos++;
+						shift = 8 - bpp;
+					}
+					if( ++x >= width ) {
+						if( interlaced ) {
+							y += g_GifInterlaceIncrement[interlacepass];
+							if( y >= height && ++interlacepass < GIF_INTERLACE_PASSES ) {
+								y = g_GifInterlaceOffset[interlacepass];
+							} 						
+						} else {
+							y++;
+						}
+						if( y >= height ) {
+							stringtable->Done();
+							break;
+						}
+						x = xpos = 0;
+						shift = 8 - bpp;
+						scanline = FreeImage_GetScanLine(dib, height - y - 1);
+					}
+				}
+				size = sizeof(buf);
+			}
+			io->read_proc(&b, 1, 1, handle);
+		}
+
+		if( page == 0 ) {
+			size_t idx;
+
+			//Logical Screen Descriptor
+			io->seek_proc(handle, 6, SEEK_SET);
+			WORD logicalwidth, logicalheight;
+			io->read_proc(&logicalwidth, 2, 1, handle);
+			io->read_proc(&logicalheight, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+			SwapShort(&logicalwidth);
+			SwapShort(&logicalheight);
+#endif
+			FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", ANIMTAG_LOGICALWIDTH, FIDT_SHORT, 1, 2, &logicalwidth);
+			FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", ANIMTAG_LOGICALHEIGHT, FIDT_SHORT, 1, 2, &logicalheight);
+
+			//Global Color Table
+			if( info->global_color_table_offset != 0 ) {
+				RGBQUAD globalpalette[256];
+				io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET);
+				int i = 0;
+				while( i < info->global_color_table_size ) {
+					io->read_proc(&globalpalette[i].rgbRed, 1, 1, handle);
+					io->read_proc(&globalpalette[i].rgbGreen, 1, 1, handle);
+					io->read_proc(&globalpalette[i].rgbBlue, 1, 1, handle);
+					globalpalette[i].rgbReserved = 0;
+					i++;
+				}
+				FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "GlobalPalette", ANIMTAG_GLOBALPALETTE, FIDT_PALETTE, info->global_color_table_size, info->global_color_table_size * 4, globalpalette);
+				//background color
+				if( info->background_color < info->global_color_table_size ) {
+					FreeImage_SetBackgroundColor(dib, &globalpalette[info->background_color]);
+				}
+			}
+
+			//Application Extension
+			LONG loop = 1; //If no AE with a loop count is found, the default must be 1
+			for( idx = 0; idx < info->application_extension_offsets.size(); idx++ ) {
+				io->seek_proc(handle, (long)info->application_extension_offsets[idx], SEEK_SET);
+				io->read_proc(&b, 1, 1, handle);
+				if( b == 11 ) { //All AEs start with an 11 byte sub-block to determine what type of AE it is
+					char buf[11];
+					io->read_proc(buf, 11, 1, handle);
+					if( !memcmp(buf, "NETSCAPE2.0", 11) || !memcmp(buf, "ANIMEXTS1.0", 11) ) { //Not everybody recognizes ANIMEXTS1.0 but it is valid
+						io->read_proc(&b, 1, 1, handle);
+						if( b == 3 ) { //we're supposed to have a 3 byte sub-block now
+							io->read_proc(&b, 1, 1, handle); //this should be 0x01 but isn't really important
+							io->read_proc(&w, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+							SwapShort(&w);
+#endif
+							loop = w;
+							if( loop > 0 ) loop++;
+							break;
+						}
+					}
+				}
+			}
+			FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Loop", ANIMTAG_LOOP, FIDT_LONG, 1, 4, &loop);
+
+			//Comment Extension
+			for( idx = 0; idx < info->comment_extension_offsets.size(); idx++ ) {
+				io->seek_proc(handle, (long)info->comment_extension_offsets[idx], SEEK_SET);
+				std::string comment;
+				char buf[255];
+				io->read_proc(&b, 1, 1, handle);
+				while( b ) {
+					io->read_proc(buf, b, 1, handle);
+					comment.append(buf, b);
+					io->read_proc(&b, 1, 1, handle);
+				}
+				comment.append(1, '\0');
+				sprintf(buf, "Comment%d", idx);
+				DWORD comment_size = (DWORD)comment.size();
+				FreeImage_SetMetadataEx(FIMD_COMMENTS, dib, buf, 1, FIDT_ASCII, comment_size, comment_size, comment.c_str());
+			}
+		}
+
+		//Graphic Control Extension
+		if( info->graphic_control_extension_offsets[page] != 0 ) {
+			io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[page] + 1), SEEK_SET);
+			io->read_proc(&packed, 1, 1, handle);
+			io->read_proc(&w, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+			SwapShort(&w);
+#endif
+			io->read_proc(&b, 1, 1, handle);
+			have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
+			disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
+			delay_time = w * 10; //convert cs to ms
+			transparent_color = b;
+			if( have_transparent ) {
+				int size = 1 << bpp;
+				if( transparent_color <= size ) {
+					BYTE table[256];
+					memset(table, 0xFF, size);
+					table[transparent_color] = 0;
+					FreeImage_SetTransparencyTable(dib, table, size);
+				}
+			}
+		}
+		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
+		b = (BYTE)disposal_method;
+		FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", ANIMTAG_DISPOSALMETHOD, FIDT_BYTE, 1, 1, &b);
+
+		delete stringtable;
+
+	} catch (const char *msg) {
+		if( dib != NULL ) {
+			FreeImage_Unload(dib);
+		}
+		FreeImage_OutputMessageProc(s_format_id, msg);
+		return NULL;
+	}
+
+	return dib;
+}
+
+static BOOL DLL_CALLCONV 
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	if( data == NULL ) {
+		return FALSE;
+	}
+	//GIFinfo *info = (GIFinfo *)data;
+
+	if( page == -1 ) {
+		page = 0;
+	}
+
+	try {
+		BYTE packed, b;
+		WORD w;
+		FITAG *tag;
+
+		int bpp = FreeImage_GetBPP(dib);
+		if( bpp != 1 && bpp != 4 && bpp != 8 ) {
+			throw "Only 1, 4, or 8 bpp images supported";
+		}
+
+		bool have_transparent = false, no_local_palette = false, interlaced = false;
+		int disposal_method = GIF_DISPOSAL_BACKGROUND, delay_time = 100, transparent_color = 0;
+		WORD left = 0, top = 0, width = (WORD)FreeImage_GetWidth(dib), height = (WORD)FreeImage_GetHeight(dib);
+		WORD output_height = height;
+		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", FIDT_SHORT, &tag) ) {
+			left = *(WORD *)FreeImage_GetTagValue(tag);
+		}
+		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", FIDT_SHORT, &tag) ) {
+			top = *(WORD *)FreeImage_GetTagValue(tag);
+		}
+		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", FIDT_BYTE, &tag) ) {
+			no_local_palette = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
+		}
+		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", FIDT_BYTE, &tag) ) {
+			interlaced = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
+		}
+		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", FIDT_LONG, &tag) ) {
+			delay_time = *(LONG *)FreeImage_GetTagValue(tag);
+		}
+		if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", FIDT_BYTE, &tag) ) {
+			disposal_method = *(BYTE *)FreeImage_GetTagValue(tag);
+		}
+
+		RGBQUAD *pal = FreeImage_GetPalette(dib);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapShort(&left);
+		SwapShort(&top);
+		SwapShort(&width);
+		SwapShort(&height);
+#endif
+
+		if( page == 0 ) {
+			//gather some info
+			WORD logicalwidth = width; // width has already been swapped...
+			if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", FIDT_SHORT, &tag) ) {
+				logicalwidth = *(WORD *)FreeImage_GetTagValue(tag);
+#ifdef FREEIMAGE_BIGENDIAN
+				SwapShort(&logicalwidth);
+#endif
+			}
+			WORD logicalheight = height; // height has already been swapped...
+			if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", FIDT_SHORT, &tag) ) {
+				logicalheight = *(WORD *)FreeImage_GetTagValue(tag);
+#ifdef FREEIMAGE_BIGENDIAN
+				SwapShort(&logicalheight);
+#endif
+			}
+			RGBQUAD *globalpalette = NULL;
+			int globalpalette_size = 0;
+			if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "GlobalPalette", FIDT_PALETTE, &tag) ) {
+				globalpalette_size = FreeImage_GetTagCount(tag);
+				if( globalpalette_size >= 2 ) {
+					globalpalette = (RGBQUAD *)FreeImage_GetTagValue(tag);
+				}
+			}
+
+			//Logical Screen Descriptor
+			io->write_proc(&logicalwidth, 2, 1, handle);
+			io->write_proc(&logicalheight, 2, 1, handle);
+			packed = GIF_PACKED_LSD_COLORRES;
+			b = 0;
+			RGBQUAD background_color;
+			if( globalpalette != NULL ) {
+				packed |= GIF_PACKED_LSD_HAVEGCT;
+				if( globalpalette_size < 4 ) {
+					globalpalette_size = 2;
+					packed |= 0 & GIF_PACKED_LSD_GCTSIZE;
+				} else if( globalpalette_size < 8 ) {
+					globalpalette_size = 4;
+					packed |= 1 & GIF_PACKED_LSD_GCTSIZE;
+				} else if( globalpalette_size < 16 ) {
+					globalpalette_size = 8;
+					packed |= 2 & GIF_PACKED_LSD_GCTSIZE;
+				} else if( globalpalette_size < 32 ) {
+					globalpalette_size = 16;
+					packed |= 3 & GIF_PACKED_LSD_GCTSIZE;
+				} else if( globalpalette_size < 64 ) {
+					globalpalette_size = 32;
+					packed |= 4 & GIF_PACKED_LSD_GCTSIZE;
+				} else if( globalpalette_size < 128 ) {
+					globalpalette_size = 64;
+					packed |= 5 & GIF_PACKED_LSD_GCTSIZE;
+				} else if( globalpalette_size < 256 ) {
+					globalpalette_size = 128;
+					packed |= 6 & GIF_PACKED_LSD_GCTSIZE;
+				} else {
+					globalpalette_size = 256;
+					packed |= 7 & GIF_PACKED_LSD_GCTSIZE;
+				}
+				if( FreeImage_GetBackgroundColor(dib, &background_color) ) {
+					for( int i = 0; i < globalpalette_size; i++ ) {
+						if( background_color.rgbRed == globalpalette[i].rgbRed &&
+							background_color.rgbGreen == globalpalette[i].rgbGreen &&
+							background_color.rgbBlue == globalpalette[i].rgbBlue ) {
+
+							b = (BYTE)i;
+							break;
+						}
+					}
+				}
+			} else {
+				packed |= (bpp - 1) & GIF_PACKED_LSD_GCTSIZE;
+			}
+			io->write_proc(&packed, 1, 1, handle);
+			io->write_proc(&b, 1, 1, handle);
+			b = 0;
+			io->write_proc(&b, 1, 1, handle);
+
+			//Global Color Table
+			if( globalpalette != NULL ) {
+				int i = 0;
+				while( i < globalpalette_size ) {
+					io->write_proc(&globalpalette[i].rgbRed, 1, 1, handle);
+					io->write_proc(&globalpalette[i].rgbGreen, 1, 1, handle);
+					io->write_proc(&globalpalette[i].rgbBlue, 1, 1, handle);
+					i++;
+				}
+			}
+
+			//Application Extension
+			LONG loop = 0;
+			if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "Loop", FIDT_LONG, &tag) ) {
+				loop = *(LONG *)FreeImage_GetTagValue(tag);
+			}
+			if( loop != 1 ) {
+				//the Netscape extension is really "repeats" not "loops"
+				if( loop > 1 ) loop--;
+				if( loop > 0xFFFF ) loop = 0xFFFF;
+				w = (WORD)loop;
+#ifdef FREEIMAGE_BIGENDIAN
+				SwapShort(&w);
+#endif
+				io->write_proc((void *)"\x21\xFF\x0BNETSCAPE2.0\x03\x01", 16, 1, handle);
+				io->write_proc(&w, 2, 1, handle);
+				b = 0;
+				io->write_proc(&b, 1, 1, handle);
+			}
+
+			//Comment Extension
+			FIMETADATA *mdhandle = NULL;
+			FITAG *tag = NULL;
+			mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag);
+			if( mdhandle ) {
+				do {
+					if( FreeImage_GetTagType(tag) == FIDT_ASCII ) {
+						int length = FreeImage_GetTagLength(tag) - 1;
+						char *value = (char *)FreeImage_GetTagValue(tag);
+						io->write_proc((void *)"\x21\xFE", 2, 1, handle);
+						while( length > 0 ) {
+							b = (BYTE)(length >= 255 ? 255 : length);
+							io->write_proc(&b, 1, 1, handle);
+							io->write_proc(value, b, 1, handle);
+							value += b;
+							length -= b;
+						}
+						b = 0;
+						io->write_proc(&b, 1, 1, handle);
+					}
+				} while(FreeImage_FindNextMetadata(mdhandle, &tag));
+
+				FreeImage_FindCloseMetadata(mdhandle);
+			}
+		}
+
+		//Graphic Control Extension
+		if( FreeImage_IsTransparent(dib) ) {
+			int count = FreeImage_GetTransparencyCount(dib);
+			BYTE *table = FreeImage_GetTransparencyTable(dib);
+			for( int i = 0; i < count; i++ ) {
+				if( table[i] == 0 ) {
+					have_transparent = true;
+					transparent_color = i;
+					break;
+				}
+			}
+		}
+		io->write_proc((void *)"\x21\xF9\x04", 3, 1, handle);
+		b = (BYTE)((disposal_method << 2) & GIF_PACKED_GCE_DISPOSAL);
+		if( have_transparent ) b |= GIF_PACKED_GCE_HAVETRANS;
+		io->write_proc(&b, 1, 1, handle);
+		//Notes about delay time for GIFs:
+		//IE5/IE6 have a minimum and default of 100ms
+		//Mozilla/Firefox/Netscape 6+/Opera have a minimum of 20ms and a default of 100ms if <20ms is specified or the GCE is absent
+		//Netscape 4 has a minimum of 10ms if 0ms is specified, but will use 0ms if the GCE is absent
+		w = (WORD)(delay_time / 10); //convert ms to cs
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapShort(&w);
+#endif
+		io->write_proc(&w, 2, 1, handle);
+		b = (BYTE)transparent_color;
+		io->write_proc(&b, 1, 1, handle);
+		b = 0;
+		io->write_proc(&b, 1, 1, handle);
+
+		//Image Descriptor
+		b = GIF_BLOCK_IMAGE_DESCRIPTOR;
+		io->write_proc(&b, 1, 1, handle);
+		io->write_proc(&left, 2, 1, handle);
+		io->write_proc(&top, 2, 1, handle);
+		io->write_proc(&width, 2, 1, handle);
+		io->write_proc(&height, 2, 1, handle);
+		packed = 0;
+		if( !no_local_palette ) packed |= GIF_PACKED_ID_HAVELCT | ((bpp - 1) & GIF_PACKED_ID_LCTSIZE);
+		if( interlaced ) packed |= GIF_PACKED_ID_INTERLACED;
+		io->write_proc(&packed, 1, 1, handle);
+
+		//Local Color Table
+		if( !no_local_palette ) {
+			int palsize = 1 << bpp;
+			for( int i = 0; i < palsize; i++ ) {
+				io->write_proc(&pal[i].rgbRed, 1, 1, handle);
+				io->write_proc(&pal[i].rgbGreen, 1, 1, handle);
+				io->write_proc(&pal[i].rgbBlue, 1, 1, handle);
+			}
+		}
+
+
+		//LZW Minimum Code Size
+		b = (BYTE)(bpp == 1 ? 2 : bpp);
+		io->write_proc(&b, 1, 1, handle);
+		StringTable *stringtable = new(std::nothrow) StringTable;
+		stringtable->Initialize(b);
+		stringtable->CompressStart(bpp, width);
+
+		//Image Data Sub-blocks
+		int y = 0, interlacepass = 0, line = FreeImage_GetLine(dib);
+		BYTE buf[255], *bufptr = buf; //255 is the max sub-block length
+		int size = sizeof(buf);
+		b = sizeof(buf);
+		while( y < output_height ) {
+			memcpy(stringtable->FillInputBuffer(line), FreeImage_GetScanLine(dib, output_height - y - 1), line);
+			while( stringtable->Compress(bufptr, &size) ) {
+				bufptr += size;
+				if( bufptr - buf == sizeof(buf) ) {
+					io->write_proc(&b, 1, 1, handle);
+					io->write_proc(buf, sizeof(buf), 1, handle);
+					size = sizeof(buf);
+					bufptr = buf;
+				} else {
+					size = (int)(sizeof(buf) - (bufptr - buf));
+				}
+			}
+			if( interlaced ) {
+				y += g_GifInterlaceIncrement[interlacepass];
+				if( y >= output_height && ++interlacepass < GIF_INTERLACE_PASSES ) {
+					y = g_GifInterlaceOffset[interlacepass];
+				}		
+			} else {
+				y++;
+			}
+		}
+		size = (int)(bufptr - buf);
+		BYTE last[4];
+		w = (WORD)stringtable->CompressEnd(last);
+		if( size + w >= sizeof(buf) ) {
+			//one last full size sub-block
+			io->write_proc(&b, 1, 1, handle);
+			io->write_proc(buf, size, 1, handle);
+			io->write_proc(last, sizeof(buf) - size, 1, handle);
+			//and possibly a tiny additional sub-block
+			b = (BYTE)(w - (sizeof(buf) - size));
+			if( b > 0 ) {
+				io->write_proc(&b, 1, 1, handle);
+				io->write_proc(last + w - b, b, 1, handle);
+			}
+		} else {
+			//last sub-block less than full size
+			b = (BYTE)(size + w);
+			io->write_proc(&b, 1, 1, handle);
+			io->write_proc(buf, size, 1, handle);
+			io->write_proc(last, w, 1, handle);
+		}
+
+		//Block Terminator
+		b = 0;
+		io->write_proc(&b, 1, 1, handle);
+
+		delete stringtable;
+
+	} catch (const char *msg) {
+		FreeImage_OutputMessageProc(s_format_id, msg);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV 
+InitGIF(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = PageCount;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/files/Source/FreeImage/PluginHDR.cpp b/files/Source/FreeImage/PluginHDR.cpp
new file mode 100644
index 0000000..878f4ff
--- /dev/null
+++ b/files/Source/FreeImage/PluginHDR.cpp
@@ -0,0 +1,723 @@
+// ==========================================================
+// HDR Loader and writer
+//
+// 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 <cmath>
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// RGBE library
+// ==========================================================
+
+// ----------------------------------------------------------
+
+// maximum size of a line in the header
+#define HDR_MAXLINE	256
+
+// flags indicating which fields in an rgbeHeaderInfo are valid
+#define RGBE_VALID_PROGRAMTYPE	0x01
+#define RGBE_VALID_COMMENT		0x02
+#define RGBE_VALID_GAMMA		0x04
+#define RGBE_VALID_EXPOSURE		0x08
+
+// offsets to red, green, and blue components in a data (float) pixel
+#define RGBE_DATA_RED    0
+#define RGBE_DATA_GREEN  1
+#define RGBE_DATA_BLUE   2
+
+// ----------------------------------------------------------
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagHeaderInfo {
+	int valid;					// indicate which fields are valid
+	char programtype[16];		// listed at beginning of file to identify it after "#?". defaults to "RGBE"
+	char comment[HDR_MAXLINE];	// comment beginning with "# " 
+	float gamma;				// image has already been gamma corrected with given gamma. defaults to 1.0 (no correction)
+	float exposure;				// a value of 1.0 in an image corresponds to <exposure> watts/steradian/m^2. defaults to 1.0
+} rgbeHeaderInfo;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+typedef enum {
+	rgbe_read_error,
+	rgbe_write_error,
+	rgbe_format_error,
+	rgbe_memory_error
+} rgbe_error_code;
+
+// ----------------------------------------------------------
+// Prototypes
+// ----------------------------------------------------------
+
+static BOOL rgbe_Error(rgbe_error_code error_code, const char *msg);
+static BOOL rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length);
+static inline void rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf);
+static inline void rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]);
+static BOOL rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info);
+static BOOL rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info);
+static BOOL rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels);
+static BOOL rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels);
+static BOOL rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines);
+static BOOL rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes);
+static BOOL rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines);
+static BOOL rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info);
+static BOOL rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info);
+
+// ----------------------------------------------------------
+
+/**
+Default error routine.  change this to change error handling 
+*/
+static BOOL  
+rgbe_Error(rgbe_error_code error_code, const char *msg) {
+	switch (error_code) {
+		case rgbe_read_error:
+			FreeImage_OutputMessageProc(s_format_id, "RGBE read error");
+			break;
+		case rgbe_write_error:
+			FreeImage_OutputMessageProc(s_format_id, "RGBE write error");
+			break;
+		case rgbe_format_error:
+			FreeImage_OutputMessageProc(s_format_id, "RGBE bad file format: %s\n", msg);
+			break;
+		default:
+		case rgbe_memory_error:
+			FreeImage_OutputMessageProc(s_format_id, "RGBE error: %s\n",msg);
+	}
+
+	return FALSE;
+}
+
+/**
+Get a line from a ASCII io stream
+*/
+static BOOL 
+rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length) {
+	int i;
+	memset(buffer, 0, length);
+	for(i = 0; i < length; i++) {
+		if(!io->read_proc(&buffer[i], 1, 1, handle))
+			return FALSE;
+		if(buffer[i] == 0x0A)
+			break;
+	}
+	
+	return (i < length) ? TRUE : FALSE;
+}
+
+/**
+Standard conversion from float pixels to rgbe pixels. 
+Note: you can remove the "inline"s if your compiler complains about it 
+*/
+static inline void 
+rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf) {
+	float v;
+	int e;
+
+	v = rgbf->red;
+	if (rgbf->green > v) v = rgbf->green;
+	if (rgbf->blue > v) v = rgbf->blue;
+	if (v < 1e-32) {
+		rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
+	}
+	else {
+          v = (float)(std::frexp(v, &e) * 256.0 / v);
+          rgbe[0] = (BYTE)(rgbf->red * v);
+          rgbe[1] = (BYTE)(rgbf->green * v);
+          rgbe[2] = (BYTE)(rgbf->blue * v);
+          rgbe[3] = (BYTE)(e + 128);
+        }
+}
+
+/**
+Standard conversion from rgbe to float pixels. 
+Note: Ward uses ldexp(col+0.5,exp-(128+8)). 
+However we wanted pixels in the range [0,1] to map back into the range [0,1].
+*/
+static inline void 
+rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]) {
+	if (rgbe[3]) {   // nonzero pixel
+		float f = (float)(ldexp(1.0, rgbe[3] - (int)(128+8)));
+		rgbf->red   = rgbe[0] * f;
+		rgbf->green = rgbe[1] * f;
+		rgbf->blue  = rgbe[2] * f;
+
+	}
+	else {
+		rgbf->red = rgbf->green = rgbf->blue = 0;
+	}
+}
+
+/**
+Minimal header reading. Modify if you want to parse more information 
+*/
+static BOOL 
+rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info) {
+	char buf[HDR_MAXLINE];
+	float tempf;
+	int i;
+	BOOL bFormatFound = FALSE;
+	BOOL bHeaderFound = FALSE;
+
+	header_info->valid = 0;
+	header_info->programtype[0] = 0;
+	header_info->gamma = 1.0;
+	header_info->exposure = 1.0;
+
+	// get the first line
+	if(!rgbe_GetLine(io, handle, buf, HDR_MAXLINE))
+		return rgbe_Error(rgbe_read_error, NULL);
+
+	// check the signature
+
+	if ((buf[0] != '#')||(buf[1] != '?')) {
+		// if you don't want to require the magic token then comment the next line
+		return rgbe_Error(rgbe_format_error,"bad initial token");
+	}
+	else {
+		header_info->valid |= RGBE_VALID_PROGRAMTYPE;
+		for(i = 0; i < sizeof(header_info->programtype) - 1; i++) {
+			if((buf[i+2] == 0) || isspace(buf[i+2]))
+				break;
+			header_info->programtype[i] = buf[i+2];
+		}
+		header_info->programtype[i] = 0;
+	}
+
+	for(;;) {
+		// get next line
+		if(!rgbe_GetLine(io, handle, buf, HDR_MAXLINE))
+			return rgbe_Error(rgbe_read_error, NULL);
+
+		if((buf[0] == 0) || (buf[0] == '\n')) {
+			// end of header so break out of loop
+			bHeaderFound = TRUE;
+			break;
+		}
+		else if(strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0) {
+			bFormatFound = TRUE;
+		}
+		else if(sscanf(buf, "GAMMA=%g", &tempf) == 1) {
+			header_info->gamma = tempf;
+			header_info->valid |= RGBE_VALID_GAMMA;
+		}
+		else if(sscanf(buf,"EXPOSURE=%g",&tempf) == 1) {
+			header_info->exposure = tempf;
+			header_info->valid |= RGBE_VALID_EXPOSURE;
+		}
+		else if((buf[0] == '#') && (buf[1] == 0x20)) {
+			header_info->valid |= RGBE_VALID_COMMENT;
+			strcpy(header_info->comment, buf);
+		}
+	}
+	if(!bHeaderFound || !bFormatFound) {
+		return rgbe_Error(rgbe_format_error, "invalid header");
+	}
+
+	// get next line
+	if(!rgbe_GetLine(io, handle, buf, HDR_MAXLINE))
+		return rgbe_Error(rgbe_read_error, NULL);
+
+	// get the image width & height
+	if(sscanf(buf,"-Y %d +X %d", height, width) < 2) {
+		if(sscanf(buf,"+X %d +Y %d", height, width) < 2) {
+			return rgbe_Error(rgbe_format_error, "missing image size specifier");
+		}
+	}
+
+	return TRUE;
+}
+
+/**
+ default minimal header. modify if you want more information in header 
+*/
+static BOOL 
+rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info) {
+	char buffer[HDR_MAXLINE];
+
+	const char *programtype = "RADIANCE";
+
+	if(info && (info->valid & RGBE_VALID_PROGRAMTYPE)) {
+		programtype = info->programtype;
+	}
+	// The #? is to identify file type, the programtype is optional
+	sprintf(buffer, "#?%s\n", programtype);
+	if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+		return rgbe_Error(rgbe_write_error, NULL);
+	sprintf(buffer, "%s\n", info->comment);
+	if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+		return rgbe_Error(rgbe_write_error, NULL);
+	sprintf(buffer, "FORMAT=32-bit_rle_rgbe\n");
+	if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+		return rgbe_Error(rgbe_write_error, NULL);
+	if(info && (info->valid & RGBE_VALID_GAMMA)) {
+		sprintf(buffer, "GAMMA=%g\n", info->gamma);
+		if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+			return rgbe_Error(rgbe_write_error, NULL);
+	}
+	if(info && (info->valid & RGBE_VALID_EXPOSURE)) {
+		sprintf(buffer,"EXPOSURE=%g\n", info->exposure);
+		if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+			return rgbe_Error(rgbe_write_error, NULL);
+	}
+	sprintf(buffer, "\n-Y %d +X %d\n", height, width);
+	if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+		return rgbe_Error(rgbe_write_error, NULL);
+
+	return TRUE;
+}
+
+static BOOL 
+rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) {
+	return TRUE;
+}
+static BOOL 
+rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) {
+	header_info->gamma = 1;
+	header_info->valid |= RGBE_VALID_GAMMA;
+	header_info->exposure = 0;
+	header_info->valid |= RGBE_VALID_EXPOSURE;
+
+	return TRUE;
+}
+
+/** 
+Simple read routine. Will not correctly handle run length encoding 
+*/
+static BOOL 
+rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) {
+  BYTE rgbe[4];
+
+  for(unsigned x = 0; x < numpixels; x++) {
+	if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 1) {
+		return rgbe_Error(rgbe_read_error, NULL);
+	}
+	rgbe_RGBEToFloat(&data[x], rgbe);
+  }
+
+  return TRUE;
+}
+
+/**
+ Simple write routine that does not use run length encoding. 
+ These routines can be made faster by allocating a larger buffer and
+ fread-ing and fwrite-ing the data in larger chunks.
+*/
+static BOOL 
+rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) {
+  BYTE rgbe[4];
+
+  for(unsigned x = 0; x < numpixels; x++) {
+	  rgbe_FloatToRGBE(rgbe, &data[x]);
+	  if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1)
+		  return rgbe_Error(rgbe_write_error, NULL);
+  }
+
+  return TRUE;
+}
+
+static BOOL 
+rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines) {
+	BYTE rgbe[4], *scanline_buffer, *ptr, *ptr_end;
+	int i, count;
+	BYTE buf[2];
+	
+	if ((scanline_width < 8)||(scanline_width > 0x7fff)) {
+		// run length encoding is not allowed so read flat
+		return rgbe_ReadPixels(io, handle, data, scanline_width * num_scanlines);
+	}
+	scanline_buffer = NULL;
+	// read in each successive scanline 
+	while(num_scanlines > 0) {
+		if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 1) {
+			free(scanline_buffer);
+			return rgbe_Error(rgbe_read_error,NULL);
+		}
+		if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) {
+			// this file is not run length encoded
+			rgbe_RGBEToFloat(data, rgbe);
+			data ++;
+			free(scanline_buffer);
+			return rgbe_ReadPixels(io, handle, data, scanline_width * num_scanlines - 1);
+		}
+		if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) {
+			free(scanline_buffer);
+			return rgbe_Error(rgbe_format_error,"wrong scanline width");
+		}
+		if(scanline_buffer == NULL) {
+			scanline_buffer = (BYTE*)malloc(sizeof(BYTE) * 4 * scanline_width);
+			if(scanline_buffer == NULL) {
+				return rgbe_Error(rgbe_memory_error, "unable to allocate buffer space");
+			}
+		}
+		
+		ptr = &scanline_buffer[0];
+		// read each of the four channels for the scanline into the buffer
+		for(i = 0; i < 4; i++) {
+			ptr_end = &scanline_buffer[(i+1)*scanline_width];
+			while(ptr < ptr_end) {
+				if(io->read_proc(buf, 1, 2 * sizeof(BYTE), handle) < 1) {
+					free(scanline_buffer);
+					return rgbe_Error(rgbe_read_error, NULL);
+				}
+				if(buf[0] > 128) {
+					// a run of the same value
+					count = buf[0] - 128;
+					if((count == 0) || (count > ptr_end - ptr)) {
+						free(scanline_buffer);
+						return rgbe_Error(rgbe_format_error, "bad scanline data");
+					}
+					while(count-- > 0)
+						*ptr++ = buf[1];
+				}
+				else {
+					// a non-run
+					count = buf[0];
+					if((count == 0) || (count > ptr_end - ptr)) {
+						free(scanline_buffer);
+						return rgbe_Error(rgbe_format_error, "bad scanline data");
+					}
+					*ptr++ = buf[1];
+					if(--count > 0) {
+						if(io->read_proc(ptr, 1, sizeof(BYTE) * count, handle) < 1) {
+							free(scanline_buffer);
+							return rgbe_Error(rgbe_read_error, NULL);
+						}
+						ptr += count;
+					}
+				}
+			}
+		}
+		// now convert data from buffer into floats
+		for(i = 0; i < scanline_width; i++) {
+			rgbe[0] = scanline_buffer[i];
+			rgbe[1] = scanline_buffer[i+scanline_width];
+			rgbe[2] = scanline_buffer[i+2*scanline_width];
+			rgbe[3] = scanline_buffer[i+3*scanline_width];
+			rgbe_RGBEToFloat(data, rgbe);
+			data ++;
+		}
+
+		num_scanlines--;
+	}
+
+	free(scanline_buffer);
+	
+	return TRUE;
+}
+
+/**
+ The code below is only needed for the run-length encoded files.
+ Run length encoding adds considerable complexity but does 
+ save some space.  For each scanline, each channel (r,g,b,e) is 
+ encoded separately for better compression. 
+ @return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes) {
+	static const int MINRUNLENGTH = 4;
+	int cur, beg_run, run_count, old_run_count, nonrun_count;
+	BYTE buf[2];
+	
+	cur = 0;
+	while(cur < numbytes) {
+		beg_run = cur;
+		// find next run of length at least 4 if one exists 
+		run_count = old_run_count = 0;
+		while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
+			beg_run += run_count;
+			old_run_count = run_count;
+			run_count = 1;
+			while((beg_run + run_count < numbytes) && (run_count < 127) && (data[beg_run] == data[beg_run + run_count])) {
+				run_count++;
+			}
+		}
+		// if data before next big run is a short run then write it as such 
+		if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) {
+			buf[0] = (BYTE)(128 + old_run_count);   // write short run
+			buf[1] = data[cur];
+			if(io->write_proc(buf, 2 * sizeof(BYTE), 1, handle) < 1)
+				return rgbe_Error(rgbe_write_error, NULL);
+			cur = beg_run;
+		}
+		// write out bytes until we reach the start of the next run 
+		while(cur < beg_run) {
+			nonrun_count = beg_run - cur;
+			if (nonrun_count > 128) 
+				nonrun_count = 128;
+			buf[0] = (BYTE)nonrun_count;
+			if(io->write_proc(buf, sizeof(buf[0]), 1, handle) < 1)
+				return rgbe_Error(rgbe_write_error,NULL);
+			if(io->write_proc(&data[cur], sizeof(data[0]) * nonrun_count, 1, handle) < 1)
+				return rgbe_Error(rgbe_write_error,NULL);
+			cur += nonrun_count;
+		}
+		// write out next run if one was found 
+		if (run_count >= MINRUNLENGTH) {
+			buf[0] = (BYTE)(128 + run_count);
+			buf[1] = data[beg_run];
+			if(io->write_proc(buf, sizeof(buf[0]) * 2, 1, handle) < 1)
+				return rgbe_Error(rgbe_write_error,NULL);
+			cur += run_count;
+		}
+	}
+	
+	return TRUE;
+}
+
+static BOOL 
+rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines) {
+	BYTE rgbe[4];
+	BYTE *buffer;
+	
+	if ((scanline_width < 8)||(scanline_width > 0x7fff)) {
+		// run length encoding is not allowed so write flat
+		return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines);
+	}
+	buffer = (BYTE*)malloc(sizeof(BYTE) * 4 * scanline_width);
+	if (buffer == NULL) {
+		// no buffer space so write flat 
+		return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines);
+	}
+	while(num_scanlines-- > 0) {
+		rgbe[0] = (BYTE)2;
+		rgbe[1] = (BYTE)2;
+		rgbe[2] = (BYTE)(scanline_width >> 8);
+		rgbe[3] = (BYTE)(scanline_width & 0xFF);
+		if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1) {
+			free(buffer);
+			return rgbe_Error(rgbe_write_error, NULL);
+		}
+		for(unsigned x = 0; x < scanline_width; x++) {
+			rgbe_FloatToRGBE(rgbe, data);
+			buffer[x] = rgbe[0];
+			buffer[x+scanline_width] = rgbe[1];
+			buffer[x+2*scanline_width] = rgbe[2];
+			buffer[x+3*scanline_width] = rgbe[3];
+			data ++;
+		}
+		// write out each of the four channels separately run length encoded
+		// first red, then green, then blue, then exponent
+		for(int i = 0; i < 4; i++) {
+			BOOL bOK = rgbe_WriteBytes_RLE(io, handle, &buffer[i*scanline_width], scanline_width);
+			if(!bOK) {
+				free(buffer);
+				return bOK;
+			}
+		}
+	}
+	free(buffer);
+	
+	return TRUE;
+}
+
+
+// ----------------------------------------------------------
+
+
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "HDR";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "High Dynamic Range Image";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "hdr";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/vnd.radiance";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE hdr_signature[] = { '#', '?' };
+	BYTE signature[] = { 0, 0 };
+
+	io->read_proc(signature, 1, 2, handle);
+
+	return (memcmp(hdr_signature, signature, 2) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_RGBF) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	FIBITMAP *dib = NULL;
+
+	if(!handle) {
+		return NULL;
+	}
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	try {
+
+		rgbeHeaderInfo header_info;
+		unsigned width, height;
+
+		// Read the header
+		if(rgbe_ReadHeader(io, handle, &width, &height, &header_info) == FALSE) {
+			return NULL;
+		}
+
+		// allocate a RGBF image
+		dib = FreeImage_AllocateHeaderT(header_only, FIT_RGBF, width, height);
+		if(!dib) {
+			throw FI_MSG_ERROR_MEMORY;
+		}
+
+		// set the metadata as comments
+		rgbe_ReadMetadata(dib, &header_info);
+
+		if(header_only) {
+			// header only mode
+			return dib;
+		}
+
+		// read the image pixels and fill the dib
+		
+		for(unsigned y = 0; y < height; y++) {
+			FIRGBF *scanline = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
+			if(!rgbe_ReadPixels_RLE(io, handle, scanline, width, 1)) {
+				FreeImage_Unload(dib);
+				return NULL;
+			}
+		}
+
+	}
+	catch(const char *text) {
+		if(dib != NULL) {
+			FreeImage_Unload(dib);
+		}
+		FreeImage_OutputMessageProc(s_format_id, text);
+	}
+
+	return dib;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	if(!dib) return FALSE;
+
+	FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+	if(src_type != FIT_RGBF) {
+		FreeImage_OutputMessageProc(s_format_id, "FREE_IMAGE_TYPE: Unable to convert from type %d to type %d.\n No such conversion exists.", src_type, FIT_RGBF);
+		return FALSE;
+	}
+
+	unsigned width  = FreeImage_GetWidth(dib);
+	unsigned height = FreeImage_GetHeight(dib);
+
+	// write the header
+
+	rgbeHeaderInfo header_info;
+	memset(&header_info, 0, sizeof(rgbeHeaderInfo));
+	// fill the header with correct gamma and exposure
+	rgbe_WriteMetadata(dib, &header_info);
+	// fill a comment
+	sprintf(header_info.comment, "# Made with FreeImage %s", FreeImage_GetVersion());
+	if(!rgbe_WriteHeader(io, handle, width, height, &header_info)) {
+		return FALSE;
+	}
+
+	// write each scanline
+
+	for(unsigned y = 0; y < height; y++) {
+		FIRGBF *scanline = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
+		if(!rgbe_WritePixels_RLE(io, handle, scanline, width, 1)) {
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitHDR(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginICO.cpp b/files/Source/FreeImage/PluginICO.cpp
new file mode 100644
index 0000000..c818379
--- /dev/null
+++ b/files/Source/FreeImage/PluginICO.cpp
@@ -0,0 +1,824 @@
+// ==========================================================
+// ICO Loader and Writer
+//
+// 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 "Utilities.h"
+
+// ----------------------------------------------------------
+//   Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+// These next two structs represent how the icon information is stored
+// in an ICO file.
+
+typedef struct tagICONHEADER {
+	WORD			idReserved;   // reserved
+	WORD			idType;       // resource type (1 for icons)
+	WORD			idCount;      // how many images?
+} ICONHEADER;
+
+typedef struct tagICONDIRECTORYENTRY {
+	BYTE	bWidth;               // width of the image
+	BYTE	bHeight;              // height of the image (times 2)
+	BYTE	bColorCount;          // number of colors in image (0 if >=8bpp)
+	BYTE	bReserved;            // reserved
+	WORD	wPlanes;              // color Planes
+	WORD	wBitCount;            // bits per pixel
+	DWORD	dwBytesInRes;         // how many bytes in this resource?
+	DWORD	dwImageOffset;        // where in the file is this image
+} ICONDIRENTRY;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Static helpers
+// ==========================================================
+
+/**  How wide, in bytes, would this many bits be, DWORD aligned ?
+*/
+static int 
+WidthBytes(int bits) {
+	return ((((bits) + 31)>>5)<<2);
+}
+
+/** Calculates the size of a single icon image
+@return Returns the size for that image
+*/
+static DWORD 
+CalculateImageSize(FIBITMAP* icon_dib) {
+	DWORD dwNumBytes = 0;
+
+	unsigned colors		= FreeImage_GetColorsUsed(icon_dib);
+	unsigned width		= FreeImage_GetWidth(icon_dib);
+	unsigned height		= FreeImage_GetHeight(icon_dib);
+	unsigned pitch		= FreeImage_GetPitch(icon_dib);
+
+	dwNumBytes = sizeof( BITMAPINFOHEADER );	// header
+	dwNumBytes += colors * sizeof(RGBQUAD);		// palette
+	dwNumBytes += height * pitch;				// XOR mask
+	dwNumBytes += height * WidthBytes(width);	// AND mask
+
+	return dwNumBytes;
+}
+
+/** Calculates the file offset for an icon image
+@return Returns the file offset for that image
+*/
+static DWORD 
+CalculateImageOffset(std::vector<FIBITMAP*>& vPages, int nIndex ) {
+	DWORD	dwSize;
+
+    // calculate the ICO header size
+    dwSize = sizeof(ICONHEADER); 
+    // add the ICONDIRENTRY's
+    dwSize += (DWORD)( vPages.size() * sizeof(ICONDIRENTRY) );
+    // add the sizes of the previous images
+    for(int k = 0; k < nIndex; k++) {
+		FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
+		dwSize += CalculateImageSize(icon_dib);
+	}
+
+    return dwSize;
+}
+
+/**
+Vista icon support
+@return Returns TRUE if the bitmap data is stored in PNG format
+*/
+static BOOL
+IsPNG(FreeImageIO *io, fi_handle handle) {
+	BYTE png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+	BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	long tell = io->tell_proc(handle);
+	io->read_proc(&signature, 1, 8, handle);
+	BOOL bIsPNG = (memcmp(png_signature, signature, 8) == 0);
+	io->seek_proc(handle, tell, SEEK_SET);
+
+	return bIsPNG;
+}
+
+#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
+SwapIconHeader(ICONHEADER *header) {
+	SwapShort(&header->idReserved);
+	SwapShort(&header->idType);
+	SwapShort(&header->idCount);
+}
+
+static void
+SwapIconDirEntries(ICONDIRENTRY *ent, int num) {
+	while(num) {
+		SwapShort(&ent->wPlanes);
+		SwapShort(&ent->wBitCount);
+		SwapLong(&ent->dwBytesInRes);
+		SwapLong(&ent->dwImageOffset);
+		num--;
+		ent++;
+	}
+}
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "ICO";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Windows Icon";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "ico";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/vnd.microsoft.icon";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	ICONHEADER icon_header;
+
+	io->read_proc(&icon_header, sizeof(ICONHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapIconHeader(&icon_header);
+#endif
+
+	return ((icon_header.idReserved == 0) && (icon_header.idType == 1) && (icon_header.idCount > 0));
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+			(depth == 1) ||
+			(depth == 4) ||
+			(depth == 8) ||
+			(depth == 16) ||
+			(depth == 24) ||
+			(depth == 32)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	// Allocate memory for the header structure
+	ICONHEADER *lpIH = (ICONHEADER*)malloc(sizeof(ICONHEADER));
+	if(lpIH == NULL) {
+		return NULL;
+	}
+
+	if (read) {
+		// Read in the header
+		io->read_proc(lpIH, 1, sizeof(ICONHEADER), handle);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapIconHeader(lpIH);
+#endif
+
+		if(!(lpIH->idReserved == 0) || !(lpIH->idType == 1)) {
+			// Not an ICO file
+			free(lpIH);
+			return NULL;
+		}
+	}
+	else {
+		// Fill the header
+		lpIH->idReserved = 0;
+		lpIH->idType = 1;
+		lpIH->idCount = 0;
+	}
+
+	return lpIH;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+	// free the header structure
+	ICONHEADER *lpIH = (ICONHEADER*)data;
+	free(lpIH);
+}
+
+// ----------------------------------------------------------
+
+static int DLL_CALLCONV
+PageCount(FreeImageIO *io, fi_handle handle, void *data) {
+	ICONHEADER *lpIH = (ICONHEADER*)data;
+
+	if(lpIH) {
+		return lpIH->idCount;
+	}
+	return 1;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP*
+LoadStandardIcon(FreeImageIO *io, fi_handle handle, int flags, BOOL header_only) {
+	FIBITMAP *dib = NULL;
+
+	// load the BITMAPINFOHEADER
+	BITMAPINFOHEADER bmih;
+	io->read_proc(&bmih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapInfoHeader(&bmih);
+#endif
+
+	// allocate the bitmap
+	int width  = bmih.biWidth;
+	int height = bmih.biHeight / 2; // height == xor + and mask
+	unsigned bit_count = bmih.biBitCount;
+	unsigned line   = CalculateLine(width, bit_count);
+	unsigned pitch  = CalculatePitch(line);
+
+	// allocate memory for one icon
+
+	dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
+
+	if (dib == NULL) {
+		return NULL;
+	}
+
+	if( bmih.biBitCount <= 8 ) {
+		// read the palette data
+		io->read_proc(FreeImage_GetPalette(dib), CalculateUsedPaletteEntries(bit_count) * sizeof(RGBQUAD), 1, handle);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+		RGBQUAD *pal = FreeImage_GetPalette(dib);
+		for(unsigned i = 0; i < CalculateUsedPaletteEntries(bit_count); i++) {
+			INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
+		}
+#endif
+	}
+	
+	if(header_only) {
+		// header only mode
+		return dib;
+	}
+
+	// read the icon
+	io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle);
+
+#ifdef FREEIMAGE_BIGENDIAN
+	if (bit_count == 16) {
+		for(int y = 0; y < height; y++) {
+			WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
+			for(int x = 0; x < width; x++) {
+				SwapShort(pixel);
+				pixel++;
+			}
+		}
+	}
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+	if (bit_count == 24 || bit_count == 32) {
+		for(int y = 0; y < height; y++) {
+			BYTE *pixel = FreeImage_GetScanLine(dib, y);
+			for(int x = 0; x < width; x++) {
+				INPLACESWAP(pixel[0], pixel[2]);
+				pixel += (bit_count>>3);
+			}
+		}
+	}
+#endif
+	// bitmap has been loaded successfully!
+
+	// convert to 32bpp and generate an alpha channel
+	// apply the AND mask only if the image is not 32 bpp
+	if(((flags & ICO_MAKEALPHA) == ICO_MAKEALPHA) && (bit_count < 32)) {
+		FIBITMAP *dib32 = FreeImage_ConvertTo32Bits(dib);
+		FreeImage_Unload(dib);
+
+		if (dib32 == NULL) {
+			return NULL;
+		}
+
+		int width_and	= WidthBytes(width);
+		BYTE *line_and	= (BYTE *)malloc(width_and);
+
+		if( line_and == NULL ) {
+			FreeImage_Unload(dib32);
+			return NULL;
+		}
+
+		//loop through each line of the AND-mask generating the alpha channel, invert XOR-mask
+		for(int y = 0; y < height; y++) {
+			RGBQUAD *quad = (RGBQUAD *)FreeImage_GetScanLine(dib32, y);
+			io->read_proc(line_and, width_and, 1, handle);
+			for(int x = 0; x < width; x++) {
+				quad->rgbReserved = (line_and[x>>3] & (0x80 >> (x & 0x07))) != 0 ? 0 : 0xFF;
+				if( quad->rgbReserved == 0 ) {
+					quad->rgbBlue ^= 0xFF;
+					quad->rgbGreen ^= 0xFF;
+					quad->rgbRed ^= 0xFF;
+				}
+				quad++;
+			}
+		}
+		free(line_and);
+
+		return dib32;
+	}
+
+	return dib;
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	if (page == -1) {
+		page = 0;
+	}
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	if (handle != NULL) {
+		FIBITMAP *dib = NULL;
+
+		// get the icon header
+		ICONHEADER *icon_header = (ICONHEADER*)data;
+
+		if (icon_header) {
+			// load the icon descriptions
+			ICONDIRENTRY *icon_list = (ICONDIRENTRY*)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
+			if(icon_list == NULL) {
+				return NULL;
+			}
+			io->seek_proc(handle, sizeof(ICONHEADER), SEEK_SET);
+			io->read_proc(icon_list, icon_header->idCount * sizeof(ICONDIRENTRY), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+			SwapIconDirEntries(icon_list, icon_header->idCount);
+#endif
+
+			// load the requested icon
+			if (page < icon_header->idCount) {
+				// seek to the start of the bitmap data for the icon
+				io->seek_proc(handle, icon_list[page].dwImageOffset, SEEK_SET);
+
+				if( IsPNG(io, handle) ) {
+					// Vista icon support
+					// see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
+					dib = FreeImage_LoadFromHandle(FIF_PNG, io, handle, header_only ? FIF_LOAD_NOPIXELS : PNG_DEFAULT);
+				}
+				else {
+					// standard icon support
+					// see http://msdn.microsoft.com/en-us/library/ms997538.aspx
+					// see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/18/10077133.aspx
+					dib = LoadStandardIcon(io, handle, flags, header_only);
+				}
+
+				free(icon_list);
+
+				return dib;
+
+			} else {
+				free(icon_list);
+				FreeImage_OutputMessageProc(s_format_id, "Page doesn't exist");
+			}
+		} else {
+			FreeImage_OutputMessageProc(s_format_id, "File is not an ICO file");
+		}
+	}
+
+	return NULL;
+}
+
+// ----------------------------------------------------------
+
+static BOOL 
+SaveStandardIcon(FreeImageIO *io, FIBITMAP *dib, fi_handle handle) {
+	BITMAPINFOHEADER *bmih = NULL;
+
+	// write the BITMAPINFOHEADER
+	bmih = FreeImage_GetInfoHeader(dib);
+	bmih->biHeight *= 2;	// height == xor + and mask
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapInfoHeader(bmih);
+#endif
+	io->write_proc(bmih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapInfoHeader(bmih);
+#endif
+	bmih->biHeight /= 2;
+
+	// write the palette data
+	if (FreeImage_GetPalette(dib) != NULL) {
+		RGBQUAD *pal = FreeImage_GetPalette(dib);
+		FILE_BGRA bgra;
+		for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
+			bgra.b = pal[i].rgbBlue;
+			bgra.g = pal[i].rgbGreen;
+			bgra.r = pal[i].rgbRed;
+			bgra.a = pal[i].rgbReserved;
+			io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle);
+		}
+	}
+
+	// write the bits
+	int width			= bmih->biWidth;
+	int height			= bmih->biHeight;
+	unsigned bit_count	= bmih->biBitCount;
+	unsigned line		= CalculateLine(width, bit_count);
+	unsigned pitch		= CalculatePitch(line);
+	int size_xor		= height * pitch;
+	int size_and		= height * WidthBytes(width);
+
+	// XOR mask
+#ifdef FREEIMAGE_BIGENDIAN
+	if (bit_count == 16) {
+		WORD pixel;
+		for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+			BYTE *line = FreeImage_GetScanLine(dib, y);
+			for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+				pixel = ((WORD *)line)[x];
+				SwapShort(&pixel);
+				if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1)
+					return FALSE;
+			}
+		}
+	} else
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+	if (bit_count == 24) {
+		FILE_BGR bgr;
+		for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+			BYTE *line = FreeImage_GetScanLine(dib, y);
+			for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+				RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x;
+				bgr.b = triple->rgbtBlue;
+				bgr.g = triple->rgbtGreen;
+				bgr.r = triple->rgbtRed;
+				if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1)
+					return FALSE;
+			}
+		}
+	} else if (bit_count == 32) {
+		FILE_BGRA bgra;
+		for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+			BYTE *line = FreeImage_GetScanLine(dib, y);
+			for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+				RGBQUAD *quad = ((RGBQUAD *)line)+x;
+				bgra.b = quad->rgbBlue;
+				bgra.g = quad->rgbGreen;
+				bgra.r = quad->rgbRed;
+				bgra.a = quad->rgbReserved;
+				if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1)
+					return FALSE;
+			}
+		}
+	} else
+#endif
+#if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+	{
+#endif
+		BYTE *xor_mask = FreeImage_GetBits(dib);
+		io->write_proc(xor_mask, size_xor, 1, handle);
+#if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+	}
+#endif
+	// AND mask
+	BYTE *and_mask = (BYTE*)malloc(size_and);
+	if(!and_mask) {
+		return FALSE;
+	}
+
+	if(FreeImage_IsTransparent(dib)) {
+
+		if(bit_count == 32) {
+			// create the AND mask from the alpha channel
+
+			int width_and  = WidthBytes(width);
+			BYTE *and_bits = and_mask;
+
+			// clear the mask
+			memset(and_mask, 0, size_and);
+
+			for(int y = 0; y < height; y++) {
+				RGBQUAD *bits = (RGBQUAD*)FreeImage_GetScanLine(dib, y);
+
+				for(int x = 0; x < width; x++) {
+					if(bits[x].rgbReserved != 0xFF) {
+						// set any transparent color to full transparency
+						and_bits[x >> 3] |= (0x80 >> (x & 0x7)); 
+					}
+				}
+
+				and_bits += width_and;
+			}
+		} 
+		else if(bit_count <= 8) {
+			// create the AND mask from the transparency table
+
+			BYTE *trns = FreeImage_GetTransparencyTable(dib);
+
+			int width_and  = WidthBytes(width);
+			BYTE *and_bits = and_mask;
+
+			// clear the mask
+			memset(and_mask, 0, size_and);
+
+			switch(FreeImage_GetBPP(dib)) {
+				case 1:
+				{
+					for(int y = 0; y < height; y++) {
+						BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+						for(int x = 0; x < width; x++) {
+							// get pixel at (x, y)
+							BYTE index = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+							if(trns[index] != 0xFF) {
+								// set any transparent color to full transparency
+								and_bits[x >> 3] |= (0x80 >> (x & 0x7)); 
+							}
+						}
+						and_bits += width_and;
+					}
+				}
+				break;
+
+				case 4:
+				{
+					for(int y = 0; y < height; y++) {
+						BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+						for(int x = 0; x < width; x++) {
+							// get pixel at (x, y)
+							BYTE shift = (BYTE)((1 - x % 2) << 2);
+							BYTE index = (bits[x >> 1] & (0x0F << shift)) >> shift;
+							if(trns[index] != 0xFF) {
+								// set any transparent color to full transparency
+								and_bits[x >> 3] |= (0x80 >> (x & 0x7)); 
+							}
+						}
+						and_bits += width_and;
+					}
+				}
+				break;
+
+				case 8:
+				{
+					for(int y = 0; y < height; y++) {
+						BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+						for(int x = 0; x < width; x++) {
+							// get pixel at (x, y)
+							BYTE index = bits[x];
+							if(trns[index] != 0xFF) {
+								// set any transparent color to full transparency
+								and_bits[x >> 3] |= (0x80 >> (x & 0x7)); 
+							}
+						}
+						and_bits += width_and;
+					}
+				}
+				break;
+
+			}
+		}
+	}
+	else {
+		// empty AND mask
+		memset(and_mask, 0, size_and);
+	}
+
+	io->write_proc(and_mask, size_and, 1, handle);
+	free(and_mask);
+
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	ICONHEADER *icon_header = NULL;
+	std::vector<FIBITMAP*> vPages;
+	int k;
+
+	if(!dib || !handle || !data) {
+		return FALSE;
+	}
+
+	// check format limits
+	unsigned w = FreeImage_GetWidth(dib);
+	unsigned h = FreeImage_GetHeight(dib);
+	if((w < 16) || (w > 256) || (h < 16) || (h > 256) || (w != h)) {
+		FreeImage_OutputMessageProc(s_format_id, "Unsupported icon size: width x height = %d x %d", w, h);
+		return FALSE;
+	}
+
+	if (page == -1) {
+		page = 0;
+	}
+	
+	// get the icon header
+	icon_header = (ICONHEADER*)data;
+
+	try {
+		FIBITMAP *icon_dib = NULL;
+
+		// load all icons
+		for(k = 0; k < icon_header->idCount; k++) {
+			icon_dib = Load(io, handle, k, flags, data);
+			if(!icon_dib) {
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+			vPages.push_back(icon_dib);
+		}
+
+		// add the page
+		icon_dib = FreeImage_Clone(dib);
+		vPages.push_back(icon_dib);
+		icon_header->idCount++;
+
+		// write the header
+		io->seek_proc(handle, 0, SEEK_SET);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapIconHeader(icon_header);
+#endif
+		io->write_proc(icon_header, sizeof(ICONHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapIconHeader(icon_header);
+#endif
+
+		// write all icons
+		// ...
+		
+		// save the icon descriptions
+
+		ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
+		if(!icon_list) {
+			throw FI_MSG_ERROR_MEMORY;
+		}
+		memset(icon_list, 0, icon_header->idCount * sizeof(ICONDIRENTRY));
+
+		for(k = 0; k < icon_header->idCount; k++) {
+			icon_dib = (FIBITMAP*)vPages[k];
+
+			// convert internal format to ICONDIRENTRY
+			// take into account Vista icons whose size is 256x256
+			const BITMAPINFOHEADER *bmih = FreeImage_GetInfoHeader(icon_dib);
+			icon_list[k].bWidth			= (bmih->biWidth > 255)  ? 0 : (BYTE)bmih->biWidth;
+			icon_list[k].bHeight		= (bmih->biHeight > 255) ? 0 : (BYTE)bmih->biHeight;
+			icon_list[k].bReserved		= 0;
+			icon_list[k].wPlanes		= bmih->biPlanes;
+			icon_list[k].wBitCount		= bmih->biBitCount;
+			if( (icon_list[k].wPlanes * icon_list[k].wBitCount) >= 8 ) {
+				icon_list[k].bColorCount = 0;
+			} else {
+				icon_list[k].bColorCount = (BYTE)(1 << (icon_list[k].wPlanes * icon_list[k].wBitCount));
+			}
+			// initial guess (correct only for standard icons)
+			icon_list[k].dwBytesInRes	= CalculateImageSize(icon_dib);
+			icon_list[k].dwImageOffset = CalculateImageOffset(vPages, k);
+		}
+
+		// make a room for icon dir entries, until later update
+		const long directory_start = io->tell_proc(handle);
+		io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
+
+		// write the image bits for each image
+		
+		DWORD dwImageOffset = (DWORD)io->tell_proc(handle);
+
+		for(k = 0; k < icon_header->idCount; k++) {
+			icon_dib = (FIBITMAP*)vPages[k];
+			
+			if((icon_list[k].bWidth == 0) && (icon_list[k].bHeight == 0)) {
+				// Vista icon support
+				FreeImage_SaveToHandle(FIF_PNG, icon_dib, io, handle, PNG_DEFAULT);
+			}
+			else {
+				// standard icon support
+				// see http://msdn.microsoft.com/en-us/library/ms997538.aspx
+				SaveStandardIcon(io, icon_dib, handle);
+			}
+
+			// update ICONDIRENTRY members			
+			DWORD dwBytesInRes = (DWORD)io->tell_proc(handle) - dwImageOffset;
+			icon_list[k].dwImageOffset = dwImageOffset;
+			icon_list[k].dwBytesInRes  = dwBytesInRes;
+			dwImageOffset += dwBytesInRes;
+		}
+
+		// update the icon descriptions
+		const long current_pos = io->tell_proc(handle);
+		io->seek_proc(handle, directory_start, SEEK_SET);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapIconDirEntries(icon_list, icon_header->idCount);
+#endif
+		io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
+		io->seek_proc(handle, current_pos, SEEK_SET);
+
+		free(icon_list);
+
+		// free the vector class
+		for(k = 0; k < icon_header->idCount; k++) {
+			icon_dib = (FIBITMAP*)vPages[k];
+			FreeImage_Unload(icon_dib);
+		}
+
+		return TRUE;
+
+	} catch(const char *text) {
+		// free the vector class
+		for(size_t k = 0; k < vPages.size(); k++) {
+			FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
+			FreeImage_Unload(icon_dib);
+		}
+		FreeImage_OutputMessageProc(s_format_id, text);
+		return FALSE;
+	}
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitICO(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = PageCount;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginIFF.cpp b/files/Source/FreeImage/PluginIFF.cpp
new file mode 100644
index 0000000..ae1f903
--- /dev/null
+++ b/files/Source/FreeImage/PluginIFF.cpp
@@ -0,0 +1,459 @@
+// ==========================================================
+// Deluxe Paint Loader
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Mark Sibly (marksibly@blitzbasic.com)
+// - Aaron Shumate (trek@startreker.com)
+// - 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"
+
+// ----------------------------------------------------------
+//  Internal typedefs and structures
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct {
+    WORD w, h;                    /* raster width & height in pixels */
+    WORD x, y;                    /* position for this image */
+    BYTE nPlanes;                 /* # source bitplanes */
+    BYTE masking;                 /* masking technique */
+    BYTE compression;             /* compression algorithm */
+    BYTE pad1;                    /* UNUSED.  For consistency, put 0 here.*/
+    WORD transparentColor;        /* transparent "color number" */
+    BYTE xAspect, yAspect;        /* aspect ratio, a rational number x/y */
+    WORD pageWidth, pageHeight;   /* source "page" size in pixels */
+} BMHD;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+#ifndef FREEIMAGE_BIGENDIAN
+static void
+SwapHeader(BMHD *header) {
+	SwapShort(&header->w);
+	SwapShort(&header->h);
+	SwapShort(&header->x);
+	SwapShort(&header->y);
+	SwapShort(&header->transparentColor);
+	SwapShort(&header->pageWidth);
+	SwapShort(&header->pageHeight);
+}
+#endif
+
+// ----------------------------------------------------------
+
+/* IFF chunk IDs */
+
+typedef DWORD IFF_ID;
+
+#define MAKE_ID(a, b, c, d)         ((IFF_ID)(a)<<24 | (IFF_ID)(b)<<16 | (IFF_ID)(c)<<8 | (IFF_ID)(d))
+
+#define ID_FORM     MAKE_ID('F', 'O', 'R', 'M')     /* EA IFF 85 group identifier */
+#define ID_CAT      MAKE_ID('C', 'A', 'T', ' ')     /* EA IFF 85 group identifier */
+#define ID_LIST     MAKE_ID('L', 'I', 'S', 'T')     /* EA IFF 85 group identifier */
+#define ID_PROP     MAKE_ID('P', 'R', 'O', 'P')     /* EA IFF 85 group identifier */
+#define ID_END      MAKE_ID('E', 'N', 'D', ' ')     /* unofficial END-of-FORM identifier (see Amiga RKM Devices Ed.3 page 376) */
+
+#define ID_ILBM     MAKE_ID('I', 'L', 'B', 'M')     /* EA IFF 85 raster bitmap form */
+#define ID_DEEP     MAKE_ID('D', 'E', 'E', 'P')     /* Chunky pixel image files (Used in TV Paint) */
+#define ID_RGB8     MAKE_ID('R', 'G', 'B', '8')     /* RGB image forms, Turbo Silver (Impulse) */
+#define ID_RGBN     MAKE_ID('R', 'G', 'B', 'N')     /* RGB image forms, Turbo Silver (Impulse) */
+#define ID_PBM      MAKE_ID('P', 'B', 'M', ' ')     /* 256-color chunky format (DPaint 2 ?) */
+#define ID_ACBM     MAKE_ID('A', 'C', 'B', 'M')     /* Amiga Contiguous Bitmap (AmigaBasic) */
+/* generic */
+#define ID_FVER     MAKE_ID('F', 'V', 'E', 'R')     /* AmigaOS version string */
+#define ID_JUNK     MAKE_ID('J', 'U', 'N', 'K')     /* always ignore this chunk */
+#define ID_ANNO     MAKE_ID('A', 'N', 'N', 'O')     /* EA IFF 85 Generic Annotation chunk */
+#define ID_AUTH     MAKE_ID('A', 'U', 'T', 'H')     /* EA IFF 85 Generic Author chunk */
+#define ID_CHRS     MAKE_ID('C', 'H', 'R', 'S')     /* EA IFF 85 Generic character string chunk */
+#define ID_NAME     MAKE_ID('N', 'A', 'M', 'E')     /* EA IFF 85 Generic Name of art, music, etc. chunk */
+#define ID_TEXT     MAKE_ID('T', 'E', 'X', 'T')     /* EA IFF 85 Generic unformatted ASCII text chunk */
+#define ID_copy     MAKE_ID('(', 'c', ')', ' ')     /* EA IFF 85 Generic Copyright text chunk */
+/* ILBM chunks */
+#define ID_BMHD     MAKE_ID('B', 'M', 'H', 'D')     /* ILBM BitmapHeader */
+#define ID_CMAP     MAKE_ID('C', 'M', 'A', 'P')     /* ILBM 8bit RGB colormap */
+#define ID_GRAB     MAKE_ID('G', 'R', 'A', 'B')     /* ILBM "hotspot" coordiantes */
+#define ID_DEST     MAKE_ID('D', 'E', 'S', 'T')     /* ILBM destination image info */
+#define ID_SPRT     MAKE_ID('S', 'P', 'R', 'T')     /* ILBM sprite identifier */
+#define ID_CAMG     MAKE_ID('C', 'A', 'M', 'G')     /* Amiga viewportmodes */
+#define ID_BODY     MAKE_ID('B', 'O', 'D', 'Y')     /* ILBM image data */
+#define ID_CRNG     MAKE_ID('C', 'R', 'N', 'G')     /* color cycling */
+#define ID_CCRT     MAKE_ID('C', 'C', 'R', 'T')     /* color cycling */
+#define ID_CLUT     MAKE_ID('C', 'L', 'U', 'T')     /* Color Lookup Table chunk */
+#define ID_DPI      MAKE_ID('D', 'P', 'I', ' ')     /* Dots per inch chunk */
+#define ID_DPPV     MAKE_ID('D', 'P', 'P', 'V')     /* DPaint perspective chunk (EA) */
+#define ID_DRNG     MAKE_ID('D', 'R', 'N', 'G')     /* DPaint IV enhanced color cycle chunk (EA) */
+#define ID_EPSF     MAKE_ID('E', 'P', 'S', 'F')     /* Encapsulated Postscript chunk */
+#define ID_CMYK     MAKE_ID('C', 'M', 'Y', 'K')     /* Cyan, Magenta, Yellow, & Black color map (Soft-Logik) */
+#define ID_CNAM     MAKE_ID('C', 'N', 'A', 'M')     /* Color naming chunk (Soft-Logik) */
+#define ID_PCHG     MAKE_ID('P', 'C', 'H', 'G')     /* Line by line palette control information (Sebastiano Vigna) */
+#define ID_PRVW     MAKE_ID('P', 'R', 'V', 'W')     /* A mini duplicate ILBM used for preview (Gary Bonham) */
+#define ID_XBMI     MAKE_ID('X', 'B', 'M', 'I')     /* eXtended BitMap Information (Soft-Logik) */
+#define ID_CTBL     MAKE_ID('C', 'T', 'B', 'L')     /* Newtek Dynamic Ham color chunk */
+#define ID_DYCP     MAKE_ID('D', 'Y', 'C', 'P')     /* Newtek Dynamic Ham chunk */
+#define ID_SHAM     MAKE_ID('S', 'H', 'A', 'M')     /* Sliced HAM color chunk */
+#define ID_ABIT     MAKE_ID('A', 'B', 'I', 'T')     /* ACBM body chunk */
+#define ID_DCOL     MAKE_ID('D', 'C', 'O', 'L')     /* unofficial direct color */
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "IFF";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "IFF Interleaved Bitmap";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "iff,lbm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-iff";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	DWORD type = 0;
+
+	// read chunk type
+	io->read_proc(&type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+	SwapLong(&type);
+#endif
+
+	if(type != ID_FORM)
+		return FALSE;
+		
+	// skip 4 bytes
+	io->read_proc(&type, 4, 1, handle);
+
+	// read chunk type
+	io->read_proc(&type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+	SwapLong(&type);
+#endif
+
+	// File format : ID_PBM = Packed Bitmap, ID_ILBM = Interleaved Bitmap
+	return (type == ID_ILBM) || (type == ID_PBM);
+}
+
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	if (handle != NULL) {
+		FIBITMAP *dib = NULL;
+
+		DWORD type, size;
+
+		io->read_proc(&type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+		SwapLong(&type);
+#endif
+
+		if(type != ID_FORM)
+			return NULL;
+
+		io->read_proc(&size, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+		SwapLong(&size);
+#endif
+
+		io->read_proc(&type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+		SwapLong(&type);
+#endif
+
+		if((type != ID_ILBM) && (type != ID_PBM))
+			return NULL;
+
+		size -= 4;
+
+		unsigned width = 0, height = 0, planes = 0, depth = 0, comp = 0;
+
+		while (size) {
+			DWORD ch_type,ch_size;
+
+			io->read_proc(&ch_type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+			SwapLong(&ch_type);
+#endif
+
+			io->read_proc(&ch_size,4,1,handle );
+#ifndef FREEIMAGE_BIGENDIAN
+			SwapLong(&ch_size);
+#endif
+
+			unsigned ch_end = io->tell_proc(handle) + ch_size;
+
+			if (ch_type == ID_BMHD) {			// Bitmap Header
+				if (dib)
+					FreeImage_Unload(dib);
+
+				BMHD bmhd;
+
+				io->read_proc(&bmhd, sizeof(bmhd), 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+				SwapHeader(&bmhd);
+#endif
+
+				width = bmhd.w;
+				height = bmhd.h;
+				planes = bmhd.nPlanes;
+				comp = bmhd.compression;
+
+				if(bmhd.masking & 1)
+					planes++;	// there is a mask ( 'stencil' )
+
+				if (planes > 8 && planes != 24)
+					return NULL;
+
+				depth = planes > 8 ? 24 : 8;
+
+				if( depth == 24 ) {
+					dib = FreeImage_Allocate(width, height, depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				} else {
+					dib = FreeImage_Allocate(width, height, depth);
+				}
+			} else if (ch_type == ID_CMAP) {	// Palette (Color Map)
+				if (!dib)
+					return NULL;
+
+				RGBQUAD *pal = FreeImage_GetPalette(dib);
+				if(pal != NULL) {
+					unsigned palette_entries = MIN((unsigned)ch_size / 3, FreeImage_GetColorsUsed(dib));
+					for (unsigned k = 0; k < palette_entries; k++) {					
+						io->read_proc(&pal[k].rgbRed, 1, 1, handle );
+						io->read_proc(&pal[k].rgbGreen, 1, 1, handle );
+						io->read_proc(&pal[k].rgbBlue, 1, 1, handle );
+					}
+				}
+			} else if (ch_type == ID_BODY) {
+				if (!dib)
+					return NULL;
+
+				if (type == ID_PBM) {
+					// NON INTERLACED (LBM)
+
+					unsigned line = FreeImage_GetLine(dib) + 1 & ~1;
+					
+					for (unsigned i = 0; i < FreeImage_GetHeight(dib); i++) {
+						BYTE *bits = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - i - 1);
+
+						if (comp == 1) {
+							// use RLE compression
+
+							DWORD number_of_bytes_written = 0;
+							BYTE rle_count;
+							BYTE byte;
+
+							while (number_of_bytes_written < line) {
+								io->read_proc(&rle_count, 1, 1, handle);
+
+								if (rle_count < 128) {
+									for (int k = 0; k < rle_count + 1; k++) {
+										io->read_proc(&byte, 1, 1, handle);
+
+										bits[number_of_bytes_written++] += byte;
+									}
+								} else if (rle_count > 128) {
+									io->read_proc(&byte, 1, 1, handle);
+
+									for (int k = 0; k < 257 - rle_count; k++) {
+										bits[number_of_bytes_written++] += byte;
+									}
+								}
+							}
+						} else {
+							// don't use compression
+
+							io->read_proc(bits, line, 1, handle);
+						}
+					}
+
+					return dib;
+				} else {
+					// INTERLACED (ILBM)
+
+					unsigned pixel_size = depth/8;
+					unsigned n_width=(width+15)&~15;
+					unsigned plane_size = n_width/8;
+					unsigned src_size = plane_size * planes;
+					BYTE *src = (BYTE*)malloc(src_size);
+					BYTE *dest = FreeImage_GetBits(dib);
+
+					dest += FreeImage_GetPitch(dib) * height;
+
+					for (unsigned y = 0; y < height; y++) {
+						dest -= FreeImage_GetPitch(dib);
+
+						// read all planes in one hit,
+						// 'coz PSP compresses across planes...
+
+						if (comp) {
+							// unpacker algorithm
+
+							for(unsigned x = 0; x < src_size;) {
+								// read the next source byte into t
+								signed char t = 0;
+								io->read_proc(&t, 1, 1, handle);
+								
+								if (t >= 0) {
+									// t = [0..127] => copy the next t+1 bytes literally
+									unsigned size_to_read = t + 1;
+
+									if((size_to_read + x) > src_size) {
+										// sanity check for buffer overruns 
+										size_to_read = src_size - x;
+										io->read_proc(src + x, size_to_read, 1, handle);
+										x += (t + 1);
+									} else {
+										io->read_proc(src + x, size_to_read, 1, handle);
+										x += size_to_read;
+									}
+								} else if (t != -128) {
+									// t = [-1..-127]  => replicate the next byte -t+1 times
+									BYTE b = 0;
+									io->read_proc(&b, 1, 1, handle);
+									unsigned size_to_copy = (unsigned)(-(int)t + 1);
+
+									if((size_to_copy + x) > src_size) {
+										// sanity check for buffer overruns 
+										size_to_copy = src_size - x;
+										memset(src + x, b, size_to_copy);
+										x += (unsigned)(-(int)t + 1);
+									} else {
+										memset(src + x, b, size_to_copy);
+										x += size_to_copy;
+									}
+								}
+								// t = -128 => noop
+							}
+						} else {
+							io->read_proc(src, src_size, 1, handle);
+						}
+
+						// lazy planar->chunky...
+
+						for (unsigned x = 0; x < width; x++) {
+							for (unsigned n = 0; n < planes; n++) {
+								BYTE bit = (BYTE)( src[n * plane_size + (x / 8)] >> ((x^7) & 7) );
+
+								dest[x * pixel_size + (n / 8)] |= (bit & 1) << (n & 7);
+							}
+						}
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+						if (depth == 24) {
+							for (unsigned x = 0; x < width; ++x){
+								INPLACESWAP(dest[x * 3], dest[x * 3 + 2]);
+							}
+						}
+#endif
+					}
+
+					free(src);
+
+					return dib;
+				}
+			}
+
+			// Every odd-length chunk is followed by a 0 pad byte.  This pad
+			//  byte is not counted in ch_size.
+			if (ch_size & 1) {
+				ch_size++;
+				ch_end++;
+			}
+
+			io->seek_proc(handle, ch_end - io->tell_proc(handle), SEEK_CUR);
+
+			size -= ch_size + 8;
+		}
+
+		if (dib)
+			FreeImage_Unload(dib);
+	}
+
+	return 0;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitIFF(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/files/Source/FreeImage/PluginJ2K.cpp b/files/Source/FreeImage/PluginJ2K.cpp
new file mode 100644
index 0000000..edddf1d
--- /dev/null
+++ b/files/Source/FreeImage/PluginJ2K.cpp
@@ -0,0 +1,328 @@
+// ==========================================================
+// JPEG2000 J2K codestream Loader and Writer
+//
+// 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"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/**
+OpenJPEG Error callback 
+*/
+static void j2k_error_callback(const char *msg, void *client_data) {
+	FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg);
+}
+/**
+OpenJPEG Warning callback 
+*/
+static void j2k_warning_callback(const char *msg, void *client_data) {
+	FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg);
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "J2K";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "JPEG-2000 codestream";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "j2k,j2c";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/j2k";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE jpc_signature[] = { 0xFF, 0x4F };
+	BYTE signature[2] = { 0, 0 };
+
+	long tell = io->tell_proc(handle);
+	io->read_proc(signature, 1, sizeof(jpc_signature), handle);
+	io->seek_proc(handle, tell, SEEK_SET);
+
+	return (memcmp(jpc_signature, signature, sizeof(jpc_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+		(depth == 8) ||
+		(depth == 24) || 
+		(depth == 32)
+	);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (
+		(type == FIT_BITMAP)  ||
+		(type == FIT_UINT16)  ||
+		(type == FIT_RGB16) || 
+		(type == FIT_RGBA16)
+	);
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	// create the stream wrapper
+	J2KFIO_t *fio = opj_freeimage_stream_create(io, handle, read);
+	return fio;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+	// destroy the stream wrapper
+	J2KFIO_t *fio = (J2KFIO_t*)data;
+	opj_freeimage_stream_destroy(fio);
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	J2KFIO_t *fio = (J2KFIO_t*)data;
+	if (handle && fio) {
+		opj_codec_t *d_codec = NULL;	// handle to a decompressor
+		opj_dparameters_t parameters;	// decompression parameters
+		opj_image_t *image = NULL;		// decoded image 
+
+		FIBITMAP *dib = NULL;
+
+		// check the file format
+		if(!Validate(io, handle)) {
+			return NULL;
+		}
+
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+		// get the OpenJPEG stream
+		opj_stream_t *d_stream = fio->stream;
+
+		// set decoding parameters to default values 
+		opj_set_default_decoder_parameters(&parameters);
+
+		try {
+			// decode the JPEG-2000 codestream
+
+			// get a decoder handle
+			d_codec = opj_create_decompress(OPJ_CODEC_J2K);
+			
+			// configure the event callbacks
+			// catch events using our callbacks (no local context needed here)
+			opj_set_info_handler(d_codec, NULL, NULL);
+			opj_set_warning_handler(d_codec, j2k_warning_callback, NULL);
+			opj_set_error_handler(d_codec, j2k_error_callback, NULL);
+
+			// setup the decoder decoding parameters using user parameters
+			if( !opj_setup_decoder(d_codec, &parameters) ) {
+				throw "Failed to setup the decoder\n";
+			}
+			
+			// read the main header of the codestream and if necessary the JP2 boxes
+			if( !opj_read_header(d_stream, d_codec, &image)) {
+				throw "Failed to read the header\n";
+			}
+
+			// --- header only mode
+
+			if (header_only) {
+				// create output image 
+				dib = J2KImageToFIBITMAP(s_format_id, image, header_only);
+				if(!dib) {
+					throw "Failed to import JPEG2000 image";
+				}
+				// clean-up and return header data
+				opj_destroy_codec(d_codec);
+				opj_image_destroy(image);
+				return dib;
+			}
+
+			// decode the stream and fill the image structure 
+			if( !( opj_decode(d_codec, d_stream, image) && opj_end_decompress(d_codec, d_stream) ) ) {
+				throw "Failed to decode image!\n";
+			}
+
+			// free the codec context
+			opj_destroy_codec(d_codec);
+			d_codec = NULL;
+
+			// create output image 
+			dib = J2KImageToFIBITMAP(s_format_id, image, header_only);
+			if(!dib) {
+				throw "Failed to import JPEG2000 image";
+			}
+
+			// free image data structure
+			opj_image_destroy(image);
+
+			return dib;
+
+		} catch (const char *text) {
+			if(dib) {
+				FreeImage_Unload(dib);
+			}
+			// free remaining structures
+			opj_destroy_codec(d_codec);
+			opj_image_destroy(image);
+
+			FreeImage_OutputMessageProc(s_format_id, text);
+
+			return NULL;
+		}
+	}
+
+	return NULL;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	J2KFIO_t *fio = (J2KFIO_t*)data;
+	if (dib && handle && fio) {
+		BOOL bSuccess;
+		opj_codec_t *c_codec = NULL;	// handle to a compressor
+		opj_cparameters_t parameters;	// compression parameters
+		opj_image_t *image = NULL;		// image to encode
+
+		// get the OpenJPEG stream
+		opj_stream_t *c_stream = fio->stream;
+
+		// set encoding parameters to default values
+		opj_set_default_encoder_parameters(&parameters);
+
+		try {
+			parameters.tcp_numlayers = 0;
+			// if no rate entered, apply a 16:1 rate by default
+			if(flags == J2K_DEFAULT) {
+				parameters.tcp_rates[0] = (float)16;
+			} else {
+				// for now, the flags parameter is only used to specify the rate
+				parameters.tcp_rates[0] = (float)(flags & 0x3FF);
+			}
+			parameters.tcp_numlayers++;
+			parameters.cp_disto_alloc = 1;
+
+			// convert the dib to a OpenJPEG image
+			image = FIBITMAPToJ2KImage(s_format_id, dib, &parameters);
+			if(!image) {
+				return FALSE;
+			}
+
+			// decide if MCT should be used
+			parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0;
+
+			// encode the destination image
+
+			// get a J2K compressor handle
+			c_codec = opj_create_compress(OPJ_CODEC_J2K);
+
+			// configure the event callbacks
+			// catch events using our callbacks (no local context needed here)
+			opj_set_info_handler(c_codec, NULL, NULL);
+			opj_set_warning_handler(c_codec, j2k_warning_callback, NULL);
+			opj_set_error_handler(c_codec, j2k_error_callback, NULL);
+
+			// setup the encoder parameters using the current image and using user parameters
+			opj_setup_encoder(c_codec, &parameters, image);
+
+			// encode the image
+			bSuccess = opj_start_compress(c_codec, image, c_stream);
+			if(bSuccess) {
+				bSuccess = bSuccess && opj_encode(c_codec, c_stream);
+				if(bSuccess) {
+					bSuccess = bSuccess && opj_end_compress(c_codec, c_stream);
+				}
+			}
+			if (!bSuccess) {
+				throw "Failed to encode image";
+			}
+
+			// free remaining compression structures
+			opj_destroy_codec(c_codec);
+			
+			// free image data
+			opj_image_destroy(image);
+
+			return TRUE;
+
+		} catch (const char *text) {
+			if(c_codec) opj_destroy_codec(c_codec);
+			if(image) opj_image_destroy(image);
+			FreeImage_OutputMessageProc(s_format_id, text);
+			return FALSE;
+		}
+	}
+
+	return FALSE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitJ2K(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/files/Source/FreeImage/PluginJNG.cpp b/files/Source/FreeImage/PluginJNG.cpp
new file mode 100644
index 0000000..817e731
--- /dev/null
+++ b/files/Source/FreeImage/PluginJNG.cpp
@@ -0,0 +1,162 @@
+// ==========================================================
+// JNG loader
+//
+// Design and implementation by
+// - Herve 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"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ----------------------------------------------------------
+
+#define JNG_SIGNATURE_SIZE 8	// size of the signature
+
+// ----------------------------------------------------------
+
+// ----------------------------------------------------------
+//   mng interface (see MNGHelper.cpp)
+// ----------------------------------------------------------
+
+FIBITMAP* mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, int flags = 0);
+BOOL mng_WriteJNG(int format_id, FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int flags);
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "JNG";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "JPEG Network Graphics";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "jng";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-mng";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE jng_signature[8] = { 139, 74, 78, 71, 13, 10, 26, 10 };
+	BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	io->read_proc(&signature, 1, JNG_SIGNATURE_SIZE, handle);
+
+	return (memcmp(jng_signature, signature, JNG_SIGNATURE_SIZE) == 0) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+			(depth == 8) ||
+			(depth == 24) ||
+			(depth == 32)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	return NULL;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	long offset = JNG_SIGNATURE_SIZE;	// move to skip first 8 bytes of signature
+
+	// check the signature (8 bytes)
+	if(Validate(io, handle) == FALSE) {
+		return NULL;
+	}
+	
+	// parse chunks and decode a jng or mng bitmap
+	return mng_ReadChunks(s_format_id, io, handle, offset, flags);
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+
+	return mng_WriteJNG(s_format_id, io, dib, handle, flags);
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitJNG(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginJP2.cpp b/files/Source/FreeImage/PluginJP2.cpp
new file mode 100644
index 0000000..531fe7c
--- /dev/null
+++ b/files/Source/FreeImage/PluginJP2.cpp
@@ -0,0 +1,328 @@
+// ==========================================================
+// JPEG2000 JP2 file format Loader and Writer
+//
+// 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"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/**
+OpenJPEG Error callback 
+*/
+static void jp2_error_callback(const char *msg, void *client_data) {
+	FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg);
+}
+/**
+OpenJPEG Warning callback 
+*/
+static void jp2_warning_callback(const char *msg, void *client_data) {
+	FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg);
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "JP2";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "JPEG-2000 File Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "jp2";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/jp2";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE jp2_signature[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A };
+	BYTE signature[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	long tell = io->tell_proc(handle);
+	io->read_proc(signature, 1, sizeof(jp2_signature), handle);
+	io->seek_proc(handle, tell, SEEK_SET);
+
+	return (memcmp(jp2_signature, signature, sizeof(jp2_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+		(depth == 8) ||
+		(depth == 24) || 
+		(depth == 32)
+	);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (
+		(type == FIT_BITMAP)  ||
+		(type == FIT_UINT16)  ||
+		(type == FIT_RGB16) || 
+		(type == FIT_RGBA16)
+	);
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	// create the stream wrapper
+	J2KFIO_t *fio = opj_freeimage_stream_create(io, handle, read);
+	return fio;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+	// destroy the stream wrapper
+	J2KFIO_t *fio = (J2KFIO_t*)data;
+	opj_freeimage_stream_destroy(fio);
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	J2KFIO_t *fio = (J2KFIO_t*)data;
+	if (handle && fio) {
+		opj_codec_t *d_codec = NULL;	// handle to a decompressor
+		opj_dparameters_t parameters;	// decompression parameters
+		opj_image_t *image = NULL;		// decoded image 
+
+		FIBITMAP *dib = NULL;
+
+		// check the file format
+		if(!Validate(io, handle)) {
+			return NULL;
+		}
+		
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+		// get the OpenJPEG stream
+		opj_stream_t *d_stream = fio->stream;
+
+		// set decoding parameters to default values 
+		opj_set_default_decoder_parameters(&parameters);
+
+		try {
+			// decode the JPEG-2000 file
+
+			// get a decoder handle
+			d_codec = opj_create_decompress(OPJ_CODEC_JP2);
+			
+			// configure the event callbacks
+			// catch events using our callbacks (no local context needed here)
+			opj_set_info_handler(d_codec, NULL, NULL);
+			opj_set_warning_handler(d_codec, jp2_warning_callback, NULL);
+			opj_set_error_handler(d_codec, jp2_error_callback, NULL);
+
+			// setup the decoder decoding parameters using user parameters
+			if( !opj_setup_decoder(d_codec, &parameters) ) {
+				throw "Failed to setup the decoder\n";
+			}
+			
+			// read the main header of the codestream and if necessary the JP2 boxes
+			if( !opj_read_header(d_stream, d_codec, &image)) {
+				throw "Failed to read the header\n";
+			}
+
+			// --- header only mode
+
+			if (header_only) {
+				// create output image 
+				dib = J2KImageToFIBITMAP(s_format_id, image, header_only);
+				if(!dib) {
+					throw "Failed to import JPEG2000 image";
+				}
+				// clean-up and return header data
+				opj_destroy_codec(d_codec);
+				opj_image_destroy(image);
+				return dib;
+			}
+
+			// decode the stream and fill the image structure 
+			if( !( opj_decode(d_codec, d_stream, image) && opj_end_decompress(d_codec, d_stream) ) ) {
+				throw "Failed to decode image!\n";
+			}
+
+			// free the codec context
+			opj_destroy_codec(d_codec);
+			d_codec = NULL;
+
+			// create output image 
+			dib = J2KImageToFIBITMAP(s_format_id, image, header_only);
+			if(!dib) {
+				throw "Failed to import JPEG2000 image";
+			}
+
+			// free image data structure
+			opj_image_destroy(image);
+
+			return dib;
+
+		} catch (const char *text) {
+			if(dib) {
+				FreeImage_Unload(dib);
+			}
+			// free remaining structures
+			opj_destroy_codec(d_codec);
+			opj_image_destroy(image);
+
+			FreeImage_OutputMessageProc(s_format_id, text);
+
+			return NULL;
+		}
+	}
+
+	return NULL;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	J2KFIO_t *fio = (J2KFIO_t*)data;
+	if (dib && handle && fio) {
+		BOOL bSuccess;
+		opj_codec_t *c_codec = NULL;	// handle to a compressor
+		opj_cparameters_t parameters;	// compression parameters
+		opj_image_t *image = NULL;		// image to encode
+
+		// get the OpenJPEG stream
+		opj_stream_t *c_stream = fio->stream;
+
+		// set encoding parameters to default values
+		opj_set_default_encoder_parameters(&parameters);
+
+		try {
+			parameters.tcp_numlayers = 0;
+			// if no rate entered, apply a 16:1 rate by default
+			if(flags == JP2_DEFAULT) {
+				parameters.tcp_rates[0] = (float)16;
+			} else {
+				// for now, the flags parameter is only used to specify the rate
+				parameters.tcp_rates[0] = (float)(flags & 0x3FF);
+			}
+			parameters.tcp_numlayers++;
+			parameters.cp_disto_alloc = 1;
+
+			// convert the dib to a OpenJPEG image
+			image = FIBITMAPToJ2KImage(s_format_id, dib, &parameters);
+			if(!image) {
+				return FALSE;
+			}
+
+			// decide if MCT should be used
+			parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0;
+
+			// encode the destination image
+
+			// get a JP2 compressor handle
+			c_codec = opj_create_compress(OPJ_CODEC_JP2);
+
+			// configure the event callbacks
+			// catch events using our callbacks (no local context needed here)
+			opj_set_info_handler(c_codec, NULL, NULL);
+			opj_set_warning_handler(c_codec, jp2_warning_callback, NULL);
+			opj_set_error_handler(c_codec, jp2_error_callback, NULL);
+
+			// setup the encoder parameters using the current image and using user parameters
+			opj_setup_encoder(c_codec, &parameters, image);
+
+			// encode the image
+			bSuccess = opj_start_compress(c_codec, image, c_stream);
+			if(bSuccess) {
+				bSuccess = bSuccess && opj_encode(c_codec, c_stream);
+				if(bSuccess) {
+					bSuccess = bSuccess && opj_end_compress(c_codec, c_stream);
+				}
+			}
+			if (!bSuccess) {
+				throw "Failed to encode image";
+			}
+
+			// free remaining compression structures
+			opj_destroy_codec(c_codec);
+			
+			// free image data
+			opj_image_destroy(image);
+
+			return TRUE;
+
+		} catch (const char *text) {
+			if(c_codec) opj_destroy_codec(c_codec);
+			if(image) opj_image_destroy(image);
+			FreeImage_OutputMessageProc(s_format_id, text);
+			return FALSE;
+		}
+	}
+
+	return FALSE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitJP2(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/files/Source/FreeImage/PluginJPEG.cpp b/files/Source/FreeImage/PluginJPEG.cpp
new file mode 100644
index 0000000..9bb2c57
--- /dev/null
+++ b/files/Source/FreeImage/PluginJPEG.cpp
@@ -0,0 +1,1716 @@
+// ==========================================================
+// JPEG Loader and writer
+// Based on code developed by The Independent JPEG Group
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Jan L. Nauta (jln@magentammt.com)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Karl-Heinz Bussian (khbussian@moss.de)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jascha Wetzel (jascha@mainia.de)
+// - 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
+
+extern "C" {
+#define XMD_H
+#undef FAR
+#include <setjmp.h>
+
+// Building SketchUp on Linux wants to use the third_party/jpeg
+// jpeg code instead of the jpeg contained under free_image because
+// the escher code that we use itself uses third_party/jpeg and we
+// crash with conflicting jpeg library code.
+#ifdef USE_THIRD_PARTY_JPEG
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jerror.h"
+#else
+#include "../LibJPEG/jinclude.h"
+#include "../LibJPEG/jpeglib.h"
+#include "../LibJPEG/jerror.h"
+#endif
+}
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+#include "../Metadata/FreeImageTag.h"
+
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ----------------------------------------------------------
+//   Constant declarations
+// ----------------------------------------------------------
+
+#define INPUT_BUF_SIZE  4096	// choose an efficiently fread'able size 
+#define OUTPUT_BUF_SIZE 4096    // choose an efficiently fwrite'able size
+
+#define EXIF_MARKER		(JPEG_APP0+1)	// EXIF marker / Adobe XMP marker
+#define ICC_MARKER		(JPEG_APP0+2)	// ICC profile marker
+#define IPTC_MARKER		(JPEG_APP0+13)	// IPTC marker / BIM marker 
+
+#define ICC_HEADER_SIZE 14				// size of non-profile data in APP2
+#define MAX_BYTES_IN_MARKER 65533L		// maximum data length of a JPEG marker
+#define MAX_DATA_BYTES_IN_MARKER 65519L	// maximum data length of a JPEG APP2 marker
+
+#define MAX_JFXX_THUMB_SIZE (MAX_BYTES_IN_MARKER - 5 - 1)
+
+#define JFXX_TYPE_JPEG 	0x10	// JFIF extension marker: JPEG-compressed thumbnail image
+#define JFXX_TYPE_8bit 	0x11	// JFIF extension marker: palette thumbnail image
+#define JFXX_TYPE_24bit	0x13	// JFIF extension marker: RGB thumbnail image
+
+// ----------------------------------------------------------
+//   Typedef declarations
+// ----------------------------------------------------------
+
+typedef struct tagErrorManager {
+	/// "public" fields
+	struct jpeg_error_mgr pub;
+	/// for return to caller
+	jmp_buf setjmp_buffer;
+} ErrorManager;
+
+typedef struct tagSourceManager {
+	/// public fields
+	struct jpeg_source_mgr pub;
+	/// source stream
+	fi_handle infile;
+	FreeImageIO *m_io;
+	/// start of buffer
+	JOCTET * buffer;
+	/// have we gotten any data yet ?
+	boolean start_of_file;
+} SourceManager;
+
+typedef struct tagDestinationManager {
+	/// public fields
+	struct jpeg_destination_mgr pub;
+	/// destination stream
+	fi_handle outfile;
+	FreeImageIO *m_io;
+	/// start of buffer
+	JOCTET * buffer;
+} DestinationManager;
+
+typedef SourceManager*		freeimage_src_ptr;
+typedef DestinationManager* freeimage_dst_ptr;
+typedef ErrorManager*		freeimage_error_ptr;
+
+// ----------------------------------------------------------
+//   Error handling
+// ----------------------------------------------------------
+
+/** Fatal errors (print message and exit) */
+static inline void
+JPEG_EXIT(j_common_ptr cinfo, int code) {
+	freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
+	error_ptr->pub.msg_code = code;
+	error_ptr->pub.error_exit(cinfo);
+}
+
+/** Nonfatal errors (we can keep going, but the data is probably corrupt) */
+static inline void
+JPEG_WARNING(j_common_ptr cinfo, int code) {
+	freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
+	error_ptr->pub.msg_code = code;
+	error_ptr->pub.emit_message(cinfo, -1);
+}
+
+/**
+	Receives control for a fatal error.  Information sufficient to
+	generate the error message has been stored in cinfo->err; call
+	output_message to display it.  Control must NOT return to the caller;
+	generally this routine will exit() or longjmp() somewhere.
+*/
+METHODDEF(void)
+jpeg_error_exit (j_common_ptr cinfo) {
+	freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
+
+	// always display the message
+	error_ptr->pub.output_message(cinfo);
+
+	// allow JPEG with unknown markers
+	if(error_ptr->pub.msg_code != JERR_UNKNOWN_MARKER) {
+	
+		// let the memory manager delete any temp files before we die
+		jpeg_destroy(cinfo);
+		
+		// return control to the setjmp point
+		longjmp(error_ptr->setjmp_buffer, 1);		
+	}
+}
+
+/**
+	Actual output of any JPEG message.  Note that this method does not know
+	how to generate a message, only where to send it.
+*/
+METHODDEF(void)
+jpeg_output_message (j_common_ptr cinfo) {
+	char buffer[JMSG_LENGTH_MAX];
+	freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
+
+	// create the message
+	error_ptr->pub.format_message(cinfo, buffer);
+	// send it to user's message proc
+	FreeImage_OutputMessageProc(s_format_id, buffer);
+}
+
+// ----------------------------------------------------------
+//   Destination manager
+// ----------------------------------------------------------
+
+/**
+	Initialize destination.  This is called by jpeg_start_compress()
+	before any data is actually written. It must initialize
+	next_output_byte and free_in_buffer. free_in_buffer must be
+	initialized to a positive value.
+*/
+METHODDEF(void)
+init_destination (j_compress_ptr cinfo) {
+	freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
+
+	dest->buffer = (JOCTET *)
+	  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  OUTPUT_BUF_SIZE * sizeof(JOCTET));
+
+	dest->pub.next_output_byte = dest->buffer;
+	dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+}
+
+/**
+	This is called whenever the buffer has filled (free_in_buffer
+	reaches zero). In typical applications, it should write out the
+	*entire* buffer (use the saved start address and buffer length;
+	ignore the current state of next_output_byte and free_in_buffer).
+	Then reset the pointer & count to the start of the buffer, and
+	return TRUE indicating that the buffer has been dumped.
+	free_in_buffer must be set to a positive value when TRUE is
+	returned.  A FALSE return should only be used when I/O suspension is
+	desired.
+*/
+METHODDEF(boolean)
+empty_output_buffer (j_compress_ptr cinfo) {
+	freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
+
+	if (dest->m_io->write_proc(dest->buffer, 1, OUTPUT_BUF_SIZE, dest->outfile) != OUTPUT_BUF_SIZE) {
+		// let the memory manager delete any temp files before we die
+		jpeg_destroy((j_common_ptr)cinfo);
+
+		JPEG_EXIT((j_common_ptr)cinfo, JERR_FILE_WRITE);
+	}
+
+	dest->pub.next_output_byte = dest->buffer;
+	dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+
+	return TRUE;
+}
+
+/**
+	Terminate destination --- called by jpeg_finish_compress() after all
+	data has been written.  In most applications, this must flush any
+	data remaining in the buffer.  Use either next_output_byte or
+	free_in_buffer to determine how much data is in the buffer.
+*/
+METHODDEF(void)
+term_destination (j_compress_ptr cinfo) {
+	freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
+
+	size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
+
+	// write any data remaining in the buffer
+
+	if (datacount > 0) {
+		if (dest->m_io->write_proc(dest->buffer, 1, (unsigned int)datacount, dest->outfile) != datacount) {
+			// let the memory manager delete any temp files before we die
+			jpeg_destroy((j_common_ptr)cinfo);
+			
+			JPEG_EXIT((j_common_ptr)cinfo, JERR_FILE_WRITE);
+		}
+	}
+}
+
+// ----------------------------------------------------------
+//   Source manager
+// ----------------------------------------------------------
+
+/**
+	Initialize source.  This is called by jpeg_read_header() before any
+	data is actually read. Unlike init_destination(), it may leave
+	bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
+	will occur immediately).
+*/
+METHODDEF(void)
+init_source (j_decompress_ptr cinfo) {
+	freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src;
+
+	/* We reset the empty-input-file flag for each image,
+ 	 * but we don't clear the input buffer.
+	 * This is correct behavior for reading a series of images from one source.
+	*/
+
+	src->start_of_file = TRUE;
+}
+
+/**
+	This is called whenever bytes_in_buffer has reached zero and more
+	data is wanted.  In typical applications, it should read fresh data
+	into the buffer (ignoring the current state of next_input_byte and
+	bytes_in_buffer), reset the pointer & count to the start of the
+	buffer, and return TRUE indicating that the buffer has been reloaded.
+	It is not necessary to fill the buffer entirely, only to obtain at
+	least one more byte.  bytes_in_buffer MUST be set to a positive value
+	if TRUE is returned.  A FALSE return should only be used when I/O
+	suspension is desired.
+*/
+METHODDEF(boolean)
+fill_input_buffer (j_decompress_ptr cinfo) {
+	freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src;
+
+	size_t nbytes = src->m_io->read_proc(src->buffer, 1, INPUT_BUF_SIZE, src->infile);
+
+	if (nbytes <= 0) {
+		if (src->start_of_file)	{
+			// treat empty input file as fatal error
+
+			// let the memory manager delete any temp files before we die
+			jpeg_destroy((j_common_ptr)cinfo);
+
+			JPEG_EXIT((j_common_ptr)cinfo, JERR_INPUT_EMPTY);
+		}
+
+		JPEG_WARNING((j_common_ptr)cinfo, JWRN_JPEG_EOF);
+
+		/* Insert a fake EOI marker */
+
+		src->buffer[0] = (JOCTET) 0xFF;
+		src->buffer[1] = (JOCTET) JPEG_EOI;
+
+		nbytes = 2;
+	}
+
+	src->pub.next_input_byte = src->buffer;
+	src->pub.bytes_in_buffer = nbytes;
+	src->start_of_file = FALSE;
+
+	return TRUE;
+}
+
+/**
+	Skip num_bytes worth of data.  The buffer pointer and count should
+	be advanced over num_bytes input bytes, refilling the buffer as
+	needed. This is used to skip over a potentially large amount of
+	uninteresting data (such as an APPn marker). In some applications
+	it may be possible to optimize away the reading of the skipped data,
+	but it's not clear that being smart is worth much trouble; large
+	skips are uncommon.  bytes_in_buffer may be zero on return.
+	A zero or negative skip count should be treated as a no-op.
+*/
+METHODDEF(void)
+skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
+	freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src;
+
+	/* Just a dumb implementation for now.  Could use fseek() except
+     * it doesn't work on pipes.  Not clear that being smart is worth
+	 * any trouble anyway --- large skips are infrequent.
+	*/
+
+	if (num_bytes > 0) {
+		while (num_bytes > (long) src->pub.bytes_in_buffer) {
+		  num_bytes -= (long) src->pub.bytes_in_buffer;
+
+		  (void) fill_input_buffer(cinfo);
+
+		  /* note we assume that fill_input_buffer will never return FALSE,
+		   * so suspension need not be handled.
+		   */
+		}
+
+		src->pub.next_input_byte += (size_t) num_bytes;
+		src->pub.bytes_in_buffer -= (size_t) num_bytes;
+	}
+}
+
+/**
+	Terminate source --- called by jpeg_finish_decompress
+	after all data has been read.  Often a no-op.
+
+	NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+	application must deal with any cleanup that should happen even
+	for error exit.
+*/
+METHODDEF(void)
+term_source (j_decompress_ptr cinfo) {
+  // no work necessary here
+}
+
+// ----------------------------------------------------------
+//   Source manager & Destination manager setup
+// ----------------------------------------------------------
+
+/**
+	Prepare for input from a stdio stream.
+	The caller must have already opened the stream, and is responsible
+	for closing it after finishing decompression.
+*/
+GLOBAL(void)
+jpeg_freeimage_src (j_decompress_ptr cinfo, fi_handle infile, FreeImageIO *io) {
+	freeimage_src_ptr src;
+
+	// allocate memory for the buffer. is released automatically in the end
+
+	if (cinfo->src == NULL) {
+		cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
+			((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(SourceManager));
+
+		src = (freeimage_src_ptr) cinfo->src;
+
+		src->buffer = (JOCTET *) (*cinfo->mem->alloc_small)
+			((j_common_ptr) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof(JOCTET));
+	}
+
+	// initialize the jpeg pointer struct with pointers to functions
+
+	src = (freeimage_src_ptr) cinfo->src;
+	src->pub.init_source = init_source;
+	src->pub.fill_input_buffer = fill_input_buffer;
+	src->pub.skip_input_data = skip_input_data;
+	src->pub.resync_to_restart = jpeg_resync_to_restart; // use default method 
+	src->pub.term_source = term_source;
+	src->infile = infile;
+	src->m_io = io;
+	src->pub.bytes_in_buffer = 0;		// forces fill_input_buffer on first read 
+	src->pub.next_input_byte = NULL;	// until buffer loaded 
+}
+
+/**
+	Prepare for output to a stdio stream.
+	The caller must have already opened the stream, and is responsible
+	for closing it after finishing compression.
+*/
+GLOBAL(void)
+jpeg_freeimage_dst (j_compress_ptr cinfo, fi_handle outfile, FreeImageIO *io) {
+	freeimage_dst_ptr dest;
+
+	if (cinfo->dest == NULL) {
+		cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small)
+			((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(DestinationManager));
+	}
+
+	dest = (freeimage_dst_ptr) cinfo->dest;
+	dest->pub.init_destination = init_destination;
+	dest->pub.empty_output_buffer = empty_output_buffer;
+	dest->pub.term_destination = term_destination;
+	dest->outfile = outfile;
+	dest->m_io = io;
+}
+
+// ----------------------------------------------------------
+//   Special markers read functions
+// ----------------------------------------------------------
+
+/**
+	Read JPEG_COM marker (comment)
+*/
+static BOOL 
+jpeg_read_comment(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+	size_t length = datalen;
+	BYTE *profile = (BYTE*)dataptr;
+
+	// read the comment
+	char *value = (char*)malloc((length + 1) * sizeof(char));
+	if(value == NULL) return FALSE;
+	memcpy(value, profile, length);
+	value[length] = '\0';
+
+	// create a tag
+	FITAG *tag = FreeImage_CreateTag();
+	if(tag) {
+		unsigned int count = (unsigned int)length + 1;	// includes the null value
+
+		FreeImage_SetTagID(tag, JPEG_COM);
+		FreeImage_SetTagKey(tag, "Comment");
+		FreeImage_SetTagLength(tag, count);
+		FreeImage_SetTagCount(tag, count);
+		FreeImage_SetTagType(tag, FIDT_ASCII);
+		FreeImage_SetTagValue(tag, value);
+		
+		// store the tag
+		FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
+
+		// destroy the tag
+		FreeImage_DeleteTag(tag);
+	}
+
+	free(value);
+
+	return TRUE;
+}
+
+/** 
+	Read JPEG_APP2 marker (ICC profile)
+*/
+
+/**
+Handy subroutine to test whether a saved marker is an ICC profile marker.
+*/
+static BOOL 
+marker_is_icc(jpeg_saved_marker_ptr marker) {
+    // marker identifying string "ICC_PROFILE" (null-terminated)
+	const BYTE icc_signature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00 };
+
+	if(marker->marker == ICC_MARKER) {
+		// verify the identifying string
+		if(marker->data_length >= ICC_HEADER_SIZE) {
+			if(memcmp(icc_signature, marker->data, sizeof(icc_signature)) == 0) {
+				return TRUE;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+/**
+  See if there was an ICC profile in the JPEG file being read;
+  if so, reassemble and return the profile data.
+
+  TRUE is returned if an ICC profile was found, FALSE if not.
+  If TRUE is returned, *icc_data_ptr is set to point to the
+  returned data, and *icc_data_len is set to its length.
+  
+  IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
+  and must be freed by the caller with free() when the caller no longer
+  needs it.  (Alternatively, we could write this routine to use the
+  IJG library's memory allocator, so that the data would be freed implicitly
+  at jpeg_finish_decompress() time.  But it seems likely that many apps
+  will prefer to have the data stick around after decompression finishes.)
+  
+  NOTE: if the file contains invalid ICC APP2 markers, we just silently
+  return FALSE.  You might want to issue an error message instead.
+*/
+static BOOL 
+jpeg_read_icc_profile(j_decompress_ptr cinfo, JOCTET **icc_data_ptr, unsigned *icc_data_len) {
+	jpeg_saved_marker_ptr marker;
+	int num_markers = 0;
+	int seq_no;
+	JOCTET *icc_data;
+	unsigned total_length;
+
+	const int MAX_SEQ_NO = 255;			// sufficient since marker numbers are bytes
+	BYTE marker_present[MAX_SEQ_NO+1];	// 1 if marker found
+	unsigned data_length[MAX_SEQ_NO+1];	// size of profile data in marker
+	unsigned data_offset[MAX_SEQ_NO+1];	// offset for data in marker
+	
+	*icc_data_ptr = NULL;		// avoid confusion if FALSE return
+	*icc_data_len = 0;
+	
+	/**
+	this first pass over the saved markers discovers whether there are
+	any ICC markers and verifies the consistency of the marker numbering.
+	*/
+	
+	memset(marker_present, 0, (MAX_SEQ_NO + 1));
+	
+	for(marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+		if (marker_is_icc(marker)) {
+			if (num_markers == 0) {
+				// number of markers
+				num_markers = GETJOCTET(marker->data[13]);
+			}
+			else if (num_markers != GETJOCTET(marker->data[13])) {
+				return FALSE;		// inconsistent num_markers fields 
+			}
+			// sequence number
+			seq_no = GETJOCTET(marker->data[12]);
+			if (seq_no <= 0 || seq_no > num_markers) {
+				return FALSE;		// bogus sequence number 
+			}
+			if (marker_present[seq_no]) {
+				return FALSE;		// duplicate sequence numbers 
+			}
+			marker_present[seq_no] = 1;
+			data_length[seq_no] = marker->data_length - ICC_HEADER_SIZE;
+		}
+	}
+	
+	if (num_markers == 0)
+		return FALSE;
+		
+	/**
+	check for missing markers, count total space needed,
+	compute offset of each marker's part of the data.
+	*/
+	
+	total_length = 0;
+	for(seq_no = 1; seq_no <= num_markers; seq_no++) {
+		if (marker_present[seq_no] == 0) {
+			return FALSE;		// missing sequence number
+		}
+		data_offset[seq_no] = total_length;
+		total_length += data_length[seq_no];
+	}
+	
+	if (total_length <= 0)
+		return FALSE;		// found only empty markers ?
+	
+	// allocate space for assembled data 
+	icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET));
+	if (icc_data == NULL)
+		return FALSE;		// out of memory
+	
+	// and fill it in
+	for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+		if (marker_is_icc(marker)) {
+			JOCTET FAR *src_ptr;
+			JOCTET *dst_ptr;
+			unsigned length;
+			seq_no = GETJOCTET(marker->data[12]);
+			dst_ptr = icc_data + data_offset[seq_no];
+			src_ptr = marker->data + ICC_HEADER_SIZE;
+			length = data_length[seq_no];
+			while (length--) {
+				*dst_ptr++ = *src_ptr++;
+			}
+		}
+	}
+	
+	*icc_data_ptr = icc_data;
+	*icc_data_len = total_length;
+	
+	return TRUE;
+}
+
+/**
+	Read JPEG_APPD marker (IPTC or Adobe Photoshop profile)
+*/
+static BOOL 
+jpeg_read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+	return read_iptc_profile(dib, dataptr, datalen);
+}
+
+/**
+	Read JPEG_APP1 marker (XMP profile)
+	@param dib Input FIBITMAP
+	@param dataptr Pointer to the APP1 marker
+	@param datalen APP1 marker length
+	@return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL  
+jpeg_read_xmp_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+	// marker identifying string for XMP (null terminated)
+	const char *xmp_signature = "http://ns.adobe.com/xap/1.0/";
+	// XMP signature is 29 bytes long
+	const size_t xmp_signature_size = strlen(xmp_signature) + 1;
+
+	size_t length = datalen;
+	BYTE *profile = (BYTE*)dataptr;
+
+	if(length <= xmp_signature_size) {
+		// avoid reading corrupted or empty data 
+		return FALSE;
+	}
+
+	// verify the identifying string
+
+	if(memcmp(xmp_signature, profile, strlen(xmp_signature)) == 0) {
+		// XMP profile
+
+		profile += xmp_signature_size;
+		length  -= xmp_signature_size;
+
+		// create a tag
+		FITAG *tag = FreeImage_CreateTag();
+		if(tag) {
+			FreeImage_SetTagID(tag, JPEG_APP0+1);	// 0xFFE1
+			FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
+			FreeImage_SetTagLength(tag, (DWORD)length);
+			FreeImage_SetTagCount(tag, (DWORD)length);
+			FreeImage_SetTagType(tag, FIDT_ASCII);
+			FreeImage_SetTagValue(tag, profile);
+			
+			// store the tag
+			FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
+
+			// destroy the tag
+			FreeImage_DeleteTag(tag);
+		}
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+/**
+	Read JFIF "JFXX" extension APP0 marker
+	@param dib Input FIBITMAP
+	@param dataptr Pointer to the APP0 marker
+	@param datalen APP0 marker length
+	@return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL 
+jpeg_read_jfxx(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+	if(datalen < 6) {
+		return FALSE;
+	}
+	
+	const int id_length = 5;
+	const BYTE *data = dataptr + id_length;
+	unsigned remaining = datalen - id_length;
+		
+	const BYTE type = *data;
+	++data, --remaining;
+
+	switch(type) {
+		case JFXX_TYPE_JPEG:
+		{
+			// load the thumbnail
+			FIMEMORY* hmem = FreeImage_OpenMemory(const_cast<BYTE*>(data), remaining);
+			FIBITMAP* thumbnail = FreeImage_LoadFromMemory(FIF_JPEG, hmem);
+			FreeImage_CloseMemory(hmem);
+			// store the thumbnail
+			FreeImage_SetThumbnail(dib, thumbnail);
+			// then delete it
+			FreeImage_Unload(thumbnail);
+			break;
+		}
+		case JFXX_TYPE_8bit:
+			// colormapped uncompressed thumbnail (no supported)
+			break;
+		case JFXX_TYPE_24bit:
+			// truecolor uncompressed thumbnail (no supported)
+			break;
+		default:
+			break;
+	}
+
+	return TRUE;
+}
+
+
+/**
+	Read JPEG special markers
+*/
+static BOOL 
+read_markers(j_decompress_ptr cinfo, FIBITMAP *dib) {
+	jpeg_saved_marker_ptr marker;
+
+	for(marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+		switch(marker->marker) {
+			case JPEG_APP0:
+				// JFIF is handled by libjpeg already, handle JFXX
+				if(memcmp(marker->data, "JFIF" , 5) == 0) {
+					continue;
+				}
+				if(memcmp(marker->data, "JFXX" , 5) == 0) {
+					if(!cinfo->saw_JFIF_marker || cinfo->JFIF_minor_version < 2) {
+						FreeImage_OutputMessageProc(s_format_id, "Warning: non-standard JFXX segment");
+					}					
+					jpeg_read_jfxx(dib, marker->data, marker->data_length);
+				}
+				// other values such as 'Picasa' : ignore safely unknown APP0 marker
+				break;
+			case JPEG_COM:
+				// JPEG comment
+				jpeg_read_comment(dib, marker->data, marker->data_length);
+				break;
+			case EXIF_MARKER:
+				// Exif or Adobe XMP profile
+				jpeg_read_exif_profile(dib, marker->data, marker->data_length);
+				jpeg_read_xmp_profile(dib, marker->data, marker->data_length);
+				jpeg_read_exif_profile_raw(dib, marker->data, marker->data_length);
+				break;
+			case IPTC_MARKER:
+				// IPTC/NAA or Adobe Photoshop profile
+				jpeg_read_iptc_profile(dib, marker->data, marker->data_length);
+				break;
+		}
+	}
+
+	// ICC profile
+	BYTE *icc_profile = NULL;
+	unsigned icc_length = 0;
+
+	if( jpeg_read_icc_profile(cinfo, &icc_profile, &icc_length) ) {
+		// copy ICC profile data
+		FreeImage_CreateICCProfile(dib, icc_profile, icc_length);
+		// clean up
+		free(icc_profile);
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//   Special markers write functions
+// ----------------------------------------------------------
+
+/**
+	Write JPEG_COM marker (comment)
+*/
+static BOOL 
+jpeg_write_comment(j_compress_ptr cinfo, FIBITMAP *dib) {
+	FITAG *tag = NULL;
+
+	// write user comment as a JPEG_COM marker
+	FreeImage_GetMetadata(FIMD_COMMENTS, dib, "Comment", &tag);
+	if(tag) {
+		const char *tag_value = (char*)FreeImage_GetTagValue(tag);
+
+		if(NULL != tag_value) {
+			for(long i = 0; i < (long)strlen(tag_value); i+= MAX_BYTES_IN_MARKER) {
+				jpeg_write_marker(cinfo, JPEG_COM, (BYTE*)tag_value + i, MIN((long)strlen(tag_value + i), MAX_BYTES_IN_MARKER));
+			}
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+/** 
+	Write JPEG_APP2 marker (ICC profile)
+*/
+static BOOL 
+jpeg_write_icc_profile(j_compress_ptr cinfo, FIBITMAP *dib) {
+    // marker identifying string "ICC_PROFILE" (null-terminated)
+	BYTE icc_signature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00 };
+
+	FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
+
+	if (iccProfile->size && iccProfile->data) {
+		// ICC_HEADER_SIZE: ICC signature is 'ICC_PROFILE' + 2 bytes
+
+		BYTE *profile = (BYTE*)malloc((iccProfile->size + ICC_HEADER_SIZE) * sizeof(BYTE));
+		if(profile == NULL) return FALSE;
+		memcpy(profile, icc_signature, 12);
+
+		for(long i = 0; i < (long)iccProfile->size; i += MAX_DATA_BYTES_IN_MARKER) {
+			unsigned length = MIN((long)(iccProfile->size - i), MAX_DATA_BYTES_IN_MARKER);
+			// sequence number
+			profile[12] = (BYTE) ((i / MAX_DATA_BYTES_IN_MARKER) + 1);
+			// number of markers
+			profile[13] = (BYTE) (iccProfile->size / MAX_DATA_BYTES_IN_MARKER + 1);
+
+			memcpy(profile + ICC_HEADER_SIZE, (BYTE*)iccProfile->data + i, length);
+			jpeg_write_marker(cinfo, ICC_MARKER, profile, (length + ICC_HEADER_SIZE));
+        }
+
+		free(profile);
+
+		return TRUE;		
+	}
+	
+	return FALSE;
+}
+
+/** 
+	Write JPEG_APPD marker (IPTC or Adobe Photoshop profile)
+	@return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL  
+jpeg_write_iptc_profile(j_compress_ptr cinfo, FIBITMAP *dib) {
+	//const char *ps_header = "Photoshop 3.0\x08BIM\x04\x04\x0\x0\x0\x0";
+	const unsigned tag_length = 26;
+
+	if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) {
+		BYTE *profile = NULL;
+		unsigned profile_size = 0;
+
+		// create a binary profile
+		if(write_iptc_profile(dib, &profile, &profile_size)) {
+
+			// write the profile
+			for(long i = 0; i < (long)profile_size; i += 65517L) {
+				unsigned length = MIN((long)profile_size - i, 65517L);
+				unsigned roundup = length & 0x01;	// needed for Photoshop
+				BYTE *iptc_profile = (BYTE*)malloc(length + roundup + tag_length);
+				if(iptc_profile == NULL) break;
+				// Photoshop identification string
+				memcpy(&iptc_profile[0], "Photoshop 3.0\x0", 14);
+				// 8BIM segment type
+				memcpy(&iptc_profile[14], "8BIM\x04\x04\x0\x0\x0\x0", 10);
+				// segment size
+				iptc_profile[24] = (BYTE)(length >> 8);
+				iptc_profile[25] = (BYTE)(length & 0xFF);
+				// segment data
+				memcpy(&iptc_profile[tag_length], &profile[i], length);
+				if(roundup)
+					iptc_profile[length + tag_length] = 0;
+				jpeg_write_marker(cinfo, IPTC_MARKER, iptc_profile, length + roundup + tag_length);
+				free(iptc_profile);
+			}
+
+			// release profile
+			free(profile);
+
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+/** 
+	Write JPEG_APP1 marker (XMP profile)
+	@return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL  
+jpeg_write_xmp_profile(j_compress_ptr cinfo, FIBITMAP *dib) {
+	// marker identifying string for XMP (null terminated)
+	const char *xmp_signature = "http://ns.adobe.com/xap/1.0/";
+
+	FITAG *tag_xmp = NULL;
+	FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp);
+
+	if(tag_xmp) {
+		const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_xmp);
+
+		if(NULL != tag_value) {
+			// XMP signature is 29 bytes long
+			unsigned int xmp_header_size = (unsigned int)strlen(xmp_signature) + 1;
+
+			DWORD tag_length = FreeImage_GetTagLength(tag_xmp);
+
+			BYTE *profile = (BYTE*)malloc((tag_length + xmp_header_size) * sizeof(BYTE));
+			if(profile == NULL) return FALSE;
+			memcpy(profile, xmp_signature, xmp_header_size);
+
+			for(DWORD i = 0; i < tag_length; i += 65504L) {
+				unsigned length = MIN((long)(tag_length - i), 65504L);
+				
+				memcpy(profile + xmp_header_size, tag_value + i, length);
+				jpeg_write_marker(cinfo, EXIF_MARKER, profile, (length + xmp_header_size));
+			}
+
+			free(profile);
+
+			return TRUE;	
+		}
+	}
+
+	return FALSE;
+}
+
+/** 
+	Write JPEG_APP1 marker (Exif profile)
+	@return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL 
+jpeg_write_exif_profile_raw(j_compress_ptr cinfo, FIBITMAP *dib) {
+    // marker identifying string for Exif = "Exif\0\0"
+    BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
+
+	FITAG *tag_exif = NULL;
+	FreeImage_GetMetadata(FIMD_EXIF_RAW, dib, g_TagLib_ExifRawFieldName, &tag_exif);
+
+	if(tag_exif) {
+		const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_exif);
+		
+		// verify the identifying string
+		if(memcmp(exif_signature, tag_value, sizeof(exif_signature)) != 0) {
+			// not an Exif profile
+			return FALSE;
+		}
+
+		if(NULL != tag_value) {
+			DWORD tag_length = FreeImage_GetTagLength(tag_exif);
+
+			BYTE *profile = (BYTE*)malloc(tag_length * sizeof(BYTE));
+			if(profile == NULL) return FALSE;
+
+			for(DWORD i = 0; i < tag_length; i += 65504L) {
+				unsigned length = MIN((long)(tag_length - i), 65504L);
+				
+				memcpy(profile, tag_value + i, length);
+				jpeg_write_marker(cinfo, EXIF_MARKER, profile, length);
+			}
+
+			free(profile);
+
+			return TRUE;	
+		}
+	}
+
+	return FALSE;
+}
+
+/**
+	Write thumbnail (JFXX segment, JPEG compressed)
+*/
+static BOOL
+jpeg_write_jfxx(j_compress_ptr cinfo, FIBITMAP *dib) {
+	// get the thumbnail to be stored
+	FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
+	if(!thumbnail) {
+		return TRUE;
+	}
+	// check for a compatible output format
+	if((FreeImage_GetImageType(thumbnail) != FIT_BITMAP) || (FreeImage_GetBPP(thumbnail) != 8) && (FreeImage_GetBPP(thumbnail) != 24)) {
+		FreeImage_OutputMessageProc(s_format_id, FI_MSG_WARNING_INVALID_THUMBNAIL);
+		return FALSE;
+	}
+	
+	// stores the thumbnail as a baseline JPEG into a memory block
+	// return the memory block only if its size is within JFXX marker size limit!
+	FIMEMORY *stream = FreeImage_OpenMemory();
+	
+	if(FreeImage_SaveToMemory(FIF_JPEG, thumbnail, stream, JPEG_BASELINE)) {
+		// check that the memory block size is within JFXX marker size limit
+		FreeImage_SeekMemory(stream, 0, SEEK_END);
+		const long eof = FreeImage_TellMemory(stream);
+		if(eof > MAX_JFXX_THUMB_SIZE) {
+			FreeImage_OutputMessageProc(s_format_id, "Warning: attached thumbnail is %d bytes larger than maximum supported size - Thumbnail saving aborted", eof - MAX_JFXX_THUMB_SIZE);
+			FreeImage_CloseMemory(stream);
+			return FALSE;
+		}
+	} else {
+		FreeImage_CloseMemory(stream);
+		return FALSE;
+	}
+
+	BYTE* thData = NULL;
+	DWORD thSize = 0;
+	
+	FreeImage_AcquireMemory(stream, &thData, &thSize);	
+	
+	BYTE id_length = 5; //< "JFXX"
+	BYTE type = JFXX_TYPE_JPEG;
+	
+	DWORD totalsize = id_length + sizeof(type) + thSize;
+	jpeg_write_m_header(cinfo, JPEG_APP0, totalsize);
+	
+	jpeg_write_m_byte(cinfo, 'J');
+	jpeg_write_m_byte(cinfo, 'F');
+	jpeg_write_m_byte(cinfo, 'X');
+	jpeg_write_m_byte(cinfo, 'X');
+	jpeg_write_m_byte(cinfo, '\0');
+	
+	jpeg_write_m_byte(cinfo, type);
+	
+	// write thumbnail to destination.
+	// We "cram it straight into the data destination module", because write_m_byte is slow
+	
+	freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
+	
+	BYTE* & out = dest->pub.next_output_byte;
+	size_t & bufRemain = dest->pub.free_in_buffer;
+	
+	const BYTE *thData_end = thData + thSize;
+
+	while(thData < thData_end) {
+		*(out)++ = *(thData)++;
+		if (--bufRemain == 0) {	
+			// buffer full - flush
+			if (!dest->pub.empty_output_buffer(cinfo)) {
+				break;
+			}
+		}
+	}
+	
+	FreeImage_CloseMemory(stream);
+
+	return TRUE;
+}
+
+/**
+	Write JPEG special markers
+*/
+static BOOL 
+write_markers(j_compress_ptr cinfo, FIBITMAP *dib) {
+	// write thumbnail as a JFXX marker
+	jpeg_write_jfxx(cinfo, dib);
+
+	// write user comment as a JPEG_COM marker
+	jpeg_write_comment(cinfo, dib);
+
+	// write ICC profile
+	jpeg_write_icc_profile(cinfo, dib);
+
+	// write IPTC profile
+	jpeg_write_iptc_profile(cinfo, dib);
+
+	// write Adobe XMP profile
+	jpeg_write_xmp_profile(cinfo, dib);
+
+	// write Exif raw data
+	jpeg_write_exif_profile_raw(cinfo, dib);
+
+	return TRUE;
+}
+
+// ------------------------------------------------------------
+//   Keep original size info when using scale option on loading
+// ------------------------------------------------------------
+static void 
+store_size_info(FIBITMAP *dib, JDIMENSION width, JDIMENSION height) {
+	char buffer[256];
+	// create a tag
+	FITAG *tag = FreeImage_CreateTag();
+	if(tag) {
+		size_t length = 0;
+		// set the original width
+		sprintf(buffer, "%d", (int)width);
+		length = strlen(buffer) + 1;	// include the NULL/0 value
+		FreeImage_SetTagKey(tag, "OriginalJPEGWidth");
+		FreeImage_SetTagLength(tag, (DWORD)length);
+		FreeImage_SetTagCount(tag, (DWORD)length);
+		FreeImage_SetTagType(tag, FIDT_ASCII);
+		FreeImage_SetTagValue(tag, buffer);
+		FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
+		// set the original height
+		sprintf(buffer, "%d", (int)height);
+		length = strlen(buffer) + 1;	// include the NULL/0 value
+		FreeImage_SetTagKey(tag, "OriginalJPEGHeight");
+		FreeImage_SetTagLength(tag, (DWORD)length);
+		FreeImage_SetTagCount(tag, (DWORD)length);
+		FreeImage_SetTagType(tag, FIDT_ASCII);
+		FreeImage_SetTagValue(tag, buffer);
+		FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
+		// destroy the tag
+		FreeImage_DeleteTag(tag);
+	}
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "JPEG";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "JPEG - JFIF Compliant";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "jpg,jif,jpeg,jpe";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return "^\377\330\377";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/jpeg";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE jpeg_signature[] = { 0xFF, 0xD8 };
+	BYTE signature[2] = { 0, 0 };
+
+	io->read_proc(signature, 1, sizeof(jpeg_signature), handle);
+
+	return (memcmp(jpeg_signature, signature, sizeof(jpeg_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+			(depth == 8) ||
+			(depth == 24)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	if (handle) {
+		FIBITMAP *dib = NULL;
+
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+		// set up the jpeglib structures
+
+		struct jpeg_decompress_struct cinfo;
+		ErrorManager fi_error_mgr;
+
+		try {
+
+			// step 1: allocate and initialize JPEG decompression object
+
+			// we set up the normal JPEG error routines, then override error_exit & output_message
+			cinfo.err = jpeg_std_error(&fi_error_mgr.pub);
+			fi_error_mgr.pub.error_exit     = jpeg_error_exit;
+			fi_error_mgr.pub.output_message = jpeg_output_message;
+			
+			// establish the setjmp return context for jpeg_error_exit to use
+			if (setjmp(fi_error_mgr.setjmp_buffer)) {
+				// If we get here, the JPEG code has signaled an error.
+				// We need to clean up the JPEG object, close the input file, and return.
+				jpeg_destroy_decompress(&cinfo);
+				throw (const char*)NULL;
+			}
+
+			jpeg_create_decompress(&cinfo);
+
+			// step 2a: specify data source (eg, a handle)
+
+			jpeg_freeimage_src(&cinfo, handle, io);
+
+			// step 2b: save special markers for later reading
+			
+			jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
+			for(int m = 0; m < 16; m++) {
+				jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF);
+			}
+
+			// step 3: read handle parameters with jpeg_read_header()
+
+			jpeg_read_header(&cinfo, TRUE);
+
+			// step 4: set parameters for decompression
+
+			unsigned int scale_denom = 1;		// fraction by which to scale image
+			int	requested_size = flags >> 16;	// requested user size in pixels
+			if(requested_size > 0) {
+				// the JPEG codec can perform x2, x4 or x8 scaling on loading
+				// try to find the more appropriate scaling according to user's need
+				double scale = MAX((double)cinfo.image_width, (double)cinfo.image_height) / (double)requested_size;
+				if(scale >= 8) {
+					scale_denom = 8;
+				} else if(scale >= 4) {
+					scale_denom = 4;
+				} else if(scale >= 2) {
+					scale_denom = 2;
+				}
+			}
+			cinfo.scale_num = 1;
+			cinfo.scale_denom = scale_denom;
+
+			if ((flags & JPEG_ACCURATE) != JPEG_ACCURATE) {
+				cinfo.dct_method          = JDCT_IFAST;
+				cinfo.do_fancy_upsampling = FALSE;
+			}
+
+			if ((flags & JPEG_GREYSCALE) == JPEG_GREYSCALE) {
+				// force loading as a 8-bit greyscale image
+				cinfo.out_color_space = JCS_GRAYSCALE;
+			}
+
+			// step 5a: start decompressor and calculate output width and height
+
+			jpeg_start_decompress(&cinfo);
+
+			// step 5b: allocate dib and init header
+
+			if((cinfo.output_components == 4) && (cinfo.out_color_space == JCS_CMYK)) {
+				// CMYK image
+				if((flags & JPEG_CMYK) == JPEG_CMYK) {
+					// load as CMYK
+					dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+					if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
+					FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK;
+				} else {
+					// load as CMYK and convert to RGB
+					dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+					if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+			} else {
+				// RGB or greyscale image
+				dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 8 * cinfo.output_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
+
+				if (cinfo.output_components == 1) {
+					// build a greyscale palette
+					RGBQUAD *colors = FreeImage_GetPalette(dib);
+
+					for (int i = 0; i < 256; i++) {
+						colors[i].rgbRed   = (BYTE)i;
+						colors[i].rgbGreen = (BYTE)i;
+						colors[i].rgbBlue  = (BYTE)i;
+					}
+				}
+			}
+			if(scale_denom != 1) {
+				// store original size info if a scaling was requested
+				store_size_info(dib, cinfo.image_width, cinfo.image_height);
+			}
+
+			// step 5c: handle metrices
+
+			if (cinfo.density_unit == 1) {
+				// dots/inch
+				FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)cinfo.X_density) / 0.0254000 + 0.5));
+				FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)cinfo.Y_density) / 0.0254000 + 0.5));
+			} else if (cinfo.density_unit == 2) {
+				// dots/cm
+				FreeImage_SetDotsPerMeterX(dib, (unsigned) (cinfo.X_density * 100));
+				FreeImage_SetDotsPerMeterY(dib, (unsigned) (cinfo.Y_density * 100));
+			}
+			
+			// step 6: read special markers
+			
+			read_markers(&cinfo, dib);
+
+			// --- header only mode => clean-up and return
+
+			if (header_only) {
+				// release JPEG decompression object
+				jpeg_destroy_decompress(&cinfo);
+				// return header data
+				return dib;
+			}
+
+			// step 7a: while (scan lines remain to be read) jpeg_read_scanlines(...);
+
+			if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) != JPEG_CMYK)) {
+				// convert from CMYK to RGB
+
+				JSAMPARRAY buffer;		// output row buffer
+				unsigned row_stride;	// physical row width in output buffer
+
+				// JSAMPLEs per row in output buffer
+				row_stride = cinfo.output_width * cinfo.output_components;
+				// make a one-row-high sample array that will go away when done with image
+				buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+				while (cinfo.output_scanline < cinfo.output_height) {
+					JSAMPROW src = buffer[0];
+					JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);
+
+					jpeg_read_scanlines(&cinfo, buffer, 1);
+
+					for(unsigned x = 0; x < cinfo.output_width; x++) {
+						WORD K = (WORD)src[3];
+						dst[FI_RGBA_RED]   = (BYTE)((K * src[0]) / 255);	// C -> R
+						dst[FI_RGBA_GREEN] = (BYTE)((K * src[1]) / 255);	// M -> G
+						dst[FI_RGBA_BLUE]  = (BYTE)((K * src[2]) / 255);	// Y -> B
+						src += 4;
+						dst += 3;
+					}
+				}
+			} else if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) == JPEG_CMYK)) {
+				// convert from LibJPEG CMYK to standard CMYK
+
+				JSAMPARRAY buffer;		// output row buffer
+				unsigned row_stride;	// physical row width in output buffer
+
+				// JSAMPLEs per row in output buffer
+				row_stride = cinfo.output_width * cinfo.output_components;
+				// make a one-row-high sample array that will go away when done with image
+				buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+				while (cinfo.output_scanline < cinfo.output_height) {
+					JSAMPROW src = buffer[0];
+					JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);
+
+					jpeg_read_scanlines(&cinfo, buffer, 1);
+
+					for(unsigned x = 0; x < cinfo.output_width; x++) {
+						// CMYK pixels are inverted
+						dst[0] = ~src[0];	// C
+						dst[1] = ~src[1];	// M
+						dst[2] = ~src[2];	// Y
+						dst[3] = ~src[3];	// K
+						src += 4;
+						dst += 4;
+					}
+				}
+
+			} else {
+				// normal case (RGB or greyscale image)
+
+				while (cinfo.output_scanline < cinfo.output_height) {
+					JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);
+
+					jpeg_read_scanlines(&cinfo, &dst, 1);
+				}
+
+				// step 7b: swap red and blue components (see LibJPEG/jmorecfg.h: #define RGB_RED, ...)
+				// The default behavior of the JPEG library is kept "as is" because LibTIFF uses 
+				// LibJPEG "as is".
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+				SwapRedBlue32(dib);
+#endif
+			}
+
+			// step 8: finish decompression
+
+			jpeg_finish_decompress(&cinfo);
+
+			// step 9: release JPEG decompression object
+
+			jpeg_destroy_decompress(&cinfo);
+
+			// check for automatic Exif rotation
+			if(!header_only && ((flags & JPEG_EXIFROTATE) == JPEG_EXIFROTATE)) {
+				RotateExif(&dib);
+			}
+
+			// everything went well. return the loaded dib
+
+			return dib;
+
+		} catch (const char *text) {
+			jpeg_destroy_decompress(&cinfo);
+			if(NULL != dib) {
+				FreeImage_Unload(dib);
+			}
+			if(NULL != text) {
+				FreeImage_OutputMessageProc(s_format_id, text);
+			}
+		}
+	}
+
+	return NULL;
+}
+
+// ----------------------------------------------------------
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	if ((dib) && (handle)) {
+		try {
+			// Check dib format
+
+			const char *sError = "only 24-bit highcolor or 8-bit greyscale/palette bitmaps can be saved as JPEG";
+
+			FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+			WORD bpp = (WORD)FreeImage_GetBPP(dib);
+
+			if ((bpp != 24) && (bpp != 8)) {
+				throw sError;
+			}
+
+			if(bpp == 8) {
+				// allow grey, reverse grey and palette 
+				if ((color_type != FIC_MINISBLACK) && (color_type != FIC_MINISWHITE) && (color_type != FIC_PALETTE)) {
+					throw sError;
+				}
+			}
+
+
+			struct jpeg_compress_struct cinfo;
+			ErrorManager fi_error_mgr;
+
+			// Step 1: allocate and initialize JPEG compression object
+
+			// we set up the normal JPEG error routines, then override error_exit & output_message
+			cinfo.err = jpeg_std_error(&fi_error_mgr.pub);
+			fi_error_mgr.pub.error_exit     = jpeg_error_exit;
+			fi_error_mgr.pub.output_message = jpeg_output_message;
+			
+			// establish the setjmp return context for jpeg_error_exit to use
+			if (setjmp(fi_error_mgr.setjmp_buffer)) {
+				// If we get here, the JPEG code has signaled an error.
+				// We need to clean up the JPEG object, close the input file, and return.
+				jpeg_destroy_compress(&cinfo);
+				throw (const char*)NULL;
+			}
+
+			// Now we can initialize the JPEG compression object
+
+			jpeg_create_compress(&cinfo);
+
+			// Step 2: specify data destination (eg, a file)
+
+			jpeg_freeimage_dst(&cinfo, handle, io);
+
+			// Step 3: set parameters for compression 
+
+			cinfo.image_width = FreeImage_GetWidth(dib);
+			cinfo.image_height = FreeImage_GetHeight(dib);
+
+			switch(color_type) {
+				case FIC_MINISBLACK :
+				case FIC_MINISWHITE :
+					cinfo.in_color_space = JCS_GRAYSCALE;
+					cinfo.input_components = 1;
+					break;
+
+				default :
+					cinfo.in_color_space = JCS_RGB;
+					cinfo.input_components = 3;
+					break;
+			}
+
+			jpeg_set_defaults(&cinfo);
+
+		    // progressive-JPEG support
+			if((flags & JPEG_PROGRESSIVE) == JPEG_PROGRESSIVE) {
+				jpeg_simple_progression(&cinfo);
+			}
+			
+			// compute optimal Huffman coding tables for the image
+			if((flags & JPEG_OPTIMIZE) == JPEG_OPTIMIZE) {
+				cinfo.optimize_coding = TRUE;
+			}
+
+			// Set JFIF density parameters from the DIB data
+
+			cinfo.X_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib));
+			cinfo.Y_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib));
+			cinfo.density_unit = 1;	// dots / inch
+
+			// thumbnail support (JFIF 1.02 extension markers)
+			if(FreeImage_GetThumbnail(dib) != NULL) {
+				cinfo.write_JFIF_header = 1; //<### force it, though when color is CMYK it will be incorrect
+				cinfo.JFIF_minor_version = 2;
+			}
+
+			// baseline JPEG support
+			if ((flags & JPEG_BASELINE) ==  JPEG_BASELINE) {
+				cinfo.write_JFIF_header = 0;	// No marker for non-JFIF colorspaces
+				cinfo.write_Adobe_marker = 0;	// write no Adobe marker by default				
+			}
+
+			// set subsampling options if required
+
+			if(cinfo.in_color_space == JCS_RGB) {
+				if((flags & JPEG_SUBSAMPLING_411) == JPEG_SUBSAMPLING_411) { 
+					// 4:1:1 (4x1 1x1 1x1) - CrH 25% - CbH 25% - CrV 100% - CbV 100%
+					// the horizontal color resolution is quartered
+					cinfo.comp_info[0].h_samp_factor = 4;	// Y 
+					cinfo.comp_info[0].v_samp_factor = 1; 
+					cinfo.comp_info[1].h_samp_factor = 1;	// Cb 
+					cinfo.comp_info[1].v_samp_factor = 1; 
+					cinfo.comp_info[2].h_samp_factor = 1;	// Cr 
+					cinfo.comp_info[2].v_samp_factor = 1; 
+				} else if((flags & JPEG_SUBSAMPLING_420) == JPEG_SUBSAMPLING_420) {
+					// 4:2:0 (2x2 1x1 1x1) - CrH 50% - CbH 50% - CrV 50% - CbV 50%
+					// the chrominance resolution in both the horizontal and vertical directions is cut in half
+					cinfo.comp_info[0].h_samp_factor = 2;	// Y
+					cinfo.comp_info[0].v_samp_factor = 2; 
+					cinfo.comp_info[1].h_samp_factor = 1;	// Cb
+					cinfo.comp_info[1].v_samp_factor = 1; 
+					cinfo.comp_info[2].h_samp_factor = 1;	// Cr
+					cinfo.comp_info[2].v_samp_factor = 1; 
+				} else if((flags & JPEG_SUBSAMPLING_422) == JPEG_SUBSAMPLING_422){ //2x1 (low) 
+					// 4:2:2 (2x1 1x1 1x1) - CrH 50% - CbH 50% - CrV 100% - CbV 100%
+					// half of the horizontal resolution in the chrominance is dropped (Cb & Cr), 
+					// while the full resolution is retained in the vertical direction, with respect to the luminance
+					cinfo.comp_info[0].h_samp_factor = 2;	// Y 
+					cinfo.comp_info[0].v_samp_factor = 1; 
+					cinfo.comp_info[1].h_samp_factor = 1;	// Cb 
+					cinfo.comp_info[1].v_samp_factor = 1; 
+					cinfo.comp_info[2].h_samp_factor = 1;	// Cr 
+					cinfo.comp_info[2].v_samp_factor = 1; 
+				} 
+				else if((flags & JPEG_SUBSAMPLING_444) == JPEG_SUBSAMPLING_444){ //1x1 (no subsampling) 
+					// 4:4:4 (1x1 1x1 1x1) - CrH 100% - CbH 100% - CrV 100% - CbV 100%
+					// the resolution of chrominance information (Cb & Cr) is preserved 
+					// at the same rate as the luminance (Y) information
+					cinfo.comp_info[0].h_samp_factor = 1;	// Y 
+					cinfo.comp_info[0].v_samp_factor = 1; 
+					cinfo.comp_info[1].h_samp_factor = 1;	// Cb 
+					cinfo.comp_info[1].v_samp_factor = 1; 
+					cinfo.comp_info[2].h_samp_factor = 1;	// Cr 
+					cinfo.comp_info[2].v_samp_factor = 1;  
+				} 
+			}
+
+			// Step 4: set quality
+			// the first 7 bits are reserved for low level quality settings
+			// the other bits are high level (i.e. enum-ish)
+
+			int quality;
+
+			if ((flags & JPEG_QUALITYBAD) == JPEG_QUALITYBAD) {
+				quality = 10;
+			} else if ((flags & JPEG_QUALITYAVERAGE) == JPEG_QUALITYAVERAGE) {
+				quality = 25;
+			} else if ((flags & JPEG_QUALITYNORMAL) == JPEG_QUALITYNORMAL) {
+				quality = 50;
+			} else if ((flags & JPEG_QUALITYGOOD) == JPEG_QUALITYGOOD) {
+				quality = 75;
+			} else 	if ((flags & JPEG_QUALITYSUPERB) == JPEG_QUALITYSUPERB) {
+				quality = 100;
+			} else {
+				if ((flags & 0x7F) == 0) {
+					quality = 75;
+				} else {
+					quality = flags & 0x7F;
+				}
+			}
+
+			jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
+
+			// Step 5: Start compressor 
+
+			jpeg_start_compress(&cinfo, TRUE);
+
+			// Step 6: Write special markers
+			
+			if ((flags & JPEG_BASELINE) !=  JPEG_BASELINE) {
+				write_markers(&cinfo, dib);
+			}
+
+			// Step 7: while (scan lines remain to be written) 
+
+			if(color_type == FIC_RGB) {
+				// 24-bit RGB image : need to swap red and blue channels
+				unsigned pitch = FreeImage_GetPitch(dib);
+				BYTE *target = (BYTE*)malloc(pitch * sizeof(BYTE));
+				if (target == NULL) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+
+				while (cinfo.next_scanline < cinfo.image_height) {
+					// get a copy of the scanline
+					memcpy(target, FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1), pitch);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+					// swap R and B channels
+					BYTE *target_p = target;
+					for(unsigned x = 0; x < cinfo.image_width; x++) {
+						INPLACESWAP(target_p[0], target_p[2]);
+						target_p += 3;
+					}
+#endif
+					// write the scanline
+					jpeg_write_scanlines(&cinfo, &target, 1);
+				}
+				free(target);
+			}
+			else if(color_type == FIC_MINISBLACK) {
+				// 8-bit standard greyscale images
+				while (cinfo.next_scanline < cinfo.image_height) {
+					JSAMPROW b = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
+
+					jpeg_write_scanlines(&cinfo, &b, 1);
+				}
+			}
+			else if(color_type == FIC_PALETTE) {
+				// 8-bit palettized images are converted to 24-bit images
+				RGBQUAD *palette = FreeImage_GetPalette(dib);
+				BYTE *target = (BYTE*)malloc(cinfo.image_width * 3);
+				if (target == NULL) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+
+				while (cinfo.next_scanline < cinfo.image_height) {
+					BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
+					FreeImage_ConvertLine8To24(target, source, cinfo.image_width, palette);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+					// swap R and B channels
+					BYTE *target_p = target;
+					for(unsigned x = 0; x < cinfo.image_width; x++) {
+						INPLACESWAP(target_p[0], target_p[2]);
+						target_p += 3;
+					}
+#endif
+
+
+					jpeg_write_scanlines(&cinfo, &target, 1);
+				}
+
+				free(target);
+			}
+			else if(color_type == FIC_MINISWHITE) {
+				// reverse 8-bit greyscale image, so reverse grey value on the fly
+				unsigned i;
+				BYTE reverse[256];
+				BYTE *target = (BYTE *)malloc(cinfo.image_width);
+				if (target == NULL) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+
+				for(i = 0; i < 256; i++) {
+					reverse[i] = (BYTE)(255 - i);
+				}
+
+				while(cinfo.next_scanline < cinfo.image_height) {
+					BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
+					for(i = 0; i < cinfo.image_width; i++) {
+						target[i] = reverse[ source[i] ];
+					}
+					jpeg_write_scanlines(&cinfo, &target, 1);
+				}
+
+				free(target);
+			}
+
+			// Step 8: Finish compression 
+
+			jpeg_finish_compress(&cinfo);
+
+			// Step 9: release JPEG compression object 
+
+			jpeg_destroy_compress(&cinfo);
+
+			return TRUE;
+
+		} catch (const char *text) {
+			if(text) {
+				FreeImage_OutputMessageProc(s_format_id, text);
+			}
+			return FALSE;
+		} 
+	}
+
+	return FALSE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitJPEG(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginJXR.cpp b/files/Source/FreeImage/PluginJXR.cpp
new file mode 100644
index 0000000..0e14e09
--- /dev/null
+++ b/files/Source/FreeImage/PluginJXR.cpp
@@ -0,0 +1,1475 @@
+// ==========================================================
+// JPEG XR Loader & Writer
+//
+// Design and implementation by
+// - Herve 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 "../Metadata/FreeImageTag.h"
+
+#include "../LibJXR/jxrgluelib/JXRGlue.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// FreeImageIO interface (I/O streaming functions)
+// ==========================================================
+
+/**
+JXR wrapper for FreeImage I/O handle
+*/
+typedef struct tagFreeImageJXRIO {
+    FreeImageIO *io;
+	fi_handle handle;
+} FreeImageJXRIO;
+
+static ERR 
+_jxr_io_Read(WMPStream* pWS, void* pv, size_t cb) {
+	FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
+	return (fio->io->read_proc(pv, (unsigned)cb, 1, fio->handle) == 1) ? WMP_errSuccess : WMP_errFileIO;
+}
+
+static ERR 
+_jxr_io_Write(WMPStream* pWS, const void* pv, size_t cb) {
+	FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
+	if(0 != cb) {
+		return (fio->io->write_proc((void*)pv, (unsigned)cb, 1, fio->handle) == 1) ? WMP_errSuccess : WMP_errFileIO;
+	}
+	return WMP_errFileIO;
+}
+
+static ERR 
+_jxr_io_SetPos(WMPStream* pWS, size_t offPos) {
+	FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
+    return (fio->io->seek_proc(fio->handle, (long)offPos, SEEK_SET) == 0) ? WMP_errSuccess : WMP_errFileIO;
+}
+
+static ERR 
+_jxr_io_GetPos(WMPStream* pWS, size_t* poffPos) {
+	FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
+    long lOff = fio->io->tell_proc(fio->handle);
+	if(lOff == -1) {
+		return WMP_errFileIO;
+	}
+    *poffPos = (size_t)lOff;
+	return WMP_errSuccess;
+}
+
+static Bool 
+_jxr_io_EOS(WMPStream* pWS) {
+	FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
+    long currentPos = fio->io->tell_proc(fio->handle);
+    fio->io->seek_proc(fio->handle, 0, SEEK_END);
+    long fileRemaining = fio->io->tell_proc(fio->handle) - currentPos;
+    fio->io->seek_proc(fio->handle, currentPos, SEEK_SET);
+    return (fileRemaining > 0);
+}
+
+static ERR 
+_jxr_io_Close(WMPStream** ppWS) {
+	WMPStream *pWS = *ppWS;
+	// HACK : we use fMem to avoid a stream destruction by the library
+	// because FreeImage MUST HAVE the ownership of the stream
+	// see _jxr_io_Create
+	if(pWS && pWS->fMem) {
+		free(pWS);
+		*ppWS = NULL;
+	}
+	return WMP_errSuccess;
+}
+
+static ERR 
+_jxr_io_Create(WMPStream **ppWS, FreeImageJXRIO *jxr_io) {
+	*ppWS = (WMPStream*)calloc(1, sizeof(**ppWS));
+	if(*ppWS) {
+		WMPStream *pWS = *ppWS;
+
+		pWS->state.pvObj = jxr_io;
+		pWS->Close = _jxr_io_Close;
+		pWS->EOS = _jxr_io_EOS;
+		pWS->Read = _jxr_io_Read;
+		pWS->Write = _jxr_io_Write;
+		pWS->SetPos = _jxr_io_SetPos;
+		pWS->GetPos = _jxr_io_GetPos;
+
+		// HACK : we use fMem to avoid a stream destruction by the library
+		// because FreeImage MUST HAVE the ownership of the stream
+		// see _jxr_io_Close
+		pWS->fMem = FALSE;
+
+		return WMP_errSuccess;
+	}
+	return WMP_errOutOfMemory;
+}
+
+// ==========================================================
+// JPEG XR Error handling
+// ==========================================================
+
+static const char* 
+JXR_ErrorMessage(const int error) {
+	switch(error) {
+		case WMP_errNotYetImplemented:
+		case WMP_errAbstractMethod:
+			return "Not yet implemented";
+		case WMP_errOutOfMemory:
+			return "Out of memory";
+		case WMP_errFileIO:
+			return "File I/O error";
+		case WMP_errBufferOverflow:
+			return "Buffer overflow";
+		case WMP_errInvalidParameter:
+			return "Invalid parameter";
+		case WMP_errInvalidArgument:
+			return "Invalid argument";
+		case WMP_errUnsupportedFormat:
+			return "Unsupported format";
+		case WMP_errIncorrectCodecVersion:
+			return "Incorrect codec version";
+		case WMP_errIndexNotFound:
+			return "Format converter: Index not found";
+		case WMP_errOutOfSequence:
+			return "Metadata: Out of sequence";
+		case WMP_errMustBeMultipleOf16LinesUntilLastCall:
+			return "Must be multiple of 16 lines until last call";
+		case WMP_errPlanarAlphaBandedEncRequiresTempFile:
+			return "Planar alpha banded encoder requires temp files";
+		case WMP_errAlphaModeCannotBeTranscoded:
+			return "Alpha mode cannot be transcoded";
+		case WMP_errIncorrectCodecSubVersion:
+			return "Incorrect codec subversion";
+		case WMP_errFail:
+		case WMP_errNotInitialized:
+		default:
+			return "Invalid instruction - please contact the FreeImage team";
+	}
+}
+
+// ==========================================================
+// Helper functions & macro
+// ==========================================================
+
+#define JXR_CHECK(error_code) \
+	if(error_code < 0) { \
+		const char *error_message = JXR_ErrorMessage(error_code); \
+		throw error_message; \
+	}
+
+// --------------------------------------------------------------------------
+
+/**
+Input conversions natively understood by FreeImage
+@see GetNativePixelFormat
+*/
+typedef struct tagJXRInputConversion {
+	BITDEPTH_BITS bdBitDepth;
+	U32 cbitUnit;
+	FREE_IMAGE_TYPE image_type;
+	unsigned red_mask;
+	unsigned green_mask;
+	unsigned blue_mask;
+} JXRInputConversion;
+
+/**
+Conversion table for native FreeImage formats
+@see GetNativePixelFormat
+*/
+static JXRInputConversion s_FreeImagePixelInfo[] = {
+	// 1-bit bitmap
+	{ BD_1, 1, FIT_BITMAP, 0, 0, 0 },
+	// 8-, 24-, 32-bit bitmap
+	{ BD_8, 8, FIT_BITMAP, 0, 0, 0 },
+	{ BD_8, 24, FIT_BITMAP, 0, 0, 0 },
+	{ BD_8, 32, FIT_BITMAP, 0, 0, 0 },
+	// 16-bit RGB 565
+	{ BD_565, 16, FIT_BITMAP, FI16_565_RED_MASK, FI16_565_GREEN_MASK, FI16_565_BLUE_MASK },
+	// 16-bit RGB 555
+	{ BD_5, 16, FIT_BITMAP, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK },
+	// 16-bit greyscale, RGB16, RGBA16 bitmap
+	{ BD_16, 16, FIT_UINT16, 0, 0, 0 },
+	{ BD_16, 48, FIT_RGB16, 0, 0, 0 },
+	{ BD_16, 64, FIT_RGBA16, 0, 0, 0 },
+	// 32-bit float, RGBF, RGBAF bitmap
+	{ BD_32F, 32, FIT_FLOAT, 0, 0, 0 },
+	{ BD_32F, 96, FIT_RGBF, 0, 0, 0 },
+	{ BD_32F, 128, FIT_RGBAF, 0, 0, 0 }
+};
+
+/**
+Scan input pixelInfo specifications and return the equivalent FreeImage info for loading
+@param pixelInfo Image specifications
+@param out_guid_format (returned value) output pixel format
+@param image_type (returned value) Image type
+@param bpp (returned value) Image bit depth
+@param red_mask (returned value) RGB mask
+@param green_mask (returned value) RGB mask
+@param blue_mask (returned value) RGB mask
+@return Returns WMP_errSuccess if successful, returns WMP_errFail otherwise
+@see GetInputPixelFormat
+*/
+static ERR
+GetNativePixelFormat(const PKPixelInfo *pixelInfo, PKPixelFormatGUID *out_guid_format, FREE_IMAGE_TYPE *image_type, unsigned *bpp, unsigned *red_mask, unsigned *green_mask, unsigned *blue_mask) {
+	const unsigned s_FreeImagePixelInfoSize = (unsigned)sizeof(s_FreeImagePixelInfo) / sizeof(*(s_FreeImagePixelInfo));
+
+	for(unsigned i = 0; i < s_FreeImagePixelInfoSize; i++) {
+		if(pixelInfo->bdBitDepth == s_FreeImagePixelInfo[i].bdBitDepth) {
+			if(pixelInfo->cbitUnit == s_FreeImagePixelInfo[i].cbitUnit) {
+				// found ! now get dst image format specifications
+				memcpy(out_guid_format, pixelInfo->pGUIDPixFmt, sizeof(PKPixelFormatGUID));
+				*image_type = s_FreeImagePixelInfo[i].image_type;
+				*bpp = s_FreeImagePixelInfo[i].cbitUnit;
+				*red_mask	= s_FreeImagePixelInfo[i].red_mask;
+				*green_mask	= s_FreeImagePixelInfo[i].green_mask;
+				*blue_mask	= s_FreeImagePixelInfo[i].blue_mask;
+				return WMP_errSuccess;
+			}
+		}
+	}
+
+	// not found : need pixel format conversion
+	return WMP_errFail;
+}
+
+/**
+Scan input file guid format and return the equivalent FreeImage info & target guid format for loading
+@param pDecoder Decoder handle
+@param guid_format (returned value) Output pixel format
+@param image_type (returned value) Image type
+@param bpp (returned value) Image bit depth
+@param red_mask (returned value) RGB mask
+@param green_mask (returned value) RGB mask
+@param blue_mask (returned value) RGB mask
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static ERR
+GetInputPixelFormat(PKImageDecode *pDecoder, PKPixelFormatGUID *guid_format, FREE_IMAGE_TYPE *image_type, unsigned *bpp, unsigned *red_mask, unsigned *green_mask, unsigned *blue_mask) {
+	ERR error_code = 0;		// error code as returned by the interface
+	PKPixelInfo pixelInfo;	// image specifications
+
+	try {		
+		// get input file pixel format ...
+		PKPixelFormatGUID pguidSourcePF;
+		error_code = pDecoder->GetPixelFormat(pDecoder, &pguidSourcePF);
+		JXR_CHECK(error_code);
+		pixelInfo.pGUIDPixFmt = &pguidSourcePF;
+		// ... check for a supported format and get the format specifications
+		error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD);
+		JXR_CHECK(error_code);
+
+		// search for an equivalent native FreeImage format
+		error_code = GetNativePixelFormat(&pixelInfo, guid_format, image_type, bpp, red_mask, green_mask, blue_mask);
+
+		if(error_code != WMP_errSuccess) {
+			// try to find a suitable conversion function ...
+			const PKPixelFormatGUID *ppguidTargetPF = NULL;	// target pixel format
+			unsigned iIndex = 0;	// first available conversion function
+			do {
+				error_code = PKFormatConverter_EnumConversions(&pguidSourcePF, iIndex, &ppguidTargetPF);
+				if(error_code == WMP_errSuccess) {
+					// found a conversion function, is the converted format a native FreeImage format ?
+					pixelInfo.pGUIDPixFmt = ppguidTargetPF;
+					error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD);
+					JXR_CHECK(error_code);
+					error_code = GetNativePixelFormat(&pixelInfo, guid_format, image_type, bpp, red_mask, green_mask, blue_mask);
+					if(error_code == WMP_errSuccess) {
+						break;
+					}
+				}
+				// try next conversion function
+				iIndex++;
+			} while(error_code != WMP_errIndexNotFound);
+
+		}
+
+		return (error_code == WMP_errSuccess) ? WMP_errSuccess : WMP_errUnsupportedFormat;
+
+	} catch(...) {
+		return error_code;
+	}
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Scan input dib format and return the equivalent PKPixelFormatGUID format for saving
+@param dib Image to be saved
+@param guid_format (returned value) GUID format
+@param bHasAlpha (returned value) TRUE if an alpha layer is present
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static ERR
+GetOutputPixelFormat(FIBITMAP *dib, PKPixelFormatGUID *guid_format, BOOL *bHasAlpha) {
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+	const unsigned bpp = FreeImage_GetBPP(dib);
+	const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+
+	*guid_format = GUID_PKPixelFormatDontCare;
+	*bHasAlpha = FALSE;
+
+	switch(image_type) {
+		case FIT_BITMAP:	// standard image	: 1-, 4-, 8-, 16-, 24-, 32-bit
+			switch(bpp) {
+				case 1:
+					// assume FIC_MINISBLACK
+					if(color_type == FIC_MINISBLACK) {
+						*guid_format = GUID_PKPixelFormatBlackWhite;
+					}
+					break;
+				case 8:
+					// assume FIC_MINISBLACK
+					if(color_type == FIC_MINISBLACK) {
+						*guid_format = GUID_PKPixelFormat8bppGray;
+					}
+					break;
+				case 16:
+					if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+						*guid_format = GUID_PKPixelFormat16bppRGB565;
+					} else {
+						// includes case where all the masks are 0
+						*guid_format = GUID_PKPixelFormat16bppRGB555;
+					}
+					break;
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+				case 24:
+					*guid_format = GUID_PKPixelFormat24bppBGR;
+					break;
+				case 32:
+					*guid_format = GUID_PKPixelFormat32bppBGRA;
+					*bHasAlpha = TRUE;
+					break;
+#elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+				case 24:
+					*guid_format = GUID_PKPixelFormat24bppRGB;
+					break;
+				case 32:
+					*guid_format = GUID_PKPixelFormat32bppRGBA;
+					*bHasAlpha = TRUE;
+					break;
+#endif
+				case 4:
+				default:
+					// not supported
+					break;
+			}
+			break;
+		case FIT_UINT16:	// array of unsigned short	: unsigned 16-bit
+			*guid_format = GUID_PKPixelFormat16bppGray;
+			break;
+		case FIT_FLOAT:		// array of float			: 32-bit IEEE floating point
+			*guid_format = GUID_PKPixelFormat32bppGrayFloat;
+			break;
+		case FIT_RGB16:		// 48-bit RGB image			: 3 x 16-bit
+			*guid_format = GUID_PKPixelFormat48bppRGB;
+			break;
+		case FIT_RGBA16:	// 64-bit RGBA image		: 4 x 16-bit
+			*guid_format = GUID_PKPixelFormat64bppRGBA;
+			*bHasAlpha = TRUE;
+			break;
+		case FIT_RGBF:		// 96-bit RGB float image	: 3 x 32-bit IEEE floating point
+			*guid_format = GUID_PKPixelFormat96bppRGBFloat;
+			break;
+		case FIT_RGBAF:		// 128-bit RGBA float image	: 4 x 32-bit IEEE floating point
+			*guid_format = GUID_PKPixelFormat128bppRGBAFloat;
+			*bHasAlpha = TRUE;
+			break;
+
+		case FIT_INT16:		// array of short			: signed 16-bit
+		case FIT_UINT32:	// array of unsigned long	: unsigned 32-bit
+		case FIT_INT32:		// array of long			: signed 32-bit
+		case FIT_DOUBLE:	// array of double			: 64-bit IEEE floating point
+		case FIT_COMPLEX:	// array of FICOMPLEX		: 2 x 64-bit IEEE floating point
+
+		default:
+			// unsupported format
+			break;
+	}
+
+	return (*guid_format != GUID_PKPixelFormatDontCare) ? WMP_errSuccess : WMP_errUnsupportedFormat;
+}
+
+// ==========================================================
+// Metadata loading
+// ==========================================================
+
+/**
+Read a JPEG-XR IFD as a buffer
+@see ReadMetadata
+*/
+static ERR
+ReadProfile(WMPStream* pStream, unsigned cbByteCount, unsigned uOffset, BYTE **ppbProfile) {
+	// (re-)allocate profile buffer
+	BYTE *pbProfile = *ppbProfile;
+	pbProfile = (BYTE*)realloc(pbProfile, cbByteCount);
+	if(!pbProfile) {
+		return WMP_errOutOfMemory;
+	}
+	// read the profile
+	if(WMP_errSuccess == pStream->SetPos(pStream, uOffset)) {
+		if(WMP_errSuccess == pStream->Read(pStream, pbProfile, cbByteCount)) {
+			*ppbProfile = pbProfile;
+			return WMP_errSuccess;
+		}
+	}
+	return WMP_errFileIO;
+}
+
+/**
+Convert a DPKPROPVARIANT to a FITAG, then store the tag as FIMD_EXIF_MAIN
+@see ReadDescriptiveMetadata
+*/
+static BOOL
+ReadPropVariant(WORD tag_id, const DPKPROPVARIANT & varSrc, FIBITMAP *dib) {
+	DWORD dwSize;
+
+	if(varSrc.vt == DPKVT_EMPTY) {
+		return FALSE;
+	}
+
+	// get the tag key
+	TagLib& s = TagLib::instance();
+	const char *key = s.getTagFieldName(TagLib::EXIF_MAIN, tag_id, NULL);
+	if(!key) {
+		return FALSE;
+	}
+
+	// create a tag
+	FITAG *tag = FreeImage_CreateTag();
+	if(tag) {
+		// set tag ID
+		FreeImage_SetTagID(tag, tag_id);
+		// set tag type, count, length and value
+		switch (varSrc.vt) {
+			case DPKVT_LPSTR:
+				FreeImage_SetTagType(tag, FIDT_ASCII);
+				dwSize = (DWORD)strlen(varSrc.VT.pszVal) + 1;
+				FreeImage_SetTagCount(tag, dwSize);
+				FreeImage_SetTagLength(tag, dwSize);
+				FreeImage_SetTagValue(tag, varSrc.VT.pszVal);
+				break;
+			
+			case DPKVT_LPWSTR:
+				FreeImage_SetTagType(tag, FIDT_UNDEFINED);
+				dwSize = (DWORD)(sizeof(U16) * (wcslen((wchar_t *) varSrc.VT.pwszVal) + 1)); // +1 for NULL term
+				FreeImage_SetTagCount(tag, dwSize);
+				FreeImage_SetTagLength(tag, dwSize);
+				FreeImage_SetTagValue(tag, varSrc.VT.pwszVal);
+				break;
+	            
+			case DPKVT_UI2:
+				FreeImage_SetTagType(tag, FIDT_SHORT);
+				FreeImage_SetTagCount(tag, 1);
+				FreeImage_SetTagLength(tag, 2);
+				FreeImage_SetTagValue(tag, &varSrc.VT.uiVal);
+				break;
+
+			case DPKVT_UI4:
+				FreeImage_SetTagType(tag, FIDT_LONG);
+				FreeImage_SetTagCount(tag, 1);
+				FreeImage_SetTagLength(tag, 4);
+				FreeImage_SetTagValue(tag, &varSrc.VT.ulVal);
+				break;
+
+			default:
+				assert(FALSE); // This case is not handled
+				break;
+		}
+		// get the tag desctiption
+		const char *description = s.getTagDescription(TagLib::EXIF_MAIN, tag_id);
+		FreeImage_SetTagDescription(tag, description);
+
+		// store the tag
+		FreeImage_SetMetadata(FIMD_EXIF_MAIN, dib, key, tag);
+
+		FreeImage_DeleteTag(tag);
+	}
+	return TRUE;
+}
+
+/**
+Read JPEG-XR descriptive metadata and store as EXIF_MAIN metadata
+@see ReadPropVariant
+*/
+static ERR
+ReadDescriptiveMetadata(PKImageDecode *pID, FIBITMAP *dib) {
+	// get Exif TIFF metadata
+	const DESCRIPTIVEMETADATA *pDescMetadata = &pID->WMP.sDescMetadata;
+	// convert metadata to FITAG and store into the EXIF_MAIN metadata model
+	ReadPropVariant(WMP_tagImageDescription, pDescMetadata->pvarImageDescription, dib);
+	ReadPropVariant(WMP_tagCameraMake, pDescMetadata->pvarCameraMake, dib);
+	ReadPropVariant(WMP_tagCameraModel, pDescMetadata->pvarCameraModel, dib);
+	ReadPropVariant(WMP_tagSoftware, pDescMetadata->pvarSoftware, dib);
+	ReadPropVariant(WMP_tagDateTime, pDescMetadata->pvarDateTime, dib);
+	ReadPropVariant(WMP_tagArtist, pDescMetadata->pvarArtist, dib);
+	ReadPropVariant(WMP_tagCopyright, pDescMetadata->pvarCopyright, dib);
+	ReadPropVariant(WMP_tagRatingStars, pDescMetadata->pvarRatingStars, dib);
+	ReadPropVariant(WMP_tagRatingValue, pDescMetadata->pvarRatingValue, dib);
+	ReadPropVariant(WMP_tagCaption, pDescMetadata->pvarCaption, dib);
+	ReadPropVariant(WMP_tagDocumentName, pDescMetadata->pvarDocumentName, dib);
+	ReadPropVariant(WMP_tagPageName, pDescMetadata->pvarPageName, dib);
+	ReadPropVariant(WMP_tagPageNumber, pDescMetadata->pvarPageNumber, dib);
+	ReadPropVariant(WMP_tagHostComputer, pDescMetadata->pvarHostComputer, dib);
+	return WMP_errSuccess;
+}
+
+/**
+Read ICC, XMP, Exif, Exif-GPS, IPTC, descriptive (i.e. Exif-TIFF) metadata
+@see ReadProfile, ReadDescriptiveMetadata
+*/
+static ERR
+ReadMetadata(PKImageDecode *pID, FIBITMAP *dib) {
+	ERR error_code = 0;		// error code as returned by the interface
+	size_t currentPos = 0;	// current stream position
+	
+	WMPStream *pStream = pID->pStream;
+	WmpDEMisc *wmiDEMisc = &pID->WMP.wmiDEMisc;
+	BYTE *pbProfile = NULL;
+
+	try {
+		// save current position
+		error_code = pStream->GetPos(pStream, &currentPos);
+		JXR_CHECK(error_code);
+
+		// ICC profile
+		if(0 != wmiDEMisc->uColorProfileByteCount) {
+			unsigned cbByteCount = wmiDEMisc->uColorProfileByteCount;
+			unsigned uOffset = wmiDEMisc->uColorProfileOffset;
+			error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
+			JXR_CHECK(error_code);
+			FreeImage_CreateICCProfile(dib, pbProfile, cbByteCount);
+		}
+
+		// XMP metadata
+		if(0 != wmiDEMisc->uXMPMetadataByteCount) {
+			unsigned cbByteCount = wmiDEMisc->uXMPMetadataByteCount;
+			unsigned uOffset = wmiDEMisc->uXMPMetadataOffset;
+			error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
+			JXR_CHECK(error_code);
+			// store the tag as XMP
+			FITAG *tag = FreeImage_CreateTag();
+			if(tag) {
+				FreeImage_SetTagLength(tag, cbByteCount);
+				FreeImage_SetTagCount(tag, cbByteCount);
+				FreeImage_SetTagType(tag, FIDT_ASCII);
+				FreeImage_SetTagValue(tag, pbProfile);
+				FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
+				FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
+				FreeImage_DeleteTag(tag);
+			}
+		}
+
+		// IPTC metadata
+		if(0 != wmiDEMisc->uIPTCNAAMetadataByteCount) {
+			unsigned cbByteCount = wmiDEMisc->uIPTCNAAMetadataByteCount;
+			unsigned uOffset = wmiDEMisc->uIPTCNAAMetadataOffset;
+			error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
+			JXR_CHECK(error_code);
+			// decode the IPTC profile
+			read_iptc_profile(dib, pbProfile, cbByteCount);
+		}
+
+		// Exif metadata
+		if(0 != wmiDEMisc->uEXIFMetadataByteCount) {
+			unsigned cbByteCount = wmiDEMisc->uEXIFMetadataByteCount;
+			unsigned uOffset = wmiDEMisc->uEXIFMetadataOffset;
+			error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
+			JXR_CHECK(error_code);
+			// decode the Exif profile
+			jpegxr_read_exif_profile(dib, pbProfile, cbByteCount, uOffset);
+		}
+
+		// Exif-GPS metadata
+		if(0 != wmiDEMisc->uGPSInfoMetadataByteCount) {
+			unsigned cbByteCount = wmiDEMisc->uGPSInfoMetadataByteCount;
+			unsigned uOffset = wmiDEMisc->uGPSInfoMetadataOffset;
+			error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
+			JXR_CHECK(error_code);
+			// decode the Exif-GPS profile
+			jpegxr_read_exif_gps_profile(dib, pbProfile, cbByteCount, uOffset);
+		}
+
+		// free profile buffer
+		free(pbProfile);
+		// restore initial position
+		error_code = pID->pStream->SetPos(pID->pStream, currentPos);
+		JXR_CHECK(error_code);
+
+		// as a LAST STEP, read descriptive metadata
+		// these metadata overwrite possible identical Exif-TIFF metadata 
+		// that could have been read inside the Exif IFD
+		
+		return ReadDescriptiveMetadata(pID, dib);
+
+	} catch(...) {
+		// free profile buffer
+		free(pbProfile);
+		if(currentPos) {
+			// restore initial position
+			pStream->SetPos(pStream, currentPos);
+		}
+		return error_code;
+	}
+}
+
+// ==========================================================
+// Metadata saving
+// ==========================================================
+
+/**
+Convert a FITAG (coming from FIMD_EXIF_MAIN) to a DPKPROPVARIANT.
+No allocation is needed here, the function just copy pointers when needed. 
+@see WriteDescriptiveMetadata
+*/
+static BOOL
+WritePropVariant(FIBITMAP *dib, WORD tag_id, DPKPROPVARIANT & varDst) {
+	FITAG *tag = NULL;
+
+	TagLib& s = TagLib::instance();
+	
+	// clear output DPKPROPVARIANT
+	varDst.vt = DPKVT_EMPTY;
+
+	// given the tag id, get the tag key
+	const char *key = s.getTagFieldName(TagLib::EXIF_MAIN, tag_id, NULL);
+	// then, get the tag info
+	if(!FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) {
+		return FALSE;
+	}
+
+	// set the tag value
+	switch(FreeImage_GetTagType(tag)) {
+		case FIDT_ASCII:
+			varDst.vt = DPKVT_LPSTR;
+			varDst.VT.pszVal = (char*)FreeImage_GetTagValue(tag);
+			break;
+		case FIDT_BYTE:
+		case FIDT_UNDEFINED:
+			varDst.vt = DPKVT_LPWSTR;
+			varDst.VT.pwszVal = (U16*)FreeImage_GetTagValue(tag);
+			break;
+		case FIDT_SHORT:
+			varDst.vt = DPKVT_UI2;
+			varDst.VT.uiVal = *((U16*)FreeImage_GetTagValue(tag));
+			break;
+		case FIDT_LONG:
+			varDst.vt = DPKVT_UI4;
+			varDst.VT.ulVal = *((U32*)FreeImage_GetTagValue(tag));
+			break;
+		default:
+			break;
+	}
+	
+	return TRUE;
+}
+
+/**
+Write EXIF_MAIN metadata to JPEG-XR descriptive metadata
+@see WritePropVariant
+*/
+static ERR
+WriteDescriptiveMetadata(PKImageEncode *pIE, FIBITMAP *dib) {
+	ERR error_code = 0;		// error code as returned by the interface
+	DESCRIPTIVEMETADATA DescMetadata;
+
+	// fill the DESCRIPTIVEMETADATA structure (use pointers to arrays when needed)
+	WritePropVariant(dib, WMP_tagImageDescription, DescMetadata.pvarImageDescription);
+	WritePropVariant(dib, WMP_tagCameraMake, DescMetadata.pvarCameraMake);
+	WritePropVariant(dib, WMP_tagCameraModel, DescMetadata.pvarCameraModel);
+	WritePropVariant(dib, WMP_tagSoftware, DescMetadata.pvarSoftware);
+	WritePropVariant(dib, WMP_tagDateTime, DescMetadata.pvarDateTime);
+	WritePropVariant(dib, WMP_tagArtist, DescMetadata.pvarArtist);
+	WritePropVariant(dib, WMP_tagCopyright, DescMetadata.pvarCopyright);
+	WritePropVariant(dib, WMP_tagRatingStars, DescMetadata.pvarRatingStars);
+	WritePropVariant(dib, WMP_tagRatingValue, DescMetadata.pvarRatingValue);
+	WritePropVariant(dib, WMP_tagCaption, DescMetadata.pvarCaption);
+	WritePropVariant(dib, WMP_tagDocumentName, DescMetadata.pvarDocumentName);
+	WritePropVariant(dib, WMP_tagPageName, DescMetadata.pvarPageName);
+	WritePropVariant(dib, WMP_tagPageNumber, DescMetadata.pvarPageNumber);
+	WritePropVariant(dib, WMP_tagHostComputer, DescMetadata.pvarHostComputer);
+
+	// copy the structure to the encoder
+	error_code = pIE->SetDescriptiveMetadata(pIE, &DescMetadata);
+
+	// no need to free anything here
+	return error_code;
+}
+
+/**
+Write ICC, XMP, Exif, Exif-GPS, IPTC, descriptive (i.e. Exif-TIFF) metadata
+*/
+static ERR
+WriteMetadata(PKImageEncode *pIE, FIBITMAP *dib) {
+	ERR error_code = 0;		// error code as returned by the interface
+	BYTE *profile = NULL;
+	unsigned profile_size = 0;
+	
+	try {
+		// write ICC profile
+		{
+			FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
+			if(iccProfile->data) {
+				error_code = pIE->SetColorContext(pIE, (U8*)iccProfile->data, iccProfile->size);
+				JXR_CHECK(error_code);
+			}
+		}
+		
+		// write descriptive metadata
+		if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, dib)) {
+			error_code = WriteDescriptiveMetadata(pIE, dib);
+			JXR_CHECK(error_code);
+		}
+
+		// write IPTC metadata
+		if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) {
+			// create a binary profile
+			if(write_iptc_profile(dib, &profile, &profile_size)) {
+				// write the profile
+				error_code = PKImageEncode_SetIPTCNAAMetadata_WMP(pIE, profile, profile_size);
+				JXR_CHECK(error_code);
+				// release profile
+				free(profile);
+				profile = NULL;
+			}
+		}
+
+		// write XMP metadata
+		{
+			FITAG *tag_xmp = NULL;
+			if(FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp)) {
+				error_code = PKImageEncode_SetXMPMetadata_WMP(pIE, (BYTE*)FreeImage_GetTagValue(tag_xmp), FreeImage_GetTagLength(tag_xmp));
+				JXR_CHECK(error_code);
+			}
+		}
+
+		// write Exif metadata
+		{
+			if(tiff_get_ifd_profile(dib, FIMD_EXIF_EXIF, &profile, &profile_size)) {
+				error_code = PKImageEncode_SetEXIFMetadata_WMP(pIE, profile, profile_size);
+				JXR_CHECK(error_code);
+				// release profile
+				free(profile);
+				profile = NULL;
+			}
+		}
+
+		// write Exif GPS metadata
+		{
+			if(tiff_get_ifd_profile(dib, FIMD_EXIF_GPS, &profile, &profile_size)) {
+				error_code = PKImageEncode_SetGPSInfoMetadata_WMP(pIE, profile, profile_size);
+				JXR_CHECK(error_code);
+				// release profile
+				free(profile);
+				profile = NULL;
+			}
+		}
+
+		return WMP_errSuccess;
+
+	} catch(...) {
+		free(profile);
+		return error_code;
+	}
+}
+
+
+
+// ==========================================================
+// Quantization tables (Y, U, V, YHP, UHP, VHP), 
+// optimized for PSNR
+// ==========================================================
+
+static const int DPK_QPS_420[11][6] = {      // for 8 bit only
+    { 66, 65, 70, 72, 72, 77 },
+    { 59, 58, 63, 64, 63, 68 },
+    { 52, 51, 57, 56, 56, 61 },
+    { 48, 48, 54, 51, 50, 55 },
+    { 43, 44, 48, 46, 46, 49 },
+    { 37, 37, 42, 38, 38, 43 },
+    { 26, 28, 31, 27, 28, 31 },
+    { 16, 17, 22, 16, 17, 21 },
+    { 10, 11, 13, 10, 10, 13 },
+    {  5,  5,  6,  5,  5,  6 },
+    {  2,  2,  3,  2,  2,  2 }
+};
+
+static const int DPK_QPS_8[12][6] = {
+    { 67, 79, 86, 72, 90, 98 },
+    { 59, 74, 80, 64, 83, 89 },
+    { 53, 68, 75, 57, 76, 83 },
+    { 49, 64, 71, 53, 70, 77 },
+    { 45, 60, 67, 48, 67, 74 },
+    { 40, 56, 62, 42, 59, 66 },
+    { 33, 49, 55, 35, 51, 58 },
+    { 27, 44, 49, 28, 45, 50 },
+    { 20, 36, 42, 20, 38, 44 },
+    { 13, 27, 34, 13, 28, 34 },
+    {  7, 17, 21,  8, 17, 21 }, // Photoshop 100%
+    {  2,  5,  6,  2,  5,  6 }
+};
+
+static const int DPK_QPS_16[11][6] = {
+    { 197, 203, 210, 202, 207, 213 },
+    { 174, 188, 193, 180, 189, 196 },
+    { 152, 167, 173, 156, 169, 174 },
+    { 135, 152, 157, 137, 153, 158 },
+    { 119, 137, 141, 119, 138, 142 },
+    { 102, 120, 125, 100, 120, 124 },
+    {  82,  98, 104,  79,  98, 103 },
+    {  60,  76,  81,  58,  76,  81 },
+    {  39,  52,  58,  36,  52,  58 },
+    {  16,  27,  33,  14,  27,  33 },
+    {   5,   8,   9,   4,   7,   8 }
+};
+
+static const int DPK_QPS_16f[11][6] = {
+    { 148, 177, 171, 165, 187, 191 },
+    { 133, 155, 153, 147, 172, 181 },
+    { 114, 133, 138, 130, 157, 167 },
+    {  97, 118, 120, 109, 137, 144 },
+    {  76,  98, 103,  85, 115, 121 },
+    {  63,  86,  91,  62,  96,  99 },
+    {  46,  68,  71,  43,  73,  75 },
+    {  29,  48,  52,  27,  48,  51 },
+    {  16,  30,  35,  14,  29,  34 },
+    {   8,  14,  17,   7,  13,  17 },
+    {   3,   5,   7,   3,   5,   6 }
+};
+
+static const int DPK_QPS_32f[11][6] = {
+    { 194, 206, 209, 204, 211, 217 },
+    { 175, 187, 196, 186, 193, 205 },
+    { 157, 170, 177, 167, 180, 190 },
+    { 133, 152, 156, 144, 163, 168 },
+    { 116, 138, 142, 117, 143, 148 },
+    {  98, 120, 123,  96, 123, 126 },
+    {  80,  99, 102,  78,  99, 102 },
+    {  65,  79,  84,  63,  79,  84 },
+    {  48,  61,  67,  45,  60,  66 },
+    {  27,  41,  46,  24,  40,  45 },
+    {   3,  22,  24,   2,  21,  22 }
+};
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "JPEG-XR";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "JPEG XR image format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "jxr,wdp,hdp";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/vnd.ms-photo";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE jxr_signature[3] = { 0x49, 0x49, 0xBC };
+	BYTE signature[3] = { 0, 0, 0 };
+
+	io->read_proc(&signature, 1, 3, handle);
+
+	return (memcmp(jxr_signature, signature, 3) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+		(depth == 1)  ||
+		(depth == 8)  ||
+		(depth == 16) ||
+		(depth == 24) || 
+		(depth == 32)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (
+		(type == FIT_BITMAP) ||
+		(type == FIT_UINT16) ||
+		(type == FIT_RGB16)  ||
+		(type == FIT_RGBA16) ||
+		(type == FIT_FLOAT)  ||
+		(type == FIT_RGBF)   ||
+		(type == FIT_RGBAF)
+	);
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ==========================================================
+//	Open & Close
+// ==========================================================
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	WMPStream *pStream = NULL;	// stream interface
+	if(io && handle) {
+		// allocate the FreeImageIO stream wrapper
+		FreeImageJXRIO *jxr_io = (FreeImageJXRIO*)malloc(sizeof(FreeImageJXRIO));
+		if(jxr_io) {
+			jxr_io->io = io;
+			jxr_io->handle = handle;
+			// create a JXR stream wrapper
+			if(_jxr_io_Create(&pStream, jxr_io) != WMP_errSuccess) {
+				free(jxr_io);
+				return NULL;
+			}
+		}
+	}
+	return pStream;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+	WMPStream *pStream = (WMPStream*)data;
+	if(pStream) {
+		// free the FreeImageIO stream wrapper
+		FreeImageJXRIO *jxr_io = (FreeImageJXRIO*)pStream->state.pvObj;
+		free(jxr_io);
+		// free the JXR stream wrapper
+		pStream->fMem = TRUE;
+		_jxr_io_Close(&pStream);
+	}
+}
+
+// ==========================================================
+//	Load
+// ==========================================================
+
+/**
+Set decoder parameters
+@param pDecoder Decoder handle
+@param flags FreeImage load flags
+*/
+static void 
+SetDecoderParameters(PKImageDecode *pDecoder, int flags) {
+	// load image & alpha for formats with alpha
+	pDecoder->WMP.wmiSCP.uAlphaMode = 2;
+	// more options to come ...
+}
+
+/**
+Copy or convert & copy decoded pixels into the dib
+@param pDecoder Decoder handle
+@param out_guid_format Target guid format
+@param dib Output dib
+@param width Image width
+@param height Image height
+@return Returns 0 if successful, returns ERR otherwise
+*/
+static ERR
+CopyPixels(PKImageDecode *pDecoder, PKPixelFormatGUID out_guid_format, FIBITMAP *dib, int width, int height) {
+	PKFormatConverter *pConverter = NULL;	// pixel format converter
+	ERR error_code = 0;	// error code as returned by the interface
+	BYTE *pb = NULL;	// local buffer used for pixel format conversion
+	
+	// image dimensions
+	const PKRect rect = {0, 0, width, height};
+
+	try {
+		// get input file pixel format ...
+		PKPixelFormatGUID in_guid_format;
+		error_code = pDecoder->GetPixelFormat(pDecoder, &in_guid_format);
+		JXR_CHECK(error_code);
+		
+		// is a format conversion needed ?
+
+		if(IsEqualGUID(out_guid_format, in_guid_format)) {
+			// no conversion, load bytes "as is" ...
+
+			// get a pointer to dst pixel data
+			BYTE *dib_bits = FreeImage_GetBits(dib);
+
+			// get dst pitch (count of BYTE for stride)
+			const unsigned cbStride = FreeImage_GetPitch(dib);			
+
+			// decode and copy bits to dst array
+			error_code = pDecoder->Copy(pDecoder, &rect, dib_bits, cbStride);
+			JXR_CHECK(error_code);		
+		}
+		else {
+			// we need to use the conversion API ...
+			
+			// allocate the pixel format converter
+			error_code = PKCodecFactory_CreateFormatConverter(&pConverter);
+			JXR_CHECK(error_code);
+			
+			// set the conversion function
+			error_code = pConverter->Initialize(pConverter, pDecoder, NULL, out_guid_format);
+			JXR_CHECK(error_code);
+			
+			// get the maximum stride
+			unsigned cbStride = 0;
+			{
+				PKPixelInfo pPIFrom;
+				PKPixelInfo pPITo;
+				
+				pPIFrom.pGUIDPixFmt = &in_guid_format;
+				error_code = PixelFormatLookup(&pPIFrom, LOOKUP_FORWARD);
+				JXR_CHECK(error_code);
+
+				pPITo.pGUIDPixFmt = &out_guid_format;
+				error_code = PixelFormatLookup(&pPITo, LOOKUP_FORWARD);
+				JXR_CHECK(error_code);
+
+				unsigned cbStrideFrom = ((pPIFrom.cbitUnit + 7) >> 3) * width;
+				unsigned cbStrideTo = ((pPITo.cbitUnit + 7) >> 3) * width;
+				cbStride = MAX(cbStrideFrom, cbStrideTo);
+			}
+
+			// allocate a local decoder / encoder buffer
+			error_code = PKAllocAligned((void **) &pb, cbStride * height, 128);
+			JXR_CHECK(error_code);
+
+			// copy / convert pixels
+			error_code = pConverter->Copy(pConverter, &rect, pb, cbStride);
+			JXR_CHECK(error_code);
+
+			// now copy pixels into the dib
+			const size_t line_size = FreeImage_GetLine(dib);
+			for(int y = 0; y < height; y++) {
+				BYTE *src_bits = (BYTE*)(pb + y * cbStride);
+				BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+				memcpy(dst_bits, src_bits, line_size);
+			}
+			
+			// free the local buffer
+			PKFreeAligned((void **) &pb);
+
+			// free the pixel format converter
+			PKFormatConverter_Release(&pConverter);
+		}
+
+		// FreeImage DIB are upside-down relative to usual graphic conventions
+		FreeImage_FlipVertical(dib);
+
+		// post-processing ...
+		// -------------------
+
+		// swap RGB as needed
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+		if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppRGB) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppRGB)) {
+			SwapRedBlue32(dib);
+		}
+#elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+		if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppBGR) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppBGR)) {
+			SwapRedBlue32(dib);
+		}
+#endif
+		
+		return WMP_errSuccess;
+
+	} catch(...) {
+		// free the local buffer
+		PKFreeAligned((void **) &pb);
+		// free the pixel format converter
+		PKFormatConverter_Release(&pConverter);
+
+		return error_code;
+	}
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	PKImageDecode *pDecoder = NULL;	// decoder interface
+	ERR error_code = 0;				// error code as returned by the interface
+	PKPixelFormatGUID guid_format;	// loaded pixel format (== input file pixel format if no conversion needed)
+	
+	FREE_IMAGE_TYPE image_type = FIT_UNKNOWN;	// input image type
+	unsigned bpp = 0;							// input image bit depth
+	FIBITMAP *dib = NULL;
+	
+	// get the I/O stream wrapper
+	WMPStream *pDecodeStream = (WMPStream*)data;
+
+	if(!handle || !pDecodeStream) {
+		return NULL;
+	}
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	try {
+		int width, height;	// image dimensions (in pixels)
+
+		// create a JXR decoder interface and initialize function pointers with *_WMP functions
+		error_code = PKImageDecode_Create_WMP(&pDecoder);
+		JXR_CHECK(error_code);
+
+		// attach the stream to the decoder ...
+		// ... then read the image container and the metadata
+		error_code = pDecoder->Initialize(pDecoder, pDecodeStream);
+		JXR_CHECK(error_code);
+
+		// set decoder parameters
+		SetDecoderParameters(pDecoder, flags);
+
+		// get dst image format specifications
+		unsigned red_mask = 0, green_mask = 0, blue_mask = 0;
+		error_code = GetInputPixelFormat(pDecoder, &guid_format, &image_type, &bpp, &red_mask, &green_mask, &blue_mask);
+		JXR_CHECK(error_code);
+
+		// get image dimensions
+		pDecoder->GetSize(pDecoder, &width, &height);
+
+		// allocate dst image
+		{			
+			dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, bpp, red_mask, green_mask, blue_mask);
+			if(!dib) {
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+			if(FreeImage_GetBPP(dib) == 1) {
+				// BD_1 - build a FIC_MINISBLACK palette
+				RGBQUAD *pal = FreeImage_GetPalette(dib);
+				pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+				pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+			}
+		}
+
+		// get image resolution
+		{
+			float resX, resY;	// image resolution (in dots per inch)
+			// convert from English units, i.e. dots per inch to universal units, i.e. dots per meter
+			pDecoder->GetResolution(pDecoder, &resX, &resY);
+			FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX / 0.0254F + 0.5F));
+			FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY / 0.0254F + 0.5F));
+		}
+
+		// get metadata & ICC profile
+		error_code = ReadMetadata(pDecoder, dib);
+		JXR_CHECK(error_code);
+
+		if(header_only) {
+			// header only mode ...
+			
+			// free the decoder
+			pDecoder->Release(&pDecoder);
+			assert(pDecoder == NULL);
+
+			return dib;
+		}
+		
+		// copy pixels into the dib, perform pixel conversion if needed
+		error_code = CopyPixels(pDecoder, guid_format, dib, width, height);
+		JXR_CHECK(error_code);
+
+		// free the decoder
+		pDecoder->Release(&pDecoder);
+		assert(pDecoder == NULL);
+
+		return dib;
+
+	} catch (const char *message) {
+		// unload the dib
+		FreeImage_Unload(dib);
+		// free the decoder
+		pDecoder->Release(&pDecoder);
+
+		if(NULL != message) {
+			FreeImage_OutputMessageProc(s_format_id, message);
+		}
+	}
+
+	return NULL;
+}
+
+// ==========================================================
+//	Save
+// ==========================================================
+
+/**
+Configure compression parameters
+
+ImageQuality  Q (BD==1)  Q (BD==8)   Q (BD==16)  Q (BD==32F) Subsample   Overlap
+[0.0, 0.4]    8-IQ*5     (see table) (see table) (see table) 4:4:4       2
+(0.4, 0.8)    8-IQ*5     (see table) (see table) (see table) 4:4:4       1
+[0.8, 1.0)    8-IQ*5     (see table) (see table) (see table) 4:4:4       1
+[1.0, 1.0]    1          1           1           1           4:4:4       0
+
+@param wmiSCP Encoder parameters
+@param pixelInfo Image specifications
+@param fltImageQuality Image output quality in [0..1), 1 means lossless
+*/
+static void 
+SetCompression(CWMIStrCodecParam *wmiSCP, const PKPixelInfo *pixelInfo, float fltImageQuality) {
+    if(fltImageQuality < 1.0F) {
+        // overlap
+		if(fltImageQuality >= 0.5F) {
+			wmiSCP->olOverlap = OL_ONE;
+		} else {
+			wmiSCP->olOverlap = OL_TWO;
+		}
+		// chroma sub-sampling
+		if(fltImageQuality >= 0.5F || pixelInfo->uBitsPerSample > 8) {
+			wmiSCP->cfColorFormat = YUV_444;
+		} else {
+			wmiSCP->cfColorFormat = YUV_420;
+		}
+
+	    // bit depth
+		if(pixelInfo->bdBitDepth == BD_1) {
+			wmiSCP->uiDefaultQPIndex = (U8)(8 - 5.0F * fltImageQuality + 0.5F);
+		}
+		else {
+			// remap [0.8, 0.866, 0.933, 1.0] to [0.8, 0.9, 1.0, 1.1]
+            // to use 8-bit DPK QP table (0.933 == Photoshop JPEG 100)
+            if(fltImageQuality > 0.8F && pixelInfo->bdBitDepth == BD_8 && wmiSCP->cfColorFormat != YUV_420 && wmiSCP->cfColorFormat != YUV_422) {
+				fltImageQuality = 0.8F + (fltImageQuality - 0.8F) * 1.5F;
+			}
+
+            const int qi = (int) (10.0F * fltImageQuality);
+            const float qf = 10.0F * fltImageQuality - (float)qi;
+			
+			const int *pQPs = 
+				(wmiSCP->cfColorFormat == YUV_420 || wmiSCP->cfColorFormat == YUV_422) ?
+				DPK_QPS_420[qi] :
+				(pixelInfo->bdBitDepth == BD_8 ? DPK_QPS_8[qi] :
+				(pixelInfo->bdBitDepth == BD_16 ? DPK_QPS_16[qi] :
+				(pixelInfo->bdBitDepth == BD_16F ? DPK_QPS_16f[qi] :
+				DPK_QPS_32f[qi])));
+				
+			wmiSCP->uiDefaultQPIndex = (U8) (0.5F + (float) pQPs[0] * (1.0F - qf) + (float) (pQPs + 6)[0] * qf);
+			wmiSCP->uiDefaultQPIndexU = (U8) (0.5F + (float) pQPs[1] * (1.0F - qf) + (float) (pQPs + 6)[1] * qf);
+			wmiSCP->uiDefaultQPIndexV = (U8) (0.5F + (float) pQPs[2] * (1.0F - qf) + (float) (pQPs + 6)[2] * qf);
+            wmiSCP->uiDefaultQPIndexYHP = (U8) (0.5F + (float) pQPs[3] * (1.0F - qf) + (float) (pQPs + 6)[3] * qf);
+			wmiSCP->uiDefaultQPIndexUHP = (U8) (0.5F + (float) pQPs[4] * (1.0F - qf) + (float) (pQPs + 6)[4] * qf);
+			wmiSCP->uiDefaultQPIndexVHP = (U8) (0.5F + (float) pQPs[5] * (1.0F - qf) + (float) (pQPs + 6)[5] * qf);
+		}
+	} // fltImageQuality < 1.0F
+    else {
+		// lossless mode
+		wmiSCP->uiDefaultQPIndex = 1;
+	}
+}
+
+/**
+Set encoder parameters
+@param wmiSCP Encoder parameters
+@param pixelInfo Image specifications
+@param flags FreeImage save flags
+@param bHasAlpha TRUE if an alpha layer is present
+*/
+static void 
+SetEncoderParameters(CWMIStrCodecParam *wmiSCP, const PKPixelInfo *pixelInfo, int flags, BOOL bHasAlpha) {
+	float fltImageQuality = 1.0F;
+
+	// all values have been set to zero by the API
+	// update default values for some attributes
+    wmiSCP->cfColorFormat = YUV_444;		// color format
+    wmiSCP->bdBitDepth = BD_LONG;			// internal bit depth
+    wmiSCP->bfBitstreamFormat = SPATIAL;	// compressed image data in spatial order
+    wmiSCP->bProgressiveMode = FALSE;		// sequential mode
+    wmiSCP->olOverlap = OL_ONE;				// single level overlap processing 
+	wmiSCP->cNumOfSliceMinus1H = 0;			// # of horizontal slices
+	wmiSCP->cNumOfSliceMinus1V = 0;			// # of vertical slices
+    wmiSCP->sbSubband = SB_ALL;				// keep all subbands
+    wmiSCP->uAlphaMode = 0;					// 0:no alpha 1: alpha only else: something + alpha 
+    wmiSCP->uiDefaultQPIndex = 1;			// quantization for grey or rgb layer(s), 1: lossless
+    wmiSCP->uiDefaultQPIndexAlpha = 1;		// quantization for alpha layer, 1: lossless
+
+	// process the flags
+	// -----------------
+
+	// progressive mode
+	if((flags & JXR_PROGRESSIVE) == JXR_PROGRESSIVE) {
+		// turn on progressive mode (instead of sequential mode)
+		wmiSCP->bProgressiveMode = TRUE;
+	}
+
+	// quality in [0.01 - 1.0), 1.0 means lossless - default is 0.80
+	int quality = flags & 0x7F;
+	if(quality == 0) {
+		// defaut to 0.80
+		fltImageQuality = 0.8F;
+	} else if((flags & JXR_LOSSLESS) == JXR_LOSSLESS) {
+		fltImageQuality = 1.0F;
+	} else {
+		quality = (quality >= 100) ? 100 : quality;
+		fltImageQuality = quality / 100.0F;
+	}
+	SetCompression(wmiSCP, pixelInfo, fltImageQuality);
+
+	// alpha compression
+	if(bHasAlpha) {
+		wmiSCP->uAlphaMode = 2;	// encode with a planar alpha channel
+	}
+}
+
+// --------------------------------------------------------------------------
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	BOOL bIsFlipped = FALSE;		// FreeImage DIB are upside-down relative to usual graphic conventions
+	PKPixelFormatGUID guid_format;	// image format
+	PKPixelInfo pixelInfo;			// image specifications
+	BOOL bHasAlpha = FALSE;			// is alpha layer present ?
+
+	PKImageEncode *pEncoder = NULL;		// encoder interface
+	ERR error_code = 0;					// error code as returned by the interface
+
+	// get the I/O stream wrapper
+	WMPStream *pEncodeStream = (WMPStream*)data;
+
+	if(!dib || !handle || !pEncodeStream) {
+		return FALSE;
+	}
+
+	try {
+		// get image dimensions
+		unsigned width = FreeImage_GetWidth(dib);
+		unsigned height = FreeImage_GetHeight(dib);
+
+		// check JPEG-XR limits
+		if((width < MB_WIDTH_PIXEL) || (height < MB_HEIGHT_PIXEL)) {
+			FreeImage_OutputMessageProc(s_format_id, "Unsupported image size: width x height = %d x %d", width, height);
+			throw (const char*)NULL;
+		}
+
+		// get output pixel format
+		error_code = GetOutputPixelFormat(dib, &guid_format, &bHasAlpha);
+		JXR_CHECK(error_code);
+		pixelInfo.pGUIDPixFmt = &guid_format;
+		error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD);
+		JXR_CHECK(error_code);
+
+		// create a JXR encoder interface and initialize function pointers with *_WMP functions
+		error_code = PKImageEncode_Create_WMP(&pEncoder);
+		JXR_CHECK(error_code);
+
+		// attach the stream to the encoder and set all encoder parameters to zero ...
+		error_code = pEncoder->Initialize(pEncoder, pEncodeStream, &pEncoder->WMP.wmiSCP, sizeof(CWMIStrCodecParam));
+		JXR_CHECK(error_code);
+
+		// ... then configure the encoder
+		SetEncoderParameters(&pEncoder->WMP.wmiSCP, &pixelInfo, flags, bHasAlpha);
+
+		// set pixel format
+		pEncoder->SetPixelFormat(pEncoder, guid_format);
+
+		// set image size
+		pEncoder->SetSize(pEncoder, width, height);
+		
+		// set resolution (convert from universal units to English units)
+		float resX = (float)(unsigned)(0.5F + 0.0254F * FreeImage_GetDotsPerMeterX(dib));
+		float resY = (float)(unsigned)(0.5F + 0.0254F * FreeImage_GetDotsPerMeterY(dib));
+		pEncoder->SetResolution(pEncoder, resX, resY);
+
+		// set metadata
+		WriteMetadata(pEncoder, dib);
+
+		// write metadata & pixels
+		// -----------------------
+
+		// dib coordinates are upside-down relative to usual conventions
+		bIsFlipped = FreeImage_FlipVertical(dib);
+
+		// get a pointer to dst pixel data
+		BYTE *dib_bits = FreeImage_GetBits(dib);
+
+		// get dst pitch (count of BYTE for stride)
+		const unsigned cbStride = FreeImage_GetPitch(dib);
+
+		// write metadata + pixels on output
+		error_code = pEncoder->WritePixels(pEncoder, height, dib_bits, cbStride);
+		JXR_CHECK(error_code);
+
+		// recover dib coordinates
+		FreeImage_FlipVertical(dib);
+
+		// free the encoder
+		pEncoder->Release(&pEncoder);
+		assert(pEncoder == NULL);
+		
+		return TRUE;
+
+	} catch (const char *message) {
+		if(bIsFlipped) {
+			// recover dib coordinates
+			FreeImage_FlipVertical(dib);
+		}
+		if(pEncoder) {
+			// free the encoder
+			pEncoder->Release(&pEncoder);
+			assert(pEncoder == NULL);
+		}
+		if(NULL != message) {
+			FreeImage_OutputMessageProc(s_format_id, message);
+		}
+	}
+
+	return FALSE;
+}
+
+// ==========================================================
+//	 Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitJXR(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
+
diff --git a/files/Source/FreeImage/PluginKOALA.cpp b/files/Source/FreeImage/PluginKOALA.cpp
new file mode 100644
index 0000000..6c3ae52
--- /dev/null
+++ b/files/Source/FreeImage/PluginKOALA.cpp
@@ -0,0 +1,243 @@
+// ==========================================================
+// KOALA Loader
+//
+// 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"
+
+// ----------------------------------------------------------
+//   Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagKOALA {
+	BYTE image[8000];		// pixmap image
+	BYTE colour1[1000];		// first colourmap (colour 1 and 2)
+	BYTE colour2[1000];		// second colourmap (colour 3)
+	BYTE background;		// background colour
+} koala_t;
+
+struct colour_t {
+	int	r;
+	int g;
+	int b;	
+};
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ----------------------------------------------------------
+
+#define CBM_WIDTH  320
+#define CBM_HEIGHT 200
+
+// ----------------------------------------------------------
+
+const colour_t c64colours[16] = {
+	{   0,   0,   0 },	// Black
+	{ 255, 255, 255 },	// White
+	{ 170,  17,  17 },	// Red
+	{  12, 204, 204 },	// Cyan
+	{ 221,  51, 221 },	// Purple
+	{  0,  187,  0 },	// Green
+	{   0,   0, 204 },	// Blue
+	{ 255, 255, 140 },	// Yellow
+	{ 204, 119,  34 },	// Orange
+	{ 136,  68,   0 },	// Brown
+	{ 255, 153, 136 },	// Light red
+	{  92,  92,  92 },	// Gray 1
+	{ 170, 170, 170 },	// Gray 2
+	{ 140, 255, 178 },	// Light green
+	{  39, 148, 255 },	// Light blue
+	{ 196, 196, 196 }	// Gray 3
+};
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+const char * DLL_CALLCONV
+Format() {
+	return "KOALA";
+}
+
+const char * DLL_CALLCONV
+Description() {
+	return "C64 Koala Graphics";
+}
+
+const char * DLL_CALLCONV
+Extension() {
+	return "koa";
+}
+
+const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-koala";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE koala_signature[] = { 0x00, 0x60 };
+	BYTE signature[2] = { 0, 0 };
+
+	io->read_proc(signature, 1, sizeof(koala_signature), handle);
+
+	return (memcmp(koala_signature, signature, sizeof(koala_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	if (handle) {
+		koala_t image;
+
+		// read the load address
+
+		unsigned char load_address[2];  // highbit, lowbit
+
+		io->read_proc(&load_address, 1, 2, handle);
+
+		// if the load address is correct, skip it. otherwise ignore the load address
+
+		if ((load_address[0] != 0x00) || (load_address[1] != 0x60)) {
+			((BYTE *)&image)[0] = load_address[0];
+			((BYTE *)&image)[1] = load_address[1];
+
+			io->read_proc((BYTE *)&image + 2, 1, 10001 - 2, handle);
+		} else {
+			io->read_proc(&image, 1, 10001, handle);
+		}		
+
+		// build DIB in memory
+
+		FIBITMAP *dib = FreeImage_Allocate(CBM_WIDTH, CBM_HEIGHT, 4);
+
+		if (dib) {
+			// write out the commodore 64 color palette
+
+			RGBQUAD *palette = FreeImage_GetPalette(dib);
+
+			for (int i = 0; i < 16; i++) {
+				palette[i].rgbBlue  = (BYTE)c64colours[i].b;
+				palette[i].rgbGreen = (BYTE)c64colours[i].g;
+				palette[i].rgbRed   = (BYTE)c64colours[i].r;
+			}
+
+			// write out bitmap data
+
+			BYTE pixel_mask[4]         = { 0xc0, 0x30, 0x0c, 0x03 };
+			BYTE pixel_displacement[4] = { 6, 4, 2, 0 };
+			int	pixel, index, colourindex;
+			unsigned char found_color = 0;
+
+			for (int y = 0; y < 200; y++) {
+				for (int x = 0; x < 160; x++) {
+					// Get value of pixel at (x,y)
+
+					index = (x / 4) * 8 + (y % 8) + (y / 8) * CBM_WIDTH;
+					colourindex = (x / 4) + (y / 8) * 40;
+					pixel = (image.image[index] & pixel_mask[x % 4]) >> pixel_displacement[x % 4];
+
+					// Retrieve RGB values
+
+					switch (pixel) {
+						case 0: // Background
+							found_color = image.background;
+							break;
+							
+						case 1: // Colour 1
+							found_color = image.colour1[colourindex] >> 4;
+							break;
+							
+						case 2: // Colour 2
+							found_color = image.colour1[colourindex] & 0xf;
+							break;
+							
+						case 3: // Colour 3
+							found_color = image.colour2[colourindex] & 0xf;
+							break;
+					};
+
+					*(FreeImage_GetScanLine(dib, CBM_HEIGHT - y - 1) + x) = (found_color << 4) | found_color;
+				}
+			}
+
+			return dib;
+		}
+	}
+
+	return NULL;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitKOALA(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/files/Source/FreeImage/PluginMNG.cpp b/files/Source/FreeImage/PluginMNG.cpp
new file mode 100644
index 0000000..6d2f50e
--- /dev/null
+++ b/files/Source/FreeImage/PluginMNG.cpp
@@ -0,0 +1,153 @@
+// ==========================================================
+// MNG loader
+//
+// Design and implementation by
+// - Herve 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"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ----------------------------------------------------------
+
+#define MNG_SIGNATURE_SIZE 8	// size of the signature
+
+// ----------------------------------------------------------
+
+// ----------------------------------------------------------
+//   mng interface (see MNGHelper.cpp)
+// ----------------------------------------------------------
+
+FIBITMAP* mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, int flags = 0);
+
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "MNG";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Multiple-image Network Graphics";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "mng";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "video/x-mng";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE mng_signature[8] = { 138, 77, 78, 71, 13, 10, 26, 10 };
+	BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	io->read_proc(&signature, 1, MNG_SIGNATURE_SIZE, handle);
+
+	return (memcmp(mng_signature, signature, MNG_SIGNATURE_SIZE) == 0) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return FALSE;
+}
+
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	return NULL;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	long offset = MNG_SIGNATURE_SIZE;	// move to skip first 8 bytes of signature
+
+	// check the signature (8 bytes)
+	if(Validate(io, handle) == FALSE) {
+		return NULL;
+	}
+	
+	// parse chunks and decode a jng or mng bitmap
+	return mng_ReadChunks(s_format_id, io, handle, offset, flags);
+}
+
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitMNG(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginPCD.cpp b/files/Source/FreeImage/PluginPCD.cpp
new file mode 100644
index 0000000..ff0c5b8
--- /dev/null
+++ b/files/Source/FreeImage/PluginPCD.cpp
@@ -0,0 +1,251 @@
+// ==========================================================
+// Kodak PhotoCD Loader
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// 
+// Based on pascal code developed by Alex Kwak
+//
+// 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 functions
+// ==========================================================
+
+static int 
+clamp(double x) {
+	int a = (int)floor(x + 0.5);
+	return (a < 0) ? 0 : (a > 255) ? 255 : a;
+}
+
+static void
+YUV2RGB(int y, int cb, int cr, int &r, int &g, int &b) {
+	double c11 = 0.0054980  * 256.0;
+	double c12 = 0.0000001  * 256.0;
+	double c13 = 0.0051681  * 256.0;
+	double c21 = 0.0054980  * 256.0;
+	double c22 = -0.0015446 * 256.0;
+	double c23 = -0.0026325 * 256.0;
+	double c31 = 0.0054980  * 256.0;
+	double c32 = 0.0079533  * 256.0;
+	double c33 = 0.0000001  * 256.0;
+
+	r = clamp(c11 * y + c12 * (cb - 156) + c13 * (cr - 137));
+	g = clamp(c21 * y + c22 * (cb - 156) + c23 * (cr - 137));
+	b = clamp(c31 * y + c32 * (cb - 156) + c33 * (cr - 137));
+}
+
+static BOOL
+VerticalOrientation(FreeImageIO *io, fi_handle handle) {
+	char buffer[128];
+
+	io->read_proc(buffer, 128, 1, handle);
+
+	return (buffer[72] & 63) == 8;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "PCD";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Kodak PhotoCD";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "pcd";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-photo-cd";
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	FIBITMAP *dib = NULL;
+	unsigned width;
+	unsigned height;
+	const unsigned bpp = 24;
+	int scan_line_add   = 1;
+	int start_scan_line = 0;
+	
+	BYTE *y1 = NULL, *y2 = NULL, *cbcr = NULL;
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	// to make absolute seeks possible we store the current position in the file
+	
+	long offset_in_file = io->tell_proc(handle);
+	long seek = 0;
+
+	// decide which bitmap in the cabinet to load
+
+	switch (flags) {
+		case PCD_BASEDIV4 :
+			seek = 0x2000;
+			width = 192;
+			height = 128;
+			break;
+
+		case PCD_BASEDIV16 :
+			seek = 0xB800;
+			width = 384;
+			height = 256;
+			break;
+
+		default :
+			seek = 0x30000;
+			width = 768;
+			height = 512;
+			break;
+	}
+
+	try {
+		// allocate the dib and write out the header
+		dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
+
+		if(header_only) {
+			return dib;
+		}
+
+		// check if the PCD is bottom-up
+
+		if (VerticalOrientation(io, handle)) {
+			scan_line_add = -1;
+			start_scan_line = height - 1;		
+		}
+
+		// temporary stuff to load PCD
+
+		BYTE *y1 = (BYTE*)malloc(width * sizeof(BYTE));
+		BYTE *y2 = (BYTE*)malloc(width * sizeof(BYTE));
+		BYTE *cbcr = (BYTE*)malloc(width * sizeof(BYTE));
+		if(!y1 || !y2 || !cbcr) throw FI_MSG_ERROR_MEMORY;
+
+		BYTE *yl[] = { y1, y2 };
+
+		// seek to the part where the bitmap data begins
+
+		io->seek_proc(handle, offset_in_file, SEEK_SET);
+		io->seek_proc(handle, seek, SEEK_CUR);
+
+		// read the data
+
+		for (unsigned y = 0; y < height / 2; y++) {
+			io->read_proc(y1, width, 1, handle);
+			io->read_proc(y2, width, 1, handle);
+			io->read_proc(cbcr, width, 1, handle);
+
+			for (int i = 0; i < 2; i++) {
+				BYTE *bits = FreeImage_GetScanLine(dib, start_scan_line);
+				for (unsigned x = 0; x < width; x++) {
+					int r, g, b;
+
+					YUV2RGB(yl[i][x], cbcr[x / 2], cbcr[(width / 2) + (x / 2)], r, g, b);
+
+					bits[FI_RGBA_BLUE]  = (BYTE)b;
+					bits[FI_RGBA_GREEN] = (BYTE)g;
+					bits[FI_RGBA_RED]   = (BYTE)r;
+					bits += 3;
+				}
+
+				start_scan_line += scan_line_add;
+			}
+		}
+
+		free(cbcr);
+		free(y2);
+		free(y1);
+
+		return dib;
+
+	} catch(const char *text) {
+		if(dib) FreeImage_Unload(dib);
+		if(cbcr) free(cbcr);
+		if(y2) free(y2);
+		if(y1) free(y1);
+
+		FreeImage_OutputMessageProc(s_format_id, text);
+
+		return NULL;
+	}
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPCD(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = NULL;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginPCX.cpp b/files/Source/FreeImage/PluginPCX.cpp
new file mode 100644
index 0000000..cd75629
--- /dev/null
+++ b/files/Source/FreeImage/PluginPCX.cpp
@@ -0,0 +1,659 @@
+// ==========================================================
+// PCX Loader
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Jani Kajala (janik@remedy.fi)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Juergen Riecker (j.riecker@gmx.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"
+
+// ----------------------------------------------------------
+//   Constants + headers
+// ----------------------------------------------------------
+
+#define IO_BUF_SIZE	2048
+
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagPCXHEADER {
+	BYTE  manufacturer;		// Magic number (0x0A = ZSoft Z)
+	BYTE  version;			// Version	0 == 2.5
+							//          2 == 2.8 with palette info
+							//          3 == 2.8 without palette info
+							//          5 == 3.0 with palette info
+	BYTE  encoding;			// Encoding: 0 = uncompressed, 1 = PCX rle compressed
+	BYTE  bpp;				// Bits per pixel per plane (only 1 or 8)
+	WORD  window[4];		// left, upper, right,lower pixel coord.
+	WORD  hdpi;				// Horizontal resolution
+	WORD  vdpi;				// Vertical resolution
+	BYTE  color_map[48];	// Colormap for 16-color images
+	BYTE  reserved;
+	BYTE  planes;			// Number of planes (1, 3 or 4)
+	WORD  bytes_per_line;	// Bytes per row (always even)
+	WORD  palette_info;		// Palette information (1 = color or b&w; 2 = gray scale)
+	WORD  h_screen_size;
+	WORD  v_screen_size;
+	BYTE  filler[54];		// Reserved filler
+} PCXHEADER;
+		
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static BOOL 
+pcx_validate(FreeImageIO *io, fi_handle handle) {
+	BYTE pcx_signature = 0x0A;
+	BYTE signature[4] = { 0, 0, 0, 0 };
+
+	if(io->read_proc(&signature, 1, 4, handle) != 4) {
+		return FALSE;
+	}
+	// magic number (0x0A = ZSoft Z)
+	if(signature[0] == pcx_signature) {
+		// version
+		if(signature[1] <= 5) {
+			// encoding
+			if((signature[2] == 0) || (signature[2] == 1)) {
+				// bits per pixel per plane
+				if((signature[3] == 1) || (signature[3] == 8)) {
+					return TRUE;
+				}
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+static unsigned
+readline(FreeImageIO &io, fi_handle handle, BYTE *buffer, unsigned length, BOOL rle, BYTE * ReadBuf, int * ReadPos) {
+	// -----------------------------------------------------------//
+	// Read either run-length encoded or normal image data        //
+	//                                                            //
+	//       THIS IS HOW RUNTIME LENGTH ENCODING WORKS IN PCX:    //
+	//                                                            //
+	//  1) If the upper 2 bits of a byte are set,                 //
+	//     the lower 6 bits specify the count for the next byte   //
+	//                                                            //
+	//  2) If the upper 2 bits of the byte are clear,             //
+	//     the byte is actual data with a count of 1              //
+	//                                                            //
+	//  Note that a scanline always has an even number of bytes   //
+	// -------------------------------------------------------------
+
+	BYTE count = 0, value = 0;
+	unsigned written = 0;
+
+	if (rle) {
+		// run-length encoded read
+
+		while (length--) {
+			if (count == 0) {
+				if (*ReadPos >= IO_BUF_SIZE - 1 ) {
+					if (*ReadPos == IO_BUF_SIZE - 1) {
+						// we still have one BYTE, copy it to the start pos
+
+						*ReadBuf = ReadBuf[IO_BUF_SIZE - 1];
+
+						io.read_proc(ReadBuf + 1, 1, IO_BUF_SIZE - 1, handle);
+					} else {
+						// read the complete buffer
+
+						io.read_proc(ReadBuf, 1, IO_BUF_SIZE, handle);
+					}
+
+					*ReadPos = 0;
+				}
+
+				value = *(ReadBuf + (*ReadPos)++);
+
+				if ((value & 0xC0) == 0xC0) {
+					count = value & 0x3F;
+					value = *(ReadBuf + (*ReadPos)++);
+				} else {
+					count = 1;
+				}
+			}
+
+			count--;
+
+			*(buffer + written++) = value;
+		}
+
+	} else {
+		// normal read
+
+		written = io.read_proc(buffer, length, 1, handle);
+	}
+
+	return written;
+}
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapHeader(PCXHEADER *header) {
+	SwapShort(&header->window[0]);
+	SwapShort(&header->window[1]);
+	SwapShort(&header->window[2]);
+	SwapShort(&header->window[3]);
+	SwapShort(&header->hdpi);
+	SwapShort(&header->vdpi);
+	SwapShort(&header->bytes_per_line);
+	SwapShort(&header->palette_info);
+	SwapShort(&header->h_screen_size);
+	SwapShort(&header->v_screen_size);
+}
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+/*!
+    Returns the format string for the plugin. Each plugin,
+	both internal in the DLL and external in a .fip file, must have
+	a unique format string to be addressable.
+*/
+
+static const char * DLL_CALLCONV
+Format() {
+	return "PCX";
+}
+
+/*!
+    Returns a description string for the plugin. Though a
+	description is not necessary per-se,
+	it is advised to return an unique string in order to tell the
+	user what type of bitmaps this plugin will read and/or write.
+*/
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Zsoft Paintbrush";
+}
+
+/*!
+    Returns a comma separated list of file
+	extensions indicating what files this plugin can open. The
+	list, being used by FreeImage_GetFIFFromFilename, is usually
+	used as a last resort in finding the type of the bitmap we
+	are dealing with. Best is to check the first few bytes on
+	the low-level bits level first and compare them with a known
+	signature . If this fails, FreeImage_GetFIFFromFilename can be
+	used.
+*/
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "pcx";
+}
+
+/*!
+    Returns an (optional) regular expression to help
+	software identifying a bitmap type. The
+	expression can be applied to the first few bytes (header) of
+	the bitmap. FreeImage is not capable of processing regular expression itself,
+	but FreeImageQt, the FreeImage Trolltech support library, can. If RegExpr
+	returns NULL, FreeImageQt will automatically bypass Trolltech's regular
+	expression support and use its internal functions to find the bitmap type.
+*/
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-pcx";
+}
+
+/*!
+    Validates a bitmap by reading the first few bytes
+	and comparing them with a known bitmap signature.
+	TRUE is returned if the bytes match the signature, FALSE otherwise.
+	The Validate function is used by using FreeImage_GetFileType.
+	
+	Note: a plugin can safely read data any data from the bitmap without seeking back
+	to the original entry point; the entry point is stored prior to calling this
+	function and restored after.
+
+    Note: because of FreeImage's io redirection support, the header for the bitmap
+	must be on the start of the bitmap or at least on a known part in the bitmap. It is
+	forbidden to seek to the end of the bitmap or to a point relative to the end of a bitmap,
+	because the end of the bitmap is not always known.
+*/
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	return pcx_validate(io, handle);
+}
+
+/*!
+    This function is used to 'ask' the plugin if it can write
+	a bitmap in a certain bitdepth. Different bitmap types have different
+	capabilities, for example not all formats allow writing in palettized mode.
+	This function is there provide an uniform interface to the plugin's
+	capabilities. SupportsExportDepth returns TRUE if the plugin support writing
+	in the asked bitdepth, or FALSE if it doesn't. The function also
+	returns FALSE if bitmap saving is not supported by the plugin at all.
+*/
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+/*!
+    Loads a bitmap into memory. On entry it is assumed that
+	the bitmap to be loaded is of the correct type. If the bitmap
+	is of an incorrect type, the plugin might not gracefully fail but
+	crash or enter an endless loop. It is also assumed that all
+	the bitmap data is available at one time. If the bitmap is not complete,
+	for example because it is being downloaded while loaded, the plugin
+	might also not gracefully fail.
+
+	The Load function has the following parameters:
+
+    The first parameter (FreeImageIO *io) is a structure providing
+	function pointers in order to make use of FreeImage's IO redirection. Using
+	FreeImage's file i/o functions instead of standard ones it is garantueed
+	that all bitmap types, both current and future ones, can be loaded from
+	memory, file cabinets, the internet and more. The second parameter (fi_handle handle)
+	is a companion of FreeImageIO and can be best compared with the standard FILE* type,
+	in a generalized form.
+
+	The third parameter (int page) indicates wether we will be loading a certain page
+	in the bitmap or if we will load the default one. This parameter is only used if
+	the plugin supports multi-paged bitmaps, e.g. cabinet bitmaps that contain a series
+	of images or pages. If the plugin does support multi-paging, the page parameter
+	can contain either a number higher or equal to 0 to load a certain page, or -1 to 
+	load the default page. If the plugin does not support multi-paging,
+	the page parameter is always -1.
+	
+	The fourth parameter (int flags) manipulates the load function to load a bitmap
+	in a certain way. Every plugin has a different flag parameter with different meanings.
+
+	The last parameter (void *data) can contain a special data block used when
+	the file is read multi-paged. Because not every plugin supports multi-paging
+	not every plugin will use the data parameter and it will be set to NULL.However,
+	when the plugin does support multi-paging the parameter contains a pointer to a
+	block of data allocated by the Open function.
+*/
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	FIBITMAP *dib = NULL;
+	BYTE *bits;			  // Pointer to dib data
+	RGBQUAD *pal;		  // Pointer to dib palette
+	BYTE *line = NULL;	  // PCX raster line
+	BYTE *ReadBuf = NULL; // buffer;
+	BOOL bIsRLE;		  // True if the file is run-length encoded
+
+	if(!handle) {
+		return NULL;
+	}
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	try {
+		// check PCX identifier
+
+		long start_pos = io->tell_proc(handle);
+		BOOL validated = pcx_validate(io, handle);		
+		io->seek_proc(handle, start_pos, SEEK_SET);
+		if(!validated) {
+			throw FI_MSG_ERROR_MAGIC_NUMBER;
+		}
+
+		// process the header
+
+		PCXHEADER header;
+
+		if(io->read_proc(&header, sizeof(PCXHEADER), 1, handle) != 1) {
+			throw FI_MSG_ERROR_PARSING;
+		}
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapHeader(&header);
+#endif
+
+		// allocate a new DIB
+
+		unsigned width = header.window[2] - header.window[0] + 1;
+		unsigned height = header.window[3] - header.window[1] + 1;
+		unsigned bitcount = header.bpp * header.planes;
+
+		if (bitcount == 24) {
+			dib = FreeImage_AllocateHeader(header_only, width, height, bitcount, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		} else {
+			dib = FreeImage_AllocateHeader(header_only, width, height, bitcount);			
+		}
+
+		// if the dib couldn't be allocated, throw an error
+
+		if (!dib) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		// metrics handling code
+
+		FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)header.hdpi) / 0.0254000 + 0.5));
+		FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)header.vdpi) / 0.0254000 + 0.5));
+
+		// Set up the palette if needed
+		// ----------------------------
+
+		switch(bitcount) {
+			case 1:
+			{
+				pal = FreeImage_GetPalette(dib);
+				pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+				pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+				break;
+			}
+
+			case 4:
+			{
+				pal = FreeImage_GetPalette(dib);
+
+				BYTE *pColormap = &header.color_map[0];
+
+				for (int i = 0; i < 16; i++) {
+					pal[i].rgbRed   = pColormap[0];
+					pal[i].rgbGreen = pColormap[1];
+					pal[i].rgbBlue  = pColormap[2];
+					pColormap += 3;
+				}
+
+				break;
+			}
+
+			case 8:
+			{
+				BYTE palette_id;
+
+				io->seek_proc(handle, -769L, SEEK_END);
+				io->read_proc(&palette_id, 1, 1, handle);
+
+				if (palette_id == 0x0C) {
+					BYTE *cmap = (BYTE*)malloc(768 * sizeof(BYTE));
+					io->read_proc(cmap, 768, 1, handle);
+
+					pal = FreeImage_GetPalette(dib);
+					BYTE *pColormap = &cmap[0];
+
+					for(int i = 0; i < 256; i++) {
+						pal[i].rgbRed   = pColormap[0];
+						pal[i].rgbGreen = pColormap[1];
+						pal[i].rgbBlue  = pColormap[2];
+						pColormap += 3;
+					}
+
+					free(cmap);
+				}
+
+				// wrong palette ID, perhaps a gray scale is needed ?
+
+				else if (header.palette_info == 2) {
+					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;
+					}
+				}
+
+				io->seek_proc(handle, (long)sizeof(PCXHEADER), SEEK_SET);
+			}
+			break;
+		}
+
+		if(header_only) {
+			// header only mode
+			return dib;
+		}
+
+		// calculate the line length for the PCX and the DIB
+
+		// length of raster line in bytes
+		unsigned linelength = header.bytes_per_line * header.planes;
+		// length of DIB line (rounded to DWORD) in bytes
+		unsigned pitch = FreeImage_GetPitch(dib);
+
+		// run-length encoding ?
+
+		bIsRLE = (header.encoding == 1) ? TRUE : FALSE;
+
+		// load image data
+		// ---------------
+
+		line = (BYTE*)malloc(linelength * sizeof(BYTE));
+		if(!line) throw FI_MSG_ERROR_MEMORY;
+		
+		ReadBuf = (BYTE*)malloc(IO_BUF_SIZE * sizeof(BYTE));
+		if(!ReadBuf) throw FI_MSG_ERROR_MEMORY;
+		
+		bits = FreeImage_GetScanLine(dib, height - 1);
+
+		int ReadPos = IO_BUF_SIZE;
+
+		if ((header.planes == 1) && ((header.bpp == 1) || (header.bpp == 8))) {
+			BYTE skip;
+			unsigned written;
+
+			for (unsigned y = 0; y < height; y++) {
+				written = readline(*io, handle, bits, linelength, bIsRLE, ReadBuf, &ReadPos);
+
+				// skip trailing garbage at the end of the scanline
+
+				for (unsigned count = written; count < linelength; count++) {
+					if (ReadPos < IO_BUF_SIZE) {
+						ReadPos++;
+					} else {
+						io->read_proc(&skip, sizeof(BYTE), 1, handle);
+					}
+				}
+
+				bits -= pitch;
+			}
+		} else if ((header.planes == 4) && (header.bpp == 1)) {
+			BYTE bit,  mask, skip;
+			unsigned index;
+			BYTE *buffer;
+			unsigned x, y, written;
+
+			buffer = (BYTE*)malloc(width * sizeof(BYTE));
+			if(!buffer) throw FI_MSG_ERROR_MEMORY;
+
+			for (y = 0; y < height; y++) {
+				written = readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos);
+
+				// build a nibble using the 4 planes
+
+				memset(buffer, 0, width * sizeof(BYTE));
+
+				for(int plane = 0; plane < 4; plane++) {
+					bit = (BYTE)(1 << plane);
+
+					for (x = 0; x < width; x++) {
+						index = (unsigned)((x / 8) + plane * header.bytes_per_line);
+						mask = (BYTE)(0x80 >> (x & 0x07));
+						buffer[x] |= (line[index] & mask) ? bit : 0;
+					}
+				}
+
+				// then write the DIB row
+
+				for (x = 0; x < width / 2; x++) {
+					bits[x] = (buffer[2*x] << 4) | buffer[2*x+1];
+				}
+
+				// skip trailing garbage at the end of the scanline
+
+				for (unsigned count = written; count < linelength; count++) {
+					if (ReadPos < IO_BUF_SIZE) {
+						ReadPos++;
+					} else {
+						io->read_proc(&skip, sizeof(BYTE), 1, handle);
+					}
+				}
+
+				bits -= pitch;
+			}
+
+			free(buffer);
+
+		} else if((header.planes == 3) && (header.bpp == 8)) {
+			BYTE *pline;
+
+			for (unsigned y = 0; y < height; y++) {
+				readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos);
+
+				// convert the plane stream to BGR (RRRRGGGGBBBB -> BGRBGRBGRBGR)
+				// well, now with the FI_RGBA_x macros, on BIGENDIAN we convert to RGB
+
+				pline = line;
+				unsigned x;
+
+				for (x = 0; x < width; x++) {
+					bits[x * 3 + FI_RGBA_RED] = pline[x];						
+				}
+				pline += header.bytes_per_line;
+
+				for (x = 0; x < width; x++) {
+					bits[x * 3 + FI_RGBA_GREEN] = pline[x];
+				}
+				pline += header.bytes_per_line;
+
+				for (x = 0; x < width; x++) {
+					bits[x * 3 + FI_RGBA_BLUE] = pline[x];
+				}
+				pline += header.bytes_per_line;
+
+				bits -= pitch;
+			}
+		} else {
+			throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+		}
+
+		free(line);
+		free(ReadBuf);
+
+		return dib;
+
+	} catch (const char *text) {
+		// free allocated memory
+
+		if (dib != NULL) {
+			FreeImage_Unload(dib);
+		}
+		if (line != NULL) {
+			free(line);
+		}
+		if (ReadBuf != NULL) {
+			free(ReadBuf);
+		}
+
+		FreeImage_OutputMessageProc(s_format_id, text);
+	}
+	
+	return NULL;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+/*!
+    Initialises the plugin. The first parameter (Plugin *plugin)
+	contains a pointer to a pre-allocated Plugin structure
+	wherein pointers to the available plugin functions
+	has to be stored. The second parameter (int format_id) is an identification
+	number that the plugin may use to show plugin specific warning messages
+	or other information to the user. The plugin number
+	is generated by FreeImage and can differ everytime the plugin is
+	initialised.
+
+    If you want to create your own plugin you have to take some
+	rules into account. Plugin functions have to be compiled
+	__stdcall using the multithreaded c runtime libraries. Throwing
+	exceptions in plugin functions is allowed, as long as those exceptions
+	are being caught inside the same plugin. It is forbidden for a plugin
+	function to directly call FreeImage functions or to allocate memory
+	and pass it to the main DLL. Exception to this rule is the special file data
+	block that may be allocated the Open function. Allocating a FIBITMAP inside a
+	plugin can be using the function allocate_proc in the FreeImage structure,
+	which will allocate the memory using the DLL's c runtime library.
+*/
+
+void DLL_CALLCONV
+InitPCX(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginPFM.cpp b/files/Source/FreeImage/PluginPFM.cpp
new file mode 100644
index 0000000..ea3c46b
--- /dev/null
+++ b/files/Source/FreeImage/PluginPFM.cpp
@@ -0,0 +1,409 @@
+// ==========================================================
+// PFM Loader and Writer
+//
+// 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"
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/** maximum size of a line in the header */
+#define PFM_MAXLINE	256
+
+/** Big endian / Little endian float conversion */
+#define REVERSEBYTES(source, dest)		\
+{										\
+	char *j = (char *) source;			\
+	char *dj = (char *) dest;			\
+	dj[0] = j[3];						\
+	dj[1] = j[2];						\
+	dj[2] = j[1];						\
+	dj[3] = j[0];						\
+}
+
+/**
+Get a line from a ASCII io stream
+*/
+static BOOL 
+pfm_get_line(FreeImageIO *io, fi_handle handle, char *buffer, int length) {
+	int i;
+	memset(buffer, 0, length);
+	for(i = 0; i < length; i++) {
+		if(!io->read_proc(&buffer[i], 1, 1, handle))
+			return FALSE;
+		if(buffer[i] == 0x0A)
+			break;
+	}
+	
+	return (i < length) ? TRUE : FALSE;
+}
+
+/**
+Get an integer value from the actual position pointed by handle
+*/
+static int
+pfm_get_int(FreeImageIO *io, fi_handle handle) {
+    char c = 0;
+	BOOL bFirstChar;
+
+    // skip forward to start of next number
+
+	if(!io->read_proc(&c, 1, 1, handle)) {
+		throw FI_MSG_ERROR_PARSING;
+	}
+
+    while (1) {
+        // eat comments
+
+        if (c == '#') {
+			// if we're at a comment, read to end of line
+
+            bFirstChar = TRUE;
+
+            while (1) {
+				if(!io->read_proc(&c, 1, 1, handle)) {
+					throw FI_MSG_ERROR_PARSING;
+				}
+
+				if (bFirstChar && c == ' ') {
+					// loop off 1 sp after #
+					bFirstChar = FALSE;
+				} else if (c == '\n') {
+					break;
+				}
+			}
+		}
+
+        if (c >= '0' && c <='9') {
+			// we've found what we were looking for
+            break;
+		}
+
+		if(!io->read_proc(&c, 1, 1, handle)) {
+			throw FI_MSG_ERROR_PARSING;
+		}
+    }
+
+    // we're at the start of a number, continue until we hit a non-number
+
+    int i = 0;
+
+    while (1) {
+        i = (i * 10) + (c - '0');
+
+		if(!io->read_proc(&c, 1, 1, handle)) {
+			throw FI_MSG_ERROR_PARSING;
+		}
+
+		if (c < '0' || c > '9') {
+			break;
+		}
+    }
+
+    return i;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "PFM";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Portable floatmap";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "pfm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-portable-floatmap";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE pfm_id1[] = { 0x50, 0x46 };
+	BYTE pfm_id2[] = { 0x50, 0x66 };
+	BYTE signature[2] = { 0, 0 };
+
+	io->read_proc(signature, 1, sizeof(pfm_id1), handle);
+
+	if (memcmp(pfm_id1, signature, sizeof(pfm_id1)) == 0)
+		return TRUE;
+
+	if (memcmp(pfm_id2, signature, sizeof(pfm_id2)) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (
+		(type == FIT_FLOAT) ||
+		(type == FIT_RGBF)
+	);
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	char line_buffer[PFM_MAXLINE];
+	char id_one = 0, id_two = 0;
+	FIBITMAP *dib = NULL;
+	float *lineBuffer = NULL;
+
+	if (!handle) {
+		return NULL;
+	}
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	try {
+		FREE_IMAGE_TYPE image_type = FIT_UNKNOWN;
+
+		// Read the first two bytes of the file to determine the file format
+		// "PF" = color image
+		// "Pf" = greyscale image
+
+		io->read_proc(&id_one, 1, 1, handle);
+		io->read_proc(&id_two, 1, 1, handle);
+
+		if(id_one == 'P') {
+			if(id_two == 'F') {
+				image_type = FIT_RGBF;
+			} else if(id_two == 'f') {
+				image_type = FIT_FLOAT;
+			}
+		}
+		if(image_type == FIT_UNKNOWN) {
+			// signature error
+			throw FI_MSG_ERROR_MAGIC_NUMBER;
+		}
+
+		// Read the header information: width, height and the scale value
+		unsigned width  = (unsigned) pfm_get_int(io, handle);
+		unsigned height = (unsigned) pfm_get_int(io, handle);
+		float scalefactor = 1;
+
+		BOOL bResult = pfm_get_line(io, handle, line_buffer, PFM_MAXLINE);
+		if(bResult) {
+			bResult = (sscanf(line_buffer, "%f", &scalefactor) == 1) ? TRUE : FALSE;
+		}
+		if(!bResult) {
+			throw "Read error: invalid PFM header";
+		}
+
+		// Create a new DIB
+		dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height);
+		if (dib == NULL) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		if(header_only) {
+			// header only mode
+			return dib;
+		}
+
+		// Read the image...
+
+		if(image_type == FIT_RGBF) {
+			const unsigned lineWidth = 3 * width;
+			lineBuffer = (float*)malloc(lineWidth * sizeof(float));
+			if(!lineBuffer) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+
+			for (unsigned y = 0; y < height; y++) {	
+				FIRGBF *bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+				if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) {
+					throw "Read error";
+				}
+				float *channel = lineBuffer;
+				if(scalefactor > 0) {
+					// MSB
+					for (unsigned x = 0; x < width; x++) {
+						REVERSEBYTES(channel++, &bits[x].red);
+						REVERSEBYTES(channel++, &bits[x].green);
+						REVERSEBYTES(channel++, &bits[x].blue);
+					}
+				} else {
+					// LSB					
+					for (unsigned x = 0; x < width; x++) {
+						bits[x].red		= *channel++;
+						bits[x].green	= *channel++;
+						bits[x].blue	= *channel++;
+					}
+				}
+			}
+
+			free(lineBuffer);
+			lineBuffer = NULL;
+
+		} else if(image_type == FIT_FLOAT) {
+			const unsigned lineWidth = width;
+			lineBuffer = (float*)malloc(lineWidth * sizeof(float));
+			if(!lineBuffer) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+
+			for (unsigned y = 0; y < height; y++) {	
+				float *bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+				if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) {
+					throw "Read error";
+				}
+				float *channel = lineBuffer;
+				if(scalefactor > 0) {
+					// MSB - File is Big endian
+					for (unsigned x = 0; x < width; x++) {
+						REVERSEBYTES(channel++, &bits[x]);
+					}
+				} else {
+					// LSB - File is Little Endian
+					for (unsigned x = 0; x < width; x++) {
+						bits[x] = *channel++;
+					}
+				}
+			}
+
+			free(lineBuffer);
+			lineBuffer = NULL;
+		}
+		
+		return dib;
+
+	} catch (const char *text)  {
+		if(lineBuffer) free(lineBuffer);
+		if(dib) FreeImage_Unload(dib);
+
+		if(NULL != text) {
+			FreeImage_OutputMessageProc(s_format_id, text);
+		}
+
+		return NULL;
+	}
+
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	if(!dib || !handle) return FALSE;
+
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+	if((image_type != FIT_RGBF) && (image_type != FIT_FLOAT)) {
+		return FALSE;
+	}
+
+	unsigned width  = FreeImage_GetWidth(dib);
+	unsigned height = FreeImage_GetHeight(dib);
+	unsigned lineWidth = FreeImage_GetLine(dib);
+	
+	// save image as Little Endian
+	const float scalefactor = -1.0F;
+
+	char buffer[PFM_MAXLINE];	// temporary buffer whose size should be enough for what we need
+
+	// Find the appropriate magic number for this file type
+
+	char magic = 0;
+
+	switch(image_type) {
+		case FIT_RGBF:
+			magic = 'F';	// RGBF
+			break;	
+		case FIT_FLOAT:
+			magic = 'f';	// float greyscale
+			break;
+		default:
+			return FALSE;
+	}
+
+	// Write the header info
+
+	sprintf(buffer, "P%c\n%d %d\n%f\n", magic, width, height, scalefactor);
+	io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+	// Write the image data
+	for (unsigned y = 0; y < height; y++) {	
+		BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+		io->write_proc(bits, 1, lineWidth, handle);
+	}
+
+	return TRUE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPFM(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginPICT.cpp b/files/Source/FreeImage/PluginPICT.cpp
new file mode 100644
index 0000000..371056d
--- /dev/null
+++ b/files/Source/FreeImage/PluginPICT.cpp
@@ -0,0 +1,1343 @@
+// ==========================================================
+// Apple Macintosh QuickDraw/PICT Loader
+//
+// Design and implementation by
+// - Amir Ebrahimi (amir@unity3d.com)
+//
+// Based on PICT loading code from paintlib (http://www.paintlib.de/paintlib/).
+//
+// Paintlib License:
+// The paintlib source code and all documentation are copyright (c) 1996-2002 
+// Ulrich von Zadow and other contributors.
+//
+// The paintlib source code is supplied "AS IS". Ulrich von Zadow and other 
+// contributors disclaim all warranties, expressed or implied, including, without
+// limitation, the warranties of merchantability and of fitness for any purpose. 
+// The authors assume no liability for direct, indirect, incidental, special, 
+// exemplary, or consequential damages, which may result from the use of paintlib, 
+// even if advised of the possibility of such damage.
+//
+// Permission is hereby granted to use, copy, modify, and distribute this source 
+// code, or portions hereof, for any purpose, without fee, subject to the following 
+// restrictions:
+//
+// 1. The origin of this source code must not be misrepresented.
+// 2. Altered versions must be plainly marked as such and must not be misrepresented
+//    as being the original source.
+// 3. This Copyright notice may not be removed or altered from any source or altered 
+//    source distribution.
+// 4. Executables containing paintlib or parts of it must state that the software 
+//    "contains paintlib code. paintlib is copyright (c) 1996-2002 Ulrich von Zadow
+//    and other contributors.". This notice must be displayed in at least one place
+//    where the copyright for the software itself is displayed. The documentation must 
+//    also contain this notice.
+//
+// Bug fixes were made to the original code to support version 2 PICT files
+// properly.
+// 
+// Additional resources:
+// http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-458.html
+// http://www.fileformat.info/format/macpict/egff.htm
+// 
+// Notes (http://lists.apple.com/archives/java-dev/2006/Apr/msg00588.html):
+// There are three main types of PICT files:
+//  - Version 1
+//  - Version 2
+//  - Extended Version 2
+//
+// Some things to look out for:
+//  - The bounds and target DPI are stored in a different place in all three.
+//  - Some of the values are fixed-point shorts ( short / 65536f )
+//  - Values are big endian 
+//  - All of this may be *preceded* by a 512 byte header--sometimes it is
+//    there, and sometimes it isn't. You just have to check for the magic
+//	  values in both places.
+//
+// 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"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+static int s_format_id;
+
+static const int outputMessageSize = 256;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static BYTE
+Read8(FreeImageIO *io, fi_handle handle) {
+	BYTE i = 0;
+	io->read_proc(&i, 1, 1, handle);
+	return i;
+}
+
+static WORD
+Read16(FreeImageIO *io, fi_handle handle) {
+	// reads a two-byte big-endian integer from the given file and returns its value.
+	// assumes unsigned.
+	
+	unsigned hi = Read8(io, handle);
+	unsigned lo = Read8(io, handle);
+	return (WORD)(lo + (hi << 8));
+}
+
+static unsigned
+Read32(FreeImageIO *io, fi_handle handle) {
+	// reads a four-byte big-endian integer from the given file and returns its value.
+	// assumes unsigned.
+	
+	unsigned b3 = Read8(io, handle);
+	unsigned b2 = Read8(io, handle);
+	unsigned b1 = Read8(io, handle);
+	unsigned b0 = Read8(io, handle);
+	return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
+}
+
+// ----------------------------------------------------------
+
+struct OpDef
+{
+	const char * name;
+	int    len;
+	const char * description;
+};
+
+// for reserved opcodes
+#define res(length) { "reserved", (length), "reserved for Apple use" }
+#define RGB_LEN 6
+#define WORD_LEN -1
+#define NA 0
+
+static OpDef optable[] =
+{
+/* 0x00 */  { "NOP",               0, "nop" },
+/* 0x01 */  { "Clip",             NA, "clip" },
+/* 0x02 */  { "BkPat",             8, "background pattern" },
+/* 0x03 */  { "TxFont",            2, "text font (word)" },
+/* 0x04 */  { "TxFace",            1, "text face (byte)" },
+/* 0x05 */  { "TxMode",            2, "text mode (word)" },
+/* 0x06 */  { "SpExtra",           4, "space extra (fixed point)" },
+/* 0x07 */  { "PnSize",            4, "pen size (point)" },
+/* 0x08 */  { "PnMode",            2, "pen mode (word)" },
+/* 0x09 */  { "PnPat",             8, "pen pattern" },
+/* 0x0a */  { "FillPat",           8, "fill pattern" },
+/* 0x0b */  { "OvSize",            4, "oval size (point)" },
+/* 0x0c */  { "Origin",            4, "dh, dv (word)" },
+/* 0x0d */  { "TxSize",            2, "text size (word)" },
+/* 0x0e */  { "FgColor",           4, "foreground color (longword)" },
+/* 0x0f */  { "BkColor",           4, "background color (longword)" },
+/* 0x10 */  { "TxRatio",           8, "numerator (point), denominator (point)" },
+/* 0x11 */  { "Version",           1, "version (byte)" },
+/* 0x12 */  { "BkPixPat",         NA, "color background pattern" },
+/* 0x13 */  { "PnPixPat",         NA, "color pen pattern" },
+/* 0x14 */  { "FillPixPat",       NA, "color fill pattern" },
+/* 0x15 */  { "PnLocHFrac",        2, "fractional pen position" },
+/* 0x16 */  { "ChExtra",           2, "extra for each character" },
+/* 0x17 */  res(0),
+/* 0x18 */  res(0),
+/* 0x19 */  res(0),
+/* 0x1a */  { "RGBFgCol",    RGB_LEN, "RGB foreColor" },
+/* 0x1b */  { "RGBBkCol",    RGB_LEN, "RGB backColor" },
+/* 0x1c */  { "HiliteMode",        0, "hilite mode flag" },
+/* 0x1d */  { "HiliteColor", RGB_LEN, "RGB hilite color" },
+/* 0x1e */  { "DefHilite",         0, "Use default hilite color" },
+/* 0x1f */  { "OpColor",           6, "RGB OpColor for arithmetic modes" },
+/* 0x20 */  { "Line",              8, "pnLoc (point), newPt (point)" },
+/* 0x21 */  { "LineFrom",          4, "newPt (point)" },
+/* 0x22 */  { "ShortLine",         6, "pnLoc (point, dh, dv (-128 .. 127))" },
+/* 0x23 */  { "ShortLineFrom",     2, "dh, dv (-128 .. 127)" },
+/* 0x24 */  res(WORD_LEN),
+/* 0x25 */  res(WORD_LEN),
+/* 0x26 */  res(WORD_LEN),
+/* 0x27 */  res(WORD_LEN),
+/* 0x28 */  { "LongText",         NA, "txLoc (point), count (0..255), text" },
+/* 0x29 */  { "DHText",           NA, "dh (0..255), count (0..255), text" },
+/* 0x2a */  { "DVText",           NA, "dv (0..255), count (0..255), text" },
+/* 0x2b */  { "DHDVText",         NA, "dh, dv (0..255), count (0..255), text" },
+/* 0x2c */  res(WORD_LEN),
+/* 0x2d */  res(WORD_LEN),
+/* 0x2e */  res(WORD_LEN),
+/* 0x2f */  res(WORD_LEN),
+/* 0x30 */  { "frameRect",         8, "rect" },
+/* 0x31 */  { "paintRect",         8, "rect" },
+/* 0x32 */  { "eraseRect",         8, "rect" },
+/* 0x33 */  { "invertRect",        8, "rect" },
+/* 0x34 */  { "fillRect",          8, "rect" },
+/* 0x35 */  res(8),
+/* 0x36 */  res(8),
+/* 0x37 */  res(8),
+/* 0x38 */  { "frameSameRect",     0, "rect" },
+/* 0x39 */  { "paintSameRect",     0, "rect" },
+/* 0x3a */  { "eraseSameRect",     0, "rect" },
+/* 0x3b */  { "invertSameRect",    0, "rect" },
+/* 0x3c */  { "fillSameRect",      0, "rect" },
+/* 0x3d */  res(0),
+/* 0x3e */  res(0),
+/* 0x3f */  res(0),
+/* 0x40 */  { "frameRRect",        8, "rect" },
+/* 0x41 */  { "paintRRect",        8, "rect" },
+/* 0x42 */  { "eraseRRect",        8, "rect" },
+/* 0x43 */  { "invertRRect",       8, "rect" },
+/* 0x44 */  { "fillRRrect",        8, "rect" },
+/* 0x45 */  res(8),
+/* 0x46 */  res(8),
+/* 0x47 */  res(8),
+/* 0x48 */  { "frameSameRRect",    0, "rect" },
+/* 0x49 */  { "paintSameRRect",    0, "rect" },
+/* 0x4a */  { "eraseSameRRect",    0, "rect" },
+/* 0x4b */  { "invertSameRRect",   0, "rect" },
+/* 0x4c */  { "fillSameRRect",     0, "rect" },
+/* 0x4d */  res(0),
+/* 0x4e */  res(0),
+/* 0x4f */  res(0),
+/* 0x50 */  { "frameOval",         8, "rect" },
+/* 0x51 */  { "paintOval",         8, "rect" },
+/* 0x52 */  { "eraseOval",         8, "rect" },
+/* 0x53 */  { "invertOval",        8, "rect" },
+/* 0x54 */  { "fillOval",          8, "rect" },
+/* 0x55 */  res(8),
+/* 0x56 */  res(8),
+/* 0x57 */  res(8),
+/* 0x58 */  { "frameSameOval",     0, "rect" },
+/* 0x59 */  { "paintSameOval",     0, "rect" },
+/* 0x5a */  { "eraseSameOval",     0, "rect" },
+/* 0x5b */  { "invertSameOval",    0, "rect" },
+/* 0x5c */  { "fillSameOval",      0, "rect" },
+/* 0x5d */  res(0),
+/* 0x5e */  res(0),
+/* 0x5f */  res(0),
+/* 0x60 */  { "frameArc",         12, "rect, startAngle, arcAngle" },
+/* 0x61 */  { "paintArc",         12, "rect, startAngle, arcAngle" },
+/* 0x62 */  { "eraseArc",         12, "rect, startAngle, arcAngle" },
+/* 0x63 */  { "invertArc",        12, "rect, startAngle, arcAngle" },
+/* 0x64 */  { "fillArc",          12, "rect, startAngle, arcAngle" },
+/* 0x65 */  res(12),
+/* 0x66 */  res(12),
+/* 0x67 */  res(12),
+/* 0x68 */  { "frameSameArc",      4, "rect, startAngle, arcAngle" },
+/* 0x69 */  { "paintSameArc",      4, "rect, startAngle, arcAngle" },
+/* 0x6a */  { "eraseSameArc",      4, "rect, startAngle, arcAngle" },
+/* 0x6b */  { "invertSameArc",     4, "rect, startAngle, arcAngle" },
+/* 0x6c */  { "fillSameArc",       4, "rect, startAngle, arcAngle" },
+/* 0x6d */  res(4),
+/* 0x6e */  res(4),
+/* 0x6f */  res(4),
+/* 0x70 */  { "framePoly",        NA, "poly" },
+/* 0x71 */  { "paintPoly",        NA, "poly" },
+/* 0x72 */  { "erasePoly",        NA, "poly" },
+/* 0x73 */  { "invertPoly",       NA, "poly" },
+/* 0x74 */  { "fillPoly",         NA, "poly" },
+/* 0x75 */  res(NA),
+/* 0x76 */  res(NA),
+/* 0x77 */  res(NA),
+/* 0x78 */  { "frameSamePoly",     0, "poly (NYI)" },
+/* 0x79 */  { "paintSamePoly",     0, "poly (NYI)" },
+/* 0x7a */  { "eraseSamePoly",     0, "poly (NYI)" },
+/* 0x7b */  { "invertSamePoly",    0, "poly (NYI)" },
+/* 0x7c */  { "fillSamePoly",      0, "poly (NYI)" },
+/* 0x7d */  res(0),
+/* 0x7e */  res(0),
+/* 0x7f */  res(0),
+/* 0x80 */  { "frameRgn",         NA, "region" },
+/* 0x81 */  { "paintRgn",         NA, "region" },
+/* 0x82 */  { "eraseRgn",         NA, "region" },
+/* 0x83 */  { "invertRgn",        NA, "region" },
+/* 0x84 */  { "fillRgn",          NA, "region" },
+/* 0x85 */  res(NA),
+/* 0x86 */  res(NA),
+/* 0x87 */  res(NA),
+/* 0x88 */  { "frameSameRgn",      0, "region (NYI)" },
+/* 0x89 */  { "paintSameRgn",      0, "region (NYI)" },
+/* 0x8a */  { "eraseSameRgn",      0, "region (NYI)" },
+/* 0x8b */  { "invertSameRgn",     0, "region (NYI)" },
+/* 0x8c */  { "fillSameRgn",       0, "region (NYI)" },
+/* 0x8d */  res(0),
+/* 0x8e */  res(0),
+/* 0x8f */  res(0),
+/* 0x90 */  { "BitsRect",         NA, "copybits, rect clipped" },
+/* 0x91 */  { "BitsRgn",          NA, "copybits, rgn clipped" },
+/* 0x92 */  res(WORD_LEN),
+/* 0x93 */  res(WORD_LEN),
+/* 0x94 */  res(WORD_LEN),
+/* 0x95 */  res(WORD_LEN),
+/* 0x96 */  res(WORD_LEN),
+/* 0x97 */  res(WORD_LEN),
+/* 0x98 */  { "PackBitsRect",     NA, "packed copybits, rect clipped" },
+/* 0x99 */  { "PackBitsRgn",      NA, "packed copybits, rgn clipped" },
+/* 0x9a */  { "Opcode_9A",        NA, "the mysterious opcode 9A" },
+/* 0x9b */  res(WORD_LEN),
+/* 0x9c */  res(WORD_LEN),
+/* 0x9d */  res(WORD_LEN),
+/* 0x9e */  res(WORD_LEN),
+/* 0x9f */  res(WORD_LEN),
+/* 0xa0 */  { "ShortComment",      2, "kind (word)" },
+/* 0xa1 */  { "LongComment",      NA, "kind (word), size (word), data" }
+};
+
+// ----------------------------------------------------------
+
+struct MacRect
+{
+	WORD top;
+	WORD left;
+	WORD bottom;
+	WORD right;
+};
+
+struct MacpixMap
+{
+	// Ptr baseAddr              // Not used in file.
+	// short rowBytes            // read in seperatly.
+	struct MacRect Bounds;
+	WORD version;
+	WORD packType;
+	LONG packSize;
+	LONG hRes;
+	LONG vRes;
+	WORD pixelType;
+	WORD pixelSize;
+	WORD cmpCount;
+	WORD cmpSize;
+	LONG planeBytes;
+	LONG pmTable;
+	LONG pmReserved;
+};
+
+struct MacRGBColour
+{
+	WORD red;
+	WORD green;
+	WORD blue;
+};
+
+struct MacPoint
+{
+	WORD x;
+	WORD y;
+};
+
+struct MacPattern // Klaube
+{
+	BYTE pix[64];
+};
+
+// ----------------------------------------------------------
+
+static void 
+ReadRect( FreeImageIO *io, fi_handle handle, MacRect* rect ) {
+	rect->top = Read16( io, handle );
+	rect->left = Read16( io, handle );
+	rect->bottom = Read16( io, handle );
+	rect->right = Read16( io, handle );
+}
+
+static void 
+ReadPixmap( FreeImageIO *io, fi_handle handle, MacpixMap* pPixMap ) {
+	pPixMap->version = Read16( io, handle );
+	pPixMap->packType = Read16( io, handle );
+	pPixMap->packSize = Read32( io, handle );
+	pPixMap->hRes = Read16( io, handle );
+	Read16( io, handle );
+	pPixMap->vRes = Read16( io, handle );
+	Read16( io, handle );
+	pPixMap->pixelType = Read16( io, handle );
+	pPixMap->pixelSize = Read16( io, handle );
+	pPixMap->cmpCount = Read16( io, handle );
+	pPixMap->cmpSize = Read16( io, handle );
+	pPixMap->planeBytes = Read32( io, handle );
+	pPixMap->pmTable = Read32( io, handle );
+	pPixMap->pmReserved = Read32( io, handle );
+}
+
+/**
+Reads a mac color table into a bitmap palette.
+*/
+static void 
+ReadColorTable( FreeImageIO *io, fi_handle handle, WORD* pNumColors, RGBQUAD* pPal ) {
+	LONG        ctSeed;
+	WORD        ctFlags;
+	WORD        val;
+	int         i;
+	
+	ctSeed = Read32( io, handle );
+	ctFlags = Read16( io, handle );
+	WORD numColors = Read16( io, handle )+1;
+	*pNumColors = numColors;
+	
+	for (i = 0; i < numColors; i++) {
+		val = Read16( io, handle );
+		if (ctFlags & 0x8000) {
+			// The indicies in a device colour table are bogus and
+			// usually == 0, so I assume we allocate up the list of
+			// colours in order.
+			val = (WORD)i;
+		}
+		if (val >= numColors) {
+			throw "pixel value greater than color table size.";
+		}
+		// Mac colour tables contain 16-bit values for R, G, and B...
+		pPal[val].rgbRed = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF));
+		pPal[val].rgbGreen = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF));
+		pPal[val].rgbBlue = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF));
+	}
+}
+
+/**
+skips unneeded packbits.
+pixelSize == Source bits per pixel.
+*/
+static void 
+SkipBits( FreeImageIO *io, fi_handle handle, MacRect* bounds, WORD rowBytes, int pixelSize ) {
+	int    i;
+	WORD   pixwidth;           // bytes per row when uncompressed.
+	
+	int height = bounds->bottom - bounds->top;
+	int width = bounds->right - bounds->left;
+	
+	// High bit of rowBytes is flag.
+	if (pixelSize <= 8) {
+		rowBytes &= 0x7fff;
+	}
+	pixwidth = (WORD)width;
+	
+	if (pixelSize == 16) {
+		pixwidth *= 2;
+	}
+	if (rowBytes == 0) {
+		rowBytes = pixwidth;
+	}
+	if (rowBytes < 8) {
+		io->seek_proc( handle, rowBytes*height, SEEK_CUR );
+	}
+	else {
+		for (i = 0; i < height; i++) {
+			int lineLen;            // length of source line in bytes.
+			if (rowBytes > 250) {
+				lineLen = Read16( io, handle );
+			} else {
+				lineLen = Read8( io, handle );
+			}
+			io->seek_proc( handle, lineLen, SEEK_CUR );
+		}
+	}
+}
+
+/**
+Skip polygon or region
+*/
+static void 
+SkipPolyOrRegion( FreeImageIO *io, fi_handle handle ) {
+	WORD len = Read16( io, handle ) - 2;
+	io->seek_proc(handle, len, SEEK_CUR);	
+}
+
+/**
+Width in bytes for 8 bpp or less.
+Width in pixels for 16 bpp.
+Expands Width units to 32-bit pixel data.
+*/
+static void 
+expandBuf( FreeImageIO *io, fi_handle handle, int width, int bpp, BYTE* dst ) { 
+	switch (bpp) {
+		case 16:
+			for ( int i=0; i<width; i++) {
+				WORD src = Read16( io, handle );
+				dst[ FI_RGBA_BLUE ] = (src & 31)*8;				// Blue
+				dst[ FI_RGBA_GREEN ] = ((src >> 5) & 31)*8;		// Green
+				dst[ FI_RGBA_RED ] = ((src >> 10) & 31)*8;		// Red
+				dst[ FI_RGBA_ALPHA ] = 0xFF;					// Alpha
+				dst += 4;
+			}
+			break;
+		default:
+			throw "Bad bits per pixel in expandBuf.";
+	}
+}
+
+/**
+Expands Width units to 8-bit pixel data.
+Max. 8 bpp source format.
+*/
+static void 
+expandBuf8( FreeImageIO *io, fi_handle handle, int width, int bpp, BYTE* dst )
+{
+	switch (bpp) {
+		case 8:
+			io->read_proc( dst, width, 1, handle );
+			break;
+		case 4:
+			for (int i = 0; i < width; i++) {
+				WORD src = Read8( io, handle );
+				*dst = (src >> 4) & 15;
+				*(dst+1) = (src & 15);
+				dst += 2;
+			}
+			if (width & 1) { // Odd Width?
+				WORD src = Read8( io, handle );
+				*dst = (src >> 4) & 15;
+				dst++;
+			}
+			break;
+		case 2:
+			for (int i = 0; i < width; i++) {
+				WORD src = Read8( io, handle );
+				*dst = (src >> 6) & 3;
+				*(dst+1) = (src >> 4) & 3;
+				*(dst+2) = (src >> 2) & 3;
+				*(dst+3) = (src & 3);
+				dst += 4;
+			}
+			if (width & 3)  { // Check for leftover pixels
+				for (int i = 6; i > 8 - (width & 3) * 2; i -= 2) {
+					WORD src = Read8( io, handle );
+					*dst = (src >> i) & 3;
+					dst++;
+				}
+			}
+			break;
+		case 1:
+			for (int i = 0; i < width; i++) {
+				WORD src = Read8( io, handle );
+				*dst = (src >> 7) & 1;
+				*(dst+1) = (src >> 6) & 1;
+				*(dst+2) = (src >> 5) & 1;
+				*(dst+3) = (src >> 4) & 1;
+				*(dst+4) = (src >> 3) & 1;
+				*(dst+5) = (src >> 2) & 1;
+				*(dst+6) = (src >> 1) & 1;
+				*(dst+7) = (src  & 1);
+				dst += 8;
+			}
+			if (width & 7) {  // Check for leftover pixels
+				for (int i = 7; i > (8-width & 7); i--) {
+					WORD src = Read8( io, handle );
+					*dst = (src >> i) & 1;
+					dst++;
+				}
+			}
+			break;
+		default:
+			throw "Bad bits per pixel in expandBuf8.";
+	}
+}
+
+static BYTE* 
+UnpackPictRow( FreeImageIO *io, fi_handle handle, BYTE* pLineBuf, int width, int rowBytes, int srcBytes ) {	
+
+	if (rowBytes < 8) { // Ah-ha!  The bits aren't actually packed.  This will be easy.
+		io->read_proc( pLineBuf, rowBytes, 1, handle );
+	}
+	else {
+		BYTE* pCurPixel = pLineBuf;
+		
+		// Unpack RLE. The data is packed bytewise.
+		for (int j = 0; j < srcBytes; )	{
+			BYTE FlagCounter = Read8( io, handle );
+			if (FlagCounter & 0x80) {
+				if (FlagCounter == 0x80) {
+					// Special case: repeat value of 0.
+					// Apple says ignore.
+					j++;
+				} else { 
+					// Packed data.
+					int len = ((FlagCounter ^ 255) & 255) + 2;					
+					BYTE p = Read8( io, handle );
+					memset( pCurPixel, p, len);
+					pCurPixel += len;
+					j += 2;
+				}
+			}
+			else { 
+				// Unpacked data
+				int len = (FlagCounter & 255) + 1;
+				io->read_proc( pCurPixel, len, 1, handle );
+				pCurPixel += len;
+				j += len + 1;
+			}
+		}
+	}
+	
+	return pLineBuf;
+}
+
+/**
+This routine decompresses BitsRects with a packType of 4 (and 32 bits per pixel). 
+In this format, each line is separated into 8-bit-bitplanes and then compressed via RLE. 
+To decode, the routine decompresses each line & then juggles the bytes around to get pixel-oriented data.
+NumBitPlanes == 3 if RGB, 4 if RGBA
+*/
+static void 
+Unpack32Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes, int numPlanes ) {
+	int height = bounds->bottom - bounds->top;
+	int width = bounds->right - bounds->left;
+	
+	if (rowBytes == 0) {
+		rowBytes = (WORD)( width * 4 );
+	}
+	
+	BYTE* pLineBuf = (BYTE*)malloc( rowBytes ); // Let's allocate enough for 4 bit planes
+	if ( pLineBuf )	{
+		try	{
+			for ( int i = 0; i < height; i++ ) { 
+				// for each line do...
+				int linelen;            // length of source line in bytes.
+				if (rowBytes > 250) {
+					linelen = Read16( io, handle );
+				} else {
+					linelen = Read8( io, handle);
+				}
+				
+				BYTE* pBuf = UnpackPictRow( io, handle, pLineBuf, width, rowBytes, linelen );
+				
+				// Convert plane-oriented data into pixel-oriented data &
+				// copy into destination bitmap.
+				BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
+				
+				if ( numPlanes == 3 ) {
+					for ( int j = 0; j < width; j++ ) { 
+						// For each pixel in line...
+						dst[ FI_RGBA_BLUE ] = (*(pBuf+width*2));     // Blue
+						dst[ FI_RGBA_GREEN ] = (*(pBuf+width));       // Green
+						dst[ FI_RGBA_RED ] = (*pBuf);             // Red
+						dst[ FI_RGBA_ALPHA ] = (0xFF);
+						dst += 4;
+						pBuf++;
+					}
+				} else {
+					for ( int j = 0; j < width; j++ ) { 
+						// For each pixel in line...
+						dst[ FI_RGBA_BLUE ] = (*(pBuf+width*3));     // Blue
+						dst[ FI_RGBA_GREEN ] = (*(pBuf+width*2));     // Green
+						dst[ FI_RGBA_RED ] = (*(pBuf+width));       // Red
+						dst[ FI_RGBA_ALPHA ] = (*pBuf);
+						dst += 4;
+						pBuf++;
+					}
+				}
+			}
+		}
+		catch( ... ) {
+			free( pLineBuf );
+			throw;
+		}
+	}
+	free( pLineBuf );
+}
+
+/**
+Decompression routine for 8 bpp. 
+rowBytes is the number of bytes each source row would take if it were uncompressed.
+This _isn't_ equal to the number of pixels in the row - it seems apple pads the data to a word boundary and then compresses it. 
+Of course, we have to decompress the excess data and then throw it away.
+*/
+static void 
+Unpack8Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes ) {	
+	int height = bounds->bottom - bounds->top;
+	int width = bounds->right - bounds->left;
+	
+	// High bit of rowBytes is flag.
+	rowBytes &= 0x7fff;
+	
+	if (rowBytes == 0) {
+		rowBytes = (WORD)width;
+	}
+	
+	for ( int i = 0; i < height; i++ ) {
+		int linelen;            // length of source line in bytes.
+		if (rowBytes > 250) {
+			linelen = Read16( io, handle );
+		} else {
+			linelen = Read8( io, handle );
+		}
+		BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);				
+		dst = UnpackPictRow( io, handle, dst, width, rowBytes, linelen );
+	}
+}
+
+/**
+Decompression routine for everything but 8 & 32 bpp. 
+This routine is slower than the two routines above since it has to deal with a lot of special cases :-(.
+It's also a bit chaotic because of these special cases...
+unpack8bits is basically a dumber version of unpackbits.
+pixelSize == Source bits per pixel.
+*/
+static void 
+UnpackBits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes, int pixelSize ) {
+	WORD   pixwidth;           // bytes per row when uncompressed.
+	int    pkpixsize;
+	int    PixelPerRLEUnit;
+
+	char outputMessage[ outputMessageSize ] = "";
+	
+	int height = bounds->bottom - bounds->top;
+	int width = bounds->right - bounds->left;
+	
+	// High bit of rowBytes is flag.
+	if (pixelSize <= 8) {
+		rowBytes &= 0x7fff;
+	}
+	
+	pixwidth = (WORD)width;
+	pkpixsize = 1;          // RLE unit: one byte for everything...
+	if (pixelSize == 16) {    // ...except 16 bpp.
+		pkpixsize = 2;
+		pixwidth *= 2;
+	}
+	
+	if (rowBytes == 0) {
+		rowBytes = pixwidth;
+	}
+	
+	{
+		// I allocate the temporary line buffer here. I allocate too
+		// much memory to compensate for sloppy (& hence fast) decompression.
+		switch (pixelSize) {
+			case 1:
+				PixelPerRLEUnit = 8;
+				break;
+			case 2:
+				PixelPerRLEUnit = 4;
+				break;
+			case 4:
+				PixelPerRLEUnit = 2;
+				break;
+			case 8:
+				PixelPerRLEUnit = 1;
+				break;
+			case 16:
+				PixelPerRLEUnit = 1;
+				break;
+			default:
+				sprintf( outputMessage, "Illegal bpp value in unpackbits: %d\n", pixelSize );
+				throw outputMessage;
+		}
+		
+		if (rowBytes < 8) { 
+			// ah-ha!  The bits aren't actually packed.  This will be easy.
+			for ( int i = 0; i < height; i++ ) {
+				BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
+				if (pixelSize == 16) {
+					expandBuf( io, handle, width, pixelSize, dst );
+				} else {
+					expandBuf8( io, handle, width, pixelSize, dst );
+				}
+			}
+		}
+		else {
+			for ( int i = 0; i < height; i++ ) { 
+				// For each line do...
+				int    linelen;            // length of source line in bytes.
+				if (rowBytes > 250) {
+					linelen = Read16( io, handle );
+				} else {
+					linelen = Read8( io, handle );
+				}
+				
+				BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
+				BYTE FlagCounter;
+				
+				// Unpack RLE. The data is packed bytewise - except for
+				// 16 bpp data, which is packed per pixel :-(.
+				for ( int j = 0; j < linelen; ) {
+					FlagCounter = Read8( io, handle );
+					if (FlagCounter & 0x80) {
+						if (FlagCounter == 0x80) {
+							// Special case: repeat value of 0.
+							// Apple says ignore.
+							j++;
+						}
+						else { 
+							// Packed data.
+							int len = ((FlagCounter ^ 255) & 255) + 2;
+							
+							// This is slow for some formats...
+							if (pixelSize == 16) {
+								expandBuf( io, handle, 1, pixelSize, dst );
+								for ( int k = 1; k < len; k++ ) { 
+									// Repeat the pixel len times.
+									memcpy( dst+(k*4*PixelPerRLEUnit), dst,	4*PixelPerRLEUnit);
+								}
+								dst += len*4*PixelPerRLEUnit;
+							}
+							else {
+								expandBuf8( io, handle, 1, pixelSize, dst );
+								for ( int k = 1; k < len; k++ ) { 
+									// Repeat the expanded byte len times.
+									memcpy( dst+(k*PixelPerRLEUnit), dst, PixelPerRLEUnit);
+								}
+								dst += len*PixelPerRLEUnit;
+							}
+							j += pkpixsize + 1;
+						}
+					}
+					else { 
+						// Unpacked data
+						int len = (FlagCounter & 255) + 1;
+						if (pixelSize == 16) {
+							expandBuf( io, handle, len, pixelSize, dst );
+							dst += len*4*PixelPerRLEUnit;
+						}
+						else {
+							expandBuf8( io, handle, len, pixelSize, dst );
+							dst += len*PixelPerRLEUnit;
+						}
+						j += ( len * pkpixsize ) + 1;
+					}
+				}
+			}
+		}
+	}
+}
+
+static void 
+DecodeOp9a( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacpixMap* pixMap ) {
+	// Do the actual unpacking.
+	switch ( pixMap->pixelSize ) {
+		case 32:
+			Unpack32Bits( io, handle, dib, &pixMap->Bounds, 0, pixMap->cmpCount );
+			break;
+		case 8:
+			Unpack8Bits( io, handle, dib, &pixMap->Bounds, 0 );
+			break;
+		default:
+			UnpackBits( io, handle, dib, &pixMap->Bounds, 0, pixMap->pixelSize );
+	}
+}
+
+static void 
+DecodeBitmap( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, BOOL isRegion, MacRect* bounds, WORD rowBytes ) {
+	WORD mode = Read16( io, handle );
+	
+	if ( isRegion ) {
+		SkipPolyOrRegion( io, handle );
+	}
+	
+	RGBQUAD* pal = FreeImage_GetPalette( dib );
+	if ( !pal ) {
+		throw "No palette for bitmap!";
+	}
+	
+	for (int i = 0; i < 2; i++) {
+		unsigned char val = i ? 0xFF : 0x0;
+		pal[i].rgbRed = val;
+		pal[i].rgbGreen = val;
+		pal[i].rgbBlue = val;
+	}
+	
+	UnpackBits( io, handle, dib, bounds, rowBytes, 1 );
+}
+
+static void 
+DecodePixmap( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, BOOL isRegion, MacpixMap* pixMap, WORD rowBytes ) {
+	// Read mac colour table into windows palette.
+	WORD numColors;    // Palette size.
+	RGBQUAD ct[256];
+	
+	ReadColorTable( io, handle, &numColors, ct );
+	if ( FreeImage_GetBPP( dib ) == 8 ) {
+		RGBQUAD* pal = FreeImage_GetPalette( dib );
+		if ( !pal ) {
+			throw "No palette for bitmap!";
+		}
+		
+		for (int i = 0; i < numColors; i++) {
+			pal[i].rgbRed = ct[ i ].rgbRed;
+			pal[i].rgbGreen = ct[ i ].rgbGreen;
+			pal[i].rgbBlue = ct[ i ].rgbBlue;
+		}
+	}
+	
+	// Ignore source & destination rectangle as well as transfer mode.
+	MacRect tempRect;
+	ReadRect( io, handle, &tempRect );
+	ReadRect( io, handle, &tempRect );
+	WORD mode = Read16( io, handle );
+	
+	if ( isRegion) {
+		SkipPolyOrRegion( io, handle );
+	}
+	
+	switch ( pixMap->pixelSize ) {
+		case 32:
+			Unpack32Bits( io, handle, dib, &pixMap->Bounds, rowBytes, pixMap->cmpCount );
+			break;
+		case 8:
+			Unpack8Bits( io, handle, dib, &pixMap->Bounds, rowBytes );
+			break;
+		default:
+			UnpackBits( io, handle, dib, &pixMap->Bounds, rowBytes, pixMap->pixelSize );
+	}
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "PICT";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Macintosh PICT";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "pct,pict,pic";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-pict";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	if(io->seek_proc(handle, 522, SEEK_SET) == 0) {
+		BYTE pict_signature[] = { 0x00, 0x11, 0x02, 0xFF, 0x0C, 0X00 };
+		BYTE signature[6];
+
+		if(io->read_proc(signature, 1, sizeof(pict_signature), handle)) {
+			// v1.0 files have 0x11 (version operator) followed by 0x01 (version number)
+			// v2.0 files have 0x0011 (version operator) followed by 0x02ff (version number)
+			//   and additionally 0x0c00 as a header opcode
+			// Currently, we are only supporting v2.0
+			return (memcmp(pict_signature, signature, sizeof(pict_signature)) == 0);
+		} else {
+			return FALSE;
+		}
+	}
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return FALSE;
+}
+
+/**
+This plugin decodes macintosh PICT files with 1,2,4,8,16 and 32 bits per pixel as well as PICT/JPEG. 
+If an alpha channel is present in a 32-bit-PICT, it is decoded as well. 
+The PICT format is a general picture file format and can contain a lot of other elements besides bitmaps. 
+These elements are ignored.	
+*/
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	char outputMessage[ outputMessageSize ] = "";
+	FIBITMAP* dib = NULL;
+	try {		
+		// Skip empty 512 byte header.
+		if ( !io->seek_proc(handle, 512, SEEK_CUR) == 0 )
+			return NULL;
+		
+		// Read PICT header
+		Read16( io, handle ); // Skip version 1 picture size
+		
+		MacRect frame;
+		ReadRect( io, handle, &frame );
+
+		BYTE b = 0;
+		while ((b = Read8(io, handle)) == 0);
+		if ( b != 0x11 ) {
+			throw "invalid header: version number missing.";
+		}
+		
+		int version = Read8( io, handle );
+		if ( version == 2 && Read8( io, handle ) != 0xff ) {
+			throw "invalid header: illegal version number.";
+		}
+		
+		enum PICTType {none, op9a, jpeg, pixmap, bitmap};
+		PICTType pictType = none;
+		
+		MacRect bounds;
+		MacpixMap pixMap;
+		int hRes = 0x480000; // in pixels/inch (72 by default == 0x480000 in fixed point)
+		int vRes = 0x480000; // in pixels/inch (72 by default == 0x480000 in fixed point)
+		WORD rowBytes = 0;
+		BOOL isRegion = FALSE;
+		BOOL done = FALSE;
+		long currentPos = 0;
+
+		while ( !done ) {
+			WORD opcode = 0;
+
+			// get the current stream position (used to avoid infinite loops)
+			currentPos = io->tell_proc(handle);
+			
+			if ((version == 1) || ((io->tell_proc( handle ) % 2) != 0)) {
+				// align to word for version 2
+				opcode = Read8( io, handle );
+			}
+			if (version == 2) {
+				opcode = Read16( io, handle );
+			}
+			
+			if (opcode == 0xFF || opcode == 0xFFFF) {
+				done = TRUE;
+				throw "PICT contained only vector data!";
+			}
+			else if (opcode < 0xa2)	{				
+				switch (opcode)	{
+					case 0x01:
+					{
+						// skip clipping rectangle
+						MacRect clipRect;
+						WORD len = Read16( io, handle );
+
+						if (len == 0x000a) { 
+							/* null rgn */
+							ReadRect( io, handle, &clipRect );
+						} else {
+							io->seek_proc(handle, len - 2, SEEK_CUR);
+						}
+						break;
+					}						
+					case 0x12:
+					case 0x13:
+					case 0x14:
+					{
+						// skip pattern definition
+						WORD       patType;
+						WORD       rowBytes;
+						MacpixMap  p;
+						WORD       numColors;
+						
+						patType = Read16( io, handle );
+						
+						switch( patType ) {
+							case 2:
+								io->seek_proc(handle, 8, SEEK_CUR);
+								io->seek_proc(handle, 5, SEEK_CUR);
+								break;
+							case 1:
+							{
+								io->seek_proc(handle, 8, SEEK_CUR);
+								rowBytes = Read16( io, handle );
+								ReadRect( io, handle, &p.Bounds );
+								ReadPixmap( io, handle, &p);
+								
+								RGBQUAD ct[256];
+								ReadColorTable(io, handle, &numColors, ct );
+								SkipBits( io, handle, &p.Bounds, rowBytes, p.pixelSize );
+								break;
+							}
+							default:
+								throw "Unknown pattern type.";
+						}
+						
+						break;
+					}
+					case 0x70:
+					case 0x71:
+					case 0x72:
+					case 0x73:
+					case 0x74:
+					case 0x75:
+					case 0x76:
+					case 0x77:
+					{
+						SkipPolyOrRegion( io, handle );
+						break;
+					}
+					case 0x90:
+					case 0x98:
+					{
+						// Bitmap/pixmap data clipped by a rectangle.
+						rowBytes = Read16( io, handle );    // Bytes per row in source when uncompressed.						
+						isRegion = FALSE;
+						
+						if ( rowBytes & 0x8000) {
+							pictType = pixmap;
+						} else {
+							pictType = bitmap;
+						}
+						done = TRUE;
+						break;
+					}
+					case 0x91:
+					case 0x99:
+					{
+						// Bitmap/pixmap data clipped by a region.
+						rowBytes = Read16( io, handle );    // Bytes per row in source when uncompressed.						
+						isRegion = TRUE;
+						
+						if ( rowBytes & 0x8000) {
+							pictType = pixmap;
+						} else {
+							pictType = bitmap;						
+						}
+						done = TRUE;
+						break;
+					}
+					case 0x9a:
+					{
+						// DirectBitsRect.
+						Read32( io, handle );           // Skip fake len and fake EOF.
+						Read16( io, handle );			// bogus row bytes.
+						
+						// Read in the PixMap fields.
+						ReadRect( io, handle, &pixMap.Bounds );
+						ReadPixmap( io, handle, &pixMap );
+						
+						// Ignore source & destination rectangle as well as transfer mode.
+						MacRect dummy;
+						ReadRect( io, handle, &dummy );
+						ReadRect( io, handle, &dummy );
+						WORD mode = Read16( io, handle );
+						
+						pictType=op9a;
+						done = TRUE;
+						break;
+					}
+					case 0xa1:
+					{
+						// long comment
+						WORD type;
+						WORD len;
+						
+						type = Read16( io, handle );
+						len = Read16( io, handle);
+						if (len > 0) {
+							io->seek_proc(handle, len, SEEK_CUR);
+						}
+						break;
+					}
+					default:
+						// No function => skip to next opcode
+						if (optable[opcode].len == WORD_LEN) {
+							WORD len = Read16( io, handle );
+							io->seek_proc(handle, len, SEEK_CUR);
+						} else {
+							io->seek_proc(handle, optable[opcode].len, SEEK_CUR);
+						}
+						break;
+				}
+			}
+			else if (opcode == 0xc00) {
+				// version 2 header (26 bytes)
+				WORD minorVersion = Read16( io, handle );	// always FFFE (-2) for extended version 2
+				Read16( io, handle );						// reserved
+				hRes = Read32( io, handle );				// original horizontal resolution in pixels/inch
+				vRes = Read32( io, handle );				// original horizontal resolution in pixels/inch
+				MacRect dummy;
+				ReadRect( io, handle, &dummy );				// frame bounds at original resolution
+				Read32( io, handle );						// reserved
+			}
+			else if (opcode == 0x8200) {
+				// jpeg
+				long opLen = Read32( io, handle );
+				BOOL found = FALSE;
+				int i = 0;
+				
+				// skip to JPEG header.
+				while ( !found && i < opLen ) {
+//					io->seek_proc( handle, 24, SEEK_CUR );
+//					MacRect dummy;
+//					ReadRect( io, handle, &dummy );
+//					io->seek_proc( handle, 122, SEEK_CUR );
+//					found = TRUE;
+					BYTE data[ 2 ];
+					if( io->read_proc( data, 2, 1, handle ) ) {
+						io->seek_proc( handle, -2, SEEK_CUR );
+						
+						if ( data[0] == 0xFF && data[1] == 0xD8 ) {
+							found = TRUE;
+						} else {
+							Read8( io, handle );
+							i++;
+						}
+					}
+				}
+				
+				if ( found ) {
+					// Pass the data to the JPEG decoder.
+					pictType = jpeg;
+				} else {
+					throw "PICT file contains unrecognized quicktime data.";
+				}
+				done = TRUE;
+			}
+			else if (opcode >= 0xa2 && opcode <= 0xaf) {
+				// reserved
+				WORD len = Read16( io, handle );
+				io->seek_proc(handle, len, SEEK_CUR);
+			}
+			else if ((opcode >= 0xb0 && opcode <= 0xcf) || (opcode >= 0x8000 && opcode <= 0x80ff)) {
+				// just a reserved opcode, no data
+			}
+			else if ((opcode >= 0xd0 && opcode <= 0xfe) || opcode >= 8100) {
+				// reserved
+				LONG len = Read32( io, handle );
+				io->seek_proc(handle, len, SEEK_CUR);
+			}
+			else if (opcode >= 0x100 && opcode <= 0x7fff) {
+				// reserved
+				io->seek_proc(handle, ((opcode >> 7) & 255), SEEK_CUR);				
+			}
+			else {
+				sprintf( outputMessage, "Can't handle opcode %x.\n", opcode );
+				throw outputMessage;
+			}
+
+			if(currentPos == io->tell_proc(handle)) {
+				// we probaly reached the end of file as we can no longer move forward ... 
+				throw "Invalid PICT file";
+			}
+		}
+				
+		switch ( pictType )	{
+			case op9a:
+			{
+				bounds = pixMap.Bounds;
+				int width = bounds.right - bounds.left;
+				int height = bounds.bottom - bounds.top;
+				
+				if ( pixMap.pixelSize > 8 ) {
+					dib = FreeImage_Allocate( width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				} else {
+					dib = FreeImage_Allocate( width, height, 8);
+				}
+				hRes = pixMap.hRes << 16;
+				vRes = pixMap.vRes << 16;				
+				break;
+			}	
+				
+			case jpeg:
+			{
+				dib = FreeImage_LoadFromHandle( FIF_JPEG, io, handle );					
+				break;
+			}
+
+			case pixmap:
+			{
+				// Decode version 2 pixmap
+				ReadRect( io, handle, &pixMap.Bounds );
+				ReadPixmap( io, handle, &pixMap );
+				
+				bounds = pixMap.Bounds;
+				int width = bounds.right - bounds.left;
+				int height = bounds.bottom - bounds.top;
+
+				if ( pixMap.pixelSize > 8 ) {
+					dib = FreeImage_Allocate( width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				} else {
+					dib = FreeImage_Allocate( width, height, 8);
+				}
+				hRes = pixMap.hRes << 16;
+				vRes = pixMap.vRes << 16;				
+				break;
+			}
+				
+			case bitmap:
+			{
+				// Decode version 1 bitmap: 1 bpp.
+				MacRect srcRect;
+				MacRect dstRect;
+				WORD width;        // Width in pixels
+				WORD height;       // Height in pixels
+				
+				ReadRect( io, handle, &bounds );
+				ReadRect( io, handle, &srcRect );
+				ReadRect( io, handle, &dstRect );
+				
+				width = bounds.right - bounds.left;
+				height = bounds.bottom - bounds.top;
+				
+				dib = FreeImage_Allocate(width, height, 8);
+				break;
+			}			
+		}		
+		
+		if ( dib ) {
+			// need to convert resolution figures from fixed point, pixels/inch
+			// to floating point, pixels/meter.			
+			float hres_ppm = hRes * ((float)39.4 / (float)65536.0);
+			float vres_ppm = vRes * ((float)39.4 / (float)65536.0);		
+			
+			FreeImage_SetDotsPerMeterX( dib, (LONG)hres_ppm );
+			FreeImage_SetDotsPerMeterY( dib, (LONG)vres_ppm );			
+			
+			switch( pictType ) {
+				case op9a:
+					DecodeOp9a( io, handle, dib, &pixMap );
+					break;
+				case jpeg:
+					// Already decoded if the embedded format was valid.
+					break;
+				case pixmap:
+					DecodePixmap( io, handle, dib, isRegion, &pixMap, rowBytes );
+					break;
+				case bitmap:
+					DecodeBitmap( io, handle, dib, isRegion, &bounds, rowBytes );
+					break;
+				default:
+					throw "invalid pict type";
+			}			
+		}
+		
+		return dib;
+	} 
+	catch(const char *message) {
+		FreeImage_Unload( dib );
+		FreeImage_OutputMessageProc(s_format_id, message);
+	}
+
+	return NULL;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPICT(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = NULL;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+}
diff --git a/files/Source/FreeImage/PluginPNG.cpp b/files/Source/FreeImage/PluginPNG.cpp
new file mode 100644
index 0000000..c3f30bd
--- /dev/null
+++ b/files/Source/FreeImage/PluginPNG.cpp
@@ -0,0 +1,1115 @@
+// ==========================================================
+// PNG Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Herve Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Aaron Shumate (trek@startreker.com)
+// - 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!
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+#include "../Metadata/FreeImageTag.h"
+
+// ----------------------------------------------------------
+
+#define PNG_BYTES_TO_CHECK 8
+
+#undef PNG_Z_DEFAULT_COMPRESSION	// already used in ../LibPNG/pnglibconf.h
+
+// ----------------------------------------------------------
+
+#include "third_party/zlib/zlib.h"
+#include "third_party/png/png.h"
+
+// ----------------------------------------------------------
+
+typedef struct {
+    FreeImageIO *s_io;
+    fi_handle    s_handle;
+} fi_ioStructure, *pfi_ioStructure;
+
+// ==========================================================
+// libpng interface
+// ==========================================================
+
+static void
+_ReadProc(png_structp png_ptr, unsigned char *data, png_size_t size) {
+    pfi_ioStructure pfio = (pfi_ioStructure)png_get_io_ptr(png_ptr);
+	unsigned n = pfio->s_io->read_proc(data, (unsigned int)size, 1, pfio->s_handle);
+	if(size && (n == 0)) {
+		throw "Read error: invalid or corrupted PNG file";
+	}
+}
+
+static void
+_WriteProc(png_structp png_ptr, unsigned char *data, png_size_t size) {
+    pfi_ioStructure pfio = (pfi_ioStructure)png_get_io_ptr(png_ptr);
+    pfio->s_io->write_proc(data, (unsigned int)size, 1, pfio->s_handle);
+}
+
+static void
+_FlushProc(png_structp png_ptr) {
+	(png_structp)png_ptr;
+	// empty flush implementation
+}
+
+static void
+error_handler(png_structp png_ptr, const char *error) {
+	(png_structp)png_ptr;
+	throw error;
+}
+
+// in FreeImage warnings disabled
+
+static void
+warning_handler(png_structp png_ptr, const char *warning) {
+	(png_structp)png_ptr;
+	(char*)warning;
+}
+
+// ==========================================================
+// Metadata routines
+// ==========================================================
+
+static BOOL 
+ReadMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) {
+	// XMP keyword
+	const char *g_png_xmp_keyword = "XML:com.adobe.xmp";
+
+	FITAG *tag = NULL;
+	png_textp text_ptr = NULL;
+	png_timep mod_time = NULL;
+	int num_text = 0;
+
+	// iTXt/tEXt/zTXt chuncks
+	if(png_get_text(png_ptr, info_ptr, &text_ptr, &num_text) > 0) {
+		for(int i = 0; i < num_text; i++) {
+			// create a tag
+			tag = FreeImage_CreateTag();
+			if(!tag) return FALSE;
+
+			DWORD tag_length = (DWORD) MAX(text_ptr[i].text_length, text_ptr[i].itxt_length);
+
+			FreeImage_SetTagLength(tag, tag_length);
+			FreeImage_SetTagCount(tag, tag_length);
+			FreeImage_SetTagType(tag, FIDT_ASCII);
+			FreeImage_SetTagValue(tag, text_ptr[i].text);
+
+			if(strcmp(text_ptr[i].key, g_png_xmp_keyword) == 0) {
+				// store the tag as XMP
+				FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
+				FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
+			} else {
+				// store the tag as a comment
+				FreeImage_SetTagKey(tag, text_ptr[i].key);
+				FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
+			}
+			
+			// destroy the tag
+			FreeImage_DeleteTag(tag);
+		}
+	}
+
+	// timestamp chunk
+	if(png_get_tIME(png_ptr, info_ptr, &mod_time)) {
+		char timestamp[32];
+		// create a tag
+		tag = FreeImage_CreateTag();
+		if(!tag) return FALSE;
+
+		// convert as 'yyyy:MM:dd hh:mm:ss'
+		sprintf(timestamp, "%4d:%02d:%02d %2d:%02d:%02d", mod_time->year, mod_time->month, mod_time->day, mod_time->hour, mod_time->minute, mod_time->second);
+
+		DWORD tag_length = (DWORD)strlen(timestamp) + 1;
+		FreeImage_SetTagLength(tag, tag_length);
+		FreeImage_SetTagCount(tag, tag_length);
+		FreeImage_SetTagType(tag, FIDT_ASCII);
+		FreeImage_SetTagID(tag, TAG_DATETIME);
+		FreeImage_SetTagValue(tag, timestamp);
+
+		// store the tag as Exif-TIFF
+		FreeImage_SetTagKey(tag, "DateTime");
+		FreeImage_SetMetadata(FIMD_EXIF_MAIN, dib, FreeImage_GetTagKey(tag), tag);
+
+		// destroy the tag
+		FreeImage_DeleteTag(tag);
+	}
+
+	return TRUE;
+}
+
+static BOOL 
+WriteMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) {
+	// XMP keyword
+	const char *g_png_xmp_keyword = "XML:com.adobe.xmp";
+
+	FITAG *tag = NULL;
+	FIMETADATA *mdhandle = NULL;
+	BOOL bResult = TRUE;
+
+	png_text text_metadata;
+	png_time mod_time;
+
+	// set the 'Comments' metadata as iTXt chuncks
+
+	mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag);
+
+	if(mdhandle) {
+		do {
+			memset(&text_metadata, 0, sizeof(png_text));
+			text_metadata.compression = 1;							// iTXt, none
+			text_metadata.key = (char*)FreeImage_GetTagKey(tag);	// keyword, 1-79 character description of "text"
+			text_metadata.text = (char*)FreeImage_GetTagValue(tag);	// comment, may be an empty string (ie "")
+			text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string
+			text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string
+			text_metadata.lang = 0;		 // language code, 0-79 characters or a NULL pointer
+			text_metadata.lang_key = 0;	 // keyword translated UTF-8 string, 0 or more chars or a NULL pointer
+
+			// set the tag 
+			png_set_text(png_ptr, info_ptr, &text_metadata, 1);
+
+		} while(FreeImage_FindNextMetadata(mdhandle, &tag));
+
+		FreeImage_FindCloseMetadata(mdhandle);
+		bResult &= TRUE;
+	}
+
+	// set the 'XMP' metadata as iTXt chuncks
+	tag = NULL;
+	FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag);
+	if(tag && FreeImage_GetTagLength(tag)) {
+		memset(&text_metadata, 0, sizeof(png_text));
+		text_metadata.compression = 1;							// iTXt, none
+		text_metadata.key = (char*)g_png_xmp_keyword;			// keyword, 1-79 character description of "text"
+		text_metadata.text = (char*)FreeImage_GetTagValue(tag);	// comment, may be an empty string (ie "")
+		text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string
+		text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string
+		text_metadata.lang = 0;		 // language code, 0-79 characters or a NULL pointer
+		text_metadata.lang_key = 0;	 // keyword translated UTF-8 string, 0 or more chars or a NULL pointer
+
+		// set the tag 
+		png_set_text(png_ptr, info_ptr, &text_metadata, 1);
+		bResult &= TRUE;
+	}
+
+	// set the Exif-TIFF 'DateTime' metadata as a tIME chunk
+	tag = NULL;
+	FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "DateTime", &tag);
+	if(tag && FreeImage_GetTagLength(tag)) {
+		int year, month, day, hour, minute, second;
+		const char *value = (char*)FreeImage_GetTagValue(tag);
+		if(sscanf(value, "%4d:%02d:%02d %2d:%02d:%02d", &year, &month, &day, &hour, &minute, &second) == 6) {
+			mod_time.year	= (png_uint_16)year;
+			mod_time.month	= (png_byte)month;
+			mod_time.day	= (png_byte)day;
+			mod_time.hour	= (png_byte)hour;
+			mod_time.minute	= (png_byte)minute;
+			mod_time.second	= (png_byte)second;
+			png_set_tIME (png_ptr, info_ptr, &mod_time);
+		}
+	}
+
+	return bResult;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "PNG";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Portable Network Graphics";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "png";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return "^.PNG\r";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/png";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+	BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	io->read_proc(&signature, 1, 8, handle);
+
+	return (memcmp(png_signature, signature, 8) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+			(depth == 1) ||
+			(depth == 4) ||
+			(depth == 8) ||
+			(depth == 24) ||
+			(depth == 32)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (
+		(type == FIT_BITMAP) ||
+		(type == FIT_UINT16) ||
+		(type == FIT_RGB16) ||
+		(type == FIT_RGBA16)
+	);
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Configure the decoder so that decoded pixels are compatible with a FREE_IMAGE_TYPE format. 
+Set conversion instructions as needed. 
+@param png_ptr PNG handle
+@param info_ptr PNG info handle
+@param flags Decoder flags
+@param output_image_type Returned FreeImage converted image type
+@return Returns TRUE if successful, returns FALSE otherwise
+@see png_read_update_info
+*/
+static BOOL 
+ConfigureDecoder(png_structp png_ptr, png_infop info_ptr, int flags, FREE_IMAGE_TYPE *output_image_type) {
+	// get original image info
+	const int color_type = png_get_color_type(png_ptr, info_ptr);
+	const int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+	const int pixel_depth = bit_depth * png_get_channels(png_ptr, info_ptr);
+
+	FREE_IMAGE_TYPE image_type = FIT_BITMAP;	// assume standard image type
+
+	// check for transparency table or single transparent color
+	BOOL bIsTransparent = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) == PNG_INFO_tRNS ? TRUE : FALSE;
+
+	// check allowed combinations of colour type and bit depth
+	// then get converted FreeImage type
+
+	switch(color_type) {
+		case PNG_COLOR_TYPE_GRAY:		// color type '0', bitdepth = 1, 2, 4, 8, 16
+			switch(bit_depth) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+					// expand grayscale images to the full 8-bit from 2-bit/pixel
+					if (pixel_depth == 2) {
+						png_set_expand_gray_1_2_4_to_8(png_ptr);
+					}
+
+					// if a tRNS chunk is provided, we must also expand the grayscale data to 8-bits,
+					// this allows us to make use of the transparency table with existing FreeImage methods
+					if (bIsTransparent && (pixel_depth < 8)) {
+						png_set_expand_gray_1_2_4_to_8(png_ptr);
+					}
+					break;
+
+				case 16:
+					image_type = (pixel_depth == 16) ? FIT_UINT16 : FIT_UNKNOWN;
+
+					// 16-bit grayscale images can contain a transparent value (shade)
+					// if found, expand the transparent value to a full alpha channel
+					if (bIsTransparent && (image_type != FIT_UNKNOWN)) {
+						// expand tRNS to a full alpha channel
+						png_set_tRNS_to_alpha(png_ptr);
+						
+						// expand new 16-bit gray + 16-bit alpha to full 64-bit RGBA
+						png_set_gray_to_rgb(png_ptr);
+
+						image_type = FIT_RGBA16;
+					}
+					break;
+
+				default:
+					image_type = FIT_UNKNOWN;
+					break;
+			}
+			break;
+
+		case PNG_COLOR_TYPE_RGB:		// color type '2', bitdepth = 8, 16
+			switch(bit_depth) {
+				case 8:
+					image_type = (pixel_depth == 24) ? FIT_BITMAP : FIT_UNKNOWN;
+					break;
+				case 16:
+					image_type = (pixel_depth == 48) ? FIT_RGB16 : FIT_UNKNOWN;
+					break;
+				default:
+					image_type = FIT_UNKNOWN;
+					break;
+			}
+			// sometimes, 24- or 48-bit images may contain transparency information
+			// check for this use case and convert to an alpha-compatible format
+			if (bIsTransparent && (image_type != FIT_UNKNOWN)) {
+				// if the image is 24-bit RGB, mark it as 32-bit; if it is 48-bit, mark it as 64-bit
+				image_type = (pixel_depth == 24) ? FIT_BITMAP : (pixel_depth == 48) ? FIT_RGBA16 : FIT_UNKNOWN;
+				// expand tRNS chunk to alpha channel
+				png_set_tRNS_to_alpha(png_ptr);
+			}
+			break;
+
+		case PNG_COLOR_TYPE_PALETTE:	// color type '3', bitdepth = 1, 2, 4, 8
+			switch(bit_depth) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+					// expand palette images to the full 8 bits from 2 bits/pixel
+					if (pixel_depth == 2) {
+						png_set_packing(png_ptr);
+					}
+
+					// if a tRNS chunk is provided, we must also expand the palletized data to 8-bits,
+					// this allows us to make use of the transparency table with existing FreeImage methods
+					if (bIsTransparent && (pixel_depth < 8)) {
+						png_set_packing(png_ptr);
+					}
+					break;
+
+				default:
+					image_type = FIT_UNKNOWN;
+					break;
+			}
+			break;
+
+		case PNG_COLOR_TYPE_GRAY_ALPHA:	// color type '4', bitdepth = 8, 16
+			switch(bit_depth) {
+				case 8:
+					// 8-bit grayscale + 8-bit alpha => convert to 32-bit RGBA
+					image_type = (pixel_depth == 16) ? FIT_BITMAP : FIT_UNKNOWN;
+					break;
+				case 16:
+					// 16-bit grayscale + 16-bit alpha => convert to 64-bit RGBA
+					image_type = (pixel_depth == 32) ? FIT_RGBA16 : FIT_UNKNOWN;
+					break;
+				default:
+					image_type = FIT_UNKNOWN;
+					break;
+			}
+			// expand 8-bit greyscale + 8-bit alpha to 32-bit
+			// expand 16-bit greyscale + 16-bit alpha to 64-bit
+			png_set_gray_to_rgb(png_ptr);
+			break;
+
+		case PNG_COLOR_TYPE_RGB_ALPHA:	// color type '6', bitdepth = 8, 16
+			switch(bit_depth) {
+				case 8:
+					break;
+				case 16:
+					image_type = (pixel_depth == 64) ? FIT_RGBA16 : FIT_UNKNOWN;
+					break;
+				default:
+					image_type = FIT_UNKNOWN;
+					break;
+			}
+			break;
+	}
+
+	// check for unknown or invalid formats
+	if(image_type == FIT_UNKNOWN) {
+		*output_image_type = image_type;
+		return FALSE;
+	}
+
+#ifndef FREEIMAGE_BIGENDIAN
+	if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) {
+		// turn on 16-bit byte swapping
+		png_set_swap(png_ptr);
+	}
+#endif						
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+	if((image_type == FIT_BITMAP) && ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA))) {
+		// flip the RGB pixels to BGR (or RGBA to BGRA)
+		png_set_bgr(png_ptr);
+	}
+#endif
+
+	// gamma correction
+	// unlike the example in the libpng documentation, we have *no* idea where
+	// this file may have come from--so if it doesn't have a file gamma, don't
+	// do any correction ("do no harm")
+
+	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
+		double gamma = 0;
+		double screen_gamma = 2.2;
+
+		if (png_get_gAMA(png_ptr, info_ptr, &gamma) && ( flags & PNG_IGNOREGAMMA ) != PNG_IGNOREGAMMA) {
+			png_set_gamma(png_ptr, screen_gamma, gamma);
+		}
+	}
+
+	// all transformations have been registered; now update info_ptr data		
+	png_read_update_info(png_ptr, info_ptr);
+
+	// return the output image type
+	*output_image_type = image_type;
+
+	return TRUE;
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	png_structp png_ptr = NULL;
+	png_infop info_ptr = NULL;
+	png_uint_32 width, height;
+	int color_type;
+	int bit_depth;
+	int pixel_depth = 0;	// pixel_depth = bit_depth * channels
+
+	FIBITMAP *dib = NULL;
+	png_bytepp row_pointers = NULL;
+
+    fi_ioStructure fio;
+    fio.s_handle = handle;
+	fio.s_io = io;
+    
+	if (handle) {
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+		try {		
+			// check to see if the file is in fact a PNG file
+
+			BYTE png_check[PNG_BYTES_TO_CHECK];
+
+			io->read_proc(png_check, PNG_BYTES_TO_CHECK, 1, handle);
+
+			if (png_sig_cmp(png_check, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) {
+				return NULL;	// Bad signature
+			}
+			
+			// create the chunk manage structure
+
+			png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler);
+
+			if (!png_ptr) {
+				return NULL;			
+			}
+
+			// create the info structure
+
+		    info_ptr = png_create_info_struct(png_ptr);
+
+			if (!info_ptr) {
+				png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+				return NULL;
+			}
+
+			// init the IO
+
+			png_set_read_fn(png_ptr, &fio, _ReadProc);
+
+            if (setjmp(png_jmpbuf(png_ptr))) {
+				png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+				return NULL;
+			}
+
+			// because we have already read the signature...
+
+			png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
+
+			// read the IHDR chunk
+
+			png_read_info(png_ptr, info_ptr);
+			png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
+
+			// configure the decoder
+
+			FREE_IMAGE_TYPE image_type = FIT_BITMAP;
+
+			if(!ConfigureDecoder(png_ptr, info_ptr, flags, &image_type)) {
+				throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+			}
+
+			// update image info
+
+			color_type = png_get_color_type(png_ptr, info_ptr);
+			bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+			pixel_depth = bit_depth * png_get_channels(png_ptr, info_ptr);
+
+			// create a dib and write the bitmap header
+			// set up the dib palette, if needed
+
+			switch (color_type) {
+				case PNG_COLOR_TYPE_RGB:
+				case PNG_COLOR_TYPE_RGB_ALPHA:
+					dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+					break;
+
+				case PNG_COLOR_TYPE_PALETTE:
+					dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+					if(dib) {
+						png_colorp png_palette = NULL;
+						int palette_entries = 0;
+
+						png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries);
+
+						palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib));
+
+						// store the palette
+
+						RGBQUAD *palette = FreeImage_GetPalette(dib);
+						for(int i = 0; i < palette_entries; i++) {
+							palette[i].rgbRed   = png_palette[i].red;
+							palette[i].rgbGreen = png_palette[i].green;
+							palette[i].rgbBlue  = png_palette[i].blue;
+						}
+					}
+					break;
+
+				case PNG_COLOR_TYPE_GRAY:
+					dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+
+					if(dib && (pixel_depth <= 8)) {
+						RGBQUAD *palette = FreeImage_GetPalette(dib);
+						const int palette_entries = 1 << pixel_depth;
+
+						for(int i = 0; i < palette_entries; i++) {
+							palette[i].rgbRed   =
+							palette[i].rgbGreen =
+							palette[i].rgbBlue  = (BYTE)((i * 255) / (palette_entries - 1));
+						}
+					}
+					break;
+
+				default:
+					throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+			}
+
+			if(!dib) {
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+
+			// store the transparency table
+
+			if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+				// array of alpha (transparency) entries for palette
+				png_bytep trans_alpha = NULL;
+				// number of transparent entries
+				int num_trans = 0;						
+				// graylevel or color sample values of the single transparent color for non-paletted images
+				png_color_16p trans_color = NULL;
+
+				png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color);
+
+				if((color_type == PNG_COLOR_TYPE_GRAY) && trans_color) {
+					// single transparent color
+					if (trans_color->gray < 256) { 
+						BYTE table[256]; 
+						memset(table, 0xFF, 256); 
+						table[trans_color->gray] = 0; 
+						FreeImage_SetTransparencyTable(dib, table, 256); 
+					}
+					// check for a full transparency table, too
+					else if ((trans_alpha) && (pixel_depth <= 8)) {
+						FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans);
+					}
+
+				} else if((color_type == PNG_COLOR_TYPE_PALETTE) && trans_alpha) {
+					// transparency table
+					FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans);
+				}
+			}
+
+			// store the background color (only supported for FIT_BITMAP types)
+
+			if ((image_type == FIT_BITMAP) && png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) {
+				// Get the background color to draw transparent and alpha images over.
+				// Note that even if the PNG file supplies a background, you are not required to
+				// use it - you should use the (solid) application background if it has one.
+
+				png_color_16p image_background = NULL;
+				RGBQUAD rgbBkColor;
+
+				if (png_get_bKGD(png_ptr, info_ptr, &image_background)) {
+					rgbBkColor.rgbRed      = (BYTE)image_background->red;
+					rgbBkColor.rgbGreen    = (BYTE)image_background->green;
+					rgbBkColor.rgbBlue     = (BYTE)image_background->blue;
+					rgbBkColor.rgbReserved = 0;
+
+					FreeImage_SetBackgroundColor(dib, &rgbBkColor);
+				}
+			}
+
+			// get physical resolution
+
+			if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) {
+				png_uint_32 res_x, res_y;
+				
+				// we'll overload this var and use 0 to mean no phys data,
+				// since if it's not in meters we can't use it anyway
+
+				int res_unit_type = PNG_RESOLUTION_UNKNOWN;
+
+				png_get_pHYs(png_ptr,info_ptr, &res_x, &res_y, &res_unit_type);
+
+				if (res_unit_type == PNG_RESOLUTION_METER) {
+					FreeImage_SetDotsPerMeterX(dib, res_x);
+					FreeImage_SetDotsPerMeterY(dib, res_y);
+				}
+			}
+
+			// get possible ICC profile
+
+			if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
+				png_charp profile_name = NULL;
+				png_bytep profile_data = NULL;
+				png_uint_32 profile_length = 0;
+				int  compression_type;
+
+				png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_length);
+
+				// copy ICC profile data (must be done after FreeImage_AllocateHeader)
+
+				FreeImage_CreateICCProfile(dib, profile_data, profile_length);
+			}
+
+			// --- header only mode => clean-up and return
+
+			if (header_only) {
+				// get possible metadata (it can be located both before and after the image data)
+				ReadMetadata(png_ptr, info_ptr, dib);
+				if (png_ptr) {
+					// clean up after the read, and free any memory allocated - REQUIRED
+					png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+				}
+				return dib;
+			}
+
+			// set the individual row_pointers to point at the correct offsets
+
+			row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep));
+
+			if (!row_pointers) {
+				png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+				FreeImage_Unload(dib);
+				return NULL;
+			}
+
+			// read in the bitmap bits via the pointer table
+			// allow loading of PNG with minor errors (such as images with several IDAT chunks)
+
+			for (png_uint_32 k = 0; k < height; k++) {
+				row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k);
+			}
+
+			png_set_benign_errors(png_ptr, 1);
+			png_read_image(png_ptr, row_pointers);
+
+			// check if the bitmap contains transparency, if so enable it in the header
+
+			if (FreeImage_GetBPP(dib) == 32) {
+				if (FreeImage_GetColorType(dib) == FIC_RGBALPHA) {
+					FreeImage_SetTransparent(dib, TRUE);
+				} else {
+					FreeImage_SetTransparent(dib, FALSE);
+				}
+			}
+				
+			// cleanup
+
+			if (row_pointers) {
+				free(row_pointers);
+				row_pointers = NULL;
+			}
+
+			// read the rest of the file, getting any additional chunks in info_ptr
+
+			png_read_end(png_ptr, info_ptr);
+
+			// get possible metadata (it can be located both before and after the image data)
+
+			ReadMetadata(png_ptr, info_ptr, dib);
+
+			if (png_ptr) {
+				// clean up after the read, and free any memory allocated - REQUIRED
+				png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+			}
+
+			return dib;
+
+		} catch (const char *text) {
+			if (png_ptr) {
+				png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+			}
+			if (row_pointers) {
+				free(row_pointers);
+			}
+			if (dib) {
+				FreeImage_Unload(dib);
+			}
+			FreeImage_OutputMessageProc(s_format_id, text);
+			
+			return NULL;
+		}
+	}			
+
+	return NULL;
+}
+
+// --------------------------------------------------------------------------
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	png_structp png_ptr;
+	png_infop info_ptr;
+	png_colorp palette = NULL;
+	png_uint_32 width, height;
+	BOOL has_alpha_channel = FALSE;
+
+	RGBQUAD *pal;					// pointer to dib palette
+	int bit_depth, pixel_depth;		// pixel_depth = bit_depth * channels
+	int palette_entries;
+	int	interlace_type;
+
+	fi_ioStructure fio;
+    fio.s_handle = handle;
+	fio.s_io = io;
+
+	if ((dib) && (handle)) {
+		try {
+			// create the chunk manage structure
+
+			png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler);
+
+			if (!png_ptr)  {
+				return FALSE;
+			}
+
+			// allocate/initialize the image information data.
+
+			info_ptr = png_create_info_struct(png_ptr);
+
+			if (!info_ptr)  {
+				png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
+				return FALSE;
+			}
+
+			// Set error handling.  REQUIRED if you aren't supplying your own
+			// error handling functions in the png_create_write_struct() call.
+
+			if (setjmp(png_jmpbuf(png_ptr)))  {
+				// if we get here, we had a problem reading the file
+
+				png_destroy_write_struct(&png_ptr, &info_ptr);
+
+				return FALSE;
+			}
+
+			// init the IO
+            
+			png_set_write_fn(png_ptr, &fio, _WriteProc, _FlushProc);
+
+			// set physical resolution
+
+			png_uint_32 res_x = (png_uint_32)FreeImage_GetDotsPerMeterX(dib);
+			png_uint_32 res_y = (png_uint_32)FreeImage_GetDotsPerMeterY(dib);
+
+			if ((res_x > 0) && (res_y > 0))  {
+				png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER);
+			}
+	
+			// Set the image information here.  Width and height are up to 2^31,
+			// bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
+			// the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
+			// PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
+			// or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
+			// PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
+			// currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
+
+			width = FreeImage_GetWidth(dib);
+			height = FreeImage_GetHeight(dib);
+			pixel_depth = FreeImage_GetBPP(dib);
+
+			BOOL bInterlaced = FALSE;
+			if( (flags & PNG_INTERLACED) == PNG_INTERLACED) {
+				interlace_type = PNG_INTERLACE_ADAM7;
+				bInterlaced = TRUE;
+			} else {
+				interlace_type = PNG_INTERLACE_NONE;
+			}
+
+			// set the ZLIB compression level or default to PNG default compression level (ZLIB level = 6)
+			int zlib_level = flags & 0x0F;
+			if((zlib_level >= 1) && (zlib_level <= 9)) {
+				png_set_compression_level(png_ptr, zlib_level);
+			} else if((flags & PNG_Z_NO_COMPRESSION) == PNG_Z_NO_COMPRESSION) {
+				png_set_compression_level(png_ptr, Z_NO_COMPRESSION);
+			}
+
+			// filtered strategy works better for high color images
+			if(pixel_depth >= 16){
+				png_set_compression_strategy(png_ptr, Z_FILTERED);
+				png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH);
+			} else {
+				png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
+			}
+
+			FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+			if(image_type == FIT_BITMAP) {
+				// standard image type
+				bit_depth = (pixel_depth > 8) ? 8 : pixel_depth;
+			} else {
+				// 16-bit greyscale or 16-bit RGB(A)
+				bit_depth = 16;
+			}
+
+			// check for transparent images
+			BOOL bIsTransparent = 
+				(image_type == FIT_BITMAP) && FreeImage_IsTransparent(dib) && (FreeImage_GetTransparencyCount(dib) > 0) ? TRUE : FALSE;
+
+			switch (FreeImage_GetColorType(dib)) {
+				case FIC_MINISWHITE:
+					if(!bIsTransparent) {
+						// Invert monochrome files to have 0 as black and 1 as white (no break here)
+						png_set_invert_mono(png_ptr);
+					}
+					// (fall through)
+
+				case FIC_MINISBLACK:
+					if(!bIsTransparent) {
+						png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, 
+							PNG_COLOR_TYPE_GRAY, interlace_type, 
+							PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+						break;
+					}
+					// If a monochrome image is transparent, save it with a palette
+					// (fall through)
+
+				case FIC_PALETTE:
+				{
+					png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, 
+						PNG_COLOR_TYPE_PALETTE, interlace_type, 
+						PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+					// set the palette
+
+					palette_entries = 1 << bit_depth;
+					palette = (png_colorp)png_malloc(png_ptr, palette_entries * sizeof (png_color));
+					pal = FreeImage_GetPalette(dib);
+
+					for (int i = 0; i < palette_entries; i++) {
+						palette[i].red   = pal[i].rgbRed;
+						palette[i].green = pal[i].rgbGreen;
+						palette[i].blue  = pal[i].rgbBlue;
+					}
+					
+					png_set_PLTE(png_ptr, info_ptr, palette, palette_entries);
+
+					// You must not free palette here, because png_set_PLTE only makes a link to
+					// the palette that you malloced.  Wait until you are about to destroy
+					// the png structure.
+
+					break;
+				}
+
+				case FIC_RGBALPHA :
+					has_alpha_channel = TRUE;
+
+					png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, 
+						PNG_COLOR_TYPE_RGBA, interlace_type, 
+						PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+					// flip BGR pixels to RGB
+					if(image_type == FIT_BITMAP) {
+						png_set_bgr(png_ptr);
+					}
+#endif
+					break;
+	
+				case FIC_RGB:
+					png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, 
+						PNG_COLOR_TYPE_RGB, interlace_type, 
+						PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+					// flip BGR pixels to RGB
+					if(image_type == FIT_BITMAP) {
+						png_set_bgr(png_ptr);
+					}
+#endif
+					break;
+					
+				case FIC_CMYK:
+					break;
+			}
+
+			// write possible ICC profile
+
+			FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
+			if (iccProfile->size && iccProfile->data) {
+				png_set_iCCP(png_ptr, info_ptr, "Embedded Profile", 0, (png_const_bytep)iccProfile->data, iccProfile->size);
+			}
+
+			// write metadata
+
+			WriteMetadata(png_ptr, info_ptr, dib);
+
+			// Optional gamma chunk is strongly suggested if you have any guess
+			// as to the correct gamma of the image.
+			// png_set_gAMA(png_ptr, info_ptr, gamma);
+
+			// set the transparency table
+
+			if (bIsTransparent) {
+				png_set_tRNS(png_ptr, info_ptr, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib), NULL);
+			}
+
+			// set the background color
+
+			if(FreeImage_HasBackgroundColor(dib)) {
+				png_color_16 image_background;
+				RGBQUAD rgbBkColor;
+
+				FreeImage_GetBackgroundColor(dib, &rgbBkColor);
+				memset(&image_background, 0, sizeof(png_color_16));
+				image_background.blue  = rgbBkColor.rgbBlue;
+				image_background.green = rgbBkColor.rgbGreen;
+				image_background.red   = rgbBkColor.rgbRed;
+				image_background.index = rgbBkColor.rgbReserved;
+
+				png_set_bKGD(png_ptr, info_ptr, &image_background);
+			}
+			
+			// Write the file header information.
+
+			png_write_info(png_ptr, info_ptr);
+
+			// write out the image data
+
+#ifndef FREEIMAGE_BIGENDIAN
+			if (bit_depth == 16) {
+				// turn on 16 bit byte swapping
+				png_set_swap(png_ptr);
+			}
+#endif
+
+			int number_passes = 1;
+			if (bInterlaced) {
+				number_passes = png_set_interlace_handling(png_ptr);
+			}
+
+			if ((pixel_depth == 32) && (!has_alpha_channel)) {
+				BYTE *buffer = (BYTE *)malloc(width * 3);
+
+				// transparent conversion to 24-bit
+				// the number of passes is either 1 for non-interlaced images, or 7 for interlaced images
+				for (int pass = 0; pass < number_passes; pass++) {
+					for (png_uint_32 k = 0; k < height; k++) {
+						FreeImage_ConvertLine32To24(buffer, FreeImage_GetScanLine(dib, height - k - 1), width);
+						png_write_row(png_ptr, buffer);
+					}
+				}
+				free(buffer);
+			} else {
+				// the number of passes is either 1 for non-interlaced images, or 7 for interlaced images
+				for (int pass = 0; pass < number_passes; pass++) {
+					for (png_uint_32 k = 0; k < height; k++) {
+						png_write_row(png_ptr, FreeImage_GetScanLine(dib, height - k - 1));
+					}
+				}
+			}
+
+			// It is REQUIRED to call this to finish writing the rest of the file
+			// Bug with png_flush
+
+			png_write_end(png_ptr, info_ptr);
+
+			// clean up after the write, and free any memory allocated
+			if (palette) {
+				png_free(png_ptr, palette);
+			}
+
+			png_destroy_write_struct(&png_ptr, &info_ptr);
+
+			return TRUE;
+
+		} catch (const char *text) {
+			if(png_ptr) {
+				png_destroy_write_struct(&png_ptr, &info_ptr);
+			}
+			FreeImage_OutputMessageProc(s_format_id, text);
+		}
+	}
+
+	return FALSE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPNG(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginPNM.cpp b/files/Source/FreeImage/PluginPNM.cpp
new file mode 100644
index 0000000..3b4d0de
--- /dev/null
+++ b/files/Source/FreeImage/PluginPNM.cpp
@@ -0,0 +1,838 @@
+// ==========================================================
+// PNM (PPM, PGM, PBM) Loader and Writer
+//
+// 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 "Utilities.h"
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/**
+Get an integer value from the actual position pointed by handle
+*/
+static int
+GetInt(FreeImageIO *io, fi_handle handle) {
+    char c = 0;
+	BOOL bFirstChar;
+
+    // skip forward to start of next number
+
+	if(!io->read_proc(&c, 1, 1, handle)) {
+		throw FI_MSG_ERROR_PARSING;
+	}
+
+    while (1) {
+        // eat comments
+
+        if (c == '#') {
+			// if we're at a comment, read to end of line
+
+            bFirstChar = TRUE;
+
+            while (1) {
+				if(!io->read_proc(&c, 1, 1, handle)) {
+					throw FI_MSG_ERROR_PARSING;
+				}
+
+				if (bFirstChar && c == ' ') {
+					// loop off 1 sp after #
+					bFirstChar = FALSE;
+				} else if (c == '\n') {
+					break;
+				}
+			}
+		}
+
+        if (c >= '0' && c <='9') {
+			// we've found what we were looking for
+            break;
+		}
+
+		if(!io->read_proc(&c, 1, 1, handle)) {
+			throw FI_MSG_ERROR_PARSING;
+		}
+    }
+
+    // we're at the start of a number, continue until we hit a non-number
+
+    int i = 0;
+
+    while (1) {
+        i = (i * 10) + (c - '0');
+
+		if(!io->read_proc(&c, 1, 1, handle)) {
+			throw FI_MSG_ERROR_PARSING;
+		}
+
+		if (c < '0' || c > '9') {
+			break;
+		}
+    }
+
+    return i;
+}
+
+/**
+Read a WORD value taking into account the endianess issue
+*/
+static inline WORD 
+ReadWord(FreeImageIO *io, fi_handle handle) {
+	WORD level = 0;
+	io->read_proc(&level, 2, 1, handle); 
+#ifndef FREEIMAGE_BIGENDIAN
+	SwapShort(&level);	// PNM uses the big endian convention
+#endif
+	return level;
+}
+
+/**
+Write a WORD value taking into account the endianess issue
+*/
+static inline void 
+WriteWord(FreeImageIO *io, fi_handle handle, const WORD value) {
+	WORD level = value;
+#ifndef FREEIMAGE_BIGENDIAN
+	SwapShort(&level);	// PNM uses the big endian convention
+#endif
+	io->write_proc(&level, 2, 1, handle);
+}
+
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "PNM";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Portable Network Media";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "pbm,pgm,ppm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/freeimage-pnm";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE pbm_id1[] = { 0x50, 0x31 };
+	BYTE pbm_id2[] = { 0x50, 0x34 };
+	BYTE pgm_id1[] = { 0x50, 0x32 };
+	BYTE pgm_id2[] = { 0x50, 0x35 };
+	BYTE ppm_id1[] = { 0x50, 0x33 };
+	BYTE ppm_id2[] = { 0x50, 0x36 };
+	BYTE signature[2] = { 0, 0 };
+
+	io->read_proc(signature, 1, sizeof(pbm_id1), handle);
+
+	if (memcmp(pbm_id1, signature, sizeof(pbm_id1)) == 0)
+		return TRUE;
+
+	if (memcmp(pbm_id2, signature, sizeof(pbm_id2)) == 0)
+		return TRUE;
+
+	if (memcmp(pgm_id1, signature, sizeof(pgm_id1)) == 0)
+		return TRUE;
+
+	if (memcmp(pgm_id2, signature, sizeof(pgm_id2)) == 0)
+		return TRUE;
+
+	if (memcmp(ppm_id1, signature, sizeof(ppm_id1)) == 0)
+		return TRUE;
+
+	if (memcmp(ppm_id2, signature, sizeof(ppm_id2)) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+			(depth == 1) ||
+			(depth == 8) ||
+			(depth == 24)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (
+		(type == FIT_BITMAP)  ||
+		(type == FIT_UINT16)  ||
+		(type == FIT_RGB16)
+	);
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	char id_one = 0, id_two = 0;
+	int x, y;
+	FIBITMAP *dib = NULL;
+	RGBQUAD *pal;	// pointer to dib palette
+	int i;
+
+	if (!handle) {
+		return NULL;
+	}
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	try {
+		FREE_IMAGE_TYPE image_type = FIT_BITMAP;	// standard image: 1-, 8-, 24-bit
+
+		// Read the first two bytes of the file to determine the file format
+		// "P1" = ascii bitmap, "P2" = ascii greymap, "P3" = ascii pixmap,
+		// "P4" = raw bitmap, "P5" = raw greymap, "P6" = raw pixmap
+
+		io->read_proc(&id_one, 1, 1, handle);
+		io->read_proc(&id_two, 1, 1, handle);
+
+		if ((id_one != 'P') || (id_two < '1') || (id_two > '6')) {			
+			// signature error
+			throw FI_MSG_ERROR_MAGIC_NUMBER;
+		}
+
+		// Read the header information: width, height and the 'max' value if any
+
+		int width  = GetInt(io, handle);
+		int height = GetInt(io, handle);
+		int maxval = 1;
+
+		if((id_two == '2') || (id_two == '5') || (id_two == '3') || (id_two == '6')) {
+			maxval = GetInt(io, handle);
+			if((maxval <= 0) || (maxval > 65535)) {
+				FreeImage_OutputMessageProc(s_format_id, "Invalid max value : %d", maxval);
+				throw (const char*)NULL;
+			}
+		}
+
+		// Create a new DIB
+
+		switch (id_two) {
+			case '1':
+			case '4':
+				// 1-bit
+				dib = FreeImage_AllocateHeader(header_only, width, height, 1);
+				break;
+
+			case '2':
+			case '5':
+				if(maxval > 255) {
+					// 16-bit greyscale
+					image_type = FIT_UINT16;
+					dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height);
+				} else {
+					// 8-bit greyscale
+					dib = FreeImage_AllocateHeader(header_only, width, height, 8);
+				}
+				break;
+
+			case '3':
+			case '6':
+				if(maxval > 255) {
+					// 48-bit RGB
+					image_type = FIT_RGB16;
+					dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height);
+				} else {
+					// 24-bit RGB
+					dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				}
+				break;
+		}
+
+		if (dib == NULL) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		// Build a greyscale palette if needed
+
+		if(image_type == FIT_BITMAP) {
+			switch(id_two)  {
+				case '1':
+				case '4':
+					pal = FreeImage_GetPalette(dib);
+					pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+					pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+					break;
+
+				case '2':
+				case '5':
+					pal = FreeImage_GetPalette(dib);
+					for (i = 0; i < 256; i++) {
+						pal[i].rgbRed	=
+						pal[i].rgbGreen =
+						pal[i].rgbBlue	= (BYTE)i;
+					}
+					break;
+
+				default:
+					break;
+			}
+		}
+
+		if(header_only) {
+			// header only mode
+			return dib;
+		}
+
+		// Read the image...
+
+		switch(id_two)  {
+			case '1':
+			case '4':
+				// write the bitmap data
+
+				if (id_two == '1') {	// ASCII bitmap
+					for (y = 0; y < height; y++) {		
+						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+						for (x = 0; x < width; x++) {
+							if (GetInt(io, handle) == 0)
+								bits[x >> 3] |= (0x80 >> (x & 0x7));
+							else
+								bits[x >> 3] &= (0xFF7F >> (x & 0x7));
+						}
+					}
+				}  else {		// Raw bitmap
+					int line = CalculateLine(width, 1);
+
+					for (y = 0; y < height; y++) {	
+						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+						for (x = 0; x < line; x++) {
+							io->read_proc(&bits[x], 1, 1, handle);
+
+							bits[x] = ~bits[x];
+						}
+					}
+				}
+
+				return dib;
+
+			case '2':
+			case '5':
+				if(image_type == FIT_BITMAP) {
+					// write the bitmap data
+
+					if(id_two == '2') {		// ASCII greymap
+						int level = 0;
+
+						for (y = 0; y < height; y++) {	
+							BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+							for (x = 0; x < width; x++) {
+								level = GetInt(io, handle);
+								bits[x] = (BYTE)((255 * level) / maxval);
+							}
+						}
+					} else {		// Raw greymap
+						BYTE level = 0;
+
+						for (y = 0; y < height; y++) {		
+							BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+							for (x = 0; x < width; x++) {
+								io->read_proc(&level, 1, 1, handle);
+								bits[x] = (BYTE)((255 * (int)level) / maxval);
+							}
+						}
+					}
+				}
+				else if(image_type == FIT_UINT16) {
+					// write the bitmap data
+
+					if(id_two == '2') {		// ASCII greymap
+						int level = 0;
+
+						for (y = 0; y < height; y++) {	
+							WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+							for (x = 0; x < width; x++) {
+								level = GetInt(io, handle);
+								bits[x] = (WORD)((65535 * (double)level) / maxval);
+							}
+						}
+					} else {		// Raw greymap
+						WORD level = 0;
+
+						for (y = 0; y < height; y++) {		
+							WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+							for (x = 0; x < width; x++) {
+								level = ReadWord(io, handle);
+								bits[x] = (WORD)((65535 * (double)level) / maxval);
+							}
+						}
+					}
+				}
+
+				return dib;
+
+			case '3':
+			case '6':
+				if(image_type == FIT_BITMAP) {
+					// write the bitmap data
+
+					if (id_two == '3') {		// ASCII pixmap
+						int level = 0;
+
+						for (y = 0; y < height; y++) {	
+							BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+							for (x = 0; x < width; x++) {
+								level = GetInt(io, handle);
+								bits[FI_RGBA_RED] = (BYTE)((255 * level) / maxval);		// R
+								level = GetInt(io, handle);
+								bits[FI_RGBA_GREEN] = (BYTE)((255 * level) / maxval);	// G
+								level = GetInt(io, handle);
+								bits[FI_RGBA_BLUE] = (BYTE)((255 * level) / maxval);	// B
+
+								bits += 3;
+							}
+						}
+					}  else {			// Raw pixmap
+						BYTE level = 0;
+
+						for (y = 0; y < height; y++) {	
+							BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+							for (x = 0; x < width; x++) {
+								io->read_proc(&level, 1, 1, handle); 
+								bits[FI_RGBA_RED] = (BYTE)((255 * (int)level) / maxval);	// R
+
+								io->read_proc(&level, 1, 1, handle);
+								bits[FI_RGBA_GREEN] = (BYTE)((255 * (int)level) / maxval);	// G
+
+								io->read_proc(&level, 1, 1, handle);
+								bits[FI_RGBA_BLUE] = (BYTE)((255 * (int)level) / maxval);	// B
+
+								bits += 3;
+							}
+						}
+					}
+				}
+				else if(image_type == FIT_RGB16) {
+					// write the bitmap data
+
+					if (id_two == '3') {		// ASCII pixmap
+						int level = 0;
+
+						for (y = 0; y < height; y++) {	
+							FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+							for (x = 0; x < width; x++) {
+								level = GetInt(io, handle);
+								bits[x].red = (WORD)((65535 * (double)level) / maxval);		// R
+								level = GetInt(io, handle);
+								bits[x].green = (WORD)((65535 * (double)level) / maxval);	// G
+								level = GetInt(io, handle);
+								bits[x].blue = (WORD)((65535 * (double)level) / maxval);	// B
+							}
+						}
+					}  else {			// Raw pixmap
+						WORD level = 0;
+
+						for (y = 0; y < height; y++) {	
+							FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+							for (x = 0; x < width; x++) {
+								level = ReadWord(io, handle);
+								bits[x].red = (WORD)((65535 * (double)level) / maxval);		// R
+								level = ReadWord(io, handle);
+								bits[x].green = (WORD)((65535 * (double)level) / maxval);	// G
+								level = ReadWord(io, handle);
+								bits[x].blue = (WORD)((65535 * (double)level) / maxval);	// B
+							}
+						}
+					}
+				}
+
+				return dib;
+		}
+
+	} catch (const char *text)  {
+		if(dib) FreeImage_Unload(dib);
+
+		if(NULL != text) {
+			switch(id_two)  {
+				case '1':
+				case '4':
+					FreeImage_OutputMessageProc(s_format_id, text);
+					break;
+
+				case '2':
+				case '5':
+					FreeImage_OutputMessageProc(s_format_id, text);
+					break;
+
+				case '3':
+				case '6':
+					FreeImage_OutputMessageProc(s_format_id, text);
+					break;
+			}
+		}
+	}
+		
+	return NULL;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	// ----------------------------------------------------------
+	//   PNM Saving
+	// ----------------------------------------------------------
+	//
+	// Output format :
+	//
+	// Bit depth		flags			file format
+	// -------------    --------------  -----------
+	// 1-bit / pixel	PNM_SAVE_ASCII	PBM (P1)
+	// 1-bit / pixel	PNM_SAVE_RAW	PBM (P4)
+	// 8-bit / pixel	PNM_SAVE_ASCII	PGM (P2)
+	// 8-bit / pixel	PNM_SAVE_RAW	PGM (P5)
+	// 24-bit / pixel	PNM_SAVE_ASCII	PPM (P3)
+	// 24-bit / pixel	PNM_SAVE_RAW	PPM (P6)
+	// ----------------------------------------------------------
+
+	int x, y;
+
+	char buffer[256];	// temporary buffer whose size should be enough for what we need
+
+	if(!dib || !handle) return FALSE;
+	
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+	int bpp		= FreeImage_GetBPP(dib);
+	int width	= FreeImage_GetWidth(dib);
+	int height	= FreeImage_GetHeight(dib);
+
+	// Find the appropriate magic number for this file type
+
+	int magic = 0;
+	int maxval = 255;
+
+	switch(image_type) {
+		case FIT_BITMAP:
+			switch (bpp) {
+				case 1 :
+					magic = 1;	// PBM file (B & W)
+					break;
+				case 8 : 			
+					magic = 2;	// PGM file	(Greyscale)
+					break;
+
+				case 24 :
+					magic = 3;	// PPM file (RGB)
+					break;
+
+				default:
+					return FALSE;	// Invalid bit depth
+			}
+			break;
+		
+		case FIT_UINT16:
+			magic = 2;	// PGM file	(Greyscale)
+			maxval = 65535;
+			break;
+
+		case FIT_RGB16:
+			magic = 3;	// PPM file (RGB)
+			maxval = 65535;
+			break;
+
+		default:
+			return FALSE;
+	}
+
+
+	if (flags == PNM_SAVE_RAW)
+		magic += 3;
+
+	// Write the header info
+
+	sprintf(buffer, "P%d\n%d %d\n", magic, width, height);
+	io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+	if (bpp != 1) {
+		sprintf(buffer, "%d\n", maxval);
+		io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+	}
+
+	// Write the image data
+	///////////////////////
+
+	if(image_type == FIT_BITMAP) {
+		switch(bpp)  {
+			case 24 :            // 24-bit RGB, 3 bytes per pixel
+			{
+				if (flags == PNM_SAVE_RAW)  {
+					for (y = 0; y < height; y++) {
+						// write the scanline to disc
+						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+						for (x = 0; x < width; x++) {
+							io->write_proc(&bits[FI_RGBA_RED], 1, 1, handle);	// R
+							io->write_proc(&bits[FI_RGBA_GREEN], 1, 1, handle);	// G
+							io->write_proc(&bits[FI_RGBA_BLUE], 1, 1, handle);	// B
+
+							bits += 3;
+						}
+					}
+				} else {
+					int length = 0;
+
+					for (y = 0; y < height; y++) {
+						// write the scanline to disc
+						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+						
+						for (x = 0; x < width; x++) {
+							sprintf(buffer, "%3d %3d %3d ", bits[FI_RGBA_RED], bits[FI_RGBA_GREEN], bits[FI_RGBA_BLUE]);
+
+							io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+							length += 12;
+
+							if(length > 58) {
+								// No line should be longer than 70 characters
+								sprintf(buffer, "\n");
+								io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+								length = 0;
+							}
+
+							bits += 3;
+						}					
+					}
+
+				}
+			}
+			break;
+
+			case 8:		// 8-bit greyscale
+			{
+				if (flags == PNM_SAVE_RAW)  {
+					for (y = 0; y < height; y++) {
+						// write the scanline to disc
+						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+						for (x = 0; x < width; x++) {
+							io->write_proc(&bits[x], 1, 1, handle);
+						}
+					}
+				} else {
+					int length = 0;
+
+					for (y = 0; y < height; y++) {
+						// write the scanline to disc
+						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+						for (x = 0; x < width; x++) {
+							sprintf(buffer, "%3d ", bits[x]);
+
+							io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+							length += 4;
+
+							if (length > 66) {
+								// No line should be longer than 70 characters
+								sprintf(buffer, "\n");
+								io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+								length = 0;
+							}
+						}
+					}
+				}
+			}
+			break;
+
+			case 1:		// 1-bit B & W
+			{
+				int color;
+
+				if (flags == PNM_SAVE_RAW)  {
+					for(y = 0; y < height; y++) {
+						// write the scanline to disc
+						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+						for(x = 0; x < (int)FreeImage_GetLine(dib); x++)
+							io->write_proc(&bits[x], 1, 1, handle);
+					}
+				} else  {
+					int length = 0;
+
+					for (y = 0; y < height; y++) {
+						// write the scanline to disc
+						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+						for (x = 0; x < (int)FreeImage_GetLine(dib) * 8; x++)	{
+							color = (bits[x>>3] & (0x80 >> (x & 0x07))) != 0;
+
+							sprintf(buffer, "%c ", color ? '1':'0');
+
+							io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+							length += 2;
+
+							if (length > 68) {
+								// No line should be longer than 70 characters
+								sprintf(buffer, "\n");
+								io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+								length = 0;
+							}
+						}
+					}
+				}
+			}
+			
+			break;
+		}
+	} // if(FIT_BITMAP)
+
+	else if(image_type == FIT_UINT16) {		// 16-bit greyscale
+		if (flags == PNM_SAVE_RAW)  {
+			for (y = 0; y < height; y++) {
+				// write the scanline to disc
+				WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+				for (x = 0; x < width; x++) {
+					WriteWord(io, handle, bits[x]);
+				}
+			}
+		} else {
+			int length = 0;
+
+			for (y = 0; y < height; y++) {
+				// write the scanline to disc
+				WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+				for (x = 0; x < width; x++) {
+					sprintf(buffer, "%5d ", bits[x]);
+
+					io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+					length += 6;
+
+					if (length > 64) {
+						// No line should be longer than 70 characters
+						sprintf(buffer, "\n");
+						io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+						length = 0;
+					}
+				}
+			}
+		}
+	}
+
+	else if(image_type == FIT_RGB16) {		// 48-bit RGB
+		if (flags == PNM_SAVE_RAW)  {
+			for (y = 0; y < height; y++) {
+				// write the scanline to disc
+				FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+				for (x = 0; x < width; x++) {
+					WriteWord(io, handle, bits[x].red);		// R
+					WriteWord(io, handle, bits[x].green);	// G
+					WriteWord(io, handle, bits[x].blue);	// B
+				}
+			}
+		} else {
+			int length = 0;
+
+			for (y = 0; y < height; y++) {
+				// write the scanline to disc
+				FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+				
+				for (x = 0; x < width; x++) {
+					sprintf(buffer, "%5d %5d %5d ", bits[x].red, bits[x].green, bits[x].blue);
+
+					io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+					length += 18;
+
+					if(length > 52) {
+						// No line should be longer than 70 characters
+						sprintf(buffer, "\n");
+						io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+						length = 0;
+					}
+				}					
+			}
+
+		}
+	}
+
+	return TRUE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPNM(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginPSD.cpp b/files/Source/FreeImage/PluginPSD.cpp
new file mode 100644
index 0000000..e453a31
--- /dev/null
+++ b/files/Source/FreeImage/PluginPSD.cpp
@@ -0,0 +1,131 @@
+// ==========================================================
+// 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"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "PSD";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Adobe Photoshop";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "psd";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/vnd.adobe.photoshop";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE psd_id[] = { 0x38, 0x42, 0x50, 0x53 };
+	BYTE signature[4] = { 0, 0, 0, 0 };
+
+	io->read_proc(signature, 1, 4, handle);
+
+	if(memcmp(psd_id, signature, 4) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+} 
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	if(handle) {
+		psdParser parser;
+		
+		FIBITMAP *dib = parser.Load(io, handle, s_format_id, flags);
+
+		return dib;
+	}
+
+	return NULL;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPSD(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = NULL;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+	plugin->supports_no_pixels_proc = SupportsNoPixels; 
+}
diff --git a/files/Source/FreeImage/PluginRAS.cpp b/files/Source/FreeImage/PluginRAS.cpp
new file mode 100644
index 0000000..08fd558
--- /dev/null
+++ b/files/Source/FreeImage/PluginRAS.cpp
@@ -0,0 +1,512 @@
+// ==========================================================
+// Sun rasterfile Loader
+//
+// 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"
+
+// ----------------------------------------------------------
+//   Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagSUNHEADER {
+	DWORD magic;		// Magic number
+	DWORD width;		// Image width in pixels
+	DWORD height;		// Image height in pixels
+	DWORD depth;		// Depth (1, 8, 24 or 32 bits) of each pixel
+	DWORD length;		// Image length (in bytes)
+	DWORD type;			// Format of file (see RT_* below)
+	DWORD maptype;		// Type of colormap (see RMT_* below)
+	DWORD maplength;	// Length of colormap (in bytes)
+} SUNHEADER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ----------------------------------------------------------
+
+// Following the header is the colormap, for maplength bytes (unless maplength is zero),
+// then the image. Each row of the image is rounded to 2 bytes.
+
+#define RAS_MAGIC 0x59A66A95 // Magic number for Sun rasterfiles
+
+// Sun supported type's
+
+#define RT_OLD			0	// Old format (raw image in 68000 byte order)
+#define RT_STANDARD		1	// Raw image in 68000 byte order
+#define RT_BYTE_ENCODED	2	// Run-length encoding of bytes 
+#define RT_FORMAT_RGB	3	// XRGB or RGB instead of XBGR or BGR
+#define RT_FORMAT_TIFF	4	// TIFF <-> standard rasterfile
+#define RT_FORMAT_IFF	5	// IFF (TAAC format) <-> standard rasterfile
+
+#define RT_EXPERIMENTAL 0xffff	// Reserved for testing
+
+// These are the possible colormap types.
+// if it's in RGB format, the map is made up of three byte arrays
+// (red, green, then blue) that are each 1/3 of the colormap length.
+
+#define RMT_NONE		0	// maplength is expected to be 0
+#define RMT_EQUAL_RGB	1	// red[maplength/3], green[maplength/3], blue[maplength/3]
+#define RMT_RAW			2	// Raw colormap
+#define RESC			128 // Run-length encoding escape character
+
+// ----- NOTES -----
+// Each line of the image is rounded out to a multiple of 16 bits.
+// This corresponds to the rounding convention used by the memory pixrect
+// package (/usr/include/pixrect/memvar.h) of the SunWindows system.
+// The ras_encoding field (always set to 0 by Sun's supported software)
+// was renamed to ras_length in release 2.0.  As a result, rasterfiles
+// of type 0 generated by the old software claim to have 0 length; for
+// compatibility, code reading rasterfiles must be prepared to compute the
+// TRUE length from the width, height, and depth fields.
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static void
+ReadData(FreeImageIO *io, fi_handle handle, BYTE *buf, DWORD length, BOOL rle) {
+	// Read either Run-Length Encoded or normal image data
+
+	static BYTE repchar, remaining= 0;
+
+	if (rle) {
+		// Run-length encoded read
+
+		while(length--) {
+			if (remaining) {
+				remaining--;
+				*(buf++)= repchar;
+			} else {
+				io->read_proc(&repchar, 1, 1, handle);
+
+				if (repchar == RESC) {
+					io->read_proc(&remaining, 1, 1, handle);
+
+					if (remaining == 0) {
+						*(buf++)= RESC;
+					} else {
+						io->read_proc(&repchar, 1, 1, handle);
+
+						*(buf++)= repchar;
+					}
+				} else {
+					*(buf++)= repchar;
+				}
+			}
+		}
+	} else {
+		// Normal read
+	
+		io->read_proc(buf, length, 1, handle);
+	}
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "RAS";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Sun Raster Image";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "ras";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-cmu-raster";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE ras_signature[] = { 0x59, 0xA6, 0x6A, 0x95 };
+	BYTE signature[4] = { 0, 0, 0, 0 };
+
+	io->read_proc(signature, 1, sizeof(ras_signature), handle);
+
+	return (memcmp(ras_signature, signature, sizeof(ras_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	SUNHEADER header;	// Sun file header
+	WORD linelength;	// Length of raster line in bytes
+	WORD fill;			// Number of fill bytes per raster line
+	BOOL rle;			// TRUE if RLE file
+	BOOL isRGB;			// TRUE if file type is RT_FORMAT_RGB
+	BYTE fillchar;
+
+	FIBITMAP *dib = NULL;
+	BYTE *bits;			// Pointer to dib data
+	WORD x, y;
+
+	if(!handle) {
+		return NULL;
+	}
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	try {
+		// Read SUN raster header
+
+		io->read_proc(&header, sizeof(SUNHEADER), 1, handle);
+
+#ifndef FREEIMAGE_BIGENDIAN
+		// SUN rasterfiles are big endian only
+
+		SwapLong(&header.magic);
+		SwapLong(&header.width);
+		SwapLong(&header.height);
+		SwapLong(&header.depth);
+		SwapLong(&header.length);
+		SwapLong(&header.type);
+		SwapLong(&header.maptype);
+		SwapLong(&header.maplength);
+#endif
+
+		// Verify SUN identifier
+
+		if (header.magic != RAS_MAGIC) {
+			throw FI_MSG_ERROR_MAGIC_NUMBER;
+		}
+
+		// Allocate a new DIB
+
+		switch(header.depth) {
+			case 1:
+			case 8:
+				dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth);
+				break;
+
+			case 24:
+				dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				break;
+
+			case 32:
+				dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+				break;
+		}
+
+		if (dib == NULL) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		// Check the file format
+
+		rle = FALSE;
+		isRGB = FALSE;
+
+		switch(header.type) {
+			case RT_OLD:
+			case RT_STANDARD:
+			case RT_FORMAT_TIFF: // I don't even know what these format are...
+			case RT_FORMAT_IFF: //The TIFF and IFF format types indicate that the raster
+				//file was originally converted from either of these file formats.
+				//so lets at least try to process them as RT_STANDARD
+				break;
+
+			case RT_BYTE_ENCODED:
+				rle = TRUE;
+				break;
+
+			case RT_FORMAT_RGB:
+				isRGB = TRUE;
+				break;
+
+			default:
+				throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+		}
+
+		// set up the colormap if needed
+
+		switch(header.maptype) {
+			case RMT_NONE :
+			{				
+				if (header.depth < 24) {
+					// Create linear color ramp
+
+					RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+					int numcolors = 1 << header.depth;
+
+					for (int i = 0; i < numcolors; i++) {
+						pal[i].rgbRed	= (BYTE)((255 * i) / (numcolors - 1));
+						pal[i].rgbGreen = (BYTE)((255 * i) / (numcolors - 1));
+						pal[i].rgbBlue	= (BYTE)((255 * i) / (numcolors - 1));
+					}
+				}
+
+				break;
+			}
+
+			case RMT_EQUAL_RGB:
+			{
+				BYTE *r, *g, *b;
+
+				// Read SUN raster colormap
+
+				int numcolors = 1 << header.depth;
+				if((DWORD)(3 * numcolors) > header.maplength) {
+					// some RAS may have less colors than the full palette
+					numcolors = header.maplength / 3;
+				} else {
+					throw "Invalid palette";
+				}
+
+				r = (BYTE*)malloc(3 * numcolors * sizeof(BYTE));
+				g = r + numcolors;
+				b = g + numcolors;
+
+				RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+				io->read_proc(r, 3 * numcolors, 1, handle);
+
+				for (int i = 0; i < numcolors; i++) {
+					pal[i].rgbRed	= r[i];
+					pal[i].rgbGreen = g[i];
+					pal[i].rgbBlue	= b[i];
+				}
+
+				free(r);
+				break;
+			}
+
+			case RMT_RAW:
+			{
+				BYTE *colormap;
+
+				// Read (skip) SUN raster colormap.
+
+				colormap = (BYTE *)malloc(header.maplength * sizeof(BYTE));
+
+				io->read_proc(colormap, header.maplength, 1, handle);
+
+				free(colormap);
+				break;
+			}
+		}
+
+		if(header_only) {
+			// header only mode
+			return dib;
+		}
+
+		// Calculate the line + pitch
+		// Each row is multiple of 16 bits (2 bytes).
+
+		if (header.depth == 1) {
+			linelength = (WORD)((header.width / 8) + (header.width % 8 ? 1 : 0));
+		} else {
+			linelength = (WORD)header.width;
+		}
+
+		fill = (linelength % 2) ? 1 : 0;
+
+		unsigned pitch = FreeImage_GetPitch(dib);
+
+		// Read the image data
+		
+		switch(header.depth) {
+			case 1:
+			case 8:
+			{
+				bits = FreeImage_GetBits(dib) + (header.height - 1) * pitch;
+
+				for (y = 0; y < header.height; y++) {
+					ReadData(io, handle, bits, linelength, rle);
+
+					bits -= pitch;
+
+					if (fill) {
+						ReadData(io, handle, &fillchar, fill, rle);
+					}
+				}
+
+				break;
+			}
+
+			case 24:
+			{
+				BYTE *buf, *bp;
+
+				buf = (BYTE*)malloc(header.width * 3);
+
+				for (y = 0; y < header.height; y++) {
+					bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch;
+
+					ReadData(io, handle, buf, header.width * 3, rle);
+
+					bp = buf;
+
+					if (isRGB) {
+						for (x = 0; x < header.width; x++) {
+							bits[FI_RGBA_RED] = *(bp++);	// red
+							bits[FI_RGBA_GREEN] = *(bp++);	// green
+							bits[FI_RGBA_BLUE] = *(bp++);	// blue
+
+							bits += 3;
+						}
+					} else {
+						for (x = 0; x < header.width; x++) {
+							bits[FI_RGBA_RED] = *(bp + 2);	// red
+							bits[FI_RGBA_GREEN] = *(bp + 1);// green
+							bits[FI_RGBA_BLUE] = *bp;       // blue
+
+							bits += 3; bp += 3;
+						}
+					}
+
+					if (fill) {
+						ReadData(io, handle, &fillchar, fill, rle);
+					}
+				}
+
+				free(buf);
+				break;
+			}
+
+			case 32:
+			{
+				BYTE *buf, *bp;
+
+				buf = (BYTE*)malloc(header.width * 4);
+
+				for (y = 0; y < header.height; y++) {
+					bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch;
+
+					ReadData(io, handle, buf, header.width * 4, rle);
+
+					bp = buf;
+
+					if (isRGB) {
+						for (x = 0; x < header.width; x++) {
+							bits[FI_RGBA_ALPHA] = *(bp++);	// alpha
+							bits[FI_RGBA_RED] = *(bp++);	// red
+							bits[FI_RGBA_GREEN] = *(bp++);	// green
+							bits[FI_RGBA_BLUE] = *(bp++);	// blue
+
+							bits += 4;
+						}
+					}
+					else {
+						for (x = 0; x < header.width; x++) {
+							bits[FI_RGBA_RED] = *(bp + 3);	// red
+							bits[FI_RGBA_GREEN] = *(bp + 2); // green
+							bits[FI_RGBA_BLUE] = *(bp + 1);	// blue
+							bits[FI_RGBA_ALPHA] = *bp;		// alpha
+
+							bits += 4;
+							bp += 4;
+						}
+					}
+
+					if (fill) {
+						ReadData(io, handle, &fillchar, fill, rle);
+					}
+				}
+
+				free(buf);
+				break;
+			}
+		}
+		
+		return dib;
+
+	} catch (const char *text) {
+		if(dib) {
+			FreeImage_Unload(dib);
+		}
+		FreeImage_OutputMessageProc(s_format_id, text);
+	}
+
+	return NULL;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitRAS(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginRAW.cpp b/files/Source/FreeImage/PluginRAW.cpp
new file mode 100644
index 0000000..e9bd5bf
--- /dev/null
+++ b/files/Source/FreeImage/PluginRAW.cpp
@@ -0,0 +1,793 @@
+// ==========================================================
+// RAW camera image loader
+//
+// 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 "../LibRawLite/libraw/libraw.h"
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "../Metadata/FreeImageTag.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+// ----------------------------------------------------------
+//   FreeImage datastream wrapper
+// ----------------------------------------------------------
+
+class LibRaw_freeimage_datastream : public LibRaw_abstract_datastream {
+private: 
+	FreeImageIO *_io;
+	fi_handle _handle;
+	long _eof;
+	INT64 _fsize;
+
+public:
+	LibRaw_freeimage_datastream(FreeImageIO *io, fi_handle handle) : _io(io), _handle(handle) {
+		long start_pos = io->tell_proc(handle);
+		io->seek_proc(handle, 0, SEEK_END);
+		_eof = io->tell_proc(handle);
+		_fsize = _eof - start_pos;
+		io->seek_proc(handle, start_pos, SEEK_SET);
+	}
+
+	~LibRaw_freeimage_datastream() {
+	}
+
+    int valid() { 
+		return (_io && _handle);
+	}
+
+    int read(void *buffer, size_t size, size_t count) { 
+		if(substream) return substream->read(buffer, size, count);
+		return _io->read_proc(buffer, (unsigned)size, (unsigned)count, _handle);
+	}
+
+    int seek(INT64 offset, int origin) { 
+        if(substream) return substream->seek(offset, origin);
+		return _io->seek_proc(_handle, (long)offset, origin);
+	}
+
+    INT64 tell() { 
+		if(substream) return substream->tell();
+        return _io->tell_proc(_handle);
+    }
+	
+	INT64 size() {
+		return _fsize;
+	}
+
+    int get_char() { 
+		int c = 0;
+		if(substream) return substream->get_char();
+		if(!_io->read_proc(&c, 1, 1, _handle)) return -1;
+		return c;
+   }
+	
+	char* gets(char *buffer, int length) { 
+		if (substream) return substream->gets(buffer, length);
+		memset(buffer, 0, length);
+		for(int i = 0; i < length; i++) {
+			if(!_io->read_proc(&buffer[i], 1, 1, _handle))
+				return NULL;
+			if(buffer[i] == 0x0A)
+				break;
+		}
+		return buffer;
+	}
+
+	int scanf_one(const char *fmt, void* val) {
+		std::string buffer;
+		char element = 0;
+		bool bDone = false;
+		if(substream) return substream->scanf_one(fmt,val);				
+		do {
+			if(_io->read_proc(&element, 1, 1, _handle) == 1) {
+				switch(element) {
+					case '0':
+					case '\n':
+					case ' ':
+					case '\t':
+						bDone = true;
+						break;
+					default:
+						break;
+				}
+				buffer.append(&element, 1);
+			} else {
+				return 0;
+			}
+		} while(!bDone);
+
+		return sscanf(buffer.c_str(), fmt, val);
+	}
+
+	int eof() { 
+		if(substream) return substream->eof();
+        return (_io->tell_proc(_handle) >= _eof);
+    }
+
+	void * make_jas_stream() {
+		return NULL;
+	}
+};
+
+// ----------------------------------------------------------
+
+/**
+Convert a processed raw data array to a FIBITMAP
+@param RawProcessor LibRaw handle containing the processed raw image
+@return Returns the converted dib if successfull, returns NULL otherwise
+*/
+static FIBITMAP * 
+libraw_ConvertProcessedRawToDib(LibRaw *RawProcessor) {
+	FIBITMAP *dib = NULL;
+    int width, height, colors, bpp;
+
+	try {
+		int bgr = 0;	// pixel copy order: RGB if (bgr == 0) and BGR otherwise
+
+		// get image info
+		RawProcessor->get_mem_image_format(&width, &height, &colors, &bpp);
+
+		// only 3-color images supported...
+		if(colors != 3) {
+			throw "LibRaw : only 3-color images supported";
+		}
+
+		if(bpp == 16) {
+			// allocate output dib
+			dib = FreeImage_AllocateT(FIT_RGB16, width, height);
+			if(!dib) {
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+
+		} else if(bpp == 8) {
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+			bgr = 1;	// only useful for FIT_BITMAP types
+#endif
+
+			// allocate output dib
+			dib = FreeImage_AllocateT(FIT_BITMAP, width, height, 24);
+			if(!dib) {
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+		}
+
+		// copy post-processed bitmap data into FIBITMAP buffer
+		if(RawProcessor->copy_mem_image(FreeImage_GetBits(dib), FreeImage_GetPitch(dib), bgr) != LIBRAW_SUCCESS) {
+			throw "LibRaw : failed to copy data into dib";
+		}
+
+		// flip vertically
+		FreeImage_FlipVertical(dib);
+
+		return dib;
+
+	} catch(const char *text) {
+		FreeImage_Unload(dib);
+		FreeImage_OutputMessageProc(s_format_id, text);
+		return NULL;
+	}
+}
+
+
+/**
+Convert a processed raw image to a FIBITMAP
+@param image Processed raw image
+@return Returns the converted dib if successfull, returns NULL otherwise
+@see libraw_LoadEmbeddedPreview
+*/
+static FIBITMAP * 
+libraw_ConvertProcessedImageToDib(libraw_processed_image_t *image) {
+	FIBITMAP *dib = NULL;
+
+	try {
+		unsigned width = image->width;
+		unsigned height = image->height;
+		unsigned bpp = image->bits;
+		if(bpp == 16) {
+			// allocate output dib
+			dib = FreeImage_AllocateT(FIT_RGB16, width, height);
+			if(!dib) {
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+			// write data
+			WORD *raw_data = (WORD*)image->data;
+			for(unsigned y = 0; y < height; y++) {
+				FIRGB16 *output = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+				for(unsigned x = 0; x < width; x++) {
+					output[x].red   = raw_data[0];
+					output[x].green = raw_data[1];
+					output[x].blue  = raw_data[2];
+					raw_data += 3;
+				}
+			}
+		} else if(bpp == 8) {
+			// allocate output dib
+			dib = FreeImage_AllocateT(FIT_BITMAP, width, height, 24);
+			if(!dib) {
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+			// write data
+			BYTE *raw_data = (BYTE*)image->data;
+			for(unsigned y = 0; y < height; y++) {
+				RGBTRIPLE *output = (RGBTRIPLE*)FreeImage_GetScanLine(dib, height - 1 - y);
+				for(unsigned x = 0; x < width; x++) {
+					output[x].rgbtRed   = raw_data[0];
+					output[x].rgbtGreen = raw_data[1];
+					output[x].rgbtBlue  = raw_data[2];
+					raw_data += 3;
+				}
+			}
+		}
+		
+		return dib;
+
+	} catch(const char *text) {
+		FreeImage_Unload(dib);
+		FreeImage_OutputMessageProc(s_format_id, text);
+		return NULL;
+	}
+}
+
+/** 
+Get the embedded JPEG preview image from RAW picture with included Exif Data. 
+@param RawProcessor Libraw handle
+@param flags JPEG load flags
+@return Returns the loaded dib if successfull, returns NULL otherwise
+*/
+static FIBITMAP * 
+libraw_LoadEmbeddedPreview(LibRaw *RawProcessor, int flags) {
+	FIBITMAP *dib = NULL;
+	libraw_processed_image_t *thumb_image = NULL;
+	
+	try {
+		// unpack data
+		if(RawProcessor->unpack_thumb() != LIBRAW_SUCCESS) {
+			// run silently "LibRaw : failed to run unpack_thumb"
+			return NULL;
+		}
+
+		// retrieve thumb image
+		int error_code = 0;
+		thumb_image = RawProcessor->dcraw_make_mem_thumb(&error_code);
+		if(thumb_image) {
+			if(thumb_image->type != LIBRAW_IMAGE_BITMAP) {
+				// attach the binary data to a memory stream
+				FIMEMORY *hmem = FreeImage_OpenMemory((BYTE*)thumb_image->data, (DWORD)thumb_image->data_size);
+				// get the file type
+				FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0);
+				if(fif == FIF_JPEG) {
+					// rotate according to Exif orientation
+					flags |= JPEG_EXIFROTATE;
+				}
+				// load an image from the memory stream
+				dib = FreeImage_LoadFromMemory(fif, hmem, flags);
+				// close the stream
+				FreeImage_CloseMemory(hmem);
+			} else if((flags & FIF_LOAD_NOPIXELS) != FIF_LOAD_NOPIXELS) {
+				// convert processed data to output dib
+				dib = libraw_ConvertProcessedImageToDib(thumb_image);
+			}
+		} else {
+			throw "LibRaw : failed to run dcraw_make_mem_thumb";
+		}
+
+		// clean-up and return
+		RawProcessor->dcraw_clear_mem(thumb_image);
+
+		return dib;
+
+	} catch(const char *text) {
+		// clean-up and return
+		if(thumb_image) {
+			RawProcessor->dcraw_clear_mem(thumb_image);
+		}
+		if(text != NULL) {
+			FreeImage_OutputMessageProc(s_format_id, text);
+		}
+	}
+
+	return NULL;
+}
+/**
+Load raw data and convert to FIBITMAP
+@param RawProcessor Libraw handle
+@param bitspersample Output bitdepth (8- or 16-bit)
+@return Returns the loaded dib if successfull, returns NULL otherwise
+*/
+static FIBITMAP * 
+libraw_LoadRawData(LibRaw *RawProcessor, int bitspersample) {
+	FIBITMAP *dib = NULL;
+
+	try {
+		// set decoding parameters
+		// -----------------------
+		
+		// (-6) 16-bit or 8-bit
+		RawProcessor->imgdata.params.output_bps = bitspersample;
+		// (-g power toe_slope)
+		if(bitspersample == 16) {
+			// set -g 1 1 for linear curve
+			RawProcessor->imgdata.params.gamm[0] = 1;
+			RawProcessor->imgdata.params.gamm[1] = 1;
+		} else if(bitspersample == 8) {
+			// by default settings for rec. BT.709 are used: power 2.222 (i.e. gamm[0]=1/2.222) and slope 4.5
+			RawProcessor->imgdata.params.gamm[0] = 1/2.222;
+			RawProcessor->imgdata.params.gamm[1] = 4.5;
+		}
+		// (-W) Don't use automatic increase of brightness by histogram
+		RawProcessor->imgdata.params.no_auto_bright = 1;
+		// (-a) Use automatic white balance obtained after averaging over the entire image
+		RawProcessor->imgdata.params.use_auto_wb = 1;
+		// (-q 3) Adaptive homogeneity-directed demosaicing algorithm (AHD)
+		RawProcessor->imgdata.params.user_qual = 3;
+
+		// -----------------------
+
+		// unpack data
+		if(RawProcessor->unpack() != LIBRAW_SUCCESS) {
+			throw "LibRaw : failed to unpack data";
+		}
+
+		// process data (... most consuming task ...)
+		if(RawProcessor->dcraw_process() != LIBRAW_SUCCESS) {
+			throw "LibRaw : failed to process data";
+		}
+
+		// retrieve processed image
+		dib = libraw_ConvertProcessedRawToDib(RawProcessor);
+	
+		return dib;
+
+	} catch(const char *text) {
+		FreeImage_OutputMessageProc(s_format_id, text);
+		return NULL;
+	}
+}
+
+/**
+Load the Bayer matrix (unprocessed raw data) as a FIT_UINT16 image. 
+Note that some formats don't have a Bayer matrix (e.g. Foveon, Canon sRAW, demosaiced DNG files). 
+@param RawProcessor Libraw handle
+@return Returns the loaded dib if successfull, returns NULL otherwise
+*/
+static FIBITMAP * 
+libraw_LoadUnprocessedData(LibRaw *RawProcessor) {
+	FIBITMAP *dib = NULL;
+
+	try {
+		// unpack data
+		if(RawProcessor->unpack() != LIBRAW_SUCCESS) {
+			throw "LibRaw : failed to unpack data";
+		}
+
+		// check for a supported Bayer format
+		if(!(RawProcessor->imgdata.idata.filters || RawProcessor->imgdata.idata.colors == 1)) {
+			throw "LibRaw : only Bayer-pattern RAW files are supported";
+		}
+
+		// allocate output dib
+		const unsigned width = RawProcessor->imgdata.sizes.raw_width;
+		const unsigned height = RawProcessor->imgdata.sizes.raw_height;
+		const size_t line_size = width * sizeof(WORD);
+		const WORD *src_bits = (WORD*)RawProcessor->imgdata.rawdata.raw_image;
+
+		if(src_bits) {
+			dib = FreeImage_AllocateT(FIT_UINT16, width, height);
+		}
+		if(!dib) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		// retrieve the raw image
+		for(unsigned y = 0; y < height; y++) {
+			WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
+			memcpy(dst_bits, src_bits, line_size);
+			src_bits += width;
+		}
+
+		// store metadata needed for post-processing
+		{
+			char value[512];
+
+			const libraw_image_sizes_t *sizes = &RawProcessor->imgdata.sizes;
+
+			// image output width & height
+			{
+				sprintf(value, "%d", sizes->iwidth);
+				FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Output.Width", value);
+				
+				sprintf(value, "%d", sizes->iheight);
+				FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Output.Height", value);
+			}
+
+			// image output frame
+			{
+				const unsigned f_left = sizes->left_margin;
+				const unsigned f_top = sizes->top_margin;
+				const unsigned f_width = sizes->width;
+				const unsigned f_height = sizes->height;
+				
+				sprintf(value, "%d", f_left);
+				FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Frame.Left", value);
+
+				sprintf(value, "%d", f_top);
+				FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Frame.Top", value);
+
+				sprintf(value, "%d", f_width);
+				FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Frame.Width", value);
+
+				sprintf(value, "%d", f_height);
+				FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Frame.Height", value);
+			}
+
+			// Bayer pattern
+			// Mask describing the order of color pixels in the matrix. 
+			// This field describe 16 pixels (8 rows with two pixels in each, from left to right and from top to bottom). 
+
+			if(RawProcessor->imgdata.idata.filters) {
+				// description of colors numbered from 0 to 3 (RGBG,RGBE,GMCY, or GBTG)
+				char *cdesc = RawProcessor->imgdata.idata.cdesc;
+				if(!cdesc[3]) {
+					cdesc[3] = 'G';
+				}
+				char *pattern = &value[0];
+				for(int i = 0; i < 16; i++) {
+					pattern[i] = cdesc[ RawProcessor->fcol(i >> 1, i & 1) ];
+				}
+				pattern[16] = 0;
+
+				FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.BayerPattern", value);
+			}
+		}
+	
+		return dib;
+
+	} catch(const char *text) {
+		FreeImage_Unload(dib);
+		FreeImage_OutputMessageProc(s_format_id, text);
+		return NULL;
+	}
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "RAW";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "RAW camera image";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	/**
+	Below are known RAW file extensions that you can check using FreeImage_GetFIFFromFormat. 
+	If a file extension is not listed, that doesn't mean that you cannot load it. 
+	Using FreeImage_GetFileType is the best way to know if a RAW file format is supported. 
+	*/
+	static const char *raw_extensions = 
+		"3fr,"   // Hasselblad Digital Camera Raw Image Format.
+		"arw,"   // Sony Digital Camera Raw Image Format for Alpha devices.
+		"bay,"   // Casio Digital Camera Raw File Format.
+		"bmq,"   // NuCore Raw Image File.
+		"cap,"   // Phase One Digital Camera Raw Image Format.
+		"cine,"  // Phantom Software Raw Image File.
+		"cr2,"   // Canon Digital Camera RAW Image Format version 2.0. These images are based on the TIFF image standard.
+		"crw,"   // Canon Digital Camera RAW Image Format version 1.0. 
+		"cs1,"   // Sinar Capture Shop Raw Image File.
+		"dc2,"   // Kodak DC25 Digital Camera File.
+		"dcr,"   // Kodak Digital Camera Raw Image Format for these models: Kodak DSC Pro SLR/c, Kodak DSC Pro SLR/n, Kodak DSC Pro 14N, Kodak DSC PRO 14nx.
+		"drf,"   // Kodak Digital Camera Raw Image Format.
+		"dsc,"   // Kodak Digital Camera Raw Image Format.
+		"dng,"   // Adobe Digital Negative: DNG is publicly available archival format for the raw files generated by digital cameras. By addressing the lack of an open standard for the raw files created by individual camera models, DNG helps ensure that photographers will be able to access their files in the future. 
+		"erf,"   // Epson Digital Camera Raw Image Format.
+		"fff,"   // Imacon Digital Camera Raw Image Format.
+		"ia,"    // Sinar Raw Image File.
+		"iiq,"   // Phase One Digital Camera Raw Image Format.
+		"k25,"   // Kodak DC25 Digital Camera Raw Image Format.
+		"kc2,"   // Kodak DCS200 Digital Camera Raw Image Format.
+		"kdc,"   // Kodak Digital Camera Raw Image Format.
+		"mdc,"   // Minolta RD175 Digital Camera Raw Image Format.
+		"mef,"   // Mamiya Digital Camera Raw Image Format.
+		"mos,"   // Leaf Raw Image File.
+		"mrw,"   // Minolta Dimage Digital Camera Raw Image Format.
+		"nef,"   // Nikon Digital Camera Raw Image Format.
+		"nrw,"   // Nikon Digital Camera Raw Image Format.
+		"orf,"   // Olympus Digital Camera Raw Image Format.
+		"pef,"   // Pentax Digital Camera Raw Image Format.
+		"ptx,"   // Pentax Digital Camera Raw Image Format.
+		"pxn,"   // Logitech Digital Camera Raw Image Format.
+		"qtk,"   // Apple Quicktake 100/150 Digital Camera Raw Image Format.
+		"raf,"   // Fuji Digital Camera Raw Image Format.
+		"raw,"   // Panasonic Digital Camera Image Format.
+		"rdc,"   // Digital Foto Maker Raw Image File.
+		"rw2,"   // Panasonic LX3 Digital Camera Raw Image Format.
+		"rwl,"	 // Leica Camera Raw Image Format.
+		"rwz,"   // Rawzor Digital Camera Raw Image Format.
+		"sr2,"   // Sony Digital Camera Raw Image Format.
+		"srf,"   // Sony Digital Camera Raw Image Format for DSC-F828 8 megapixel digital camera or Sony DSC-R1.
+		"srw,"   // Samsung Raw Image Format.
+		"sti,"   // Sinar Capture Shop Raw Image File.
+		"x3f";   // Sigma Digital Camera Raw Image Format for devices based on Foveon X3 direct image sensor.
+	return raw_extensions;
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-dcraw";
+}
+
+static BOOL 
+HasMagicHeader(FreeImageIO *io, fi_handle handle) {
+	const unsigned signature_size = 32;
+	BYTE signature[signature_size] = { 0 };
+	/*
+	note: classic TIFF signature is
+	{ 0x49, 0x49, 0x2A, 0x00 } Classic TIFF, little-endian
+	{ 0x4D, 0x4D, 0x00, 0x2A } Classic TIFF, big-endian
+	*/
+	// Canon (CR2), little-endian byte order
+	const BYTE CR2_II[] = { 0x49, 0x49, 0x2A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x52, 0x02, 0x00 };
+	// Canon (CRW), little-endian byte order
+	const BYTE CRW_II[] = { 0x49, 0x49, 0x1A, 0x00, 0x00, 0x00, 0x48, 0x45, 0x41, 0x50, 0x43, 0x43, 0x44, 0x52, 0x02, 0x00 };
+	// Minolta (MRW)
+	const BYTE MRW[] = { 0x00, 0x4D, 0x52, 0x4D, 0x00 };
+	// Olympus (ORF), little-endian byte order
+	const BYTE ORF_IIRS[] = { 0x49, 0x49, 0x52, 0x53, 0x08, 0x00, 0x00, 0x00 }; 
+	const BYTE ORF_IIRO[] = { 0x49, 0x49, 0x52, 0x4F, 0x08, 0x00, 0x00, 0x00 }; 
+	// Olympus (ORF), big-endian byte order
+	const BYTE ORF_MMOR[] = { 0x4D, 0x4D, 0x4F, 0x52, 0x00, 0x00, 0x00, 0x08 }; 
+	// Fujifilm (RAF)
+	const BYTE RAF[] = { 0x46, 0x55, 0x4A, 0x49, 0x46, 0x49, 0x4C, 0x4D, 0x43, 0x43, 0x44, 0x2D, 0x52, 0x41, 0x57, 0x20 };
+	// Panasonic (RW2) or Leica (RWL), little-endian byte order
+	const BYTE RWx_II[] = { 0x49, 0x49, 0x55, 0x00, 0x18, 0x00, 0x00, 0x00, 0x88, 0xE7, 0x74, 0xD8, 0xF8, 0x25, 0x1D, 0x4D, 0x94, 0x7A, 0x6E, 0x77, 0x82, 0x2B, 0x5D, 0x6A };
+	// Panasonic (RAW) or Leica (RAW), little-endian byte order
+	const BYTE RAW_II[] = { 0x49, 0x49, 0x55, 0x00, 0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00 };
+	// Foveon (X3F)
+	const BYTE X3F[] = { 0x46, 0x4F, 0x56, 0x62 };
+
+	if(io->read_proc(signature, 1, signature_size, handle) != signature_size) {
+		return FALSE;
+	}
+	if(memcmp(CR2_II, signature, 12) == 0)
+		return TRUE;
+	if(memcmp(CRW_II, signature, 16) == 0)
+		return TRUE;
+	if(memcmp(MRW, signature, 5) == 0)
+		return TRUE;
+	if(memcmp(ORF_IIRS, signature, 8) == 0)
+		return TRUE;
+	if(memcmp(ORF_IIRO, signature, 8) == 0)
+		return TRUE;
+	if(memcmp(ORF_MMOR, signature, 8) == 0)
+		return TRUE;
+	if(memcmp(RAF, signature, 16) == 0)
+		return TRUE;
+	if(memcmp(RWx_II, signature, 24) == 0)
+		return TRUE;
+	if(memcmp(RAW_II, signature, 18) == 0)
+		return TRUE;
+	if(memcmp(X3F, signature, 4) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	// some RAW files have a magic signature (most of them have a TIFF signature)
+	// try to check this in order to speed up the file identification
+	{
+		long tell = io->tell_proc(handle);
+		if( HasMagicHeader(io, handle) ) {
+			return TRUE;
+		} else {
+			io->seek_proc(handle, tell, SEEK_SET);
+		}
+	}
+
+	// no magic signature : we need to open the file (it will take more time to identify it)
+	// do not declare RawProcessor on the stack as it may be huge (300 KB)
+	{
+		LibRaw *RawProcessor = new(std::nothrow) LibRaw;
+
+		if(RawProcessor) {
+			BOOL bSuccess = TRUE;
+
+			// wrap the input datastream
+			LibRaw_freeimage_datastream datastream(io, handle);
+
+			// open the datastream
+			if(RawProcessor->open_datastream(&datastream) != LIBRAW_SUCCESS) {
+				bSuccess = FALSE;	// LibRaw : failed to open input stream (unknown format)
+			}
+
+			// clean-up internal memory allocations
+			RawProcessor->recycle();
+			delete RawProcessor;
+
+			return bSuccess;
+		}
+	}
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	FIBITMAP *dib = NULL;
+	LibRaw *RawProcessor = NULL;
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	try {
+		// do not declare RawProcessor on the stack as it may be huge (300 KB)
+		RawProcessor = new(std::nothrow) LibRaw;
+		if(!RawProcessor) {
+			throw FI_MSG_ERROR_MEMORY;
+		}
+
+		// wrap the input datastream
+		LibRaw_freeimage_datastream datastream(io, handle);
+
+		// set decoding parameters
+		// the following parameters affect data reading
+		// --------------------------------------------
+
+		// (-s [0..N-1]) Select one raw image from input file
+		RawProcessor->imgdata.params.shot_select = 0;
+		// (-w) Use camera white balance, if possible (otherwise, fallback to auto_wb)
+		RawProcessor->imgdata.params.use_camera_wb = 1;
+		// (-M) Use any color matrix from the camera metadata. This option only affects Olympus, Leaf, and Phase One cameras.
+		RawProcessor->imgdata.params.use_camera_matrix = 1;
+		// (-h) outputs the image in 50% size
+		RawProcessor->imgdata.params.half_size = ((flags & RAW_HALFSIZE) == RAW_HALFSIZE) ? 1 : 0;
+
+		// open the datastream
+		if(RawProcessor->open_datastream(&datastream) != LIBRAW_SUCCESS) {
+			throw "LibRaw : failed to open input stream (unknown format)";
+		}
+
+		if(header_only) {
+			// header only mode
+			dib = FreeImage_AllocateHeaderT(header_only, FIT_RGB16, RawProcessor->imgdata.sizes.width, RawProcessor->imgdata.sizes.height);
+		}
+		else if((flags & RAW_UNPROCESSED) == RAW_UNPROCESSED) {
+			// load raw data without post-processing (i.e. as a Bayer matrix)
+			dib = libraw_LoadUnprocessedData(RawProcessor);
+		}
+		else if((flags & RAW_PREVIEW) == RAW_PREVIEW) {
+			// try to get the embedded JPEG
+			dib = libraw_LoadEmbeddedPreview(RawProcessor, 0);
+			if(!dib) {
+				// no JPEG preview: try to load as 8-bit/sample (i.e. RGB 24-bit)
+				dib = libraw_LoadRawData(RawProcessor, 8);
+			}
+		} 
+		else if((flags & RAW_DISPLAY) == RAW_DISPLAY) {
+			// load raw data as 8-bit/sample (i.e. RGB 24-bit)
+			dib = libraw_LoadRawData(RawProcessor, 8);
+		} 
+		else {
+			// default: load raw data as linear 16-bit/sample (i.e. RGB 48-bit)
+			dib = libraw_LoadRawData(RawProcessor, 16);
+		}
+
+		// save ICC profile if present
+		if(dib && (NULL != RawProcessor->imgdata.color.profile)) {
+			FreeImage_CreateICCProfile(dib, RawProcessor->imgdata.color.profile, RawProcessor->imgdata.color.profile_length);
+		}
+
+		// try to get JPEG embedded Exif metadata
+		if(dib && !((flags & RAW_PREVIEW) == RAW_PREVIEW)) {
+			FIBITMAP *metadata_dib = libraw_LoadEmbeddedPreview(RawProcessor, FIF_LOAD_NOPIXELS);
+			if(metadata_dib) {
+				FreeImage_CloneMetadata(dib, metadata_dib);
+				FreeImage_Unload(metadata_dib);
+			}
+		}
+
+		// clean-up internal memory allocations
+		RawProcessor->recycle();
+		delete RawProcessor;
+
+		return dib;
+
+	} catch(const char *text) {
+		if(RawProcessor) {
+			RawProcessor->recycle();
+			delete RawProcessor;
+		}
+		if(dib) {
+			FreeImage_Unload(dib);
+		}
+		FreeImage_OutputMessageProc(s_format_id, text);
+	}
+
+	return NULL;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitRAW(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginSGI.cpp b/files/Source/FreeImage/PluginSGI.cpp
new file mode 100644
index 0000000..35fd2ec
--- /dev/null
+++ b/files/Source/FreeImage/PluginSGI.cpp
@@ -0,0 +1,425 @@
+// ==========================================================
+// SGI Loader
+//
+// Design and implementation by
+// - Sherman Wilcox
+// - Noam Gat
+//
+// References : 
+// ------------
+// - The SGI Image File Format, Version 1.0
+// http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html
+// - SGI RGB Image Format
+// http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/
+//
+//
+// 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
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagSGIHeader {
+	/** IRIS image file magic number. This should be decimal 474. */
+	WORD magic;
+	/** Storage format: 0 for uncompressed, 1 for RLE compression. */
+	BYTE storage;
+	/** Number of bytes per pixel channel. Legally 1 or 2. */
+	BYTE bpc;
+	/**
+	Number of dimensions. Legally 1, 2, or 3. 
+	1 means a single row, XSIZE long
+	2 means a single 2D image
+	3 means multiple 2D images
+	*/
+	WORD dimension;	
+	/** X size in pixels */
+	WORD xsize;
+	/** Y size in pixels */
+	WORD ysize;
+	/**
+	Number of channels. 
+	1 indicates greyscale
+	3 indicates RGB
+	4 indicates RGB and Alpha
+	*/
+	WORD zsize;
+	/** Minimum pixel value. This is the lowest pixel value in the image.*/
+	LONG pixmin;
+	/** Maximum pixel value. This is the highest pixel value in the image.*/
+	LONG pixmax;
+	/** Ignored. Normally set to 0. */
+	char dummy[4];
+	/** Image name. Must be null terminated, therefore at most 79 bytes. */
+	char imagename[80];
+	/** 
+	Colormap ID. 
+	0 - normal mode
+	1 - dithered, 3 mits for red and green, 2 for blue, obsolete
+	2 - index colour, obsolete
+	3 - not an image but a colourmap
+	*/
+	LONG colormap;
+	/** Ignored. Should be set to 0, makes the header 512 bytes. */
+	char reserved[404];
+} SGIHeader;
+
+typedef struct tagRLEStatus {
+  int cnt;
+  int val;
+} RLEStatus;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+static const char *SGI_LESS_THAN_HEADER_LENGTH = "Incorrect header size";
+static const char *SGI_16_BIT_COMPONENTS_NOT_SUPPORTED = "No 16 bit support";
+static const char *SGI_COLORMAPS_NOT_SUPPORTED = "No colormap support";
+static const char *SGI_EOF_IN_RLE_INDEX = "EOF in run length encoding";
+static const char *SGI_EOF_IN_IMAGE_DATA = "EOF in image data";
+static const char *SGI_INVALID_CHANNEL_COUNT = "Invalid channel count";
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+#ifndef FREEIMAGE_BIGENDIAN
+static void 
+SwapHeader(SGIHeader *header) {
+	SwapShort(&header->magic);
+	SwapShort(&header->dimension);
+	SwapShort(&header->xsize);
+	SwapShort(&header->ysize);
+	SwapShort(&header->zsize);
+	SwapLong((DWORD*)&header->pixmin);
+	SwapLong((DWORD*)&header->pixmax);
+	SwapLong((DWORD*)&header->colormap);
+}
+#endif
+
+static int 
+get_rlechar(FreeImageIO *io, fi_handle handle, RLEStatus *pstatus) {
+	if (!pstatus->cnt) {
+		int cnt = 0;
+		while (0 == cnt) {
+			BYTE packed = 0;
+			if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
+				return EOF;
+			}
+			cnt = packed;
+		}
+		if (cnt == EOF) {
+			return EOF;
+		}
+		pstatus->cnt = cnt & 0x7F;
+		if (cnt & 0x80) {
+			pstatus->val = -1;
+		} else {
+			BYTE packed = 0;
+			if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
+				return EOF;
+			}
+			pstatus->val = packed;
+		}
+	}
+	pstatus->cnt--;
+	if (pstatus->val == -1) {
+		BYTE packed = 0;
+		if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
+			return EOF;
+		}
+		return packed;
+	}
+	else {
+		return pstatus->val;
+	}
+}
+
+static const char * DLL_CALLCONV
+Format() {
+  return "SGI";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+  return "SGI Image Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+  return "sgi,rgb,rgba,bw";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+  return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+  return "image/x-sgi";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE sgi_signature[2] = { 0x01, 0xDA };
+	BYTE signature[2] = { 0, 0 };
+
+	io->read_proc(signature, 1, sizeof(sgi_signature), handle);
+
+	return (memcmp(sgi_signature, signature, sizeof(sgi_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+  return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+  return FALSE;
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	int width = 0, height = 0, zsize = 0;
+	int i, dim;
+	int bitcount;
+	SGIHeader sgiHeader;
+	RLEStatus my_rle_status;
+	FIBITMAP *dib = NULL;
+	LONG *pRowIndex = NULL;
+
+	try {
+		// read the header
+		memset(&sgiHeader, 0, sizeof(SGIHeader));
+		if(io->read_proc(&sgiHeader, 1, sizeof(SGIHeader), handle) < sizeof(SGIHeader)) {
+		   throw SGI_LESS_THAN_HEADER_LENGTH;
+		}
+#ifndef FREEIMAGE_BIGENDIAN
+		SwapHeader(&sgiHeader);
+#endif
+		if(sgiHeader.magic != 474) {
+			throw FI_MSG_ERROR_MAGIC_NUMBER;
+		}
+		
+		BOOL bIsRLE = (sgiHeader.storage == 1) ? TRUE : FALSE;
+	
+		// check for unsupported image types
+		if (sgiHeader.bpc != 1) {
+			// Expected one byte per color component
+			throw SGI_16_BIT_COMPONENTS_NOT_SUPPORTED; 
+		}
+		if (sgiHeader.colormap != 0) {
+			// Indexed or dithered images not supported
+			throw SGI_COLORMAPS_NOT_SUPPORTED; 
+		}
+
+		// get the width & height
+		dim = sgiHeader.dimension;
+		width = sgiHeader.xsize;
+		if (dim < 3) {
+			zsize = 1;
+		} else {
+			zsize = sgiHeader.zsize;
+		}
+
+		if (dim < 2) {
+			height = 1;
+		} else {
+			height = sgiHeader.ysize;
+		}
+		
+		if(bIsRLE) {
+			// read the Offset Tables 
+			int index_len = height * zsize;
+			pRowIndex = (LONG*)malloc(index_len * sizeof(LONG));
+			if(!pRowIndex) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+			
+			if ((unsigned)index_len != io->read_proc(pRowIndex, sizeof(LONG), index_len, handle)) {
+				throw SGI_EOF_IN_RLE_INDEX;
+			}
+			
+#ifndef FREEIMAGE_BIGENDIAN		
+			// Fix byte order in index
+			for (i = 0; i < index_len; i++) {
+				SwapLong((DWORD*)&pRowIndex[i]);
+			}
+#endif
+			// Discard row size index
+			for (i = 0; i < (int)(index_len * sizeof(LONG)); i++) {
+				BYTE packed = 0;
+				if( io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1 ) {
+					throw SGI_EOF_IN_RLE_INDEX;
+				}
+			}
+		}
+		
+		switch(zsize) {
+			case 1:
+				bitcount = 8;
+				break;
+			case 2:
+				//Grayscale+Alpha. Need to fake RGBA
+				bitcount = 32;
+				break;
+			case 3:
+				bitcount = 24;
+				break;
+			case 4:
+				bitcount = 32;
+				break;
+			default:
+				throw SGI_INVALID_CHANNEL_COUNT;
+		}
+		
+		dib = FreeImage_Allocate(width, height, bitcount);
+		if(!dib) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+		
+		if (bitcount == 8) {
+			// 8-bit SGI files are grayscale images, so we'll generate
+			// a grayscale palette.
+			RGBQUAD *pclrs = FreeImage_GetPalette(dib);
+			for (i = 0; i < 256; i++) {
+				pclrs[i].rgbRed = (BYTE)i;
+				pclrs[i].rgbGreen = (BYTE)i;
+				pclrs[i].rgbBlue = (BYTE)i;
+				pclrs[i].rgbReserved = 0;
+			}
+		}
+
+		// decode the image
+
+		memset(&my_rle_status, 0, sizeof(RLEStatus));
+		
+		int ns = FreeImage_GetPitch(dib);                                                    
+		BYTE *pStartRow = FreeImage_GetScanLine(dib, 0);
+		int offset_table[] = { 2, 1, 0, 3 };
+		int numChannels = zsize;
+		if (zsize < 3) {
+			offset_table[0] = 0;
+		}
+		if (zsize == 2)
+		{
+			//This is how faked grayscale+alpha works.
+			//First channel goes into first 
+			//second channel goes into alpha (4th channel)
+			//Two channels are left empty and will be copied later
+			offset_table[1] = 3;
+			numChannels = 4;
+		}
+		
+		LONG *pri = pRowIndex;
+		for (i = 0; i < zsize; i++) {
+			BYTE *pRow = pStartRow + offset_table[i];
+			for (int j = 0; j < height; j++, pRow += ns, pri++) {
+				BYTE *p = pRow;
+				if (bIsRLE) {
+					my_rle_status.cnt = 0;
+					io->seek_proc(handle, *pri, SEEK_SET);
+				}
+				for (int k = 0; k < width; k++, p += numChannels) {
+					int ch;
+					BYTE packed = 0;
+					if (bIsRLE) {
+						ch = get_rlechar(io, handle, &my_rle_status);
+						packed = (BYTE)ch;
+					}
+					else {
+						ch = io->read_proc(&packed, sizeof(BYTE), 1, handle);
+					}
+					if (ch == EOF) {
+						throw SGI_EOF_IN_IMAGE_DATA;
+					}
+					*p = packed;
+				}
+			}
+		}
+		
+		if (zsize == 2)
+		{
+			BYTE *pRow = pStartRow;
+			//If faking RGBA from grayscale + alpha, copy first channel to second and third
+			for (int i=0; i<height; i++, pRow += ns)
+			{
+				BYTE *pPixel = pRow;
+				for (int j=0; j<width; j++)
+				{
+					pPixel[2] = pPixel[1] = pPixel[0];
+					pPixel += 4;
+				}
+			}
+		}
+		if(pRowIndex)
+			free(pRowIndex);
+
+		return dib;
+
+	} catch(const char *text) {
+		if(pRowIndex) free(pRowIndex);
+		if(dib) FreeImage_Unload(dib);
+		FreeImage_OutputMessageProc(s_format_id, text);
+		return NULL;
+	}
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV 
+InitSGI(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+	
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+}
+
diff --git a/files/Source/FreeImage/PluginTARGA.cpp b/files/Source/FreeImage/PluginTARGA.cpp
new file mode 100644
index 0000000..8486430
--- /dev/null
+++ b/files/Source/FreeImage/PluginTARGA.cpp
@@ -0,0 +1,1591 @@
+// ==========================================================
+// TARGA Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Jani Kajala (janik@remedy.fi)
+// - Martin Weber (martweb@gmx.net)
+// - Machiel ten Brinke (brinkem@uni-one.nl)
+// - Peter Lemmens (peter.lemmens@planetinternet.be)
+// - Hervé Drolon (drolon@infonie.fr)
+// - 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!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//   Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagTGAHEADER {
+	BYTE id_length;				//! length of the image ID field
+	BYTE color_map_type;		//! whether a color map is included
+	BYTE image_type;			//! compression and color types
+
+	WORD cm_first_entry;		//! first entry index (offset into the color map table)
+	WORD cm_length;				//! color map length (number of entries)
+	BYTE cm_size;				//! color map entry size, in bits (number of bits per pixel)
+
+	WORD is_xorigin;			//! X-origin of image (absolute coordinate of lower-left corner for displays where origin is at the lower left)
+	WORD is_yorigin;			//! Y-origin of image (as for X-origin)
+	WORD is_width;				//! image width
+	WORD is_height;				//! image height
+	BYTE is_pixel_depth;		//! bits per pixel
+	BYTE is_image_descriptor;	//! image descriptor, bits 3-0 give the alpha channel depth, bits 5-4 give direction
+} TGAHEADER;
+
+typedef struct tagTGAEXTENSIONAREA {
+	WORD extension_size;		// Size in bytes of the extension area, always 495
+	char author_name[41];		// Name of the author. If not used, bytes should be set to NULL (\0) or spaces
+	char author_comments[324];	// A comment, organized as four lines, each consisting of 80 characters plus a NULL
+	WORD datetime_stamp[6];		// Date and time at which the image was created
+	char job_name[41];			// Job ID
+	WORD job_time[3];			// Hours, minutes and seconds spent creating the file (for billing, etc.)
+	char software_id[41];		// The application that created the file
+	BYTE software_version[3];
+	DWORD key_color;
+	WORD pixel_aspect_ratio[2];
+	WORD gamma_value[2];
+	DWORD color_correction_offset;	// Number of bytes from the beginning of the file to the color correction table if present
+	DWORD postage_stamp_offset;		// Number of bytes from the beginning of the file to the postage stamp image if present
+	DWORD scan_line_offset;			// Number of bytes from the beginning of the file to the scan lines table if present
+	BYTE attributes_type;			// Specifies the alpha channel
+} TGAEXTENSIONAREA;
+
+typedef struct tagTGAFOOTER {
+	DWORD extension_offset;	// extension area offset : offset in bytes from the beginning of the file
+	DWORD developer_offset;	// developer directory offset : offset in bytes from the beginning of the file
+	char signature[18];		// signature string : contains "TRUEVISION-XFILE.\0"
+} TGAFOOTER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+static const char *FI_MSG_ERROR_CORRUPTED = "Image data corrupted";
+
+// ----------------------------------------------------------
+// Image type
+//
+#define TGA_NULL		0	// no image data included
+#define TGA_CMAP		1	// uncompressed, color-mapped image
+#define TGA_RGB			2	// uncompressed, true-color image
+#define TGA_MONO		3	// uncompressed, black-and-white image
+#define TGA_RLECMAP		9	// run-length encoded, color-mapped image
+#define TGA_RLERGB		10	// run-length encoded, true-color image
+#define TGA_RLEMONO		11	// run-length encoded, black-and-white image
+#define TGA_CMPCMAP		32	// compressed (Huffman/Delta/RLE) color-mapped image (e.g., VDA/D) - Obsolete
+#define TGA_CMPCMAP4	33	// compressed (Huffman/Delta/RLE) color-mapped four pass image (e.g., VDA/D) - Obsolete
+
+// ==========================================================
+// Thumbnail functions
+// ==========================================================
+
+class TargaThumbnail
+{
+public:
+	TargaThumbnail() : _w(0), _h(0), _depth(0), _data(NULL) { 
+	}
+	~TargaThumbnail() { 
+		if(_data) {
+			free(_data); 
+		}
+	}
+
+	BOOL isNull() const { 
+		return (_data == NULL); 
+	}
+	
+	BOOL read(FreeImageIO *io, fi_handle handle, size_t size) {
+		io->read_proc(&_w, 1, 1, handle);
+		io->read_proc(&_h, 1, 1, handle);
+		
+		const size_t sizeofData = size - 2;
+		_data = (BYTE*)malloc(sizeofData);
+		if(_data) {
+			return (io->read_proc(_data, 1, (unsigned)sizeofData, handle) == sizeofData);
+		}
+		return FALSE;
+	}
+	
+	void setDepth(BYTE dp) { 
+		_depth = dp;
+	}
+	
+	FIBITMAP* toFIBITMAP();
+	
+private:
+	BYTE _w;
+	BYTE _h;
+	BYTE _depth;
+	BYTE* _data;
+};
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void 
+swapShortPixels(FIBITMAP* dib) {
+	if(FreeImage_GetImageType(dib) != FIT_BITMAP) {
+		return;
+	}
+		
+	const unsigned Bpp = FreeImage_GetBPP(dib)/8;
+	if(Bpp != 2) {
+		return;
+	}
+		
+	BYTE* bits = FreeImage_GetBits(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+	const unsigned pitch = FreeImage_GetPitch(dib);
+	
+	BYTE* line = bits;
+	for(unsigned y = 0; y < height; y++, line += pitch) {
+		for(BYTE* pixel = line; pixel < line + pitch ; pixel += Bpp) {
+			SwapShort((WORD*)pixel);
+		}
+	}
+}
+#endif // FREEIMAGE_BIGENDIAN
+
+FIBITMAP* TargaThumbnail::toFIBITMAP() {
+	if(isNull() || _depth == 0) {
+		return NULL;
+	}
+		
+	const unsigned line_size = _depth * _w / 8;
+	FIBITMAP* dib = FreeImage_Allocate(_w, _h, _depth);
+	if(!dib) {
+		return NULL;
+	}
+
+	const BYTE* line = _data;
+	const BYTE height = _h;
+	for (BYTE h = 0; h < height; ++h, line += line_size) {
+		BYTE* dst_line = FreeImage_GetScanLine(dib, height - 1 - h);
+		memcpy(dst_line, line, line_size);
+	}
+
+#ifdef FREEIMAGE_BIGENDIAN
+	swapShortPixels(dib);
+#endif
+	
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+	SwapRedBlue32(dib);
+#endif
+
+	return dib;
+}
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/** This class is used when loading RLE compressed images, it implements io cache of fixed size.
+	In general RLE compressed images *should* be compressed line by line with line sizes stored in Scan Line Table section.
+	In reality, however there are images not obeying the specification, compressing image data continuously across lines,
+	making it impossible to load the file cached at every line.
+*/
+class IOCache
+{
+public:
+	IOCache(FreeImageIO *io, fi_handle handle, size_t size) :
+		_ptr(NULL), _begin(NULL), _end(NULL), _size(size), _io(io), _handle(handle)	{
+			_begin = (BYTE*)malloc(size);
+			if (_begin) {
+			_end = _begin + _size;
+			_ptr = _end;	// will force refill on first access
+		}
+	}
+	
+	~IOCache() {
+		if (_begin != NULL) {
+			free(_begin);
+		}	
+	}
+		
+	BOOL isNull() { return _begin == NULL;}
+	
+	inline
+	BYTE getByte() {
+		if (_ptr >= _end) {
+			// need refill
+			_ptr = _begin;
+			_io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle);	//### EOF - no problem?
+		}
+
+		BYTE result = *_ptr;
+
+		_ptr++;
+
+		return result;
+	}
+	
+	inline
+	BYTE* getBytes(size_t count /*must be < _size!*/) {
+		if (_ptr + count >= _end) {
+			
+			// need refill
+
+			// 'count' bytes might span two cache bounds,
+			// SEEK back to add the remains of the current cache again into the new one
+
+			long read = long(_ptr - _begin);
+			long remaining = long(_size - read);
+
+			_io->seek_proc(_handle, -remaining, SEEK_CUR);
+
+			_ptr = _begin;
+			_io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle);	//### EOF - no problem?
+		}
+
+		BYTE *result = _ptr;
+
+		_ptr += count;
+
+		return result;
+	}
+
+private:
+	IOCache& operator=(const IOCache& src); // deleted
+	IOCache(const IOCache& other); // deleted
+
+private:
+	BYTE *_ptr;
+	BYTE *_begin;
+	BYTE *_end;
+	const size_t _size;
+	const FreeImageIO *_io;	
+	const fi_handle _handle;	
+};
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapHeader(TGAHEADER *header) {
+	SwapShort(&header->cm_first_entry);
+	SwapShort(&header->cm_length);
+	SwapShort(&header->is_xorigin);
+	SwapShort(&header->is_yorigin);
+	SwapShort(&header->is_width);
+	SwapShort(&header->is_height);
+}
+
+static void
+SwapExtensionArea(TGAEXTENSIONAREA *ex) {
+	SwapShort(&ex->extension_size);
+	SwapShort(&ex->datetime_stamp[0]);
+	SwapShort(&ex->datetime_stamp[1]);
+	SwapShort(&ex->datetime_stamp[2]);
+	SwapShort(&ex->datetime_stamp[3]);
+	SwapShort(&ex->datetime_stamp[4]);
+	SwapShort(&ex->datetime_stamp[5]);
+	SwapShort(&ex->job_time[0]);
+	SwapShort(&ex->job_time[1]);
+	SwapShort(&ex->job_time[2]);
+	SwapLong (&ex->key_color);
+	SwapShort(&ex->pixel_aspect_ratio[0]);
+	SwapShort(&ex->pixel_aspect_ratio[1]);
+	SwapShort(&ex->gamma_value[0]);
+	SwapShort(&ex->gamma_value[1]);
+	SwapLong (&ex->color_correction_offset);
+	SwapLong (&ex->postage_stamp_offset);
+	SwapLong (&ex->scan_line_offset);
+}
+
+static void
+SwapFooter(TGAFOOTER *footer) {
+	SwapLong(&footer->extension_offset);
+	SwapLong(&footer->developer_offset);
+}
+
+#endif // FREEIMAGE_BIGENDIAN
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "TARGA";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Truevision Targa";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "tga,targa";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-tga";
+}
+
+static BOOL 
+isTARGA20(FreeImageIO *io, fi_handle handle) {
+	const unsigned sizeofSig = 18;
+	BYTE signature[sizeofSig];
+	// tga_signature = "TRUEVISION-XFILE." (TGA 2.0 only)
+	BYTE tga_signature[sizeofSig] = { 84, 82, 85, 69, 86, 73, 83, 73, 79, 78, 45, 88, 70, 73, 76, 69, 46, 0 };
+	// get the start offset
+	const long start_offset = io->tell_proc(handle);
+	// get the end-of-file
+	io->seek_proc(handle, 0, SEEK_END);
+	const long eof = io->tell_proc(handle);
+	// read the signature
+	io->seek_proc(handle, start_offset + eof - sizeofSig, SEEK_SET);
+	io->read_proc(&signature, 1, sizeofSig, handle);
+	// rewind
+	io->seek_proc(handle, start_offset, SEEK_SET);
+		
+	return (memcmp(tga_signature, signature, sizeofSig) == 0);
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) { 
+	if(isTARGA20(io, handle)) {
+		return TRUE;
+	}
+		
+	// not a 2.0 image, try testing if it's a valid TGA anyway (not robust)
+	{
+		const long start_offset = io->tell_proc(handle);
+		
+		// get the header
+		TGAHEADER header;
+		io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapHeader(&header);
+#endif
+		// rewind
+		io->seek_proc(handle, start_offset, SEEK_SET);
+
+		// the color map type should be a 0 or a 1...
+		if(header.color_map_type != 0 && header.color_map_type != 1) {
+			return FALSE;
+		}
+		// if the color map type is 1 then we validate the map entry information...
+		if(header.color_map_type > 0) {
+			// it doesn't make any sense if the first entry is larger than the color map table
+			if(header.cm_first_entry >= header.cm_length) {
+				return FALSE;
+			}
+			// check header.cm_size, don't allow 0 or anything bigger than 32
+			if(header.cm_size == 0 || header.cm_size > 32) {
+				return FALSE;
+			}
+		}
+		// the width/height shouldn't be 0, right ?
+		if(header.is_width == 0 || header.is_height == 0) {
+			return FALSE;
+		}
+		// let's now verify all the types that are supported by FreeImage (this is our final verification)
+		switch(header.image_type) {
+			case TGA_CMAP:
+			case TGA_RGB:
+			case TGA_MONO:
+			case TGA_RLECMAP:
+			case TGA_RLERGB:
+			case TGA_RLEMONO:
+				switch(header.is_pixel_depth) {
+					case 8	:
+					case 16:
+					case 24:
+					case 32:
+						return TRUE;
+					default:
+						return FALSE;
+				}
+				break;
+			default:
+				return FALSE;
+		}
+	}
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+		(depth == 8) ||
+		(depth == 16) ||
+		(depth == 24) ||
+		(depth == 32)
+		);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+/**
+Used for all 32 and 24 bit loading of uncompressed images
+*/
+static void 
+loadTrueColor(FIBITMAP* dib, int width, int height, int file_pixel_size, FreeImageIO* io, fi_handle handle, BOOL as24bit) {
+	const int pixel_size = as24bit ? 3 : file_pixel_size;
+
+	// input line cache
+	BYTE* file_line = (BYTE*)malloc( width * file_pixel_size);
+
+	if (!file_line) {
+		throw FI_MSG_ERROR_MEMORY;
+	}
+
+	for (int y = 0; y < height; y++) {
+		BYTE *bits = FreeImage_GetScanLine(dib, y);
+		io->read_proc(file_line, file_pixel_size, width, handle);
+		BYTE *bgra = file_line;
+
+		for (int x = 0; x < width; x++) {
+
+			bits[FI_RGBA_BLUE]	= bgra[0];
+			bits[FI_RGBA_GREEN] = bgra[1];
+			bits[FI_RGBA_RED]	= bgra[2];
+
+			if (!as24bit) {
+				bits[FI_RGBA_ALPHA] = bgra[3];
+			}
+
+			bgra += file_pixel_size;
+
+			bits += pixel_size;
+		}
+	}
+
+	free(file_line);
+}
+
+/**
+For the generic RLE loader we need to abstract away the pixel format.
+We use a specific overload based on bits-per-pixel for each type of pixel
+*/
+
+template <int nBITS>
+inline static void 
+_assignPixel(BYTE* bits, BYTE* val, BOOL as24bit = FALSE) {
+	// static assert should go here
+	assert(FALSE);
+}
+
+template <>
+inline void 
+_assignPixel<8>(BYTE* bits, BYTE* val, BOOL as24bit) {
+	*bits = *val;
+}
+
+template <>
+inline void 
+_assignPixel<16>(BYTE* bits, BYTE* val, BOOL as24bit) {
+	WORD value(*reinterpret_cast<WORD*>(val));
+
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapShort(&value);
+#endif
+
+	if (as24bit) {
+		bits[FI_RGBA_BLUE]  = (BYTE)((((value & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+		bits[FI_RGBA_GREEN] = (BYTE)((((value & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+		bits[FI_RGBA_RED]   = (BYTE)((((value & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+
+	} else {
+		*reinterpret_cast<WORD *>(bits) = 0x7FFF & value;
+	}
+}
+
+template <>
+inline void 
+_assignPixel<24>(BYTE* bits, BYTE* val, BOOL as24bit) {
+	bits[FI_RGBA_BLUE]	= val[0];
+	bits[FI_RGBA_GREEN] = val[1];
+	bits[FI_RGBA_RED]	= val[2];
+}
+
+template <>
+inline void 
+_assignPixel<32>(BYTE* bits, BYTE* val, BOOL as24bit) {
+	if (as24bit) {
+		_assignPixel<24>(bits, val, TRUE);
+
+	} else {
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+		*(reinterpret_cast<unsigned*>(bits)) = *(reinterpret_cast<unsigned*> (val));
+#else // NOTE This is faster then doing reinterpret_cast to int + INPLACESWAP !
+		bits[FI_RGBA_BLUE]	= val[0];
+		bits[FI_RGBA_GREEN] = val[1];
+		bits[FI_RGBA_RED]	= val[2];
+		bits[FI_RGBA_ALPHA]	= val[3];
+#endif
+	}
+}
+
+/**
+Generic RLE loader
+*/
+template<int bPP>
+static void 
+loadRLE(FIBITMAP* dib, int width, int height, FreeImageIO* io, fi_handle handle, long eof, BOOL as24bit) {
+	const int file_pixel_size = bPP/8;
+	const int pixel_size = as24bit ? 3 : file_pixel_size;
+
+	const BYTE bpp = as24bit ? 24 : bPP;
+	const int line_size = CalculateLine(width, bpp);
+
+	// Note, many of the params can be computed inside the function.
+	// However, because this is a template function, it will lead to redundant code duplication.
+
+	BYTE rle;
+	BYTE *line_bits;
+
+	// this is used to guard against writing beyond the end of the image (on corrupted rle block)
+	const BYTE* dib_end = FreeImage_GetScanLine(dib, height);//< one-past-end row
+
+	// Compute the rough size of a line...
+	long pixels_offset = io->tell_proc(handle);
+	long sz = ((eof - pixels_offset) / height);
+
+	// ...and allocate cache of this size (yields good results)
+	IOCache cache(io, handle, sz);
+	if(cache.isNull()) {
+		FreeImage_Unload(dib);
+		dib = NULL;
+		return;
+	}
+		
+	int x = 0, y = 0;
+
+	line_bits = FreeImage_GetScanLine(dib, y);
+
+	while (y < height) {
+
+		rle = cache.getByte();
+
+		BOOL has_rle = rle & 0x80;
+		rle &= ~0x80; // remove type-bit
+
+		BYTE packet_count = rle + 1;
+
+		//packet_count might be corrupt, test if we are not about to write beyond the last image bit
+
+		if ((line_bits+x) + packet_count*pixel_size > dib_end) {
+			FreeImage_OutputMessageProc(s_format_id, FI_MSG_ERROR_CORRUPTED);
+			// return what is left from the bitmap
+			return;
+		}
+
+		if (has_rle) {
+
+			// read a pixel value from file...
+			BYTE *val = cache.getBytes(file_pixel_size);
+
+			//...and fill packet_count pixels with it
+
+			for (int ix = 0; ix < packet_count; ix++) {
+				_assignPixel<bPP>((line_bits+x), val, as24bit);
+				x += pixel_size;
+
+				if (x >= line_size) {
+					x = 0;
+					y++;
+					line_bits = FreeImage_GetScanLine(dib, y);
+				}
+			}
+
+		} else {
+			// no rle commpresion
+
+			// copy packet_count pixels from file to dib
+			for (int ix = 0; ix < packet_count; ix++) {
+				BYTE *val = cache.getBytes(file_pixel_size);
+				_assignPixel<bPP>((line_bits+x), val, as24bit);
+				x += pixel_size;
+
+				if (x >= line_size) {
+					x = 0;
+					y++;
+					line_bits = FreeImage_GetScanLine(dib, y);
+				}
+			} //< packet_count
+		} //< has_rle
+
+	} //< while height
+
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	FIBITMAP *dib = NULL;
+
+	if (!handle) {
+		return NULL;
+	}
+
+	try {
+		
+		const BOOL header_only =  (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+				
+		// remember the start offset
+		long start_offset = io->tell_proc(handle);
+
+		// remember end-of-file (used for RLE cache)
+		io->seek_proc(handle, 0, SEEK_END);
+		long eof = io->tell_proc(handle);
+		io->seek_proc(handle, start_offset, SEEK_SET);
+
+		// read and process the bitmap's footer
+
+		TargaThumbnail thumbnail;
+		if(isTARGA20(io, handle)) {
+			TGAFOOTER footer;
+			const long footer_offset = start_offset + eof - sizeof(footer);
+			
+			io->seek_proc(handle, footer_offset, SEEK_SET);
+			io->read_proc(&footer, sizeof(tagTGAFOOTER), 1, handle);
+			
+#ifdef FREEIMAGE_BIGENDIAN
+			SwapFooter(&footer);
+#endif
+			BOOL hasExtensionArea = footer.extension_offset > 0;
+			if(hasExtensionArea) { 
+				TGAEXTENSIONAREA extensionarea;
+				io->seek_proc(handle, footer.extension_offset, SEEK_SET);
+				io->read_proc(&extensionarea, sizeof(extensionarea), 1, handle);
+				
+#ifdef FREEIMAGE_BIGENDIAN
+				SwapExtensionArea(&extensionarea);
+#endif
+
+				DWORD postage_stamp_offset = extensionarea.postage_stamp_offset;
+				BOOL hasThumbnail = (postage_stamp_offset > 0) && (postage_stamp_offset < (DWORD)footer_offset);
+				if(hasThumbnail) {
+					io->seek_proc(handle, postage_stamp_offset, SEEK_SET);
+					thumbnail.read(io, handle, footer_offset - postage_stamp_offset);
+				}
+			}
+		}
+		
+		// read and process the bitmap's header
+
+		TGAHEADER header;
+
+		io->seek_proc(handle, start_offset, SEEK_SET);
+		io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle);
+
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapHeader(&header);
+#endif
+
+		thumbnail.setDepth(header.is_pixel_depth);
+			
+		const int line = CalculateLine(header.is_width, header.is_pixel_depth);
+		const int pixel_bits = header.is_pixel_depth;
+		const int pixel_size = pixel_bits/8;
+
+		int fliphoriz = (header.is_image_descriptor & 0x10) ? 1 : 0;
+		int flipvert = (header.is_image_descriptor & 0x20) ? 1 : 0;
+
+		// skip comment
+		io->seek_proc(handle, header.id_length, SEEK_CUR);
+
+		switch (header.is_pixel_depth) {
+			case 8 : {
+				dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, 8);
+				
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+					
+				// read the palette (even if header only)
+
+				RGBQUAD *palette = FreeImage_GetPalette(dib);
+
+				if (header.color_map_type > 0) {
+					unsigned count, csize;
+
+					// calculate the color map size
+					csize = header.cm_length * header.cm_size / 8;
+					
+					// read the color map
+					BYTE *cmap = (BYTE*)malloc(csize * sizeof(BYTE));
+					if (cmap == NULL) {
+						throw FI_MSG_ERROR_DIB_MEMORY;
+					}
+					io->read_proc(cmap, sizeof(BYTE), csize, handle);
+
+					// build the palette
+
+					switch (header.cm_size) {
+						case 16: {
+							WORD *rgb555 = (WORD*)&cmap[0];
+							unsigned start = (unsigned)header.cm_first_entry;
+							unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length);
+
+							for (count = start; count < stop; count++) {
+								palette[count].rgbRed   = (BYTE)((((*rgb555 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+								palette[count].rgbGreen = (BYTE)((((*rgb555 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+								palette[count].rgbBlue  = (BYTE)((((*rgb555 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+								rgb555++;
+							}
+						}
+						break;
+
+						case 24: {
+							FILE_BGR *bgr = (FILE_BGR*)&cmap[0];
+							unsigned start = (unsigned)header.cm_first_entry;
+							unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length);
+
+							for (count = start; count < stop; count++) {
+								palette[count].rgbBlue  = bgr->b;
+								palette[count].rgbGreen = bgr->g;
+								palette[count].rgbRed   = bgr->r;
+								bgr++;
+							}
+						}
+						break;
+
+						case 32: {
+							BYTE trns[256];
+
+							// clear the transparency table
+							memset(trns, 0xFF, 256);
+
+							FILE_BGRA *bgra = (FILE_BGRA*)&cmap[0];
+							unsigned start = (unsigned)header.cm_first_entry;
+							unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length);
+
+							for (count = start; count < stop; count++) {
+								palette[count].rgbBlue  = bgra->b;
+								palette[count].rgbGreen = bgra->g;
+								palette[count].rgbRed   = bgra->r;
+								// alpha
+								trns[count] = bgra->a;
+								bgra++;
+							}
+
+							// set the tranparency table
+							FreeImage_SetTransparencyTable(dib, trns, 256);
+						}
+						break;
+
+					} // switch(header.cm_size)
+
+					free(cmap);
+				}
+				
+				// handle thumbnail
+				
+				FIBITMAP* th = thumbnail.toFIBITMAP();
+				if(th) {
+					RGBQUAD* pal = FreeImage_GetPalette(dib);
+					RGBQUAD* dst_pal = FreeImage_GetPalette(th);
+					if(dst_pal && pal) {
+						for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
+							dst_pal[i] = pal[i];
+						}
+					}
+
+					FreeImage_SetTransparencyTable(th, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+					
+					FreeImage_SetThumbnail(dib, th);
+					FreeImage_Unload(th);				
+				}
+
+				if(header_only) {
+					return dib;
+				}
+					
+				// read in the bitmap bits
+
+				switch (header.image_type) {
+					case TGA_CMAP:
+					case TGA_MONO: {
+						BYTE *bits = NULL;
+
+						for (unsigned count = 0; count < header.is_height; count++) {
+							bits = FreeImage_GetScanLine(dib, count);
+							io->read_proc(bits, sizeof(BYTE), line, handle);
+						}
+					}
+					break;
+
+					case TGA_RLECMAP:
+					case TGA_RLEMONO: { //(8 bit)
+						loadRLE<8>(dib, header.is_width, header.is_height, io, handle, eof, FALSE);
+					}
+					break;
+
+					default :
+						FreeImage_Unload(dib);
+						return NULL;
+				}
+			}
+			break; // header.is_pixel_depth == 8
+
+			case 15 :
+
+			case 16 : {
+				int pixel_bits = 16;
+
+				// allocate the dib
+
+				if (TARGA_LOAD_RGB888 & flags) {
+					pixel_bits = 24;
+					dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+
+				} else {
+					dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+				}
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+				
+				// handle thumbnail
+				
+				FIBITMAP* th = thumbnail.toFIBITMAP();
+				if(th) {
+					if(TARGA_LOAD_RGB888 & flags) {
+						FIBITMAP* t = FreeImage_ConvertTo24Bits(th);
+						FreeImage_Unload(th);
+						th = t;
+					}
+
+					FreeImage_SetThumbnail(dib, th);
+					FreeImage_Unload(th);
+				}
+						
+				if(header_only) {
+					return dib;
+				}
+
+				int line = CalculateLine(header.is_width, pixel_bits);
+
+				const unsigned pixel_size = unsigned(pixel_bits) / 8;
+				const unsigned src_pixel_size = sizeof(WORD);
+
+				// note header.cm_size is a misleading name, it should be seen as header.cm_bits
+				// ignore current position in file and set filepointer explicitly from the beginning of the file
+
+				int garblen = 0;
+
+				if (header.color_map_type != 0) {
+					garblen = (int)((header.cm_size + 7) / 8) * header.cm_length; /* should byte align */
+
+				} else {
+					garblen = 0;
+				}
+
+				io->seek_proc(handle, start_offset, SEEK_SET);
+				io->seek_proc(handle, sizeof(tagTGAHEADER) + header.id_length + garblen, SEEK_SET);
+
+				// read in the bitmap bits
+
+				switch (header.image_type) {
+					case TGA_RGB: { //(16 bit)
+						// input line cache
+						BYTE *in_line = (BYTE*)malloc(header.is_width * sizeof(WORD));
+
+						if (!in_line)
+							throw FI_MSG_ERROR_MEMORY;
+
+						const int h = header.is_height;
+
+						for (int y = 0; y < h; y++) {
+							
+							BYTE *bits = FreeImage_GetScanLine(dib, y);
+							io->read_proc(in_line, src_pixel_size, header.is_width, handle);
+							
+							BYTE *val = in_line;
+							for (int x = 0; x < line; x += pixel_size) {
+
+								_assignPixel<16>(bits+x, val, TARGA_LOAD_RGB888 & flags);
+
+								val += src_pixel_size;
+							}
+						}
+
+						free(in_line);
+					}
+					break;
+
+					case TGA_RLERGB: { //(16 bit)
+						loadRLE<16>(dib, header.is_width, header.is_height, io, handle, eof, TARGA_LOAD_RGB888 & flags);
+					}
+					break;
+
+					default :
+						FreeImage_Unload(dib);
+						return NULL;
+				}
+			}
+			break; // header.is_pixel_depth == 15 or 16
+
+			case 24 : {
+
+				dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+				
+				FIBITMAP* th = thumbnail.toFIBITMAP();
+				if(th) {
+					FreeImage_SetThumbnail(dib, th);
+					FreeImage_Unload(th);
+				}
+				
+				if(header_only) {
+					return dib;
+				}
+					
+				// read in the bitmap bits
+
+				switch (header.image_type) {
+					case TGA_RGB: { //(24 bit)
+						//uncompressed
+						loadTrueColor(dib, header.is_width, header.is_height, pixel_size,io, handle, TRUE);
+					}
+					break;
+
+					case TGA_RLERGB: { //(24 bit)
+						loadRLE<24>(dib, header.is_width, header.is_height, io, handle, eof, TRUE);
+					}
+					break;
+
+					default :
+						FreeImage_Unload(dib);
+						return NULL;
+				}
+			}
+			break;	// header.is_pixel_depth == 24
+
+			case 32 : {
+				int pixel_bits = 32;
+
+				if (TARGA_LOAD_RGB888 & flags) {
+					pixel_bits = 24;
+				}
+
+				dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+
+				if (dib == NULL) {
+					throw FI_MSG_ERROR_DIB_MEMORY;
+				}
+				
+				// handle thumbnail
+				
+				FIBITMAP* th = thumbnail.toFIBITMAP();
+				if(th) {
+					if(TARGA_LOAD_RGB888 & flags) {
+						FIBITMAP* t = FreeImage_ConvertTo24Bits(th);
+						FreeImage_Unload(th);
+						th = t;
+					}
+					FreeImage_SetThumbnail(dib, th);
+					FreeImage_Unload(th);
+				}
+
+				if(header_only) {
+					return dib;
+				}
+					
+				// read in the bitmap bits
+
+				switch (header.image_type) {
+					case TGA_RGB: { //(32 bit)
+						// uncompressed
+						loadTrueColor(dib, header.is_width, header.is_height, 4 /*file_pixel_size*/, io, handle, TARGA_LOAD_RGB888 & flags);
+					}
+					break;
+
+					case TGA_RLERGB: { //(32 bit)
+						loadRLE<32>(dib, header.is_width, header.is_height, io, handle, eof, TARGA_LOAD_RGB888 & flags);
+					}
+					break;
+
+					default :
+						FreeImage_Unload(dib);
+						return NULL;
+				}
+			}
+			break; // header.is_pixel_depth == 32
+
+		} // switch(header.is_pixel_depth)
+
+		if (flipvert) {
+			FreeImage_FlipVertical(dib);
+		}
+
+		if (fliphoriz) {
+			FreeImage_FlipHorizontal(dib);
+		}
+
+		return dib;
+
+	} catch (const char *message) {
+		if (dib) {
+			FreeImage_Unload(dib);
+		}
+
+		FreeImage_OutputMessageProc(s_format_id, message);
+
+		return NULL;
+	}
+}
+
+// --------------------------------------------------------------------------
+
+static BOOL 
+hasValidThumbnail(FIBITMAP* dib) {
+	FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
+	
+	return thumbnail 
+		&& SupportsExportType(FreeImage_GetImageType(thumbnail)) 
+		&& SupportsExportDepth(FreeImage_GetBPP(thumbnail))
+		// Requirements according to the specification:
+		&& FreeImage_GetBPP(thumbnail) == FreeImage_GetBPP(dib)
+		&& FreeImage_GetImageType(thumbnail) == FreeImage_GetImageType(dib)
+		&& FreeImage_GetWidth(thumbnail) <= 255
+		&& FreeImage_GetHeight(thumbnail) <= 255;
+}
+
+/**
+Writes the ready RLE packet to buffer
+*/
+static inline void 
+flushPacket(BYTE*& dest, unsigned pixel_size, BYTE* packet_begin, BYTE*& packet, BYTE& packet_count, BOOL& has_rle) {
+	if (packet_count) {
+		const BYTE type_bit = has_rle ? 0x80 : 0x0;
+		const BYTE write_count = has_rle ? 1 : packet_count;
+
+		// build packet header: zero-based count + type bit
+		assert(packet_count >= 1);
+		BYTE rle = packet_count - 1;
+		rle |= type_bit;
+
+		// write packet header
+		*dest = rle;
+		++dest;
+
+		// write packet data
+		memcpy(dest, packet_begin, write_count * pixel_size);
+		dest += write_count * pixel_size;
+
+		// reset state
+		packet_count = 0;
+		packet = packet_begin;
+		has_rle = FALSE;
+	}
+}
+
+
+static inline void 
+writeToPacket(BYTE* packet, BYTE* pixel, unsigned pixel_size) {
+	// Take care of channel and byte order here, because packet will be flushed straight to the file
+	switch (pixel_size) {
+		case 1:
+			*packet = *pixel;
+			break;
+
+		case 2: {
+			WORD val(*(WORD*)pixel);
+#ifdef FREEIMAGE_BIGENDIAN
+			SwapShort(&val);
+#endif
+			*(WORD*)packet = val;
+		}
+		break;
+
+		case 3: {
+			packet[0] = pixel[FI_RGBA_BLUE];
+			packet[1] = pixel[FI_RGBA_GREEN];
+			packet[2] = pixel[FI_RGBA_RED];
+		}
+		break;
+
+		case 4: {
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+			*(reinterpret_cast<unsigned*>(packet)) = *(reinterpret_cast<unsigned*> (pixel));
+#else 
+			packet[0] = pixel[FI_RGBA_BLUE];
+			packet[1] = pixel[FI_RGBA_GREEN];
+			packet[2] = pixel[FI_RGBA_RED];
+			packet[3] = pixel[FI_RGBA_ALPHA];
+#endif
+		}
+		break;
+
+		default:
+			assert(FALSE);
+	}
+}
+
+static inline BOOL 
+isEqualPixel(BYTE* lhs, BYTE* rhs, unsigned pixel_size) {
+	switch (pixel_size) {
+		case 1:
+			return *lhs == *rhs;
+
+		case 2:
+			return *(WORD*)lhs == *(WORD*)rhs;
+
+		case 3:
+			return *(WORD*)lhs == *(WORD*)rhs && lhs[2] == rhs[2];
+
+		case 4:
+			return *(unsigned*)lhs == *(unsigned*)rhs;
+
+		default:
+			assert(FALSE);
+			return FALSE;
+	}
+}
+
+static void 
+saveRLE(FIBITMAP* dib, FreeImageIO* io, fi_handle handle) {
+	// Image is compressed line by line, packets don't span multiple lines (TGA2.0 recommendation)
+		
+	const unsigned width = FreeImage_GetWidth(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+	const unsigned pixel_size = FreeImage_GetBPP(dib)/8;
+	const unsigned line_size = FreeImage_GetLine(dib);
+
+	const BYTE max_packet_size = 128;
+	BYTE packet_count = 0;
+	BOOL has_rle = FALSE;
+
+	// packet (compressed or not) to be written to line
+
+	BYTE* const packet_begin = (BYTE*)malloc(max_packet_size * pixel_size);
+	BYTE* packet = packet_begin;
+
+	// line to be written to disk
+	// Note: we need some extra bytes for anti-commpressed lines. The worst case is:
+	// 8 bit images were every 3th pixel is different.
+	// Rle packet becomes two pixels, but nothing is compressed: two byte pixels are transformed into byte header and byte pixel value
+	// After every rle packet there is a non-rle packet of one pixel: an extra byte for the header will be added for it
+	// In the end we gain no bytes from compression, but also must insert a byte at every 3th pixel
+
+	// add extra space for anti-commpressed lines
+	size_t extra_space = (size_t)ceil(width / 3.0);
+	BYTE* const line_begin = (BYTE*)malloc(width * pixel_size + extra_space);
+	BYTE* line = line_begin;
+
+	BYTE *current = (BYTE*)malloc(pixel_size);
+	BYTE *next    = (BYTE*)malloc(pixel_size);
+
+	for(unsigned y = 0; y < height; y++) {
+		BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+		// rewind line pointer
+		line = line_begin;
+
+		for(unsigned x = 0; x < line_size; x += pixel_size) {
+
+			AssignPixel(current, (bits + x), pixel_size);
+
+			// read next pixel from dib
+
+			if( x + 1*pixel_size < line_size) {
+				AssignPixel(next, (bits + x + 1*pixel_size), pixel_size);
+
+			} else {
+				// last pixel in line
+
+				// include current pixel and flush
+				if(!has_rle) {
+
+					writeToPacket(packet, current, pixel_size);
+					packet += pixel_size;
+
+				}
+
+				assert(packet_count < max_packet_size);
+
+				++packet_count;
+
+				flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
+
+				// start anew on next line
+				break;
+			}
+
+			if(isEqualPixel(current, next, pixel_size)) {
+
+				// has rle
+
+				if(!has_rle) {
+					// flush non rle packet
+
+					flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
+
+					// start a rle packet
+
+					has_rle = TRUE;
+
+					writeToPacket(packet, current, pixel_size);
+					packet += pixel_size;
+				}
+
+				// otherwise do nothing. We will just increase the count at the end
+
+			} else {
+
+				// no rle
+
+				if(has_rle) {
+					// flush rle packet
+
+					// include current pixel first
+					assert(packet_count < max_packet_size);
+					++packet_count;
+
+					flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
+
+					// start anew on the next pixel
+					continue;
+
+				} else {
+
+					writeToPacket(packet, current, pixel_size);
+					packet += pixel_size;
+				}
+
+			}
+
+			// increase counter on every pixel
+
+			++packet_count;
+
+			if(packet_count == max_packet_size) {
+				flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
+			}
+
+		}//for width
+
+		// write line to disk
+		io->write_proc(line_begin, 1, (unsigned)(line - line_begin), handle);
+
+	}//for height
+
+	free(line_begin);
+	free(packet_begin);
+	free(current);
+	free(next);
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	if ((dib == NULL) || (handle == NULL)) {
+		return FALSE;
+	}
+
+	RGBQUAD *palette = FreeImage_GetPalette(dib);
+	const unsigned bpp = FreeImage_GetBPP(dib);
+
+	// write the file header
+
+	TGAHEADER header;
+
+	header.id_length = 0;
+	header.cm_first_entry = 0;
+	header.is_xorigin = 0;
+	header.is_yorigin = 0;
+	header.is_width = (WORD)FreeImage_GetWidth(dib);
+	header.is_height = (WORD)FreeImage_GetHeight(dib);
+	header.is_pixel_depth = (BYTE)bpp;
+	header.is_image_descriptor = (bpp == 32 ? 8 : 0);
+
+	if (palette) {
+		header.color_map_type = 1;
+		header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLECMAP : TGA_CMAP;
+		header.cm_length = (WORD)(1 << bpp);
+
+		if (FreeImage_IsTransparent(dib)) {
+			header.cm_size = 32;
+		} else {
+			header.cm_size = 24;
+		}
+
+	} else {
+		header.color_map_type = 0;
+		header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLERGB : TGA_RGB;
+		header.cm_length = 0;
+		header.cm_size = 0;
+	}
+
+	// write the header
+
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapHeader(&header);
+#endif
+
+	io->write_proc(&header, sizeof(header), 1, handle);
+
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapHeader(&header);
+#endif
+
+	// write the palette
+
+	if (palette) {
+		if (FreeImage_IsTransparent(dib)) {
+			FILE_BGRA *bgra_pal = (FILE_BGRA*)malloc(header.cm_length * sizeof(FILE_BGRA));
+
+			// get the transparency table
+			BYTE *trns = FreeImage_GetTransparencyTable(dib);
+
+			for (unsigned i = 0; i < header.cm_length; i++) {
+				bgra_pal[i].b = palette[i].rgbBlue;
+				bgra_pal[i].g = palette[i].rgbGreen;
+				bgra_pal[i].r = palette[i].rgbRed;
+				bgra_pal[i].a = trns[i];
+			}
+
+			io->write_proc(bgra_pal, sizeof(FILE_BGRA), header.cm_length, handle);
+
+			free(bgra_pal);
+
+		} else {
+			FILE_BGR *bgr_pal = (FILE_BGR*)malloc(header.cm_length * sizeof(FILE_BGR));
+
+			for (unsigned i = 0; i < header.cm_length; i++) {
+				bgr_pal[i].b = palette[i].rgbBlue;
+				bgr_pal[i].g = palette[i].rgbGreen;
+				bgr_pal[i].r = palette[i].rgbRed;
+			}
+
+			io->write_proc(bgr_pal, sizeof(FILE_BGR), header.cm_length, handle);
+
+			free(bgr_pal);
+		}
+	}
+
+	// write the data bits 
+
+
+	if (TARGA_SAVE_RLE & flags) {
+		
+		saveRLE(dib, io, handle);
+
+	} else {
+		
+		// -- no rle compression --
+		
+		const unsigned width = header.is_width;
+		const unsigned height = header.is_height;
+		const unsigned pixel_size = bpp/8;
+
+		BYTE *line, *const line_begin = (BYTE*)malloc(width * pixel_size);
+		BYTE *line_source = line_begin;
+
+		for (unsigned y = 0; y < height; y++) {
+			BYTE *scanline = FreeImage_GetScanLine(dib, y);
+
+			// rewind the line pointer
+			line = line_begin;
+
+			switch (bpp) {
+				case 8: {
+					// don't copy line, read straight from dib
+					line_source = scanline;
+				}
+				break;
+
+				case 16: {
+					for (unsigned x = 0; x < width; x++) {
+						WORD pixel = *(((WORD *)scanline) + x);
+						
+#ifdef FREEIMAGE_BIGENDIAN
+						SwapShort(&pixel);
+#endif
+						*(WORD*)line = pixel;
+
+						line += pixel_size;
+					}
+				}
+				break;
+
+				case 24: {
+					
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+						line_source = scanline;
+#else 
+					for (unsigned x = 0; x < width; ++x) {
+						RGBTRIPLE* trip = ((RGBTRIPLE *)scanline) + x;
+						line[0] = trip->rgbtBlue;
+						line[1] = trip->rgbtGreen;
+						line[2] = trip->rgbtRed;
+
+						line += pixel_size;
+					}
+#endif
+				}
+				break;
+
+				case 32: {
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+					line_source = scanline;
+#else 
+					for (unsigned x = 0; x < width; ++x) {
+						RGBQUAD* quad = ((RGBQUAD *)scanline) + x;
+						line[0] = quad->rgbBlue;
+						line[1] = quad->rgbGreen;
+						line[2] = quad->rgbRed;
+						line[3] = quad->rgbReserved;
+						
+						line += pixel_size;
+					}
+#endif
+				}
+				break;
+
+			}//switch(bpp)
+
+			// write line to disk
+
+			io->write_proc(line_source, pixel_size, width, handle);
+
+		}//for height
+
+		free(line_begin);
+	}
+
+	
+	long extension_offset = 0 ;
+	if(hasValidThumbnail(dib)) {
+		// write extension area
+		
+		extension_offset = io->tell_proc(handle);
+		
+		TGAEXTENSIONAREA ex;
+		memset(&ex, 0, sizeof(ex));
+		
+		assert(sizeof(ex) == 495);
+		ex.extension_size = sizeof(ex);
+		ex.postage_stamp_offset = extension_offset + ex.extension_size + 0 /*< no Scan Line Table*/;
+		ex.attributes_type = FreeImage_GetBPP(dib) == 32 ? 3 /*< useful Alpha channel data*/ : 0 /*< no Alpha data*/;
+		
+#ifdef FREEIMAGE_BIGENDIAN
+		SwapExtensionArea(&ex);
+#endif
+
+		io->write_proc(&ex, sizeof(ex), 1, handle); 
+		
+		// (no Scan Line Table)
+		
+		// write thumbnail
+		
+		io->seek_proc(handle, ex.postage_stamp_offset, SEEK_SET);
+		
+		FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
+		BYTE width = (BYTE)FreeImage_GetWidth(thumbnail);
+		BYTE height = (BYTE)FreeImage_GetHeight(thumbnail);
+		
+		io->write_proc(&width, 1, 1, handle); 
+		io->write_proc(&height, 1, 1, handle); 
+		
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+		SwapRedBlue32(dib); 
+#endif
+		
+#ifdef FREEIMAGE_BIGENDIAN
+		swapShortPixels(dib); 
+#endif
+		
+		const unsigned line_size = FreeImage_GetLine(thumbnail);
+
+		for (BYTE h = 0; h < height; ++h) {
+			BYTE* src_line = FreeImage_GetScanLine(thumbnail, height - 1 - h);
+			io->write_proc(src_line, 1, line_size, handle); 
+		}
+	}
+	
+	// (no Color Correction Table)
+	
+	// write the footer
+	
+	TGAFOOTER footer;
+	footer.extension_offset = extension_offset;
+	footer.developer_offset = 0;
+	strcpy(footer.signature, "TRUEVISION-XFILE.");
+	
+
+#ifdef FREEIMAGE_BIGENDIAN
+	SwapFooter(&footer);
+#endif
+
+	io->write_proc(&footer, sizeof(footer), 1, handle);
+
+	return TRUE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitTARGA(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/files/Source/FreeImage/PluginTIFF.cpp b/files/Source/FreeImage/PluginTIFF.cpp
new file mode 100644
index 0000000..e042d3c
--- /dev/null
+++ b/files/Source/FreeImage/PluginTIFF.cpp
@@ -0,0 +1,2640 @@
+// ==========================================================
+// TIFF Loader and Writer
+//
+// Design and implementation by 
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Luca Piergentili (l.pierge@terra.es)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - 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
+
+#ifdef unix
+#undef unix
+#endif
+#ifdef __unix
+#undef __unix
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "third_party/tiff/libtiff/tiffiop.h"
+#include "third_party/ilmbase/Half/half.h"
+#include "../Metadata/FreeImageTag.h"
+
+#include "FreeImageIO.h"
+#include "PSDParser.h"
+
+// --------------------------------------------------------------------------
+// GeoTIFF profile (see XTIFF.cpp)
+// --------------------------------------------------------------------------
+void XTIFFInitialize();
+BOOL tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib);
+BOOL tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib);
+
+// --------------------------------------------------------------------------
+// TIFF Exif profile (see XTIFF.cpp)
+// ----------------------------------------------------------
+BOOL tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib);
+BOOL tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib);
+
+// --------------------------------------------------------------------------
+//   LogLuv conversion functions interface (see TIFFLogLuv.cpp)
+// --------------------------------------------------------------------------
+void tiff_ConvertLineXYZToRGB(BYTE *target, BYTE *source, double stonits, int width_in_pixels);
+void tiff_ConvertLineRGBToXYZ(BYTE *target, BYTE *source, int width_in_pixels);
+
+// ----------------------------------------------------------
+
+/** Supported loading methods */
+typedef enum {
+	LoadAsRBGA			= 0, 
+	LoadAsCMYK			= 1, 
+	LoadAs8BitTrns		= 2, 
+	LoadAsGenericStrip	= 3, 
+	LoadAsTiled			= 4,
+	LoadAsLogLuv		= 5,
+	LoadAsHalfFloat		= 6
+} TIFFLoadMethod;
+
+// ----------------------------------------------------------
+//   local prototypes
+// ----------------------------------------------------------
+
+static tmsize_t _tiffReadProc(thandle_t handle, void* buf, tmsize_t size);
+static tmsize_t _tiffWriteProc(thandle_t handle, void* buf, tmsize_t size);
+static toff_t _tiffSeekProc(thandle_t handle, toff_t off, int whence);
+static int _tiffCloseProc(thandle_t fd);
+static int _tiffMapProc(thandle_t fd, void** pbase, toff_t* psize);
+static void _tiffUnmapProc(thandle_t fd, void* base, toff_t size);
+
+static uint16 CheckColormap(int n, uint16* r, uint16* g, uint16* b);
+static uint16 GetPhotometric(FIBITMAP *dib);
+
+static void ReadResolution(TIFF *tiff, FIBITMAP *dib);
+static void WriteResolution(TIFF *tiff, FIBITMAP *dib);
+
+static void ReadPalette(TIFF *tiff, uint16 photometric, uint16 bitspersample, FIBITMAP *dib);
+
+static FIBITMAP* CreateImageType(BOOL header_only, FREE_IMAGE_TYPE fit, int width, int height, uint16 bitspersample, uint16 samplesperpixel);
+static FREE_IMAGE_TYPE ReadImageType(TIFF *tiff, uint16 bitspersample, uint16 samplesperpixel);
+static void WriteImageType(TIFF *tiff, FREE_IMAGE_TYPE fit);
+
+static void WriteCompression(TIFF *tiff, uint16 bitspersample, uint16 samplesperpixel, uint16 photometric, int flags);
+
+static BOOL tiff_read_iptc_profile(TIFF *tiff, FIBITMAP *dib);
+static BOOL tiff_read_xmp_profile(TIFF *tiff, FIBITMAP *dib);
+static BOOL tiff_read_exif_profile(TIFF *tiff, FIBITMAP *dib);
+static void ReadMetadata(TIFF *tiff, FIBITMAP *dib);
+
+static BOOL tiff_write_iptc_profile(TIFF *tiff, FIBITMAP *dib);
+static BOOL tiff_write_xmp_profile(TIFF *tiff, FIBITMAP *dib);
+static void WriteMetadata(TIFF *tiff, FIBITMAP *dib);
+
+static TIFFLoadMethod FindLoadMethod(TIFF *tif, uint16 photometric, uint16 bitspersample, uint16 samplesperpixel, FREE_IMAGE_TYPE image_type, int flags);
+
+static void ReadThumbnail(FreeImageIO *io, fi_handle handle, void *data, TIFF *tiff, FIBITMAP *dib);
+
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+typedef struct {
+    FreeImageIO *io;
+	fi_handle handle;
+	TIFF *tif;
+} fi_TIFFIO;
+
+// ----------------------------------------------------------
+//   libtiff interface 
+// ----------------------------------------------------------
+
+static tmsize_t 
+_tiffReadProc(thandle_t handle, void *buf, tmsize_t size) {
+	fi_TIFFIO *fio = (fi_TIFFIO*)handle;
+	return fio->io->read_proc(buf, (unsigned)size, 1, fio->handle) * size;
+}
+
+static tmsize_t
+_tiffWriteProc(thandle_t handle, void *buf, tmsize_t size) {
+	fi_TIFFIO *fio = (fi_TIFFIO*)handle;
+	return fio->io->write_proc(buf, (unsigned)size, 1, fio->handle) * size;
+}
+
+static toff_t
+_tiffSeekProc(thandle_t handle, toff_t off, int whence) {
+	fi_TIFFIO *fio = (fi_TIFFIO*)handle;
+	fio->io->seek_proc(fio->handle, (long)off, whence);
+	return fio->io->tell_proc(fio->handle);
+}
+
+static int
+_tiffCloseProc(thandle_t fd) {
+	return 0;
+}
+
+#include <sys/stat.h>
+
+static toff_t
+_tiffSizeProc(thandle_t handle) {
+    fi_TIFFIO *fio = (fi_TIFFIO*)handle;
+    long currPos = fio->io->tell_proc(fio->handle);
+    fio->io->seek_proc(fio->handle, 0, SEEK_END);
+    long fileSize = fio->io->tell_proc(fio->handle);
+    fio->io->seek_proc(fio->handle, currPos, SEEK_SET);
+    return fileSize;
+}
+
+static int
+_tiffMapProc(thandle_t, void** base, toff_t* size) {
+	return 0;
+}
+
+static void
+_tiffUnmapProc(thandle_t, void* base, toff_t size) {
+}
+
+/**
+Open a TIFF file descriptor for reading or writing
+@param handle File handle
+@param name Name of the file handle
+@param mode Specifies if the file is to be opened for reading ("r") or writing ("w")
+*/
+TIFF *
+TIFFFdOpen(thandle_t handle, const char *name, const char *mode) {
+	TIFF *tif;
+	
+	// Open the file; the callback will set everything up
+	tif = TIFFClientOpen(name, mode, handle,
+	    _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc,
+	    _tiffSizeProc, _tiffMapProc, _tiffUnmapProc);
+
+	return tif;
+}
+
+// Ifdef around this code so that we can build for SketchUp on Linux.
+// FreeImage for SketchUp on Linux uses the third_party/tiff library
+// instead of the FreeImage tiff library to avoid conflicts with
+// escher code.  In this case these methods are already defined and
+// we don't want to define them again.
+#ifndef USE_THIRD_PARTY_TIFF
+
+/**
+Open a TIFF file for reading or writing
+@param name
+@param mode
+*/
+TIFF*
+TIFFOpen(const char* name, const char* mode) {
+	return 0;
+}
+
+// ----------------------------------------------------------
+//   TIFF library FreeImage-specific routines.
+// ----------------------------------------------------------
+
+void*
+_TIFFmalloc(tmsize_t s) {
+	return malloc(s);
+}
+
+void
+_TIFFfree(void *p) {
+	free(p);
+}
+
+void*
+_TIFFrealloc(void* p, tmsize_t s) {
+	return realloc(p, s);
+}
+
+void
+_TIFFmemset(void* p, int v, tmsize_t c) {
+	memset(p, v, (size_t) c);
+}
+
+void
+_TIFFmemcpy(void* d, const void* s, tmsize_t c) {
+	memcpy(d, s, (size_t) c);
+}
+
+int
+_TIFFmemcmp(const void* p1, const void* p2, tmsize_t c) {
+	return (memcmp(p1, p2, (size_t) c));
+}
+
+// ----------------------------------------------------------
+//   in FreeImage warnings and errors are disabled
+// ----------------------------------------------------------
+
+static void
+msdosWarningHandler(const char* module, const char* fmt, va_list ap) {
+}
+
+TIFFErrorHandler _TIFFwarningHandler = msdosWarningHandler;
+
+static void
+msdosErrorHandler(const char* module, const char* fmt, va_list ap) {
+	
+	// use this for diagnostic only (do not use otherwise, even in DEBUG mode)
+	/*
+	if (module != NULL) {
+		char msg[1024];
+		vsprintf(msg, fmt, ap);
+		FreeImage_OutputMessageProc(s_format_id, "%s: %s", module, msg);
+	}
+	*/
+}
+
+TIFFErrorHandler _TIFFerrorHandler = msdosErrorHandler;
+
+#endif
+
+// ----------------------------------------------------------
+
+#define CVT(x)      (((x) * 255L) / ((1L<<16)-1))
+#define	SCALE(x)	(((x)*((1L<<16)-1))/255)
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static uint16
+CheckColormap(int n, uint16* r, uint16* g, uint16* b) {
+    while (n-- > 0) {
+        if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256) {
+			return 16;
+		}
+	}
+
+    return 8;
+}
+
+/**
+Get the TIFFTAG_PHOTOMETRIC value from the dib
+*/
+static uint16
+GetPhotometric(FIBITMAP *dib) {
+	FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+	switch(color_type) {
+		case FIC_MINISWHITE:	// min value is white
+			return PHOTOMETRIC_MINISWHITE;
+		case FIC_MINISBLACK:	// min value is black
+			return PHOTOMETRIC_MINISBLACK;
+		case FIC_PALETTE:		// color map indexed
+			return PHOTOMETRIC_PALETTE;
+		case FIC_RGB:			// RGB color model
+		case FIC_RGBALPHA:		// RGB color model with alpha channel
+			return PHOTOMETRIC_RGB;
+		case FIC_CMYK:			// CMYK color model
+			return PHOTOMETRIC_RGB;	// default to RGB unless the save flag is set to TIFF_CMYK
+		default:
+			return PHOTOMETRIC_MINISBLACK;
+	}
+}
+
+/**
+Get the resolution from the TIFF and fill the dib with universal units
+*/
+static void 
+ReadResolution(TIFF *tiff, FIBITMAP *dib) {
+	float fResX = 300.0;
+	float fResY = 300.0;
+	uint16 resUnit = RESUNIT_INCH;
+
+	TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resUnit);
+	TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &fResX);
+	TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &fResY);
+	
+	// If we don't have a valid resolution unit and valid resolution is specified then assume inch
+	if (resUnit == RESUNIT_NONE && fResX > 0.0 && fResY > 0.0) {
+		resUnit = RESUNIT_INCH;
+	}
+	if (resUnit == RESUNIT_INCH) {
+		FreeImage_SetDotsPerMeterX(dib, (unsigned) (fResX/0.0254000 + 0.5));
+		FreeImage_SetDotsPerMeterY(dib, (unsigned) (fResY/0.0254000 + 0.5));
+	} else if(resUnit == RESUNIT_CENTIMETER) {
+		FreeImage_SetDotsPerMeterX(dib, (unsigned) (fResX*100.0 + 0.5));
+		FreeImage_SetDotsPerMeterY(dib, (unsigned) (fResY*100.0 + 0.5));
+	}
+}
+
+/**
+Set the resolution to the TIFF using english units
+*/
+static void 
+WriteResolution(TIFF *tiff, FIBITMAP *dib) {
+	double res;
+
+	TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+
+	res = (unsigned long) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib));
+	TIFFSetField(tiff, TIFFTAG_XRESOLUTION, res);
+
+	res = (unsigned long) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib));
+	TIFFSetField(tiff, TIFFTAG_YRESOLUTION, res);
+}
+
+/**
+Fill the dib palette according to the TIFF photometric
+*/
+static void 
+ReadPalette(TIFF *tiff, uint16 photometric, uint16 bitspersample, FIBITMAP *dib) {
+	RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+	switch(photometric) {
+		case PHOTOMETRIC_MINISBLACK:	// bitmap and greyscale image types
+		case PHOTOMETRIC_MINISWHITE:
+			// Monochrome image
+
+			if (bitspersample == 1) {
+				if (photometric == PHOTOMETRIC_MINISWHITE) {
+					pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255;
+					pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 0;
+				} else {
+					pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+					pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+				}
+
+			} else if ((bitspersample == 4) ||(bitspersample == 8)) {
+				// need to build the scale for greyscale images
+				int ncolors = FreeImage_GetColorsUsed(dib);
+
+				if (photometric == PHOTOMETRIC_MINISBLACK) {
+					for (int i = 0; i < ncolors; i++) {
+						pal[i].rgbRed	=
+						pal[i].rgbGreen =
+						pal[i].rgbBlue	= (BYTE)(i*(255/(ncolors-1)));
+					}
+				} else {
+					for (int i = 0; i < ncolors; i++) {
+						pal[i].rgbRed	=
+						pal[i].rgbGreen =
+						pal[i].rgbBlue	= (BYTE)(255-i*(255/(ncolors-1)));
+					}
+				}
+			}
+
+			break;
+
+		case PHOTOMETRIC_PALETTE:	// color map indexed
+			uint16 *red;
+			uint16 *green;
+			uint16 *blue;
+			
+			TIFFGetField(tiff, TIFFTAG_COLORMAP, &red, &green, &blue); 
+
+			// load the palette in the DIB
+
+			if (CheckColormap(1<<bitspersample, red, green, blue) == 16) {
+				for (int i = (1 << bitspersample) - 1; i >= 0; i--) {
+					pal[i].rgbRed =(BYTE) CVT(red[i]);
+					pal[i].rgbGreen = (BYTE) CVT(green[i]);
+					pal[i].rgbBlue = (BYTE) CVT(blue[i]);           
+				}
+			} else {
+				for (int i = (1 << bitspersample) - 1; i >= 0; i--) {
+					pal[i].rgbRed = (BYTE) red[i];
+					pal[i].rgbGreen = (BYTE) green[i];
+					pal[i].rgbBlue = (BYTE) blue[i];        
+				}
+			}
+
+			break;
+	}
+}
+
+/** 
+Allocate a FIBITMAP
+@param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP
+@param fit Image type
+@param width Image width in pixels
+@param height Image height in pixels
+@param bitspersample # bits per sample
+@param samplesperpixel # samples per pixel
+@return Returns the allocated image if successful, returns NULL otherwise
+*/
+static FIBITMAP* 
+CreateImageType(BOOL header_only, FREE_IMAGE_TYPE fit, int width, int height, uint16 bitspersample, uint16 samplesperpixel) {
+	FIBITMAP *dib = NULL;
+
+	if((width < 0) || (height < 0)) {
+		// check for malicious images
+		return NULL;
+	}
+
+	int bpp = bitspersample * samplesperpixel;
+
+	if(fit == FIT_BITMAP) {
+		// standard bitmap type 
+
+		if(bpp == 16) {
+			
+			if((samplesperpixel == 2) && (bitspersample == 8)) {
+				// 8-bit indexed + 8-bit alpha channel -> convert to 8-bit transparent
+				dib = FreeImage_AllocateHeader(header_only, width, height, 8);
+			} else {
+				// 16-bit RGB -> expect it to be 565
+				dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI16_565_RED_MASK, FI16_565_GREEN_MASK, FI16_565_BLUE_MASK);
+			}
+			
+		}
+		else {
+
+			dib = FreeImage_AllocateHeader(header_only, width, height, MIN(bpp, 32), FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		}
+
+
+	} else {
+		// other bitmap types
+		
+		dib = FreeImage_AllocateHeaderT(header_only, fit, width, height, bpp);
+	}
+
+	return dib;
+}
+
+/** 
+Read the TIFFTAG_SAMPLEFORMAT tag and convert to FREE_IMAGE_TYPE
+@param tiff LibTIFF TIFF Handle
+@param bitspersample # bit per sample
+@param samplesperpixel # samples per pixel
+@return Returns the image type as a FREE_IMAGE_TYPE value
+*/
+static FREE_IMAGE_TYPE 
+ReadImageType(TIFF *tiff, uint16 bitspersample, uint16 samplesperpixel) {
+	uint16 sampleformat = 0;
+	FREE_IMAGE_TYPE fit = FIT_BITMAP ; 
+
+	uint16 bpp = bitspersample * samplesperpixel;
+
+	// try the sampleformat tag
+    if(TIFFGetField(tiff, TIFFTAG_SAMPLEFORMAT, &sampleformat)) {
+
+        switch (sampleformat) {
+			case SAMPLEFORMAT_UINT:
+				switch (bpp) {
+					case 1:
+					case 4:
+					case 8:
+					case 24:
+						fit = FIT_BITMAP;
+						break;
+					case 16:
+						// 8-bit + alpha or 16-bit greyscale
+						if(samplesperpixel == 2) {
+							fit = FIT_BITMAP;
+						} else {
+							fit = FIT_UINT16;
+						}
+						break;
+					case 32:
+						if(samplesperpixel == 4) {
+							fit = FIT_BITMAP;
+						} else {
+							fit = FIT_UINT32;
+						}
+						break;
+					case 48:
+						if(samplesperpixel == 3) {
+							fit = FIT_RGB16;
+						}
+						break;
+					case 64:
+						if(samplesperpixel == 4) {
+							fit = FIT_RGBA16;
+						}
+						break;
+				}
+				break;
+
+			case SAMPLEFORMAT_INT:
+				switch (bpp) {
+					case 16:
+						if(samplesperpixel == 3) {
+							fit = FIT_BITMAP;
+						} else {
+							fit = FIT_INT16;
+						}
+						break;
+					case 32:
+						fit = FIT_INT32;
+						break;
+				}
+				break;
+
+			case SAMPLEFORMAT_IEEEFP:
+				switch (bpp) {
+					case 32:
+						fit = FIT_FLOAT;
+						break;
+					case 48:
+						// 3 x half float => convert to RGBF
+						if((samplesperpixel == 3) && (bitspersample == 16)) {
+							fit = FIT_RGBF;
+						}
+						break;
+					case 64:
+						if(samplesperpixel == 2) {
+							fit = FIT_FLOAT;
+						} else {
+							fit = FIT_DOUBLE;
+						}
+						break;
+					case 96:
+						fit = FIT_RGBF;
+						break;
+					default:
+						if(bpp >= 128) {
+							fit = FIT_RGBAF;
+						}
+					break;
+				}
+				break;
+			case SAMPLEFORMAT_COMPLEXIEEEFP:
+				switch (bpp) {
+					case 64:
+						break;
+					case 128:
+						fit = FIT_COMPLEX;
+						break;
+				}
+				break;
+
+			}
+    }
+	// no sampleformat tag : assume SAMPLEFORMAT_UINT
+	else {
+		if(samplesperpixel == 1) {
+			switch (bpp) {
+				case 16:
+					fit = FIT_UINT16;
+					break;
+					
+				case 32:
+					fit = FIT_UINT32;
+					break;
+			}
+		}
+		else if(samplesperpixel == 3) {
+			if(bpp == 48) fit = FIT_RGB16;
+		}
+		else if(samplesperpixel >= 4) { 
+			if(bitspersample == 16) {
+				fit = FIT_RGBA16;
+			}
+		}
+
+	}
+
+    return fit;
+}
+
+/** 
+Convert FREE_IMAGE_TYPE and write TIFFTAG_SAMPLEFORMAT
+@param tiff LibTIFF TIFF Handle
+@param fit Image type as a FREE_IMAGE_TYPE value
+*/
+static void 
+WriteImageType(TIFF *tiff, FREE_IMAGE_TYPE fit) {
+	switch(fit) {
+		case FIT_BITMAP:	// standard image: 1-, 4-, 8-, 16-, 24-, 32-bit
+		case FIT_UINT16:	// array of unsigned short	: unsigned 16-bit
+		case FIT_UINT32:	// array of unsigned long	: unsigned 32-bit
+		case FIT_RGB16:		// 48-bit RGB image			: 3 x 16-bit
+		case FIT_RGBA16:	// 64-bit RGBA image		: 4 x 16-bit
+			TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
+			break;
+
+		case FIT_INT16:		// array of short	: signed 16-bit
+		case FIT_INT32:		// array of long	: signed 32-bit
+			TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT);
+			break;
+
+		case FIT_FLOAT:		// array of float	: 32-bit
+		case FIT_DOUBLE:	// array of double	: 64-bit
+		case FIT_RGBF:		// 96-bit RGB float image	: 3 x 32-bit IEEE floating point
+		case FIT_RGBAF:		// 128-bit RGBA float image	: 4 x 32-bit IEEE floating point
+			TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
+			break;
+
+		case FIT_COMPLEX:	// array of COMPLEX : 2 x 64-bit
+			TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_COMPLEXIEEEFP);
+			break;
+	}
+}
+
+/**
+Select the compression algorithm
+@param tiff LibTIFF TIFF Handle
+@param 
+*/
+static void 
+WriteCompression(TIFF *tiff, uint16 bitspersample, uint16 samplesperpixel, uint16 photometric, int flags) {
+	uint16 compression;
+	uint16 bitsperpixel = bitspersample * samplesperpixel;
+
+	if(photometric == PHOTOMETRIC_LOGLUV) {
+		compression = COMPRESSION_SGILOG;
+	} else if ((flags & TIFF_PACKBITS) == TIFF_PACKBITS) {
+		compression = COMPRESSION_PACKBITS;
+	} else if ((flags & TIFF_DEFLATE) == TIFF_DEFLATE) {
+		compression = COMPRESSION_DEFLATE;
+	} else if ((flags & TIFF_ADOBE_DEFLATE) == TIFF_ADOBE_DEFLATE) {
+		compression = COMPRESSION_ADOBE_DEFLATE;
+	} else if ((flags & TIFF_NONE) == TIFF_NONE) {
+		compression = COMPRESSION_NONE;
+	} else if ((bitsperpixel == 1) && ((flags & TIFF_CCITTFAX3) == TIFF_CCITTFAX3)) {
+		compression = COMPRESSION_CCITTFAX3;
+	} else if ((bitsperpixel == 1) && ((flags & TIFF_CCITTFAX4) == TIFF_CCITTFAX4)) {
+		compression = COMPRESSION_CCITTFAX4;
+	} else if ((flags & TIFF_LZW) == TIFF_LZW) {
+		compression = COMPRESSION_LZW;
+	} else if ((flags & TIFF_JPEG) == TIFF_JPEG) {
+		if(((bitsperpixel == 8) && (photometric != PHOTOMETRIC_PALETTE)) || (bitsperpixel == 24)) {
+			compression = COMPRESSION_JPEG;
+			// RowsPerStrip must be multiple of 8 for JPEG
+			uint32 rowsperstrip = (uint32) -1;
+			rowsperstrip = TIFFDefaultStripSize(tiff, rowsperstrip);
+            rowsperstrip = rowsperstrip + (8 - (rowsperstrip % 8));
+			// overwrite previous RowsPerStrip
+			TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
+		} else {
+			// default to LZW
+			compression = COMPRESSION_LZW;
+		}
+	}
+	else {
+		// default compression scheme
+
+		switch(bitsperpixel) {
+			case 1:
+				compression = COMPRESSION_CCITTFAX4;
+				break;
+
+			case 4:
+			case 8:
+			case 16:
+			case 24:
+			case 32:
+				compression = COMPRESSION_LZW;
+				break;
+			case 48:
+			case 64:
+			case 96:
+			case 128:
+				compression = COMPRESSION_LZW;
+				break;
+
+			default :
+				compression = COMPRESSION_NONE;
+				break;
+		}
+	}
+
+	TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression);
+
+	if(compression == COMPRESSION_LZW) {
+		// This option is only meaningful with LZW compression: a predictor value of 2 
+		// causes each scanline of the output image to undergo horizontal differencing 
+		// before it is encoded; a value of 1 forces each scanline to be encoded without differencing.
+
+		// Found on LibTIFF mailing list : 
+		// LZW without differencing works well for 1-bit images, 4-bit grayscale images, 
+		// and many palette-color images. But natural 24-bit color images and some 8-bit 
+		// grayscale images do much better with differencing.
+
+		if((bitspersample == 8) || (bitspersample == 16)) {
+			if ((bitsperpixel >= 8) && (photometric != PHOTOMETRIC_PALETTE)) {
+				TIFFSetField(tiff, TIFFTAG_PREDICTOR, 2);
+			} else {
+				TIFFSetField(tiff, TIFFTAG_PREDICTOR, 1);
+			}
+		} else {
+			TIFFSetField(tiff, TIFFTAG_PREDICTOR, 1);
+		}
+	}
+	else if((compression == COMPRESSION_CCITTFAX3) || (compression == COMPRESSION_CCITTFAX4)) {
+		uint32 imageLength = 0;
+		TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &imageLength);
+		// overwrite previous RowsPerStrip
+		TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, imageLength);
+
+		if(compression == COMPRESSION_CCITTFAX3) {
+			// try to be compliant with the TIFF Class F specification
+			// that documents the TIFF tags specific to FAX applications
+			// see http://palimpsest.stanford.edu/bytopic/imaging/std/tiff-f.html
+			uint32 group3options = GROUP3OPT_2DENCODING | GROUP3OPT_FILLBITS;	
+			TIFFSetField(tiff, TIFFTAG_GROUP3OPTIONS, group3options);	// 2d-encoded, has aligned EOL
+			TIFFSetField(tiff, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);	// lsb-to-msb fillorder
+		}
+	}
+}
+
+// ==========================================================
+// TIFF metadata routines
+// ==========================================================
+
+/**
+	Read the TIFFTAG_RICHTIFFIPTC tag (IPTC/NAA or Adobe Photoshop profile)
+*/
+static BOOL 
+tiff_read_iptc_profile(TIFF *tiff, FIBITMAP *dib) {
+	BYTE *profile = NULL;
+	uint32 profile_size = 0;
+
+    if(TIFFGetField(tiff,TIFFTAG_RICHTIFFIPTC, &profile_size, &profile) == 1) {
+		if (TIFFIsByteSwapped(tiff) != 0) {
+			TIFFSwabArrayOfLong((uint32 *) profile, (unsigned long)profile_size);
+		}
+
+		return read_iptc_profile(dib, profile, 4 * profile_size);
+	}
+
+	return FALSE;
+}
+
+/**
+	Read the TIFFTAG_XMLPACKET tag (XMP profile)
+	@param dib Input FIBITMAP
+	@param tiff LibTIFF TIFF handle
+	@return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL  
+tiff_read_xmp_profile(TIFF *tiff, FIBITMAP *dib) {
+	BYTE *profile = NULL;
+	uint32 profile_size = 0;
+
+	if (TIFFGetField(tiff, TIFFTAG_XMLPACKET, &profile_size, &profile) == 1) {
+		// create a tag
+		FITAG *tag = FreeImage_CreateTag();
+		if(!tag) return FALSE;
+
+		FreeImage_SetTagID(tag, TIFFTAG_XMLPACKET);	// 700
+		FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
+		FreeImage_SetTagLength(tag, profile_size);
+		FreeImage_SetTagCount(tag, profile_size);
+		FreeImage_SetTagType(tag, FIDT_ASCII);
+		FreeImage_SetTagValue(tag, profile);
+
+		// store the tag
+		FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
+
+		// destroy the tag
+		FreeImage_DeleteTag(tag);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+/**
+	Read the Exif profile embedded in a TIFF
+	@param dib Input FIBITMAP
+	@param tiff LibTIFF TIFF handle
+	@return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL 
+tiff_read_exif_profile(TIFF *tiff, FIBITMAP *dib) {
+	BOOL bResult = FALSE;
+    toff_t exif_offset = 0;
+
+	// read EXIF-TIFF tags
+	bResult = tiff_read_exif_tags(tiff, TagLib::EXIF_MAIN, dib);
+
+	// get the IFD offset
+	if(TIFFGetField(tiff, TIFFTAG_EXIFIFD, &exif_offset)) {
+
+		// read EXIF tags
+		if(!TIFFReadEXIFDirectory(tiff, exif_offset)) {
+			return FALSE;
+		}
+
+		// read all known exif tags
+		bResult = tiff_read_exif_tags(tiff, TagLib::EXIF_EXIF, dib);
+	}
+
+	return bResult;
+}
+
+/**
+Read TIFF special profiles
+*/
+static void 
+ReadMetadata(TIFF *tiff, FIBITMAP *dib) {
+
+	// IPTC/NAA
+	tiff_read_iptc_profile(tiff, dib);
+
+	// Adobe XMP
+	tiff_read_xmp_profile(tiff, dib);
+
+	// GeoTIFF
+	tiff_read_geotiff_profile(tiff, dib);
+
+	// Exif-TIFF
+	tiff_read_exif_profile(tiff, dib);
+}
+
+// ----------------------------------------------------------
+
+/**
+	Write the TIFFTAG_RICHTIFFIPTC tag (IPTC/NAA or Adobe Photoshop profile)
+*/
+static BOOL 
+tiff_write_iptc_profile(TIFF *tiff, FIBITMAP *dib) {
+	if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) {
+		BYTE *profile = NULL;
+		uint32 profile_size = 0;
+		// create a binary profile
+		if(write_iptc_profile(dib, &profile, &profile_size)) {
+			uint32 iptc_size = profile_size;
+			iptc_size += (4-(iptc_size & 0x03)); // Round up for long word alignment
+			BYTE *iptc_profile = (BYTE*)malloc(iptc_size);
+			if(!iptc_profile) {
+				free(profile);
+				return FALSE;
+			}
+			memset(iptc_profile, 0, iptc_size);
+			memcpy(iptc_profile, profile, profile_size);
+			if (TIFFIsByteSwapped(tiff)) {
+				TIFFSwabArrayOfLong((uint32 *) iptc_profile, (unsigned long)iptc_size/4);
+			}
+			// Tag is type TIFF_LONG so byte length is divided by four
+			TIFFSetField(tiff, TIFFTAG_RICHTIFFIPTC, iptc_size/4, iptc_profile);
+			// release the profile data
+			free(iptc_profile);
+			free(profile);
+
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+/**
+	Write the TIFFTAG_XMLPACKET tag (XMP profile)
+	@param dib Input FIBITMAP
+	@param tiff LibTIFF TIFF handle
+	@return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL  
+tiff_write_xmp_profile(TIFF *tiff, FIBITMAP *dib) {
+	FITAG *tag_xmp = NULL;
+	FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp);
+
+	if(tag_xmp && (NULL != FreeImage_GetTagValue(tag_xmp))) {
+		
+		TIFFSetField(tiff, TIFFTAG_XMLPACKET, (uint32)FreeImage_GetTagLength(tag_xmp), (BYTE*)FreeImage_GetTagValue(tag_xmp));
+
+		return TRUE;		
+	}
+
+	return FALSE;
+}
+
+/**
+	Write the Exif profile to TIFF
+	@param dib Input FIBITMAP
+	@param tiff LibTIFF TIFF handle
+	@return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+tiff_write_exif_profile(TIFF *tiff, FIBITMAP *dib) {
+	BOOL bResult = FALSE;
+	
+	// write EXIF_MAIN tags, EXIF_EXIF not supported yet
+	bResult = tiff_write_exif_tags(tiff, TagLib::EXIF_MAIN, dib);
+
+	return bResult;
+}
+
+/**
+Write TIFF special profiles
+*/
+static void 
+WriteMetadata(TIFF *tiff, FIBITMAP *dib) {
+	// IPTC
+	tiff_write_iptc_profile(tiff, dib);
+	
+	// Adobe XMP
+	tiff_write_xmp_profile(tiff, dib);
+	
+	// EXIF_MAIN tags
+	tiff_write_exif_profile(tiff, dib);
+	
+	// GeoTIFF tags
+	tiff_write_geotiff_profile(tiff, dib);
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "TIFF";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Tagged Image File Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "tif,tiff";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return "^[MI][MI][\\x01*][\\x01*]";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/tiff";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {	
+	BYTE tiff_id1[] = { 0x49, 0x49, 0x2A, 0x00 };	// Classic TIFF, little-endian
+	BYTE tiff_id2[] = { 0x4D, 0x4D, 0x00, 0x2A };	// Classic TIFF, big-endian
+	BYTE tiff_id3[] = { 0x49, 0x49, 0x2B, 0x00 };	// Big TIFF, little-endian
+	BYTE tiff_id4[] = { 0x4D, 0x4D, 0x00, 0x2B };	// Big TIFF, big-endian
+	BYTE signature[4] = { 0, 0, 0, 0 };
+
+	io->read_proc(signature, 1, 4, handle);
+
+	if(memcmp(tiff_id1, signature, 4) == 0)
+		return TRUE;
+	if(memcmp(tiff_id2, signature, 4) == 0)
+		return TRUE;
+	if(memcmp(tiff_id3, signature, 4) == 0)
+		return TRUE;
+	if(memcmp(tiff_id4, signature, 4) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+			(depth == 1)  ||
+			(depth == 4)  ||
+			(depth == 8)  ||
+			(depth == 24) ||
+			(depth == 32)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (
+		(type == FIT_BITMAP)  ||
+		(type == FIT_UINT16)  ||
+		(type == FIT_INT16)   ||
+		(type == FIT_UINT32)  ||
+		(type == FIT_INT32)   ||
+		(type == FIT_FLOAT)   ||
+		(type == FIT_DOUBLE)  ||
+		(type == FIT_COMPLEX) || 
+		(type == FIT_RGB16)   || 
+		(type == FIT_RGBA16)  || 
+		(type == FIT_RGBF)    ||
+		(type == FIT_RGBAF)
+	);
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+} 
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	// wrapper for TIFF I/O
+	fi_TIFFIO *fio = (fi_TIFFIO*)malloc(sizeof(fi_TIFFIO));
+	if(!fio) return NULL;
+	fio->io = io;
+	fio->handle = handle;
+
+	if (read) {
+		fio->tif = TIFFFdOpen((thandle_t)fio, "", "r");
+	} else {
+		// mode = "w"	: write Classic TIFF
+		// mode = "w8"	: write Big TIFF
+		fio->tif = TIFFFdOpen((thandle_t)fio, "", "w");
+	}
+	if(fio->tif == NULL) {
+		free(fio);
+		FreeImage_OutputMessageProc(s_format_id, "Error while opening TIFF: data is invalid");
+		return NULL;
+	}
+	return fio;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+	if(data) {
+		fi_TIFFIO *fio = (fi_TIFFIO*)data;
+		TIFFClose(fio->tif);
+		free(fio);
+	}
+}
+
+// ----------------------------------------------------------
+
+static int DLL_CALLCONV
+PageCount(FreeImageIO *io, fi_handle handle, void *data) {
+	if(data) {
+		fi_TIFFIO *fio = (fi_TIFFIO*)data;
+		TIFF *tif = (TIFF *)fio->tif;
+		int nr_ifd = 0;
+
+		do {
+			nr_ifd++;
+		} while (TIFFReadDirectory(tif));
+				
+		return nr_ifd;
+	}
+
+	return 0;
+}
+
+// ----------------------------------------------------------
+
+/**
+check for uncommon bitspersample values (e.g. 10, 12, ...)
+@param photometric TIFFTAG_PHOTOMETRIC tiff tag
+@param bitspersample TIFFTAG_BITSPERSAMPLE tiff tag
+@return Returns FALSE if a uncommon bit-depth is encountered, returns TRUE otherwise
+*/
+static BOOL 
+IsValidBitsPerSample(uint16 photometric, uint16 bitspersample) {
+
+	switch(bitspersample) {
+		case 1:
+		case 4:
+			if((photometric == PHOTOMETRIC_MINISWHITE) || (photometric == PHOTOMETRIC_MINISBLACK) || (photometric == PHOTOMETRIC_PALETTE)) { 
+				return TRUE;
+			} else {
+				return FALSE;
+			}
+			break;
+		case 8:
+			return TRUE;
+		case 16:
+			if(photometric != PHOTOMETRIC_PALETTE) { 
+				return TRUE;
+			} else {
+				return FALSE;
+			}
+			break;
+		case 32:
+			if((photometric == PHOTOMETRIC_MINISWHITE) || (photometric == PHOTOMETRIC_MINISBLACK) || (photometric == PHOTOMETRIC_LOGLUV)) { 
+				return TRUE;
+			} else {
+				return FALSE;
+			}
+			break;
+		case 64:
+		case 128:
+			if(photometric == PHOTOMETRIC_MINISBLACK) { 
+				return TRUE;
+			} else {
+				return FALSE;
+			}
+			break;
+		default:
+			return FALSE;
+	}
+}
+
+static TIFFLoadMethod  
+FindLoadMethod(TIFF *tif, FREE_IMAGE_TYPE image_type, int flags) {
+	uint16 bitspersample	= (uint16)-1;
+	uint16 samplesperpixel	= (uint16)-1;
+	uint16 photometric		= (uint16)-1;
+	uint16 planar_config	= (uint16)-1;
+
+	TIFFLoadMethod loadMethod = LoadAsGenericStrip;
+
+	TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
+	TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
+	TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);
+	TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);
+
+	BOOL bIsTiled = (TIFFIsTiled(tif) == 0) ? FALSE:TRUE;
+
+	switch(photometric) {
+		// convert to 24 or 32 bits RGB if the image is full color
+		case PHOTOMETRIC_RGB:
+			if((image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) {
+				// load 48-bit RGB and 64-bit RGBA without conversion 
+				loadMethod = LoadAsGenericStrip;
+			} 
+			else if(image_type == FIT_RGBF) {
+				if((samplesperpixel == 3) && (bitspersample == 16)) {
+					// load 3 x 16-bit half as RGBF
+					loadMethod = LoadAsHalfFloat;
+				}
+			}
+			break;
+		case PHOTOMETRIC_YCBCR:
+		case PHOTOMETRIC_CIELAB:
+		case PHOTOMETRIC_ICCLAB:
+		case PHOTOMETRIC_ITULAB:
+			loadMethod = LoadAsRBGA;
+			break;
+		case PHOTOMETRIC_LOGLUV:
+			loadMethod = LoadAsLogLuv;
+			break;
+		case PHOTOMETRIC_SEPARATED:
+			// if image is PHOTOMETRIC_SEPARATED _and_ comes with an ICC profile, 
+			// then the image should preserve its original (CMYK) colour model and 
+			// should be read as CMYK (to keep the match of pixel and profile and 
+			// to avoid multiple conversions. Conversion can be done by changing 
+			// the profile from it's original CMYK to an RGB profile with an 
+			// apropriate color management system. Works with non-tiled TIFFs.
+			if(!bIsTiled) {
+				loadMethod = LoadAsCMYK;
+			}
+			break;
+		case PHOTOMETRIC_MINISWHITE:
+		case PHOTOMETRIC_MINISBLACK:
+		case PHOTOMETRIC_PALETTE:
+			// When samplesperpixel = 2 and bitspersample = 8, set the image as a
+			// 8-bit indexed image + 8-bit alpha layer image
+			// and convert to a 8-bit image with a transparency table
+			if((samplesperpixel > 1) && (bitspersample == 8)) {
+				loadMethod = LoadAs8BitTrns;
+			} else {
+				loadMethod = LoadAsGenericStrip;
+			}
+			break;
+		default:
+			loadMethod = LoadAsGenericStrip;
+			break;
+	}
+
+	if((loadMethod == LoadAsGenericStrip) && bIsTiled) {
+		loadMethod = LoadAsTiled;
+	}
+
+	return loadMethod;
+}
+
+// ==========================================================
+// TIFF thumbnail routines
+// ==========================================================
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data);
+
+/**
+Read embedded thumbnail
+*/
+static void 
+ReadThumbnail(FreeImageIO *io, fi_handle handle, void *data, TIFF *tiff, FIBITMAP *dib) {
+	FIBITMAP* thumbnail = NULL;
+
+	// read exif thumbnail (IFD 1) ...
+
+	uint32 exif_offset = 0;
+	if(TIFFGetField(tiff, TIFFTAG_EXIFIFD, &exif_offset)) {
+
+		if(TIFFLastDirectory(tiff) != 0) {
+			// save current position
+			long tell_pos = io->tell_proc(handle);
+			uint16 cur_dir = TIFFCurrentDirectory(tiff);
+
+			// load the thumbnail
+			int page = 1; 
+			int flags = TIFF_DEFAULT;
+			thumbnail = Load(io, handle, page, flags, data);
+			// store the thumbnail (remember to release it later ...)
+			FreeImage_SetThumbnail(dib, thumbnail);
+
+			// restore current position
+			io->seek_proc(handle, tell_pos, SEEK_SET);
+			TIFFSetDirectory(tiff, cur_dir);
+		}
+	}
+
+	// ... or read the first subIFD
+
+	if(!thumbnail) {
+		uint16 subIFD_count = 0;
+		uint64* subIFD_offsets = NULL;
+		// ### Theoretically this should also read the first subIFD from a Photoshop-created file with "pyramid".
+		// It does not however - the tag is there (using Tag Viewer app) but libtiff refuses to read it
+		if(TIFFGetField(tiff, TIFFTAG_SUBIFD, &subIFD_count, &subIFD_offsets)) {
+			if(subIFD_count > 0) {
+				// save current position
+				long tell_pos = io->tell_proc(handle);
+				uint16 cur_dir = TIFFCurrentDirectory(tiff);
+				if(TIFFSetSubDirectory(tiff, subIFD_offsets[0])) {
+					// load the thumbnail
+					int page = -1; 
+					int flags = TIFF_DEFAULT;
+					thumbnail = Load(io, handle, page, flags, data);
+					// store the thumbnail (remember to release it later ...)
+					FreeImage_SetThumbnail(dib, thumbnail);
+				}
+				// restore current position
+				io->seek_proc(handle, tell_pos, SEEK_SET);
+				TIFFSetDirectory(tiff, cur_dir);
+			}
+		}
+	}
+	
+	// ... or read Photoshop thumbnail
+
+	if(!thumbnail) {
+		uint32 ps_size = 0;
+		void *ps_data = NULL;
+
+		if(TIFFGetField(tiff, TIFFTAG_PHOTOSHOP, &ps_size, &ps_data)) {
+			FIMEMORY *handle = FreeImage_OpenMemory((BYTE*)ps_data, ps_size);
+
+			FreeImageIO io;
+			SetMemoryIO(&io);
+		
+			psdParser parser;
+			parser.ReadImageResources(&io, handle, ps_size);
+
+			FreeImage_SetThumbnail(dib, parser.GetThumbnail());
+			
+			FreeImage_CloseMemory(handle);
+		}
+		
+	}
+
+	// release thumbnail
+	FreeImage_Unload(thumbnail);
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	if (!handle || !data ) {
+		return NULL;
+	}
+	
+	TIFF   *tif = NULL;
+	uint32 height = 0; 
+	uint32 width = 0; 
+	uint16 bitspersample = 1;
+	uint16 samplesperpixel = 1;
+	uint32 rowsperstrip = (uint32)-1;  
+	uint16 photometric = PHOTOMETRIC_MINISWHITE;
+	uint16 compression = (uint16)-1;
+	uint16 planar_config;
+
+	FIBITMAP *dib = NULL;
+	uint32 iccSize = 0;		// ICC profile length
+	void *iccBuf = NULL;	// ICC profile data		
+
+	const BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+	
+	try {	
+		fi_TIFFIO *fio = (fi_TIFFIO*)data;
+		tif = fio->tif;
+
+		if (page != -1) {
+			if (!tif || !TIFFSetDirectory(tif, (uint16)page)) {
+				throw "Error encountered while opening TIFF file";			
+			}
+		}
+		
+		const BOOL asCMYK = (flags & TIFF_CMYK) == TIFF_CMYK;
+
+		// first, get the photometric, the compression and basic metadata
+		// ---------------------------------------------------------------------------------
+
+		TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
+		TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
+
+		// check for HDR formats
+		// ---------------------------------------------------------------------------------
+
+		if(photometric == PHOTOMETRIC_LOGLUV) {
+			// check the compression
+			if(compression != COMPRESSION_SGILOG && compression != COMPRESSION_SGILOG24) {
+				throw "Only support SGILOG compressed LogLuv data";
+			}
+			// set decoder to output in IEEE 32-bit float XYZ values
+			TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT);
+		}
+
+		// ---------------------------------------------------------------------------------
+
+		TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
+		TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
+		TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
+		TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);
+		TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);   			
+		TIFFGetField(tif, TIFFTAG_ICCPROFILE, &iccSize, &iccBuf);
+		TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);
+
+		// check for unsupported formats
+		// ---------------------------------------------------------------------------------
+
+		if(IsValidBitsPerSample(photometric, bitspersample) == FALSE) {
+			FreeImage_OutputMessageProc(s_format_id, 
+				"Unable to handle this format: bitspersample = %d, samplesperpixel = %d, photometric = %d", 
+				(int)bitspersample, (int)samplesperpixel, (int)photometric);
+			throw (char*)NULL;
+		}
+
+		// ---------------------------------------------------------------------------------
+
+		// get image data type
+
+		FREE_IMAGE_TYPE image_type = ReadImageType(tif, bitspersample, samplesperpixel);
+
+		// get the most appropriate loading method
+
+		TIFFLoadMethod loadMethod = FindLoadMethod(tif, image_type, flags);
+
+		// ---------------------------------------------------------------------------------
+
+		if(loadMethod == LoadAsRBGA) {
+			// ---------------------------------------------------------------------------------
+			// RGB[A] loading using the TIFFReadRGBAImage() API
+			// ---------------------------------------------------------------------------------
+
+			BOOL has_alpha = FALSE;   
+
+			// Read the whole image into one big RGBA buffer and then 
+			// convert it to a DIB. This is using the traditional
+			// TIFFReadRGBAImage() API that we trust.
+			
+			uint32 *raster = NULL;
+
+			if(!header_only) {
+
+				raster = (uint32*)_TIFFmalloc(width * height * sizeof(uint32));
+				if (raster == NULL) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+
+				// read the image in one chunk into an RGBA array
+
+				if (!TIFFReadRGBAImage(tif, width, height, raster, 1)) {
+					_TIFFfree(raster);
+					throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+				}
+			}
+			// TIFFReadRGBAImage always deliveres 3 or 4 samples per pixel images
+			// (RGB or RGBA, see below). Cut-off possibly present channels (additional 
+			// alpha channels) from e.g. Photoshop. Any CMYK(A..) is now treated as RGB,
+			// any additional alpha channel on RGB(AA..) is lost on conversion to RGB(A)
+
+			if(samplesperpixel > 4) { // TODO Write to Extra Channels
+				FreeImage_OutputMessageProc(s_format_id, "Warning: %d additional alpha channel(s) ignored", samplesperpixel-4);
+				samplesperpixel = 4;
+			}
+
+			// create a new DIB (take care of different samples-per-pixel in case 
+			// of converted CMYK image (RGB conversion is on sample per pixel less)
+
+			if (photometric == PHOTOMETRIC_SEPARATED && samplesperpixel == 4) {
+				samplesperpixel = 3;
+			}
+
+			dib = CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel);
+			if (dib == NULL) {
+				// free the raster pointer and output an error if allocation failed
+				if(raster) {
+					_TIFFfree(raster);
+				}
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+			
+			// fill in the resolution (english or universal)
+
+			ReadResolution(tif, dib);
+
+			if(!header_only) {
+
+				// read the raster lines and save them in the DIB
+				// with RGB mode, we have to change the order of the 3 samples RGB
+				// We use macros for extracting components from the packed ABGR 
+				// form returned by TIFFReadRGBAImage.
+
+				uint32 *row = &raster[0];
+
+				if (samplesperpixel == 4) {
+					// 32-bit RGBA
+					for (uint32 y = 0; y < height; y++) {
+						BYTE *bits = FreeImage_GetScanLine(dib, y);
+						for (uint32 x = 0; x < width; x++) {
+							bits[FI_RGBA_BLUE]	= (BYTE)TIFFGetB(row[x]);
+							bits[FI_RGBA_GREEN] = (BYTE)TIFFGetG(row[x]);
+							bits[FI_RGBA_RED]	= (BYTE)TIFFGetR(row[x]);
+							bits[FI_RGBA_ALPHA] = (BYTE)TIFFGetA(row[x]);
+
+							if (bits[FI_RGBA_ALPHA] != 0) {
+								has_alpha = TRUE;
+							}
+
+							bits += 4;
+						}
+						row += width;
+					}
+				} else {
+					// 24-bit RGB
+					for (uint32 y = 0; y < height; y++) {
+						BYTE *bits = FreeImage_GetScanLine(dib, y);
+						for (uint32 x = 0; x < width; x++) {
+							bits[FI_RGBA_BLUE]	= (BYTE)TIFFGetB(row[x]);
+							bits[FI_RGBA_GREEN] = (BYTE)TIFFGetG(row[x]);
+							bits[FI_RGBA_RED]	= (BYTE)TIFFGetR(row[x]);
+
+							bits += 3;
+						}
+						row += width;
+					}
+				}
+
+				_TIFFfree(raster);
+			}
+			
+			// ### Not correct when header only
+			FreeImage_SetTransparent(dib, has_alpha);
+
+		} else if(loadMethod == LoadAs8BitTrns) {
+			// ---------------------------------------------------------------------------------
+			// 8-bit + 8-bit alpha layer loading
+			// ---------------------------------------------------------------------------------
+
+			// create a new 8-bit DIB
+			dib = CreateImageType(header_only, image_type, width, height, bitspersample, MIN<uint16>(2, samplesperpixel));
+			if (dib == NULL) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+
+			// fill in the resolution (english or universal)
+
+			ReadResolution(tif, dib);
+
+			// set up the colormap based on photometric	
+
+			ReadPalette(tif, photometric, bitspersample, dib);
+
+			// calculate the line + pitch (separate for scr & dest)
+
+			const tmsize_t src_line = TIFFScanlineSize(tif);
+			// here, the pitch is 2x less than the original as we only keep the first layer				
+			int dst_pitch = FreeImage_GetPitch(dib);
+
+			// transparency table for 8-bit + 8-bit alpha images
+
+			BYTE trns[256]; 
+			// clear the transparency table
+			memset(trns, 0xFF, 256 * sizeof(BYTE));
+
+			// In the tiff file the lines are saved from up to down 
+			// In a DIB the lines must be saved from down to up
+
+			BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+			// read the tiff lines and save them in the DIB
+
+			if(planar_config == PLANARCONFIG_CONTIG && !header_only) {
+
+				BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+				if(buf == NULL) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+
+				for (uint32 y = 0; y < height; y += rowsperstrip) {
+					int32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+					if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, nrow * src_line) == -1) {
+						free(buf);
+						throw FI_MSG_ERROR_PARSING;
+					}
+					for (int l = 0; l < nrow; l++) {
+						BYTE *p = bits;
+						BYTE *b = buf + l * src_line;
+
+						for(uint32 x = 0; x < (uint32)(src_line / samplesperpixel); x++) {
+							// copy the 8-bit layer
+							*p = b[0];
+							// convert the 8-bit alpha layer to a trns table
+							trns[ b[0] ] = b[1];
+
+							p++;
+							b += samplesperpixel;
+						}
+						bits -= dst_pitch;
+					}
+				}
+
+				free(buf);
+			}
+			else if(planar_config == PLANARCONFIG_SEPARATE && !header_only) {
+				tmsize_t stripsize = TIFFStripSize(tif) * sizeof(BYTE);
+				BYTE *buf = (BYTE*)malloc(2 * stripsize);
+				if(buf == NULL) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+				BYTE *grey = buf;
+				BYTE *alpha = buf + stripsize;
+
+				for (uint32 y = 0; y < height; y += rowsperstrip) {
+					int32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+					if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), grey, nrow * src_line) == -1) {
+						free(buf);
+						throw FI_MSG_ERROR_PARSING;
+					} 
+					if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 1), alpha, nrow * src_line) == -1) {
+						free(buf);
+						throw FI_MSG_ERROR_PARSING;
+					} 
+
+					for (int l = 0; l < nrow; l++) {
+						BYTE *p = bits;
+						BYTE *g = grey + l * src_line;
+						BYTE *a = alpha + l * src_line;
+
+						for(uint32 x = 0; x < (uint32)(src_line); x++) {
+							// copy the 8-bit layer
+							*p = g[0];
+							// convert the 8-bit alpha layer to a trns table
+							trns[ g[0] ] = a[0];
+
+							p++;
+							g++;
+							a++;
+						}
+						bits -= dst_pitch;
+					}
+				}
+
+				free(buf);
+
+			}
+			
+			FreeImage_SetTransparencyTable(dib, &trns[0], 256);
+			FreeImage_SetTransparent(dib, TRUE);
+
+		} else if(loadMethod == LoadAsCMYK) {
+			// ---------------------------------------------------------------------------------
+			// CMYK loading
+			// ---------------------------------------------------------------------------------
+
+			// At this place, samplesperpixel could be > 4, esp. when a CMYK(A) format
+			// is recognized. Where all other formats are handled straight-forward, this
+			// format has to be handled special 
+
+			BOOL isCMYKA = (photometric == PHOTOMETRIC_SEPARATED) && (samplesperpixel > 4);
+
+			// We use a temp dib to store the alpha for the CMYKA to RGBA conversion
+			// NOTE this is until we have Extra channels implementation.
+			// Also then it will be possible to merge LoadAsCMYK with LoadAsGenericStrip
+			
+			FIBITMAP *alpha = NULL;
+			unsigned alpha_pitch = 0;
+			BYTE *alpha_bits = NULL;
+			unsigned alpha_Bpp = 0;
+
+			if(isCMYKA && !asCMYK && !header_only) {
+				if(bitspersample == 16) {
+					alpha = FreeImage_AllocateT(FIT_UINT16, width, height);
+				} else if (bitspersample == 8) {
+					alpha = FreeImage_Allocate(width, height, 8);
+				}
+					
+				if(!alpha) {
+					FreeImage_OutputMessageProc(s_format_id, "Failed to allocate temporary alpha channel");
+				} else {
+					alpha_bits = FreeImage_GetScanLine(alpha, height - 1);
+					alpha_pitch = FreeImage_GetPitch(alpha);
+					alpha_Bpp = FreeImage_GetBPP(alpha) / 8;
+				}
+				
+			}
+			
+			// create a new DIB
+			const uint16 chCount = MIN<uint16>(samplesperpixel, 4);
+			dib = CreateImageType(header_only, image_type, width, height, bitspersample, chCount);
+			if (dib == NULL) {
+				FreeImage_Unload(alpha);
+				throw FI_MSG_ERROR_MEMORY;
+			}
+
+			// fill in the resolution (english or universal)
+
+			ReadResolution(tif, dib);
+
+			if(!header_only) {
+
+				// calculate the line + pitch (separate for scr & dest)
+
+				const tmsize_t src_line = TIFFScanlineSize(tif);
+				const tmsize_t dst_line = FreeImage_GetLine(dib);
+				const unsigned dib_pitch = FreeImage_GetPitch(dib);
+				const unsigned dibBpp = FreeImage_GetBPP(dib) / 8;
+				const unsigned Bpc = dibBpp / chCount;
+				const unsigned srcBpp = bitspersample * samplesperpixel / 8;
+
+				assert(Bpc <= 2); //< CMYK is only BYTE or SHORT 
+				
+				// In the tiff file the lines are save from up to down 
+				// In a DIB the lines must be saved from down to up
+
+				BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+				// read the tiff lines and save them in the DIB
+
+				BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+				if(buf == NULL) {
+					FreeImage_Unload(alpha);
+					throw FI_MSG_ERROR_MEMORY;
+				}
+
+				if(planar_config == PLANARCONFIG_CONTIG) {
+					
+					// - loop for strip blocks -
+					
+					for (uint32 y = 0; y < height; y += rowsperstrip) {
+						const int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+						if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, strips * src_line) == -1) {
+							free(buf);
+							FreeImage_Unload(alpha);
+							throw FI_MSG_ERROR_PARSING;
+						} 
+						
+						// - loop for strips -
+						
+						if(src_line != dst_line) {
+							// CMYKA+
+							if(alpha) {
+								for (int l = 0; l < strips; l++) {					
+									for(BYTE *pixel = bits, *al_pixel = alpha_bits, *src_pixel =  buf + l * src_line; pixel < bits + dib_pitch; pixel += dibBpp, al_pixel += alpha_Bpp, src_pixel += srcBpp) {
+										// copy pixel byte by byte
+										BYTE b = 0;
+										for( ; b < dibBpp; ++b) {
+											pixel[b] =  src_pixel[b];
+										}
+										// TODO write the remaining bytes to extra channel(s)
+										
+										// HACK write the first alpha to a separate dib (assume BYTE or WORD)
+										al_pixel[0] = src_pixel[b];
+										if(Bpc > 1) {
+											al_pixel[1] = src_pixel[b + 1];
+										}
+										
+									}
+									bits -= dib_pitch;
+									alpha_bits -= alpha_pitch;
+								}
+							}
+							else {
+								// alpha/extra channels alloc failed
+								for (int l = 0; l < strips; l++) {
+									for(BYTE* pixel = bits, * src_pixel =  buf + l * src_line; pixel < bits + dst_line; pixel += dibBpp, src_pixel += srcBpp) {
+										AssignPixel(pixel, src_pixel, dibBpp);
+									}
+									bits -= dib_pitch;
+								}
+							}
+						}
+						else { 
+							// CMYK to CMYK
+							for (int l = 0; l < strips; l++) {
+								BYTE *b = buf + l * src_line;
+								memcpy(bits, b, src_line);
+								bits -= dib_pitch;
+							}
+						}
+
+					} // height
+				
+				}
+				else if(planar_config == PLANARCONFIG_SEPARATE) {
+
+					BYTE *dib_strip = bits;
+					BYTE *al_strip = alpha_bits;
+
+					// - loop for strip blocks -
+					
+					for (uint32 y = 0; y < height; y += rowsperstrip) {
+						const int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
+						
+						// - loop for channels (planes) -
+						
+						for(uint16 sample = 0; sample < samplesperpixel; sample++) {
+							
+							if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, sample), buf, strips * src_line) == -1) {
+								free(buf);
+								FreeImage_Unload(alpha);
+								throw FI_MSG_ERROR_PARSING;
+							} 
+									
+							BYTE *dst_strip = dib_strip;
+							unsigned dst_pitch = dib_pitch;
+							uint16 ch = sample;
+							unsigned Bpp = dibBpp;
+
+							if(sample >= chCount) {
+								// TODO Write to Extra Channel
+								
+								// HACK redirect write to temp alpha
+								if(alpha && sample == chCount) {
+
+									dst_strip = al_strip;
+									dst_pitch = alpha_pitch;
+
+									ch = 0;
+									Bpp = alpha_Bpp;
+								}
+								else {
+									break; 
+								}
+							}
+							
+							const unsigned channelOffset = ch * Bpc;			
+							
+							// - loop for strips in block -
+							
+							BYTE *src_line_begin = buf;
+							BYTE *dst_line_begin = dst_strip;
+							for (int l = 0; l < strips; l++, src_line_begin += src_line, dst_line_begin -= dst_pitch ) {
+								// - loop for pixels in strip -
+								
+								const BYTE* const src_line_end = src_line_begin + src_line;
+								for (BYTE *src_bits = src_line_begin, * dst_bits = dst_line_begin; src_bits < src_line_end; src_bits += Bpc, dst_bits += Bpp) {
+									AssignPixel(dst_bits + channelOffset, src_bits, Bpc);									
+								} // line
+								
+							} // strips
+															
+						} // channels
+							
+						// done with a strip block, incr to the next
+						dib_strip -= strips * dib_pitch;
+						al_strip -= strips * alpha_pitch;
+							
+					} //< height
+					
+				}
+
+				free(buf);
+			
+				if(!asCMYK) {
+					ConvertCMYKtoRGBA(dib);
+					
+					// The ICC Profile is invalid, clear it
+					iccSize = 0;
+					iccBuf = NULL;
+					
+					if(isCMYKA) {
+						// HACK until we have Extra channels. (ConvertCMYKtoRGBA will then do the work)
+						
+						FreeImage_SetChannel(dib, alpha, FICC_ALPHA);
+						FreeImage_Unload(alpha);
+						alpha = NULL;
+					}
+					else {
+						FIBITMAP *t = RemoveAlphaChannel(dib);
+						if(t) {
+							FreeImage_Unload(dib);
+							dib = t;
+						}
+						else {
+							FreeImage_OutputMessageProc(s_format_id, "Cannot allocate memory for buffer. CMYK image converted to RGB + pending Alpha");
+						}
+					}
+				}
+				
+			} // !header_only
+			
+		} else if(loadMethod == LoadAsGenericStrip) {
+			// ---------------------------------------------------------------------------------
+			// Generic loading
+			// ---------------------------------------------------------------------------------
+
+			// create a new DIB
+			const uint16 chCount = MIN<uint16>(samplesperpixel, 4);
+			dib = CreateImageType(header_only, image_type, width, height, bitspersample, chCount);
+			if (dib == NULL) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+
+			// fill in the resolution (english or universal)
+
+			ReadResolution(tif, dib);
+
+			// set up the colormap based on photometric	
+
+			ReadPalette(tif, photometric, bitspersample, dib);
+	
+			if(!header_only) {
+				// calculate the line + pitch (separate for scr & dest)
+
+				const tmsize_t src_line = TIFFScanlineSize(tif);
+				const tmsize_t dst_line = FreeImage_GetLine(dib);
+				const unsigned dst_pitch = FreeImage_GetPitch(dib);
+				const unsigned Bpp = FreeImage_GetBPP(dib) / 8;
+				const unsigned srcBpp = bitspersample * samplesperpixel / 8;
+
+				// In the tiff file the lines are save from up to down 
+				// In a DIB the lines must be saved from down to up
+
+				BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+				// read the tiff lines and save them in the DIB
+
+				BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+				if(buf == NULL) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+				memset(buf, 0, TIFFStripSize(tif) * sizeof(BYTE));
+				
+				BOOL bThrowMessage = FALSE;
+				
+				if(planar_config == PLANARCONFIG_CONTIG) {
+
+					for (uint32 y = 0; y < height; y += rowsperstrip) {
+						int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+						if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, strips * src_line) == -1) {
+							// ignore errors as they can be frequent and not really valid errors, especially with fax images
+							bThrowMessage = TRUE;							
+							/*
+							free(buf);
+							throw FI_MSG_ERROR_PARSING;
+							*/
+						} 
+						if(src_line == dst_line) {
+							// channel count match
+							for (int l = 0; l < strips; l++) {							
+								memcpy(bits, buf + l * src_line, src_line);
+								bits -= dst_pitch;
+							}
+						}
+						else {
+							for (int l = 0; l < strips; l++) {
+								for(BYTE *pixel = bits, *src_pixel =  buf + l * src_line; pixel < bits + dst_pitch; pixel += Bpp, src_pixel += srcBpp) {
+									AssignPixel(pixel, src_pixel, Bpp);
+								}
+								bits -= dst_pitch;
+							}
+						}
+					}
+				}
+				else if(planar_config == PLANARCONFIG_SEPARATE) {
+					
+					const unsigned Bpc = bitspersample / 8;
+					BYTE* dib_strip = bits;
+					// - loop for strip blocks -
+					
+					for (uint32 y = 0; y < height; y += rowsperstrip) {
+						const int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
+						
+						// - loop for channels (planes) -
+						
+						for(uint16 sample = 0; sample < samplesperpixel; sample++) {
+							
+							if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, sample), buf, strips * src_line) == -1) {
+								// ignore errors as they can be frequent and not really valid errors, especially with fax images
+								bThrowMessage = TRUE;	
+							} 
+									
+							if(sample >= chCount) {
+								// TODO Write to Extra Channel
+								break; 
+							}
+							
+							const unsigned channelOffset = sample * Bpc;			
+							
+							// - loop for strips in block -
+							
+							BYTE* src_line_begin = buf;
+							BYTE* dst_line_begin = dib_strip;
+							for (int l = 0; l < strips; l++, src_line_begin += src_line, dst_line_begin -= dst_pitch ) {
+									
+								// - loop for pixels in strip -
+								
+								const BYTE* const src_line_end = src_line_begin + src_line;
+
+								for (BYTE* src_bits = src_line_begin, * dst_bits = dst_line_begin; src_bits < src_line_end; src_bits += Bpc, dst_bits += Bpp) {
+									// actually assigns channel
+									AssignPixel(dst_bits + channelOffset, src_bits, Bpc); 
+								} // line
+
+							} // strips
+
+						} // channels
+							
+						// done with a strip block, incr to the next
+						dib_strip -= strips * dst_pitch;
+							
+					} // height
+
+				}
+				free(buf);
+				
+				if(bThrowMessage) {
+					FreeImage_OutputMessageProc(s_format_id, "Warning: parsing error. Image may be incomplete or contain invalid data !");
+				}
+				
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+				SwapRedBlue32(dib);
+#endif
+
+			} // !header only
+			
+		} else if(loadMethod == LoadAsTiled) {
+			// ---------------------------------------------------------------------------------
+			// Tiled image loading
+			// ---------------------------------------------------------------------------------
+
+			uint32 tileWidth, tileHeight;
+			uint32 src_line = 0;
+
+			// create a new DIB
+			dib = CreateImageType( header_only, image_type, width, height, bitspersample, samplesperpixel);
+			if (dib == NULL) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+
+			// fill in the resolution (english or universal)
+
+			ReadResolution(tif, dib);
+
+			// set up the colormap based on photometric	
+
+			ReadPalette(tif, photometric, bitspersample, dib);
+
+			// get the tile geometry
+			if(!TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tileWidth) || !TIFFGetField(tif, TIFFTAG_TILELENGTH, &tileHeight)) {
+				throw "Invalid tiled TIFF image";
+			}
+
+			// read the tiff lines and save them in the DIB
+
+			if(planar_config == PLANARCONFIG_CONTIG && !header_only) {
+				
+				// get the maximum number of bytes required to contain a tile
+				tmsize_t tileSize = TIFFTileSize(tif);
+
+				// allocate tile buffer
+				BYTE *tileBuffer = (BYTE*)malloc(tileSize * sizeof(BYTE));
+				if(tileBuffer == NULL) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+
+				// calculate src line and dst pitch
+				int dst_pitch = FreeImage_GetPitch(dib);
+				uint32 tileRowSize = (uint32)TIFFTileRowSize(tif);
+				uint32 imageRowSize = (uint32)TIFFScanlineSize(tif);
+
+
+				// In the tiff file the lines are saved from up to down 
+				// In a DIB the lines must be saved from down to up
+
+				BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+				
+				for (uint32 y = 0; y < height; y += tileHeight) {						
+					int32 nrows = (y + tileHeight > height ? height - y : tileHeight);					
+
+					for (uint32 x = 0, rowSize = 0; x < width; x += tileWidth, rowSize += tileRowSize) {
+						memset(tileBuffer, 0, tileSize);
+
+						// read one tile
+						if (TIFFReadTile(tif, tileBuffer, x, y, 0, 0) < 0) {
+							free(tileBuffer);
+							throw "Corrupted tiled TIFF file";
+						}
+						// convert to strip
+						if(x + tileWidth > width) {
+							src_line = imageRowSize - rowSize;
+						} else {
+							src_line = tileRowSize;
+						}
+						BYTE *src_bits = tileBuffer;
+						BYTE *dst_bits = bits + rowSize;
+						for(int k = 0; k < nrows; k++) {
+							memcpy(dst_bits, src_bits, src_line);
+							src_bits += tileRowSize;
+							dst_bits -= dst_pitch;
+						}
+					}
+
+					bits -= nrows * dst_pitch;
+				}
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+				SwapRedBlue32(dib);
+#endif
+				free(tileBuffer);
+			}
+			else if(planar_config == PLANARCONFIG_SEPARATE) {
+				throw "Separated tiled TIFF images are not supported"; 
+			}
+
+
+		} else if(loadMethod == LoadAsLogLuv) {
+			// ---------------------------------------------------------------------------------
+			// RGBF LogLuv compressed loading
+			// ---------------------------------------------------------------------------------
+
+			double	stonits;	// input conversion to nits
+			if (!TIFFGetField(tif, TIFFTAG_STONITS, &stonits)) {
+				stonits = 1;
+			}
+			
+			// create a new DIB
+			dib = CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel);
+			if (dib == NULL) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+
+			// fill in the resolution (english or universal)
+
+			ReadResolution(tif, dib);
+
+			if(planar_config == PLANARCONFIG_CONTIG && !header_only) {
+				// calculate the line + pitch (separate for scr & dest)
+
+				tmsize_t src_line = TIFFScanlineSize(tif);
+				int dst_pitch = FreeImage_GetPitch(dib);
+
+				// In the tiff file the lines are save from up to down 
+				// In a DIB the lines must be saved from down to up
+
+				BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+				// read the tiff lines and save them in the DIB
+
+				BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+				if(buf == NULL) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+
+				for (uint32 y = 0; y < height; y += rowsperstrip) {
+					int32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+					if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, nrow * src_line) == -1) {
+						free(buf);
+						throw FI_MSG_ERROR_PARSING;
+					} 
+					// convert from XYZ to RGB
+					for (int l = 0; l < nrow; l++) {						
+						tiff_ConvertLineXYZToRGB(bits, buf + l * src_line, stonits, width);
+						bits -= dst_pitch;
+					}
+				}
+
+				free(buf);
+			}
+			else if(planar_config == PLANARCONFIG_SEPARATE) {
+				// this cannot happen according to the LogLuv specification
+				throw "Unable to handle PLANARCONFIG_SEPARATE LogLuv images";
+			}
+
+		} else if(loadMethod == LoadAsHalfFloat) {
+			// ---------------------------------------------------------------------------------
+			// RGBF loading from a half format
+			// ---------------------------------------------------------------------------------
+
+			// create a new DIB
+			dib = CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel);
+			if (dib == NULL) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+
+			// fill in the resolution (english or universal)
+
+			ReadResolution(tif, dib);
+
+			if(!header_only) {
+
+				// calculate the line + pitch (separate for scr & dest)
+
+				tmsize_t src_line = TIFFScanlineSize(tif);
+				unsigned dst_pitch = FreeImage_GetPitch(dib);
+
+				// In the tiff file the lines are save from up to down 
+				// In a DIB the lines must be saved from down to up
+
+				BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+				// read the tiff lines and save them in the DIB
+
+				if(planar_config == PLANARCONFIG_CONTIG) {
+
+					BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+					if(buf == NULL) {
+						throw FI_MSG_ERROR_MEMORY;
+					}
+
+					for (uint32 y = 0; y < height; y += rowsperstrip) {
+						uint32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+						if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, nrow * src_line) == -1) {
+							free(buf);
+							throw FI_MSG_ERROR_PARSING;
+						} 
+
+						// convert from half (16-bit) to float (32-bit)
+						// !!! use OpenEXR half helper class
+
+						half half_value;
+
+						for (uint32 l = 0; l < nrow; l++) {
+							WORD *src_pixel = (WORD*)(buf + l * src_line);
+							float *dst_pixel = (float*)bits;
+
+							for(tmsize_t x = 0; x < (tmsize_t)(src_line / sizeof(WORD)); x++) {
+								half_value.setBits(src_pixel[x]);
+								dst_pixel[x] = half_value;
+							}
+
+							bits -= dst_pitch;
+						}
+					}
+
+					free(buf);
+				}
+				else if(planar_config == PLANARCONFIG_SEPARATE) {
+					// this use case was never encountered yet
+					throw "Unable to handle PLANARCONFIG_SEPARATE RGB half float images";
+				}
+				
+			} // !header only
+
+		} else {
+			// ---------------------------------------------------------------------------------
+			// Unknown or unsupported format
+			// ---------------------------------------------------------------------------------
+
+			throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+		}
+
+		// copy ICC profile data (must be done after FreeImage_Allocate)
+
+		FreeImage_CreateICCProfile(dib, iccBuf, iccSize);		
+		if (photometric == PHOTOMETRIC_SEPARATED && asCMYK) {
+			FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK;
+		}			
+
+		// copy TIFF metadata (must be done after FreeImage_Allocate)
+
+		ReadMetadata(tif, dib);
+
+		// copy TIFF thumbnail (must be done after FreeImage_Allocate)
+		
+		ReadThumbnail(io, handle, data, tif, dib);
+
+		return (FIBITMAP *)dib;
+
+	} catch (const char *message) {			
+		if(dib)	{
+			FreeImage_Unload(dib);
+		}
+		if(message) {
+			FreeImage_OutputMessageProc(s_format_id, message);
+		}
+		return NULL;
+	}
+  
+}
+
+// --------------------------------------------------------------------------
+
+static BOOL 
+SaveOneTIFF(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data, unsigned ifd, unsigned ifdCount) {
+	if (!dib || !handle || !data) {
+		return FALSE;
+	} 
+	
+	try { 
+		fi_TIFFIO *fio = (fi_TIFFIO*)data;
+		TIFF *out = fio->tif;
+
+		const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+		const uint32 width = FreeImage_GetWidth(dib);
+		const uint32 height = FreeImage_GetHeight(dib);
+		const uint16 bitsperpixel = (uint16)FreeImage_GetBPP(dib);
+
+		const FIICCPROFILE* iccProfile = FreeImage_GetICCProfile(dib);
+		
+		// setup out-variables based on dib and flag options
+		
+		uint16 bitspersample;
+		uint16 samplesperpixel;
+		uint16 photometric;
+
+		if(image_type == FIT_BITMAP) {
+			// standard image: 1-, 4-, 8-, 16-, 24-, 32-bit
+
+			samplesperpixel = ((bitsperpixel == 24) ? 3 : ((bitsperpixel == 32) ? 4 : 1));
+			bitspersample = bitsperpixel / samplesperpixel;
+			photometric	= GetPhotometric(dib);
+
+			if((bitsperpixel == 8) && FreeImage_IsTransparent(dib)) {
+				// 8-bit transparent picture : convert later to 8-bit + 8-bit alpha
+				samplesperpixel = 2;
+				bitspersample = 8;
+			}
+			else if(bitsperpixel == 32) {
+				// 32-bit images : check for CMYK or alpha transparency
+
+				if((((iccProfile->flags & FIICC_COLOR_IS_CMYK) == FIICC_COLOR_IS_CMYK) || ((flags & TIFF_CMYK) == TIFF_CMYK))) {
+					// CMYK support
+					photometric = PHOTOMETRIC_SEPARATED;
+					TIFFSetField(out, TIFFTAG_INKSET, INKSET_CMYK);
+					TIFFSetField(out, TIFFTAG_NUMBEROFINKS, 4);
+				}
+				else if(photometric == PHOTOMETRIC_RGB) {
+					// transparency mask support
+					uint16 sampleinfo[1]; 
+					// unassociated alpha data is transparency information
+					sampleinfo[0] = EXTRASAMPLE_UNASSALPHA;
+					TIFFSetField(out, TIFFTAG_EXTRASAMPLES, 1, sampleinfo);
+				}
+			}
+		} else if(image_type == FIT_RGB16) {
+			// 48-bit RGB
+
+			samplesperpixel = 3;
+			bitspersample = bitsperpixel / samplesperpixel;
+			photometric	= PHOTOMETRIC_RGB;
+		} else if(image_type == FIT_RGBA16) {
+			// 64-bit RGBA
+
+			samplesperpixel = 4;
+			bitspersample = bitsperpixel / samplesperpixel;
+			if((((iccProfile->flags & FIICC_COLOR_IS_CMYK) == FIICC_COLOR_IS_CMYK) || ((flags & TIFF_CMYK) == TIFF_CMYK))) {
+				// CMYK support
+				photometric = PHOTOMETRIC_SEPARATED;
+				TIFFSetField(out, TIFFTAG_INKSET, INKSET_CMYK);
+				TIFFSetField(out, TIFFTAG_NUMBEROFINKS, 4);
+			}
+			else {
+				photometric	= PHOTOMETRIC_RGB;
+				// transparency mask support
+				uint16 sampleinfo[1]; 
+				// unassociated alpha data is transparency information
+				sampleinfo[0] = EXTRASAMPLE_UNASSALPHA;
+				TIFFSetField(out, TIFFTAG_EXTRASAMPLES, 1, sampleinfo);
+			}
+		} else if(image_type == FIT_RGBF) {
+			// 96-bit RGBF => store with a LogLuv encoding ?
+
+			samplesperpixel = 3;
+			bitspersample = bitsperpixel / samplesperpixel;
+			// the library converts to and from floating-point XYZ CIE values
+			if((flags & TIFF_LOGLUV) == TIFF_LOGLUV) {
+				photometric	= PHOTOMETRIC_LOGLUV;
+				TIFFSetField(out, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT);
+				// TIFFSetField(out, TIFFTAG_STONITS, 1.0);   // assume unknown 
+			}
+			else {
+				// store with default compression (LZW) or with input compression flag
+				photometric	= PHOTOMETRIC_RGB;
+			}
+			
+		} else if (image_type == FIT_RGBAF) {
+			// 128-bit RGBAF => store with default compression (LZW) or with input compression flag
+			
+			samplesperpixel = 4;
+			bitspersample = bitsperpixel / samplesperpixel;
+			photometric	= PHOTOMETRIC_RGB;
+		} else {
+			// special image type (int, long, double, ...)
+			
+			samplesperpixel = 1;
+			bitspersample = bitsperpixel;
+			photometric	= PHOTOMETRIC_MINISBLACK;
+		}
+
+		// set image data type
+
+		WriteImageType(out, image_type);
+		
+		// write possible ICC profile
+
+		if (iccProfile->size && iccProfile->data) {
+			TIFFSetField(out, TIFFTAG_ICCPROFILE, iccProfile->size, iccProfile->data);
+		}
+
+		// handle standard width/height/bpp stuff
+
+		TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
+		TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
+		TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
+		TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bitspersample);
+		TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric);
+		TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);	// single image plane 
+		TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+		TIFFSetField(out, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+		TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(out, (uint32) -1)); 
+
+		// handle metrics
+
+		WriteResolution(out, dib);
+
+		// multi-paging
+
+		if (page >= 0) {
+			char page_number[20];
+			sprintf(page_number, "Page %d", page);
+
+			TIFFSetField(out, TIFFTAG_SUBFILETYPE, (uint32)FILETYPE_PAGE);
+			TIFFSetField(out, TIFFTAG_PAGENUMBER, (uint16)page, (uint16)0);
+			TIFFSetField(out, TIFFTAG_PAGENAME, page_number);
+
+		} else {
+			// is it a thumbnail ? 
+			TIFFSetField(out, TIFFTAG_SUBFILETYPE, (ifd == 0) ? (uint32)0 : (uint32)FILETYPE_REDUCEDIMAGE);
+		}
+
+		// palettes (image colormaps are automatically scaled to 16-bits)
+
+		if (photometric == PHOTOMETRIC_PALETTE) {
+			uint16 *r, *g, *b;
+			uint16 nColors = (uint16)FreeImage_GetColorsUsed(dib);
+			RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+			r = (uint16 *) _TIFFmalloc(sizeof(uint16) * 3 * nColors);
+			if(r == NULL) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+			g = r + nColors;
+			b = g + nColors;
+
+			for (int i = nColors - 1; i >= 0; i--) {
+				r[i] = SCALE((uint16)pal[i].rgbRed);
+				g[i] = SCALE((uint16)pal[i].rgbGreen);
+				b[i] = SCALE((uint16)pal[i].rgbBlue);
+			}
+
+			TIFFSetField(out, TIFFTAG_COLORMAP, r, g, b);
+
+			_TIFFfree(r);
+		}
+
+		// compression tag
+
+		WriteCompression(out, bitspersample, samplesperpixel, photometric, flags);
+
+		// metadata
+
+		WriteMetadata(out, dib);
+
+		// thumbnail tag
+
+		if((ifd == 0) && (ifdCount > 1)) {
+			uint16 nsubifd = 1;
+			uint64 subifd[1];
+			subifd[0] = 0;
+			TIFFSetField(out, TIFFTAG_SUBIFD, nsubifd, subifd);
+		}
+
+		// read the DIB lines from bottom to top
+		// and save them in the TIF
+		// -------------------------------------
+		
+		const uint32 pitch = FreeImage_GetPitch(dib);
+
+		if(image_type == FIT_BITMAP) {
+			// standard bitmap type
+		
+			switch(bitsperpixel) {
+				case 1 :
+				case 4 :
+				case 8 :
+				{
+					if((bitsperpixel == 8) && FreeImage_IsTransparent(dib)) {
+						// 8-bit transparent picture : convert to 8-bit + 8-bit alpha
+
+						// get the transparency table
+						BYTE *trns = FreeImage_GetTransparencyTable(dib);
+
+						BYTE *buffer = (BYTE *)malloc(2 * width * sizeof(BYTE));
+						if(buffer == NULL) {
+							throw FI_MSG_ERROR_MEMORY;
+						}
+
+						for (int y = height - 1; y >= 0; y--) {
+							BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+							BYTE *p = bits, *b = buffer;
+
+							for(uint32 x = 0; x < width; x++) {
+								// copy the 8-bit layer
+								b[0] = *p;
+								// convert the trns table to a 8-bit alpha layer
+								b[1] = trns[ b[0] ];
+
+								p++;
+								b += samplesperpixel;
+							}
+
+							// write the scanline to disc
+
+							TIFFWriteScanline(out, buffer, height - y - 1, 0);
+						}
+
+						free(buffer);
+					}
+					else {
+						// other cases
+						BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
+						if(buffer == NULL) {
+							throw FI_MSG_ERROR_MEMORY;
+						}
+
+						for (uint32 y = 0; y < height; y++) {
+							// get a copy of the scanline
+							memcpy(buffer, FreeImage_GetScanLine(dib, height - y - 1), pitch);
+							// write the scanline to disc
+							TIFFWriteScanline(out, buffer, y, 0);
+						}
+						free(buffer);
+					}
+
+					break;
+				}				
+
+				case 24:
+				case 32:
+				{
+					BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
+					if(buffer == NULL) {
+						throw FI_MSG_ERROR_MEMORY;
+					}
+
+					for (uint32 y = 0; y < height; y++) {
+						// get a copy of the scanline
+
+						memcpy(buffer, FreeImage_GetScanLine(dib, height - y - 1), pitch);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+						if (photometric != PHOTOMETRIC_SEPARATED) {
+							// TIFFs store color data RGB(A) instead of BGR(A)
+		
+							BYTE *pBuf = buffer;
+		
+							for (uint32 x = 0; x < width; x++) {
+								INPLACESWAP(pBuf[0], pBuf[2]);
+								pBuf += samplesperpixel;
+							}
+						}
+#endif
+						// write the scanline to disc
+
+						TIFFWriteScanline(out, buffer, y, 0);
+					}
+
+					free(buffer);
+
+					break;
+				}
+			}//< switch (bitsperpixel)
+
+		} else if(image_type == FIT_RGBF && (flags & TIFF_LOGLUV) == TIFF_LOGLUV) {
+			// RGBF image => store as XYZ using a LogLuv encoding
+
+			BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
+			if(buffer == NULL) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+
+			for (uint32 y = 0; y < height; y++) {
+				// get a copy of the scanline and convert from RGB to XYZ
+				tiff_ConvertLineRGBToXYZ(buffer, FreeImage_GetScanLine(dib, height - y - 1), width);
+				// write the scanline to disc
+				TIFFWriteScanline(out, buffer, y, 0);
+			}
+			free(buffer);
+		} else {
+			// just dump the dib (tiff supports all dib types)
+			
+			BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
+			if(buffer == NULL) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+			
+			for (uint32 y = 0; y < height; y++) {
+				// get a copy of the scanline
+				memcpy(buffer, FreeImage_GetScanLine(dib, height - y - 1), pitch);
+				// write the scanline to disc
+				TIFFWriteScanline(out, buffer, y, 0);
+			}
+			free(buffer);
+		}
+
+		// write out the directory tag if we wrote a page other than -1 or if we have a thumbnail to write later
+
+		if( (page >= 0) || ((ifd == 0) && (ifdCount > 1)) ) {
+			TIFFWriteDirectory(out);
+			// else: TIFFClose will WriteDirectory
+		}
+
+		return TRUE;
+		
+	} catch(const char *text) {
+		FreeImage_OutputMessageProc(s_format_id, text);
+		return FALSE;
+	} 
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	BOOL bResult = FALSE;
+	
+	// handle thumbnail as SubIFD
+	const BOOL bHasThumbnail = (FreeImage_GetThumbnail(dib) != NULL);
+	const unsigned ifdCount = bHasThumbnail ? 2 : 1;
+	
+	FIBITMAP *bitmap = dib;
+
+	for(unsigned ifd = 0; ifd < ifdCount; ifd++) {
+		// redirect dib to thumbnail for the second pass
+		if(ifd == 1) {
+			bitmap = FreeImage_GetThumbnail(dib);
+		}
+
+		bResult = SaveOneTIFF(io, bitmap, handle, page, flags, data, ifd, ifdCount);
+		if(!bResult) {
+			return FALSE;
+		}
+	}
+
+	return bResult;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitTIFF(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+    // Set up the callback for extended TIFF directory tag support (see XTIFF.cpp)
+	// Must be called before using libtiff
+    XTIFFInitialize();	
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = PageCount;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+	plugin->supports_no_pixels_proc = SupportsNoPixels; 
+}
diff --git a/files/Source/FreeImage/PluginWBMP.cpp b/files/Source/FreeImage/PluginWBMP.cpp
new file mode 100644
index 0000000..2ff8f02
--- /dev/null
+++ b/files/Source/FreeImage/PluginWBMP.cpp
@@ -0,0 +1,372 @@
+// ==========================================================
+// Wireless Bitmap Format Loader and Writer
+//
+// 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"
+
+// ----------------------------------------------------------
+// Wireless Bitmap Format
+// ----------------------
+// The WBMP format enables graphical information to be sent to a variety of handsets.
+// The WBMP format is terminal independent and describes only graphical information.
+
+// IMPLEMENTATION NOTES:
+// ------------------------
+// The WBMP format is configured according to a type field value (TypeField below),
+// which maps to all relevant image encoding information, such as:
+// · Pixel organisation and encoding
+// · Palette organisation and encoding
+// · Compression characteristics
+// · Animation encoding
+// For each TypeField value, all relevant image characteristics are 
+// fully specified as part of the WAP documentation.
+// Currently, a simple compact, monochrome image format is defined
+// within the WBMP type space :
+//
+// Image Type Identifier, multi-byte integer	0
+// Image Format description						0 B/W, no compression
+// -------------------------------------------------------------------------------
+
+// WBMP Header
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagWBMPHEADER {
+	WORD TypeField;			// Image type identifier of multi-byte length
+	BYTE FixHeaderField;	// Octet of general header information
+	BYTE ExtHeaderFields;	// Zero or more extension header fields
+	WORD Width;				// Multi-byte width field
+	WORD Height;			// Multi-byte height field
+} WBMPHEADER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// The extension headers may be of type binary 00 through binary 11, defined as follows.
+
+// - Type 00 indicates a multi-byte bitfield used to specify additional header information.
+// The first bit is set if a type 00, extension header is set if more data follows.
+//  The other bits are reserved for future use.
+// - Type 01 - reserved for future use.
+// - Type 10 - reserved for future use.
+// - Type 11 indicates a sequence of parameter/value pairs. These can be used for 
+// optimisations and special purpose extensions, eg, animation image formats.
+// The parameter size tells the length (1-8 bytes) of the following parameter name.
+// The value size gives the length (1-16 bytes) of the following parameter value.
+// The concatenation flag indicates whether another parameter/value pair will follow
+// after reading the specified bytes of data.
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static DWORD
+multiByteRead(FreeImageIO *io, fi_handle handle) {
+	// Multi-byte encoding / decoding
+	// -------------------------------
+	// A multi-byte integer consists of a series of octets, where the most significant bit
+	// is the continuation flag, and the remaining seven bits are a scalar value.
+	// The continuation flag is used to indicate that an octet is not the end of the multi-byte
+	// sequence.
+
+	DWORD Out = 0;
+	BYTE In = 0;
+
+	while (io->read_proc(&In, 1, 1, handle)) {
+		Out += (In & 0x7F);
+
+		if ((In & 0x80) == 0x00)
+			break;
+
+		Out <<= 7;
+	}
+
+	return Out;
+}
+
+static void
+multiByteWrite(FreeImageIO *io, fi_handle handle, DWORD In) {
+	BYTE Out, k = 1;
+  
+	while (In & (0x7F << 7*k))
+		k++;
+  
+	while (k > 1) {
+		k--;
+
+		Out = (BYTE)(0x80 | (In >> 7*k) & 0xFF);
+
+		io->write_proc(&Out, 1, 1, handle);
+	}
+
+	Out = (BYTE)(In & 0x7F);
+
+	io->write_proc(&Out, 1, 1, handle);
+}
+
+static void
+readExtHeader(FreeImageIO *io, fi_handle handle, BYTE b) {
+    // Extension header fields
+    // ------------------------
+    // Read the extension header fields
+    // (since we don't use them for the moment, we skip them).
+
+	switch (b & 0x60) {
+		// Type 00: read multi-byte bitfield
+
+		case 0x00:
+		{
+			DWORD info = multiByteRead(io, handle);
+			break;
+		}		
+
+		// Type 11: read a sequence of parameter/value pairs.
+
+		case 0x60:
+		{
+			BYTE sizeParamIdent = (b & 0x70) >> 4;	// Size of Parameter Identifier (in bytes)
+			BYTE sizeParamValue = (b & 0x0F);		// Size of Parameter Value (in bytes)
+			
+			BYTE *Ident = (BYTE*)malloc(sizeParamIdent * sizeof(BYTE));
+			BYTE *Value = (BYTE*)malloc(sizeParamValue * sizeof(BYTE));
+		
+			io->read_proc(Ident, sizeParamIdent, 1, handle);
+			io->read_proc(Value, sizeParamValue, 1, handle);
+			
+			free(Ident);
+			free(Value);
+			break;
+		}		
+
+		// reserved for future use
+
+		case 0x20:	// Type 01
+		case 0x40:	// Type 10
+			break;
+	}
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "WBMP";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Wireless Bitmap";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "wap,wbmp,wbm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/vnd.wap.wbmp";
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+		(depth == 1)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	WORD x, y, width, height;
+	FIBITMAP *dib;
+    BYTE *bits;		// pointer to dib data
+	RGBQUAD *pal;	// pointer to dib palette
+
+	WBMPHEADER header;
+
+	if (handle) {
+		try {
+			// Read header information
+			// -----------------------
+
+			// Type
+
+			header.TypeField = (WORD)multiByteRead(io, handle);
+
+			if (header.TypeField != 0) {
+				throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+			}
+
+			// FixHeaderField
+
+			io->read_proc(&header.FixHeaderField, 1, 1, handle);
+
+			// ExtHeaderFields
+			// 1 = more will follow, 0 = last octet
+
+			if (header.FixHeaderField & 0x80) {
+				header.ExtHeaderFields = 0x80;
+
+				while(header.ExtHeaderFields & 0x80) {
+					io->read_proc(&header.ExtHeaderFields, 1, 1, handle);
+
+					readExtHeader(io, handle, header.ExtHeaderFields);
+				}
+			}
+
+			// width & height
+
+			width  = (WORD)multiByteRead(io, handle);
+			height = (WORD)multiByteRead(io, handle);
+
+			// Allocate a new dib
+
+			dib = FreeImage_Allocate(width, height, 1);
+			if (!dib) {
+				throw FI_MSG_ERROR_DIB_MEMORY;
+			}
+
+			// write the palette data
+
+			pal = FreeImage_GetPalette(dib);
+			pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+			pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+
+			// read the bitmap data
+			
+			int line = FreeImage_GetLine(dib);
+
+			for (y = 0; y < height; y++) {
+				bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+				for (x = 0; x < line; x++) {
+					io->read_proc(&bits[x], 1, 1, handle);
+				}
+			}
+
+			return dib;
+
+		} catch(const char *text)  {
+			FreeImage_OutputMessageProc(s_format_id, text);
+
+			return NULL;
+		}
+
+	}
+
+	return NULL;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+    BYTE *bits;	// pointer to dib data
+
+	if ((dib) && (handle)) {
+		try {
+			if (FreeImage_GetBPP(dib) != 1)
+				throw "Only 1-bit depth bitmaps can be saved as WBMP";
+
+			// write the header
+
+			WBMPHEADER header;
+			header.TypeField = 0;								// Type 0: B/W, no compression
+			header.FixHeaderField = 0;							// No ExtHeaderField
+			header.Width = (WORD)FreeImage_GetWidth(dib);		// Image width
+			header.Height = (WORD)FreeImage_GetHeight(dib);		// Image height
+
+			multiByteWrite(io, handle, header.TypeField);
+			
+			io->write_proc(&header.FixHeaderField, 1, 1, handle);
+
+			multiByteWrite(io, handle, header.Width);
+			multiByteWrite(io, handle, header.Height);
+
+			// write the bitmap data
+
+			WORD linelength = (WORD)FreeImage_GetLine(dib);
+
+			for (WORD y = 0; y < header.Height; y++) {
+				bits = FreeImage_GetScanLine(dib, header.Height - 1 - y);
+
+				io->write_proc(&bits[0], linelength, 1, handle);
+			}
+
+			return TRUE;
+
+		} catch (const char* text) {
+			FreeImage_OutputMessageProc(s_format_id, text);
+		}
+	}
+
+	return FALSE;
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitWBMP(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = NULL;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/files/Source/FreeImage/PluginWebP.cpp b/files/Source/FreeImage/PluginWebP.cpp
new file mode 100644
index 0000000..8be6873
--- /dev/null
+++ b/files/Source/FreeImage/PluginWebP.cpp
@@ -0,0 +1,696 @@
+// ==========================================================
+// Google WebP Loader & Writer
+//
+// Design and implementation by
+// - Herve 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 "../Metadata/FreeImageTag.h"
+
+#include "third_party/libwebp/webp.h"
+#include "third_party/libwebp/webp/mux.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ----------------------------------------------------------
+//   Helpers for the load function
+// ----------------------------------------------------------
+
+/**
+Read the whole file into memory
+*/
+static BOOL
+ReadFileToWebPData(FreeImageIO *io, fi_handle handle, WebPData * const bitstream) {
+  uint8_t *raw_data = NULL;
+
+  try {
+	  // Read the input file and put it in memory
+	  long start_pos = io->tell_proc(handle);
+	  io->seek_proc(handle, 0, SEEK_END);
+	  size_t file_length = (size_t)(io->tell_proc(handle) - start_pos);
+	  io->seek_proc(handle, start_pos, SEEK_SET);
+	  raw_data = (uint8_t*)malloc(file_length * sizeof(uint8_t));
+	  if(!raw_data) {
+		  throw FI_MSG_ERROR_MEMORY;
+	  }
+	  if(io->read_proc(raw_data, 1, (unsigned)file_length, handle) != file_length) {
+		  throw "Error while reading input stream";
+	  }
+	  
+	  // copy pointers (must be released later using free)
+	  bitstream->bytes = raw_data;
+	  bitstream->size = file_length;
+
+	  return TRUE;
+
+  } catch(const char *text) {
+	  if(raw_data) {
+		  free(raw_data);
+	  }
+	  memset(bitstream, 0, sizeof(WebPData));
+	  if(NULL != text) {
+		  FreeImage_OutputMessageProc(s_format_id, text);
+	  }
+	  return FALSE;
+  }
+}
+
+// ----------------------------------------------------------
+//   Helpers for the save function
+// ----------------------------------------------------------
+
+/**
+Output function. Should return 1 if writing was successful.
+data/data_size is the segment of data to write, and 'picture' is for
+reference (and so one can make use of picture->custom_ptr).
+*/
+static int 
+WebP_MemoryWriter(const BYTE *data, size_t data_size, const WebPPicture* const picture) {
+	FIMEMORY *hmem = (FIMEMORY*)picture->custom_ptr;
+	return data_size ? (FreeImage_WriteMemory(data, 1, (unsigned)data_size, hmem) == data_size) : 0;
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "WebP";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "Google WebP image format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "webp";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/webp";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	BYTE riff_signature[4] = { 0x52, 0x49, 0x46, 0x46 };
+	BYTE webp_signature[4] = { 0x57, 0x45, 0x42, 0x50 };
+	BYTE signature[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	io->read_proc(signature, 1, 12, handle);
+
+	if(memcmp(riff_signature, signature, 4) == 0) {
+		if(memcmp(webp_signature, signature + 8, 4) == 0) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+		(depth == 24) || 
+		(depth == 32)
+		);
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+	return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+	WebPMux *mux = NULL;
+	int copy_data = 1;	// 1 : copy data into the mux, 0 : keep a link to local data
+
+	if(read) {
+		// create the MUX object from the input stream
+		WebPData bitstream;
+		// read the input file and put it in memory
+		if(!ReadFileToWebPData(io, handle, &bitstream)) {
+			return NULL;
+		}
+		// create the MUX object
+		mux = WebPMuxCreate(&bitstream, copy_data);
+		// no longer needed since copy_data == 1
+		free((void*)bitstream.bytes);
+		if(mux == NULL) {
+			FreeImage_OutputMessageProc(s_format_id, "Failed to create mux object from file");
+			return NULL;
+		}
+	} else {
+		// creates an empty mux object
+		mux = WebPMuxNew();
+		if(mux == NULL) {
+			FreeImage_OutputMessageProc(s_format_id, "Failed to create empty mux object");
+			return NULL;
+		}
+	}
+	
+	return mux;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+	WebPMux *mux = (WebPMux*)data;
+	if(mux != NULL) {
+		// free the MUX object
+		WebPMuxDelete(mux);
+	}
+}
+
+// ----------------------------------------------------------
+
+/**
+Decode a WebP image and returns a FIBITMAP image
+@param webp_image Raw WebP image
+@param flags FreeImage load flags
+@return Returns a dib if successfull, returns NULL otherwise
+*/
+static FIBITMAP *
+DecodeImage(WebPData *webp_image, int flags) {
+	FIBITMAP *dib = NULL;
+
+	const uint8_t* data = webp_image->bytes;	// raw image data
+	const size_t data_size = webp_image->size;	// raw image size
+
+    VP8StatusCode webp_status = VP8_STATUS_OK;
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+	// Main object storing the configuration for advanced decoding
+	WebPDecoderConfig decoder_config;
+	// Output buffer
+	WebPDecBuffer* const output_buffer = &decoder_config.output;
+	// Features gathered from the bitstream
+	WebPBitstreamFeatures* const bitstream = &decoder_config.input;
+
+	try {
+		// Initialize the configuration as empty
+		// This function must always be called first, unless WebPGetFeatures() is to be called
+		if(!WebPInitDecoderConfig(&decoder_config)) {
+			throw "Library version mismatch";
+		}
+
+		// Retrieve features from the bitstream
+		// The bitstream structure is filled with information gathered from the bitstream
+		webp_status = WebPGetFeatures(data, data_size, bitstream);
+		if(webp_status != VP8_STATUS_OK) {
+			throw FI_MSG_ERROR_PARSING;
+		}
+
+		// Allocate output dib
+
+		unsigned bpp = bitstream->has_alpha ? 32 : 24;	
+		unsigned width = (unsigned)bitstream->width;
+		unsigned height = (unsigned)bitstream->height;
+
+		dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		if(!dib) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		if(header_only) {
+			WebPFreeDecBuffer(output_buffer);
+			return dib;
+		}
+
+		// --- Set decoding options ---
+
+		// use multi-threaded decoding
+		decoder_config.options.use_threads = 1;
+		// set output color space
+		output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
+
+		// ---
+
+		// decode the input stream, taking 'config' into account. 
+		
+		webp_status = WebPDecode(data, data_size, &decoder_config);
+		if(webp_status != VP8_STATUS_OK) {
+			throw FI_MSG_ERROR_PARSING;
+		}
+
+		// fill the dib with the decoded data
+
+		const BYTE *src_bitmap = output_buffer->u.RGBA.rgba;
+		const unsigned src_pitch = (unsigned)output_buffer->u.RGBA.stride;
+
+		switch(bpp) {
+			case 24:
+				for(unsigned y = 0; y < height; y++) {
+					const BYTE *src_bits = src_bitmap + y * src_pitch;						
+					BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, height-1-y);
+					for(unsigned x = 0; x < width; x++) {
+						dst_bits[FI_RGBA_BLUE]	= src_bits[0];	// B
+						dst_bits[FI_RGBA_GREEN]	= src_bits[1];	// G
+						dst_bits[FI_RGBA_RED]	= src_bits[2];	// R
+						src_bits += 3;
+						dst_bits += 3;
+					}
+				}
+				break;
+			case 32:
+				for(unsigned y = 0; y < height; y++) {
+					const BYTE *src_bits = src_bitmap + y * src_pitch;						
+					BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, height-1-y);
+					for(unsigned x = 0; x < width; x++) {
+						dst_bits[FI_RGBA_BLUE]	= src_bits[0];	// B
+						dst_bits[FI_RGBA_GREEN]	= src_bits[1];	// G
+						dst_bits[FI_RGBA_RED]	= src_bits[2];	// R
+						dst_bits[FI_RGBA_ALPHA]	= src_bits[3];	// A
+						src_bits += 4;
+						dst_bits += 4;
+					}
+				}
+				break;
+		}
+
+		// Free the decoder
+		WebPFreeDecBuffer(output_buffer);
+
+		return dib;
+
+	} catch (const char *text) {
+		if(dib) {
+			FreeImage_Unload(dib);
+		}
+		WebPFreeDecBuffer(output_buffer);
+
+		if(NULL != text) {
+			FreeImage_OutputMessageProc(s_format_id, text);
+		}
+
+		return NULL;
+	}
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	WebPMux *mux = NULL;
+	WebPMuxFrameInfo webp_frame = { 0 };	// raw image
+	WebPData color_profile;	// ICC raw data
+	WebPData xmp_metadata;	// XMP raw data
+	WebPData exif_metadata;	// EXIF raw data
+	FIBITMAP *dib = NULL;
+	WebPMuxError error_status;
+
+	if(!handle) {
+		return NULL;
+	}
+
+	try {
+		// get the MUX object
+		mux = (WebPMux*)data;
+		if(!mux) {
+			throw (1);
+		}
+		
+		// gets the feature flags from the mux object
+		uint32_t webp_flags = 0;
+		error_status = WebPMuxGetFeatures(mux, &webp_flags);
+		if(error_status != WEBP_MUX_OK) {
+			throw (1);
+		}
+
+		// get image data
+		error_status = WebPMuxGetFrame(mux, 1, &webp_frame);
+
+		if(error_status == WEBP_MUX_OK) {
+			// decode the data (can be limited to the header if flags uses FIF_LOAD_NOPIXELS)
+			dib = DecodeImage(&webp_frame.bitstream, flags);
+			if(!dib) {
+				throw (1);
+			}
+			
+			// get ICC profile
+			if(webp_flags & ICCP_FLAG) {
+				error_status = WebPMuxGetChunk(mux, "ICCP", &color_profile);
+				if(error_status == WEBP_MUX_OK) {
+					FreeImage_CreateICCProfile(dib, (void*)color_profile.bytes, (long)color_profile.size);
+				}
+			}
+
+			// get XMP metadata
+			if(webp_flags & XMP_FLAG) {
+				error_status = WebPMuxGetChunk(mux, "XMP ", &xmp_metadata);
+				if(error_status == WEBP_MUX_OK) {
+					// create a tag
+					FITAG *tag = FreeImage_CreateTag();
+					if(tag) {
+						FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
+						FreeImage_SetTagLength(tag, (DWORD)xmp_metadata.size);
+						FreeImage_SetTagCount(tag, (DWORD)xmp_metadata.size);
+						FreeImage_SetTagType(tag, FIDT_ASCII);
+						FreeImage_SetTagValue(tag, xmp_metadata.bytes);
+						
+						// store the tag
+						FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
+
+						// destroy the tag
+						FreeImage_DeleteTag(tag);
+					}
+				}
+			}
+
+			// get Exif metadata
+			if(webp_flags & EXIF_FLAG) {
+				error_status = WebPMuxGetChunk(mux, "EXIF", &exif_metadata);
+				if(error_status == WEBP_MUX_OK) {
+					// read the Exif raw data as a blob
+					jpeg_read_exif_profile_raw(dib, exif_metadata.bytes, (unsigned)exif_metadata.size);
+					// read and decode the Exif data
+					jpeg_read_exif_profile(dib, exif_metadata.bytes, (unsigned)exif_metadata.size);
+				}
+			}
+		}
+
+		WebPDataClear(&webp_frame.bitstream);
+
+		return dib;
+
+	} catch(int) {
+		WebPDataClear(&webp_frame.bitstream);
+		return NULL;
+	}
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Encode a FIBITMAP to a WebP image
+@param hmem Memory output stream, containing on return the encoded image
+@param dib The FIBITMAP to encode
+@param flags FreeImage save flags
+@return Returns TRUE if successfull, returns FALSE otherwise
+*/
+static BOOL
+EncodeImage(FIMEMORY *hmem, FIBITMAP *dib, int flags) {
+	WebPPicture picture;	// Input buffer
+	WebPConfig config;		// Coding parameters
+
+	BOOL bIsFlipped = FALSE;
+
+	try {
+		const unsigned width = FreeImage_GetWidth(dib);
+		const unsigned height = FreeImage_GetHeight(dib);
+		const unsigned bpp = FreeImage_GetBPP(dib);
+		const unsigned pitch = FreeImage_GetPitch(dib);
+
+		// check image type
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+		if( !((image_type == FIT_BITMAP) && ((bpp == 24) || (bpp == 32))) )  {
+			throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+		}
+
+		// check format limits
+		if(MAX(width, height) > WEBP_MAX_DIMENSION) {
+			FreeImage_OutputMessageProc(s_format_id, "Unsupported image size: width x height = %d x %d", width, height);
+			return FALSE;
+		}
+
+		// Initialize output I/O
+		if(WebPPictureInit(&picture) == 1) {
+			picture.writer = WebP_MemoryWriter;
+			picture.custom_ptr = hmem;
+			picture.width = (int)width;
+			picture.height = (int)height;
+		} else {
+			throw "Couldn't initialize WebPPicture";
+		}
+
+		// --- Set encoding parameters ---
+
+		// Initialize encoding parameters to default values
+		WebPConfigInit(&config);
+
+		// quality/speed trade-off (0=fast, 6=slower-better)
+		config.method = 6;
+
+		if((flags & WEBP_LOSSLESS) == WEBP_LOSSLESS) {
+			// lossless encoding
+			config.lossless = 1;
+			picture.use_argb = 1;
+		} else if((flags & 0x7F) > 0) {
+			// lossy encoding
+			config.lossless = 0;
+			// quality is between 1 (smallest file) and 100 (biggest) - default to 75
+			config.quality = (float)(flags & 0x7F);
+			if(config.quality > 100) {
+				config.quality = 100;
+			}
+		}
+
+		// validate encoding parameters
+		if(WebPValidateConfig(&config) == 0) {
+			throw "Failed to initialize encoder";
+		}
+
+		// --- Perform encoding ---
+		
+		// Invert dib scanlines
+		bIsFlipped = FreeImage_FlipVertical(dib);
+
+
+		// convert dib buffer to output stream
+
+		const BYTE *bits = FreeImage_GetBits(dib);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+		switch(bpp) {
+			case 24:
+				WebPPictureImportBGR(&picture, bits, pitch);
+				break;
+			case 32:
+				WebPPictureImportBGRA(&picture, bits, pitch);
+				break;
+		}
+#else
+		switch(bpp) {
+			case 24:
+				WebPPictureImportRGB(&picture, bits, pitch);
+				break;
+			case 32:
+				WebPPictureImportRGBA(&picture, bits, pitch);
+				break;
+		}
+
+#endif // FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+
+		if(!WebPEncode(&config, &picture)) {
+			throw "Failed to encode image";
+		}
+
+		WebPPictureFree(&picture);
+
+		if(bIsFlipped) {
+			// invert dib scanlines
+			FreeImage_FlipVertical(dib);
+		}
+
+		return TRUE;
+
+	} catch (const char* text) {
+
+		WebPPictureFree(&picture);
+
+		if(bIsFlipped) {
+			// invert dib scanlines
+			FreeImage_FlipVertical(dib);
+		}
+
+		if(NULL != text) {
+			FreeImage_OutputMessageProc(s_format_id, text);
+		}
+	}
+
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	WebPMux *mux = NULL;
+	FIMEMORY *hmem = NULL;
+	WebPData webp_image;
+	WebPData output_data = { 0 };
+	WebPMuxError error_status;
+
+	int copy_data = 1;	// 1 : copy data into the mux, 0 : keep a link to local data
+
+	if(!dib || !handle || !data) {
+		return FALSE;
+	}
+
+	try {
+
+		// get the MUX object
+		mux = (WebPMux*)data;
+		if(!mux) {
+			return FALSE;
+		}
+
+		// --- prepare image data ---
+
+		// encode image as a WebP blob
+		hmem = FreeImage_OpenMemory();
+		if(!hmem || !EncodeImage(hmem, dib, flags)) {
+			throw (1);
+		}
+		// store the blob into the mux
+		BYTE *data = NULL;
+		DWORD data_size = 0;
+		FreeImage_AcquireMemory(hmem, &data, &data_size);
+		webp_image.bytes = data;
+		webp_image.size = data_size;
+		error_status = WebPMuxSetImage(mux, &webp_image, copy_data);
+		// no longer needed since copy_data == 1
+		FreeImage_CloseMemory(hmem);
+		hmem = NULL;
+		if(error_status != WEBP_MUX_OK) {
+			throw (1);
+		}
+
+		// --- set metadata ---
+		
+		// set ICC color profile
+		{
+			FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
+			if (iccProfile->size && iccProfile->data) {
+				WebPData icc_profile;
+				icc_profile.bytes = (uint8_t*)iccProfile->data;
+				icc_profile.size = (size_t)iccProfile->size;
+				error_status = WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
+				if(error_status != WEBP_MUX_OK) {
+					throw (1);
+				}
+			}
+		}
+
+		// set XMP metadata
+		{
+			FITAG *tag = NULL;
+			if(FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag)) {
+				WebPData xmp_profile;
+				xmp_profile.bytes = (uint8_t*)FreeImage_GetTagValue(tag);
+				xmp_profile.size = (size_t)FreeImage_GetTagLength(tag);
+				error_status = WebPMuxSetChunk(mux, "XMP ", &xmp_profile, copy_data);
+				if(error_status != WEBP_MUX_OK) {
+					throw (1);
+				}
+			}
+		}
+
+		// set Exif metadata
+		{
+			FITAG *tag = NULL;
+			if(FreeImage_GetMetadata(FIMD_EXIF_RAW, dib, g_TagLib_ExifRawFieldName, &tag)) {
+				WebPData exif_profile;
+				exif_profile.bytes = (uint8_t*)FreeImage_GetTagValue(tag);
+				exif_profile.size = (size_t)FreeImage_GetTagLength(tag);
+				error_status = WebPMuxSetChunk(mux, "EXIF", &exif_profile, copy_data);
+				if(error_status != WEBP_MUX_OK) {
+					throw (1);
+				}
+			}
+		}
+		
+		// get data from mux in WebP RIFF format
+		error_status = WebPMuxAssemble(mux, &output_data);
+		if(error_status != WEBP_MUX_OK) {
+			FreeImage_OutputMessageProc(s_format_id, "Failed to create webp output file");
+			throw (1);
+		}
+
+		// write the file to the output stream
+		if(io->write_proc((void*)output_data.bytes, 1, (unsigned)output_data.size, handle) != output_data.size) {
+			FreeImage_OutputMessageProc(s_format_id, "Failed to write webp output file");
+			throw (1);
+		}
+
+		// free WebP output file
+		WebPDataClear(&output_data);
+
+		return TRUE;
+
+	} catch(int) {
+		if(hmem) {
+			FreeImage_CloseMemory(hmem);
+		}
+		
+		WebPDataClear(&output_data);
+
+		return FALSE;
+	}
+}
+
+// ==========================================================
+//	 Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitWEBP(Plugin *plugin, int format_id) {
+	s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = Open;
+	plugin->close_proc = Close;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
+
diff --git a/files/Source/FreeImage/PluginXBM.cpp b/files/Source/FreeImage/PluginXBM.cpp
new file mode 100644
index 0000000..0aac48c
--- /dev/null
+++ b/files/Source/FreeImage/PluginXBM.cpp
@@ -0,0 +1,399 @@
+// ==========================================================
+// XBM Loader
+//
+// Design and implementation by
+// - Hervé Drolon <drolon@infonie.fr>
+// part of the code adapted from the netPBM package (xbmtopbm.c)
+//
+// 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 functions
+// ==========================================================
+
+#define MAX_LINE	512
+
+static const char *ERR_XBM_SYNTAX	= "Syntax error";
+static const char *ERR_XBM_LINE		= "Line too long";
+static const char *ERR_XBM_DECL		= "Unable to find a line in the file containing the start of C array declaration (\"static char\" or whatever)";
+static const char *ERR_XBM_EOFREAD	= "EOF / read error";
+static const char *ERR_XBM_WIDTH	= "Invalid width";
+static const char *ERR_XBM_HEIGHT	= "Invalid height";
+static const char *ERR_XBM_MEMORY	= "Out of memory";
+
+/**
+Get a string from a stream. 
+Read the string from the current stream to the first newline character. 
+The result stored in str is appended with a null character.
+@param str Storage location for data 
+@param n Maximum number of characters to read 
+@param io Pointer to the FreeImageIO structure
+@param handle Handle to the stream
+@return Returns str. NULL is returned to indicate an error or an end-of-file condition.
+*/
+static char* 
+readLine(char *str, int n, FreeImageIO *io, fi_handle handle) {
+	char c;
+	int count, i = 0;
+	do {
+		count = io->read_proc(&c, 1, 1, handle);
+		str[i++] = c;
+	} while((c != '\n') && (i < n));
+	if(count <= 0)
+		return NULL;
+	str[i] = '\0';
+	return str;
+}
+
+/**
+Get a char from the stream
+@param io Pointer to the FreeImageIO structure
+@param handle Handle to the stream
+@return Returns the next character in the stream
+*/
+static int 
+readChar(FreeImageIO *io, fi_handle handle) {
+	BYTE c;
+	io->read_proc(&c, 1, 1, handle);
+	return c;
+}
+
+/**
+Read an XBM file into a buffer
+@param io Pointer to the FreeImageIO structure
+@param handle Handle to the stream
+@param widthP (return value) Pointer to the bitmap width
+@param heightP (return value) Pointer to the bitmap height
+@param dataP (return value) Pointer to the bitmap buffer
+@return Returns NULL if OK, returns an error message otherwise
+*/
+static const char* 
+readXBMFile(FreeImageIO *io, fi_handle handle, int *widthP, int *heightP, char **dataP) {
+	char line[MAX_LINE], name_and_type[MAX_LINE];
+	char* ptr;
+	char* t;
+	int version = 0;
+	int raster_length, v;
+	int bytes, bytes_per_line, padding;
+	int c1, c2, value1, value2;
+	int hex_table[256];
+	BOOL found_declaration;
+	/* in scanning through the bitmap file, we have found the first
+	 line of the C declaration of the array (the "static char ..."
+	 or whatever line)
+	 */
+	BOOL eof;	// we've encountered end of file while searching file
+
+	*widthP = *heightP = -1;
+
+	found_declaration = FALSE;    // haven't found it yet; haven't even looked
+	eof = FALSE;                  // haven't encountered end of file yet 
+	
+	while(!found_declaration && !eof) {
+
+		if( readLine(line, MAX_LINE, io, handle) == NULL) {
+			eof = TRUE;
+		}
+		else {
+			if( strlen( line ) == MAX_LINE - 1 )
+				return( ERR_XBM_LINE );
+			if( sscanf(line, "#define %s %d", name_and_type, &v) == 2 ) {
+				if( ( t = strrchr( name_and_type, '_' ) ) == NULL )
+					t = name_and_type;
+				else
+					t++;
+				if ( ! strcmp( "width", t ) )
+					*widthP = v;
+				else if ( ! strcmp( "height", t ) )
+					*heightP = v;
+				continue;
+			}
+
+			if( sscanf( line, "static short %s = {", name_and_type ) == 1 ) {
+				version = 10;
+				found_declaration = TRUE;
+			}
+			else if( sscanf( line, "static char %s = {", name_and_type ) == 1 ) {
+				version = 11;
+				found_declaration = TRUE;
+			}
+			else if(sscanf(line, "static unsigned char %s = {", name_and_type ) == 1 ) {
+				version = 11;
+				found_declaration = TRUE;
+			}
+		}
+	}
+
+	if(!found_declaration) 
+		return( ERR_XBM_DECL );
+
+	if(*widthP == -1 )
+		return( ERR_XBM_WIDTH );
+	if( *heightP == -1 )
+		return( ERR_XBM_HEIGHT );
+
+	padding = 0;
+	if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && (version == 10) )
+		padding = 1;
+
+	bytes_per_line = (*widthP + 7) / 8 + padding;
+
+	raster_length =  bytes_per_line * *heightP;
+	*dataP = (char*) malloc( raster_length );
+	if ( *dataP == (char*) 0 )
+		return( ERR_XBM_MEMORY );
+
+	// initialize hex_table
+	for ( c1 = 0; c1 < 256; c1++ ) {
+		hex_table[c1] = 256;
+	}
+	hex_table['0'] = 0;
+	hex_table['1'] = 1;
+	hex_table['2'] = 2;
+	hex_table['3'] = 3;
+	hex_table['4'] = 4;
+	hex_table['5'] = 5;
+	hex_table['6'] = 6;
+	hex_table['7'] = 7;
+	hex_table['8'] = 8;
+	hex_table['9'] = 9;
+	hex_table['A'] = 10;
+	hex_table['B'] = 11;
+	hex_table['C'] = 12;
+	hex_table['D'] = 13;
+	hex_table['E'] = 14;
+	hex_table['F'] = 15;
+	hex_table['a'] = 10;
+	hex_table['b'] = 11;
+	hex_table['c'] = 12;
+	hex_table['d'] = 13;
+	hex_table['e'] = 14;
+	hex_table['f'] = 15;
+
+	if(version == 10) {
+		for( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) {
+			while( ( c1 = readChar(io, handle) ) != 'x' ) {
+				if ( c1 == EOF )
+					return( ERR_XBM_EOFREAD );
+			}
+
+			c1 = readChar(io, handle);
+			c2 = readChar(io, handle);
+			if( c1 == EOF || c2 == EOF )
+				return( ERR_XBM_EOFREAD );
+			value1 = ( hex_table[c1] << 4 ) + hex_table[c2];
+			if ( value1 >= 256 )
+				return( ERR_XBM_SYNTAX );
+			c1 = readChar(io, handle);
+			c2 = readChar(io, handle);
+			if( c1 == EOF || c2 == EOF )
+				return( ERR_XBM_EOFREAD );
+			value2 = ( hex_table[c1] << 4 ) + hex_table[c2];
+			if ( value2 >= 256 )
+				return( ERR_XBM_SYNTAX );
+			*ptr++ = (char)value2;
+			if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) )
+				*ptr++ = (char)value1;
+		}
+	}
+	else {
+		for(bytes = 0, ptr = *dataP; bytes < raster_length; bytes++ ) {
+			/*
+			** skip until digit is found
+			*/
+			for( ; ; ) {
+				c1 = readChar(io, handle);
+				if ( c1 == EOF )
+					return( ERR_XBM_EOFREAD );
+				value1 = hex_table[c1];
+				if ( value1 != 256 )
+					break;
+			}
+			/*
+			** loop on digits
+			*/
+			for( ; ; ) {
+				c2 = readChar(io, handle);
+				if ( c2 == EOF )
+					return( ERR_XBM_EOFREAD );
+				value2 = hex_table[c2];
+				if ( value2 != 256 ) {
+					value1 = (value1 << 4) | value2;
+					if ( value1 >= 256 )
+						return( ERR_XBM_SYNTAX );
+				}
+				else if ( c2 == 'x' || c2 == 'X' ) {
+					if ( value1 == 0 )
+						continue;
+					else return( ERR_XBM_SYNTAX );
+				}
+				else break;
+			}
+			*ptr++ = (char)value1;
+		}
+	}
+
+	return NULL;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "XBM";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "X11 Bitmap Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "xbm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-xbitmap";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	char magic[8];
+	if(readLine(magic, 7, io, handle)) {
+		if(strcmp(magic, "#define") == 0)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV 
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return FALSE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	char *buffer = NULL;
+	int width, height;
+	FIBITMAP *dib = NULL;
+
+	try {
+
+		// load the bitmap data
+		const char* error = readXBMFile(io, handle, &width, &height, &buffer);
+		// Microsoft doesn't implement throw between functions :(
+		if(error) throw (char*)error;
+
+
+		// allocate a new dib
+		dib = FreeImage_Allocate(width, height, 1);
+		if(!dib) throw (char*)ERR_XBM_MEMORY;
+
+		// write the palette data
+		RGBQUAD *pal = FreeImage_GetPalette(dib);
+		pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+		pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+
+		// copy the bitmap
+		BYTE *bP = (BYTE*)buffer;
+		for(int y = 0; y < height; y++) {
+			BYTE count = 0;
+			BYTE mask = 1;
+			BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+			for(int x = 0; x < width; x++) {
+				if(count >= 8) {
+					bP++;
+					count = 0;
+					mask = 1;
+				}
+				if(*bP & mask) {
+					// Set bit(x, y) to 0
+					bits[x >> 3] &= (0xFF7F >> (x & 0x7));
+				} else {
+					// Set bit(x, y) to 1
+					bits[x >> 3] |= (0x80 >> (x & 0x7));
+				}
+				count++;
+				mask <<= 1;
+			}
+			bP++;
+		}
+
+		free(buffer);
+		return dib;
+
+	} catch(const char *text) {
+		if(buffer)	free(buffer);
+		if(dib)		FreeImage_Unload(dib);
+		FreeImage_OutputMessageProc(s_format_id, text);
+		return NULL;
+	}
+}
+
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitXBM(Plugin *plugin, int format_id) {
+    s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = NULL;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+}
+
diff --git a/files/Source/FreeImage/PluginXPM.cpp b/files/Source/FreeImage/PluginXPM.cpp
new file mode 100644
index 0000000..81a733b
--- /dev/null
+++ b/files/Source/FreeImage/PluginXPM.cpp
@@ -0,0 +1,487 @@
+// ==========================================================
+// XPM Loader and Writer
+//
+// Design and implementation by
+// - Ryan Rubley (ryan@lostreality.org)
+//
+// 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
+
+// IMPLEMENTATION NOTES:
+// ------------------------
+// Initial design and implementation by
+// - Karl-Heinz Bussian (khbussian@moss.de)
+// - Hervé Drolon (drolon@infonie.fr)
+// Completely rewritten from scratch by Ryan Rubley (ryan@lostreality.org)
+// in order to address the following major fixes:
+// * Supports any number of chars per pixel (not just 1 or 2)
+// * Files with 2 chars per pixel but <= 256colors are loaded as 256 color (not 24bit)
+// * Loads much faster, uses much less memory
+// * supports #rgb #rrrgggbbb and #rrrrggggbbbb colors (not just #rrggbb)
+// * supports symbolic color names
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+static int s_format_id;
+
+// ==========================================================
+// Internal Functions
+// ==========================================================
+
+// read in and skip all junk until we find a certain char
+static BOOL
+FindChar(FreeImageIO *io, fi_handle handle, BYTE look_for) {
+	BYTE c = '\0';
+	io->read_proc(&c, sizeof(BYTE), 1, handle);
+	while(c != look_for) {
+		if( io->read_proc(&c, sizeof(BYTE), 1, handle) != 1 )
+			return FALSE;
+	}
+	return TRUE;
+}
+
+// find start of string, read data until ending quote found, allocate memory and return a string
+static char *
+ReadString(FreeImageIO *io, fi_handle handle) {
+	if( !FindChar(io, handle,'"') )
+		return NULL;
+	BYTE c;
+	std::string s;
+	io->read_proc(&c, sizeof(BYTE), 1, handle);
+	while(c != '"') {
+		s += c;
+		if( io->read_proc(&c, sizeof(BYTE), 1, handle) != 1 )
+			return NULL;
+	}
+	char *cstr = (char *)malloc(s.length()+1);
+	strcpy(cstr,s.c_str());
+	return cstr;
+}
+
+static char *
+Base92(unsigned int num) {
+	static char b92[16]; //enough for more then 64 bits
+	static char digit[] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjklzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
+	b92[15] = '\0';
+	int i = 14;
+	do {
+		b92[i--] = digit[num % 92];
+		num /= 92;
+	} while( num && i >= 0 );
+	return b92+i+1;
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+	return "XPM";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+	return "X11 Pixmap Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+	return "xpm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+	return "^[ \\t]*/\\* XPM \\*/[ \\t]$";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+	return "image/x-xpixmap";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+	char buffer[256];
+
+	// checks the first 256 characters for the magic string
+	int count = io->read_proc(buffer, 1, 256, handle);
+	if(count <= 9) return FALSE;
+	for(int i = 0; i < (count - 9); i++) {
+		if(strncmp(&buffer[i], "/* XPM */", 9) == 0)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+	return (
+			(depth == 8) ||
+			(depth == 24)
+		);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+	return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+	char msg[256];
+    FIBITMAP *dib = NULL;
+
+    if (!handle) return NULL;
+
+    try {
+		char *str;
+		
+		BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+		
+		//find the starting brace
+		if( !FindChar(io, handle,'{') )
+			throw "Could not find starting brace";
+
+		//read info string
+		str = ReadString(io, handle);
+		if(!str)
+			throw "Error reading info string";
+
+		int width, height, colors, cpp;
+		if( sscanf(str, "%d %d %d %d", &width, &height, &colors, &cpp) != 4 ) {
+			free(str);
+			throw "Improperly formed info string";
+		}
+		free(str);
+
+        if (colors > 256) {
+			dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		} else {
+			dib = FreeImage_AllocateHeader(header_only, width, height, 8);
+		}
+
+		//build a map of color chars to rgb values
+		std::map<std::string,FILE_RGBA> rawpal; //will store index in Alpha if 8bpp
+		for(int i = 0; i < colors; i++ ) {
+			FILE_RGBA rgba;
+
+			str = ReadString(io, handle);
+			if(!str)
+				throw "Error reading color strings";
+
+			std::string chrs(str,cpp); //create a string for the color chars using the first cpp chars
+			char *keys = str + cpp; //the color keys for these chars start after the first cpp chars
+
+			//translate all the tabs to spaces
+			char *tmp = keys;
+			while( strchr(tmp,'\t') ) {
+				tmp = strchr(tmp,'\t');
+				*tmp++ = ' ';
+			}
+
+			//prefer the color visual
+			if( strstr(keys," c ") ) {
+				char *clr = strstr(keys," c ") + 3;
+				while( *clr == ' ' ) clr++; //find the start of the hex rgb value
+				if( *clr == '#' ) {
+					int red = 0, green = 0, blue = 0, n;
+					clr++;
+					//end string at first space, if any found
+					if( strchr(clr,' ') )
+						*(strchr(clr,' ')) = '\0';
+					//parse hex color, it can be #rgb #rrggbb #rrrgggbbb or #rrrrggggbbbb
+					switch( strlen(clr) ) {
+						case 3:	n = sscanf(clr,"%01x%01x%01x",&red,&green,&blue);
+							red |= (red << 4);
+							green |= (green << 4);
+							blue |= (blue << 4);
+							break;
+						case 6:	n = sscanf(clr,"%02x%02x%02x",&red,&green,&blue);
+							break;
+						case 9:	n = sscanf(clr,"%03x%03x%03x",&red,&green,&blue);
+							red >>= 4;
+							green >>= 4;
+							blue >>= 4;
+							break;
+						case 12: n = sscanf(clr,"%04x%04x%04x",&red,&green,&blue);
+							red >>= 8;
+							green >>= 8;
+							blue >>= 8;
+							break;
+						default:
+							n = 0;
+							break;
+					}
+					if( n != 3 ) {
+						free(str);
+						throw "Improperly formed hex color value";
+					}
+					rgba.r = (BYTE)red;
+					rgba.g = (BYTE)green;
+					rgba.b = (BYTE)blue;
+				} else if( !strncmp(clr,"None",4) || !strncmp(clr,"none",4) ) {
+					rgba.r = rgba.g = rgba.b = 0xFF;
+				} else {
+					char *tmp = clr;
+
+					//scan forward for each space, if its " x " or " xx " end the string there
+					//this means its probably some other visual data beyond that point and not
+					//part of the color name.  How many named color end with a 1 or 2 character
+					//word? Probably none in our list at least.
+					while( (tmp = strchr(tmp,' ')) != NULL ) {
+						if( tmp[1] != ' ' ) {
+							if( (tmp[2] == ' ') || (tmp[2] != ' ' && tmp[3] == ' ') ) {
+								tmp[0] = '\0';
+								break;
+							}
+						}
+						tmp++;
+					}
+
+					//remove any trailing spaces
+					tmp = clr+strlen(clr)-1;
+					while( *tmp == ' ' ) {
+						*tmp = '\0';
+						tmp--;
+					}
+
+					if (!FreeImage_LookupX11Color(clr,  &rgba.r, &rgba.g, &rgba.b)) {
+						sprintf(msg, "Unknown color name '%s'", str);
+						free(str);
+						throw msg;
+					}
+				}
+			} else {
+				free(str);
+				throw "Only color visuals are supported";
+			}
+
+			//add color to map
+			rgba.a = (BYTE)((colors > 256) ? 0 : i);
+			rawpal[chrs] = rgba;
+
+			//build palette if needed
+			if( colors <= 256 ) {
+				RGBQUAD *pal = FreeImage_GetPalette(dib);
+				pal[i].rgbBlue = rgba.b;
+				pal[i].rgbGreen = rgba.g;
+				pal[i].rgbRed = rgba.r;
+			}
+
+			free(str);
+		}
+		//done parsing color map
+
+		if(header_only) {
+			// header only mode
+			return dib;
+		}
+
+		//read in pixel data
+		for(int y = 0; y < height; y++ ) {
+			BYTE *line = FreeImage_GetScanLine(dib, height - y - 1);
+			str = ReadString(io, handle);
+			if(!str)
+				throw "Error reading pixel strings";
+			char *pixel_ptr = str;
+
+			for(int x = 0; x < width; x++ ) {
+				//locate the chars in the color map
+				std::string chrs(pixel_ptr,cpp);
+				FILE_RGBA rgba = rawpal[chrs];
+
+				if( colors > 256 ) {
+					line[FI_RGBA_BLUE] = rgba.b;
+					line[FI_RGBA_GREEN] = rgba.g;
+					line[FI_RGBA_RED] = rgba.r;
+					line += 3;
+				} else {
+					*line = rgba.a;
+					line++;
+				}
+
+				pixel_ptr += cpp;
+			}
+
+			free(str);
+		}
+		//done reading pixel data
+
+		return dib;
+	} catch(const char *text) {
+       FreeImage_OutputMessageProc(s_format_id, text);
+
+       if( dib != NULL )
+           FreeImage_Unload(dib);
+
+       return NULL;
+    }
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+	if ((dib != NULL) && (handle != NULL)) {
+		char header[] = "/* XPM */\nstatic char *freeimage[] = {\n/* width height num_colors chars_per_pixel */\n\"",
+		start_colors[] = "\",\n/* colors */\n\"",
+		start_pixels[] = "\",\n/* pixels */\n\"",
+		new_line[] = "\",\n\"",
+		footer[] = "\"\n};\n",
+		buf[256]; //256 is more then enough to sprintf 4 ints into, or the base-92 chars and #rrggbb line
+
+		if( io->write_proc(header, (unsigned int)strlen(header), 1, handle) != 1 )
+			return FALSE;
+
+		int width = FreeImage_GetWidth(dib), height = FreeImage_GetHeight(dib), bpp = FreeImage_GetBPP(dib);
+		RGBQUAD *pal = FreeImage_GetPalette(dib);
+		int x,y;
+
+		//map base92 chrs to the rgb value to create the palette
+		std::map<DWORD,FILE_RGB> chrs2color;
+		//map 8bpp index or 24bpp rgb value to the base92 chrs to create pixel data
+		typedef union {
+			DWORD index;
+			FILE_RGBA rgba;
+		} DWORDRGBA;
+		std::map<DWORD,std::string> color2chrs;
+
+		//loop thru entire dib, if new color, inc num_colors and add to both maps
+		int num_colors = 0;
+		for(y = 0; y < height; y++ ) {
+			BYTE *line = FreeImage_GetScanLine(dib, height - y - 1);
+			for(x = 0; x < width; x++ ) {
+				FILE_RGB rgb;
+				DWORDRGBA u;
+				if( bpp > 8 ) {
+					u.rgba.b = rgb.b = line[FI_RGBA_BLUE];
+					u.rgba.g = rgb.g = line[FI_RGBA_GREEN];
+					u.rgba.r = rgb.r = line[FI_RGBA_RED];
+					u.rgba.a = 0;
+					line += 3;
+				} else {
+					u.index = *line;
+					rgb.b = pal[u.index].rgbBlue;
+					rgb.g = pal[u.index].rgbGreen;
+					rgb.r = pal[u.index].rgbRed;
+					line++;
+				}
+				if( color2chrs.find(u.index) == color2chrs.end() ) { //new color
+					std::string chrs(Base92(num_colors));
+					color2chrs[u.index] = chrs;
+					chrs2color[num_colors] = rgb;
+					num_colors++;
+				}
+			}
+		}
+
+		int cpp = (int)(log((double)num_colors)/log(92.0)) + 1;
+
+		sprintf(buf, "%d %d %d %d", FreeImage_GetWidth(dib), FreeImage_GetHeight(dib), num_colors, cpp );
+		if( io->write_proc(buf, (unsigned int)strlen(buf), 1, handle) != 1 )
+			return FALSE;
+
+		if( io->write_proc(start_colors, (unsigned int)strlen(start_colors), 1, handle) != 1 )
+			return FALSE;
+
+		//write colors, using map of chrs->rgb
+		for(x = 0; x < num_colors; x++ ) {
+			sprintf(buf, "%*s c #%02x%02x%02x", cpp, Base92(x), chrs2color[x].r, chrs2color[x].g, chrs2color[x].b );
+			if( io->write_proc(buf, (unsigned int)strlen(buf), 1, handle) != 1 )
+				return FALSE;
+			if( x == num_colors - 1 ) {
+				if( io->write_proc(start_pixels, (unsigned int)strlen(start_pixels), 1, handle) != 1 )
+					return FALSE;
+			} else {
+				if( io->write_proc(new_line, (unsigned int)strlen(new_line), 1, handle) != 1 )
+					return FALSE;
+			}
+		}
+
+
+		//write pixels, using map of rgb(if 24bpp) or index(if 8bpp)->chrs
+		for(y = 0; y < height; y++ ) {
+			BYTE *line = FreeImage_GetScanLine(dib, height - y - 1);
+			for(x = 0; x < width; x++ ) {
+				DWORDRGBA u;
+				if( bpp > 8 ) {
+					u.rgba.b = line[FI_RGBA_BLUE];
+					u.rgba.g = line[FI_RGBA_GREEN];
+					u.rgba.r = line[FI_RGBA_RED];
+					u.rgba.a = 0;
+					line += 3;
+				} else {
+					u.index = *line;
+					line++;
+				}
+				sprintf(buf, "%*s", cpp, (char *)color2chrs[u.index].c_str());
+				if( io->write_proc(buf, cpp, 1, handle) != 1 )
+					return FALSE;
+			}
+			if( y == height - 1 ) {
+				if( io->write_proc(footer, (unsigned int)strlen(footer), 1, handle) != 1 )
+					return FALSE;
+			} else {
+				if( io->write_proc(new_line, (unsigned int)strlen(new_line), 1, handle) != 1 )
+					return FALSE;
+			}
+		}
+
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+// ==========================================================
+//   Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitXPM(Plugin *plugin, int format_id)
+{
+    s_format_id = format_id;
+
+	plugin->format_proc = Format;
+	plugin->description_proc = Description;
+	plugin->extension_proc = Extension;
+	plugin->regexpr_proc = RegExpr;
+	plugin->open_proc = NULL;
+	plugin->close_proc = NULL;
+	plugin->pagecount_proc = NULL;
+	plugin->pagecapability_proc = NULL;
+	plugin->load_proc = Load;
+	plugin->save_proc = Save;
+	plugin->validate_proc = Validate;
+	plugin->mime_proc = MimeType;
+	plugin->supports_export_bpp_proc = SupportsExportDepth;
+	plugin->supports_export_type_proc = SupportsExportType;
+	plugin->supports_icc_profiles_proc = NULL;
+	plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
+
diff --git a/files/Source/FreeImage/TIFFLogLuv.cpp b/files/Source/FreeImage/TIFFLogLuv.cpp
new file mode 100644
index 0000000..124b25f
--- /dev/null
+++ b/files/Source/FreeImage/TIFFLogLuv.cpp
@@ -0,0 +1,65 @@
+// ==========================================================
+// XYZ to RGB TIFF 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"
+
+void tiff_ConvertLineXYZToRGB(BYTE *target, BYTE *source, double stonits, int width_in_pixels) {
+	FIRGBF *rgbf = (FIRGBF*)target;
+	float *xyz = (float*)source;
+	
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		// assume CCIR-709 primaries (matrix from tif_luv.c)
+		// LOG Luv XYZ (D65) -> sRGB (CIE Illuminant E)
+		rgbf->red	= (float)( 2.690*xyz[0] + -1.276*xyz[1] + -0.414*xyz[2]);
+		rgbf->green	= (float)(-1.022*xyz[0] +  1.978*xyz[1] +  0.044*xyz[2]);
+		rgbf->blue	= (float)( 0.061*xyz[0] + -0.224*xyz[1] +  1.163*xyz[2]);
+		
+		/*
+		if (stonits != 0.0) {
+			rgbf->red	= (float)(rgbf->red   * stonits);
+			rgbf->green	= (float)(rgbf->green * stonits);
+			rgbf->blue	= (float)(rgbf->blue  * stonits);
+		} 
+		*/
+
+		rgbf++;
+		xyz += 3;
+	}
+}
+
+void tiff_ConvertLineRGBToXYZ(BYTE *target, BYTE *source, int width_in_pixels) {
+	FIRGBF *rgbf = (FIRGBF*)source;
+	float *xyz = (float*)target;
+	
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		// assume CCIR-709 primaries, whitepoint x = 1/3 y = 1/3 (D_E)
+		// "The LogLuv Encoding for Full Gamut, High Dynamic Range Images" <G.Ward>
+		// sRGB ( CIE Illuminant E ) -> LOG Luv XYZ (D65)
+		xyz[0] =  (float)(0.497*rgbf->red +  0.339*rgbf->green +  0.164*rgbf->blue);
+		xyz[1] =  (float)(0.256*rgbf->red +  0.678*rgbf->green +  0.066*rgbf->blue);
+		xyz[2] =  (float)(0.023*rgbf->red +  0.113*rgbf->green +  0.864*rgbf->blue);
+
+		rgbf++;
+		xyz += 3;
+	}
+}
+
diff --git a/files/Source/FreeImage/ToneMapping.cpp b/files/Source/FreeImage/ToneMapping.cpp
new file mode 100644
index 0000000..27f8c95
--- /dev/null
+++ b/files/Source/FreeImage/ToneMapping.cpp
@@ -0,0 +1,75 @@
+// ==========================================================
+// Tone mapping operators
+//
+// 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"
+
+/**
+Performs a tone mapping on a 48-bit RGB or a 96-bit RGBF image and returns a 24-bit image. 
+The meaning of the parameters depends on the choosen algorithm. 
+When both parameters are set to zero, a default set of parameters is used. 
+@param dib Input RGB/RGBF image
+@param tmo Tone mapping operator
+@param first_param First parameter of the algorithm
+@param second_param Second parameter of the algorithm
+return Returns a 24-bit tone mapped image if successful, returns NULL otherwise
+*/ 
+FIBITMAP * DLL_CALLCONV
+FreeImage_ToneMapping(FIBITMAP *dib, FREE_IMAGE_TMO tmo, double first_param, double second_param) {
+	if(FreeImage_HasPixels(dib)) {
+		switch(tmo) {
+			// Adaptive logarithmic mapping (F. Drago, 2003)
+			case FITMO_DRAGO03:
+				if((first_param == 0) && (second_param == 0)) {
+					// use default values (gamma = 2.2, exposure = 0)
+					return FreeImage_TmoDrago03(dib, 2.2, 0);
+				} else {
+					// use user's value
+					return FreeImage_TmoDrago03(dib, first_param, second_param);
+				}
+				break;
+			// Dynamic range reduction inspired by photoreceptor phhysiology (E. Reinhard, 2005)
+			case FITMO_REINHARD05:
+				if((first_param == 0) && (second_param == 0)) {
+					// use default values by setting intensity to 0 and contrast to 0
+					return FreeImage_TmoReinhard05(dib, 0, 0);
+				} else {
+					// use user's value
+					return FreeImage_TmoReinhard05(dib, first_param, second_param);
+				}
+				break;
+			// Gradient Domain HDR Compression (R. Fattal, 2002)
+			case FITMO_FATTAL02:
+				if((first_param == 0) && (second_param == 0)) {
+					// use default values by setting color saturation to 0.5 and attenuation to 0.85
+					return FreeImage_TmoFattal02(dib, 0.5, 0.85);
+				} else {
+					// use user's value
+					return FreeImage_TmoFattal02(dib, first_param, second_param);
+				}
+				break;
+		}
+	}
+
+	return NULL;
+}
+
+
diff --git a/files/Source/FreeImage/WuQuantizer.cpp b/files/Source/FreeImage/WuQuantizer.cpp
new file mode 100644
index 0000000..66d3706
--- /dev/null
+++ b/files/Source/FreeImage/WuQuantizer.cpp
@@ -0,0 +1,559 @@
+///////////////////////////////////////////////////////////////////////
+//	    C Implementation of Wu's Color Quantizer (v. 2)
+//	    (see Graphics Gems vol. II, pp. 126-133)
+//
+// Author:	Xiaolin Wu
+// Dept. of Computer Science
+// Univ. of Western Ontario
+// London, Ontario N6A 5B7
+// wu@csd.uwo.ca
+// 
+// Algorithm: Greedy orthogonal bipartition of RGB space for variance
+// 	   minimization aided by inclusion-exclusion tricks.
+// 	   For speed no nearest neighbor search is done. Slightly
+// 	   better performance can be expected by more sophisticated
+// 	   but more expensive versions.
+// 
+// The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of
+// additional documentation and a cure to a previous bug.
+// 
+// Free to distribute, comments and suggestions are appreciated.
+///////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////
+// History
+// -------
+// July 2000:  C++ Implementation of Wu's Color Quantizer
+//             and adaptation 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)
+///////////////////////////////////////////////////////////////////////
+
+#include "Quantizers.h"
+#include "FreeImage.h"
+#include "Utilities.h"
+
+///////////////////////////////////////////////////////////////////////
+
+// Size of a 3D array : 33 x 33 x 33
+#define SIZE_3D	35937
+
+// 3D array indexation
+#define INDEX(r, g, b)	((r << 10) + (r << 6) + r + (g << 5) + g + b)
+
+#define MAXCOLOR	256
+
+// Constructor / Destructor
+
+WuQuantizer::WuQuantizer(FIBITMAP *dib) {
+	width = FreeImage_GetWidth(dib);
+	height = FreeImage_GetHeight(dib);
+	pitch = FreeImage_GetPitch(dib);
+	m_dib = dib;
+
+	gm2 = NULL;
+	wt = mr = mg = mb = NULL;
+	Qadd = NULL;
+
+	// Allocate 3D arrays
+	gm2 = (float*)malloc(SIZE_3D * sizeof(float));
+	wt = (LONG*)malloc(SIZE_3D * sizeof(LONG));
+	mr = (LONG*)malloc(SIZE_3D * sizeof(LONG));
+	mg = (LONG*)malloc(SIZE_3D * sizeof(LONG));
+	mb = (LONG*)malloc(SIZE_3D * sizeof(LONG));
+
+	// Allocate Qadd
+	Qadd = (WORD *)malloc(sizeof(WORD) * width * height);
+
+	if(!gm2 || !wt || !mr || !mg || !mb || !Qadd) {
+		if(gm2)	free(gm2);
+		if(wt)	free(wt);
+		if(mr)	free(mr);
+		if(mg)	free(mg);
+		if(mb)	free(mb);
+		if(Qadd)  free(Qadd);
+		throw FI_MSG_ERROR_MEMORY;
+	}
+	memset(gm2, 0, SIZE_3D * sizeof(float));
+	memset(wt, 0, SIZE_3D * sizeof(LONG));
+	memset(mr, 0, SIZE_3D * sizeof(LONG));
+	memset(mg, 0, SIZE_3D * sizeof(LONG));
+	memset(mb, 0, SIZE_3D * sizeof(LONG));
+	memset(Qadd, 0, sizeof(WORD) * width * height);
+}
+
+WuQuantizer::~WuQuantizer() {
+	if(gm2)	free(gm2);
+	if(wt)	free(wt);
+	if(mr)	free(mr);
+	if(mg)	free(mg);
+	if(mb)	free(mb);
+	if(Qadd)  free(Qadd);
+}
+
+
+// Histogram is in elements 1..HISTSIZE along each axis,
+// element 0 is for base or marginal value
+// NB: these must start out 0!
+
+// Build 3-D color histogram of counts, r/g/b, c^2
+void 
+WuQuantizer::Hist3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2, int ReserveSize, RGBQUAD *ReservePalette) {
+	int ind = 0;
+	int inr, ing, inb, table[256];
+	int i;
+	unsigned y, x;
+
+	for(i = 0; i < 256; i++)
+		table[i] = i * i;
+
+	if (FreeImage_GetBPP(m_dib) == 24) {
+		for(y = 0; y < height; y++) {
+			BYTE *bits = FreeImage_GetScanLine(m_dib, y);
+
+			for(x = 0; x < width; x++)	{
+				inr = (bits[FI_RGBA_RED] >> 3) + 1;
+				ing = (bits[FI_RGBA_GREEN] >> 3) + 1;
+				inb = (bits[FI_RGBA_BLUE] >> 3) + 1;
+				ind = INDEX(inr, ing, inb);
+				Qadd[y*width + x] = (WORD)ind;
+				// [inr][ing][inb]
+				vwt[ind]++;
+				vmr[ind] += bits[FI_RGBA_RED];
+				vmg[ind] += bits[FI_RGBA_GREEN];
+				vmb[ind] += bits[FI_RGBA_BLUE];
+				m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]);
+				bits += 3;
+			}
+		}
+	} else {
+		for(y = 0; y < height; y++) {
+			BYTE *bits = FreeImage_GetScanLine(m_dib, y);
+
+			for(x = 0; x < width; x++)	{
+				inr = (bits[FI_RGBA_RED] >> 3) + 1;
+				ing = (bits[FI_RGBA_GREEN] >> 3) + 1;
+				inb = (bits[FI_RGBA_BLUE] >> 3) + 1;
+				ind = INDEX(inr, ing, inb);
+				Qadd[y*width + x] = (WORD)ind;
+				// [inr][ing][inb]
+				vwt[ind]++;
+				vmr[ind] += bits[FI_RGBA_RED];
+				vmg[ind] += bits[FI_RGBA_GREEN];
+				vmb[ind] += bits[FI_RGBA_BLUE];
+				m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]);
+				bits += 4;
+			}
+		}
+	}
+
+	if( ReserveSize > 0 ) {
+		int max = 0;
+		for(i = 0; i < SIZE_3D; i++) {
+			if( vwt[i] > max ) max = vwt[i];
+		}
+		max++;
+		for(i = 0; i < ReserveSize; i++) {
+			inr = (ReservePalette[i].rgbRed >> 3) + 1;
+			ing = (ReservePalette[i].rgbGreen >> 3) + 1;
+			inb = (ReservePalette[i].rgbBlue >> 3) + 1;
+			ind = INDEX(inr, ing, inb);
+			wt[ind] = max;
+			mr[ind] = max * ReservePalette[i].rgbRed;
+			mg[ind] = max * ReservePalette[i].rgbGreen;
+			mb[ind] = max * ReservePalette[i].rgbBlue;
+			gm2[ind] = (float)max * (float)(table[ReservePalette[i].rgbRed] + table[ReservePalette[i].rgbGreen] + table[ReservePalette[i].rgbBlue]);
+		}
+	}
+}
+
+
+// At conclusion of the histogram step, we can interpret
+// wt[r][g][b] = sum over voxel of P(c)
+// mr[r][g][b] = sum over voxel of r*P(c)  ,  similarly for mg, mb
+// m2[r][g][b] = sum over voxel of c^2*P(c)
+// Actually each of these should be divided by 'ImageSize' to give the usual
+// interpretation of P() as ranging from 0 to 1, but we needn't do that here.
+
+
+// We now convert histogram into moments so that we can rapidly calculate
+// the sums of the above quantities over any desired box.
+
+// Compute cumulative moments
+void 
+WuQuantizer::M3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2) {
+	unsigned ind1, ind2;
+	BYTE i, r, g, b;
+	LONG line, line_r, line_g, line_b;
+	LONG area[33], area_r[33], area_g[33], area_b[33];
+	float line2, area2[33];
+
+    for(r = 1; r <= 32; r++) {
+		for(i = 0; i <= 32; i++) {
+			area2[i] = 0;
+			area[i] = area_r[i] = area_g[i] = area_b[i] = 0;
+		}
+		for(g = 1; g <= 32; g++) {
+			line2 = 0;
+			line = line_r = line_g = line_b = 0;
+			for(b = 1; b <= 32; b++) {			 
+				ind1 = INDEX(r, g, b); // [r][g][b]
+				line += vwt[ind1];
+				line_r += vmr[ind1]; 
+				line_g += vmg[ind1]; 
+				line_b += vmb[ind1];
+				line2 += m2[ind1];
+				area[b] += line;
+				area_r[b] += line_r;
+				area_g[b] += line_g;
+				area_b[b] += line_b;
+				area2[b] += line2;
+				ind2 = ind1 - 1089; // [r-1][g][b]
+				vwt[ind1] = vwt[ind2] + area[b];
+				vmr[ind1] = vmr[ind2] + area_r[b];
+				vmg[ind1] = vmg[ind2] + area_g[b];
+				vmb[ind1] = vmb[ind2] + area_b[b];
+				m2[ind1] = m2[ind2] + area2[b];
+			}
+		}
+	}
+}
+
+// Compute sum over a box of any given statistic
+LONG 
+WuQuantizer::Vol( Box *cube, LONG *mmt ) {
+    return( mmt[INDEX(cube->r1, cube->g1, cube->b1)] 
+		  - mmt[INDEX(cube->r1, cube->g1, cube->b0)]
+		  - mmt[INDEX(cube->r1, cube->g0, cube->b1)]
+		  + mmt[INDEX(cube->r1, cube->g0, cube->b0)]
+		  - mmt[INDEX(cube->r0, cube->g1, cube->b1)]
+		  + mmt[INDEX(cube->r0, cube->g1, cube->b0)]
+		  + mmt[INDEX(cube->r0, cube->g0, cube->b1)]
+		  - mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
+}
+
+// The next two routines allow a slightly more efficient calculation
+// of Vol() for a proposed subbox of a given box.  The sum of Top()
+// and Bottom() is the Vol() of a subbox split in the given direction
+// and with the specified new upper bound.
+
+
+// Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1
+// (depending on dir)
+
+LONG 
+WuQuantizer::Bottom(Box *cube, BYTE dir, LONG *mmt) {
+    switch(dir)
+	{
+		case FI_RGBA_RED:
+			return( - mmt[INDEX(cube->r0, cube->g1, cube->b1)]
+				    + mmt[INDEX(cube->r0, cube->g1, cube->b0)]
+					+ mmt[INDEX(cube->r0, cube->g0, cube->b1)]
+					- mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
+			break;
+		case FI_RGBA_GREEN:
+			return( - mmt[INDEX(cube->r1, cube->g0, cube->b1)]
+				    + mmt[INDEX(cube->r1, cube->g0, cube->b0)]
+					+ mmt[INDEX(cube->r0, cube->g0, cube->b1)]
+					- mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
+			break;
+		case FI_RGBA_BLUE:
+			return( - mmt[INDEX(cube->r1, cube->g1, cube->b0)]
+				    + mmt[INDEX(cube->r1, cube->g0, cube->b0)]
+					+ mmt[INDEX(cube->r0, cube->g1, cube->b0)]
+					- mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
+			break;
+	}
+
+	return 0;
+}
+
+
+// Compute remainder of Vol(cube, mmt), substituting pos for
+// r1, g1, or b1 (depending on dir)
+
+LONG 
+WuQuantizer::Top(Box *cube, BYTE dir, int pos, LONG *mmt) {
+    switch(dir)
+	{
+		case FI_RGBA_RED:
+			return( mmt[INDEX(pos, cube->g1, cube->b1)] 
+				   -mmt[INDEX(pos, cube->g1, cube->b0)]
+				   -mmt[INDEX(pos, cube->g0, cube->b1)]
+				   +mmt[INDEX(pos, cube->g0, cube->b0)] );
+			break;
+		case FI_RGBA_GREEN:
+			return( mmt[INDEX(cube->r1, pos, cube->b1)] 
+				   -mmt[INDEX(cube->r1, pos, cube->b0)]
+				   -mmt[INDEX(cube->r0, pos, cube->b1)]
+				   +mmt[INDEX(cube->r0, pos, cube->b0)] );
+			break;
+		case FI_RGBA_BLUE:
+			return( mmt[INDEX(cube->r1, cube->g1, pos)]
+				   -mmt[INDEX(cube->r1, cube->g0, pos)]
+				   -mmt[INDEX(cube->r0, cube->g1, pos)]
+				   +mmt[INDEX(cube->r0, cube->g0, pos)] );
+			break;
+	}
+
+	return 0;
+}
+
+// Compute the weighted variance of a box 
+// NB: as with the raw statistics, this is really the variance * ImageSize 
+
+float
+WuQuantizer::Var(Box *cube) {
+    float dr = (float) Vol(cube, mr); 
+    float dg = (float) Vol(cube, mg); 
+    float db = (float) Vol(cube, mb);
+    float xx =  gm2[INDEX(cube->r1, cube->g1, cube->b1)] 
+			-gm2[INDEX(cube->r1, cube->g1, cube->b0)]
+			 -gm2[INDEX(cube->r1, cube->g0, cube->b1)]
+			 +gm2[INDEX(cube->r1, cube->g0, cube->b0)]
+			 -gm2[INDEX(cube->r0, cube->g1, cube->b1)]
+			 +gm2[INDEX(cube->r0, cube->g1, cube->b0)]
+			 +gm2[INDEX(cube->r0, cube->g0, cube->b1)]
+			 -gm2[INDEX(cube->r0, cube->g0, cube->b0)];
+
+    return (xx - (dr*dr+dg*dg+db*db)/(float)Vol(cube,wt));    
+}
+
+// We want to minimize the sum of the variances of two subboxes.
+// The sum(c^2) terms can be ignored since their sum over both subboxes
+// is the same (the sum for the whole box) no matter where we split.
+// The remaining terms have a minus sign in the variance formula,
+// so we drop the minus sign and MAXIMIZE the sum of the two terms.
+
+float
+WuQuantizer::Maximize(Box *cube, BYTE dir, int first, int last , int *cut, LONG whole_r, LONG whole_g, LONG whole_b, LONG whole_w) {
+	LONG half_r, half_g, half_b, half_w;
+	int i;
+	float temp;
+
+    LONG base_r = Bottom(cube, dir, mr);
+    LONG base_g = Bottom(cube, dir, mg);
+    LONG base_b = Bottom(cube, dir, mb);
+    LONG base_w = Bottom(cube, dir, wt);
+
+    float max = 0.0;
+
+    *cut = -1;
+
+    for (i = first; i < last; i++) {
+		half_r = base_r + Top(cube, dir, i, mr);
+		half_g = base_g + Top(cube, dir, i, mg);
+		half_b = base_b + Top(cube, dir, i, mb);
+		half_w = base_w + Top(cube, dir, i, wt);
+
+        // now half_x is sum over lower half of box, if split at i
+
+		if (half_w == 0) {		// subbox could be empty of pixels!
+			continue;			// never split into an empty box
+		} else {
+			temp = ((float)half_r*half_r + (float)half_g*half_g + (float)half_b*half_b)/half_w;
+		}
+
+		half_r = whole_r - half_r;
+		half_g = whole_g - half_g;
+		half_b = whole_b - half_b;
+		half_w = whole_w - half_w;
+
+        if (half_w == 0) {		// subbox could be empty of pixels!
+			continue;			// never split into an empty box
+		} else {
+			temp += ((float)half_r*half_r + (float)half_g*half_g + (float)half_b*half_b)/half_w;
+		}
+
+    	if (temp > max) {
+			max=temp;
+			*cut=i;
+		}
+    }
+
+    return max;
+}
+
+bool
+WuQuantizer::Cut(Box *set1, Box *set2) {
+	BYTE dir;
+	int cutr, cutg, cutb;
+
+    LONG whole_r = Vol(set1, mr);
+    LONG whole_g = Vol(set1, mg);
+    LONG whole_b = Vol(set1, mb);
+    LONG whole_w = Vol(set1, wt);
+
+    float maxr = Maximize(set1, FI_RGBA_RED, set1->r0+1, set1->r1, &cutr, whole_r, whole_g, whole_b, whole_w);    
+	float maxg = Maximize(set1, FI_RGBA_GREEN, set1->g0+1, set1->g1, &cutg, whole_r, whole_g, whole_b, whole_w);    
+	float maxb = Maximize(set1, FI_RGBA_BLUE, set1->b0+1, set1->b1, &cutb, whole_r, whole_g, whole_b, whole_w);
+
+    if ((maxr >= maxg) && (maxr >= maxb)) {
+		dir = FI_RGBA_RED;
+
+		if (cutr < 0) {
+			return false; // can't split the box
+		}
+    } else if ((maxg >= maxr) && (maxg>=maxb)) {
+		dir = FI_RGBA_GREEN;
+	} else {
+		dir = FI_RGBA_BLUE;
+	}
+
+	set2->r1 = set1->r1;
+    set2->g1 = set1->g1;
+    set2->b1 = set1->b1;
+
+    switch (dir) {
+		case FI_RGBA_RED:
+			set2->r0 = set1->r1 = cutr;
+			set2->g0 = set1->g0;
+			set2->b0 = set1->b0;
+			break;
+
+		case FI_RGBA_GREEN:
+			set2->g0 = set1->g1 = cutg;
+			set2->r0 = set1->r0;
+			set2->b0 = set1->b0;
+			break;
+
+		case FI_RGBA_BLUE:
+			set2->b0 = set1->b1 = cutb;
+			set2->r0 = set1->r0;
+			set2->g0 = set1->g0;
+			break;
+    }
+
+    set1->vol = (set1->r1-set1->r0)*(set1->g1-set1->g0)*(set1->b1-set1->b0);
+    set2->vol = (set2->r1-set2->r0)*(set2->g1-set2->g0)*(set2->b1-set2->b0);
+
+    return true;
+}
+
+
+void
+WuQuantizer::Mark(Box *cube, int label, BYTE *tag) {
+    for (int r = cube->r0 + 1; r <= cube->r1; r++) {
+		for (int g = cube->g0 + 1; g <= cube->g1; g++) {
+			for (int b = cube->b0 + 1; b <= cube->b1; b++) {
+				tag[INDEX(r, g, b)] = (BYTE)label;
+			}
+		}
+	}
+}
+
+// Wu Quantization algorithm
+FIBITMAP *
+WuQuantizer::Quantize(int PaletteSize, int ReserveSize, RGBQUAD *ReservePalette) {
+	BYTE *tag = NULL;
+
+	try {
+		Box	cube[MAXCOLOR];
+		int	next;
+		LONG i, weight;
+		int k;
+		float vv[MAXCOLOR], temp;
+		
+		// Compute 3D histogram
+
+		Hist3D(wt, mr, mg, mb, gm2, ReserveSize, ReservePalette);
+
+		// Compute moments
+
+		M3D(wt, mr, mg, mb, gm2);
+
+		cube[0].r0 = cube[0].g0 = cube[0].b0 = 0;
+		cube[0].r1 = cube[0].g1 = cube[0].b1 = 32;
+		next = 0;
+
+		for (i = 1; i < PaletteSize; i++) {
+			if(Cut(&cube[next], &cube[i])) {
+				// volume test ensures we won't try to cut one-cell box
+				vv[next] = (cube[next].vol > 1) ? Var(&cube[next]) : 0;
+				vv[i] = (cube[i].vol > 1) ? Var(&cube[i]) : 0;
+			} else {
+				  vv[next] = 0.0;   // don't try to split this box again
+				  i--;              // didn't create box i
+			}
+
+			next = 0; temp = vv[0];
+
+			for (k = 1; k <= i; k++) {
+				if (vv[k] > temp) {
+					temp = vv[k]; next = k;
+				}
+			}
+
+			if (temp <= 0.0) {
+				  PaletteSize = i + 1;
+
+				  // Error: "Only got 'PaletteSize' boxes"
+
+				  break;
+			}
+		}
+
+		// Partition done
+
+		// the space for array gm2 can be freed now
+
+		free(gm2);
+
+		gm2 = NULL;
+
+		// Allocate a new dib
+
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 8);
+
+		if (new_dib == NULL) {
+			throw FI_MSG_ERROR_MEMORY;
+		}
+
+		// create an optimized palette
+
+		RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+		tag = (BYTE*) malloc(SIZE_3D * sizeof(BYTE));
+		if (tag == NULL) {
+			throw FI_MSG_ERROR_MEMORY;
+		}
+		memset(tag, 0, SIZE_3D * sizeof(BYTE));
+
+		for (k = 0; k < PaletteSize ; k++) {
+			Mark(&cube[k], k, tag);
+			weight = Vol(&cube[k], wt);
+
+			if (weight) {
+				new_pal[k].rgbRed	= (BYTE)(((float)Vol(&cube[k], mr) / (float)weight) + 0.5f);
+				new_pal[k].rgbGreen = (BYTE)(((float)Vol(&cube[k], mg) / (float)weight) + 0.5f);
+				new_pal[k].rgbBlue	= (BYTE)(((float)Vol(&cube[k], mb) / (float)weight) + 0.5f);
+			} else {
+				// Error: bogus box 'k'
+
+				new_pal[k].rgbRed = new_pal[k].rgbGreen = new_pal[k].rgbBlue = 0;		
+			}
+		}
+
+		int npitch = FreeImage_GetPitch(new_dib);
+
+		for (unsigned y = 0; y < height; y++) {
+			BYTE *new_bits = FreeImage_GetBits(new_dib) + (y * npitch);
+
+			for (unsigned x = 0; x < width; x++) {
+				new_bits[x] = tag[Qadd[y*width + x]];
+			}
+		}
+
+		// output 'new_pal' as color look-up table contents,
+		// 'new_bits' as the quantized image (array of table addresses).
+
+		free(tag);
+
+		return (FIBITMAP*) new_dib;
+	} catch(...) {
+		free(tag);
+	}
+
+	return NULL;
+}
diff --git a/files/Source/FreeImage/ZLibInterface.cpp b/files/Source/FreeImage/ZLibInterface.cpp
new file mode 100644
index 0000000..ee27d27
--- /dev/null
+++ b/files/Source/FreeImage/ZLibInterface.cpp
@@ -0,0 +1,224 @@
+// ==========================================================
+// ZLib library interface
+//
+// 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 "zlib.h"
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "zutil.h"	/* must be the last header because of error C3163 in VS2008 (_vsnprintf defined in stdio.h) */
+
+
+/**
+Compresses a source buffer into a target buffer, using the ZLib library. 
+Upon entry, target_size is the total size of the destination buffer, 
+which must be at least 0.1% larger than source_size plus 12 bytes. 
+
+@param target Destination buffer
+@param target_size Size of the destination buffer, in bytes
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the actual size of the compressed buffer, returns 0 if an error occured
+@see FreeImage_ZLibUncompress
+*/
+DWORD DLL_CALLCONV 
+FreeImage_ZLibCompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
+	uLongf dest_len = (uLongf)target_size;
+
+	int zerr = compress(target, &dest_len, source, source_size);
+	switch(zerr) {
+		case Z_MEM_ERROR:	// not enough memory
+		case Z_BUF_ERROR:	// not enough room in the output buffer
+			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
+			return 0;
+		case Z_OK:
+			return dest_len;
+	}
+
+	return 0;
+}
+
+/**
+Decompresses a source buffer into a target buffer, using the ZLib library. 
+Upon entry, target_size is the total size of the destination buffer, 
+which must be large enough to hold the entire uncompressed data. 
+The size of the uncompressed data must have been saved previously by the compressor 
+and transmitted to the decompressor by some mechanism outside the scope of this 
+compression library.
+
+@param target Destination buffer
+@param target_size Size of the destination buffer, in bytes
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the actual size of the uncompressed buffer, returns 0 if an error occured
+@see FreeImage_ZLibCompress
+*/
+DWORD DLL_CALLCONV 
+FreeImage_ZLibUncompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
+	uLongf dest_len = (uLongf)target_size;
+
+	int zerr = uncompress(target, &dest_len, source, source_size);
+	switch(zerr) {
+		case Z_MEM_ERROR:	// not enough memory
+		case Z_BUF_ERROR:	// not enough room in the output buffer
+		case Z_DATA_ERROR:	// input data was corrupted
+			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
+			return 0;
+		case Z_OK:
+			return dest_len;
+	}
+
+	return 0;
+}
+
+/**
+Compresses a source buffer into a target buffer, using the ZLib library. 
+On success, the target buffer contains a GZIP compatible layout.
+Upon entry, target_size is the total size of the destination buffer, 
+which must be at least 0.1% larger than source_size plus 24 bytes. 
+
+@param target Destination buffer
+@param target_size Size of the destination buffer, in bytes
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the actual size of the compressed buffer, returns 0 if an error occured
+@see FreeImage_ZLibCompress
+*/
+DWORD DLL_CALLCONV 
+FreeImage_ZLibGZip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
+	uLongf dest_len = (uLongf)target_size - 12;
+	DWORD crc = crc32(0L, NULL, 0);
+
+    // set up header (stolen from zlib/gzio.c)
+    sprintf((char *)target, "%c%c%c%c%c%c%c%c", 0x1f, 0x8b,
+         Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/);
+    int zerr = compress2(target + 8, &dest_len, source, source_size, Z_BEST_COMPRESSION);
+	switch(zerr) {
+		case Z_MEM_ERROR:	// not enough memory
+		case Z_BUF_ERROR:	// not enough room in the output buffer
+			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
+			return 0;
+        case Z_OK: {
+            // patch header, setup crc and length (stolen from mod_trace_output)
+            BYTE *p = target + 8; *p++ = 2; *p = OS_CODE; // xflags, os_code
+ 	        crc = crc32(crc, source, source_size);
+	        memcpy(target + 4 + dest_len, &crc, 4);
+	        memcpy(target + 8 + dest_len, &source_size, 4);
+            return dest_len + 12;
+        }
+	}
+	return 0;
+}
+
+/**
+Decompresses a gzipped source buffer into a target buffer, using the ZLib library. 
+Upon entry, target_size is the total size of the destination buffer, 
+which must be large enough to hold the entire uncompressed data. 
+The size of the uncompressed data must have been saved previously by the compressor 
+and transmitted to the decompressor by some mechanism outside the scope of this 
+compression library.
+
+@param target Destination buffer
+@param target_size Size of the destination buffer, in bytes
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the actual size of the uncompressed buffer, returns 0 if an error occured
+@see FreeImage_ZLibGZip
+*/
+
+static int get_byte(z_stream *stream) {
+    if (stream->avail_in <= 0) return EOF;
+    stream->avail_in--;
+    return *(stream->next_in)++;
+}
+
+static int checkheader(z_stream *stream) {
+    int flags, c;
+    DWORD len;
+
+    if (get_byte(stream) != 0x1f || get_byte(stream) != 0x8b)
+        return Z_DATA_ERROR;
+    if (get_byte(stream) != Z_DEFLATED || ((flags = get_byte(stream)) & 0xE0) != 0)
+        return Z_DATA_ERROR;
+    for (len = 0; len < 6; len++) (void)get_byte(stream);
+
+    if ((flags & 0x04) != 0) { /* skip the extra field */
+        len  =  (DWORD)get_byte(stream);
+        len += ((DWORD)get_byte(stream)) << 8;
+        /* len is garbage if EOF but the loop below will quit anyway */
+        while (len-- != 0 && get_byte(stream) != EOF) ;
+    }
+    if ((flags & 0x08) != 0) { /* skip the original file name */
+        while ((c = get_byte(stream)) != 0 && c != EOF) ;
+    }
+    if ((flags & 0x10) != 0) {   /* skip the .gz file comment */
+        while ((c = get_byte(stream)) != 0 && c != EOF) ;
+    }
+    if ((flags & 0x02) != 0) {  /* skip the header crc */
+        for (len = 0; len < 2; len++) (void)get_byte(stream);
+    }
+    return Z_OK;
+}
+
+DWORD DLL_CALLCONV 
+FreeImage_ZLibGUnzip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
+    DWORD src_len  = source_size;
+    DWORD dest_len = target_size;
+    int   zerr     = Z_DATA_ERROR;
+
+    if (src_len > 0) {
+        z_stream stream;
+        memset(&stream, 0, sizeof (stream));
+        if ((zerr = inflateInit2(&stream, -MAX_WBITS)) == Z_OK) {
+            stream.next_in  = source;
+            stream.avail_in = source_size;
+
+            stream.next_out  = target;
+            stream.avail_out = target_size;
+
+            if ((zerr = checkheader(&stream)) == Z_OK) {
+                zerr = inflate (&stream, Z_NO_FLUSH);
+                dest_len = target_size - stream.avail_out;
+
+                if (zerr == Z_OK || zerr == Z_STREAM_END)
+                    inflateEnd(&stream);
+            } 
+        }
+    }
+    if (zerr != Z_OK && zerr != Z_STREAM_END) {
+        FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
+        return 0;
+    }
+    return dest_len;
+}
+
+/**
+Update a running crc from source and return the updated crc, using the ZLib library.
+If source is NULL, this function returns the required initial value for the crc.
+
+@param crc Running crc value
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the new crc value
+*/
+DWORD DLL_CALLCONV 
+FreeImage_ZLibCRC32(DWORD crc, BYTE *source, DWORD source_size) {
+
+    return crc32(crc, source, source_size);
+}
diff --git a/files/Source/FreeImage/tmoColorConvert.cpp b/files/Source/FreeImage/tmoColorConvert.cpp
new file mode 100644
index 0000000..64e9e24
--- /dev/null
+++ b/files/Source/FreeImage/tmoColorConvert.cpp
@@ -0,0 +1,483 @@
+// ==========================================================
+// High Dynamic Range bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - 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!
+// ==========================================================
+
+#include <cmath>
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "ToneMapping.h"
+
+// ----------------------------------------------------------
+// Convert RGB to and from Yxy, same as in Reinhard et al. SIGGRAPH 2002
+// References : 
+// [1] Radiance Home Page [Online] http://radsite.lbl.gov/radiance/HOME.html
+// [2] E. Reinhard, M. Stark, P. Shirley, and J. Ferwerda,  
+//     Photographic Tone Reproduction for Digital Images, ACM Transactions on Graphics, 
+//     21(3):267-276, 2002 (Proceedings of SIGGRAPH 2002). 
+// [3] J. Tumblin and H.E. Rushmeier, 
+//     Tone Reproduction for Realistic Images. IEEE Computer Graphics and Applications, 
+//     13(6):42-48, 1993.
+// ----------------------------------------------------------
+
+/**
+nominal CRT primaries 
+*/
+/*
+static const float CIE_x_r = 0.640F;
+static const float CIE_y_r = 0.330F;
+static const float CIE_x_g = 0.290F;
+static const float CIE_y_g = 0.600F;
+static const float CIE_x_b = 0.150F;
+static const float CIE_y_b = 0.060F;
+static const float CIE_x_w = 0.3333F;	// use true white
+static const float CIE_y_w = 0.3333F;
+*/
+/**
+sRGB primaries
+*/
+static const float CIE_x_r = 0.640F;
+static const float CIE_y_r = 0.330F;
+static const float CIE_x_g = 0.300F;
+static const float CIE_y_g = 0.600F;
+static const float CIE_x_b = 0.150F;
+static const float CIE_y_b = 0.060F;
+static const float CIE_x_w = 0.3127F;	// Illuminant D65
+static const float CIE_y_w = 0.3290F;
+
+static const float CIE_D = ( CIE_x_r*(CIE_y_g - CIE_y_b) + CIE_x_g*(CIE_y_b - CIE_y_r) + CIE_x_b*(CIE_y_r - CIE_y_g) );
+static const float CIE_C_rD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_g - CIE_y_b) - CIE_y_w*(CIE_x_g - CIE_x_b) + CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) );
+static const float CIE_C_gD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_b - CIE_y_r) - CIE_y_w*(CIE_x_b - CIE_x_r) - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) );
+static const float CIE_C_bD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_r - CIE_y_g) - CIE_y_w*(CIE_x_r - CIE_x_g) + CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) );
+
+/**
+RGB to XYZ (no white balance)
+*/
+static const float  RGB2XYZ[3][3] = {
+	{ CIE_x_r*CIE_C_rD / CIE_D, 
+	  CIE_x_g*CIE_C_gD / CIE_D, 
+	  CIE_x_b*CIE_C_bD / CIE_D 
+	},
+	{ CIE_y_r*CIE_C_rD / CIE_D, 
+	  CIE_y_g*CIE_C_gD / CIE_D, 
+	  CIE_y_b*CIE_C_bD / CIE_D 
+	},
+	{ (1 - CIE_x_r-CIE_y_r)*CIE_C_rD / CIE_D,
+	  (1 - CIE_x_g-CIE_y_g)*CIE_C_gD / CIE_D,
+	  (1 - CIE_x_b-CIE_y_b)*CIE_C_bD / CIE_D
+	}
+};
+
+/**
+XYZ to RGB (no white balance)
+*/
+static const float  XYZ2RGB[3][3] = {
+	{(CIE_y_g - CIE_y_b - CIE_x_b*CIE_y_g + CIE_y_b*CIE_x_g) / CIE_C_rD,
+	 (CIE_x_b - CIE_x_g - CIE_x_b*CIE_y_g + CIE_x_g*CIE_y_b) / CIE_C_rD,
+	 (CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) / CIE_C_rD
+	},
+	{(CIE_y_b - CIE_y_r - CIE_y_b*CIE_x_r + CIE_y_r*CIE_x_b) / CIE_C_gD,
+	 (CIE_x_r - CIE_x_b - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) / CIE_C_gD,
+	 (CIE_x_b*CIE_y_r - CIE_x_r*CIE_y_b) / CIE_C_gD
+	},
+	{(CIE_y_r - CIE_y_g - CIE_y_r*CIE_x_g + CIE_y_g*CIE_x_r) / CIE_C_bD,
+	 (CIE_x_g - CIE_x_r - CIE_x_g*CIE_y_r + CIE_x_r*CIE_y_g) / CIE_C_bD,
+	 (CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) / CIE_C_bD
+	}
+};
+
+/**
+This gives approximately the following matrices : 
+
+static const float RGB2XYZ[3][3] = { 
+	{ 0.41239083F, 0.35758433F, 0.18048081F },
+	{ 0.21263903F, 0.71516865F, 0.072192319F },
+	{ 0.019330820F, 0.11919473F, 0.95053220F }
+};
+static const float XYZ2RGB[3][3] = { 
+	{ 3.2409699F, -1.5373832F, -0.49861079F },
+	{ -0.96924376F, 1.8759676F, 0.041555084F },
+	{ 0.055630036F, -0.20397687F, 1.0569715F }
+};
+*/
+
+// ----------------------------------------------------------
+
+static const float EPSILON = 1e-06F;
+static const float INF = 1e+10F;
+
+/**
+Convert in-place floating point RGB data to Yxy.<br>
+On output, pixel->red == Y, pixel->green == x, pixel->blue == y
+@param dib Input RGBF / Output Yxy image
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL 
+ConvertInPlaceRGBFToYxy(FIBITMAP *dib) {
+	float result[3];
+
+	if(FreeImage_GetImageType(dib) != FIT_RGBF)
+		return FALSE;
+
+	const unsigned width  = FreeImage_GetWidth(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+	const unsigned pitch  = FreeImage_GetPitch(dib);
+	
+	BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+	for(unsigned y = 0; y < height; y++) {
+		FIRGBF *pixel = (FIRGBF*)bits;
+		for(unsigned x = 0; x < width; x++) {
+			result[0] = result[1] = result[2] = 0;
+			for (int i = 0; i < 3; i++) {
+				result[i] += RGB2XYZ[i][0] * pixel[x].red;
+				result[i] += RGB2XYZ[i][1] * pixel[x].green;
+				result[i] += RGB2XYZ[i][2] * pixel[x].blue;
+			}
+			const float W = result[0] + result[1] + result[2];
+			const float Y = result[1];
+			if(W > 0) { 
+				pixel[x].red   = Y;			    // Y 
+				pixel[x].green = result[0] / W;	// x 
+				pixel[x].blue  = result[1] / W;	// y 	
+			} else {
+				pixel[x].red = pixel[x].green = pixel[x].blue = 0;
+			}
+		}
+		// next line
+		bits += pitch;
+	}
+
+	return TRUE;
+}
+
+/**
+Convert in-place Yxy image to floating point RGB data.<br>
+On input, pixel->red == Y, pixel->green == x, pixel->blue == y
+@param dib Input Yxy / Output RGBF image
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL 
+ConvertInPlaceYxyToRGBF(FIBITMAP *dib) {
+	float result[3];
+	float X, Y, Z;
+
+	if(FreeImage_GetImageType(dib) != FIT_RGBF)
+		return FALSE;
+
+	const unsigned width  = FreeImage_GetWidth(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+	const unsigned pitch  = FreeImage_GetPitch(dib);
+
+	BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+	for(unsigned y = 0; y < height; y++) {
+		FIRGBF *pixel = (FIRGBF*)bits;
+		for(unsigned x = 0; x < width; x++) {
+			Y = pixel[x].red;	        // Y 
+			result[1] = pixel[x].green;	// x 
+			result[2] = pixel[x].blue;	// y 
+			if ((Y > EPSILON) && (result[1] > EPSILON) && (result[2] > EPSILON)) {
+				X = (result[1] * Y) / result[2];
+				Z = (X / result[1]) - X - Y;
+			} else {
+				X = Z = EPSILON;
+			}
+			pixel[x].red   = X;
+			pixel[x].green = Y;
+			pixel[x].blue  = Z;
+			result[0] = result[1] = result[2] = 0;
+			for (int i = 0; i < 3; i++) {
+				result[i] += XYZ2RGB[i][0] * pixel[x].red;
+				result[i] += XYZ2RGB[i][1] * pixel[x].green;
+				result[i] += XYZ2RGB[i][2] * pixel[x].blue;
+			}
+			pixel[x].red   = result[0];	// R
+			pixel[x].green = result[1];	// G
+			pixel[x].blue  = result[2];	// B
+		}
+		// next line
+		bits += pitch;
+	}
+
+	return TRUE;
+}
+
+/**
+Get the maximum, minimum and average luminance.<br>
+On input, pixel->red == Y, pixel->green == x, pixel->blue == y
+@param Yxy Source Yxy image to analyze
+@param maxLum Maximum luminance
+@param minLum Minimum luminance
+@param worldLum Average luminance (world adaptation luminance)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL 
+LuminanceFromYxy(FIBITMAP *Yxy, float *maxLum, float *minLum, float *worldLum) {
+	if(FreeImage_GetImageType(Yxy) != FIT_RGBF)
+		return FALSE;
+
+	const unsigned width  = FreeImage_GetWidth(Yxy);
+	const unsigned height = FreeImage_GetHeight(Yxy);
+	const unsigned pitch  = FreeImage_GetPitch(Yxy);
+
+	float max_lum = 0, min_lum = 0;
+	double sum = 0;
+
+	BYTE *bits = (BYTE*)FreeImage_GetBits(Yxy);
+	for(unsigned y = 0; y < height; y++) {
+		const FIRGBF *pixel = (FIRGBF*)bits;
+		for(unsigned x = 0; x < width; x++) {
+			const float Y = MAX(0.0F, pixel[x].red);// avoid negative values
+			max_lum = (max_lum < Y) ? Y : max_lum;	// max Luminance in the scene
+			min_lum = (min_lum < Y) ? min_lum : Y;	// min Luminance in the scene
+                        sum += std::log(
+                            2.3e-5F + Y);  // contrast constant in Tumblin paper
+                }
+                // next line
+                bits += pitch;
+	}
+	// maximum luminance
+	*maxLum = max_lum;
+	// minimum luminance
+	*minLum = min_lum;
+	// average log luminance
+	double avgLogLum = (sum / (width * height));
+	// world adaptation luminance
+	*worldLum = (float)exp(avgLogLum);
+
+	return TRUE;
+}
+
+/**
+Clamp RGBF image highest values to display white, 
+then convert to 24-bit RGB
+*/
+FIBITMAP* 
+ClampConvertRGBFTo24(FIBITMAP *src) {
+	if(FreeImage_GetImageType(src) != FIT_RGBF)
+		return FALSE;
+
+	const unsigned width  = FreeImage_GetWidth(src);
+	const unsigned height = FreeImage_GetHeight(src);
+
+	FIBITMAP *dst = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+	if(!dst) return NULL;
+
+	const unsigned src_pitch  = FreeImage_GetPitch(src);
+	const unsigned dst_pitch  = FreeImage_GetPitch(dst);
+
+	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;
+		BYTE *dst_pixel = (BYTE*)dst_bits;
+		for(unsigned x = 0; x < width; x++) {
+			const float red   = (src_pixel[x].red > 1)   ? 1 : src_pixel[x].red;
+			const float green = (src_pixel[x].green > 1) ? 1 : src_pixel[x].green;
+			const float blue  = (src_pixel[x].blue > 1)  ? 1 : src_pixel[x].blue;
+			
+			dst_pixel[FI_RGBA_RED]   = (BYTE)(255.0F * red   + 0.5F);
+			dst_pixel[FI_RGBA_GREEN] = (BYTE)(255.0F * green + 0.5F);
+			dst_pixel[FI_RGBA_BLUE]  = (BYTE)(255.0F * blue  + 0.5F);
+			dst_pixel += 3;
+		}
+		src_bits += src_pitch;
+		dst_bits += dst_pitch;
+	}
+
+	return dst;
+}
+
+/**
+Extract the luminance channel L from a RGBF image. 
+Luminance is calculated from the sRGB model (RGB2XYZ matrix) 
+using a D65 white point : 
+L = ( 0.2126 * r ) + ( 0.7152 * g ) + ( 0.0722 * b )
+Reference : 
+A Standard Default Color Space for the Internet - sRGB. 
+[online] http://www.w3.org/Graphics/Color/sRGB
+*/
+FIBITMAP*  
+ConvertRGBFToY(FIBITMAP *src) {
+	if(FreeImage_GetImageType(src) != FIT_RGBF)
+		return FALSE;
+
+	const unsigned width  = FreeImage_GetWidth(src);
+	const unsigned height = FreeImage_GetHeight(src);
+
+	FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height);
+	if(!dst) return NULL;
+
+	const unsigned src_pitch  = FreeImage_GetPitch(src);
+	const unsigned dst_pitch  = FreeImage_GetPitch(dst);
+
+	
+	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;
+		float  *dst_pixel = (float*)dst_bits;
+		for(unsigned x = 0; x < width; x++) {
+			const float L = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue);
+			dst_pixel[x] = (L > 0) ? L : 0;
+		}
+		// next line
+		src_bits += src_pitch;
+		dst_bits += dst_pitch;
+	}
+
+	return dst;
+}
+
+/**
+Get the maximum, minimum, average luminance and log average luminance from a Y image
+@param dib Source Y image to analyze
+@param maxLum Maximum luminance
+@param minLum Minimum luminance
+@param Lav Average luminance
+@param Llav Log average luminance (also known as 'world adaptation luminance')
+@return Returns TRUE if successful, returns FALSE otherwise
+@see ConvertRGBFToY, FreeImage_TmoReinhard05Ex
+*/
+BOOL 
+LuminanceFromY(FIBITMAP *dib, float *maxLum, float *minLum, float *Lav, float *Llav) {
+	if(FreeImage_GetImageType(dib) != FIT_FLOAT)
+		return FALSE;
+
+	unsigned width  = FreeImage_GetWidth(dib);
+	unsigned height = FreeImage_GetHeight(dib);
+	unsigned pitch  = FreeImage_GetPitch(dib);
+
+	float max_lum = -1e20F, min_lum = 1e20F;
+	double sumLum = 0, sumLogLum = 0;
+
+	BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+	for(unsigned y = 0; y < height; y++) {
+		const float *pixel = (float*)bits;
+		for(unsigned x = 0; x < width; x++) {
+			const float Y = pixel[x];
+			max_lum = (max_lum < Y) ? Y : max_lum;				// max Luminance in the scene
+			min_lum = ((Y > 0) && (min_lum < Y)) ? min_lum : Y;	// min Luminance in the scene
+			sumLum += Y;										// average luminance
+                        sumLogLum += std::log(
+                            2.3e-5F + Y);  // contrast constant in Tumblin paper
+                }
+                // next line
+                bits += pitch;
+	}
+
+	// maximum luminance
+	*maxLum = max_lum;
+	// minimum luminance
+	*minLum = min_lum;
+	// average luminance
+	*Lav = (float)(sumLum / (width * height));
+	// average log luminance, a.k.a. world adaptation luminance
+	*Llav = (float)exp(sumLogLum / (width * height));
+
+	return TRUE;
+}
+// --------------------------------------------------------------------------
+
+static void findMaxMinPercentile(FIBITMAP *Y, float minPrct, float *minLum, float maxPrct, float *maxLum) {
+	int x, y;
+	int width = FreeImage_GetWidth(Y);
+	int height = FreeImage_GetHeight(Y);
+	int pitch = FreeImage_GetPitch(Y);
+
+	std::vector<float> vY(width * height);
+
+	BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
+	for(y = 0; y < height; y++) {
+		float *pixel = (float*)bits;
+		for(x = 0; x < width; x++) {
+			if(pixel[x] != 0) {
+				vY.push_back(pixel[x]);
+			}
+		}
+		bits += pitch;
+	}
+
+	std::sort(vY.begin(), vY.end());
+	
+	*minLum = vY.at( int(minPrct * vY.size()) );
+	*maxLum = vY.at( int(maxPrct * vY.size()) );
+}
+
+/**
+Clipping function<br>
+Remove any extremely bright and/or extremely dark pixels 
+and normalize between 0 and 1. 
+@param Y Input/Output image
+@param minPrct Minimum percentile
+@param maxPrct Maximum percentile
+*/
+void 
+NormalizeY(FIBITMAP *Y, float minPrct, float maxPrct) {
+	int x, y;
+	float maxLum, minLum;
+
+	if(minPrct > maxPrct) {
+		// swap values
+		float t = minPrct; minPrct = maxPrct; maxPrct = t;
+	}
+	if(minPrct < 0) minPrct = 0;
+	if(maxPrct > 1) maxPrct = 1;
+
+	int width = FreeImage_GetWidth(Y);
+	int height = FreeImage_GetHeight(Y);
+	int pitch = FreeImage_GetPitch(Y);
+
+	// find max & min luminance values
+	if((minPrct > 0) || (maxPrct < 1)) {
+		maxLum = 0, minLum = 0;
+		findMaxMinPercentile(Y, minPrct, &minLum, maxPrct, &maxLum);
+	} else {
+		maxLum = -1e20F, minLum = 1e20F;
+		BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
+		for(y = 0; y < height; y++) {
+			const float *pixel = (float*)bits;
+			for(x = 0; x < width; x++) {
+				const float value = pixel[x];
+				maxLum = (maxLum < value) ? value : maxLum;	// max Luminance in the scene
+				minLum = (minLum < value) ? minLum : value;	// min Luminance in the scene
+			}
+			// next line
+			bits += pitch;
+		}
+	}
+	if(maxLum == minLum) return;
+
+	// normalize to range 0..1 
+	const float divider = maxLum - minLum;
+	BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
+	for(y = 0; y < height; y++) {
+		float *pixel = (float*)bits;
+		for(x = 0; x < width; x++) {
+			pixel[x] = (pixel[x] - minLum) / divider;
+			if(pixel[x] <= 0) pixel[x] = EPSILON;
+			if(pixel[x] > 1) pixel[x] = 1;
+		}
+		// next line
+		bits += pitch;
+	}
+}
diff --git a/files/Source/FreeImage/tmoDrago03.cpp b/files/Source/FreeImage/tmoDrago03.cpp
new file mode 100644
index 0000000..cfd5db7
--- /dev/null
+++ b/files/Source/FreeImage/tmoDrago03.cpp
@@ -0,0 +1,300 @@
+// ==========================================================
+// Tone mapping operator (Drago, 2003)
+//
+// 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 <cmath>
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "ToneMapping.h"
+
+// ----------------------------------------------------------
+// Logarithmic mapping operator
+// Reference: 
+// [1] F. Drago, K. Myszkowski, T. Annen, and N. Chiba, 
+// Adaptive Logarithmic Mapping for Displaying High Contrast Scenes, 
+// Eurographics 2003.
+// ----------------------------------------------------------
+
+/**
+Bias function
+*/
+static inline double 
+biasFunction(const double b, const double x) {
+	return pow (x, b);		// pow(x, log(bias)/log(0.5)
+}
+
+/**
+Padé approximation of log(x + 1)
+x(6+x)/(6+4x) good if x < 1
+x*(6 + 0.7662x)/(5.9897 + 3.7658x) between 1 and 2
+See http://www.nezumi.demon.co.uk/consult/logx.htm
+*/
+static inline double 
+pade_log(const double x) {
+	if(x < 1) {
+		return (x * (6 + x) / (6 + 4 * x));
+	} else if(x < 2) {
+		return (x * (6 + 0.7662 * x) / (5.9897 + 3.7658 * x));
+	}
+	return log(x + 1);
+}
+
+/**
+Log mapping operator
+@param dib Input / Output Yxy image
+@param maxLum Maximum luminance
+@param avgLum Average luminance (world adaptation luminance)
+@param biasParam Bias parameter (a zero value default to 0.85)
+@param exposure Exposure parameter (default to 0)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+ToneMappingDrago03(FIBITMAP *dib, const float maxLum, const float avgLum, float biasParam, const float exposure) {
+	const float LOG05 = -0.693147F;	// log(0.5) 
+
+	double Lmax, divider, interpol, biasP;
+	unsigned x, y;
+	double L;
+
+	if(FreeImage_GetImageType(dib) != FIT_RGBF)
+		return FALSE;
+
+	const unsigned width  = FreeImage_GetWidth(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+	const unsigned pitch  = FreeImage_GetPitch(dib);
+
+
+	// arbitrary Bias Parameter 
+	if(biasParam == 0) 
+		biasParam = 0.85F;
+
+	// normalize maximum luminance by average luminance
+	Lmax = maxLum / avgLum;
+	
+	divider = log10(Lmax+1);
+        biasP = std::log(biasParam) / LOG05;
+
+#if !defined(DRAGO03_FAST)
+
+	/**
+	Normal tone mapping of every pixel
+	further acceleration is obtained by a Padé approximation of log(x + 1)
+	*/
+	BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+	for(y = 0; y < height; y++) {
+		FIRGBF *pixel = (FIRGBF*)bits;
+		for(x = 0; x < width; x++) {
+			double Yw = pixel[x].red / avgLum;
+			Yw *= exposure;
+			interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8);
+			L = pade_log(Yw);// log(Yw + 1)
+			pixel[x].red = (float)((L / interpol) / divider);
+		}
+		// next line
+		bits += pitch;
+	}
+
+#else
+	unsigned index;
+	int i, j;
+
+	unsigned max_width  = width - (width % 3);
+	unsigned max_height = height - (height % 3); 
+	unsigned fpitch = pitch / sizeof(FIRGBF);
+
+	/**
+	fast tone mapping
+	split the image into 3x3 pixel tiles and perform the computation for each group of 9 pixels
+	further acceleration is obtained by a Padé approximation of log(x + 1)
+	=> produce artifacts and not so faster, so the code has been disabled
+	*/
+#define PIXEL(x, y)	image[y*fpitch + x].red
+
+	FIRGBF *image = (FIRGBF*)FreeImage_GetBits(dib);
+	for(y = 0; y < max_height; y += 3) {
+		for(x = 0; x < max_width; x += 3) {
+			double average = 0;
+			for(i = 0; i < 3; i++) {
+				for(j = 0; j < 3; j++) {
+					index = (y + i)*fpitch + (x + j);
+					image[index].red /= (float)avgLum;
+					image[index].red *= exposure; 
+					average += image[index].red;
+				}
+			}
+			average = average / 9 - PIXEL(x, y);
+			if(average > -1 && average < 1) {
+				interpol = log(2 + pow(PIXEL(x + 1, y + 1) / Lmax, biasP) * 8);
+				for(i = 0; i < 3; i++) {
+					for(j = 0; j < 3; j++) {
+						index = (y + i)*fpitch + (x + j);
+						L = pade_log(image[index].red);// log(image[index].red + 1)
+						image[index].red = (float)((L / interpol) / divider);
+					}
+				}
+			}
+			else {
+				for(i = 0; i < 3; i++) {
+					for(j = 0; j < 3; j++) {
+						index = (y + i)*fpitch + (x + j);
+						interpol = log(2 + pow(image[index].red / Lmax, biasP) * 8);
+						L = pade_log(image[index].red);// log(image[index].red + 1)
+						image[index].red = (float)((L / interpol) / divider);
+					}
+				}
+			}
+		} //x
+	} // y
+
+	/**
+	Normal tone mapping of every pixel for the remaining right and bottom bands
+	*/
+	BYTE *bits;
+
+	// right band
+	bits = (BYTE*)FreeImage_GetBits(dib);
+	for(y = 0; y < height; y++) {
+		FIRGBF *pixel = (FIRGBF*)bits;
+		for(x = max_width; x < width; x++) {
+			double Yw = pixel[x].red / avgLum;
+			Yw *= exposure;
+			interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8);
+			L = pade_log(Yw);// log(Yw + 1)
+			pixel[x].red = (float)((L / interpol) / divider);
+		}
+		// next line
+		bits += pitch;
+	}
+	// bottom band
+	bits = (BYTE*)FreeImage_GetBits(dib);
+	for(y = max_height; y < height; y++) {
+		FIRGBF *pixel = (FIRGBF*)bits;
+		for(x = 0; x < max_width; x++) {
+			double Yw = pixel[x].red / avgLum;
+			Yw *= exposure;
+			interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8);
+			L = pade_log(Yw);// log(Yw + 1)
+			pixel[x].red = (float)((L / interpol) / divider);
+		}
+		// next line
+		bits += pitch;
+	}
+
+#endif	// DRAGO03_FAST
+
+	return TRUE;
+}
+
+/**
+Custom gamma correction based on the ITU-R BT.709 standard
+@param dib RGBF image to be corrected
+@param gammaval Gamma value (2.2 is a good default value)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+REC709GammaCorrection(FIBITMAP *dib, const float gammaval) {
+	if(FreeImage_GetImageType(dib) != FIT_RGBF)
+		return FALSE;
+
+	float slope = 4.5F;
+	float start = 0.018F;
+	
+	const float fgamma = (float)((0.45 / gammaval) * 2);
+	if(gammaval >= 2.1F) {
+		start = (float)(0.018 / ((gammaval - 2) * 7.5));
+		slope = (float)(4.5 * ((gammaval - 2) * 7.5));
+	} else if (gammaval <= 1.9F) {
+		start = (float)(0.018 * ((2 - gammaval) * 7.5));
+		slope = (float)(4.5 / ((2 - gammaval) * 7.5));
+	}
+
+	const unsigned width  = FreeImage_GetWidth(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+	const unsigned pitch  = FreeImage_GetPitch(dib);
+
+	BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+	for(unsigned y = 0; y < height; y++) {
+		float *pixel = (float*)bits;
+		for(unsigned x = 0; x < width; x++) {
+			for(int i = 0; i < 3; i++) {
+                          *pixel = (*pixel <= start)
+                                       ? *pixel * slope
+                                       : (1.099F * std::pow(*pixel, fgamma) -
+                                          0.099F);
+                          pixel++;
+                        }
+		}
+		bits += pitch;
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//  Main algorithm
+// ----------------------------------------------------------
+
+/**
+Apply the Adaptive Logarithmic Mapping operator to a HDR image and convert to 24-bit RGB
+@param src Input RGB16 or RGB[A]F image
+@param gamma Gamma correction (gamma > 0). 1 means no correction, 2.2 in the original paper.
+@param exposure Exposure parameter (0 means no correction, 0 in the original paper)
+@return Returns a 24-bit RGB image if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV 
+FreeImage_TmoDrago03(FIBITMAP *src, double gamma, double exposure) {
+	float maxLum, minLum, avgLum;
+
+	if(!FreeImage_HasPixels(src)) return NULL;
+
+	// working RGBF variable
+	FIBITMAP *dib = NULL;
+
+	dib = FreeImage_ConvertToRGBF(src);
+	if(!dib) return NULL;
+
+	// default algorithm parameters
+	const float biasParam = 0.85F;
+	const float expoParam = (float)pow(2.0, exposure); //default exposure is 1, 2^0
+
+	// convert to Yxy
+	ConvertInPlaceRGBFToYxy(dib);
+	// get the luminance
+	LuminanceFromYxy(dib, &maxLum, &minLum, &avgLum);
+	// perform the tone mapping
+	ToneMappingDrago03(dib, maxLum, avgLum, biasParam, expoParam);
+	// convert back to RGBF
+	ConvertInPlaceYxyToRGBF(dib);
+	if(gamma != 1) {
+		// perform gamma correction
+		REC709GammaCorrection(dib, (float)gamma);
+	}
+	// clamp image highest values to display white, then convert to 24-bit RGB
+	FIBITMAP *dst = ClampConvertRGBFTo24(dib);
+
+	// clean-up and return
+	FreeImage_Unload(dib);
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+	
+	return dst;
+}
diff --git a/files/Source/FreeImage/tmoFattal02.cpp b/files/Source/FreeImage/tmoFattal02.cpp
new file mode 100644
index 0000000..17a5917
--- /dev/null
+++ b/files/Source/FreeImage/tmoFattal02.cpp
@@ -0,0 +1,695 @@
+// ==========================================================
+// Tone mapping operator (Fattal, 2002)
+//
+// 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 <cmath>
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "ToneMapping.h"
+
+// ----------------------------------------------------------
+// Gradient domain HDR compression
+// Reference:
+// [1] R. Fattal, D. Lischinski, and M.Werman, 
+// Gradient domain high dynamic range compression,
+// ACM Transactions on Graphics, special issue on Proc. of ACM SIGGRAPH 2002, 
+// San Antonio, Texas, vol. 21(3), pp. 257-266, 2002.
+// ----------------------------------------------------------
+
+static const float EPSILON = 1e-4F;
+
+/**
+Performs a 5 by 5 gaussian filtering using two 1D convolutions, 
+followed by a subsampling by 2. 
+@param dib Input image
+@return Returns a blurred image of size SIZE(dib)/2
+@see GaussianPyramid
+*/
+static FIBITMAP* GaussianLevel5x5(FIBITMAP *dib) {
+	FIBITMAP *h_dib = NULL, *v_dib = NULL, *dst = NULL;
+	float *src_pixel, *dst_pixel;
+
+	try {
+		const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+		if(image_type != FIT_FLOAT) throw(1);
+
+		const unsigned width = FreeImage_GetWidth(dib);
+		const unsigned height = FreeImage_GetHeight(dib);
+
+		h_dib = FreeImage_AllocateT(image_type, width, height);
+		v_dib = FreeImage_AllocateT(image_type, width, height);
+		if(!h_dib || !v_dib) throw(1);
+
+		const unsigned pitch = FreeImage_GetPitch(dib) / sizeof(float);
+
+		// horizontal convolution dib -> h_dib
+
+		src_pixel = (float*)FreeImage_GetBits(dib);
+		dst_pixel = (float*)FreeImage_GetBits(h_dib);
+
+		for(unsigned y = 0; y < height; y++) {
+			// work on line y
+			for(unsigned x = 2; x < width - 2; x++) {
+				dst_pixel[x] = src_pixel[x-2] + src_pixel[x+2] + 4 * (src_pixel[x-1] + src_pixel[x+1]) + 6 * src_pixel[x];
+				dst_pixel[x] /= 16;
+			}
+			// boundary mirroring
+			dst_pixel[0] = (2 * src_pixel[2] + 8 * src_pixel[1] + 6 * src_pixel[0]) / 16;
+			dst_pixel[1] = (src_pixel[3] + 4 * (src_pixel[0] + src_pixel[2]) + 7 * src_pixel[1]) / 16;
+			dst_pixel[width-2] = (src_pixel[width-4] + 5 * src_pixel[width-1] + 4 * src_pixel[width-3] + 6 * src_pixel[width-2]) / 16;
+			dst_pixel[width-1] = (src_pixel[width-3] + 5 * src_pixel[width-2] + 10 * src_pixel[width-1]) / 16;
+
+			// next line
+			src_pixel += pitch;
+			dst_pixel += pitch;
+		}
+
+		// vertical convolution h_dib -> v_dib
+
+		src_pixel = (float*)FreeImage_GetBits(h_dib);
+		dst_pixel = (float*)FreeImage_GetBits(v_dib);
+
+		for(unsigned x = 0; x < width; x++) {		
+			// work on column x
+			for(unsigned y = 2; y < height - 2; y++) {
+				const unsigned index = y*pitch + x;
+				dst_pixel[index] = src_pixel[index-2*pitch] + src_pixel[index+2*pitch] + 4 * (src_pixel[index-pitch] + src_pixel[index+pitch]) + 6 * src_pixel[index];
+				dst_pixel[index] /= 16;
+			}
+			// boundary mirroring
+			dst_pixel[x] = (2 * src_pixel[x+2*pitch] + 8 * src_pixel[x+pitch] + 6 * src_pixel[x]) / 16;
+			dst_pixel[x+pitch] = (src_pixel[x+3*pitch] + 4 * (src_pixel[x] + src_pixel[x+2*pitch]) + 7 * src_pixel[x+pitch]) / 16;
+			dst_pixel[(height-2)*pitch+x] = (src_pixel[(height-4)*pitch+x] + 5 * src_pixel[(height-1)*pitch+x] + 4 * src_pixel[(height-3)*pitch+x] + 6 * src_pixel[(height-2)*pitch+x]) / 16;
+			dst_pixel[(height-1)*pitch+x] = (src_pixel[(height-3)*pitch+x] + 5 * src_pixel[(height-2)*pitch+x] + 10 * src_pixel[(height-1)*pitch+x]) / 16;
+		}
+
+		FreeImage_Unload(h_dib); h_dib = NULL;
+
+		// perform downsampling
+
+		dst = FreeImage_Rescale(v_dib, width/2, height/2, FILTER_BILINEAR);
+
+		FreeImage_Unload(v_dib);
+
+		return dst;
+
+	} catch(int) {
+		if(h_dib) FreeImage_Unload(h_dib);
+		if(v_dib) FreeImage_Unload(v_dib);
+		if(dst) FreeImage_Unload(dst);
+		return NULL;
+	}
+}
+
+/**
+Compute a Gaussian pyramid using the specified number of levels. 
+@param H Original bitmap
+@param pyramid Resulting pyramid array 
+@param nlevels Number of resolution levels
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL GaussianPyramid(FIBITMAP *H, FIBITMAP **pyramid, int nlevels) {
+	try {
+		// first level is the original image
+		pyramid[0] = FreeImage_Clone(H);
+		if(pyramid[0] == NULL) throw(1);
+		// compute next levels
+		for(int k = 1; k < nlevels; k++) {
+			pyramid[k] = GaussianLevel5x5(pyramid[k-1]);
+			if(pyramid[k] == NULL) throw(1);
+		}
+		return TRUE;
+	} catch(int) {
+		for(int k = 0; k < nlevels; k++) {
+			if(pyramid[k] != NULL) {
+				FreeImage_Unload(pyramid[k]);
+				pyramid[k] = NULL;
+			}
+		}
+		return FALSE;
+	}
+}
+
+/**
+Compute the gradient magnitude of an input image H using central differences, 
+and returns the average gradient. 
+@param H Input image
+@param avgGrad [out] Average gradient
+@param k Level number
+@return Returns the gradient magnitude if successful, returns NULL otherwise
+@see GradientPyramid
+*/
+static FIBITMAP* GradientLevel(FIBITMAP *H, float *avgGrad, int k) {
+	FIBITMAP *G = NULL;
+
+	try {
+		const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(H);
+		if(image_type != FIT_FLOAT) throw(1);
+
+		const unsigned width = FreeImage_GetWidth(H);
+		const unsigned height = FreeImage_GetHeight(H);
+
+		G = FreeImage_AllocateT(image_type, width, height);
+		if(!G) throw(1);
+		
+		const unsigned pitch = FreeImage_GetPitch(H) / sizeof(float);
+		
+		const float divider = (float)(1 << (k + 1));
+		float average = 0;
+		
+		float *src_pixel = (float*)FreeImage_GetBits(H);
+		float *dst_pixel = (float*)FreeImage_GetBits(G);
+
+		for(unsigned y = 0; y < height; y++) {
+			const unsigned n = (y == 0 ? 0 : y-1);
+			const unsigned s = (y+1 == height ? y : y+1);
+			for(unsigned x = 0; x < width; x++) {
+				const unsigned w = (x == 0 ? 0 : x-1);
+				const unsigned e = (x+1 == width ? x : x+1);		
+				// central difference
+				const float gx = (src_pixel[y*pitch+e] - src_pixel[y*pitch+w]) / divider; // [Hk(x+1, y) - Hk(x-1, y)] / 2**(k+1)
+				const float gy = (src_pixel[s*pitch+x] - src_pixel[n*pitch+x]) / divider; // [Hk(x, y+1) - Hk(x, y-1)] / 2**(k+1)
+				// gradient
+                                dst_pixel[x] = std::sqrt(gx * gx + gy * gy);
+                                // average gradient
+                                average += dst_pixel[x];
+                        }
+			// next line
+			dst_pixel += pitch;
+		}
+		
+		*avgGrad = average / (width * height);
+
+		return G;
+
+	} catch(int) {
+		if(G) FreeImage_Unload(G);
+		return NULL;
+	}
+}
+
+/**
+Calculate gradient magnitude and its average value on each pyramid level
+@param pyramid Gaussian pyramid (nlevels levels)
+@param nlevels Number of levels
+@param gradients [out] Gradient pyramid (nlevels levels)
+@param avgGrad [out] Average gradient on each level (array of size nlevels)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL GradientPyramid(FIBITMAP **pyramid, int nlevels, FIBITMAP **gradients, float *avgGrad) {
+	try {
+		for(int k = 0; k < nlevels; k++) {
+			FIBITMAP *Hk = pyramid[k];
+			gradients[k] = GradientLevel(Hk, &avgGrad[k], k);
+			if(gradients[k] == NULL) throw(1);
+		}
+		return TRUE;
+	} catch(int) {
+		for(int k = 0; k < nlevels; k++) {
+			if(gradients[k] != NULL) {
+				FreeImage_Unload(gradients[k]);
+				gradients[k] = NULL;
+			}
+		}
+		return FALSE;
+	}
+}
+
+/**
+Compute the gradient attenuation function PHI(x, y)
+@param gradients Gradient pyramid (nlevels levels)
+@param avgGrad Average gradient on each level (array of size nlevels)
+@param nlevels Number of levels
+@param alpha Parameter alpha in the paper
+@param beta Parameter beta in the paper
+@return Returns the attenuation matrix Phi if successful, returns NULL otherwise
+*/
+static FIBITMAP* PhiMatrix(FIBITMAP **gradients, float *avgGrad, int nlevels, float alpha, float beta) {
+	float *src_pixel, *dst_pixel;
+	FIBITMAP **phi = NULL;
+
+	try {
+		phi = (FIBITMAP**)malloc(nlevels * sizeof(FIBITMAP*));
+		if(!phi) throw(1);
+		memset(phi, 0, nlevels * sizeof(FIBITMAP*));
+
+		for(int k = nlevels-1; k >= 0; k--) {
+			// compute phi(k)
+
+			FIBITMAP *Gk = gradients[k];
+
+			const unsigned width = FreeImage_GetWidth(Gk);
+			const unsigned height = FreeImage_GetHeight(Gk);
+			const unsigned pitch = FreeImage_GetPitch(Gk) / sizeof(float);
+
+			// parameter alpha is 0.1 times the average gradient magnitude
+			// also, note the factor of 2**k in the denominator; 
+			// that is there to correct for the fact that an average gradient avgGrad(H) over 2**k pixels 
+			// in the original image will appear as a gradient grad(Hk) = 2**k*avgGrad(H) over a single pixel in Hk. 
+			float ALPHA =  alpha * avgGrad[k] * (float)((int)1 << k);
+			if(ALPHA == 0) ALPHA = EPSILON;
+
+			phi[k] = FreeImage_AllocateT(FIT_FLOAT, width, height);
+			if(!phi[k]) throw(1);
+			
+			src_pixel = (float*)FreeImage_GetBits(Gk);
+			dst_pixel = (float*)FreeImage_GetBits(phi[k]);
+			for(unsigned y = 0; y < height; y++) {
+				for(unsigned x = 0; x < width; x++) {
+					// compute (alpha / grad) * (grad / alpha) ** beta
+					const float v = src_pixel[x] / ALPHA;
+                                        const float value = (float)std::pow(
+                                            (float)v, (float)(beta - 1));
+                                        dst_pixel[x] = (value > 1) ? 1 : value;
+                                }
+				// next line
+				src_pixel += pitch;
+				dst_pixel += pitch;
+			}
+
+			if(k < nlevels-1) {
+				// compute PHI(k) = L( PHI(k+1) ) * phi(k)
+				FIBITMAP *L = FreeImage_Rescale(phi[k+1], width, height, FILTER_BILINEAR);
+				if(!L) throw(1);
+
+				src_pixel = (float*)FreeImage_GetBits(L);
+				dst_pixel = (float*)FreeImage_GetBits(phi[k]);
+				for(unsigned y = 0; y < height; y++) {
+					for(unsigned x = 0; x < width; x++) {
+						dst_pixel[x] *= src_pixel[x];
+					}
+					// next line
+					src_pixel += pitch;
+					dst_pixel += pitch;
+				}
+
+				FreeImage_Unload(L);
+
+				// PHI(k+1) is no longer needed
+				FreeImage_Unload(phi[k+1]);
+				phi[k+1] = NULL;
+			}
+
+			// next level
+		}
+
+		// get the final result and return
+		FIBITMAP *dst = phi[0];
+
+		free(phi);
+
+		return dst;
+
+	} catch(int) {
+		if(phi) {
+			for(int k = nlevels-1; k >= 0; k--) {
+				if(phi[k]) FreeImage_Unload(phi[k]);
+			}
+			free(phi);
+		}
+		return NULL;
+	}
+}
+
+/**
+Compute gradients in x and y directions, attenuate them with the attenuation matrix, 
+then compute the divergence div G from the attenuated gradient. 
+@param H Normalized luminance
+@param PHI Attenuation matrix
+@return Returns the divergence matrix if successful, returns NULL otherwise
+*/
+static FIBITMAP* Divergence(FIBITMAP *H, FIBITMAP *PHI) {
+	FIBITMAP *Gx = NULL, *Gy = NULL, *divG = NULL;
+	float *phi, *h, *gx, *gy, *divg;
+
+	try {
+		const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(H);
+		if(image_type != FIT_FLOAT) throw(1);
+
+		const unsigned width = FreeImage_GetWidth(H);
+		const unsigned height = FreeImage_GetHeight(H);
+
+		Gx = FreeImage_AllocateT(image_type, width, height);
+		if(!Gx) throw(1);
+		Gy = FreeImage_AllocateT(image_type, width, height);
+		if(!Gy) throw(1);
+		
+		const unsigned pitch = FreeImage_GetPitch(H) / sizeof(float);
+		
+		// perform gradient attenuation
+
+		phi = (float*)FreeImage_GetBits(PHI);
+		h   = (float*)FreeImage_GetBits(H);
+		gx  = (float*)FreeImage_GetBits(Gx);
+		gy  = (float*)FreeImage_GetBits(Gy);
+
+		for(unsigned y = 0; y < height; y++) {
+			const unsigned s = (y+1 == height ? y : y+1);
+			for(unsigned x = 0; x < width; x++) {				
+				const unsigned e = (x+1 == width ? x : x+1);
+				// forward difference
+				const unsigned index = y*pitch + x;
+				const float phi_xy = phi[index];
+				const float h_xy   = h[index];
+				gx[x] = (h[y*pitch+e] - h_xy) * phi_xy; // [H(x+1, y) - H(x, y)] * PHI(x, y)
+				gy[x] = (h[s*pitch+x] - h_xy) * phi_xy; // [H(x, y+1) - H(x, y)] * PHI(x, y)
+			}
+			// next line
+			gx += pitch;
+			gy += pitch;
+		}
+
+		// calculate the divergence
+
+		divG = FreeImage_AllocateT(image_type, width, height);
+		if(!divG) throw(1);
+		
+		gx  = (float*)FreeImage_GetBits(Gx);
+		gy  = (float*)FreeImage_GetBits(Gy);
+		divg = (float*)FreeImage_GetBits(divG);
+
+		for(unsigned y = 0; y < height; y++) {
+			for(unsigned x = 0; x < width; x++) {				
+				// backward difference approximation
+				// divG = Gx(x, y) - Gx(x-1, y) + Gy(x, y) - Gy(x, y-1)
+				const unsigned index = y*pitch + x;
+				divg[index] = gx[index] + gy[index];
+				if(x > 0) divg[index] -= gx[index-1];
+				if(y > 0) divg[index] -= gy[index-pitch];
+			}
+		}
+
+		// no longer needed ... 
+		FreeImage_Unload(Gx);
+		FreeImage_Unload(Gy);
+
+		// return the divergence
+		return divG;
+
+	} catch(int) {
+		if(Gx) FreeImage_Unload(Gx);
+		if(Gy) FreeImage_Unload(Gy);
+		if(divG) FreeImage_Unload(divG);
+		return NULL;
+	}
+}
+
+/**
+Given the luminance channel, find max & min luminance values, 
+normalize to range 0..100 and take the logarithm. 
+@param Y Image luminance
+@return Returns the normalized luminance H if successful, returns NULL otherwise
+*/
+static FIBITMAP* LogLuminance(FIBITMAP *Y) {
+	FIBITMAP *H = NULL;
+
+	try {
+		// get the luminance channel
+		FIBITMAP *H = FreeImage_Clone(Y);
+		if(!H) throw(1);
+
+		const unsigned width  = FreeImage_GetWidth(H);
+		const unsigned height = FreeImage_GetHeight(H);
+		const unsigned pitch  = FreeImage_GetPitch(H);
+
+		// find max & min luminance values
+		float maxLum = -1e20F, minLum = 1e20F;
+
+		BYTE *bits = (BYTE*)FreeImage_GetBits(H);
+		for(unsigned y = 0; y < height; y++) {
+			const float *pixel = (float*)bits;
+			for(unsigned x = 0; x < width; x++) {
+				const float value = pixel[x];
+				maxLum = (maxLum < value) ? value : maxLum;	// max Luminance in the scene
+				minLum = (minLum < value) ? minLum : value;	// min Luminance in the scene
+			}
+			// next line
+			bits += pitch;
+		}
+		if(maxLum == minLum) throw(1);
+
+		// normalize to range 0..100 and take the logarithm
+		const float scale = 100.F / (maxLum - minLum);
+		bits = (BYTE*)FreeImage_GetBits(H);
+		for(unsigned y = 0; y < height; y++) {
+			float *pixel = (float*)bits;
+			for(unsigned x = 0; x < width; x++) {
+				const float value = (pixel[x] - minLum) * scale;
+                                pixel[x] = std::log(value + EPSILON);
+                        }
+                        // next line
+                        bits += pitch;
+		}
+
+		return H;
+
+	} catch(int) {
+		if(H) FreeImage_Unload(H);
+		return NULL;
+	}
+}
+
+/**
+Given a normalized luminance, perform exponentiation and recover the log compressed image 
+@param Y Input/Output luminance image
+*/
+static void ExpLuminance(FIBITMAP *Y) {
+	const unsigned width = FreeImage_GetWidth(Y);
+	const unsigned height = FreeImage_GetHeight(Y);
+	const unsigned pitch = FreeImage_GetPitch(Y);
+
+	BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
+	for(unsigned y = 0; y < height; y++) {
+		float *pixel = (float*)bits;
+		for(unsigned x = 0; x < width; x++) {
+                  pixel[x] = std::exp(pixel[x]) - EPSILON;
+                }
+                bits += pitch;
+        }
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Gradient Domain HDR tone mapping operator
+@param Y Image luminance values
+@param alpha Parameter alpha of the paper (suggested value is 0.1)
+@param beta Parameter beta of the paper (suggested value is between 0.8 and 0.9)
+@return returns the tone mapped luminance
+*/
+static FIBITMAP* tmoFattal02(FIBITMAP *Y, float alpha, float beta) {
+	const unsigned MIN_PYRAMID_SIZE = 32;	// minimun size (width or height) of the coarsest level of the pyramid
+
+	FIBITMAP *H = NULL;
+	FIBITMAP **pyramid = NULL;
+	FIBITMAP **gradients = NULL;
+	FIBITMAP *phy = NULL;
+	FIBITMAP *divG = NULL;
+	FIBITMAP *U = NULL;
+	float *avgGrad = NULL;
+
+	int k;
+	int nlevels = 0;
+
+	try {
+		// get the normalized luminance
+		FIBITMAP *H = LogLuminance(Y);
+		if(!H) throw(1);
+		
+		// get the number of levels for the pyramid
+		const unsigned width = FreeImage_GetWidth(H);
+		const unsigned height = FreeImage_GetHeight(H);
+		unsigned minsize = MIN(width, height);
+		while(minsize >= MIN_PYRAMID_SIZE) {
+			nlevels++;
+			minsize /= 2;
+		}
+
+		// create the Gaussian pyramid
+		pyramid = (FIBITMAP**)malloc(nlevels * sizeof(FIBITMAP*));
+		if(!pyramid) throw(1);
+		memset(pyramid, 0, nlevels * sizeof(FIBITMAP*));
+
+		if(!GaussianPyramid(H, pyramid, nlevels)) throw(1);
+
+		// calculate gradient magnitude and its average value on each pyramid level
+		gradients = (FIBITMAP**)malloc(nlevels * sizeof(FIBITMAP*));
+		if(!gradients) throw(1);
+		memset(gradients, 0, nlevels * sizeof(FIBITMAP*));
+		avgGrad = (float*)malloc(nlevels * sizeof(float));
+		if(!avgGrad) throw(1);
+
+		if(!GradientPyramid(pyramid, nlevels, gradients, avgGrad)) throw(1);
+
+		// free the Gaussian pyramid
+		for(k = 0; k < nlevels; k++) {
+			if(pyramid[k]) FreeImage_Unload(pyramid[k]);
+		}
+		free(pyramid); pyramid = NULL;
+
+		// compute the gradient attenuation function PHI(x, y)
+		phy = PhiMatrix(gradients, avgGrad, nlevels, alpha, beta);
+		if(!phy) throw(1);
+
+		// free the gradient pyramid
+		for(k = 0; k < nlevels; k++) {
+			if(gradients[k]) FreeImage_Unload(gradients[k]);
+		}
+		free(gradients); gradients = NULL;
+		free(avgGrad); avgGrad = NULL;
+
+		// compute gradients in x and y directions, attenuate them with the attenuation matrix, 
+		// then compute the divergence div G from the attenuated gradient. 
+		divG = Divergence(H, phy);
+		if(!divG) throw(1);
+
+		// H & phy no longer needed
+		FreeImage_Unload(H); H = NULL;
+		FreeImage_Unload(phy); phy = NULL;
+
+		// solve the PDE (Poisson equation) using a multigrid solver and 3 cycles
+		FIBITMAP *U = FreeImage_MultigridPoissonSolver(divG, 3);
+		if(!U) throw(1);
+
+		FreeImage_Unload(divG);
+
+		// perform exponentiation and recover the log compressed image
+		ExpLuminance(U);
+
+		return U;
+
+	} catch(int) {
+		if(H) FreeImage_Unload(H);
+		if(pyramid) {
+			for(int i = 0; i < nlevels; i++) {
+				if(pyramid[i]) FreeImage_Unload(pyramid[i]);
+			}
+			free(pyramid);
+		}
+		if(gradients) {
+			for(int i = 0; i < nlevels; i++) {
+				if(gradients[i]) FreeImage_Unload(gradients[i]);
+			}
+			free(gradients);
+		}
+		if(avgGrad) free(avgGrad);
+		if(phy) FreeImage_Unload(phy);
+		if(divG) FreeImage_Unload(divG);
+		if(U) FreeImage_Unload(U);
+
+		return NULL;
+	}
+}
+
+// ----------------------------------------------------------
+//  Main algorithm
+// ----------------------------------------------------------
+
+/**
+Apply the Gradient Domain High Dynamic Range Compression to a RGBF image and convert to 24-bit RGB
+@param dib Input RGBF / RGB16 image
+@param color_saturation Color saturation (s parameter in the paper) in [0.4..0.6]
+@param attenuation Atenuation factor (beta parameter in the paper) in [0.8..0.9]
+@return Returns a 24-bit RGB image if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV 
+FreeImage_TmoFattal02(FIBITMAP *dib, double color_saturation, double attenuation) {	
+	const float alpha = 0.1F;									// parameter alpha = 0.1
+	const float beta = (float)MAX(0.8, MIN(0.9, attenuation));	// parameter beta = [0.8..0.9]
+	const float s = (float)MAX(0.4, MIN(0.6, color_saturation));// exponent s controls color saturation = [0.4..0.6]
+
+	FIBITMAP *src = NULL;
+	FIBITMAP *Yin = NULL;
+	FIBITMAP *Yout = NULL;
+	FIBITMAP *dst = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	try {
+
+		// convert to RGBF
+		src = FreeImage_ConvertToRGBF(dib);
+		if(!src) throw(1);
+
+		// get the luminance channel
+		Yin = ConvertRGBFToY(src);
+		if(!Yin) throw(1);
+
+		// perform the tone mapping
+		Yout = tmoFattal02(Yin, alpha, beta);
+		if(!Yout) throw(1);
+
+		// clip low and high values and normalize to [0..1]
+		//NormalizeY(Yout, 0.001F, 0.995F);
+		NormalizeY(Yout, 0, 1);
+
+		// compress the dynamic range
+
+		const unsigned width = FreeImage_GetWidth(src);
+		const unsigned height = FreeImage_GetHeight(src);
+
+		const unsigned rgb_pitch = FreeImage_GetPitch(src);
+		const unsigned y_pitch = FreeImage_GetPitch(Yin);
+
+		BYTE *bits      = (BYTE*)FreeImage_GetBits(src);
+		BYTE *bits_yin  = (BYTE*)FreeImage_GetBits(Yin);
+		BYTE *bits_yout = (BYTE*)FreeImage_GetBits(Yout);
+
+		for(unsigned y = 0; y < height; y++) {
+			float *Lin = (float*)bits_yin;
+			float *Lout = (float*)bits_yout;
+			float *color = (float*)bits;
+			for(unsigned x = 0; x < width; x++) {
+				for(unsigned c = 0; c < 3; c++) {
+                                  *color = (Lin[x] > 0)
+                                               ? std::pow(*color / Lin[x], s) *
+                                                     Lout[x]
+                                               : 0;
+                                  color++;
+                                }
+			}
+			bits += rgb_pitch;
+			bits_yin += y_pitch;
+			bits_yout += y_pitch;
+		}
+
+		// not needed anymore
+		FreeImage_Unload(Yin);  Yin  = NULL;
+		FreeImage_Unload(Yout); Yout = NULL;
+
+		// clamp image highest values to display white, then convert to 24-bit RGB
+		dst = ClampConvertRGBFTo24(src);
+
+		// clean-up and return
+		FreeImage_Unload(src); src = NULL;
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(dst, dib);
+		
+		return dst;
+
+	} catch(int) {
+		if(src) FreeImage_Unload(src);
+		if(Yin) FreeImage_Unload(Yin);
+		if(Yout) FreeImage_Unload(Yout);
+		return NULL;
+	}
+}
diff --git a/files/Source/FreeImage/tmoReinhard05.cpp b/files/Source/FreeImage/tmoReinhard05.cpp
new file mode 100644
index 0000000..48cbcb2
--- /dev/null
+++ b/files/Source/FreeImage/tmoReinhard05.cpp
@@ -0,0 +1,270 @@
+// ==========================================================
+// Tone mapping operator (Reinhard, 2005)
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - 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!
+// ==========================================================
+
+#include <cmath>
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "ToneMapping.h"
+
+// ----------------------------------------------------------
+// Global and/or local tone mapping operator
+// References: 
+// [1] Erik Reinhard and Kate Devlin, 'Dynamic Range Reduction Inspired by Photoreceptor Physiology', 
+//     IEEE Transactions on Visualization and Computer Graphics, 11(1), Jan/Feb 2005. 
+// [2] Erik Reinhard, 'Parameter estimation for photographic tone reproduction',
+//     Journal of Graphics Tools, vol. 7, no. 1, pp. 45–51, 2003.
+// ----------------------------------------------------------
+
+/**
+Tone mapping operator
+@param dib Input / Output RGBF image
+@param Y Input luminance image version of dib
+@param f Overall intensity in range [-8:8] : default to 0
+@param m Contrast in range [0.3:1) : default to 0
+@param a Adaptation in range [0:1] : default to 1
+@param c Color correction in range [0:1] : default to 0
+@return Returns TRUE if successful, returns FALSE otherwise
+@see LuminanceFromY
+*/
+static BOOL 
+ToneMappingReinhard05(FIBITMAP *dib, FIBITMAP *Y, float f, float m, float a, float c) {
+	float Cav[3];		// channel average
+	float Lav = 0;		// average luminance
+	float Llav = 0;		// log average luminance
+	float minLum = 1;	// min luminance
+	float maxLum = 1;	// max luminance
+
+	float L;		// pixel luminance
+	float I_g, I_l; // global and local light adaptation
+	float I_a;		// interpolated pixel light adaptation
+	float k;		// key (low-key means overall dark image, high-key means overall light image)
+
+	// check input parameters 
+
+	if((FreeImage_GetImageType(dib) != FIT_RGBF) || (FreeImage_GetImageType(Y) != FIT_FLOAT)) {
+		return FALSE;
+	}
+
+	if(f < -8) f = -8; if(f > 8) f = 8;
+    if(m < 0)  m = 0;  if(m > 1) m = 1;
+    if(a < 0)  a = 0;  if(a > 1) a = 1;
+    if(c < 0)  c = 0;  if(c > 1) c = 1;
+
+	const unsigned width  = FreeImage_GetWidth(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+
+	const unsigned dib_pitch  = FreeImage_GetPitch(dib);
+	const unsigned y_pitch    = FreeImage_GetPitch(Y);
+
+	int i;
+	unsigned x, y;
+	BYTE *bits = NULL, *Ybits = NULL;
+
+	// get statistics about the data (but only if its really needed)
+
+        f = std::exp(-f);
+        if ((m == 0) || (a != 1) && (c != 1)) {
+          // avoid these calculations if its not needed after ...
+          LuminanceFromY(Y, &maxLum, &minLum, &Lav, &Llav);
+          k = (std::log(maxLum) - Llav) / (std::log(maxLum) - std::log(minLum));
+          if (k < 0) {
+            // pow(k, 1.4F) is undefined ...
+            // there's an ambiguity about the calculation of Llav between
+            // Reinhard papers and the various implementations  ... try another
+            // world adaptation luminance formula using instead 'worldLum =
+            // log(Llav)'
+            k = (std::log(maxLum) - std::log(Llav)) /
+                (std::log(maxLum) - std::log(minLum));
+            if (k < 0) m = 0.3F;
+          }
+        }
+        m = (m > 0) ? m : (float)(0.3 + 0.7 * std::pow(k, 1.4F));
+
+        float max_color = -1e6F;
+	float min_color = +1e6F;
+
+	// tone map image
+
+	bits  = (BYTE*)FreeImage_GetBits(dib);
+	Ybits = (BYTE*)FreeImage_GetBits(Y);
+
+	if((a == 1) && (c == 0)) {
+		// when using default values, use a fastest code
+
+		for(y = 0; y < height; y++) {
+			float *Y     = (float*)Ybits;
+			float *color = (float*)bits;
+
+			for(x = 0; x < width; x++) {
+				I_a = Y[x];	// luminance(x, y)
+				for (i = 0; i < 3; i++) {
+                                  *color /= (*color + std::pow(f * I_a, m));
+
+                                  max_color =
+                                      (*color > max_color) ? *color : max_color;
+                                  min_color =
+                                      (*color < min_color) ? *color : min_color;
+
+                                  color++;
+                                }
+			}
+			// next line
+			bits  += dib_pitch;
+			Ybits += y_pitch;
+		}
+	} else {
+		// complete algorithm
+
+		// channel averages
+
+		Cav[0] = Cav[1] = Cav[2] = 0;
+		if((a != 1) && (c != 0)) {
+			// channel averages are not needed when (a == 1) or (c == 0)
+			bits = (BYTE*)FreeImage_GetBits(dib);
+			for(y = 0; y < height; y++) {
+				float *color = (float*)bits;
+				for(x = 0; x < width; x++) {
+					for(i = 0; i < 3; i++) {
+						Cav[i] += *color;
+						color++;
+					}
+				}
+				// next line
+				bits += dib_pitch;
+			}
+			const float image_size = (float)width * height;
+			for(i = 0; i < 3; i++) {
+				Cav[i] /= image_size;
+			}
+		}
+
+		// perform tone mapping
+
+		bits = (BYTE*)FreeImage_GetBits(dib);
+		for(y = 0; y < height; y++) {
+			const float *Y     = (float*)Ybits;
+			float *color = (float*)bits;
+
+			for(x = 0; x < width; x++) {
+				L = Y[x];	// luminance(x, y)
+				for (i = 0; i < 3; i++) {
+					I_l = c * *color + (1-c) * L;
+					I_g = c * Cav[i] + (1-c) * Lav;
+					I_a = a * I_l + (1-a) * I_g;
+                                        *color /=
+                                            (*color + std::pow(f * I_a, m));
+
+                                        max_color = (*color > max_color)
+                                                        ? *color
+                                                        : max_color;
+                                        min_color = (*color < min_color) ? *color : min_color;
+
+					color++;
+				}
+			}
+			// next line
+			bits  += dib_pitch;
+			Ybits += y_pitch;
+		}
+	}
+
+	// normalize intensities
+
+	if(max_color != min_color) {
+		bits = (BYTE*)FreeImage_GetBits(dib);
+		const float range = max_color - min_color;
+		for(y = 0; y < height; y++) {
+			float *color = (float*)bits;
+			for(x = 0; x < width; x++) {
+				for(i = 0; i < 3; i++) {
+					*color = (*color - min_color) / range;
+					color++;
+				}
+			}
+			// next line
+			bits += dib_pitch;
+		}
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//  Main algorithm
+// ----------------------------------------------------------
+
+/**
+Apply the global/local tone mapping operator to a RGBF image and convert to 24-bit RGB<br>
+User parameters control intensity, contrast, and level of adaptation
+@param src Input RGBF image
+@param intensity Overall intensity in range [-8:8] : default to 0
+@param contrast Contrast in range [0.3:1) : default to 0
+@param adaptation Adaptation in range [0:1] : default to 1
+@param color_correction Color correction in range [0:1] : default to 0
+@return Returns a 24-bit RGB image if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV 
+FreeImage_TmoReinhard05Ex(FIBITMAP *src, double intensity, double contrast, double adaptation, double color_correction) {
+	if(!FreeImage_HasPixels(src)) return NULL;
+
+	// working RGBF variable
+	FIBITMAP *dib = NULL, *Y = NULL;
+
+	dib = FreeImage_ConvertToRGBF(src);
+	if(!dib) return NULL;
+
+	// get the Luminance channel
+	Y = ConvertRGBFToY(dib);
+	if(!Y) {
+		FreeImage_Unload(dib);
+		return NULL;
+	}
+
+	// perform the tone mapping
+	ToneMappingReinhard05(dib, Y, (float)intensity, (float)contrast, (float)adaptation, (float)color_correction);
+	// not needed anymore
+	FreeImage_Unload(Y);
+	// clamp image highest values to display white, then convert to 24-bit RGB
+	FIBITMAP *dst = ClampConvertRGBFTo24(dib);
+
+	// clean-up and return
+	FreeImage_Unload(dib);
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+
+	return dst;
+}
+
+/**
+Apply the global tone mapping operator to a RGBF image and convert to 24-bit RGB<br>
+User parameters control intensity and contrast
+@param src Input RGBF image
+@param intensity Overall intensity in range [-8:8] : default to 0
+@param contrast Contrast in range [0.3:1) : default to 0
+@return Returns a 24-bit RGB image if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV 
+FreeImage_TmoReinhard05(FIBITMAP *src, double intensity, double contrast) {
+	return FreeImage_TmoReinhard05Ex(src, intensity, contrast, 1, 0);
+}
diff --git a/files/Source/FreeImageIO.h b/files/Source/FreeImageIO.h
new file mode 100644
index 0000000..c846b5b
--- /dev/null
+++ b/files/Source/FreeImageIO.h
@@ -0,0 +1,63 @@
+// ==========================================================
+// 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!
+// ==========================================================
+
+#ifndef FREEIMAGEIO_H
+#define FREEIMAGEIO_H
+
+#ifndef FREEIMAGE_H
+#include "FreeImage.h"
+#endif
+
+// ----------------------------------------------------------
+
+FI_STRUCT (FIMEMORYHEADER) {
+	/**
+	Flag used to remember to delete the 'data' buffer.
+	When the buffer is a wrapped buffer, it is read-only, no need to delete it. 
+	When the buffer is a read/write buffer, it is allocated dynamically and must be deleted when no longer needed.
+	*/
+	BOOL delete_me;
+	/**
+	file_length is equal to the input buffer size when the buffer is a wrapped buffer, i.e. file_length == data_length. 
+	file_length is the amount of the written bytes when the buffer is a read/write buffer.
+	*/
+	long file_length;
+	/**
+	When using read-only input buffers, data_length is equal to the input buffer size, i.e. the file_length.
+	When using read/write buffers, data_length is the size of the allocated buffer, 
+	whose size is greater than or equal to file_length.
+	*/
+	long data_length;
+	/**
+	start buffer address
+	*/
+	void *data;
+	/**
+	Current position into the memory stream
+	*/
+	long current_position;
+};
+
+void SetDefaultIO(FreeImageIO *io);
+
+void SetMemoryIO(FreeImageIO *io);
+
+#endif // !FREEIMAGEIO_H
diff --git a/files/Source/FreeImageToolkit/BSplineRotate.cpp b/files/Source/FreeImageToolkit/BSplineRotate.cpp
new file mode 100644
index 0000000..690db87
--- /dev/null
+++ b/files/Source/FreeImageToolkit/BSplineRotate.cpp
@@ -0,0 +1,730 @@
+// ==========================================================
+// Bitmap rotation using B-Splines
+//
+// Design and implementation by
+// - Philippe Thévenaz (philippe.thevenaz@epfl.ch)
+// Adaptation for FreeImage 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!
+// ==========================================================
+
+/* 
+==========================================================
+This code was taken and adapted from the following reference : 
+
+[1] Philippe Thévenaz, Spline interpolation, a C source code 
+implementation. http://bigwww.epfl.ch/thevenaz/
+
+It implements ideas described in the following papers : 
+
+[2] Unser M., Splines: A Perfect Fit for Signal and Image Processing. 
+IEEE Signal Processing Magazine, vol. 16, no. 6, pp. 22-38, November 1999. 
+
+[3] Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part I--Theory.
+IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 821-832, February 1993. 
+
+[4] Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part II--Efficient Design and Applications.
+IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 834-848, February 1993.
+
+========================================================== 
+*/
+
+
+#include <float.h>
+#include "FreeImage.h"
+#include "Utilities.h"
+
+#define PI	((double)3.14159265358979323846264338327950288419716939937510)
+
+#define ROTATE_QUADRATIC 2L	// Use B-splines of degree 2 (quadratic interpolation)
+#define ROTATE_CUBIC     3L	// Use B-splines of degree 3 (cubic interpolation)
+#define ROTATE_QUARTIC   4L	// Use B-splines of degree 4 (quartic interpolation)
+#define ROTATE_QUINTIC   5L	// Use B-splines of degree 5 (quintic interpolation)
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Prototypes definition
+
+static void ConvertToInterpolationCoefficients(double *c, long DataLength, double *z, long NbPoles,	double Tolerance);
+static double InitialCausalCoefficient(double *c, long DataLength, double z, double Tolerance);
+static void GetColumn(double *Image, long Width, long x, double *Line, long Height);
+static void	GetRow(double *Image, long y, double *Line, long Width);
+static double InitialAntiCausalCoefficient(double *c, long DataLength, double z);
+static void	PutColumn(double *Image, long Width, long x, double *Line, long Height);
+static void	PutRow(double *Image, long y, double *Line, long Width);
+static bool SamplesToCoefficients(double *Image, long Width, long Height, long spline_degree);
+static double InterpolatedValue(double *Bcoeff, long Width, long Height, double x, double y, long spline_degree);
+
+static FIBITMAP * Rotate8Bit(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, long spline_degree, BOOL use_mask);
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Coefficients routines
+
+/**
+ ConvertToInterpolationCoefficients
+
+ @param c Input samples --> output coefficients
+ @param DataLength Number of samples or coefficients
+ @param z Poles
+ @param NbPoles Number of poles
+ @param Tolerance Admissible relative error
+*/
+static void 
+ConvertToInterpolationCoefficients(double *c, long DataLength, double *z, long NbPoles,	double Tolerance) {
+	double	Lambda = 1;
+	long	n, k;
+
+	// special case required by mirror boundaries
+	if(DataLength == 1L) {
+		return;
+	}
+	// compute the overall gain
+	for(k = 0L; k < NbPoles; k++) {
+		Lambda = Lambda * (1.0 - z[k]) * (1.0 - 1.0 / z[k]);
+	}
+	// apply the gain 
+	for (n = 0L; n < DataLength; n++) {
+		c[n] *= Lambda;
+	}
+	// loop over all poles 
+	for (k = 0L; k < NbPoles; k++) {
+		// causal initialization 
+		c[0] = InitialCausalCoefficient(c, DataLength, z[k], Tolerance);
+		// causal recursion 
+		for (n = 1L; n < DataLength; n++) {
+			c[n] += z[k] * c[n - 1L];
+		}
+		// anticausal initialization 
+		c[DataLength - 1L] = InitialAntiCausalCoefficient(c, DataLength, z[k]);
+		// anticausal recursion 
+		for (n = DataLength - 2L; 0 <= n; n--) {
+			c[n] = z[k] * (c[n + 1L] - c[n]);
+		}
+	}
+} 
+
+/**
+ InitialCausalCoefficient
+
+ @param c Coefficients
+ @param DataLength Number of coefficients
+ @param z Actual pole
+ @param Tolerance Admissible relative error
+ @return
+*/
+static double 
+InitialCausalCoefficient(double	*c, long DataLength, double	z, double Tolerance) {
+	double	Sum, zn, z2n, iz;
+	long	n, Horizon;
+
+	// this initialization corresponds to mirror boundaries 
+	Horizon = DataLength;
+	if(Tolerance > 0) {
+		Horizon = (long)ceil(log(Tolerance) / log(fabs(z)));
+	}
+	if(Horizon < DataLength) {
+		// accelerated loop
+		zn = z;
+		Sum = c[0];
+		for (n = 1L; n < Horizon; n++) {
+			Sum += zn * c[n];
+			zn *= z;
+		}
+		return(Sum);
+	}
+	else {
+		// full loop 
+		zn = z;
+		iz = 1.0 / z;
+		z2n = pow(z, (double)(DataLength - 1L));
+		Sum = c[0] + z2n * c[DataLength - 1L];
+		z2n *= z2n * iz;
+		for (n = 1L; n <= DataLength - 2L; n++) {
+			Sum += (zn + z2n) * c[n];
+			zn *= z;
+			z2n *= iz;
+		}
+		return(Sum / (1.0 - zn * zn));
+	}
+}
+
+/**
+ GetColumn
+
+ @param Image Input image array
+ @param Width Width of the image
+ @param x x coordinate of the selected line
+ @param Line Output linear array
+ @param Height Length of the line
+*/
+static void 
+GetColumn(double *Image, long Width, long x, double *Line, long Height) {
+	long y;
+
+	Image = Image + x;
+	for(y = 0L; y < Height; y++) {
+		Line[y] = (double)*Image;
+		Image += Width;
+	}
+}
+
+/**
+ GetRow
+
+ @param Image Input image array
+ @param y y coordinate of the selected line
+ @param Line Output linear array
+ @param Width Length of the line
+*/
+static void	
+GetRow(double *Image, long y, double *Line, long Width) {
+	long	x;
+
+	Image = Image + (y * Width);
+	for(x = 0L; x < Width; x++) {
+		Line[x] = (double)*Image++;
+	}
+}
+
+/**
+ InitialAntiCausalCoefficient
+
+ @param c Coefficients
+ @param DataLength Number of samples or coefficients
+ @param z Actual pole
+ @return
+*/
+static double 
+InitialAntiCausalCoefficient(double	*c, long DataLength, double	z) {
+	// this initialization corresponds to mirror boundaries
+	return((z / (z * z - 1.0)) * (z * c[DataLength - 2L] + c[DataLength - 1L]));
+}
+
+/**
+ PutColumn
+
+ @param Image Output image array
+ @param Width Width of the image
+ @param x x coordinate of the selected line
+ @param Line Input linear array
+ @param Height Length of the line and height of the image
+*/
+static void	
+PutColumn(double *Image, long Width, long x, double *Line, long Height) {
+	long	y;
+
+	Image = Image + x;
+	for(y = 0L; y < Height; y++) {
+		*Image = (double)Line[y];
+		Image += Width;
+	}
+}
+
+/**
+ PutRow
+
+ @param Image Output image array
+ @param y y coordinate of the selected line
+ @param Line Input linear array
+ @param Width length of the line and width of the image
+*/
+static void	
+PutRow(double *Image, long y, double *Line, long Width) {
+	long	x;
+
+	Image = Image + (y * Width);
+	for(x = 0L; x < Width; x++) {
+		*Image++ = (double)Line[x];
+	}
+}
+
+/**
+ SamplesToCoefficients.<br>
+ Implement the algorithm that converts the image samples into B-spline coefficients. 
+ This efficient procedure essentially relies on the three papers cited above; 
+ data are processed in-place. 
+ Even though this algorithm is robust with respect to quantization, 
+ we advocate the use of a floating-point format for the data. 
+
+ @param Image Input / Output image (in-place processing)
+ @param Width Width of the image
+ @param Height Height of the image
+ @param spline_degree Degree of the spline model
+ @return Returns true if success, false otherwise
+*/
+static bool	
+SamplesToCoefficients(double *Image, long Width, long Height, long spline_degree) {
+	double	*Line;
+	double	Pole[2];
+	long	NbPoles;
+	long	x, y;
+
+	// recover the poles from a lookup table
+	switch (spline_degree) {
+		case 2L:
+			NbPoles = 1L;
+			Pole[0] = sqrt(8.0) - 3.0;
+			break;
+		case 3L:
+			NbPoles = 1L;
+			Pole[0] = sqrt(3.0) - 2.0;
+			break;
+		case 4L:
+			NbPoles = 2L;
+			Pole[0] = sqrt(664.0 - sqrt(438976.0)) + sqrt(304.0) - 19.0;
+			Pole[1] = sqrt(664.0 + sqrt(438976.0)) - sqrt(304.0) - 19.0;
+			break;
+		case 5L:
+			NbPoles = 2L;
+			Pole[0] = sqrt(135.0 / 2.0 - sqrt(17745.0 / 4.0)) + sqrt(105.0 / 4.0)
+				- 13.0 / 2.0;
+			Pole[1] = sqrt(135.0 / 2.0 + sqrt(17745.0 / 4.0)) - sqrt(105.0 / 4.0)
+				- 13.0 / 2.0;
+			break;
+		default:
+			// Invalid spline degree
+			return false;
+	}
+
+	// convert the image samples into interpolation coefficients 
+
+	// in-place separable process, along x 
+	Line = (double *)malloc(Width * sizeof(double));
+	if (Line == NULL) {
+		// Row allocation failed
+		return false;
+	}
+	for (y = 0L; y < Height; y++) {
+		GetRow(Image, y, Line, Width);
+		ConvertToInterpolationCoefficients(Line, Width, Pole, NbPoles, DBL_EPSILON);
+		PutRow(Image, y, Line, Width);
+	}
+	free(Line);
+
+	// in-place separable process, along y 
+	Line = (double *)malloc(Height * sizeof(double));
+	if (Line == NULL) {
+		// Column allocation failed
+		return false;
+	}
+	for (x = 0L; x < Width; x++) {
+		GetColumn(Image, Width, x, Line, Height);
+		ConvertToInterpolationCoefficients(Line, Height, Pole, NbPoles, DBL_EPSILON);
+		PutColumn(Image, Width, x, Line, Height);
+	}
+	free(Line);
+
+	return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interpolation routines
+
+/**
+Perform the bidimensional interpolation of an image.
+Given an array of spline coefficients, return the value of 
+the underlying continuous spline model, sampled at the location (x, y). 
+The model degree can be 2 (quadratic), 3 (cubic), 4 (quartic), or 5 (quintic).
+
+@param Bcoeff Input B-spline array of coefficients
+@param Width Width of the image
+@param Height Height of the image
+@param x x coordinate where to interpolate
+@param y y coordinate where to interpolate
+@param spline_degree Degree of the spline model
+@return Returns the value of the underlying continuous spline model, 
+sampled at the location (x, y)
+*/
+static double 
+InterpolatedValue(double *Bcoeff, long Width, long Height, double x, double y, long spline_degree) {
+	double	*p;
+	double	xWeight[6], yWeight[6];
+	double	interpolated;
+	double	w, w2, w4, t, t0, t1;
+	long	xIndex[6], yIndex[6];
+	long	Width2 = 2L * Width - 2L, Height2 = 2L * Height - 2L;
+	long	i, j, k;
+
+	// compute the interpolation indexes
+	if (spline_degree & 1L) {
+		i = (long)floor(x) - spline_degree / 2L;
+		j = (long)floor(y) - spline_degree / 2L;
+		for(k = 0; k <= spline_degree; k++) {
+			xIndex[k] = i++;
+			yIndex[k] = j++;
+		}
+	}
+	else {
+		i = (long)floor(x + 0.5) - spline_degree / 2L;
+		j = (long)floor(y + 0.5) - spline_degree / 2L;
+		for (k = 0; k <= spline_degree; k++) {
+			xIndex[k] = i++;
+			yIndex[k] = j++;
+		}
+	}
+
+	// compute the interpolation weights
+	switch (spline_degree) {
+		case 2L:
+			/* x */
+			w = x - (double)xIndex[1];
+			xWeight[1] = 3.0 / 4.0 - w * w;
+			xWeight[2] = (1.0 / 2.0) * (w - xWeight[1] + 1.0);
+			xWeight[0] = 1.0 - xWeight[1] - xWeight[2];
+			/* y */
+			w = y - (double)yIndex[1];
+			yWeight[1] = 3.0 / 4.0 - w * w;
+			yWeight[2] = (1.0 / 2.0) * (w - yWeight[1] + 1.0);
+			yWeight[0] = 1.0 - yWeight[1] - yWeight[2];
+			break;
+		case 3L:
+			/* x */
+			w = x - (double)xIndex[1];
+			xWeight[3] = (1.0 / 6.0) * w * w * w;
+			xWeight[0] = (1.0 / 6.0) + (1.0 / 2.0) * w * (w - 1.0) - xWeight[3];
+			xWeight[2] = w + xWeight[0] - 2.0 * xWeight[3];
+			xWeight[1] = 1.0 - xWeight[0] - xWeight[2] - xWeight[3];
+			/* y */
+			w = y - (double)yIndex[1];
+			yWeight[3] = (1.0 / 6.0) * w * w * w;
+			yWeight[0] = (1.0 / 6.0) + (1.0 / 2.0) * w * (w - 1.0) - yWeight[3];
+			yWeight[2] = w + yWeight[0] - 2.0 * yWeight[3];
+			yWeight[1] = 1.0 - yWeight[0] - yWeight[2] - yWeight[3];
+			break;
+		case 4L:
+			/* x */
+			w = x - (double)xIndex[2];
+			w2 = w * w;
+			t = (1.0 / 6.0) * w2;
+			xWeight[0] = 1.0 / 2.0 - w;
+			xWeight[0] *= xWeight[0];
+			xWeight[0] *= (1.0 / 24.0) * xWeight[0];
+			t0 = w * (t - 11.0 / 24.0);
+			t1 = 19.0 / 96.0 + w2 * (1.0 / 4.0 - t);
+			xWeight[1] = t1 + t0;
+			xWeight[3] = t1 - t0;
+			xWeight[4] = xWeight[0] + t0 + (1.0 / 2.0) * w;
+			xWeight[2] = 1.0 - xWeight[0] - xWeight[1] - xWeight[3] - xWeight[4];
+			/* y */
+			w = y - (double)yIndex[2];
+			w2 = w * w;
+			t = (1.0 / 6.0) * w2;
+			yWeight[0] = 1.0 / 2.0 - w;
+			yWeight[0] *= yWeight[0];
+			yWeight[0] *= (1.0 / 24.0) * yWeight[0];
+			t0 = w * (t - 11.0 / 24.0);
+			t1 = 19.0 / 96.0 + w2 * (1.0 / 4.0 - t);
+			yWeight[1] = t1 + t0;
+			yWeight[3] = t1 - t0;
+			yWeight[4] = yWeight[0] + t0 + (1.0 / 2.0) * w;
+			yWeight[2] = 1.0 - yWeight[0] - yWeight[1] - yWeight[3] - yWeight[4];
+			break;
+		case 5L:
+			/* x */
+			w = x - (double)xIndex[2];
+			w2 = w * w;
+			xWeight[5] = (1.0 / 120.0) * w * w2 * w2;
+			w2 -= w;
+			w4 = w2 * w2;
+			w -= 1.0 / 2.0;
+			t = w2 * (w2 - 3.0);
+			xWeight[0] = (1.0 / 24.0) * (1.0 / 5.0 + w2 + w4) - xWeight[5];
+			t0 = (1.0 / 24.0) * (w2 * (w2 - 5.0) + 46.0 / 5.0);
+			t1 = (-1.0 / 12.0) * w * (t + 4.0);
+			xWeight[2] = t0 + t1;
+			xWeight[3] = t0 - t1;
+			t0 = (1.0 / 16.0) * (9.0 / 5.0 - t);
+			t1 = (1.0 / 24.0) * w * (w4 - w2 - 5.0);
+			xWeight[1] = t0 + t1;
+			xWeight[4] = t0 - t1;
+			/* y */
+			w = y - (double)yIndex[2];
+			w2 = w * w;
+			yWeight[5] = (1.0 / 120.0) * w * w2 * w2;
+			w2 -= w;
+			w4 = w2 * w2;
+			w -= 1.0 / 2.0;
+			t = w2 * (w2 - 3.0);
+			yWeight[0] = (1.0 / 24.0) * (1.0 / 5.0 + w2 + w4) - yWeight[5];
+			t0 = (1.0 / 24.0) * (w2 * (w2 - 5.0) + 46.0 / 5.0);
+			t1 = (-1.0 / 12.0) * w * (t + 4.0);
+			yWeight[2] = t0 + t1;
+			yWeight[3] = t0 - t1;
+			t0 = (1.0 / 16.0) * (9.0 / 5.0 - t);
+			t1 = (1.0 / 24.0) * w * (w4 - w2 - 5.0);
+			yWeight[1] = t0 + t1;
+			yWeight[4] = t0 - t1;
+			break;
+		default:
+			// Invalid spline degree
+			return 0;
+	}
+
+	// apply the mirror boundary conditions
+	for(k = 0; k <= spline_degree; k++) {
+		xIndex[k] = (Width == 1L) ? (0L) : ((xIndex[k] < 0L) ?
+			(-xIndex[k] - Width2 * ((-xIndex[k]) / Width2))
+			: (xIndex[k] - Width2 * (xIndex[k] / Width2)));
+		if (Width <= xIndex[k]) {
+			xIndex[k] = Width2 - xIndex[k];
+		}
+		yIndex[k] = (Height == 1L) ? (0L) : ((yIndex[k] < 0L) ?
+			(-yIndex[k] - Height2 * ((-yIndex[k]) / Height2))
+			: (yIndex[k] - Height2 * (yIndex[k] / Height2)));
+		if (Height <= yIndex[k]) {
+			yIndex[k] = Height2 - yIndex[k];
+		}
+	}
+
+	// perform interpolation
+	interpolated = 0.0;
+	for(j = 0; j <= spline_degree; j++) {
+		p = Bcoeff + (yIndex[j] * Width);
+		w = 0.0;
+		for(i = 0; i <= spline_degree; i++) {
+			w += xWeight[i] * p[xIndex[i]];
+		}
+		interpolated += yWeight[j] * w;
+	}
+
+	return interpolated;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// FreeImage implementation
+
+
+/** 
+ Image translation and rotation using B-Splines.
+
+ @param dib Input 8-bit greyscale image
+ @param angle Output image rotation in degree
+ @param x_shift Output image horizontal shift
+ @param y_shift Output image vertical shift
+ @param x_origin Output origin of the x-axis
+ @param y_origin Output origin of the y-axis
+ @param spline_degree Output degree of the B-spline model
+ @param use_mask Whether or not to mask the image
+ @return Returns the translated & rotated dib if successful, returns NULL otherwise
+*/
+static FIBITMAP * 
+Rotate8Bit(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, long spline_degree, BOOL use_mask) {
+	double	*ImageRasterArray;
+	double	p;
+	double	a11, a12, a21, a22;
+	double	x0, y0, x1, y1;
+	long	x, y;
+	long	spline;
+	bool	bResult;
+
+	int bpp = FreeImage_GetBPP(dib);
+	if(bpp != 8) {
+		return NULL;
+	}
+	
+	int width = FreeImage_GetWidth(dib);
+	int height = FreeImage_GetHeight(dib);
+	switch(spline_degree) {
+		case ROTATE_QUADRATIC:
+			spline = 2L;	// Use splines of degree 2 (quadratic interpolation)
+			break;
+		case ROTATE_CUBIC:
+			spline = 3L;	// Use splines of degree 3 (cubic interpolation)
+			break;
+		case ROTATE_QUARTIC:
+			spline = 4L;	// Use splines of degree 4 (quartic interpolation)
+			break;
+		case ROTATE_QUINTIC:
+			spline = 5L;	// Use splines of degree 5 (quintic interpolation)
+			break;
+		default:
+			spline = 3L;
+	}
+
+	// allocate output image
+	FIBITMAP *dst = FreeImage_Allocate(width, height, bpp);
+	if(!dst)
+		return NULL;
+	// buid a grey scale palette
+	RGBQUAD *pal = FreeImage_GetPalette(dst);
+	for(int i = 0; i < 256; i++) {
+		pal[i].rgbRed = pal[i].rgbGreen = pal[i].rgbBlue = (BYTE)i;
+	}
+
+	// allocate a temporary array
+	ImageRasterArray = (double*)malloc(width * height * sizeof(double));
+	if(!ImageRasterArray) {
+		FreeImage_Unload(dst);
+		return NULL;
+	}
+	// copy data samples
+	for(y = 0; y < height; y++) {
+		double *pImage = &ImageRasterArray[y*width];
+		BYTE *src_bits = FreeImage_GetScanLine(dib, height-1-y);
+
+		for(x = 0; x < width; x++) {
+			pImage[x] = (double)src_bits[x];
+		}
+	}
+
+	// convert between a representation based on image samples
+	// and a representation based on image B-spline coefficients
+	bResult = SamplesToCoefficients(ImageRasterArray, width, height, spline);
+	if(!bResult) {
+		FreeImage_Unload(dst);
+		free(ImageRasterArray);
+		return NULL;
+	}
+
+	// prepare the geometry
+	angle *= PI / 180.0;
+	a11 = cos(angle);
+	a12 = -sin(angle);
+	a21 = sin(angle);
+	a22 = cos(angle);
+	x0 = a11 * (x_shift + x_origin) + a12 * (y_shift + y_origin);
+	y0 = a21 * (x_shift + x_origin) + a22 * (y_shift + y_origin);
+	x_shift = x_origin - x0;
+	y_shift = y_origin - y0;
+
+	// visit all pixels of the output image and assign their value
+	for(y = 0; y < height; y++) {
+		BYTE *dst_bits = FreeImage_GetScanLine(dst, height-1-y);
+		
+		x0 = a12 * (double)y + x_shift;
+		y0 = a22 * (double)y + y_shift;
+
+		for(x = 0; x < width; x++) {
+			x1 = x0 + a11 * (double)x;
+			y1 = y0 + a21 * (double)x;
+			if(use_mask) {
+				if((x1 <= -0.5) || (((double)width - 0.5) <= x1) || (y1 <= -0.5) || (((double)height - 0.5) <= y1)) {
+					p = 0;
+				}
+				else {
+					p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline);
+				}
+			}
+			else {
+				p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline);
+			}
+			// clamp and convert to BYTE
+			dst_bits[x] = (BYTE)MIN(MAX((int)0, (int)(p + 0.5)), (int)255);
+		}
+	}
+
+	// free working array and return
+	free(ImageRasterArray);
+
+	return dst;
+}
+
+/** 
+ Image rotation using a 3rd order (cubic) B-Splines.
+
+ @param dib Input dib (8, 24 or 32-bit)
+ @param angle Output image rotation
+ @param x_shift Output image horizontal shift
+ @param y_shift Output image vertical shift
+ @param x_origin Output origin of the x-axis
+ @param y_origin Output origin of the y-axis
+ @param use_mask Whether or not to mask the image
+ @return Returns the translated & rotated dib if successful, returns NULL otherwise
+*/
+FIBITMAP * DLL_CALLCONV 
+FreeImage_RotateEx(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask) {
+
+	int x, y, bpp;
+	int channel, nb_channels;
+	BYTE *src_bits, *dst_bits;
+	FIBITMAP *src8 = NULL, *dst8 = NULL, *dst = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	try {
+
+		bpp = FreeImage_GetBPP(dib);
+
+		if(bpp == 8) {
+			FIBITMAP *dst_8 = Rotate8Bit(dib, angle, x_shift, y_shift, x_origin, y_origin, ROTATE_CUBIC, use_mask);
+			if(dst_8) {
+				// copy metadata from src to dst
+				FreeImage_CloneMetadata(dst_8, dib);
+			}
+			return dst_8;
+		}
+		if((bpp == 24) || (bpp == 32)) {
+			// allocate dst image
+			int width  = FreeImage_GetWidth(dib);
+			int height = FreeImage_GetHeight(dib);
+			if( bpp == 24 ) {
+				dst = FreeImage_Allocate(width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+			} else {
+				dst = FreeImage_Allocate(width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+			}
+			if(!dst) throw(1);
+
+			// allocate a temporary 8-bit dib (no need to build a palette)
+			src8 = FreeImage_Allocate(width, height, 8);
+			if(!src8) throw(1);
+
+			// process each channel separately
+			// -------------------------------
+			nb_channels = (bpp / 8);
+
+			for(channel = 0; channel < nb_channels; channel++) {
+				// extract channel from source dib
+				for(y = 0; y < height; y++) {
+					src_bits = FreeImage_GetScanLine(dib, y);
+					dst_bits = FreeImage_GetScanLine(src8, y);
+					for(x = 0; x < width; x++) {
+						dst_bits[x] = src_bits[channel];
+						src_bits += nb_channels;
+					}
+				}
+
+				// process channel
+				dst8 = Rotate8Bit(src8, angle, x_shift, y_shift, x_origin, y_origin, ROTATE_CUBIC, use_mask);
+				if(!dst8) throw(1);
+
+				// insert channel to destination dib
+				for(y = 0; y < height; y++) {
+					src_bits = FreeImage_GetScanLine(dst8, y);
+					dst_bits = FreeImage_GetScanLine(dst, y);
+					for(x = 0; x < width; x++) {
+						dst_bits[channel] = src_bits[x];
+						dst_bits += nb_channels;
+					}
+				}
+
+				FreeImage_Unload(dst8);
+			}
+
+			FreeImage_Unload(src8);
+
+			// copy metadata from src to dst
+			FreeImage_CloneMetadata(dst, dib);
+			
+			return dst;
+		}
+	} catch(int) {
+		if(src8) FreeImage_Unload(src8);
+		if(dst8) FreeImage_Unload(dst8);
+		if(dst)  FreeImage_Unload(dst);
+	}
+
+	return NULL;
+}
diff --git a/files/Source/FreeImageToolkit/Background.cpp b/files/Source/FreeImageToolkit/Background.cpp
new file mode 100644
index 0000000..06b31aa
--- /dev/null
+++ b/files/Source/FreeImageToolkit/Background.cpp
@@ -0,0 +1,895 @@
+// ==========================================================
+// Background filling routines
+//
+// Design and implementation by
+// - Carsten Klein (c.klein@datagis.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"
+
+/** @brief Determines, whether a palletized image is visually greyscale or not.
+ 
+ Unlike with FreeImage_GetColorType, which returns either FIC_MINISBLACK or
+ FIC_MINISWHITE for a greyscale image with a linear ramp palette, the return  
+ value of this function does not depend on the palette's order, but only on the
+ palette's individual colors.
+ @param dib The image to be tested.
+ @return Returns TRUE if the palette of the image specified contains only
+ greyscales, FALSE otherwise.
+ */
+static BOOL
+IsVisualGreyscaleImage(FIBITMAP *dib) {
+
+	switch (FreeImage_GetBPP(dib)) {
+		case 1:
+		case 4:
+		case 8: {
+			unsigned ncolors = FreeImage_GetColorsUsed(dib);
+			RGBQUAD *rgb = FreeImage_GetPalette(dib);
+			for (unsigned i = 0; i< ncolors; i++) {
+				if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) {
+					return FALSE;
+				}
+			}
+			return TRUE;
+		}
+		default: {
+			return (FreeImage_GetColorType(dib) == FIC_MINISBLACK);
+		}
+	}
+}
+
+/** @brief Looks up a specified color in a FIBITMAP's palette and returns the color's
+ palette index or -1 if the color was not found.
+
+ Unlike with FreeImage_GetColorType, which returns either FIC_MINISBLACK or
+ FIC_MINISWHITE for a greyscale image with a linear ramp palette, the return
+ value of this function does not depend on the palette's order, but only on the
+ palette's individual colors.
+ @param dib The image, whose palette should be searched through.
+ @param color The color to be searched in the palette.
+ @param options Options that affect the color search process.
+ @param color_type A pointer, that optionally specifies the image's color type as
+ returned by FreeImage_GetColorType. If invalid or NULL, this function determines the
+ color type with FreeImage_GetColorType.
+ @return Returns the specified color's palette index, the color's rgbReserved member
+ if option FI_COLOR_ALPHA_IS_INDEX was specified or -1, if the color was not found
+ in the image's palette or if the specified image is non-palletized.
+ */
+static int
+GetPaletteIndex(FIBITMAP *dib, const RGBQUAD *color, int options, FREE_IMAGE_COLOR_TYPE *color_type) {
+	
+	int result = -1;
+	
+	if ((!dib) || (!color)) {
+		return result;
+	}
+	
+	int bpp = FreeImage_GetBPP(dib);
+
+	// First check trivial case: return color->rgbReserved if only
+	// FI_COLOR_ALPHA_IS_INDEX is set.
+	if ((options & FI_COLOR_ALPHA_IS_INDEX) == FI_COLOR_ALPHA_IS_INDEX) {
+		if (bpp == 1) {
+			return color->rgbReserved & 0x01;
+		} else if (bpp == 4) {
+			return color->rgbReserved & 0x0F;
+		}
+		return color->rgbReserved;
+	}
+	
+	if (bpp == 8) {
+		FREE_IMAGE_COLOR_TYPE ct =
+			(color_type == NULL || *color_type < 0) ?
+				FreeImage_GetColorType(dib) : *color_type;
+		if (ct == FIC_MINISBLACK) {
+			return GREY(color->rgbRed, color->rgbGreen, color->rgbBlue);
+		}
+		if (ct == FIC_MINISWHITE) {
+			return 255 - GREY(color->rgbRed, color->rgbGreen, color->rgbBlue);
+		}
+	} else if (bpp > 8) {
+		// for palettized images only
+		return result;
+	}
+
+	if (options & FI_COLOR_FIND_EQUAL_COLOR) {
+		
+		// Option FI_COLOR_ALPHA_IS_INDEX is implicit here so, set
+		// index to color->rgbReserved
+		result = color->rgbReserved;
+		if (bpp == 1) {
+			result &= 0x01;
+		} else if (bpp == 4) {
+			result &= 0x0F;
+		}		
+
+		unsigned ucolor;
+		if (!IsVisualGreyscaleImage(dib)) {
+			ucolor = (*((unsigned *)color)) & 0xFFFFFF;
+		} else {
+			ucolor = GREY(color->rgbRed, color->rgbGreen, color->rgbBlue) * 0x010101;
+			//ucolor = (ucolor | (ucolor << 8) | (ucolor << 16));
+		}
+		unsigned ncolors = FreeImage_GetColorsUsed(dib);
+		unsigned *palette = (unsigned *)FreeImage_GetPalette(dib);
+		for (unsigned i = 0; i < ncolors; i++) {
+			if ((palette[i] & 0xFFFFFF) == ucolor) {
+				result = i;
+				break;
+			}
+		}
+	} else {
+		unsigned minimum = UINT_MAX;
+		unsigned ncolors = FreeImage_GetColorsUsed(dib);
+		BYTE *palette = (BYTE *)FreeImage_GetPalette(dib);
+		BYTE red, green, blue;
+		if (!IsVisualGreyscaleImage(dib)) {
+			red = color->rgbRed;
+			green = color->rgbGreen;
+			blue = color->rgbBlue;
+		} else {
+			red = GREY(color->rgbRed, color->rgbGreen, color->rgbBlue);
+			green = blue = red;
+		}
+		for (unsigned i = 0; i < ncolors; i++) {
+			unsigned m = abs(palette[FI_RGBA_BLUE] - blue)
+					+ abs(palette[FI_RGBA_GREEN] - green)
+					+ abs(palette[FI_RGBA_RED] - red);
+			if (m < minimum) {
+				minimum = m;
+				result = i;
+				if (m == 0) {
+					break;
+				}
+			}
+			palette += sizeof(RGBQUAD);
+		}		
+	}
+	return result;
+}
+
+/** @brief Blends an alpha-transparent foreground color over an opaque background
+ color.
+ 
+ This function blends the alpha-transparent foreground color fgcolor over the
+ background color bgcolor. The background color is considered fully opaque,
+ whatever it's alpha value contains, whereas the foreground color is considered
+ to be a real RGBA color with an alpha value, which is used for the blend
+ operation. The resulting color is returned through the blended parameter.
+ @param bgcolor The background color for the blend operation.
+ @param fgcolor The foreground color for the blend operation. This color's alpha
+ value, stored in the rgbReserved member, is the alpha value used for the blend
+ operation.
+ @param blended This out parameter takes the blended color and so, returns it to
+ the caller. This color's alpha value will be 0xFF (255) so, the blended color
+ itself has no transparency. The this argument is not changed, if the function
+ fails. 
+ @return Returns TRUE on success, FALSE otherwise. This function fails if any of
+ the color arguments is a null pointer.
+ */
+static BOOL
+GetAlphaBlendedColor(const RGBQUAD *bgcolor, const RGBQUAD *fgcolor, RGBQUAD *blended) {
+	
+	if ((!bgcolor) || (!fgcolor) || (!blended)) {
+		return FALSE;
+	}
+	
+	BYTE alpha = fgcolor->rgbReserved;
+	BYTE not_alpha = ~alpha;
+	
+	blended->rgbRed   = (BYTE)( ((WORD)fgcolor->rgbRed   * alpha + not_alpha * (WORD)bgcolor->rgbRed)   >> 8 );
+	blended->rgbGreen = (BYTE)( ((WORD)fgcolor->rgbGreen * alpha + not_alpha * (WORD)bgcolor->rgbGreen) >> 8) ;
+	blended->rgbBlue  = (BYTE)( ((WORD)fgcolor->rgbRed   * alpha + not_alpha * (WORD)bgcolor->rgbBlue)  >> 8 );
+	blended->rgbReserved = 0xFF;
+
+	return TRUE;
+}
+
+/** @brief Fills a FIT_BITMAP image with the specified color.
+
+ This function does the dirty work for FreeImage_FillBackground for FIT_BITMAP
+ images.
+ @param dib The image to be filled.
+ @param color The color, the specified image should be filled with.
+ @param options Options that affect the color search process for palletized images.
+ @return Returns TRUE on success, FALSE otherwise. This function fails if any of
+ the dib and color is NULL or the provided image is not a FIT_BITMAP image.
+ */
+static BOOL
+FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) {
+
+	if ((!dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+		return FALSE;;
+	}
+	
+	if (!color) {
+		return FALSE;
+	}
+	
+	const RGBQUAD *color_intl = color;
+	unsigned bpp = FreeImage_GetBPP(dib);
+	unsigned width = FreeImage_GetWidth(dib);
+	unsigned height = FreeImage_GetHeight(dib);
+	
+	FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+	
+	// get a pointer to the first scanline (bottom line)
+	BYTE *src_bits = FreeImage_GetScanLine(dib, 0);
+	BYTE *dst_bits = src_bits;	
+	
+	BOOL supports_alpha = ((bpp >= 24) || ((bpp == 8) && (color_type != FIC_PALETTE)));
+	
+	// Check for RGBA case if bitmap supports alpha 
+	// blending (8-bit greyscale, 24- or 32-bit images)
+	if (supports_alpha && (options & FI_COLOR_IS_RGBA_COLOR)) {
+		
+		if (color->rgbReserved == 0) {
+			// the fill color is fully transparent; we are done
+			return TRUE;
+		}
+		
+		// Only if the fill color is NOT fully opaque, draw it with
+		// the (much) slower FreeImage_DrawLine function and return.
+		// Since we do not have the FreeImage_DrawLine function in this
+		// release, just assume to have an unicolor background and fill
+		// all with an 'alpha-blended' color.
+		if (color->rgbReserved < 255) {
+							
+			// If we will draw on an unicolor background, it's
+			// faster to draw opaque with an alpha blended color.
+			// So, first get the color from the first pixel in the
+			// image (bottom-left pixel).
+			RGBQUAD bgcolor;
+			if (bpp == 8) {
+				bgcolor = FreeImage_GetPalette(dib)[*src_bits];
+			} else {	
+				bgcolor.rgbBlue = src_bits[FI_RGBA_BLUE];
+				bgcolor.rgbGreen = src_bits[FI_RGBA_GREEN];
+				bgcolor.rgbRed = src_bits[FI_RGBA_RED];
+				bgcolor.rgbReserved = 0xFF;
+			}
+			RGBQUAD blend;
+			GetAlphaBlendedColor(&bgcolor, color_intl, &blend);
+			color_intl = &blend;
+		}
+	}
+	
+	int index = (bpp <= 8) ? GetPaletteIndex(dib, color_intl, options, &color_type) : 0;
+	if (index == -1) {
+		// No palette index found for a palletized
+		// image. This should never happen...
+		return FALSE;
+	}
+	
+	// first, build the first scanline (line 0)
+	switch (bpp) {
+		case 1: {
+			unsigned bytes = (width / 8);
+			memset(dst_bits, ((index == 1) ? 0xFF : 0x00), bytes);
+			//int n = width % 8;
+			int n = width & 7;
+			if (n) {
+				if (index == 1) {
+					// set n leftmost bits
+					dst_bits[bytes] |= (0xFF << (8 - n));
+				} else {
+					// clear n leftmost bits
+					dst_bits[bytes] &= (0xFF >> n);
+				}
+			}
+			break;
+		}
+		case 4: {
+			unsigned bytes = (width / 2);
+			memset(dst_bits, (index | (index << 4)), bytes);
+			//if (bytes % 2) {
+			if (bytes & 1) {
+				dst_bits[bytes] &= 0x0F;
+				dst_bits[bytes] |= (index << 4);
+			}
+			break;
+		}
+		case 8: {
+			memset(dst_bits, index, FreeImage_GetLine(dib));
+			break;
+		}
+		case 16: {
+			WORD wcolor = RGBQUAD_TO_WORD(dib, color_intl);
+			for (unsigned x = 0; x < width; x++) {
+				((WORD *)dst_bits)[x] = wcolor;
+			}
+			break;
+		}
+		case 24: {
+			RGBTRIPLE rgbt = *((RGBTRIPLE *)color_intl);
+			for (unsigned x = 0; x < width; x++) {
+				((RGBTRIPLE *)dst_bits)[x] = rgbt;
+			}
+			break;
+		}
+		case 32: {
+			RGBQUAD rgbq;
+			rgbq.rgbBlue = ((RGBTRIPLE *)color_intl)->rgbtBlue;
+			rgbq.rgbGreen = ((RGBTRIPLE *)color_intl)->rgbtGreen;
+			rgbq.rgbRed = ((RGBTRIPLE *)color_intl)->rgbtRed;
+			rgbq.rgbReserved = 0xFF;
+			for (unsigned x = 0; x < width; x++) {
+				((RGBQUAD *)dst_bits)[x] = rgbq;
+			}
+			break;
+		}
+		default:
+			return FALSE;
+	}
+
+	// Then, copy the first scanline into all following scanlines.
+	// 'src_bits' is a pointer to the first scanline and is already
+	// set up correctly.
+	if (src_bits) {
+		unsigned pitch = FreeImage_GetPitch(dib);
+		unsigned bytes = FreeImage_GetLine(dib);
+		dst_bits = src_bits + pitch;
+		for (unsigned y = 1; y < height; y++) {
+			memcpy(dst_bits, src_bits, bytes);
+			dst_bits += pitch;
+		}
+	}
+	return TRUE;
+}
+
+/** @brief Fills an image with the specified color.
+
+ This function sets all pixels of an image to the color provided through the color
+ parameter. Since this should work for all image types supported by FreeImage, the
+ pointer color must point to a memory location, which is at least as large as the
+ image's color value, if this size is greater than 4 bytes. As the color is specified
+ by an RGBQUAD structure for all images of type FIT_BITMAP (including all palletized
+ images), the smallest possible size of this memory is the size of the RGBQUAD structure,
+ which uses 4 bytes.
+
+ So, color must point to a double, if the image to be filled is of type FIT_DOUBLE and
+ point to a RGBF structure if the image is of type FIT_RGBF and so on.
+
+ However, the fill color is always specified through a RGBQUAD structure for all images
+ of type FIT_BITMAP. So, for 32- and 24-bit images, the red, green and blue members of
+ the RGBQUAD structure are directly used for the image's red, green and blue channel
+ respectively. Although alpha transparent RGBQUAD colors are supported, the alpha channel
+ of a 32-bit image never gets modified by this function. A fill color with an alpha value
+ smaller than 255 gets blended with the image's actual background color, which is determined
+ from the image's bottom-left pixel. So, currently using alpha enabled colors, assumes the
+ image to be unicolor before the fill operation. However, the RGBQUAD's rgbReserved member is
+ only taken into account, if option FI_COLOR_IS_RGBA_COLOR has been specified.
+
+ For 16-bit images, the red-, green- and blue components of the specified color are
+ transparently translated into either the 16-bit 555 or 565 representation. This depends
+ on the image's actual red- green- and blue masks.
+
+ Special attention must be payed for palletized images. Generally, the RGB color specified
+ is looked up in the image's palette. The found palette index is then used to fill the image.
+ There are some option flags, that affect this lookup process:
+
+ no option specified       (0x00)   Uses the color, that is nearest to the specified color.
+                                    This is the default behavior and should always find a
+                                    color in the palette. However, the visual result may
+                                    far from what was expected and mainly depends on the
+                                    image's palette.
+
+ FI_COLOR_FIND_EQUAL_COLOR (0x02)	Searches the image's palette for the specified color
+                                    but only uses the returned palette index, if the specified
+                                    color exactly matches the palette entry. Of course,
+                                    depending on the image's actual palette entries, this
+                                    operation may fail. In this case, the function falls back
+                                    to option FI_COLOR_ALPHA_IS_INDEX and uses the RGBQUAD's
+                                    rgbReserved member (or its low nibble for 4-bit images
+                                    or its least significant bit (LSB) for 1-bit images) as
+                                    the palette index used for the fill operation.
+
+ FI_COLOR_ALPHA_IS_INDEX   (0x04)   Does not perform any color lookup from the palette, but
+                                    uses the RGBQUAD's alpha channel member rgbReserved as
+                                    the palette index to be used for the fill operation.
+                                    However, for 4-bit images, only the low nibble of the
+                                    rgbReserved member are used and for 1-bit images, only
+                                    the least significant bit (LSB) is used.
+
+ This function fails if any of dib and color is NULL.
+
+ @param dib The image to be filled.
+ @param color A pointer to the color value to be used for filling the image. The
+ memory pointed to by this pointer is always assumed to be at least as large as the
+ image's color value, but never smaller than the size of an RGBQUAD structure.
+ @param options Options that affect the color search process for palletized images.
+ @return Returns TRUE on success, FALSE otherwise. This function fails if any of
+ dib and color is NULL.
+ */
+BOOL DLL_CALLCONV
+FreeImage_FillBackground(FIBITMAP *dib, const void *color, int options) {
+
+	if (!FreeImage_HasPixels(dib)) {
+		return FALSE;
+	}
+	
+	if (!color) {
+		return FALSE;
+	}
+
+	// handle FIT_BITMAP images with FreeImage_FillBackground()
+	if (FreeImage_GetImageType(dib) == FIT_BITMAP) {
+		return FillBackgroundBitmap(dib, (RGBQUAD *)color, options);
+	}
+	
+	// first, construct the first scanline (bottom line)
+	unsigned bytespp = (FreeImage_GetBPP(dib) / 8);
+	BYTE *src_bits = FreeImage_GetScanLine(dib, 0);
+	BYTE *dst_bits = src_bits;
+	for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+		memcpy(dst_bits, color, bytespp);
+		dst_bits += bytespp;
+	}
+
+	// then, copy the first scanline into all following scanlines
+	unsigned height = FreeImage_GetHeight(dib);
+	unsigned pitch = FreeImage_GetPitch(dib);
+	unsigned bytes = FreeImage_GetLine(dib);
+	dst_bits = src_bits + pitch;
+	for (unsigned y = 1; y < height; y++) {
+		memcpy(dst_bits, src_bits, bytes);
+		dst_bits += pitch;
+	}
+	return TRUE;
+}
+
+/** @brief Allocates a new image of the specified type, width, height and bit depth and
+ optionally fills it with the specified color.
+
+ This function is an extension to FreeImage_AllocateT, which additionally supports specifying
+ a palette to be set for the newly create image, as well as specifying a background color,
+ the newly created image should initially be filled with.
+
+ Basically, this function internally relies on function FreeImage_AllocateT, followed by a
+ call to FreeImage_FillBackground. This is why both parameters color and options behave the
+ same as it is documented for function FreeImage_FillBackground. So, please refer to the
+ documentation of FreeImage_FillBackground to learn more about parameters color and options.
+
+ The palette specified through parameter palette is only copied to the newly created
+ image, if its image type is FIT_BITMAP and the desired bit depth is smaller than or equal
+ to 8 bits per pixel. In other words, the palette parameter is only taken into account for
+ palletized images. However, if the preceding conditions match and if palette is not NULL,
+ the memory pointed to by the palette pointer is assumed to be at least as large as size
+ of a fully populated palette for the desired bit depth. So, for an 8-bit image, this size
+ is 256 x sizeof(RGBQUAD), for an 4-bit image it is 16 x sizeof(RGBQUAD) and it is
+ 2 x sizeof(RGBQUAD) for a 1-bit image. In other words, this function does not support
+ partial palettes.
+
+ However, specifying a palette is not necessarily needed, even for palletized images. This
+ function is capable of implicitly creating a palette, if parameter palette is NULL. If the
+ specified background color is a greyscale value (red = green = blue) or if option
+ FI_COLOR_ALPHA_IS_INDEX is specified, a greyscale palette is created. For a 1-bit image, only
+ if the specified background color is either black or white, a monochrome palette, consisting
+ of black and white only is created. In any case, the darker colors are stored at the smaller
+ palette indices.
+
+ If the specified background color is not a greyscale value, or is neither black nor white
+ for a 1-bit image, solely this single color is injected into the otherwise black-initialized
+ palette. For this operation, option FI_COLOR_ALPHA_IS_INDEX is implicit, so the specified
+ color is applied to the palette entry, specified by the background color's rgbReserved
+ member. The image is then filled with this palette index.
+
+ This function returns a newly created image as function FreeImage_AllocateT does, if both
+ parameters color and palette are NULL. If only color is NULL, the palette pointed to by
+ parameter palette is initially set for the new image, if a palletized image of type
+ FIT_BITMAP is created. However, in the latter case, this function returns an image, whose
+ pixels are all initialized with zeros so, the image will be filled with the color of the
+ first palette entry.
+
+ @param type Specifies the image type of the new image.
+ @param width The desired width in pixels of the new image.
+ @param height The desired height in pixels of the new image.
+ @param bpp The desired bit depth of the new image.
+ @param color A pointer to the color value to be used for filling the image. The
+ memory pointed to by this pointer is always assumed to be at least as large as the
+ image's color value but never smaller than the size of an RGBQUAD structure.
+ @param options Options that affect the color search process for palletized images.
+ @param red_mask Specifies the bits used to store the red components of a pixel.
+ @param green_mask Specifies the bits used to store the green components of a pixel.
+ @param blue_mask Specifies the bits used to store the blue components of a pixel.
+ @return Returns a pointer to a newly allocated image on success, NULL otherwise.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateExT(FREE_IMAGE_TYPE type, int width, int height, int bpp, const void *color, int options, const RGBQUAD *palette, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+
+	FIBITMAP *bitmap = FreeImage_AllocateT(type, width, height, bpp, red_mask, green_mask, blue_mask);
+	
+	if (!color) {
+		if ((palette) && (type == FIT_BITMAP) && (bpp <= 8)) {
+			memcpy(FreeImage_GetPalette(bitmap), palette, FreeImage_GetColorsUsed(bitmap) * sizeof(RGBQUAD));
+		}
+		return bitmap;
+	}
+
+	if (bitmap != NULL) {
+		
+		// Only fill the new bitmap if the specified color
+		// differs from "black", that is not all bytes of the
+		// color are equal to zero.
+		switch (bpp) {
+			case 1: {
+				// although 1-bit implies FIT_BITMAP, better get an unsigned 
+				// color and palette
+				unsigned *urgb = (unsigned *)color;
+				unsigned *upal = (unsigned *)FreeImage_GetPalette(bitmap);
+				RGBQUAD rgbq = RGBQUAD();
+
+				if (palette != NULL) {
+					// clone the specified palette
+					memcpy(FreeImage_GetPalette(bitmap), palette, 2 * sizeof(RGBQUAD));
+				} else if (options & FI_COLOR_ALPHA_IS_INDEX) {
+					CREATE_GREYSCALE_PALETTE(upal, 2);
+				} else {
+					// check, whether the specified color is either black or white
+					if ((*urgb & 0xFFFFFF) == 0x000000) {
+						// in any case build a FIC_MINISBLACK palette
+						CREATE_GREYSCALE_PALETTE(upal, 2);
+						color = &rgbq;
+					} else if ((*urgb & 0xFFFFFF) == 0xFFFFFF) {
+						// in any case build a FIC_MINISBLACK palette
+						CREATE_GREYSCALE_PALETTE(upal, 2);
+						rgbq.rgbReserved = 1;
+						color = &rgbq;
+					} else {
+						// Otherwise inject the specified color into the so far
+						// black-only palette. We use color->rgbReserved as the
+						// desired palette index.
+						BYTE index = ((RGBQUAD *)color)->rgbReserved & 0x01;
+						upal[index] = *urgb & 0x00FFFFFF;  
+					}
+					options |= FI_COLOR_ALPHA_IS_INDEX;
+				}
+				// and defer to FreeImage_FillBackground
+				FreeImage_FillBackground(bitmap, color, options);
+				break;
+			}
+			case 4: {
+				// 4-bit implies FIT_BITMAP so, get a RGBQUAD color
+				RGBQUAD *rgb = (RGBQUAD *)color;
+				RGBQUAD *pal = FreeImage_GetPalette(bitmap);
+				RGBQUAD rgbq = RGBQUAD();
+				
+				if (palette != NULL) {
+					// clone the specified palette
+					memcpy(pal, palette, 16 * sizeof(RGBQUAD));
+				} else if (options & FI_COLOR_ALPHA_IS_INDEX) {
+					CREATE_GREYSCALE_PALETTE(pal, 16);
+				} else {
+					// check, whether the specified color is a grey one
+					if ((rgb->rgbRed == rgb->rgbGreen) && (rgb->rgbRed == rgb->rgbBlue)) {
+						// if so, build a greyscale palette
+						CREATE_GREYSCALE_PALETTE(pal, 16);
+						rgbq.rgbReserved = rgb->rgbRed >> 4;
+						color = &rgbq;
+					} else {
+						// Otherwise inject the specified color into the so far
+						// black-only palette. We use color->rgbReserved as the
+						// desired palette index.
+						BYTE index = (rgb->rgbReserved & 0x0F);
+						((unsigned *)pal)[index] = *((unsigned *)rgb) & 0x00FFFFFF;
+					}
+					options |= FI_COLOR_ALPHA_IS_INDEX;
+				}
+				// and defer to FreeImage_FillBackground
+				FreeImage_FillBackground(bitmap, color, options);
+				break;
+			}
+			case 8: {
+				// 8-bit implies FIT_BITMAP so, get a RGBQUAD color
+				RGBQUAD *rgb = (RGBQUAD *)color;
+				RGBQUAD *pal = FreeImage_GetPalette(bitmap);
+				RGBQUAD rgbq;
+
+				if (palette != NULL) {
+					// clone the specified palette
+					memcpy(pal, palette, 256 * sizeof(RGBQUAD));
+				} else if (options & FI_COLOR_ALPHA_IS_INDEX) {
+					CREATE_GREYSCALE_PALETTE(pal, 256);
+				} else {
+					// check, whether the specified color is a grey one
+					if ((rgb->rgbRed == rgb->rgbGreen) && (rgb->rgbRed == rgb->rgbBlue)) {
+						// if so, build a greyscale palette
+						CREATE_GREYSCALE_PALETTE(pal, 256);
+						rgbq.rgbReserved = rgb->rgbRed;
+						color = &rgbq;
+					} else {
+						// Otherwise inject the specified color into the so far
+						// black-only palette. We use color->rgbReserved as the
+						// desired palette index.
+						BYTE index = rgb->rgbReserved;
+						((unsigned *)pal)[index] = *((unsigned *)rgb) & 0x00FFFFFF;  
+					}
+					options |= FI_COLOR_ALPHA_IS_INDEX;
+				}
+				// and defer to FreeImage_FillBackground
+				FreeImage_FillBackground(bitmap, color, options);
+				break;
+			}
+			case 16: {
+				WORD wcolor = (type == FIT_BITMAP) ?
+					RGBQUAD_TO_WORD(bitmap, ((RGBQUAD *)color)) : *((WORD *)color);
+				if (wcolor != 0) {
+					FreeImage_FillBackground(bitmap, color, options);
+				}
+				break;
+			}
+			default: {
+				int bytespp = bpp / 8;
+				for (int i = 0; i < bytespp; i++) {
+					if (((BYTE *)color)[i] != 0) {
+						FreeImage_FillBackground(bitmap, color, options);
+						break;
+					}
+				}
+				break;
+			}
+		}
+	}
+	return bitmap;
+}
+
+/** @brief Allocates a new image of the specified width, height and bit depth and optionally
+ fills it with the specified color.
+
+ This function is an extension to FreeImage_Allocate, which additionally supports specifying
+ a palette to be set for the newly create image, as well as specifying a background color,
+ the newly created image should initially be filled with.
+
+ Basically, this function internally relies on function FreeImage_Allocate, followed by a
+ call to FreeImage_FillBackground. This is why both parameters color and options behave the
+ same as it is documented for function FreeImage_FillBackground. So, please refer to the
+ documentation of FreeImage_FillBackground to learn more about parameters color and options.
+
+ The palette specified through parameter palette is only copied to the newly created
+ image, if the desired bit depth is smaller than or equal to 8 bits per pixel. In other words,
+ the palette parameter is only taken into account for palletized images. However, if the
+ image to be created is a palletized image and if palette is not NULL, the memory pointed to
+ by the palette pointer is assumed to be at least as large as size of a fully populated
+ palette for the desired bit depth. So, for an 8-bit image, this size is 256 x sizeof(RGBQUAD),
+ for an 4-bit image it is 16 x sizeof(RGBQUAD) and it is 2 x sizeof(RGBQUAD) for a 1-bit
+ image. In other words, this function does not support partial palettes.
+
+ However, specifying a palette is not necessarily needed, even for palletized images. This
+ function is capable of implicitly creating a palette, if parameter palette is NULL. If the
+ specified background color is a greyscale value (red = green = blue) or if option
+ FI_COLOR_ALPHA_IS_INDEX is specified, a greyscale palette is created. For a 1-bit image, only
+ if the specified background color is either black or white, a monochrome palette, consisting
+ of black and white only is created. In any case, the darker colors are stored at the smaller
+ palette indices.
+
+ If the specified background color is not a greyscale value, or is neither black nor white
+ for a 1-bit image, solely this single color is injected into the otherwise black-initialized
+ palette. For this operation, option FI_COLOR_ALPHA_IS_INDEX is implicit, so the specified
+ color is applied to the palette entry, specified by the background color's rgbReserved
+ member. The image is then filled with this palette index.
+
+ This function returns a newly created image as function FreeImage_Allocate does, if both
+ parameters color and palette are NULL. If only color is NULL, the palette pointed to by
+ parameter palette is initially set for the new image, if a palletized image of type
+ FIT_BITMAP is created. However, in the latter case, this function returns an image, whose
+ pixels are all initialized with zeros so, the image will be filled with the color of the
+ first palette entry.
+
+ @param width The desired width in pixels of the new image.
+ @param height The desired height in pixels of the new image.
+ @param bpp The desired bit depth of the new image.
+ @param color A pointer to an RGBQUAD structure, that provides the color to be used for
+ filling the image.
+ @param options Options that affect the color search process for palletized images.
+ @param red_mask Specifies the bits used to store the red components of a pixel.
+ @param green_mask Specifies the bits used to store the green components of a pixel.
+ @param blue_mask Specifies the bits used to store the blue components of a pixel.
+ @return Returns a pointer to a newly allocated image on success, NULL otherwise.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateEx(int width, int height, int bpp, const RGBQUAD *color, int options, const RGBQUAD *palette, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+	return FreeImage_AllocateExT(FIT_BITMAP, width, height, bpp, ((void *)color), options, palette, red_mask, green_mask, blue_mask);
+}
+
+/** @brief Enlarges or shrinks an image selectively per side and fills newly added areas
+ with the specified background color.
+
+ This function enlarges or shrinks an image selectively per side. The main purpose of this
+ function is to add borders to an image. To add a border to any of the image's sides, a
+ positive integer value must be passed in any of the parameters left, top, right or bottom.
+ This value represents the border's width in pixels. Newly created parts of the image (the
+ border areas) are filled with the specified color. Specifying a negative integer value for
+ a certain side, will shrink or crop the image on this side. Consequently, specifying zero
+ for a certain side will not change the image's extension on that side.
+
+ So, calling this function with all parameters left, top, right and bottom set to zero, is
+ effectively the same as calling function FreeImage_Clone; setting all parameters left, top,
+ right and bottom to value equal to or smaller than zero, my easily be substituted by a call
+ to function FreeImage_Copy. Both these cases produce a new image, which is guaranteed not to
+ be larger than the input image. Thus, since the specified color is not needed in these cases,
+ the pointer color may be NULL.
+
+ Both parameters color and options work according to function FreeImage_FillBackground. So,
+ please refer to the documentation of FreeImage_FillBackground to learn more about parameters
+ color and options. For palletized images, the palette of the input image src is
+ transparently copied to the newly created enlarged or shrunken image, so any color
+ look-ups are performed on this palette.
+
+ Here are some examples, that illustrate, how to use the parameters left, top, right and
+ bottom:
+
+ // create a white color
+ RGBQUAD c;
+ c.rgbRed = 0xFF;
+ c.rgbGreen = 0xFF;
+ c.rgbBlue = 0xFF;
+ c.rgbReserved = 0x00;
+
+ // add a white, symmetric 10 pixel wide border to the image
+ dib2 = FreeImage_EnlargeCanvas(dib, 10, 10, 10, 10, &c, FI_COLOR_IS_RGB_COLOR);
+
+ // add white, 20 pixel wide stripes to the top and bottom side of the image
+ dib3 = FreeImage_EnlargeCanvas(dib, 0, 20, 0, 20, &c, FI_COLOR_IS_RGB_COLOR);
+
+ // add white, 30 pixel wide stripes to the right side of the image and
+ // cut off the 40 leftmost pixel columns
+ dib3 = FreeImage_EnlargeCanvas(dib, -40, 0, 30, 0, &c, FI_COLOR_IS_RGB_COLOR);
+
+ This function fails if either the input image is NULL or the pointer to the color is
+ NULL, while at least on of left, top, right and bottom is greater than zero. This
+ function also returns NULL, if the new image's size will be negative in either x- or
+ y-direction.
+
+ @param dib The image to be enlarged or shrunken.
+ @param left The number of pixels, the image should be enlarged on its left side. Negative
+ values shrink the image on its left side.
+ @param top The number of pixels, the image should be enlarged on its top side. Negative
+ values shrink the image on its top side.
+ @param right The number of pixels, the image should be enlarged on its right side. Negative
+ values shrink the image on its right side.
+ @param bottom The number of pixels, the image should be enlarged on its bottom side. Negative
+ values shrink the image on its bottom side.
+ @param color The color, the enlarged sides of the image should be filled with.
+ @param options Options that affect the color search process for palletized images.
+ @return Returns a pointer to a newly allocated enlarged or shrunken image on success,
+ NULL otherwise. This function fails if either the input image is NULL or the pointer to the
+ color is NULL, while at least on of left, top, right and bottom is greater than zero. This
+ function also returns NULL, if the new image's size will be negative in either x- or
+ y-direction.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_EnlargeCanvas(FIBITMAP *src, int left, int top, int right, int bottom, const void *color, int options) {
+
+	if(!FreeImage_HasPixels(src)) return NULL;
+
+	// Just return a clone of the image, if left, top, right and bottom are
+	// all zero.
+	if ((left == 0) && (right == 0) && (top == 0) && (bottom == 0)) {
+		return FreeImage_Clone(src);
+	}
+
+	int width = FreeImage_GetWidth(src);
+	int height = FreeImage_GetHeight(src);
+
+	// Relay on FreeImage_Copy, if all parameters left, top, right and
+	// bottom are smaller than or equal zero. The color pointer may be
+	// NULL in this case.
+	if ((left <= 0) && (right <= 0) && (top <= 0) && (bottom <= 0)) {
+		return FreeImage_Copy(src, -left, -top,	width + right, height + bottom);
+	}
+
+	// From here, we need a valid color, since the image will be enlarged on
+	// at least one side. So, fail if we don't have a valid color pointer.
+	if (!color) {
+		return NULL;
+	}
+
+	if (((left < 0) && (-left >= width)) || ((right < 0) && (-right >= width)) ||
+		((top < 0) && (-top >= height)) || ((bottom < 0) && (-bottom >= height))) {
+		return NULL;
+	}
+
+	unsigned newWidth = width + left + right;
+	unsigned newHeight = height + top + bottom;
+
+	FREE_IMAGE_TYPE type = FreeImage_GetImageType(src);
+	unsigned bpp = FreeImage_GetBPP(src);
+
+	FIBITMAP *dst = FreeImage_AllocateExT(
+		type, newWidth, newHeight, bpp, color, options,
+		FreeImage_GetPalette(src),
+		FreeImage_GetRedMask(src),
+		FreeImage_GetGreenMask(src),
+		FreeImage_GetBlueMask(src));
+
+	if (!dst) {
+		return NULL;
+	}
+
+	if ((type == FIT_BITMAP) && (bpp <= 4)) {
+		FIBITMAP *copy = FreeImage_Copy(src,
+			((left >= 0) ? 0 : -left),
+			((top >= 0) ? 0 : -top),
+			((width+right)>width)?width:(width+right),
+			((height+bottom)>height)?height:(height+bottom));
+		
+		if (!copy) {
+			FreeImage_Unload(dst);
+			return NULL;
+		}
+
+		if (!FreeImage_Paste(dst, copy,
+				((left <= 0) ? 0 : left),
+				((top <= 0) ? 0 : top), 256)) {
+			FreeImage_Unload(copy);
+			FreeImage_Unload(dst);
+			return NULL;
+		}
+
+		FreeImage_Unload(copy);
+
+	} else {
+
+		int bytespp = bpp / 8;
+		BYTE *srcPtr = FreeImage_GetScanLine(src, height - 1 - ((top >= 0) ? 0 : -top));
+		BYTE *dstPtr = FreeImage_GetScanLine(dst, newHeight - 1 - ((top <= 0) ? 0 : top));
+
+		unsigned srcPitch = FreeImage_GetPitch(src);
+		unsigned dstPitch = FreeImage_GetPitch(dst);
+
+		int lineWidth = bytespp * (width + MIN(0, left) + MIN(0, right));
+		int lines = height + MIN(0, top) + MIN(0, bottom);
+
+		if (left <= 0) {
+			srcPtr += (-left * bytespp);
+		} else {
+			dstPtr += (left * bytespp);
+		}
+
+		for (int i = 0; i < lines; i++) {
+			memcpy(dstPtr, srcPtr, lineWidth);
+			srcPtr -= srcPitch;
+			dstPtr -= dstPitch;
+		}
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+	
+	// copy transparency table 
+	FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(src), FreeImage_GetTransparencyCount(src));
+	
+	// copy background color 
+	RGBQUAD bkcolor; 
+	if( FreeImage_GetBackgroundColor(src, &bkcolor) ) {
+		FreeImage_SetBackgroundColor(dst, &bkcolor); 
+	}
+	
+	// clone resolution 
+	FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src)); 
+	FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src)); 
+	
+	// clone ICC profile 
+	FIICCPROFILE *src_profile = FreeImage_GetICCProfile(src); 
+	FIICCPROFILE *dst_profile = FreeImage_CreateICCProfile(dst, src_profile->data, src_profile->size); 
+	dst_profile->flags = src_profile->flags; 
+
+	return dst;
+}
+
diff --git a/files/Source/FreeImageToolkit/Channels.cpp b/files/Source/FreeImageToolkit/Channels.cpp
new file mode 100644
index 0000000..5f01ad8
--- /dev/null
+++ b/files/Source/FreeImageToolkit/Channels.cpp
@@ -0,0 +1,488 @@
+// ==========================================================
+// Channel processing support
+//
+// 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"
+
+
+/** @brief Retrieves the red, green, blue or alpha channel of a BGR[A] image. 
+@param src Input image to be processed.
+@param channel Color channel to extract
+@return Returns the extracted channel if successful, returns NULL otherwise.
+*/
+FIBITMAP * DLL_CALLCONV 
+FreeImage_GetChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
+
+	if(!FreeImage_HasPixels(src)) return NULL;
+
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+	unsigned bpp = FreeImage_GetBPP(src);
+
+	// 24- or 32-bit 
+	if(image_type == FIT_BITMAP && ((bpp == 24) || (bpp == 32))) {
+		int c;
+
+		// select the channel to extract
+		switch(channel) {
+			case FICC_BLUE:
+				c = FI_RGBA_BLUE;
+				break;
+			case FICC_GREEN:
+				c = FI_RGBA_GREEN;
+				break;
+			case FICC_RED: 
+				c = FI_RGBA_RED;
+				break;
+			case FICC_ALPHA:
+				if(bpp != 32) return NULL;
+				c = FI_RGBA_ALPHA;
+				break;
+			default:
+				return NULL;
+		}
+
+		// allocate a 8-bit dib
+		unsigned width  = FreeImage_GetWidth(src);
+		unsigned height = FreeImage_GetHeight(src);
+		FIBITMAP *dst = FreeImage_Allocate(width, height, 8) ;
+		if(!dst) return NULL;
+		// build a greyscale palette
+		RGBQUAD *pal = FreeImage_GetPalette(dst);
+		for(int i = 0; i < 256; i++) {
+			pal[i].rgbBlue = pal[i].rgbGreen = pal[i].rgbRed = (BYTE)i;
+		}
+
+		// perform extraction
+
+		int bytespp = bpp / 8;	// bytes / pixel
+
+		for(unsigned y = 0; y < height; y++) {
+			BYTE *src_bits = FreeImage_GetScanLine(src, y);
+			BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+			for(unsigned x = 0; x < width; x++) {
+				dst_bits[x] = src_bits[c];
+				src_bits += bytespp;
+			}
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(dst, src);
+		
+		return dst;
+	}
+
+	// 48-bit RGB or 64-bit RGBA images
+	if((image_type == FIT_RGB16) ||  (image_type == FIT_RGBA16)) {
+		int c;
+
+		// select the channel to extract (always RGB[A])
+		switch(channel) {
+			case FICC_BLUE:
+				c = 2;
+				break;
+			case FICC_GREEN:
+				c = 1;
+				break;
+			case FICC_RED: 
+				c = 0;
+				break;
+			case FICC_ALPHA:
+				if(bpp != 64) return NULL;
+				c = 3;
+				break;
+			default:
+				return NULL;
+		}
+
+		// allocate a greyscale dib
+		unsigned width  = FreeImage_GetWidth(src);
+		unsigned height = FreeImage_GetHeight(src);
+		FIBITMAP *dst = FreeImage_AllocateT(FIT_UINT16, width, height) ;
+		if(!dst) return NULL;
+
+		// perform extraction
+
+		int bytespp = bpp / 16;	// words / pixel
+
+		for(unsigned y = 0; y < height; y++) {
+			unsigned short *src_bits = (unsigned short*)FreeImage_GetScanLine(src, y);
+			unsigned short *dst_bits = (unsigned short*)FreeImage_GetScanLine(dst, y);
+			for(unsigned x = 0; x < width; x++) {
+				dst_bits[x] = src_bits[c];
+				src_bits += bytespp;
+			}
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(dst, src);
+		
+		return dst;
+	}
+
+	// 96-bit RGBF or 128-bit RGBAF images
+	if((image_type == FIT_RGBF) ||  (image_type == FIT_RGBAF)) {
+		int c;
+
+		// select the channel to extract (always RGB[A])
+		switch(channel) {
+			case FICC_BLUE:
+				c = 2;
+				break;
+			case FICC_GREEN:
+				c = 1;
+				break;
+			case FICC_RED: 
+				c = 0;
+				break;
+			case FICC_ALPHA:
+				if(bpp != 128) return NULL;
+				c = 3;
+				break;
+			default:
+				return NULL;
+		}
+
+		// allocate a greyscale dib
+		unsigned width  = FreeImage_GetWidth(src);
+		unsigned height = FreeImage_GetHeight(src);
+		FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height) ;
+		if(!dst) return NULL;
+
+		// perform extraction
+
+		int bytespp = bpp / 32;	// floats / pixel
+
+		for(unsigned y = 0; y < height; y++) {
+			float *src_bits = (float*)FreeImage_GetScanLine(src, y);
+			float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
+			for(unsigned x = 0; x < width; x++) {
+				dst_bits[x] = src_bits[c];
+				src_bits += bytespp;
+			}
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(dst, src);
+		
+		return dst;
+	}
+
+	return NULL;
+}
+
+/** @brief Insert a greyscale dib into a RGB[A] image. 
+Both src and dst must have the same width and height.
+@param dst Image to modify (RGB or RGBA)
+@param src Input greyscale image to insert
+@param channel Color channel to modify
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV 
+FreeImage_SetChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
+	int c;
+
+	if(!FreeImage_HasPixels(src) || !FreeImage_HasPixels(dst)) return FALSE;
+	
+	// src and dst images should have the same width and height
+	unsigned src_width  = FreeImage_GetWidth(src);
+	unsigned src_height = FreeImage_GetHeight(src);
+	unsigned dst_width  = FreeImage_GetWidth(dst);
+	unsigned dst_height = FreeImage_GetHeight(dst);
+	if((src_width != dst_width) || (src_height != dst_height))
+		return FALSE;
+
+	// src image should be grayscale, dst image should be RGB or RGBA
+	FREE_IMAGE_COLOR_TYPE src_type = FreeImage_GetColorType(src);
+	FREE_IMAGE_COLOR_TYPE dst_type = FreeImage_GetColorType(dst);
+	if((dst_type != FIC_RGB) && (dst_type != FIC_RGBALPHA) || (src_type != FIC_MINISBLACK)) {
+		return FALSE;
+	}
+
+	FREE_IMAGE_TYPE src_image_type = FreeImage_GetImageType(src);
+	FREE_IMAGE_TYPE dst_image_type = FreeImage_GetImageType(dst);
+
+	if((dst_image_type == FIT_BITMAP) && (src_image_type == FIT_BITMAP)) {
+
+		// src image should be grayscale, dst image should be 24- or 32-bit
+		unsigned src_bpp = FreeImage_GetBPP(src);
+		unsigned dst_bpp = FreeImage_GetBPP(dst);
+		if((src_bpp != 8) || (dst_bpp != 24) && (dst_bpp != 32))
+			return FALSE;
+
+
+		// select the channel to modify
+		switch(channel) {
+			case FICC_BLUE:
+				c = FI_RGBA_BLUE;
+				break;
+			case FICC_GREEN:
+				c = FI_RGBA_GREEN;
+				break;
+			case FICC_RED: 
+				c = FI_RGBA_RED;
+				break;
+			case FICC_ALPHA:
+				if(dst_bpp != 32) return FALSE;
+				c = FI_RGBA_ALPHA;
+				break;
+			default:
+				return FALSE;
+		}
+
+		// perform insertion
+
+		int bytespp = dst_bpp / 8;	// bytes / pixel
+
+		for(unsigned y = 0; y < dst_height; y++) {
+			BYTE *src_bits = FreeImage_GetScanLine(src, y);
+			BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+			for(unsigned x = 0; x < dst_width; x++) {
+				dst_bits[c] = src_bits[x];
+				dst_bits += bytespp;
+			}
+		}
+
+		return TRUE;
+	}
+
+	if(((dst_image_type == FIT_RGB16) || (dst_image_type == FIT_RGBA16)) && (src_image_type == FIT_UINT16)) {
+
+		// src image should be grayscale, dst image should be 48- or 64-bit
+		unsigned src_bpp = FreeImage_GetBPP(src);
+		unsigned dst_bpp = FreeImage_GetBPP(dst);
+		if((src_bpp != 16) || (dst_bpp != 48) && (dst_bpp != 64))
+			return FALSE;
+
+
+		// select the channel to modify (always RGB[A])
+		switch(channel) {
+			case FICC_BLUE:
+				c = 2;
+				break;
+			case FICC_GREEN:
+				c = 1;
+				break;
+			case FICC_RED: 
+				c = 0;
+				break;
+			case FICC_ALPHA:
+				if(dst_bpp != 64) return FALSE;
+				c = 3;
+				break;
+			default:
+				return FALSE;
+		}
+
+		// perform insertion
+
+		int bytespp = dst_bpp / 16;	// words / pixel
+
+		for(unsigned y = 0; y < dst_height; y++) {
+			unsigned short *src_bits = (unsigned short*)FreeImage_GetScanLine(src, y);
+			unsigned short *dst_bits = (unsigned short*)FreeImage_GetScanLine(dst, y);
+			for(unsigned x = 0; x < dst_width; x++) {
+				dst_bits[c] = src_bits[x];
+				dst_bits += bytespp;
+			}
+		}
+
+		return TRUE;
+	}
+	
+	if(((dst_image_type == FIT_RGBF) || (dst_image_type == FIT_RGBAF)) && (src_image_type == FIT_FLOAT)) {
+
+		// src image should be grayscale, dst image should be 96- or 128-bit
+		unsigned src_bpp = FreeImage_GetBPP(src);
+		unsigned dst_bpp = FreeImage_GetBPP(dst);
+		if((src_bpp != 32) || (dst_bpp != 96) && (dst_bpp != 128))
+			return FALSE;
+
+
+		// select the channel to modify (always RGB[A])
+		switch(channel) {
+			case FICC_BLUE:
+				c = 2;
+				break;
+			case FICC_GREEN:
+				c = 1;
+				break;
+			case FICC_RED: 
+				c = 0;
+				break;
+			case FICC_ALPHA:
+				if(dst_bpp != 128) return FALSE;
+				c = 3;
+				break;
+			default:
+				return FALSE;
+		}
+
+		// perform insertion
+
+		int bytespp = dst_bpp / 32;	// floats / pixel
+
+		for(unsigned y = 0; y < dst_height; y++) {
+			float *src_bits = (float*)FreeImage_GetScanLine(src, y);
+			float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
+			for(unsigned x = 0; x < dst_width; x++) {
+				dst_bits[c] = src_bits[x];
+				dst_bits += bytespp;
+			}
+		}
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+/** @brief Retrieves the real part, imaginary part, magnitude or phase of a complex image.
+@param src Input image to be processed.
+@param channel Channel to extract
+@return Returns the extracted channel if successful, returns NULL otherwise.
+*/
+FIBITMAP * DLL_CALLCONV 
+FreeImage_GetComplexChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
+	unsigned x, y;
+	double mag, phase;
+	FICOMPLEX *src_bits = NULL;
+	double *dst_bits = NULL;
+	FIBITMAP *dst = NULL;
+
+	if(!FreeImage_HasPixels(src)) return NULL;
+
+	if(FreeImage_GetImageType(src) == FIT_COMPLEX) {
+		// allocate a dib of type FIT_DOUBLE
+		unsigned width  = FreeImage_GetWidth(src);
+		unsigned height = FreeImage_GetHeight(src);
+		dst = FreeImage_AllocateT(FIT_DOUBLE, width, height) ;
+		if(!dst) return NULL;
+
+		// perform extraction
+
+		switch(channel) {
+			case FICC_REAL: // real part
+				for(y = 0; y < height; y++) {
+					src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
+					dst_bits = (double *)FreeImage_GetScanLine(dst, y);
+					for(x = 0; x < width; x++) {
+						dst_bits[x] = src_bits[x].r;
+					}
+				}
+				break;
+
+			case FICC_IMAG: // imaginary part
+				for(y = 0; y < height; y++) {
+					src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
+					dst_bits = (double *)FreeImage_GetScanLine(dst, y);
+					for(x = 0; x < width; x++) {
+						dst_bits[x] = src_bits[x].i;
+					}
+				}
+				break;
+
+			case FICC_MAG: // magnitude
+				for(y = 0; y < height; y++) {
+					src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
+					dst_bits = (double *)FreeImage_GetScanLine(dst, y);
+					for(x = 0; x < width; x++) {
+						mag = src_bits[x].r * src_bits[x].r + src_bits[x].i * src_bits[x].i;
+						dst_bits[x] = sqrt(mag);
+					}
+				}
+				break;
+
+			case FICC_PHASE: // phase
+				for(y = 0; y < height; y++) {
+					src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
+					dst_bits = (double *)FreeImage_GetScanLine(dst, y);
+					for(x = 0; x < width; x++) {
+						if((src_bits[x].r == 0) && (src_bits[x].i == 0)) {
+							phase = 0;
+						} else {
+							phase = atan2(src_bits[x].i, src_bits[x].r);
+						}
+						dst_bits[x] = phase;
+					}
+				}
+				break;
+		}
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+	
+	return dst;
+}
+
+/** @brief Set the real or imaginary part of a complex image.
+Both src and dst must have the same width and height.
+@param dst Image to modify (image of type FIT_COMPLEX)
+@param src Input image of type FIT_DOUBLE
+@param channel Channel to modify
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV 
+FreeImage_SetComplexChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
+	unsigned x, y;
+	double *src_bits = NULL;
+	FICOMPLEX *dst_bits = NULL;
+
+	if(!FreeImage_HasPixels(src) || !FreeImage_HasPixels(dst)) return FALSE;
+
+	// src image should be of type FIT_DOUBLE, dst image should be of type FIT_COMPLEX
+	const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(src);
+	const FREE_IMAGE_TYPE dst_type = FreeImage_GetImageType(dst);
+	if((src_type != FIT_DOUBLE) || (dst_type != FIT_COMPLEX))
+		return FALSE;
+
+	// src and dst images should have the same width and height
+	unsigned src_width  = FreeImage_GetWidth(src);
+	unsigned src_height = FreeImage_GetHeight(src);
+	unsigned dst_width  = FreeImage_GetWidth(dst);
+	unsigned dst_height = FreeImage_GetHeight(dst);
+	if((src_width != dst_width) || (src_height != dst_height))
+		return FALSE;
+
+	// select the channel to modify
+	switch(channel) {
+		case FICC_REAL: // real part
+			for(y = 0; y < dst_height; y++) {
+				src_bits = (double *)FreeImage_GetScanLine(src, y);
+				dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
+				for(x = 0; x < dst_width; x++) {
+					dst_bits[x].r = src_bits[x];
+				}
+			}
+			break;
+		case FICC_IMAG: // imaginary part
+			for(y = 0; y < dst_height; y++) {
+				src_bits = (double *)FreeImage_GetScanLine(src, y);
+				dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
+				for(x = 0; x < dst_width; x++) {
+					dst_bits[x].i = src_bits[x];
+				}
+			}
+			break;
+	}
+
+	return TRUE;
+}
diff --git a/files/Source/FreeImageToolkit/ClassicRotate.cpp b/files/Source/FreeImageToolkit/ClassicRotate.cpp
new file mode 100644
index 0000000..83c2f92
--- /dev/null
+++ b/files/Source/FreeImageToolkit/ClassicRotate.cpp
@@ -0,0 +1,917 @@
+// ==========================================================
+// Bitmap rotation by means of 3 shears.
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Thorsten Radde (support@IdealSoftware.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!
+// ==========================================================
+
+/* 
+ ============================================================
+ References : 
+ [1] Paeth A., A Fast Algorithm for General Raster Rotation. 
+ Graphics Gems, p. 179, Andrew Glassner editor, Academic Press, 1990. 
+ [2] Yariv E., High quality image rotation (rotate by shear). 
+ [Online] http://www.codeproject.com/bitmap/rotatebyshear.asp
+ [3] Treskunov A., Fast and high quality true-color bitmap rotation function.
+ [Online] http://anton.treskunov.net/Software/doc/fast_and_high_quality_true_color_bitmap_rotation_function.html
+ ============================================================
+*/
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+#define RBLOCK		64	// image blocks of RBLOCK*RBLOCK pixels
+
+// --------------------------------------------------------------------------
+
+/**
+Skews a row horizontally (with filtered weights). 
+Limited to 45 degree skewing only. Filters two adjacent pixels.
+Parameter T can be BYTE, WORD of float. 
+@param src Pointer to source image to rotate
+@param dst Pointer to destination image
+@param row Row index
+@param iOffset Skew offset
+@param dWeight Relative weight of right pixel
+@param bkcolor Background color
+*/
+template <class T> void 
+HorizontalSkewT(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double weight, const void *bkcolor = NULL) {
+	int iXPos;
+
+	const unsigned src_width  = FreeImage_GetWidth(src);
+	const unsigned dst_width  = FreeImage_GetWidth(dst);
+
+	T pxlSrc[4], pxlLeft[4], pxlOldLeft[4];	// 4 = 4*sizeof(T) max
+	
+	// background
+	const T pxlBlack[4] = {0, 0, 0, 0 };
+	const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max
+	if(!pxlBkg) {
+		// default background color is black
+		pxlBkg = pxlBlack;
+	}
+
+	// calculate the number of bytes per pixel
+	const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+	// calculate the number of samples per pixel
+	const unsigned samples = bytespp / sizeof(T);
+
+	BYTE *src_bits = FreeImage_GetScanLine(src, row);
+	BYTE *dst_bits = FreeImage_GetScanLine(dst, row);
+
+	// fill gap left of skew with background
+	if(bkcolor) {
+		for(int k = 0; k < iOffset; k++) {
+			memcpy(&dst_bits[k * bytespp], bkcolor, bytespp);
+		}
+		AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)bkcolor, bytespp);
+	} else {
+		if(iOffset > 0) {
+			memset(dst_bits, 0, iOffset * bytespp);
+		}		
+		memset(&pxlOldLeft[0], 0, bytespp);
+	}
+
+	for(unsigned i = 0; i < src_width; i++) {
+		// loop through row pixels
+		AssignPixel((BYTE*)&pxlSrc[0], (BYTE*)src_bits, bytespp);
+		// calculate weights
+		for(unsigned j = 0; j < samples; j++) {
+			pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);
+		}
+		// check boundaries 
+		iXPos = i + iOffset;
+		if((iXPos >= 0) && (iXPos < (int)dst_width)) {
+			// update left over on source
+			for(unsigned j = 0; j < samples; j++) {
+				pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
+			}
+			AssignPixel((BYTE*)&dst_bits[iXPos*bytespp], (BYTE*)&pxlSrc[0], bytespp);
+		}
+		// save leftover for next pixel in scan
+		AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)&pxlLeft[0], bytespp);
+
+		// next pixel in scan
+		src_bits += bytespp;
+	}			
+
+	// go to rightmost point of skew
+	iXPos = src_width + iOffset; 
+
+	if((iXPos >= 0) && (iXPos < (int)dst_width)) {
+		dst_bits = FreeImage_GetScanLine(dst, row) + iXPos * bytespp;
+
+		// If still in image bounds, put leftovers there
+		AssignPixel((BYTE*)dst_bits, (BYTE*)&pxlOldLeft[0], bytespp);
+
+		// clear to the right of the skewed line with background
+		dst_bits += bytespp;
+		if(bkcolor) {
+			for(unsigned i = 0; i < dst_width - iXPos - 1; i++) {
+				memcpy(&dst_bits[i * bytespp], bkcolor, bytespp);
+			}
+		} else {
+			memset(dst_bits, 0, bytespp * (dst_width - iXPos - 1));
+		}
+
+	}
+}
+
+/**
+Skews a row horizontally (with filtered weights). 
+Limited to 45 degree skewing only. Filters two adjacent pixels.
+@param src Pointer to source image to rotate
+@param dst Pointer to destination image
+@param row Row index
+@param iOffset Skew offset
+@param dWeight Relative weight of right pixel
+@param bkcolor Background color
+*/
+static void 
+HorizontalSkew(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double dWeight, const void *bkcolor) {
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+	switch(image_type) {
+		case FIT_BITMAP:
+			switch(FreeImage_GetBPP(src)) {
+				case 8:
+				case 24:
+				case 32:
+					HorizontalSkewT<BYTE>(src, dst, row, iOffset, dWeight, bkcolor);
+				break;
+			}
+			break;
+		case FIT_UINT16:
+		case FIT_RGB16:
+		case FIT_RGBA16:
+			HorizontalSkewT<WORD>(src, dst, row, iOffset, dWeight, bkcolor);
+			break;
+		case FIT_FLOAT:
+		case FIT_RGBF:
+		case FIT_RGBAF:
+			HorizontalSkewT<float>(src, dst, row, iOffset, dWeight, bkcolor);
+			break;
+	}
+}
+
+/**
+Skews a column vertically (with filtered weights). 
+Limited to 45 degree skewing only. Filters two adjacent pixels.
+Parameter T can be BYTE, WORD of float. 
+@param src Pointer to source image to rotate
+@param dst Pointer to destination image
+@param col Column index
+@param iOffset Skew offset
+@param dWeight Relative weight of upper pixel
+@param bkcolor Background color
+*/
+template <class T> void 
+VerticalSkewT(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double weight, const void *bkcolor = NULL) {
+	int iYPos;
+
+	unsigned src_height = FreeImage_GetHeight(src);
+	unsigned dst_height = FreeImage_GetHeight(dst);
+
+	T pxlSrc[4], pxlLeft[4], pxlOldLeft[4];	// 4 = 4*sizeof(T) max
+
+	// background
+	const T pxlBlack[4] = {0, 0, 0, 0 };
+	const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max
+	if(!pxlBkg) {
+		// default background color is black
+		pxlBkg = pxlBlack;
+	}
+
+	// calculate the number of bytes per pixel
+	const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+	// calculate the number of samples per pixel
+	const unsigned samples = bytespp / sizeof(T);
+
+	const unsigned src_pitch = FreeImage_GetPitch(src);
+	const unsigned dst_pitch = FreeImage_GetPitch(dst);
+	const unsigned index = col * bytespp;
+
+	BYTE *src_bits = FreeImage_GetBits(src) + index;
+	BYTE *dst_bits = FreeImage_GetBits(dst) + index;
+
+	// fill gap above skew with background
+	if(bkcolor) {
+		for(int k = 0; k < iOffset; k++) {
+			memcpy(dst_bits, bkcolor, bytespp);
+			dst_bits += dst_pitch;
+		}
+		memcpy(&pxlOldLeft[0], bkcolor, bytespp);
+	} else {
+		for(int k = 0; k < iOffset; k++) {
+			memset(dst_bits, 0, bytespp);
+			dst_bits += dst_pitch;
+		}
+		memset(&pxlOldLeft[0], 0, bytespp);
+	}
+
+	for(unsigned i = 0; i < src_height; i++) {
+		// loop through column pixels
+		AssignPixel((BYTE*)(&pxlSrc[0]), src_bits, bytespp);
+		// calculate weights
+		for(unsigned j = 0; j < samples; j++) {
+			pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);
+		}
+		// check boundaries
+		iYPos = i + iOffset;
+		if((iYPos >= 0) && (iYPos < (int)dst_height)) {
+			// update left over on source
+			for(unsigned j = 0; j < samples; j++) {
+				pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
+			}
+			dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;
+			AssignPixel(dst_bits, (BYTE*)(&pxlSrc[0]), bytespp);
+		}
+		// save leftover for next pixel in scan
+		AssignPixel((BYTE*)(&pxlOldLeft[0]), (BYTE*)(&pxlLeft[0]), bytespp);
+
+		// next pixel in scan
+		src_bits += src_pitch;
+	}
+	// go to bottom point of skew
+	iYPos = src_height + iOffset;
+
+	if((iYPos >= 0) && (iYPos < (int)dst_height)) {
+		dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;
+
+		// if still in image bounds, put leftovers there				
+		AssignPixel((BYTE*)(dst_bits), (BYTE*)(&pxlOldLeft[0]), bytespp);
+
+		// clear below skewed line with background
+		if(bkcolor) {
+			while(++iYPos < (int)dst_height) {					
+				dst_bits += dst_pitch;
+				AssignPixel((BYTE*)(dst_bits), (BYTE*)(bkcolor), bytespp);
+			}
+		} else {
+			while(++iYPos < (int)dst_height) {					
+				dst_bits += dst_pitch;
+				memset(dst_bits, 0, bytespp);
+			}
+		}
+	}
+}
+
+/**
+Skews a column vertically (with filtered weights). 
+Limited to 45 degree skewing only. Filters two adjacent pixels.
+@param src Pointer to source image to rotate
+@param dst Pointer to destination image
+@param col Column index
+@param iOffset Skew offset
+@param dWeight Relative weight of upper pixel
+@param bkcolor Background color
+*/
+static void 
+VerticalSkew(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double dWeight, const void *bkcolor) {
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+	switch(image_type) {
+		case FIT_BITMAP:
+			switch(FreeImage_GetBPP(src)) {
+				case 8:
+				case 24:
+				case 32:
+					VerticalSkewT<BYTE>(src, dst, col, iOffset, dWeight, bkcolor);
+					break;
+			}
+			break;
+			case FIT_UINT16:
+			case FIT_RGB16:
+			case FIT_RGBA16:
+				VerticalSkewT<WORD>(src, dst, col, iOffset, dWeight, bkcolor);
+				break;
+			case FIT_FLOAT:
+			case FIT_RGBF:
+			case FIT_RGBAF:
+				VerticalSkewT<float>(src, dst, col, iOffset, dWeight, bkcolor);
+				break;
+	}
+} 
+
+/**
+Rotates an image by 90 degrees (counter clockwise). 
+Precise rotation, no filters required.<br>
+Code adapted from CxImage (http://www.xdp.it/cximage.htm)
+@param src Pointer to source image to rotate
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP* 
+Rotate90(FIBITMAP *src) {
+
+	const unsigned bpp = FreeImage_GetBPP(src);
+
+	const unsigned src_width  = FreeImage_GetWidth(src);
+	const unsigned src_height = FreeImage_GetHeight(src);	
+	const unsigned dst_width  = src_height;
+	const unsigned dst_height = src_width;
+
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+	// allocate and clear dst image
+	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);
+	if(NULL == dst) return NULL;
+
+	// get src and dst scan width
+	const unsigned src_pitch  = FreeImage_GetPitch(src);
+	const unsigned dst_pitch  = FreeImage_GetPitch(dst);
+
+	switch(image_type) {
+		case FIT_BITMAP:
+			if(bpp == 1) {
+				// speedy rotate for BW images
+
+				BYTE *bsrc  = FreeImage_GetBits(src); 
+				BYTE *bdest = FreeImage_GetBits(dst);
+
+				BYTE *dbitsmax = bdest + dst_height * dst_pitch - 1;
+
+				for(unsigned y = 0; y < src_height; y++) {
+					// figure out the column we are going to be copying to
+					const div_t div_r = div(y, 8);
+					// set bit pos of src column byte
+					const BYTE bitpos = (BYTE)(128 >> div_r.rem);
+					BYTE *srcdisp = bsrc + y * src_pitch;
+					for(unsigned x = 0; x < src_pitch; x++) {
+						// get source bits
+						BYTE *sbits = srcdisp + x;
+						// get destination column
+						BYTE *nrow = bdest + (dst_height - 1 - (x * 8)) * dst_pitch + div_r.quot;
+						for (int z = 0; z < 8; z++) {
+						   // get destination byte
+							BYTE *dbits = nrow - z * dst_pitch;
+							if ((dbits < bdest) || (dbits > dbitsmax)) break;
+							if (*sbits & (128 >> z)) *dbits |= bitpos;
+						}
+					}
+				}
+			}
+			else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
+				// anything other than BW :
+				// This optimized version of rotation rotates image by smaller blocks. It is quite
+				// a bit faster than obvious algorithm, because it produces much less CPU cache misses.
+				// This optimization can be tuned by changing block size (RBLOCK). 96 is good value for current
+				// CPUs (tested on Athlon XP and Celeron D). Larger value (if CPU has enough cache) will increase
+				// speed somehow, but once you drop out of CPU's cache, things will slow down drastically.
+				// For older CPUs with less cache, lower value would yield better results.
+
+				BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels
+				BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels
+
+				// 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 all image blocks of RBLOCK*RBLOCK pixels
+				
+				// x-segment
+				for(unsigned xs = 0; xs < dst_width; xs += RBLOCK) {
+					// y-segment
+					for(unsigned ys = 0; ys < dst_height; ys += RBLOCK) {
+						for(unsigned y = ys; y < MIN(dst_height, ys + RBLOCK); y++) {    // do rotation
+							const unsigned y2 = dst_height - y - 1;
+							// point to src pixel at (y2, xs)
+							BYTE *src_bits = bsrc + (xs * src_pitch) + (y2 * bytespp);
+							// point to dst pixel at (xs, y)
+							BYTE *dst_bits = bdest + (y * dst_pitch) + (xs * bytespp);
+							for(unsigned x = xs; x < MIN(dst_width, xs + RBLOCK); x++) {
+								// dst.SetPixel(x, y, src.GetPixel(y2, x));
+								AssignPixel(dst_bits, src_bits, bytespp);
+								dst_bits += bytespp;
+								src_bits += src_pitch;
+							}
+						}
+					}
+				}
+			}
+			break;
+		case FIT_UINT16:
+		case FIT_RGB16:
+		case FIT_RGBA16:
+		case FIT_FLOAT:
+		case FIT_RGBF:
+		case FIT_RGBAF:
+		{
+			BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels
+			BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels
+
+			// calculate the number of bytes per pixel
+			const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+			for(unsigned y = 0; y < dst_height; y++) {
+				BYTE *src_bits = bsrc + (src_width - 1 - y) * bytespp;
+				BYTE *dst_bits = bdest + (y * dst_pitch);
+				for(unsigned x = 0; x < dst_width; x++) {
+					AssignPixel(dst_bits, src_bits, bytespp);
+					src_bits += src_pitch;
+					dst_bits += bytespp;
+				}
+			}
+		}
+		break;
+	}
+
+	return dst;
+}
+
+/**
+Rotates an image by 180 degrees (counter clockwise). 
+Precise rotation, no filters required.
+@param src Pointer to source image to rotate
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP* 
+Rotate180(FIBITMAP *src) {
+	int x, y, k, pos;
+
+	const int bpp = FreeImage_GetBPP(src);
+
+	const int src_width  = FreeImage_GetWidth(src);
+	const int src_height = FreeImage_GetHeight(src);
+	const int dst_width  = src_width;
+	const int dst_height = src_height;
+
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);
+	if(NULL == dst) return NULL;
+
+	switch(image_type) {
+		case FIT_BITMAP:
+			if(bpp == 1) {
+				for(int y = 0; y < src_height; y++) {
+					BYTE *src_bits = FreeImage_GetScanLine(src, y);
+					BYTE *dst_bits = FreeImage_GetScanLine(dst, dst_height - y - 1);
+					for(int x = 0; x < src_width; x++) {
+						// get bit at (x, y)
+						k = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+						// set bit at (dst_width - x - 1, dst_height - y - 1)
+						pos = dst_width - x - 1;
+						k ? dst_bits[pos >> 3] |= (0x80 >> (pos & 0x7)) : dst_bits[pos >> 3] &= (0xFF7F >> (pos & 0x7));
+					}			
+				}
+				break;
+			}
+			// else if((bpp == 8) || (bpp == 24) || (bpp == 32)) FALL TROUGH
+		case FIT_UINT16:
+		case FIT_RGB16:
+		case FIT_RGBA16:
+		case FIT_FLOAT:
+		case FIT_RGBF:
+		case FIT_RGBAF:
+		{
+			 // Calculate the number of bytes per pixel
+			const int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+			for(y = 0; y < src_height; y++) {
+				BYTE *src_bits = FreeImage_GetScanLine(src, y);
+				BYTE *dst_bits = FreeImage_GetScanLine(dst, dst_height - y - 1) + (dst_width - 1) * bytespp;
+				for(x = 0; x < src_width; x++) {
+					// get pixel at (x, y)
+					// set pixel at (dst_width - x - 1, dst_height - y - 1)
+					AssignPixel(dst_bits, src_bits, bytespp);
+					src_bits += bytespp;
+					dst_bits -= bytespp;					
+				}				
+			}
+		}
+		break;
+	}
+
+	return dst;
+}
+
+/**
+Rotates an image by 270 degrees (counter clockwise). 
+Precise rotation, no filters required.<br>
+Code adapted from CxImage (http://www.xdp.it/cximage.htm)
+@param src Pointer to source image to rotate
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP* 
+Rotate270(FIBITMAP *src) {
+	int x2, dlineup;
+
+	const unsigned bpp = FreeImage_GetBPP(src);
+
+	const unsigned src_width  = FreeImage_GetWidth(src);
+	const unsigned src_height = FreeImage_GetHeight(src);	
+	const unsigned dst_width  = src_height;
+	const unsigned dst_height = src_width;
+
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+	// allocate and clear dst image
+	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);
+	if(NULL == dst) return NULL;
+
+	// get src and dst scan width
+	const unsigned src_pitch  = FreeImage_GetPitch(src);
+	const unsigned dst_pitch  = FreeImage_GetPitch(dst);
+	
+	switch(image_type) {
+		case FIT_BITMAP:
+			if(bpp == 1) {
+				// speedy rotate for BW images
+				
+				BYTE *bsrc  = FreeImage_GetBits(src); 
+				BYTE *bdest = FreeImage_GetBits(dst);
+				BYTE *dbitsmax = bdest + dst_height * dst_pitch - 1;
+				dlineup = 8 * dst_pitch - dst_width;
+
+				for(unsigned y = 0; y < src_height; y++) {
+					// figure out the column we are going to be copying to
+					const div_t div_r = div(y + dlineup, 8);
+					// set bit pos of src column byte
+					const BYTE bitpos = (BYTE)(1 << div_r.rem);
+					const BYTE *srcdisp = bsrc + y * src_pitch;
+					for(unsigned x = 0; x < src_pitch; x++) {
+						// get source bits
+						const BYTE *sbits = srcdisp + x;
+						// get destination column
+						BYTE *nrow = bdest + (x * 8) * dst_pitch + dst_pitch - 1 - div_r.quot;
+						for(unsigned z = 0; z < 8; z++) {
+						   // get destination byte
+							BYTE *dbits = nrow + z * dst_pitch;
+							if ((dbits < bdest) || (dbits > dbitsmax)) break;
+							if (*sbits & (128 >> z)) *dbits |= bitpos;
+						}
+					}
+				}
+			} 
+			else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
+				// anything other than BW :
+				// This optimized version of rotation rotates image by smaller blocks. It is quite
+				// a bit faster than obvious algorithm, because it produces much less CPU cache misses.
+				// This optimization can be tuned by changing block size (RBLOCK). 96 is good value for current
+				// CPUs (tested on Athlon XP and Celeron D). Larger value (if CPU has enough cache) will increase
+				// speed somehow, but once you drop out of CPU's cache, things will slow down drastically.
+				// For older CPUs with less cache, lower value would yield better results.
+
+				BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels
+				BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels
+
+				// 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 all image blocks of RBLOCK*RBLOCK pixels
+
+				// x-segment
+				for(unsigned xs = 0; xs < dst_width; xs += RBLOCK) {
+					// y-segment
+					for(unsigned ys = 0; ys < dst_height; ys += RBLOCK) {
+						for(unsigned x = xs; x < MIN(dst_width, xs + RBLOCK); x++) {    // do rotation
+							x2 = dst_width - x - 1;
+							// point to src pixel at (ys, x2)
+							BYTE *src_bits = bsrc + (x2 * src_pitch) + (ys * bytespp);
+							// point to dst pixel at (x, ys)
+							BYTE *dst_bits = bdest + (ys * dst_pitch) + (x * bytespp);
+							for(unsigned y = ys; y < MIN(dst_height, ys + RBLOCK); y++) {
+								// dst.SetPixel(x, y, src.GetPixel(y, x2));
+								AssignPixel(dst_bits, src_bits, bytespp);
+								src_bits += bytespp;
+								dst_bits += dst_pitch;
+							}
+						}
+					}
+				}
+			}
+			break;
+		case FIT_UINT16:
+		case FIT_RGB16:
+		case FIT_RGBA16:
+		case FIT_FLOAT:
+		case FIT_RGBF:
+		case FIT_RGBAF:
+		{
+			BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels
+			BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels
+
+			// calculate the number of bytes per pixel
+			const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+			for(unsigned y = 0; y < dst_height; y++) {
+				BYTE *src_bits = bsrc + (src_height - 1) * src_pitch + y * bytespp;
+				BYTE *dst_bits = bdest + (y * dst_pitch);
+				for(unsigned x = 0; x < dst_width; x++) {
+					AssignPixel(dst_bits, src_bits, bytespp);
+					src_bits -= src_pitch;
+					dst_bits += bytespp;
+				}
+			}
+		}
+		break;
+	}
+
+	return dst;
+}
+
+/**
+Rotates an image by a given degree in range [-45 .. +45] (counter clockwise) 
+using the 3-shear technique.
+@param src Pointer to source image to rotate
+@param dAngle Rotation angle
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP* 
+Rotate45(FIBITMAP *src, double dAngle, const void *bkcolor) {
+	const double ROTATE_PI = double(3.1415926535897932384626433832795);
+
+	unsigned u;
+
+	const unsigned bpp = FreeImage_GetBPP(src);
+
+	const double dRadAngle = dAngle * ROTATE_PI / double(180); // Angle in radians
+	const double dSinE = sin(dRadAngle);
+	const double dTan = tan(dRadAngle / 2);
+
+	const unsigned src_width  = FreeImage_GetWidth(src);
+	const unsigned src_height = FreeImage_GetHeight(src);
+
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+	// Calc first shear (horizontal) destination image dimensions 
+	const unsigned width_1  = src_width + unsigned((double)src_height * fabs(dTan) + 0.5);
+	const unsigned height_1 = src_height; 
+
+	// Perform 1st shear (horizontal)
+	// ----------------------------------------------------------------------
+
+	// Allocate image for 1st shear
+	FIBITMAP *dst1 = FreeImage_AllocateT(image_type, width_1, height_1, bpp);
+	if(NULL == dst1) {
+		return NULL;
+	}
+	
+	for(u = 0; u < height_1; u++) {  
+		double dShear;
+
+		if(dTan >= 0)	{
+			// Positive angle
+			dShear = (u + 0.5) * dTan;
+		}
+		else {
+			// Negative angle
+			dShear = (double(u) - height_1 + 0.5) * dTan;
+		}
+		int iShear = int(floor(dShear));
+		HorizontalSkew(src, dst1, u, iShear, dShear - double(iShear), bkcolor);
+	}
+
+	// Perform 2nd shear  (vertical)
+	// ----------------------------------------------------------------------
+
+	// Calc 2nd shear (vertical) destination image dimensions
+	const unsigned width_2  = width_1;
+	unsigned height_2 = unsigned((double)src_width * fabs(dSinE) + (double)src_height * cos(dRadAngle) + 0.5) + 1;
+
+	// Allocate image for 2nd shear
+	FIBITMAP *dst2 = FreeImage_AllocateT(image_type, width_2, height_2, bpp);
+	if(NULL == dst2) {
+		FreeImage_Unload(dst1);
+		return NULL;
+	}
+
+	double dOffset;     // Variable skew offset
+	if(dSinE > 0)	{   
+		// Positive angle
+		dOffset = (src_width - 1.0) * dSinE;
+	}
+	else {
+		// Negative angle
+		dOffset = -dSinE * (double(src_width) - width_2);
+	}
+
+	for(u = 0; u < width_2; u++, dOffset -= dSinE) {
+		int iShear = int(floor(dOffset));
+		VerticalSkew(dst1, dst2, u, iShear, dOffset - double(iShear), bkcolor);
+	}
+
+	// Perform 3rd shear (horizontal)
+	// ----------------------------------------------------------------------
+
+	// Free result of 1st shear
+	FreeImage_Unload(dst1);
+
+	// Calc 3rd shear (horizontal) destination image dimensions
+	const unsigned width_3  = unsigned(double(src_height) * fabs(dSinE) + double(src_width) * cos(dRadAngle) + 0.5) + 1;
+	const unsigned height_3 = height_2;
+
+	// Allocate image for 3rd shear
+	FIBITMAP *dst3 = FreeImage_AllocateT(image_type, width_3, height_3, bpp);
+	if(NULL == dst3) {
+		FreeImage_Unload(dst2);
+		return NULL;
+	}
+
+	if(dSinE >= 0) {
+		// Positive angle
+		dOffset = (src_width - 1.0) * dSinE * -dTan;
+	}
+	else {
+		// Negative angle
+		dOffset = dTan * ( (src_width - 1.0) * -dSinE + (1.0 - height_3) );
+	}
+	for(u = 0; u < height_3; u++, dOffset += dTan) {
+		int iShear = int(floor(dOffset));
+		HorizontalSkew(dst2, dst3, u, iShear, dOffset - double(iShear), bkcolor);
+	}
+	// Free result of 2nd shear    
+	FreeImage_Unload(dst2);
+
+	// Return result of 3rd shear
+	return dst3;      
+}
+
+/**
+Rotates a 1-, 8-, 24- or 32-bit image by a given angle (given in degree). 
+Angle is unlimited, except for 1-bit images (limited to integer multiples of 90 degree). 
+3-shears technique is used.
+@param src Pointer to source image to rotate
+@param dAngle Rotation angle
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP* 
+RotateAny(FIBITMAP *src, double dAngle, const void *bkcolor) {
+	if(NULL == src) {
+		return NULL;
+	}
+
+	FIBITMAP *image = src;
+
+	while(dAngle >= 360) {
+		// Bring angle to range of (-INF .. 360)
+		dAngle -= 360;
+	}
+	while(dAngle < 0) {
+		// Bring angle to range of [0 .. 360) 
+		dAngle += 360;
+	}
+	if((dAngle > 45) && (dAngle <= 135)) {
+		// Angle in (45 .. 135] 
+		// Rotate image by 90 degrees into temporary image,
+		// so it requires only an extra rotation angle 
+		// of -45 .. +45 to complete rotation.
+		image = Rotate90(src);
+		dAngle -= 90;
+	}
+	else if((dAngle > 135) && (dAngle <= 225)) { 
+		// Angle in (135 .. 225] 
+		// Rotate image by 180 degrees into temporary image,
+		// so it requires only an extra rotation angle 
+		// of -45 .. +45 to complete rotation.
+		image = Rotate180(src);
+		dAngle -= 180;
+	}
+	else if((dAngle > 225) && (dAngle <= 315)) { 
+		// Angle in (225 .. 315] 
+		// Rotate image by 270 degrees into temporary image,
+		// so it requires only an extra rotation angle 
+		// of -45 .. +45 to complete rotation.
+		image = Rotate270(src);
+		dAngle -= 270;
+	}
+
+	// If we got here, angle is in (-45 .. +45]
+
+	if(NULL == image)	{
+		// Failed to allocate middle image
+		return NULL;
+	}
+
+	if(0 == dAngle) {
+		if(image == src) {
+			// Nothing to do ...
+			return FreeImage_Clone(src);
+		} else {
+			// No more rotation needed
+			return image;
+		}
+	}
+	else {
+		// Perform last rotation
+		FIBITMAP *dst = Rotate45(image, dAngle, bkcolor);
+
+		if(src != image) {
+			// Middle image was required, free it now.
+			FreeImage_Unload(image);
+		}
+
+		return dst;
+	}
+}
+
+// ==========================================================
+
+FIBITMAP *DLL_CALLCONV 
+FreeImage_Rotate(FIBITMAP *dib, double angle, const void *bkcolor) {
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	if(0 == angle) {
+		return FreeImage_Clone(dib);
+	}
+	// DIB are stored upside down ...
+	angle *= -1;
+
+	try {
+		unsigned bpp = FreeImage_GetBPP(dib);
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+		
+		switch(image_type) {
+			case FIT_BITMAP:
+				if(bpp == 1) {
+					// only rotate for integer multiples of 90 degree
+					if(fmod(angle, 90) != 0)
+						return NULL;
+
+					// perform the rotation
+					FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
+					if(!dst) throw(1);
+
+					// build a greyscale palette
+					RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
+					if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
+						dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 0;
+						dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 255;			
+					} else {
+						dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 255;
+						dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 0;			
+					}
+
+					// copy metadata from src to dst
+					FreeImage_CloneMetadata(dst, dib);
+
+					return dst;
+				}
+				else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
+					FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
+					if(!dst) throw(1);
+					
+					if(bpp == 8) {
+						// copy original palette to rotated bitmap
+						RGBQUAD *src_pal = FreeImage_GetPalette(dib);
+						RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
+						memcpy(&dst_pal[0], &src_pal[0], 256 * sizeof(RGBQUAD));
+
+						// copy transparency table 
+						FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+
+						// copy background color 
+						RGBQUAD bkcolor; 
+						if( FreeImage_GetBackgroundColor(dib, &bkcolor) ) {
+							FreeImage_SetBackgroundColor(dst, &bkcolor); 
+						}
+
+					}
+
+					// copy metadata from src to dst
+					FreeImage_CloneMetadata(dst, dib);
+
+					return dst;
+				}
+				break;
+			case FIT_UINT16:
+			case FIT_RGB16:
+			case FIT_RGBA16:
+			case FIT_FLOAT:
+			case FIT_RGBF:
+			case FIT_RGBAF:
+			{
+				FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
+				if(!dst) throw(1);
+
+				// copy metadata from src to dst
+				FreeImage_CloneMetadata(dst, dib);
+
+				return dst;
+			}
+			break;
+		}
+
+	} catch(int) {
+		return NULL;
+	}
+
+	return NULL;
+}
+
diff --git a/files/Source/FreeImageToolkit/Colors.cpp b/files/Source/FreeImageToolkit/Colors.cpp
new file mode 100644
index 0000000..6719176
--- /dev/null
+++ b/files/Source/FreeImageToolkit/Colors.cpp
@@ -0,0 +1,967 @@
+// ==========================================================
+// Color manipulation routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - 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!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//   Macros + structures
+// ----------------------------------------------------------
+
+#define GET_HI_NIBBLE(byte)     ((byte) >> 4)
+#define SET_HI_NIBBLE(byte, n)  byte &= 0x0F, byte |= ((n) << 4)
+#define GET_LO_NIBBLE(byte)     ((byte) & 0x0F)
+#define SET_LO_NIBBLE(byte, n)  byte &= 0xF0, byte |= ((n) & 0x0F)
+#define GET_NIBBLE(cn, byte)    ((cn) ? (GET_HI_NIBBLE(byte)) : (GET_LO_NIBBLE(byte))) 
+#define SET_NIBBLE(cn, byte, n) if (cn) SET_HI_NIBBLE(byte, n); else SET_LO_NIBBLE(byte, n) 
+
+// ----------------------------------------------------------
+
+
+/** @brief Inverts each pixel data.
+
+@param src Input image to be processed.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV 
+FreeImage_Invert(FIBITMAP *src) {
+
+	if (!FreeImage_HasPixels(src)) return FALSE;
+	
+	unsigned i, x, y, k;
+	
+	const unsigned width = FreeImage_GetWidth(src);
+	const unsigned height = FreeImage_GetHeight(src);
+	const unsigned bpp = FreeImage_GetBPP(src);
+
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+	if(image_type == FIT_BITMAP) {
+		switch(bpp) {
+			case 1 :
+			case 4 :
+			case 8 :
+			{
+				// if the dib has a colormap, just invert it
+				// else, keep the linear grayscale
+
+				if (FreeImage_GetColorType(src) == FIC_PALETTE) {
+					RGBQUAD *pal = FreeImage_GetPalette(src);
+
+					for(i = 0; i < FreeImage_GetColorsUsed(src); i++) {
+						pal[i].rgbRed	= 255 - pal[i].rgbRed;
+						pal[i].rgbGreen = 255 - pal[i].rgbGreen;
+						pal[i].rgbBlue	= 255 - pal[i].rgbBlue;
+					}
+				} else {
+					for(y = 0; y < height; y++) {
+						BYTE *bits = FreeImage_GetScanLine(src, y);
+
+						for (x = 0; x < FreeImage_GetLine(src); x++) {
+							bits[x] = ~bits[x];
+						}
+					}
+				}
+
+				break;
+			}		
+
+			case 24 :
+			case 32 :
+			{
+				// Calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit)
+				const unsigned bytespp = FreeImage_GetLine(src) / width;
+
+				for(y = 0; y < height; y++) {
+					BYTE *bits = FreeImage_GetScanLine(src, y);
+					for(x = 0; x < width; x++) {
+						for(k = 0; k < bytespp; k++) {
+							bits[k] = ~bits[k];
+						}
+						bits += bytespp;
+					}
+				}
+
+				break;
+			}
+			default:
+				return FALSE;
+		}
+	}
+	else if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) {
+		// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+		const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+		for(y = 0; y < height; y++) {
+			WORD *bits = (WORD*)FreeImage_GetScanLine(src, y);
+			for(x = 0; x < width; x++) {
+				for(k = 0; k < wordspp; k++) {
+					bits[k] = ~bits[k];
+				}
+				bits += wordspp;
+			}
+		}
+	}
+	else {
+		// anything else ... 
+		return FALSE;
+	}
+		
+	return TRUE;
+}
+
+/** @brief Perfoms an histogram transformation on a 8, 24 or 32-bit image 
+according to the values of a lookup table (LUT).
+
+The transformation is done as follows.<br>
+Image 8-bit : if the image has a color palette, the LUT is applied to this palette, 
+otherwise, it is applied to the grey values.<br>
+Image 24-bit & 32-bit : if channel == FICC_RGB, the same LUT is applied to each color
+plane (R,G, and B). Otherwise, the LUT is applied to the specified channel only.
+@param src Input image to be processed.
+@param LUT Lookup table. <b>The size of 'LUT' is assumed to be 256.</b>
+@param channel The color channel to be processed (only used with 24 & 32-bit DIB).
+@return Returns TRUE if successful, FALSE otherwise.
+@see FREE_IMAGE_COLOR_CHANNEL
+*/
+BOOL DLL_CALLCONV 
+FreeImage_AdjustCurve(FIBITMAP *src, BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel) {
+	unsigned x, y;
+	BYTE *bits = NULL;
+
+	if(!FreeImage_HasPixels(src) || !LUT || (FreeImage_GetImageType(src) != FIT_BITMAP))
+		return FALSE;
+
+	int bpp = FreeImage_GetBPP(src);
+	if((bpp != 8) && (bpp != 24) && (bpp != 32))
+		return FALSE;
+
+	// apply the LUT
+	switch(bpp) {
+
+		case 8 :
+		{
+			// if the dib has a colormap, apply the LUT to it
+			// else, apply the LUT to pixel values
+
+			if(FreeImage_GetColorType(src) == FIC_PALETTE) {
+				RGBQUAD *rgb = FreeImage_GetPalette(src);
+				for (unsigned pal = 0; pal < FreeImage_GetColorsUsed(src); pal++) {
+					rgb->rgbRed   = LUT[rgb->rgbRed];
+					rgb->rgbGreen = LUT[rgb->rgbGreen];
+					rgb->rgbBlue  = LUT[rgb->rgbBlue];
+					rgb++;
+				}
+			}
+			else {
+				for(y = 0; y < FreeImage_GetHeight(src); y++) {
+					bits =  FreeImage_GetScanLine(src, y);
+					for(x = 0; x < FreeImage_GetWidth(src); x++) {
+						bits[x] = LUT[ bits[x] ];
+					}
+				}
+			}
+
+			break;
+		}
+
+		case 24 :
+		case 32 :
+		{
+			int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+			switch(channel) {
+				case FICC_RGB :
+					for(y = 0; y < FreeImage_GetHeight(src); y++) {
+						bits =  FreeImage_GetScanLine(src, y);
+						for(x = 0; x < FreeImage_GetWidth(src); x++) {
+							bits[FI_RGBA_BLUE]	= LUT[ bits[FI_RGBA_BLUE] ];	// B
+							bits[FI_RGBA_GREEN] = LUT[ bits[FI_RGBA_GREEN] ];	// G
+							bits[FI_RGBA_RED]	= LUT[ bits[FI_RGBA_RED] ];		// R
+							
+							bits += bytespp;
+						}
+					}
+					break;
+
+				case FICC_BLUE :
+					for(y = 0; y < FreeImage_GetHeight(src); y++) {
+						bits =  FreeImage_GetScanLine(src, y);
+						for(x = 0; x < FreeImage_GetWidth(src); x++) {
+							bits[FI_RGBA_BLUE] = LUT[ bits[FI_RGBA_BLUE] ];		// B
+							
+							bits += bytespp;
+						}
+					}
+					break;
+
+				case FICC_GREEN :
+					for(y = 0; y < FreeImage_GetHeight(src); y++) {
+						bits =  FreeImage_GetScanLine(src, y);
+						for(x = 0; x < FreeImage_GetWidth(src); x++) {
+							bits[FI_RGBA_GREEN] = LUT[ bits[FI_RGBA_GREEN] ];	// G
+							
+							bits += bytespp;
+						}
+					}
+					break;
+
+				case FICC_RED :
+					for(y = 0; y < FreeImage_GetHeight(src); y++) {
+						bits =  FreeImage_GetScanLine(src, y);
+						for(x = 0; x < FreeImage_GetWidth(src); x++) {
+							bits[FI_RGBA_RED] = LUT[ bits[FI_RGBA_RED] ];		// R
+							
+							bits += bytespp;
+						}
+					}
+					break;
+					
+				case FICC_ALPHA :
+					if(32 == bpp) {
+						for(y = 0; y < FreeImage_GetHeight(src); y++) {
+							bits =  FreeImage_GetScanLine(src, y);
+							for(x = 0; x < FreeImage_GetWidth(src); x++) {
+								bits[FI_RGBA_ALPHA] = LUT[ bits[FI_RGBA_ALPHA] ];	// A
+								
+								bits += bytespp;
+							}
+						}
+					}
+					break;
+
+				default:
+					break;
+			}
+			break;
+		}
+	}
+
+	return TRUE;
+}
+
+/** @brief Performs gamma correction on a 8, 24 or 32-bit image.
+
+@param src Input image to be processed.
+@param gamma Gamma value to use. A value of 1.0 leaves the image alone, 
+less than one darkens it, and greater than one lightens it.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV 
+FreeImage_AdjustGamma(FIBITMAP *src, double gamma) {
+	BYTE LUT[256];		// Lookup table
+
+	if(!FreeImage_HasPixels(src) || (gamma <= 0))
+		return FALSE;
+	
+	// Build the lookup table
+
+	double exponent = 1 / gamma;
+	double v = 255.0 * (double)pow((double)255, -exponent);
+	for(int i = 0; i < 256; i++) {
+		double color = (double)pow((double)i, exponent) * v;
+		if(color > 255)
+			color = 255;
+		LUT[i] = (BYTE)floor(color + 0.5);
+	}
+
+	// Apply the gamma correction
+	return FreeImage_AdjustCurve(src, LUT, FICC_RGB);
+}
+
+/** @brief Adjusts the brightness of a 8, 24 or 32-bit image by a certain amount.
+
+@param src Input image to be processed.
+@param percentage Where -100 <= percentage <= 100<br>
+A value 0 means no change, less than 0 will make the image darker 
+and greater than 0 will make the image brighter.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV 
+FreeImage_AdjustBrightness(FIBITMAP *src, double percentage) {
+	BYTE LUT[256];		// Lookup table
+	double value;
+
+	if(!FreeImage_HasPixels(src))
+		return FALSE;
+	
+	// Build the lookup table
+	const double scale = (100 + percentage) / 100;
+	for(int i = 0; i < 256; i++) {
+		value = i * scale;
+		value = MAX(0.0, MIN(value, 255.0));
+		LUT[i] = (BYTE)floor(value + 0.5);
+	}
+	return FreeImage_AdjustCurve(src, LUT, FICC_RGB);
+}
+
+/** @brief Adjusts the contrast of a 8, 24 or 32-bit image by a certain amount.
+
+@param src Input image to be processed.
+@param percentage Where -100 <= percentage <= 100<br>
+A value 0 means no change, less than 0 will decrease the contrast 
+and greater than 0 will increase the contrast of the image.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV 
+FreeImage_AdjustContrast(FIBITMAP *src, double percentage) {
+	BYTE LUT[256];		// Lookup table
+	double value;
+
+	if(!FreeImage_HasPixels(src))
+		return FALSE;
+	
+	// Build the lookup table
+	const double scale = (100 + percentage) / 100;
+	for(int i = 0; i < 256; i++) {
+		value = 128 + (i - 128) * scale;
+		value = MAX(0.0, MIN(value, 255.0));
+		LUT[i] = (BYTE)floor(value + 0.5);
+	}
+	return FreeImage_AdjustCurve(src, LUT, FICC_RGB);
+}
+
+/** @brief Computes image histogram
+
+For 24-bit and 32-bit images, histogram can be computed from red, green, blue and 
+black channels. For 8-bit images, histogram is computed from the black channel. Other 
+bit depth is not supported (nothing is done).
+@param src Input image to be processed.
+@param histo Histogram array to fill. <b>The size of 'histo' is assumed to be 256.</b>
+@param channel Color channel to use
+@return Returns TRUE if succesful, returns FALSE if the image bit depth isn't supported.
+*/
+BOOL DLL_CALLCONV 
+FreeImage_GetHistogram(FIBITMAP *src, DWORD *histo, FREE_IMAGE_COLOR_CHANNEL channel) {
+	BYTE pixel;
+	BYTE *bits = NULL;
+	unsigned x, y;
+
+	if(!FreeImage_HasPixels(src) || !histo) return FALSE;
+
+	unsigned width  = FreeImage_GetWidth(src);
+	unsigned height = FreeImage_GetHeight(src);
+	unsigned bpp    = FreeImage_GetBPP(src);
+
+	if(bpp == 8) {
+		// clear histogram array
+		memset(histo, 0, 256 * sizeof(DWORD));
+		// compute histogram for black channel
+		for(y = 0; y < height; y++) {
+			bits = FreeImage_GetScanLine(src, y);
+			for(x = 0; x < width; x++) {
+				// get pixel value
+				pixel = bits[x];
+				histo[pixel]++;
+			}
+		}
+		return TRUE;
+	}
+	else if((bpp == 24) || (bpp == 32)) {
+		int bytespp = bpp / 8;	// bytes / pixel
+
+		// clear histogram array
+		memset(histo, 0, 256 * sizeof(DWORD));
+
+		switch(channel) {
+			case FICC_RED:
+				// compute histogram for red channel
+				for(y = 0; y < height; y++) {
+					bits =  FreeImage_GetScanLine(src, y);
+					for(x = 0; x < width; x++) {
+						pixel = bits[FI_RGBA_RED];	// R
+						histo[pixel]++;
+						bits += bytespp;
+					}
+				}
+				return TRUE;
+
+			case FICC_GREEN:
+				// compute histogram for green channel
+				for(y = 0; y < height; y++) {
+					bits =  FreeImage_GetScanLine(src, y);
+					for(x = 0; x < width; x++) {
+						pixel = bits[FI_RGBA_GREEN];	// G
+						histo[pixel]++;
+						bits += bytespp;
+					}
+				}
+				return TRUE;
+
+			case FICC_BLUE:
+				// compute histogram for blue channel
+				for(y = 0; y < height; y++) {
+					bits =  FreeImage_GetScanLine(src, y);
+					for(x = 0; x < width; x++) {
+						pixel = bits[FI_RGBA_BLUE];	// B
+						histo[pixel]++;
+						bits += bytespp;
+					}
+				}
+				return TRUE;
+
+			case FICC_BLACK:
+			case FICC_RGB:
+				// compute histogram for black channel
+				for(y = 0; y < height; y++) {
+					bits =  FreeImage_GetScanLine(src, y);
+					for(x = 0; x < width; x++) {
+						// RGB to GREY conversion
+						pixel = GREY(bits[FI_RGBA_RED], bits[FI_RGBA_GREEN], bits[FI_RGBA_BLUE]);
+						histo[pixel]++;
+						bits += bytespp;
+					}
+				}
+				return TRUE;
+				
+			default:
+				return FALSE;
+		}
+	}
+
+	return FALSE;
+}
+
+// ----------------------------------------------------------
+
+
+/** @brief Creates a lookup table to be used with FreeImage_AdjustCurve() which
+ may adjust brightness and contrast, correct gamma and invert the image with a
+ single call to FreeImage_AdjustCurve().
+ 
+ This function creates a lookup table to be used with FreeImage_AdjustCurve()
+ which may adjust brightness and contrast, correct gamma and invert the image
+ with a single call to FreeImage_AdjustCurve(). If more than one of these image
+ display properties need to be adjusted, using a combined lookup table should be
+ preferred over calling each adjustment function separately. That's particularly
+ true for huge images or if performance is an issue. Then, the expensive process
+ of iterating over all pixels of an image is performed only once and not up to
+ four times.
+ 
+ Furthermore, the lookup table created does not depend on the order, in which
+ each single adjustment operation is performed. Due to rounding and byte casting
+ issues, it actually matters in which order individual adjustment operations
+ are performed. Both of the following snippets most likely produce different
+ results:
+ 
+ // snippet 1: contrast, brightness
+ FreeImage_AdjustContrast(dib, 15.0);
+ FreeImage_AdjustBrightness(dib, 50.0); 
+ 
+ // snippet 2: brightness, contrast
+ FreeImage_AdjustBrightness(dib, 50.0);
+ FreeImage_AdjustContrast(dib, 15.0);
+ 
+ Better and even faster would be snippet 3:
+ 
+ // snippet 3:
+ BYTE LUT[256];
+ FreeImage_GetAdjustColorsLookupTable(LUT, 50.0, 15.0, 1.0, FALSE); 
+ FreeImage_AdjustCurve(dib, LUT, FICC_RGB);
+ 
+ This function is also used internally by FreeImage_AdjustColors(), which does
+ not return the lookup table, but uses it to call FreeImage_AdjustCurve() on the
+ passed image.
+ 
+ @param LUT Output lookup table to be used with FreeImage_AdjustCurve(). <b>The
+ size of 'LUT' is assumed to be 256.</b>
+ @param brightness Percentage brightness value where -100 <= brightness <= 100<br>
+ A value of 0 means no change, less than 0 will make the image darker and greater
+ than 0 will make the image brighter.
+ @param contrast Percentage contrast value where -100 <= contrast <= 100<br>
+ A value of 0 means no change, less than 0 will decrease the contrast
+ and greater than 0 will increase the contrast of the image.
+ @param gamma Gamma value to be used for gamma correction. A value of 1.0 leaves
+ the image alone, less than one darkens it, and greater than one lightens it.
+ This parameter must not be zero or smaller than zero. If so, it will be ignored
+ and no gamma correction will be performed using the lookup table created.
+ @param invert If set to TRUE, the image will be inverted.
+ @return Returns the number of adjustments applied to the resulting lookup table
+ compared to a blind lookup table.
+ */
+int DLL_CALLCONV
+FreeImage_GetAdjustColorsLookupTable(BYTE *LUT, double brightness, double contrast, double gamma, BOOL invert) {
+	double dblLUT[256];
+	double value;
+	int result = 0;
+
+	if ((brightness == 0.0) && (contrast == 0.0) && (gamma == 1.0) && (!invert)) {
+		// nothing to do, if all arguments have their default values
+		// return a blind LUT
+		for (int i = 0; i < 256; i++) {
+			LUT[i] = (BYTE)i;
+		}
+		return 0;
+	}
+
+	// first, create a blind LUT, which does nothing to the image
+	for (int i = 0; i < 256; i++) {
+		dblLUT[i] = i;
+	}
+
+	if (contrast != 0.0) {
+		// modify lookup table with contrast adjustment data
+		const double v = (100.0 + contrast) / 100.0;
+		for (int i = 0; i < 256; i++) {
+			value = 128 + (dblLUT[i] - 128) * v;
+			dblLUT[i] = MAX(0.0, MIN(value, 255.0));
+		}
+		result++;
+	}
+
+	if (brightness != 0.0) {
+		// modify lookup table with brightness adjustment data
+		const double v = (100.0 + brightness) / 100.0;
+		for (int i = 0; i < 256; i++) {
+			value = dblLUT[i] * v;
+			dblLUT[i] = MAX(0.0, MIN(value, 255.0));
+		}
+		result++;
+	}
+
+	if ((gamma > 0) && (gamma != 1.0)) {
+		// modify lookup table with gamma adjustment data
+		double exponent = 1 / gamma;
+		const double v = 255.0 * (double)pow((double)255, -exponent);
+		for (int i = 0; i < 256; i++) {
+			value = pow(dblLUT[i], exponent) * v;
+			dblLUT[i] = MAX(0.0, MIN(value, 255.0));
+		}
+		result++;
+	}
+
+	if (!invert) {
+		for (int i = 0; i < 256; i++) {
+			LUT[i] = (BYTE)floor(dblLUT[i] + 0.5);
+		}
+	} else {
+		for (int i = 0; i < 256; i++) {
+			LUT[i] = 255 - (BYTE)floor(dblLUT[i] + 0.5);
+		}
+		result++;
+	}
+	// return the number of adjustments made
+	return result;
+}
+
+/** @brief Adjusts an image's brightness, contrast and gamma as well as it may
+ optionally invert the image within a single operation.
+ 
+ This function adjusts an image's brightness, contrast and gamma as well as it
+ may optionally invert the image within a single operation. If more than one of
+ these image display properties need to be adjusted, using this function should
+ be preferred over calling each adjustment function separately. That's
+ particularly true for huge images or if performance is an issue.
+ 
+ This function relies on FreeImage_GetAdjustColorsLookupTable(), which creates a
+ single lookup table, that combines all adjustment operations requested.
+ 
+ Furthermore, the lookup table created by FreeImage_GetAdjustColorsLookupTable()
+ does not depend on the order, in which each single adjustment operation is
+ performed. Due to rounding and byte casting issues, it actually matters in which
+ order individual adjustment operations are performed. Both of the following
+ snippets most likely produce different results:
+ 
+ // snippet 1: contrast, brightness
+ FreeImage_AdjustContrast(dib, 15.0);
+ FreeImage_AdjustBrightness(dib, 50.0); 
+ 
+ // snippet 2: brightness, contrast
+ FreeImage_AdjustBrightness(dib, 50.0);
+ FreeImage_AdjustContrast(dib, 15.0);
+ 
+ Better and even faster would be snippet 3:
+ 
+ // snippet 3:
+ FreeImage_AdjustColors(dib, 50.0, 15.0, 1.0, FALSE);
+ 
+ @param dib Input/output image to be processed.
+ @param brightness Percentage brightness value where -100 <= brightness <= 100<br>
+ A value of 0 means no change, less than 0 will make the image darker and greater
+ than 0 will make the image brighter.
+ @param contrast Percentage contrast value where -100 <= contrast <= 100<br>
+ A value of 0 means no change, less than 0 will decrease the contrast
+ and greater than 0 will increase the contrast of the image.
+ @param gamma Gamma value to be used for gamma correction. A value of 1.0 leaves
+ the image alone, less than one darkens it, and greater than one lightens it.<br>
+ This parameter must not be zero or smaller than zero. If so, it will be ignored
+ and no gamma correction will be performed on the image.
+ @param invert If set to TRUE, the image will be inverted.
+ @return Returns TRUE on success, FALSE otherwise (e.g. when the bitdeph of the
+ source dib cannot be handled).
+ */
+BOOL DLL_CALLCONV
+FreeImage_AdjustColors(FIBITMAP *dib, double brightness, double contrast, double gamma, BOOL invert) {
+	BYTE LUT[256];
+
+	if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+		return FALSE;
+	}
+
+	int bpp = FreeImage_GetBPP(dib);
+	if ((bpp != 8) && (bpp != 24) && (bpp != 32)) {
+		return FALSE;
+	}
+
+	if (FreeImage_GetAdjustColorsLookupTable(LUT, brightness, contrast, gamma, invert)) {
+		return FreeImage_AdjustCurve(dib, LUT, FICC_RGB);
+	}
+	return FALSE;
+}
+
+/** @brief Applies color mapping for one or several colors on a 1-, 4- or 8-bit
+ palletized or a 16-, 24- or 32-bit high color image.
+
+ This function maps up to <i>count</i> colors specified in <i>srccolors</i> to
+ these specified in <i>dstcolors</i>. Thereby, color <i>srccolors[N]</i>,
+ if found in the image, will be replaced by color <i>dstcolors[N]</i>. If
+ parameter <i>swap</i> is TRUE, additionally all colors specified in
+ <i>dstcolors</i> are also mapped to these specified in <i>srccolors</i>. For
+ high color images, the actual image data will be modified whereas, for
+ palletized images only the palette will be changed.<br>
+
+ The function returns the number of pixels changed or zero, if no pixels were
+ changed. 
+
+ Both arrays <i>srccolors</i> and <i>dstcolors</i> are assumed not to hold less
+ than <i>count</i> colors.<br>
+
+ For 16-bit images, all colors specified are transparently converted to their 
+ proper 16-bit representation (either in RGB555 or RGB565 format, which is
+ determined by the image's red- green- and blue-mask).<br>
+
+ <b>Note, that this behaviour is different from what FreeImage_ApplyPaletteIndexMapping()
+ does, which modifies the actual image data on palletized images.</b>
+
+ @param dib Input/output image to be processed.
+ @param srccolors Array of colors to be used as the mapping source.
+ @param dstcolors Array of colors to be used as the mapping destination.
+ @param count The number of colors to be mapped. This is the size of both
+ <i>srccolors</i> and <i>dstcolors</i>.  
+ @param ignore_alpha If TRUE, 32-bit images and colors are treated as 24-bit.
+ @param swap If TRUE, source and destination colors are swapped, that is,
+ each destination color is also mapped to the corresponding source color.  
+ @return Returns the total number of pixels changed. 
+ */
+unsigned DLL_CALLCONV
+FreeImage_ApplyColorMapping(FIBITMAP *dib, RGBQUAD *srccolors, RGBQUAD *dstcolors, unsigned count, BOOL ignore_alpha, BOOL swap) {
+	unsigned result = 0;
+
+	if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+		return 0;
+	}
+
+	// validate parameters
+	if ((!srccolors) || (!dstcolors)|| (count < 1)) {
+		return 0;
+	}
+
+	int bpp = FreeImage_GetBPP(dib);
+	switch (bpp) {
+		case 1:
+		case 4:
+		case 8: {
+			unsigned size = FreeImage_GetColorsUsed(dib);
+			RGBQUAD *pal = FreeImage_GetPalette(dib);
+			RGBQUAD *a, *b;
+			for (unsigned x = 0; x < size; x++) {
+				for (unsigned j = 0; j < count; j++) {
+					a = srccolors;
+					b = dstcolors;
+					for (int i = (swap ? 0 : 1); i < 2; i++) {
+						if ((pal[x].rgbBlue == a[j].rgbBlue)&&(pal[x].rgbGreen == a[j].rgbGreen) &&(pal[x].rgbRed== a[j].rgbRed)) {
+							pal[x].rgbBlue = b[j].rgbBlue;
+							pal[x].rgbGreen = b[j].rgbGreen;
+							pal[x].rgbRed = b[j].rgbRed;
+							result++;
+							j = count;
+							break;
+						}
+						a = dstcolors;
+						b = srccolors;
+					}
+				}
+			}
+			return result;
+		}
+		case 16: {
+			WORD *src16 = (WORD *)malloc(sizeof(WORD) * count);
+			if (NULL == src16) {
+				return 0;
+			}
+
+			WORD *dst16 = (WORD *)malloc(sizeof(WORD) * count);
+			if (NULL == dst16) {
+				free(src16);
+				return 0;
+			}
+
+			for (unsigned j = 0; j < count; j++) {
+				src16[j] = RGBQUAD_TO_WORD(dib, (srccolors + j));
+				dst16[j] = RGBQUAD_TO_WORD(dib, (dstcolors + j));
+			}
+
+			unsigned height = FreeImage_GetHeight(dib);
+			unsigned width = FreeImage_GetWidth(dib);
+			WORD *a, *b;
+			for (unsigned y = 0; y < height; y++) {
+				WORD *bits = (WORD *)FreeImage_GetScanLine(dib, y);
+				for (unsigned x = 0; x < width; x++, bits++) {
+					for (unsigned j = 0; j < count; j++) {
+						a = src16;
+						b = dst16;
+						for (int i = (swap ? 0 : 1); i < 2; i++) {
+							if (*bits == a[j]) {
+								*bits = b[j];
+								result++;
+								j = count;
+								break;
+							}
+							a = dst16;
+							b = src16;
+						}
+					}
+				}
+			}
+			free(src16);
+			free(dst16);
+			return result;
+		}
+		case 24: {
+			unsigned height = FreeImage_GetHeight(dib);
+			unsigned width = FreeImage_GetWidth(dib);
+			RGBQUAD *a, *b;
+			for (unsigned y = 0; y < height; y++) {
+				BYTE *bits = FreeImage_GetScanLine(dib, y);
+				for (unsigned x = 0; x < width; x++, bits += 3) {
+					for (unsigned j = 0; j < count; j++) {
+						a = srccolors;
+						b = dstcolors;
+						for (int i = (swap ? 0 : 1); i < 2; i++) {
+							if ((bits[FI_RGBA_BLUE] == a[j].rgbBlue) && (bits[FI_RGBA_GREEN] == a[j].rgbGreen) &&(bits[FI_RGBA_RED] == a[j].rgbRed)) {
+								bits[FI_RGBA_BLUE] = b[j].rgbBlue;
+								bits[FI_RGBA_GREEN] = b[j].rgbGreen;
+								bits[FI_RGBA_RED] = b[j].rgbRed;
+								result++;
+								j = count;
+								break;
+							}
+							a = dstcolors;
+							b = srccolors;
+						}
+					}
+				}
+			}
+			return result;
+		}
+		case 32: {
+			unsigned height = FreeImage_GetHeight(dib);
+			unsigned width = FreeImage_GetWidth(dib);
+			RGBQUAD *a, *b;
+			for (unsigned y = 0; y < height; y++) {
+				BYTE *bits = FreeImage_GetScanLine(dib, y);
+				for (unsigned x = 0; x < width; x++, bits += 4) {
+					for (unsigned j = 0; j < count; j++) {
+						a = srccolors;
+						b = dstcolors;
+						for (int i = (swap ? 0 : 1); i < 2; i++) {
+							if ((bits[FI_RGBA_BLUE] == a[j].rgbBlue) &&(bits[FI_RGBA_GREEN] == a[j].rgbGreen) &&(bits[FI_RGBA_RED] == a[j].rgbRed)
+								&&((ignore_alpha) || (bits[FI_RGBA_ALPHA] == a[j].rgbReserved))) {
+								bits[FI_RGBA_BLUE] = b[j].rgbBlue;
+								bits[FI_RGBA_GREEN] = b[j].rgbGreen;
+								bits[FI_RGBA_RED] = b[j].rgbRed;
+								if (!ignore_alpha) {
+									bits[FI_RGBA_ALPHA] = b[j].rgbReserved;
+								}
+								result++;
+								j = count;
+								break;
+							}
+							a = dstcolors;
+							b = srccolors;
+						}
+					}
+				}
+			}
+			return result;
+		}
+		default: {
+			return 0;
+		}
+	}
+}
+
+/** @brief Swaps two specified colors on a 1-, 4- or 8-bit palletized
+ or a 16-, 24- or 32-bit high color image.
+
+ This function swaps the two specified colors <i>color_a</i> and <i>color_b</i>
+ on a palletized or high color image. For high color images, the actual image
+ data will be modified whereas, for palletized images only the palette will be
+ changed.<br>
+
+ <b>Note, that this behaviour is different from what FreeImage_SwapPaletteIndices()
+ does, which modifies the actual image data on palletized images.</b><br>
+
+ This is just a thin wrapper for FreeImage_ApplyColorMapping() and resolves to:<br>
+ <i>return FreeImage_ApplyColorMapping(dib, color_a, color_b, 1, ignore_alpha, TRUE);</i>
+
+ @param dib Input/output image to be processed.
+ @param color_a On of the two colors to be swapped.
+ @param color_b The other of the two colors to be swapped.
+ @param ignore_alpha If TRUE, 32-bit images and colors are treated as 24-bit. 
+ @return Returns the total number of pixels changed. 
+ */
+unsigned DLL_CALLCONV
+FreeImage_SwapColors(FIBITMAP *dib, RGBQUAD *color_a, RGBQUAD *color_b, BOOL ignore_alpha) {
+	return FreeImage_ApplyColorMapping(dib, color_a, color_b, 1, ignore_alpha, TRUE);
+}
+
+/** @brief Applies palette index mapping for one or several indices on a 1-, 4- 
+ or 8-bit palletized image.
+
+ This function maps up to <i>count</i> palette indices specified in
+ <i>srcindices</i> to these specified in <i>dstindices</i>. Thereby, index 
+ <i>srcindices[N]</i>, if present in the image, will be replaced by index
+ <i>dstindices[N]</i>. If parameter <i>swap</i> is TRUE, additionally all indices
+ specified in <i>dstindices</i> are also mapped to these specified in 
+ <i>srcindices</i>.<br>
+
+ The function returns the number of pixels changed or zero, if no pixels were
+ changed. 
+
+ Both arrays <i>srcindices</i> and <i>dstindices</i> are assumed not to hold less
+ than <i>count</i> indices.<br>
+
+ <b>Note, that this behaviour is different from what FreeImage_ApplyColorMapping()
+ does, which modifies the actual image data on palletized images.</b>
+
+ @param dib Input/output image to be processed.
+ @param srcindices Array of palette indices to be used as the mapping source.
+ @param dstindices Array of palette indices to be used as the mapping destination.
+ @param count The number of palette indices to be mapped. This is the size of both
+ <i>srcindices</i> and <i>dstindices</i>.  
+ @param swap If TRUE, source and destination palette indices are swapped, that is,
+ each destination index is also mapped to the corresponding source index.  
+ @return Returns the total number of pixels changed. 
+ */
+unsigned DLL_CALLCONV
+FreeImage_ApplyPaletteIndexMapping(FIBITMAP *dib, BYTE *srcindices,	BYTE *dstindices, unsigned count, BOOL swap) {
+	unsigned result = 0;
+
+	if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+		return 0;
+	}
+
+	// validate parameters
+	if ((!srcindices) || (!dstindices)|| (count < 1)) {
+		return 0;
+	}
+
+	unsigned height = FreeImage_GetHeight(dib);
+	unsigned width = FreeImage_GetLine(dib);
+	BYTE *a, *b;
+
+	int bpp = FreeImage_GetBPP(dib);
+	switch (bpp) {
+		case 1: {
+
+			return result;
+		}
+		case 4: {
+			int skip_last = (FreeImage_GetWidth(dib) & 0x01);
+			unsigned max_x = width - 1;
+			for (unsigned y = 0; y < height; y++) {
+				BYTE *bits = FreeImage_GetScanLine(dib, y);
+				for (unsigned x = 0; x < width; x++) {
+					int start = ((skip_last) && (x == max_x)) ? 1 : 0;
+					for (int cn = start; cn < 2; cn++) {
+						for (unsigned j = 0; j < count; j++) {
+							a = srcindices;
+							b = dstindices;
+							for (int i = ((swap) ? 0 : 1); i < 2; i++) {
+								if (GET_NIBBLE(cn, bits[x]) == (a[j] & 0x0F)) {
+									SET_NIBBLE(cn, bits[x], b[j]);
+									result++;
+									j = count;
+									break;
+								}
+								a = dstindices;
+								b = srcindices;
+							}
+						}
+					}
+				}
+			}
+			return result;
+		}
+		case 8: {
+			for (unsigned y = 0; y < height; y++) {
+				BYTE *bits = FreeImage_GetScanLine(dib, y);
+				for (unsigned x = 0; x < width; x++) {
+					for (unsigned j = 0; j < count; j++) {
+						a = srcindices;
+						b = dstindices;
+						for (int i = ((swap) ? 0 : 1); i < 2; i++) {
+							if (bits[x] == a[j]) {
+								bits[x] = b[j];
+								result++;
+								j = count;
+								break;
+							}
+							a = dstindices;
+							b = srcindices;
+						}
+					}
+				}
+			}
+			return result;
+		}
+		default: {
+			return 0;
+		}
+	}
+}
+
+/** @brief Swaps two specified palette indices on a 1-, 4- or 8-bit palletized
+ image.
+
+ This function swaps the two specified palette indices <i>index_a</i> and
+ <i>index_b</i> on a palletized image. Therefore, not the palette, but the
+ actual image data will be modified.<br>
+
+ <b>Note, that this behaviour is different from what FreeImage_SwapColors() does
+ on palletized images, which only swaps the colors in the palette.</b><br>
+
+ This is just a thin wrapper for FreeImage_ApplyColorMapping() and resolves to:<br>
+ <i>return FreeImage_ApplyPaletteIndexMapping(dib, index_a, index_b, 1, TRUE);</i>
+
+ @param dib Input/output image to be processed.
+ @param index_a On of the two palette indices to be swapped.
+ @param index_b The other of the two palette indices to be swapped.
+ @return Returns the total number of pixels changed. 
+ */
+unsigned DLL_CALLCONV 
+FreeImage_SwapPaletteIndices(FIBITMAP *dib, BYTE *index_a, BYTE *index_b) {
+	return FreeImage_ApplyPaletteIndexMapping(dib, index_a, index_b, 1, TRUE);
+}
+
diff --git a/files/Source/FreeImageToolkit/CopyPaste.cpp b/files/Source/FreeImageToolkit/CopyPaste.cpp
new file mode 100644
index 0000000..d05a5df
--- /dev/null
+++ b/files/Source/FreeImageToolkit/CopyPaste.cpp
@@ -0,0 +1,861 @@
+// ==========================================================
+// Copy / paste routines
+//
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Alexander Dymerets (sashad@te.net.ua)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Manfred Tausch (manfred.tausch@t-online.de)
+// - Riley McNiff (rmcniff@marexgroup.com)
+// - 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"
+
+// ----------------------------------------------------------
+//   Helpers
+// ----------------------------------------------------------
+
+/////////////////////////////////////////////////////////////
+// Alpha blending / combine functions
+
+// ----------------------------------------------------------
+/// 1-bit
+static BOOL Combine1(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 4-bit
+static BOOL Combine4(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 8-bit
+static BOOL Combine8(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 16-bit 555
+static BOOL Combine16_555(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 16-bit 565
+static BOOL Combine16_565(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 24-bit
+static BOOL Combine24(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 32- bit
+static BOOL Combine32(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+// ----------------------------------------------------------
+
+// ----------------------------------------------------------
+//   1-bit
+// ----------------------------------------------------------
+
+static BOOL 
+Combine1(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+	BOOL value;
+
+	// check the bit depth of src and dst images
+	if((FreeImage_GetBPP(dst_dib) != 1) || (FreeImage_GetBPP(src_dib) != 1)) {
+		return FALSE;
+	}
+
+	// check the size of src image
+	if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+		return FALSE;
+	}
+
+	BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib));
+	BYTE *src_bits = FreeImage_GetBits(src_dib);	
+
+	// combine images
+	for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+		for(unsigned cols = 0; cols < FreeImage_GetWidth(src_dib); cols++) {
+			// get bit at (rows, cols) in src image
+			value = (src_bits[cols >> 3] & (0x80 >> (cols & 0x07))) != 0;
+			// set bit at (rows, x+cols) in dst image	
+			value ? dst_bits[(x + cols) >> 3] |= (0x80 >> ((x + cols) & 0x7)) : dst_bits[(x + cols) >> 3] &= (0xFF7F >> ((x + cols) & 0x7));
+		}
+
+		dst_bits += FreeImage_GetPitch(dst_dib);
+		src_bits += FreeImage_GetPitch(src_dib);
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//   4-bit
+// ----------------------------------------------------------
+
+static BOOL 
+Combine4(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+	int swapTable[16];
+	BOOL bOddStart, bOddEnd;
+
+	// check the bit depth of src and dst images
+	if((FreeImage_GetBPP(dst_dib) != 4) || (FreeImage_GetBPP(src_dib) != 4)) {
+		return FALSE;
+	}
+
+	// check the size of src image
+	if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+		return FALSE;
+	}
+
+	// get src and dst palettes
+	RGBQUAD *src_pal = FreeImage_GetPalette(src_dib);
+	RGBQUAD *dst_pal = FreeImage_GetPalette(dst_dib);
+	if (src_pal == NULL || dst_pal == NULL) {
+		return FALSE;
+	}
+
+	// build a swap table for the closest color match from the source palette to the destination palette
+
+	for (int i = 0; i < 16; i++)	{
+		WORD min_diff = (WORD)-1;
+
+		for (int j = 0; j < 16; j++)	{
+			// calculates the color difference using a Manhattan distance
+			WORD abs_diff = (WORD)(
+				abs(src_pal[i].rgbBlue - dst_pal[j].rgbBlue)
+				+ abs(src_pal[i].rgbGreen - dst_pal[j].rgbGreen)
+				+ abs(src_pal[i].rgbRed - dst_pal[j].rgbRed)
+				);
+
+			if (abs_diff < min_diff)	{
+				swapTable[i] = j;
+				min_diff = abs_diff;
+				if (abs_diff == 0) {
+					break;
+				}
+			}
+		}
+	}
+
+	BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) *	FreeImage_GetPitch(dst_dib)) + (x >> 1);
+	BYTE *src_bits = FreeImage_GetBits(src_dib);    
+
+	// combine images
+
+	// allocate space for our temporary row
+	unsigned src_line   = FreeImage_GetLine(src_dib);
+	unsigned src_width  = FreeImage_GetWidth(src_dib);
+	unsigned src_height = FreeImage_GetHeight(src_dib);
+
+	BYTE *buffer = (BYTE *)malloc(src_line * sizeof(BYTE));
+	if (buffer == NULL) {
+		return FALSE;
+	}
+
+	bOddStart = (x & 0x01) ? TRUE : FALSE;
+
+	if ((bOddStart && !(src_width & 0x01)) || (!bOddStart && (src_width & 0x01)))	{
+		bOddEnd = TRUE;
+	}
+	else {
+		bOddEnd = FALSE;
+	}
+
+	for(unsigned rows = 0; rows < src_height; rows++) {
+		memcpy(buffer, src_bits, src_line);
+		
+		// change the values in the temp row to be those from the swap table
+		
+		for (unsigned cols = 0; cols < src_line; cols++) {
+			buffer[cols] = (BYTE)((swapTable[HINIBBLE(buffer[cols]) >> 4] << 4) + swapTable[LOWNIBBLE(buffer[cols])]);
+		}
+
+		if (bOddStart) {	
+			buffer[0] = HINIBBLE(dst_bits[0]) + LOWNIBBLE(buffer[0]);
+		}
+		
+		if (bOddEnd)	{
+			buffer[src_line - 1] = HINIBBLE(buffer[src_line - 1]) + LOWNIBBLE(dst_bits[src_line - 1]);
+		}
+
+		memcpy(dst_bits, buffer, src_line);
+		
+		dst_bits += FreeImage_GetPitch(dst_dib);
+		src_bits += FreeImage_GetPitch(src_dib);
+	}
+
+	free(buffer);
+
+	return TRUE;
+
+}
+
+// ----------------------------------------------------------
+//   8-bit
+// ----------------------------------------------------------
+
+static BOOL 
+Combine8(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+	// check the bit depth of src and dst images
+	if((FreeImage_GetBPP(dst_dib) != 8) || (FreeImage_GetBPP(src_dib) != 8)) {
+		return FALSE;
+	}
+
+	// check the size of src image
+	if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+		return FALSE;
+	}
+
+	BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x);
+	BYTE *src_bits = FreeImage_GetBits(src_dib);	
+
+	if(alpha > 255) {
+		// combine images
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	} else {
+		// alpha blend images
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			for (unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols++) {							
+				dst_bits[cols] = (BYTE)(((src_bits[cols] - dst_bits[cols]) * alpha + (dst_bits[cols] << 8)) >> 8);
+			}
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//   16-bit
+// ----------------------------------------------------------
+
+static BOOL 
+Combine16_555(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+	// check the bit depth of src and dst images
+	if((FreeImage_GetBPP(dst_dib) != 16) || (FreeImage_GetBPP(src_dib) != 16)) {
+		return FALSE;
+	}
+
+	// check the size of src image
+	if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+		return FALSE;
+	}
+
+	BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x * 2);
+	BYTE *src_bits = FreeImage_GetBits(src_dib);	
+
+	if (alpha > 255) {
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	} else {
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			for(unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols += 2) {
+				RGBTRIPLE color_s;
+				RGBTRIPLE color_t;
+				
+				WORD *tmp1 = (WORD *)&dst_bits[cols];
+				WORD *tmp2 = (WORD *)&src_bits[cols];
+
+				// convert 16-bit colors to 24-bit
+
+				color_s.rgbtRed = (BYTE)(((*tmp1 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) << 3);
+				color_s.rgbtGreen = (BYTE)(((*tmp1 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) << 3);
+				color_s.rgbtBlue = (BYTE)(((*tmp1 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) << 3);
+
+				color_t.rgbtRed = (BYTE)(((*tmp2 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) << 3);
+				color_t.rgbtGreen = (BYTE)(((*tmp2 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) << 3);
+				color_t.rgbtBlue = (BYTE)(((*tmp2 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) << 3);
+
+				// alpha blend
+
+				color_s.rgbtRed = (BYTE)(((color_t.rgbtRed - color_s.rgbtRed) * alpha + (color_s.rgbtRed << 8)) >> 8);
+				color_s.rgbtGreen = (BYTE)(((color_t.rgbtGreen - color_s.rgbtGreen) * alpha + (color_s.rgbtGreen << 8)) >> 8);
+				color_s.rgbtBlue = (BYTE)(((color_t.rgbtBlue - color_s.rgbtBlue) * alpha + (color_s.rgbtBlue << 8)) >> 8);
+
+				// convert 24-bit color back to 16-bit
+
+				*tmp1 = RGB555(color_s.rgbtRed, color_s.rgbtGreen, color_s.rgbtBlue);
+			}
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	}
+
+	return TRUE;
+}
+
+static BOOL 
+Combine16_565(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+	// check the bit depth of src and dst images
+	if((FreeImage_GetBPP(dst_dib) != 16) || (FreeImage_GetBPP(src_dib) != 16)) {
+		return FALSE;
+	}
+
+	// check the size of src image
+	if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+		return FALSE;
+	}
+
+	BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x * 2);
+	BYTE *src_bits = FreeImage_GetBits(src_dib);	
+
+	if (alpha > 255) {
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	} else {
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			for(unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols += 2) {
+				RGBTRIPLE color_s;
+				RGBTRIPLE color_t;
+				
+				WORD *tmp1 = (WORD *)&dst_bits[cols];
+				WORD *tmp2 = (WORD *)&src_bits[cols];
+
+				// convert 16-bit colors to 24-bit
+
+				color_s.rgbtRed = (BYTE)(((*tmp1 & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) << 3);
+				color_s.rgbtGreen = (BYTE)(((*tmp1 & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) << 2);
+				color_s.rgbtBlue = (BYTE)(((*tmp1 & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) << 3);
+
+				color_t.rgbtRed = (BYTE)(((*tmp2 & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) << 3);
+				color_t.rgbtGreen = (BYTE)(((*tmp2 & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) << 2);
+				color_t.rgbtBlue = (BYTE)(((*tmp2 & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) << 3);
+
+				// alpha blend
+
+				color_s.rgbtRed = (BYTE)(((color_t.rgbtRed - color_s.rgbtRed) * alpha + (color_s.rgbtRed << 8)) >> 8);
+				color_s.rgbtGreen = (BYTE)(((color_t.rgbtGreen - color_s.rgbtGreen) * alpha + (color_s.rgbtGreen << 8)) >> 8);
+				color_s.rgbtBlue = (BYTE)(((color_t.rgbtBlue - color_s.rgbtBlue) * alpha + (color_s.rgbtBlue << 8)) >> 8);
+
+				// convert 24-bit color back to 16-bit
+
+				*tmp1 = RGB565(color_s.rgbtRed, color_s.rgbtGreen, color_s.rgbtBlue);
+			}
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	}
+	
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//   24-bit
+// ----------------------------------------------------------
+
+static BOOL 
+Combine24(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+	// check the bit depth of src and dst images
+	if((FreeImage_GetBPP(dst_dib) != 24) || (FreeImage_GetBPP(src_dib) != 24)) {
+		return FALSE;
+	}
+
+	// check the size of src image
+	if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+		return FALSE;
+	}
+
+	BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x * 3);
+	BYTE *src_bits = FreeImage_GetBits(src_dib);	
+
+	if(alpha > 255) {
+		// combine images
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	} else {
+		// alpha blend images
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			for (unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols++) {							
+				dst_bits[cols] = (BYTE)(((src_bits[cols] - dst_bits[cols]) * alpha + (dst_bits[cols] << 8)) >> 8);
+			}
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//   32-bit
+// ----------------------------------------------------------
+
+static BOOL 
+Combine32(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+	// check the bit depth of src and dst images
+	if((FreeImage_GetBPP(dst_dib) != 32) || (FreeImage_GetBPP(src_dib) != 32)) {
+		return FALSE;
+	}
+
+	// check the size of src image
+	if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+		return FALSE;
+	}
+
+	BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x * 4);
+	BYTE *src_bits = FreeImage_GetBits(src_dib);	
+
+	if (alpha > 255) {
+		// combine images
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	} else {
+		// alpha blend images
+		for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+			for(unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols++) {
+				dst_bits[cols] = (BYTE)(((src_bits[cols] - dst_bits[cols]) * alpha + (dst_bits[cols] << 8)) >> 8);
+			}
+
+			dst_bits += FreeImage_GetPitch(dst_dib);
+			src_bits += FreeImage_GetPitch(src_dib);
+		}
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//   Any type other than FIBITMAP
+// ----------------------------------------------------------
+
+static BOOL 
+CombineSameType(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y) {
+	// check the bit depth of src and dst images
+	if(FreeImage_GetImageType(dst_dib) != FreeImage_GetImageType(src_dib)) {
+		return FALSE;
+	}
+
+	unsigned src_width  = FreeImage_GetWidth(src_dib);
+	unsigned src_height = FreeImage_GetHeight(src_dib);
+	unsigned src_pitch  = FreeImage_GetPitch(src_dib);
+	unsigned src_line   = FreeImage_GetLine(src_dib);
+	unsigned dst_width  = FreeImage_GetWidth(dst_dib);
+	unsigned dst_height = FreeImage_GetHeight(dst_dib);
+	unsigned dst_pitch  = FreeImage_GetPitch(dst_dib);
+	
+	// check the size of src image
+	if((x + src_width > dst_width) || (y + src_height > dst_height)) {
+		return FALSE;
+	}	
+
+	BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((dst_height - src_height - y) * dst_pitch) + (x * (src_line / src_width));
+	BYTE *src_bits = FreeImage_GetBits(src_dib);	
+
+	// combine images	
+	for(unsigned rows = 0; rows < src_height; rows++) {
+		memcpy(dst_bits, src_bits, src_line);
+
+		dst_bits += dst_pitch;
+		src_bits += src_pitch;
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//   FreeImage interface
+// ----------------------------------------------------------
+
+/**
+Copy a sub part of the current image and returns it as a FIBITMAP*.
+Works with any bitmap type.
+@param left Specifies the left position of the cropped rectangle. 
+@param top Specifies the top position of the cropped rectangle. 
+@param right Specifies the right position of the cropped rectangle. 
+@param bottom Specifies the bottom position of the cropped rectangle. 
+@return Returns the subimage if successful, NULL otherwise.
+*/
+FIBITMAP * DLL_CALLCONV 
+FreeImage_Copy(FIBITMAP *src, int left, int top, int right, int bottom) {
+
+	if(!FreeImage_HasPixels(src)) 
+		return NULL;
+
+	// normalize the rectangle
+	if(right < left) {
+		INPLACESWAP(left, right);
+	}
+	if(bottom < top) {
+		INPLACESWAP(top, bottom);
+	}
+	// check the size of the sub image
+	int src_width  = FreeImage_GetWidth(src);
+	int src_height = FreeImage_GetHeight(src);
+	if((left < 0) || (right > src_width) || (top < 0) || (bottom > src_height)) {
+		return NULL;
+	}
+
+	// allocate the sub image
+	unsigned bpp = FreeImage_GetBPP(src);
+	int dst_width = (right - left);
+	int dst_height = (bottom - top);
+
+	FIBITMAP *dst = 
+		FreeImage_AllocateT(FreeImage_GetImageType(src), 
+							dst_width, 
+							dst_height, 
+							bpp, 
+							FreeImage_GetRedMask(src), FreeImage_GetGreenMask(src), FreeImage_GetBlueMask(src));
+
+	if(NULL == dst) return NULL;
+
+	// get the dimensions
+	int dst_line = FreeImage_GetLine(dst);
+	int dst_pitch = FreeImage_GetPitch(dst);
+	int src_pitch = FreeImage_GetPitch(src);
+
+	// get the pointers to the bits and such
+
+	BYTE *src_bits = FreeImage_GetScanLine(src, src_height - top - dst_height);
+	switch(bpp) {
+		case 1:
+			// point to x = 0
+			break;
+
+		case 4:
+			// point to x = 0
+			break;
+
+		default:
+		{
+			// calculate the number of bytes per pixel
+			unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+			// point to x = left
+			src_bits += left * bytespp;
+		}
+		break;
+	}
+
+	// point to x = 0
+	BYTE *dst_bits = FreeImage_GetBits(dst);
+
+	// copy the palette
+
+	memcpy(FreeImage_GetPalette(dst), FreeImage_GetPalette(src), FreeImage_GetColorsUsed(src) * sizeof(RGBQUAD));
+
+	// copy the bits
+	if(bpp == 1) {
+		BOOL value;
+		unsigned y_src, y_dst;
+
+		for(int y = 0; y < dst_height; y++) {
+			y_src = y * src_pitch;
+			y_dst = y * dst_pitch;
+			for(int x = 0; x < dst_width; x++) {
+				// get bit at (y, x) in src image
+				value = (src_bits[y_src + ((left+x) >> 3)] & (0x80 >> ((left+x) & 0x07))) != 0;
+				// set bit at (y, x) in dst image
+				value ? dst_bits[y_dst + (x >> 3)] |= (0x80 >> (x & 0x7)) : dst_bits[y_dst + (x >> 3)] &= (0xff7f >> (x & 0x7));
+			}
+		}
+	}
+
+	else if(bpp == 4) {
+		BYTE shift, value;
+		unsigned y_src, y_dst;
+
+		for(int y = 0; y < dst_height; y++) {
+			y_src = y * src_pitch;
+			y_dst = y * dst_pitch;
+			for(int x = 0; x < dst_width; x++) {
+				// get nibble at (y, x) in src image
+				shift = (BYTE)((1 - (left+x) % 2) << 2);
+				value = (src_bits[y_src + ((left+x) >> 1)] & (0x0F << shift)) >> shift;
+				// set nibble at (y, x) in dst image
+				shift = (BYTE)((1 - x % 2) << 2);
+				dst_bits[y_dst + (x >> 1)] &= ~(0x0F << shift);
+				dst_bits[y_dst + (x >> 1)] |= ((value & 0x0F) << shift);
+			}
+		}
+	}
+
+	else if(bpp >= 8) {
+		for(int y = 0; y < dst_height; y++) {
+			memcpy(dst_bits + (y * dst_pitch), src_bits + (y * src_pitch), dst_line);
+		}
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+	
+	// copy transparency table 
+	FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(src), FreeImage_GetTransparencyCount(src));
+	
+	// copy background color 
+	RGBQUAD bkcolor; 
+	if( FreeImage_GetBackgroundColor(src, &bkcolor) ) {
+		FreeImage_SetBackgroundColor(dst, &bkcolor); 
+	}
+	
+	// clone resolution 
+	FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src)); 
+	FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src)); 
+	
+	// clone ICC profile 
+	FIICCPROFILE *src_profile = FreeImage_GetICCProfile(src); 
+	FIICCPROFILE *dst_profile = FreeImage_CreateICCProfile(dst, src_profile->data, src_profile->size); 
+	dst_profile->flags = src_profile->flags; 
+	
+	return dst;
+}
+
+/**
+Alpha blend or combine a sub part image with the current image.
+The bit depth of dst bitmap must be greater than or equal to the bit depth of src. 
+Upper promotion of src is done internally. Supported bit depth equals to 1, 4, 8, 16, 24 or 32.
+@param src Source subimage
+@param left Specifies the left position of the sub image. 
+@param top Specifies the top position of the sub image. 
+@param alpha Alpha blend factor. The source and destination images are alpha blended if 
+alpha = 0..255. If alpha > 255, then the source image is combined to the destination image.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV 
+FreeImage_Paste(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha) {
+	BOOL bResult = FALSE;
+
+	if(!FreeImage_HasPixels(src) || !FreeImage_HasPixels(dst)) return FALSE;
+
+	// check the size of src image
+	if((left < 0) || (top < 0)) {
+		return FALSE;
+	}
+	if((left + FreeImage_GetWidth(src) > FreeImage_GetWidth(dst)) || (top + FreeImage_GetHeight(src) > FreeImage_GetHeight(dst))) {
+		return FALSE;
+	}
+
+	// check data type
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dst);
+	if(image_type != FreeImage_GetImageType(src)) {
+		// no conversion between data type is done
+		return FALSE;
+	}
+
+	if(image_type == FIT_BITMAP) {
+		FIBITMAP *clone = NULL;
+
+		// check the bit depth of src and dst images
+		unsigned bpp_src = FreeImage_GetBPP(src);
+		unsigned bpp_dst = FreeImage_GetBPP(dst);
+		BOOL isRGB565 = FALSE;
+
+		if ((FreeImage_GetRedMask(dst) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dst) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dst) == FI16_565_BLUE_MASK)) {
+			isRGB565 = TRUE;
+		} else {
+			// includes case where all the masks are 0
+			isRGB565 = FALSE;
+		}
+
+		// perform promotion if needed
+		if(bpp_dst == bpp_src) {
+			clone = src;
+		} else if(bpp_dst > bpp_src) {
+			// perform promotion
+			switch(bpp_dst) {
+				case 4:
+					clone = FreeImage_ConvertTo4Bits(src);
+					break;
+				case 8:
+					clone = FreeImage_ConvertTo8Bits(src);
+					break;
+				case 16:
+					if (isRGB565) {
+						clone = FreeImage_ConvertTo16Bits565(src);
+					} else {
+						// includes case where all the masks are 0
+						clone = FreeImage_ConvertTo16Bits555(src);
+					}
+					break;
+				case 24:
+					clone = FreeImage_ConvertTo24Bits(src);
+					break;
+				case 32:
+					clone = FreeImage_ConvertTo32Bits(src);
+					break;
+				default:
+					return FALSE;
+			}
+		} else {
+			return FALSE;
+		}
+
+		if(!clone) return FALSE;
+
+		// paste src to dst
+		switch(FreeImage_GetBPP(dst)) {
+			case 1:
+				bResult = Combine1(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+				break;
+			case 4:
+				bResult = Combine4(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+				break;
+			case 8:
+				bResult = Combine8(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+				break;
+			case 16:
+				if (isRGB565) {
+					bResult = Combine16_565(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+				} else {
+					// includes case where all the masks are 0
+					bResult = Combine16_555(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+				}
+				break;
+			case 24:
+				bResult = Combine24(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+				break;
+			case 32:
+				bResult = Combine32(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+				break;
+		}
+
+		if(clone != src)
+			FreeImage_Unload(clone);
+
+		}
+	else {	// any type other than FITBITMAP
+		bResult = CombineSameType(dst, src, (unsigned)left, (unsigned)top);
+	}
+
+	return bResult;
+}
+
+// ----------------------------------------------------------
+
+/** @brief Creates a dynamic read/write view into a FreeImage bitmap.
+
+ A dynamic view is a FreeImage bitmap with its own width and height, that,
+ however, shares its bits with another FreeImage bitmap. Typically, views
+ are used to define one or more rectangular sub-images of an existing
+ bitmap. All FreeImage operations, like saving, displaying and all the
+ toolkit functions, when applied to the view, only affect the view's
+ rectangular area.
+
+ Although the view's backing image's bits not need to be copied around,
+ which makes the view much faster than similar solutions using
+ FreeImage_Copy, a view uses some private memory that needs to be freed by
+ calling FreeImage_Unload on the view's handle to prevent memory leaks.
+
+ Only the backing image's pixels are shared by the view. For all other image
+ data, notably for the resolution, background color, color palette,
+ transparency table and for the ICC profile, the view gets a private copy
+ of the data. By default, the backing image's metadata is NOT copied to
+ the view.
+
+ As with all FreeImage functions that take a rectangle region, top and left
+ positions are included, whereas right and bottom positions are excluded
+ from the rectangle area.
+
+ Since the memory block shared by the backing image and the view must start
+ at a byte boundary, the value of parameter left must be a multiple of 8
+ for 1-bit images and a multiple of 2 for 4-bit images.
+
+ @param dib The FreeImage bitmap on which to create the view.
+ @param left The left position of the view's area.
+ @param top The top position of the view's area.
+ @param right The right position of the view's area.
+ @param bottom The bottom position of the view's area.
+ @return Returns a handle to the newly created view or NULL if the view
+ was not created.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_CreateView(FIBITMAP *dib, unsigned left, unsigned top, unsigned right, unsigned bottom) {
+	if (!FreeImage_HasPixels(dib)) {
+		return NULL;
+	}
+
+	// normalize the rectangle
+	if (right < left) {
+		INPLACESWAP(left, right);
+	}
+	if (bottom < top) {
+		INPLACESWAP(top, bottom);
+	}
+
+	// check the size of the sub image
+	unsigned width = FreeImage_GetWidth(dib);
+	unsigned height = FreeImage_GetHeight(dib);
+	if (left < 0 || right > width || top < 0 || bottom > height) {
+		return NULL;
+	}
+
+	unsigned bpp = FreeImage_GetBPP(dib);
+	BYTE *bits = FreeImage_GetScanLine(dib, height - bottom);
+	switch (bpp) {
+		case 1:
+			if (left % 8 != 0) {
+				// view can only start at a byte boundary
+				return NULL;
+			}
+			bits += (left / 8);
+			break;
+		case 4:
+			if (left % 2 != 0) {
+				// view can only start at a byte boundary
+				return NULL;
+				}
+			bits += (left / 2);
+			break;
+		default:
+			bits += left * (bpp / 8);
+			break;
+	}
+
+	FIBITMAP *dst = FreeImage_AllocateHeaderForBits(bits, FreeImage_GetPitch(dib), FreeImage_GetImageType(dib), 
+		right - left, bottom - top, 
+		bpp, 
+		FreeImage_GetRedMask(dib), FreeImage_GetGreenMask(dib), FreeImage_GetBlueMask(dib));
+
+	if (dst == NULL) {
+		return NULL;
+	}
+
+	// copy some basic image properties needed for displaying and saving
+
+	// resolution
+	FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(dib));
+	FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(dib));
+
+	// background color
+	RGBQUAD bkcolor;
+	if (FreeImage_GetBackgroundColor(dib, &bkcolor)) {
+		FreeImage_SetBackgroundColor(dst, &bkcolor);
+	}
+
+	// palette
+	memcpy(FreeImage_GetPalette(dst), FreeImage_GetPalette(dib), FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD));
+
+	// transparency table
+	FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+
+	// ICC profile
+	FIICCPROFILE *src_profile = FreeImage_GetICCProfile(dib);
+	FIICCPROFILE *dst_profile = FreeImage_CreateICCProfile(dst, src_profile->data, src_profile->size);
+	dst_profile->flags = src_profile->flags;
+
+	return dst;
+}
diff --git a/files/Source/FreeImageToolkit/Display.cpp b/files/Source/FreeImageToolkit/Display.cpp
new file mode 100644
index 0000000..245c5c3
--- /dev/null
+++ b/files/Source/FreeImageToolkit/Display.cpp
@@ -0,0 +1,230 @@
+// ==========================================================
+// Display 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"
+
+
+/**
+@brief Composite a foreground image against a background color or a background image.
+
+The equation for computing a composited sample value is:<br>
+output = alpha * foreground + (1-alpha) * background<br>
+where alpha and the input and output sample values are expressed as fractions in the range 0 to 1. 
+For colour images, the computation is done separately for R, G, and B samples.
+
+@param fg Foreground image
+@param useFileBkg If TRUE and a file background is present, use it as the background color
+@param appBkColor If not equal to NULL, and useFileBkg is FALSE, use this color as the background color
+@param bg If not equal to NULL and useFileBkg is FALSE and appBkColor is NULL, use this as the background image
+@return Returns the composite image if successful, returns NULL otherwise
+@see FreeImage_IsTransparent, FreeImage_HasBackgroundColor
+*/
+FIBITMAP * DLL_CALLCONV
+FreeImage_Composite(FIBITMAP *fg, BOOL useFileBkg, RGBQUAD *appBkColor, FIBITMAP *bg) {
+	if(!FreeImage_HasPixels(fg)) return NULL;
+
+	int width  = FreeImage_GetWidth(fg);
+	int height = FreeImage_GetHeight(fg);
+	int bpp    = FreeImage_GetBPP(fg);
+
+	if((bpp != 8) && (bpp != 32))
+		return NULL;
+
+	if(bg) {
+		int bg_width  = FreeImage_GetWidth(bg);
+		int bg_height = FreeImage_GetHeight(bg);
+		int bg_bpp    = FreeImage_GetBPP(bg);
+		if((bg_width != width) || (bg_height != height) || (bg_bpp != 24))
+			return NULL;
+	}
+
+	int bytespp = (bpp == 8) ? 1 : 4;
+
+	
+	int x, y, c;
+	BYTE alpha = 0, not_alpha;
+	BYTE index;
+	RGBQUAD fgc;	// foreground color
+	RGBQUAD bkc;	// background color
+
+	memset(&fgc, 0, sizeof(RGBQUAD));
+	memset(&bkc, 0, sizeof(RGBQUAD));
+
+	// allocate the composite image
+	FIBITMAP *composite = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+	if(!composite) return NULL;
+
+	// get the palette
+	RGBQUAD *pal = FreeImage_GetPalette(fg);
+
+	// retrieve the alpha table from the foreground image
+	BOOL bIsTransparent = FreeImage_IsTransparent(fg);
+	BYTE *trns = FreeImage_GetTransparencyTable(fg);
+
+	// retrieve the background color from the foreground image
+	BOOL bHasBkColor = FALSE;
+
+	if(useFileBkg && FreeImage_HasBackgroundColor(fg)) {
+		FreeImage_GetBackgroundColor(fg, &bkc);
+		bHasBkColor = TRUE;
+	} else {
+		// no file background color
+		// use application background color ?
+		if(appBkColor) {
+			memcpy(&bkc, appBkColor, sizeof(RGBQUAD));
+			bHasBkColor = TRUE;
+		}
+		// use background image ?
+		else if(bg) {
+			bHasBkColor = FALSE;
+		}
+	}
+
+	for(y = 0; y < height; y++) {
+		// foreground
+		BYTE *fg_bits = FreeImage_GetScanLine(fg, y);
+		// background
+		BYTE *bg_bits = FreeImage_GetScanLine(bg, y);
+		// composite image
+		BYTE *cp_bits = FreeImage_GetScanLine(composite, y);
+
+		for(x = 0; x < width; x++) {
+
+			// foreground color + alpha
+
+			if(bpp == 8) {
+				// get the foreground color
+				index = fg_bits[0];
+				memcpy(&fgc, &pal[index], sizeof(RGBQUAD));
+				// get the alpha
+				if(bIsTransparent) {
+					alpha = trns[index];
+				} else {
+					alpha = 255;
+				}
+			}
+			else if(bpp == 32) {
+				// get the foreground color
+				fgc.rgbBlue  = fg_bits[FI_RGBA_BLUE];
+				fgc.rgbGreen = fg_bits[FI_RGBA_GREEN];
+				fgc.rgbRed   = fg_bits[FI_RGBA_RED];
+				// get the alpha
+				alpha = fg_bits[FI_RGBA_ALPHA];
+			}
+
+			// background color
+
+			if(!bHasBkColor) {
+				if(bg) {
+					// get the background color from the background image
+					bkc.rgbBlue  = bg_bits[FI_RGBA_BLUE];
+					bkc.rgbGreen = bg_bits[FI_RGBA_GREEN];
+					bkc.rgbRed   = bg_bits[FI_RGBA_RED];
+				}
+				else {
+					// use a checkerboard pattern
+					c = (((y & 0x8) == 0) ^ ((x & 0x8) == 0)) * 192;
+					c = c ? c : 255;
+					bkc.rgbBlue  = (BYTE)c;
+					bkc.rgbGreen = (BYTE)c;
+					bkc.rgbRed   = (BYTE)c;
+				}
+			}
+
+			// composition
+
+			if(alpha == 0) {
+				// output = background
+				cp_bits[FI_RGBA_BLUE] = bkc.rgbBlue;
+				cp_bits[FI_RGBA_GREEN] = bkc.rgbGreen;
+				cp_bits[FI_RGBA_RED] = bkc.rgbRed;
+			}
+			else if(alpha == 255) {
+				// output = foreground
+				cp_bits[FI_RGBA_BLUE] = fgc.rgbBlue;
+				cp_bits[FI_RGBA_GREEN] = fgc.rgbGreen;
+				cp_bits[FI_RGBA_RED] = fgc.rgbRed;
+			}
+			else {
+				// output = alpha * foreground + (1-alpha) * background
+				not_alpha = (BYTE)~alpha;
+				cp_bits[FI_RGBA_BLUE] = (BYTE)((alpha * (WORD)fgc.rgbBlue  + not_alpha * (WORD)bkc.rgbBlue) >> 8);
+				cp_bits[FI_RGBA_GREEN] = (BYTE)((alpha * (WORD)fgc.rgbGreen + not_alpha * (WORD)bkc.rgbGreen) >> 8);
+				cp_bits[FI_RGBA_RED] = (BYTE)((alpha * (WORD)fgc.rgbRed   + not_alpha * (WORD)bkc.rgbRed) >> 8);
+			}
+
+			fg_bits += bytespp;
+			bg_bits += 3;
+			cp_bits += 3;
+		}
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(composite, fg);
+	
+	return composite;	
+}
+
+/**
+Pre-multiplies a 32-bit image's red-, green- and blue channels with it's alpha channel 
+for to be used with e.g. the Windows GDI function AlphaBlend(). 
+The transformation changes the red-, green- and blue channels according to the following equation:  
+channel(x, y) = channel(x, y) * alpha_channel(x, y) / 255  
+@param dib Input/Output dib to be premultiplied
+@return Returns TRUE on success, FALSE otherwise (e.g. when the bitdepth of the source dib cannot be handled). 
+*/
+BOOL DLL_CALLCONV 
+FreeImage_PreMultiplyWithAlpha(FIBITMAP *dib) {
+	if (!FreeImage_HasPixels(dib)) return FALSE;
+	
+	if ((FreeImage_GetBPP(dib) != 32) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+		return FALSE;
+	}
+
+	int width = FreeImage_GetWidth(dib);
+	int height = FreeImage_GetHeight(dib);
+
+	for(int y = 0; y < height; y++) {
+		BYTE *bits = FreeImage_GetScanLine(dib, y);
+		for (int x = 0; x < width; x++, bits += 4) {
+			const BYTE alpha = bits[FI_RGBA_ALPHA];
+			// slightly faster: care for two special cases
+			if(alpha == 0x00) {
+				// special case for alpha == 0x00
+				// color * 0x00 / 0xFF = 0x00
+				bits[FI_RGBA_BLUE] = 0x00;
+				bits[FI_RGBA_GREEN] = 0x00;
+				bits[FI_RGBA_RED] = 0x00;
+			} else if(alpha == 0xFF) {
+				// nothing to do for alpha == 0xFF
+				// color * 0xFF / 0xFF = color
+				continue;
+			} else {
+				bits[FI_RGBA_BLUE] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_BLUE] + 127) / 255 );
+				bits[FI_RGBA_GREEN] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_GREEN] + 127) / 255 );
+				bits[FI_RGBA_RED] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_RED] + 127) / 255 );
+			}
+		}
+	}
+	return TRUE;
+}
+
diff --git a/files/Source/FreeImageToolkit/Filters.h b/files/Source/FreeImageToolkit/Filters.h
new file mode 100644
index 0000000..7a45c49
--- /dev/null
+++ b/files/Source/FreeImageToolkit/Filters.h
@@ -0,0 +1,287 @@
+// ==========================================================
+// Upsampling / downsampling filters
+//
+// 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!
+// ==========================================================
+
+#ifndef _FILTERS_H_
+#define _FILTERS_H_
+
+/**
+ CGenericFilter is a generic abstract filter class used to access to the filter library.<br>
+ Filters used in this library have been mainly taken from the following references : <br>
+<b>Main reference</b> : <br>
+Paul Heckbert, C code to zoom raster images up or down, with nice filtering. 
+UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
+
+<b>Heckbert references</b> : <br>
+<ul>
+<li>Oppenheim A.V., Schafer R.W., Digital Signal Processing, Prentice-Hall, 1975
+<li>Hamming R.W., Digital Filters, Prentice-Hall, Englewood Cliffs, NJ, 1983
+<li>Pratt W.K., Digital Image Processing, John Wiley and Sons, 1978
+<li>Hou H.S., Andrews H.C., "Cubic Splines for Image Interpolation and Digital Filtering", 
+IEEE Trans. Acoustics, Speech, and Signal Proc., vol. ASSP-26, no. 6, pp. 508-517, Dec. 1978.
+</ul>
+
+*/
+class CGenericFilter
+{
+protected:
+
+    #define FILTER_PI  double (3.1415926535897932384626433832795)
+    #define FILTER_2PI double (2.0 * 3.1415926535897932384626433832795)
+    #define FILTER_4PI double (4.0 * 3.1415926535897932384626433832795)
+
+    /// Filter support
+	double  m_dWidth;
+
+public:
+    
+    /// Constructor
+	CGenericFilter (double dWidth) : m_dWidth (dWidth) {}
+	/// Destructor
+    virtual ~CGenericFilter() {}
+
+    /// Returns the filter support
+	double GetWidth()                   { return m_dWidth; }
+	/// Change the filter suport
+    void   SetWidth (double dWidth)     { m_dWidth = dWidth; }
+
+    /// Returns F(dVal) where F is the filter's impulse response
+	virtual double Filter (double dVal) = 0;
+};
+
+// -----------------------------------------------------------------------------------
+// Filters library
+// All filters are centered on 0
+// -----------------------------------------------------------------------------------
+
+/**
+ Box filter<br>
+ Box, pulse, Fourier window, 1st order (constant) b-spline.<br><br>
+
+ <b>Reference</b> : <br>
+ Glassner A.S., Principles of digital image synthesis. Morgan Kaufmann Publishers, Inc, San Francisco, Vol. 2, 1995
+*/
+class CBoxFilter : public CGenericFilter
+{
+public:
+    /**
+	Constructor<br>
+	Default fixed width = 0.5
+	*/
+    CBoxFilter() : CGenericFilter(0.5) {}
+    virtual ~CBoxFilter() {}
+
+    double Filter (double dVal) { return (fabs(dVal) <= m_dWidth ? 1.0 : 0.0); }
+};
+
+/** Bilinear filter
+*/
+class CBilinearFilter : public CGenericFilter
+{
+public:
+
+    CBilinearFilter () : CGenericFilter(1) {}
+    virtual ~CBilinearFilter() {}
+
+    double Filter (double dVal) {
+		dVal = fabs(dVal); 
+		return (dVal < m_dWidth ? m_dWidth - dVal : 0.0); 
+	}
+};
+
+
+/**
+ Mitchell & Netravali's two-param cubic filter<br>
+
+ The parameters b and c can be used to adjust the properties of the cubic. 
+ They are sometimes referred to as "blurring" and "ringing" respectively. 
+ The default is b = 1/3 and c = 1/3, which were the values recommended by 
+ Mitchell and Netravali as yielding the most visually pleasing results in subjective tests of human beings. 
+ Larger values of b and c can produce interesting op-art effects--for example, try b = 0 and c = -5. <br><br>
+
+ <b>Reference</b> : <br>
+ Don P. Mitchell and Arun N. Netravali, Reconstruction filters in computer graphics. 
+ In John Dill, editor, Computer Graphics (SIGGRAPH '88 Proceedings), Vol. 22, No. 4, August 1988, pp. 221-228.
+*/
+class CBicubicFilter : public CGenericFilter
+{
+protected:
+	// data for parameterized Mitchell filter
+    double p0, p2, p3;
+    double q0, q1, q2, q3;
+
+public:
+    /**
+	Constructor<br>
+	Default fixed width = 2
+	@param b Filter parameter (default value is 1/3)
+	@param c Filter parameter (default value is 1/3)
+	*/
+    CBicubicFilter (double b = (1/(double)3), double c = (1/(double)3)) : CGenericFilter(2) {
+		p0 = (6 - 2*b) / 6;
+		p2 = (-18 + 12*b + 6*c) / 6;
+		p3 = (12 - 9*b - 6*c) / 6;
+		q0 = (8*b + 24*c) / 6;
+		q1 = (-12*b - 48*c) / 6;
+		q2 = (6*b + 30*c) / 6;
+		q3 = (-b - 6*c) / 6;
+	}
+    virtual ~CBicubicFilter() {}
+
+    double Filter(double dVal) { 
+		dVal = fabs(dVal);
+		if(dVal < 1)
+			return (p0 + dVal*dVal*(p2 + dVal*p3));
+		if(dVal < 2)
+			return (q0 + dVal*(q1 + dVal*(q2 + dVal*q3)));
+		return 0;
+	}
+};
+
+/**
+ Catmull-Rom spline, Overhauser spline<br>
+
+ When using CBicubicFilter filters, you have to set parameters b and c such that <br>
+ b + 2 * c = 1<br>
+ in order to use the numerically most accurate filter.<br>
+ This gives for b = 0 the maximum value for c = 0.5, which is the Catmull-Rom 
+ spline and a good suggestion for sharpness.<br><br>
+
+
+ <b>References</b> : <br>
+ <ul>
+ <li>Mitchell Don P., Netravali Arun N., Reconstruction filters in computer graphics. 
+ In John Dill, editor, Computer Graphics (SIGGRAPH '88 Proceedings), Vol. 22, No. 4, August 1988, pp. 221-228.
+ <li>Keys R.G., Cubic Convolution Interpolation for Digital Image Processing. 
+ IEEE Trans. Acoustics, Speech, and Signal Processing, vol. 29, no. 6, pp. 1153-1160, Dec. 1981.
+ </ul>
+
+*/
+class CCatmullRomFilter : public CGenericFilter
+{
+public:
+
+    /**
+	Constructor<br>
+	Default fixed width = 2
+	*/
+	CCatmullRomFilter() : CGenericFilter(2) {}
+    virtual ~CCatmullRomFilter() {}
+
+    double Filter(double dVal) { 
+		if(dVal < -2) return 0;
+		if(dVal < -1) return (0.5*(4 + dVal*(8 + dVal*(5 + dVal))));
+		if(dVal < 0)  return (0.5*(2 + dVal*dVal*(-5 - 3*dVal)));
+		if(dVal < 1)  return (0.5*(2 + dVal*dVal*(-5 + 3*dVal)));
+		if(dVal < 2)  return (0.5*(4 + dVal*(-8 + dVal*(5 - dVal))));
+		return 0;
+	}
+};
+
+/**
+ Lanczos-windowed sinc filter<br>
+ 
+ Lanczos3 filter is an alternative to CBicubicFilter with high values of c about 0.6 ... 0.75 
+ which produces quite strong sharpening. It usually offers better quality (fewer artifacts) and a sharp image.<br><br>
+
+*/
+class CLanczos3Filter : public CGenericFilter
+{
+public:
+    /**
+	Constructor<br>
+	Default fixed width = 3
+	*/
+	CLanczos3Filter() : CGenericFilter(3) {}
+    virtual ~CLanczos3Filter() {}
+
+    double Filter(double dVal) { 
+		dVal = fabs(dVal); 
+		if(dVal < m_dWidth)	{
+			return (sinc(dVal) * sinc(dVal / m_dWidth));
+		}
+		return 0;
+	}
+
+private:
+	double sinc(double value) {
+		if(value != 0) {
+			value *= FILTER_PI;
+			return (sin(value) / value);
+		} 
+		return 1;
+	}
+};
+
+/**
+ 4th order (cubic) b-spline<br>
+
+*/
+class CBSplineFilter : public CGenericFilter
+{
+public:
+
+    /**
+	Constructor<br>
+	Default fixed width = 2
+	*/
+	CBSplineFilter() : CGenericFilter(2) {}
+    virtual ~CBSplineFilter() {}
+
+    double Filter(double dVal) { 
+
+		dVal = fabs(dVal);
+		if(dVal < 1) return (4 + dVal*dVal*(-6 + 3*dVal)) / 6;
+		if(dVal < 2) {
+			double t = 2 - dVal;
+			return (t*t*t / 6);
+		}
+		return 0;
+	}
+};
+
+// -----------------------------------------------------------------------------------
+// Window function library
+// -----------------------------------------------------------------------------------
+
+/** 
+ Blackman window
+*/
+class CBlackmanFilter : public CGenericFilter
+{
+public:
+    /**
+	Constructor<br>
+	Default width = 0.5
+	*/
+    CBlackmanFilter (double dWidth = double(0.5)) : CGenericFilter(dWidth) {}
+    virtual ~CBlackmanFilter() {}
+
+    double Filter (double dVal) {
+		if(fabs (dVal) > m_dWidth) {
+			return 0; 
+        }
+        double dN = 2 * m_dWidth + 1; 
+		dVal /= (dN - 1);
+        return 0.42 + 0.5*cos(FILTER_2PI*dVal) + 0.08*cos(FILTER_4PI*dVal); 
+    }
+};
+
+#endif  // _FILTERS_H_
diff --git a/files/Source/FreeImageToolkit/Flip.cpp b/files/Source/FreeImageToolkit/Flip.cpp
new file mode 100644
index 0000000..c7898a7
--- /dev/null
+++ b/files/Source/FreeImageToolkit/Flip.cpp
@@ -0,0 +1,166 @@
+// ==========================================================
+// Flipping routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jim Keir (jimkeir@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"
+
+/**
+Flip the image horizontally along the vertical axis.
+@param src Input image to be processed.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV 
+FreeImage_FlipHorizontal(FIBITMAP *src) {
+	if (!FreeImage_HasPixels(src)) return FALSE;
+
+	unsigned line   = FreeImage_GetLine(src);
+	unsigned width	= FreeImage_GetWidth(src);
+	unsigned height = FreeImage_GetHeight(src);
+
+	unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+	// copy between aligned memories
+	BYTE *new_bits = (BYTE*)FreeImage_Aligned_Malloc(line * sizeof(BYTE), FIBITMAP_ALIGNMENT);
+	if (!new_bits) return FALSE;
+
+	// mirror the buffer
+
+	for (unsigned y = 0; y < height; y++) {
+		BYTE *bits = FreeImage_GetScanLine(src, y);
+		memcpy(new_bits, bits, line);
+
+		switch (FreeImage_GetBPP(src)) {
+			case 1 :
+			{				
+				for(unsigned x = 0; x < width; x++) {
+					// get pixel at (x, y)
+					BOOL value = (new_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+					// set pixel at (new_x, y)
+					unsigned new_x = width - 1 - x;
+					value ? bits[new_x >> 3] |= (0x80 >> (new_x & 0x7)) : bits[new_x >> 3] &= (0xff7f >> (new_x & 0x7));
+				}
+			}
+			break;
+
+			case 4 :
+			{
+				for(unsigned c = 0; c < line; c++) {
+					bits[c] = new_bits[line - c - 1];
+
+					BYTE nibble = (bits[c] & 0xF0) >> 4;
+
+					bits[c] = bits[c] << 4;
+					bits[c] |= nibble;
+				}
+			}
+			break;
+
+			case 8:
+			{				
+				BYTE *dst_data = (BYTE*) bits; 				
+				BYTE *src_data = (BYTE*) (new_bits + line - bytespp); 				
+				for(unsigned c = 0; c < width; c++) { 			
+					*dst_data++ = *src_data--;  
+				} 
+			}
+			break;
+
+			case 16:
+			{				
+				WORD *dst_data = (WORD*) bits; 				
+				WORD *src_data = (WORD*) (new_bits + line - bytespp); 				
+				for(unsigned c = 0; c < width; c++) { 			
+					*dst_data++ = *src_data--;  
+				} 
+			}
+			break;
+
+			case 24 :
+			case 32 :
+			case 48:
+			case 64:
+			case 96:
+			case 128:
+			{				
+				BYTE *dst_data = (BYTE*) bits; 				
+				BYTE *src_data = (BYTE*) (new_bits + line - bytespp); 				
+				for(unsigned c = 0; c < width; c++) { 		
+					for(unsigned k = 0; k < bytespp; k++) {
+						*dst_data++ = src_data[k];  
+					}
+					src_data -= bytespp;
+				} 
+			}
+			break;
+
+		}
+	}
+
+	FreeImage_Aligned_Free(new_bits);
+
+	return TRUE;
+}
+
+
+/**
+Flip the image vertically along the horizontal axis.
+@param src Input image to be processed.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+
+BOOL DLL_CALLCONV 
+FreeImage_FlipVertical(FIBITMAP *src) {
+	BYTE *From, *Mid;
+
+	if (!FreeImage_HasPixels(src)) return FALSE;
+
+	// swap the buffer
+
+	unsigned pitch  = FreeImage_GetPitch(src);
+	unsigned height = FreeImage_GetHeight(src);
+
+	// copy between aligned memories
+	Mid = (BYTE*)FreeImage_Aligned_Malloc(pitch * sizeof(BYTE), FIBITMAP_ALIGNMENT);
+	if (!Mid) return FALSE;
+
+	From = FreeImage_GetBits(src);
+	
+	unsigned line_s = 0;
+	unsigned line_t = (height-1) * pitch;
+
+	for(unsigned y = 0; y < height/2; y++) {
+
+		memcpy(Mid, From + line_s, pitch);
+		memcpy(From + line_s, From + line_t, pitch);
+		memcpy(From + line_t, Mid, pitch);
+
+		line_s += pitch;
+		line_t -= pitch;
+
+	}
+
+	FreeImage_Aligned_Free(Mid);
+
+	return TRUE;
+}
+
diff --git a/files/Source/FreeImageToolkit/JPEGTransform.cpp b/files/Source/FreeImageToolkit/JPEGTransform.cpp
new file mode 100644
index 0000000..bd03a3d
--- /dev/null
+++ b/files/Source/FreeImageToolkit/JPEGTransform.cpp
@@ -0,0 +1,623 @@
+// ==========================================================
+// JPEG lossless transformations
+//
+// Design and implementation by
+// - Petr Pytelka (pyta@lightcomp.com)
+// - Hervé Drolon (drolon@infonie.fr)
+// - 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!
+// ==========================================================
+
+extern "C" {
+#define XMD_H
+#undef FAR
+#include <setjmp.h>
+
+#include "third_party/jpeg/jinclude.h"
+#include "third_party/jpeg/jpeglib.h"
+#include "third_party/jpeg/jerror.h"
+#include "third_party/jpeg/transupp.h"
+}
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+
+// ----------------------------------------------------------
+//   Source manager & Destination manager setup
+//   (see PluginJPEG.cpp)
+// ----------------------------------------------------------
+
+void jpeg_freeimage_src(j_decompress_ptr cinfo, fi_handle infile, FreeImageIO *io);
+void jpeg_freeimage_dst(j_compress_ptr cinfo, fi_handle outfile, FreeImageIO *io);
+
+// ----------------------------------------------------------
+//   Error handling
+//   (see also PluginJPEG.cpp)
+// ----------------------------------------------------------
+
+/**
+	Receives control for a fatal error.  Information sufficient to
+	generate the error message has been stored in cinfo->err; call
+	output_message to display it.  Control must NOT return to the caller;
+	generally this routine will exit() or longjmp() somewhere.
+*/
+METHODDEF(void)
+ls_jpeg_error_exit (j_common_ptr cinfo) {
+	// always display the message
+	(*cinfo->err->output_message)(cinfo);
+
+	// allow JPEG with a premature end of file
+	if((cinfo)->err->msg_parm.i[0] != 13) {
+
+		// let the memory manager delete any temp files before we die
+		jpeg_destroy(cinfo);
+
+		throw FIF_JPEG;
+	}
+}
+
+/**
+	Actual output of any JPEG message.  Note that this method does not know
+	how to generate a message, only where to send it.
+*/
+METHODDEF(void)
+ls_jpeg_output_message (j_common_ptr cinfo) {
+	char buffer[JMSG_LENGTH_MAX];
+
+	// create the message
+	(*cinfo->err->format_message)(cinfo, buffer);
+	// send it to user's message proc
+	FreeImage_OutputMessageProc(FIF_JPEG, buffer);
+}
+
+// ----------------------------------------------------------
+//   Main program
+// ----------------------------------------------------------
+
+/**
+Build a crop string. 
+
+@param crop Output crop string
+@param left Specifies the left position of the cropped rectangle
+@param top Specifies the top position of the cropped rectangle
+@param right Specifies the right position of the cropped rectangle
+@param bottom Specifies the bottom position of the cropped rectangle
+@param width Image width
+@param height Image height
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+getCropString(char* crop, int* left, int* top, int* right, int* bottom, int width, int height) {
+	if(!left || !top || !right || !bottom) {
+		return FALSE;
+	}
+
+	*left = CLAMP(*left, 0, width);
+	*top = CLAMP(*top, 0, height);
+
+	// negative/zero right and bottom count from the edges inwards
+
+	if(*right <= 0) {
+		*right = width + *right;
+	}
+	if(*bottom <= 0) {
+		*bottom = height + *bottom;
+	}
+
+	*right = CLAMP(*right, 0, width);
+	*bottom = CLAMP(*bottom, 0, height);
+
+	// test for empty rect
+
+	if(((*left - *right) == 0) || ((*top - *bottom) == 0)) {
+		return FALSE;
+	}
+
+	// normalize the rectangle
+
+	if(*right < *left) {
+		INPLACESWAP(*left, *right);
+	}
+	if(*bottom < *top) {
+		INPLACESWAP(*top, *bottom);
+	}
+
+	// test for "noop" rect
+
+	if(*left == 0 && *right == width && *top == 0 && *bottom == height) {
+		return FALSE;
+	}
+
+	// build the crop option
+	sprintf(crop, "%dx%d+%d+%d", *right - *left, *bottom - *top, *left, *top);
+
+	return TRUE;
+}
+
+static BOOL
+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) {
+	const BOOL onlyReturnCropRect = (dst_io == NULL) || (dst_handle == NULL);
+	const long stream_start = onlyReturnCropRect ? 0 : dst_io->tell_proc(dst_handle);
+	BOOL swappedDim = FALSE;
+	BOOL trimH = FALSE;
+	BOOL trimV = FALSE;
+
+	// Set up the jpeglib structures
+	jpeg_decompress_struct srcinfo;
+	jpeg_compress_struct dstinfo;
+	jpeg_error_mgr jsrcerr, jdsterr;
+	jvirt_barray_ptr *src_coef_arrays = NULL;
+	jvirt_barray_ptr *dst_coef_arrays = NULL;
+	// Support for copying optional markers from source to destination file
+	JCOPY_OPTION copyoption;
+	// Image transformation options
+	jpeg_transform_info transfoptions;
+
+	// Initialize structures
+	memset(&srcinfo, 0, sizeof(srcinfo));
+	memset(&jsrcerr, 0, sizeof(jsrcerr));
+	memset(&jdsterr, 0, sizeof(jdsterr));
+	memset(&dstinfo, 0, sizeof(dstinfo));
+	memset(&transfoptions, 0, sizeof(transfoptions));
+
+	// Copy all extra markers from source file
+	copyoption = JCOPYOPT_ALL;
+
+	// Set up default JPEG parameters
+	transfoptions.force_grayscale = FALSE;
+	transfoptions.crop = FALSE;
+
+	// Select the transform option
+	switch(operation) {
+		case FIJPEG_OP_FLIP_H:		// horizontal flip
+			transfoptions.transform = JXFORM_FLIP_H;
+			trimH = TRUE;
+			break;
+		case FIJPEG_OP_FLIP_V:		// vertical flip
+			transfoptions.transform = JXFORM_FLIP_V;
+			trimV = TRUE;
+			break;
+		case FIJPEG_OP_TRANSPOSE:	// transpose across UL-to-LR axis
+			transfoptions.transform = JXFORM_TRANSPOSE;
+			swappedDim = TRUE;
+			break;
+		case FIJPEG_OP_TRANSVERSE:	// transpose across UR-to-LL axis
+			transfoptions.transform = JXFORM_TRANSVERSE;
+			trimH = TRUE;
+			trimV = TRUE;
+			swappedDim = TRUE;
+			break;
+		case FIJPEG_OP_ROTATE_90:	// 90-degree clockwise rotation
+			transfoptions.transform = JXFORM_ROT_90;
+			trimH = TRUE;
+			swappedDim = TRUE;
+			break;
+		case FIJPEG_OP_ROTATE_180:	// 180-degree rotation
+			trimH = TRUE;
+			trimV = TRUE;
+			transfoptions.transform = JXFORM_ROT_180;
+			break;
+		case FIJPEG_OP_ROTATE_270:	// 270-degree clockwise (or 90 ccw)
+			transfoptions.transform = JXFORM_ROT_270;
+			trimV = TRUE;
+			swappedDim = TRUE;
+			break;
+		default:
+		case FIJPEG_OP_NONE:		// no transformation
+			transfoptions.transform = JXFORM_NONE;
+			break;
+	}
+	// (perfect == TRUE) ==> fail if there is non-transformable edge blocks
+	transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE;
+	// Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle.
+	transfoptions.trim = TRUE;
+
+	try {
+
+		// Initialize the JPEG decompression object with default error handling
+		srcinfo.err = jpeg_std_error(&jsrcerr);
+		srcinfo.err->error_exit = ls_jpeg_error_exit;
+		srcinfo.err->output_message = ls_jpeg_output_message;
+		jpeg_create_decompress(&srcinfo);
+
+		// Initialize the JPEG compression object with default error handling
+		dstinfo.err = jpeg_std_error(&jdsterr);
+		dstinfo.err->error_exit = ls_jpeg_error_exit;
+		dstinfo.err->output_message = ls_jpeg_output_message;
+		jpeg_create_compress(&dstinfo);
+
+		// Specify data source for decompression
+		jpeg_freeimage_src(&srcinfo, src_handle, src_io);
+
+		// Enable saving of extra markers that we want to copy
+		jcopy_markers_setup(&srcinfo, copyoption);
+
+		// Read the file header
+		jpeg_read_header(&srcinfo, TRUE);
+
+		// crop option
+		char crop[64];
+		const BOOL hasCrop = getCropString(crop, left, top, right, bottom, swappedDim ? srcinfo.image_height : srcinfo.image_width, swappedDim ? srcinfo.image_width : srcinfo.image_height);
+
+		if(hasCrop) {
+			if(!jtransform_parse_crop_spec(&transfoptions, crop)) {
+				FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop);
+				throw(1);
+			}
+		}
+
+		// Any space needed by a transform option must be requested before
+		// jpeg_read_coefficients so that memory allocation will be done right
+
+		// Prepare transformation workspace
+		// Fails right away if perfect flag is TRUE and transformation is not perfect
+		if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) {
+			FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect");
+			throw(1);
+		}
+
+		if(left || top) {
+			// compute left and top offsets, it's a bit tricky, taking into account both
+			// transform, which might have trimed the image,
+			// and crop itself, which is adjusted to lie on a iMCU boundary
+
+			const int fullWidth = swappedDim ? srcinfo.image_height : srcinfo.image_width;
+			const int fullHeight = swappedDim ? srcinfo.image_width : srcinfo.image_height;
+
+			int transformedFullWidth = fullWidth;
+			int transformedFullHeight = fullHeight;
+
+			if(trimH && transformedFullWidth/transfoptions.iMCU_sample_width > 0) {
+				transformedFullWidth = (transformedFullWidth/transfoptions.iMCU_sample_width) * transfoptions.iMCU_sample_width;
+			}
+			if(trimV && transformedFullHeight/transfoptions.iMCU_sample_height > 0) {
+				transformedFullHeight = (transformedFullHeight/transfoptions.iMCU_sample_height) * transfoptions.iMCU_sample_height;
+			}
+
+			const int trimmedWidth = fullWidth - transformedFullWidth;
+			const int trimmedHeight = fullHeight - transformedFullHeight;
+
+			if(left) {
+				*left = trimmedWidth + transfoptions.x_crop_offset * transfoptions.iMCU_sample_width;
+			}
+			if(top) {
+				*top = trimmedHeight + transfoptions.y_crop_offset * transfoptions.iMCU_sample_height;
+			}
+		}
+
+		if(right) {
+			*right = (left ? *left : 0) + transfoptions.output_width;
+		}
+		if(bottom) {
+			*bottom = (top ? *top : 0) + transfoptions.output_height;
+		}
+
+		// if only the crop rect is requested, we are done
+
+		if(onlyReturnCropRect) {
+			jpeg_destroy_compress(&dstinfo);
+			jpeg_destroy_decompress(&srcinfo);
+			return TRUE;
+		}
+
+		// Read source file as DCT coefficients
+		src_coef_arrays = jpeg_read_coefficients(&srcinfo);
+
+		// Initialize destination compression parameters from source values
+		jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
+
+		// Adjust destination parameters if required by transform options;
+		// also find out which set of coefficient arrays will hold the output
+		dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
+
+		// Note: we assume that jpeg_read_coefficients consumed all input
+		// until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
+		// only consume more while (! cinfo->inputctl->eoi_reached).
+		// We cannot call jpeg_finish_decompress here since we still need the
+		// virtual arrays allocated from the source object for processing.
+
+		if(src_handle == dst_handle) {
+			dst_io->seek_proc(dst_handle, stream_start, SEEK_SET);
+		}
+
+		// Specify data destination for compression
+		jpeg_freeimage_dst(&dstinfo, dst_handle, dst_io);
+
+		// Start compressor (note no image data is actually written here)
+		jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
+
+		// Copy to the output file any extra markers that we want to preserve
+		jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
+
+		// Execute image transformation, if any
+		jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
+
+		// Finish compression and release memory
+		jpeg_finish_compress(&dstinfo);
+		jpeg_destroy_compress(&dstinfo);
+		jpeg_finish_decompress(&srcinfo);
+		jpeg_destroy_decompress(&srcinfo);
+
+	}
+	catch(...) {
+		jpeg_destroy_compress(&dstinfo);
+		jpeg_destroy_decompress(&srcinfo);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//   FreeImage interface
+// ----------------------------------------------------------
+
+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) {
+	return JPEGTransformFromHandle(src_io, src_handle, dst_io, dst_handle, operation, left, top, right, bottom, perfect);
+}
+
+static void
+closeStdIO(fi_handle src_handle, fi_handle dst_handle) {
+	if(src_handle) {
+		fclose((FILE*)src_handle);
+	}
+	if(dst_handle && (dst_handle != src_handle)) {
+		fclose((FILE*)dst_handle);
+	}
+}
+
+static BOOL
+openStdIO(const char* src_file, const char* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
+	*src_handle = NULL;
+	*dst_handle = NULL;
+	
+	FreeImageIO io;
+	SetDefaultIO (&io);
+	
+	const BOOL isSameFile = (dst_file && (strcmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
+	
+	FILE* srcp = NULL;
+	FILE* dstp = NULL;
+	
+	if(isSameFile) {
+		srcp = fopen(src_file, "r+b");
+		dstp = srcp;
+	}
+	else {
+		srcp = fopen(src_file, "rb");
+		if(dst_file) {
+			dstp = fopen(dst_file, "wb");
+		}
+	}
+	
+	if(!srcp || (dst_file && !dstp)) {
+		if(!srcp) {
+			FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for reading", src_file);
+		} else {
+			FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for writing", dst_file);
+		}
+		closeStdIO(srcp, dstp);
+		return FALSE;
+	}
+
+	if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
+		FreeImage_OutputMessageProc(FIF_JPEG, " Source file \"%s\" is not jpeg", src_file);
+		closeStdIO(srcp, dstp);
+		return FALSE;
+	}
+
+	*dst_io = io;
+	*src_handle = srcp;
+	*dst_handle = dstp;
+
+	return TRUE;
+}
+
+static BOOL
+openStdIOU(const wchar_t* src_file, const wchar_t* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
+#ifdef _WIN32
+
+	*src_handle = NULL;
+	*dst_handle = NULL;
+
+	FreeImageIO io;
+	SetDefaultIO (&io);
+	
+	const BOOL isSameFile = (dst_file && (wcscmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
+
+	FILE* srcp = NULL;
+	FILE* dstp = NULL;
+
+	if(isSameFile) {
+		srcp = _wfopen(src_file, L"r+b");
+		dstp = srcp;
+	} else {
+		srcp = _wfopen(src_file, L"rb");
+		if(dst_file) {
+			dstp = _wfopen(dst_file, L"wb");
+		}
+	}
+
+	if(!srcp || (dst_file && !dstp)) {
+		if(!srcp) {
+			FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open source file for reading");
+		} else {
+			FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open destination file for writing");
+		}
+		closeStdIO(srcp, dstp);
+		return FALSE;
+	}
+
+	if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
+		FreeImage_OutputMessageProc(FIF_JPEG, " Source file is not jpeg");
+		closeStdIO(srcp, dstp);
+		return FALSE;
+	}
+
+	*dst_io = io;
+	*src_handle = srcp;
+	*dst_handle = dstp;
+
+	return TRUE;
+
+#else
+	return FALSE;
+#endif // _WIN32
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
+	FreeImageIO io;
+	fi_handle src;
+	fi_handle dst;
+	
+	if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
+		return FALSE;
+	}
+	
+	BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
+
+	closeStdIO(src, dst);
+
+	return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) {
+	FreeImageIO io;
+	fi_handle src;
+	fi_handle dst;
+	
+	if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
+		return FALSE;
+	}
+	
+	BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
+	
+	closeStdIO(src, dst);
+	
+	return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
+	FreeImageIO io;
+	fi_handle src;
+	fi_handle dst;
+	
+	if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
+		return FALSE;
+	}
+	
+	BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
+	
+	closeStdIO(src, dst);
+
+	return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) {
+	FreeImageIO io;
+	fi_handle src;
+	fi_handle dst;
+	
+	if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
+		return FALSE;
+	}
+	
+	BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
+
+	closeStdIO(src, dst);
+
+	return ret;
+}
+
+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) {
+	FreeImageIO io;
+	fi_handle src;
+	fi_handle dst;
+	
+	if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
+		return FALSE;
+	}
+	
+	BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
+
+	closeStdIO(src, dst);
+
+	return ret;
+}
+
+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) {
+	FreeImageIO io;
+	fi_handle src;
+	fi_handle dst;
+	
+	if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
+		return FALSE;
+	}
+	
+	BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
+
+	closeStdIO(src, dst);
+
+	return ret;
+}
+
+// --------------------------------------------------------------------------
+
+static BOOL
+getMemIO(FIMEMORY* src_stream, FIMEMORY* dst_stream, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
+	*src_handle = NULL;
+	*dst_handle = NULL;
+
+	FreeImageIO io;
+	SetMemoryIO (&io);
+
+	if(dst_stream) {
+		FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(dst_stream->data);
+		if(mem_header->delete_me != TRUE) {
+			// do not save in a user buffer
+			FreeImage_OutputMessageProc(FIF_JPEG, "Destination memory buffer is read only");
+			return FALSE;
+		}
+	}
+
+	*dst_io = io;
+	*src_handle = src_stream;
+	*dst_handle = dst_stream;
+
+	return TRUE;
+}
+
+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) {
+	FreeImageIO io;
+	fi_handle src;
+	fi_handle dst;
+	
+	if(!getMemIO(src_stream, dst_stream, &io, &src, &dst)) {
+		return FALSE;
+	}
+	
+	return FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
+}
+
diff --git a/files/Source/FreeImageToolkit/MultigridPoissonSolver.cpp b/files/Source/FreeImageToolkit/MultigridPoissonSolver.cpp
new file mode 100644
index 0000000..3b577cb
--- /dev/null
+++ b/files/Source/FreeImageToolkit/MultigridPoissonSolver.cpp
@@ -0,0 +1,505 @@
+// ==========================================================
+// Poisson solver based on a full multigrid algorithm
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// Reference:
+// PRESS, W. H., TEUKOLSKY, S. A., VETTERLING, W. T., AND FLANNERY, B. P.
+// 1992. Numerical Recipes in C: The Art of Scientific Computing, 2nd ed. Cambridge University Press.
+//
+// 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 "ToneMapping.h"
+
+static const int NPRE	= 1;		// Number of relaxation sweeps before ...
+static const int NPOST	= 1;		// ... and after the coarse-grid correction is computed
+static const int NGMAX	= 15;		// Maximum number of grids
+
+/**
+Copy src into dst
+*/
+static inline void fmg_copyArray(FIBITMAP *dst, FIBITMAP *src) {
+	memcpy(FreeImage_GetBits(dst), FreeImage_GetBits(src), FreeImage_GetHeight(dst) * FreeImage_GetPitch(dst));
+}
+
+/**
+Fills src with zeros
+*/
+static inline void fmg_fillArrayWithZeros(FIBITMAP *src) {
+	memset(FreeImage_GetBits(src), 0, FreeImage_GetHeight(src) * FreeImage_GetPitch(src));
+}
+
+/**
+Half-weighting restriction. nc is the coarse-grid dimension. The fine-grid solution is input in
+uf[0..2*nc-2][0..2*nc-2], the coarse-grid solution is returned in uc[0..nc-1][0..nc-1].
+*/
+static void fmg_restrict(FIBITMAP *UC, FIBITMAP *UF, int nc) {
+	int row_uc, row_uf, col_uc, col_uf;
+
+	const int uc_pitch  = FreeImage_GetPitch(UC) / sizeof(float);
+	const int uf_pitch  = FreeImage_GetPitch(UF) / sizeof(float);
+	
+	float *uc_bits = (float*)FreeImage_GetBits(UC);
+	const float *uf_bits = (float*)FreeImage_GetBits(UF);
+
+	// interior points
+	{
+		float *uc_scan = uc_bits + uc_pitch;
+		for (row_uc = 1, row_uf = 2; row_uc < nc-1; row_uc++, row_uf += 2) {
+			const float *uf_scan = uf_bits + row_uf * uf_pitch;
+			for (col_uc = 1, col_uf = 2; col_uc < nc-1; col_uc++, col_uf += 2) { 
+				// calculate 
+				// UC(row_uc, col_uc) = 
+				// 0.5 * UF(row_uf, col_uf) + 0.125 * [ UF(row_uf+1, col_uf) + UF(row_uf-1, col_uf) + UF(row_uf, col_uf+1) + UF(row_uf, col_uf-1) ]
+				float *uc_pixel = uc_scan + col_uc;
+				const float *uf_center = uf_scan + col_uf;
+				*uc_pixel = 0.5F * *uf_center + 0.125F * ( *(uf_center + uf_pitch) + *(uf_center - uf_pitch) + *(uf_center + 1) + *(uf_center - 1) );
+			}
+			uc_scan += uc_pitch;
+		}
+	}
+	// boundary points
+	const int ncc = 2*nc-1;
+	{
+		/*
+		calculate the following: 
+		for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) { 
+			UC(row_uc, 0) = UF(row_uf, 0);		
+			UC(row_uc, nc-1) = UF(row_uf, ncc-1);
+		}
+		*/
+		float *uc_scan = uc_bits;
+		for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) { 
+			const float *uf_scan = uf_bits + row_uf * uf_pitch;
+			uc_scan[0] = uf_scan[0];
+			uc_scan[nc-1] = uf_scan[ncc-1];
+			uc_scan += uc_pitch;
+		}
+	}
+	{
+		/*
+		calculate the following: 
+		for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
+			UC(0, col_uc) = UF(0, col_uf);
+			UC(nc-1, col_uc) = UF(ncc-1, col_uf);
+		}
+		*/
+		float *uc_scan_top = uc_bits;
+		float *uc_scan_bottom = uc_bits + (nc-1)*uc_pitch;
+		const float *uf_scan_top = uf_bits + (ncc-1)*uf_pitch;
+		const float *uf_scan_bottom = uf_bits;
+		for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
+			uc_scan_top[col_uc] = uf_scan_top[col_uf];
+			uc_scan_bottom[col_uc] = uf_scan_bottom[col_uf];
+		}
+	}
+}
+
+/**
+Solution of the model problem on the coarsest grid, where h = 1/2 . 
+The right-hand side is input
+in rhs[0..2][0..2] and the solution is returned in u[0..2][0..2].
+*/
+static void fmg_solve(FIBITMAP *U, FIBITMAP *RHS) {
+	// fill U with zeros
+	fmg_fillArrayWithZeros(U);
+	// calculate U(1, 1) = -h*h*RHS(1, 1)/4.0 where h = 1/2
+	float *u_scan = (float*)FreeImage_GetScanLine(U, 1);
+	const float *rhs_scan = (float*)FreeImage_GetScanLine(RHS, 1);
+	u_scan[1] = -rhs_scan[1] / 16;
+}
+
+/**
+Coarse-to-fine prolongation by bilinear interpolation. nf is the fine-grid dimension. The coarsegrid
+solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2 + 1. The fine-grid solution is
+returned in uf[0..nf-1][0..nf-1].
+*/
+static void fmg_prolongate(FIBITMAP *UF, FIBITMAP *UC, int nf) {
+	int row_uc, row_uf, col_uc, col_uf;
+
+	const int uf_pitch  = FreeImage_GetPitch(UF) / sizeof(float);
+	const int uc_pitch  = FreeImage_GetPitch(UC) / sizeof(float);
+	
+	float *uf_bits = (float*)FreeImage_GetBits(UF);
+	const float *uc_bits = (float*)FreeImage_GetBits(UC);
+	
+	// do elements that are copies
+	{
+		const int nc = nf/2 + 1;
+
+		float *uf_scan = uf_bits;
+		const float *uc_scan = uc_bits;		
+		for (row_uc = 0; row_uc < nc; row_uc++) {
+			for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
+				// calculate UF(2*row_uc, col_uf) = UC(row_uc, col_uc);
+				uf_scan[col_uf] = uc_scan[col_uc];
+			}
+			uc_scan += uc_pitch;
+			uf_scan += 2 * uf_pitch;
+		}
+	}
+	// do odd-numbered columns, interpolating vertically
+	{		
+		for(row_uf = 1; row_uf < nf-1; row_uf += 2) {
+			float *uf_scan = uf_bits + row_uf * uf_pitch;
+			for (col_uf = 0; col_uf < nf; col_uf += 2) {
+				// calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf+1, col_uf) + UF(row_uf-1, col_uf) )
+				uf_scan[col_uf] = 0.5F * ( *(uf_scan + uf_pitch + col_uf) + *(uf_scan - uf_pitch + col_uf) );
+			}
+		}
+	}
+	// do even-numbered columns, interpolating horizontally
+	{
+		float *uf_scan = uf_bits;
+		for(row_uf = 0; row_uf < nf; row_uf++) {
+			for (col_uf = 1; col_uf < nf-1; col_uf += 2) {
+				// calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf, col_uf+1) + UF(row_uf, col_uf-1) )
+				uf_scan[col_uf] = 0.5F * ( uf_scan[col_uf + 1] + uf_scan[col_uf - 1] );
+			}
+			uf_scan += uf_pitch;
+		}
+	}
+}
+
+/**
+Red-black Gauss-Seidel relaxation for model problem. Updates the current value of the solution
+u[0..n-1][0..n-1], using the right-hand side function rhs[0..n-1][0..n-1].
+*/
+static void fmg_relaxation(FIBITMAP *U, FIBITMAP *RHS, int n) {
+	int row, col, ipass, isw, jsw;
+	const float h = 1.0F / (n - 1);
+	const float h2 = h*h;
+
+	const int u_pitch  = FreeImage_GetPitch(U) / sizeof(float);
+	const int rhs_pitch  = FreeImage_GetPitch(RHS) / sizeof(float);
+	
+	float *u_bits = (float*)FreeImage_GetBits(U);
+	const float *rhs_bits = (float*)FreeImage_GetBits(RHS);
+
+	for (ipass = 0, jsw = 1; ipass < 2; ipass++, jsw = 3-jsw) { // Red and black sweeps
+		float *u_scan = u_bits + u_pitch;
+		const float *rhs_scan = rhs_bits + rhs_pitch;
+		for (row = 1, isw = jsw; row < n-1; row++, isw = 3-isw) {
+			for (col = isw; col < n-1; col += 2) { 
+				// Gauss-Seidel formula
+				// calculate U(row, col) = 
+				// 0.25 * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - h2 * RHS(row, col) ]		 
+				float *u_center = u_scan + col;
+				const float *rhs_center = rhs_scan + col;
+				*u_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1);
+				*u_center -= h2 * *rhs_center;
+				*u_center *= 0.25F;
+			}
+			u_scan += u_pitch;
+			rhs_scan += rhs_pitch;
+		}
+	}
+}
+
+/**
+Returns minus the residual for the model problem. Input quantities are u[0..n-1][0..n-1] and
+rhs[0..n-1][0..n-1], while res[0..n-1][0..n-1] is returned.
+*/
+static void fmg_residual(FIBITMAP *RES, FIBITMAP *U, FIBITMAP *RHS, int n) {
+	int row, col;
+
+	const float h = 1.0F / (n-1);	
+	const float h2i = 1.0F / (h*h);
+
+	const int res_pitch  = FreeImage_GetPitch(RES) / sizeof(float);
+	const int u_pitch  = FreeImage_GetPitch(U) / sizeof(float);
+	const int rhs_pitch  = FreeImage_GetPitch(RHS) / sizeof(float);
+	
+	float *res_bits = (float*)FreeImage_GetBits(RES);
+	const float *u_bits = (float*)FreeImage_GetBits(U);
+	const float *rhs_bits = (float*)FreeImage_GetBits(RHS);
+
+	// interior points
+	{
+		float *res_scan = res_bits + res_pitch;
+		const float *u_scan = u_bits + u_pitch;
+		const float *rhs_scan = rhs_bits + rhs_pitch;
+		for (row = 1; row < n-1; row++) {
+			for (col = 1; col < n-1; col++) {
+				// calculate RES(row, col) = 
+				// -h2i * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - 4 * U(row, col) ] + RHS(row, col);
+				float *res_center = res_scan + col;
+				const float *u_center = u_scan + col;
+				const float *rhs_center = rhs_scan + col;
+				*res_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1) - 4 * *u_center;
+				*res_center *= -h2i;
+				*res_center += *rhs_center;
+			}
+			res_scan += res_pitch;
+			u_scan += u_pitch;
+			rhs_scan += rhs_pitch;
+		}
+	}
+
+	// boundary points
+	{
+		memset(FreeImage_GetScanLine(RES, 0), 0, FreeImage_GetPitch(RES));
+		memset(FreeImage_GetScanLine(RES, n-1), 0, FreeImage_GetPitch(RES));
+		float *left = res_bits;
+		float *right = res_bits + (n-1);
+		for(int k = 0; k < n; k++) {
+			*left = 0;
+			*right = 0;
+			left += res_pitch;
+			right += res_pitch;
+		}
+	}
+}
+
+/**
+Does coarse-to-fine interpolation and adds result to uf. nf is the fine-grid dimension. The
+coarse-grid solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2+1. The fine-grid solution
+is returned in uf[0..nf-1][0..nf-1]. res[0..nf-1][0..nf-1] is used for temporary storage.
+*/
+static void fmg_addint(FIBITMAP *UF, FIBITMAP *UC, FIBITMAP *RES, int nf) {
+	fmg_prolongate(RES, UC, nf);
+
+	const int uf_pitch  = FreeImage_GetPitch(UF) / sizeof(float);
+	const int res_pitch  = FreeImage_GetPitch(RES) / sizeof(float);	
+
+	float *uf_bits = (float*)FreeImage_GetBits(UF);
+	const float *res_bits = (float*)FreeImage_GetBits(RES);
+
+	for(int row = 0; row < nf; row++) {
+		for(int col = 0; col < nf; col++) {
+			// calculate UF(row, col) = UF(row, col) + RES(row, col);
+			uf_bits[col] += res_bits[col];
+		}
+		uf_bits += uf_pitch;
+		res_bits += res_pitch;
+	}
+}
+
+/**
+Full Multigrid Algorithm for solution of linear elliptic equation, here the model problem (19.0.6).
+On input u[0..n-1][0..n-1] contains the right-hand side ñ, while on output it returns the solution.
+The dimension n must be of the form 2^j + 1 for some integer j. (j is actually the number of
+grid levels used in the solution, called ng below.) ncycle is the number of V-cycles to be
+used at each level.
+*/
+static BOOL fmg_mglin(FIBITMAP *U, int n, int ncycle) {
+	int j, jcycle, jj, jpost, jpre, nf, ngrid;
+
+	FIBITMAP **IRHO = NULL;
+	FIBITMAP **IU   = NULL;
+	FIBITMAP **IRHS = NULL;
+	FIBITMAP **IRES = NULL;
+	
+	int ng = 0;		// number of allocated grids
+
+// --------------------------------------------------------------------------
+
+#define _CREATE_ARRAY_GRID_(array, array_size) \
+	array = (FIBITMAP**)malloc(array_size * sizeof(FIBITMAP*));\
+	if(!array) throw(1);\
+	memset(array, 0, array_size * sizeof(FIBITMAP*))
+
+#define _FREE_ARRAY_GRID_(array, array_size) \
+	if(NULL != array) {\
+		for(int k = 0; k < array_size; k++) {\
+			if(NULL != array[k]) {\
+				FreeImage_Unload(array[k]); array[k] = NULL;\
+			}\
+		}\
+		free(array);\
+	}
+
+// --------------------------------------------------------------------------
+
+	try {
+		int nn = n;
+		// check grid size and grid levels
+		while (nn >>= 1) ng++;
+		if (n != 1 + (1L << ng)) {
+			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: n = %d, while n-1 must be a power of 2.", n);
+			throw(1);
+		}
+		if (ng > NGMAX) {
+			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: ng = %d while NGMAX = %d, increase NGMAX.", ng, NGMAX);
+			throw(1);
+		}
+		// allocate grid arrays
+		{
+			_CREATE_ARRAY_GRID_(IRHO, ng);
+			_CREATE_ARRAY_GRID_(IU, ng);
+			_CREATE_ARRAY_GRID_(IRHS, ng);
+			_CREATE_ARRAY_GRID_(IRES, ng);
+		}
+
+		nn = n/2 + 1;
+		ngrid = ng - 2;
+
+		// allocate storage for r.h.s. on grid (ng - 2) ...
+		IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+		if(!IRHO[ngrid]) throw(1);
+
+		// ... and fill it by restricting from the fine grid
+		fmg_restrict(IRHO[ngrid], U, nn);	
+
+		// similarly allocate storage and fill r.h.s. on all coarse grids.
+		while (nn > 3) {
+			nn = nn/2 + 1; 
+			ngrid--;
+			IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+			if(!IRHO[ngrid]) throw(1);
+			fmg_restrict(IRHO[ngrid], IRHO[ngrid+1], nn);
+		}
+
+		nn = 3;
+
+		IU[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+		if(!IU[0]) throw(1);
+		IRHS[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+		if(!IRHS[0]) throw(1);
+
+		// initial solution on coarsest grid
+		fmg_solve(IU[0], IRHO[0]);
+		// irho[0] no longer needed ...
+		FreeImage_Unload(IRHO[0]); IRHO[0] = NULL;
+
+		ngrid = ng;
+
+		// nested iteration loop
+		for (j = 1; j < ngrid; j++) {
+			nn = 2*nn - 1;
+
+			IU[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+			if(!IU[j]) throw(1);
+			IRHS[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+			if(!IRHS[j]) throw(1);
+			IRES[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+			if(!IRES[j]) throw(1);
+
+			fmg_prolongate(IU[j], IU[j-1], nn);
+			
+			// interpolate from coarse grid to next finer grid
+
+			// set up r.h.s.
+			fmg_copyArray(IRHS[j], j != (ngrid - 1) ? IRHO[j] : U);
+			
+			// V-cycle loop
+			for (jcycle = 0; jcycle < ncycle; jcycle++) {
+				nf = nn;
+				// downward stoke of the V
+				for (jj = j; jj >= 1; jj--) {
+					// pre-smoothing
+					for (jpre = 0; jpre < NPRE; jpre++) {
+						fmg_relaxation(IU[jj], IRHS[jj], nf);
+					}
+					fmg_residual(IRES[jj], IU[jj], IRHS[jj], nf);
+					nf = nf/2 + 1;
+					// restriction of the residual is the next r.h.s.
+					fmg_restrict(IRHS[jj-1], IRES[jj], nf);				
+					// zero for initial guess in next relaxation
+					fmg_fillArrayWithZeros(IU[jj-1]);
+				}
+				// bottom of V: solve on coarsest grid
+				fmg_solve(IU[0], IRHS[0]); 
+				nf = 3; 
+				// upward stroke of V.
+				for (jj = 1; jj <= j; jj++) { 
+					nf = 2*nf - 1;
+					// use res for temporary storage inside addint
+					fmg_addint(IU[jj], IU[jj-1], IRES[jj], nf);				
+					// post-smoothing
+					for (jpost = 0; jpost < NPOST; jpost++) {
+						fmg_relaxation(IU[jj], IRHS[jj], nf);
+					}
+				}
+			}
+		}
+
+		// return solution in U
+		fmg_copyArray(U, IU[ngrid-1]);
+
+		// delete allocated arrays
+		_FREE_ARRAY_GRID_(IRES, ng);
+		_FREE_ARRAY_GRID_(IRHS, ng);
+		_FREE_ARRAY_GRID_(IU, ng);
+		_FREE_ARRAY_GRID_(IRHO, ng);
+
+		return TRUE;
+
+	} catch(int) {
+		// delete allocated arrays
+		_FREE_ARRAY_GRID_(IRES, ng);
+		_FREE_ARRAY_GRID_(IRHS, ng);
+		_FREE_ARRAY_GRID_(IU, ng);
+		_FREE_ARRAY_GRID_(IRHO, ng);
+
+		return FALSE;
+	}
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Poisson solver based on a multigrid algorithm. 
+This routine solves a Poisson equation, remap result pixels to [0..1] and returns the solution. 
+NB: The input image is first stored inside a square image whose size is (2^j + 1)x(2^j + 1) for some integer j, 
+where j is such that 2^j is the nearest larger dimension corresponding to MAX(image width, image height). 
+@param Laplacian Laplacian image
+@param ncycle Number of cycles in the multigrid algorithm (usually 2 or 3)
+@return Returns the solved PDE equations if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV 
+FreeImage_MultigridPoissonSolver(FIBITMAP *Laplacian, int ncycle) {
+	if(!FreeImage_HasPixels(Laplacian)) return NULL;
+
+	int width = FreeImage_GetWidth(Laplacian);
+	int height = FreeImage_GetHeight(Laplacian);
+
+	// get nearest larger dimension length that is acceptable by the algorithm
+	int n = MAX(width, height);
+	int size = 0;
+	while((n >>= 1) > 0) size++;
+	if((1 << size) < MAX(width, height)) {
+		size++;
+	}
+	// size must be of the form 2^j + 1 for some integer j
+	size = 1 + (1 << size);
+
+	// allocate a temporary square image I
+	FIBITMAP *I = FreeImage_AllocateT(FIT_FLOAT, size, size);
+	if(!I) return NULL;
+
+	// copy Laplacian into I and shift pixels to create a boundary
+	FreeImage_Paste(I, Laplacian, 1, 1, 255);
+
+	// solve the PDE equation
+	fmg_mglin(I, size, ncycle);
+
+	// shift pixels back
+	FIBITMAP *U = FreeImage_Copy(I, 1, 1, width + 1, height + 1);
+	FreeImage_Unload(I);
+
+	// remap pixels to [0..1]
+	NormalizeY(U, 0, 1);
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(U, Laplacian);
+
+	// return the integrated image
+	return U;
+}
+
diff --git a/files/Source/FreeImageToolkit/Rescale.cpp b/files/Source/FreeImageToolkit/Rescale.cpp
new file mode 100644
index 0000000..4f885c2
--- /dev/null
+++ b/files/Source/FreeImageToolkit/Rescale.cpp
@@ -0,0 +1,192 @@
+// ==========================================================
+// Upsampling / downsampling routine
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - 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 "Resize.h"
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_RescaleRect(FIBITMAP *src, int dst_width, int dst_height, int src_left, int src_top, int src_right, int src_bottom, FREE_IMAGE_FILTER filter, unsigned flags) {
+	FIBITMAP *dst = NULL;
+
+	const int src_width = FreeImage_GetWidth(src);
+	const int src_height = FreeImage_GetHeight(src);
+
+	if (!FreeImage_HasPixels(src) || (dst_width <= 0) || (dst_height <= 0) || (src_width <= 0) || (src_height <= 0)) {
+		return NULL;
+	}
+
+	// normalize the rectangle
+	if (src_right < src_left) {
+		INPLACESWAP(src_left, src_right);
+	}
+	if (src_bottom < src_top) {
+		INPLACESWAP(src_top, src_bottom);
+	}
+
+	// check the size of the sub image
+	if((src_left < 0) || (src_right > src_width) || (src_top < 0) || (src_bottom > src_height)) {
+		return NULL;
+	}
+
+	// select the filter
+	CGenericFilter *pFilter = NULL;
+	switch (filter) {
+		case FILTER_BOX:
+			pFilter = new(std::nothrow) CBoxFilter();
+			break;
+		case FILTER_BICUBIC:
+			pFilter = new(std::nothrow) CBicubicFilter();
+			break;
+		case FILTER_BILINEAR:
+			pFilter = new(std::nothrow) CBilinearFilter();
+			break;
+		case FILTER_BSPLINE:
+			pFilter = new(std::nothrow) CBSplineFilter();
+			break;
+		case FILTER_CATMULLROM:
+			pFilter = new(std::nothrow) CCatmullRomFilter();
+			break;
+		case FILTER_LANCZOS3:
+			pFilter = new(std::nothrow) CLanczos3Filter();
+			break;
+	}
+
+	if (!pFilter) {
+		return NULL;
+	}
+
+	CResizeEngine Engine(pFilter);
+
+	dst = Engine.scale(src, dst_width, dst_height, src_left, src_top,
+			src_right - src_left, src_bottom - src_top, flags);
+
+	delete pFilter;
+
+	if ((flags & FI_RESCALE_OMIT_METADATA) != FI_RESCALE_OMIT_METADATA) {
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(dst, src);
+	}
+
+	return dst;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTER filter) {
+	return FreeImage_RescaleRect(src, dst_width, dst_height, 0, 0, FreeImage_GetWidth(src), FreeImage_GetHeight(src), filter, FI_RESCALE_DEFAULT);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert) {
+	FIBITMAP *thumbnail = NULL;
+	int new_width, new_height;
+
+	if(!FreeImage_HasPixels(dib) || (max_pixel_size <= 0)) return NULL;
+
+	int width	= FreeImage_GetWidth(dib);
+	int height = FreeImage_GetHeight(dib);
+
+	if(max_pixel_size == 0) max_pixel_size = 1;
+
+	if((width < max_pixel_size) && (height < max_pixel_size)) {
+		// image is smaller than the requested thumbnail
+		return FreeImage_Clone(dib);
+	}
+
+	if(width > height) {
+		new_width = max_pixel_size;
+		// change image height with the same ratio
+		double ratio = ((double)new_width / (double)width);
+		new_height = (int)(height * ratio + 0.5);
+		if(new_height == 0) new_height = 1;
+	} else {
+		new_height = max_pixel_size;
+		// change image width with the same ratio
+		double ratio = ((double)new_height / (double)height);
+		new_width = (int)(width * ratio + 0.5);
+		if(new_width == 0) new_width = 1;
+	}
+
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+	// perform downsampling using a bilinear interpolation
+
+	switch(image_type) {
+		case FIT_BITMAP:
+		case FIT_UINT16:
+		case FIT_RGB16:
+		case FIT_RGBA16:
+		case FIT_FLOAT:
+		case FIT_RGBF:
+		case FIT_RGBAF:
+		{
+			FREE_IMAGE_FILTER filter = FILTER_BILINEAR;
+			thumbnail = FreeImage_Rescale(dib, new_width, new_height, filter);
+		}
+		break;
+
+		case FIT_INT16:
+		case FIT_UINT32:
+		case FIT_INT32:
+		case FIT_DOUBLE:
+		case FIT_COMPLEX:
+		default:
+			// cannot rescale this kind of image
+			thumbnail = NULL;
+			break;
+	}
+
+	if((thumbnail != NULL) && (image_type != FIT_BITMAP) && convert) {
+		// convert to a standard bitmap
+		FIBITMAP *bitmap = NULL;
+		switch(image_type) {
+			case FIT_UINT16:
+				bitmap = FreeImage_ConvertTo8Bits(thumbnail);
+				break;
+			case FIT_RGB16:
+				bitmap = FreeImage_ConvertTo24Bits(thumbnail);
+				break;
+			case FIT_RGBA16:
+				bitmap = FreeImage_ConvertTo32Bits(thumbnail);
+				break;
+			case FIT_FLOAT:
+				bitmap = FreeImage_ConvertToStandardType(thumbnail, TRUE);
+				break;
+			case FIT_RGBF:
+				bitmap = FreeImage_ToneMapping(thumbnail, FITMO_DRAGO03);
+				break;
+			case FIT_RGBAF:
+				// no way to keep the transparency yet ...
+				FIBITMAP *rgbf = FreeImage_ConvertToRGBF(thumbnail);
+				bitmap = FreeImage_ToneMapping(rgbf, FITMO_DRAGO03);
+				FreeImage_Unload(rgbf);
+				break;
+		}
+		if(bitmap != NULL) {
+			FreeImage_Unload(thumbnail);
+			thumbnail = bitmap;
+		}
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(thumbnail, dib);
+
+	return thumbnail;
+}
diff --git a/files/Source/FreeImageToolkit/Resize.cpp b/files/Source/FreeImageToolkit/Resize.cpp
new file mode 100644
index 0000000..dbc738f
--- /dev/null
+++ b/files/Source/FreeImageToolkit/Resize.cpp
@@ -0,0 +1,2116 @@
+// ==========================================================
+// Upsampling / downsampling classes
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.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 "Resize.h"
+
+/**
+Returns the color type of a bitmap. In contrast to FreeImage_GetColorType,
+this function optionally supports a boolean OUT parameter, that receives TRUE,
+if the specified bitmap is greyscale, that is, it consists of grey colors only.
+Although it returns the same value as returned by FreeImage_GetColorType for all
+image types, this extended function primarily is intended for palletized images,
+since the boolean pointed to by 'bIsGreyscale' remains unchanged for RGB(A/F)
+images. However, the outgoing boolean is properly maintained for palletized images,
+as well as for any non-RGB image type, like FIT_UINTxx and FIT_DOUBLE, for example.
+@param dib A pointer to a FreeImage bitmap to calculate the extended color type for
+@param bIsGreyscale A pointer to a boolean, that receives TRUE, if the specified bitmap
+is greyscale, that is, it consists of grey colors only. This parameter can be NULL.
+@return the color type of the specified bitmap
+*/
+static FREE_IMAGE_COLOR_TYPE
+GetExtendedColorType(FIBITMAP *dib, BOOL *bIsGreyscale) {
+	const unsigned bpp = FreeImage_GetBPP(dib);
+	const unsigned size = CalculateUsedPaletteEntries(bpp);
+	const RGBQUAD * const pal = FreeImage_GetPalette(dib);
+	FREE_IMAGE_COLOR_TYPE color_type = FIC_MINISBLACK;
+	BOOL bIsGrey = TRUE;
+
+	switch (bpp) {
+		case 1:
+		{
+			for (unsigned i = 0; i < size; i++) {
+				if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
+					color_type = FIC_PALETTE;
+					bIsGrey = FALSE;
+					break;
+				}
+			}
+			if (bIsGrey) {
+				if (pal[0].rgbBlue == 255 && pal[1].rgbBlue == 0) {
+					color_type = FIC_MINISWHITE;
+				} else if (pal[0].rgbBlue != 0 || pal[1].rgbBlue != 255) {
+					color_type = FIC_PALETTE;
+				}
+			}
+			break;
+		}
+
+		case 4:
+		case 8:
+		{
+			for (unsigned i = 0; i < size; i++) {
+				if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
+					color_type = FIC_PALETTE;
+					bIsGrey = FALSE;
+					break;
+				}
+				if (color_type != FIC_PALETTE && pal[i].rgbBlue != i) {
+					if ((size - i - 1) != pal[i].rgbBlue) {
+						color_type = FIC_PALETTE;
+						if (!bIsGreyscale) {
+							// exit loop if we're not setting
+							// bIsGreyscale parameter
+							break;
+						}
+					} else {
+						color_type = FIC_MINISWHITE;
+					}
+				}
+			}
+			break;
+		}
+
+		default:
+		{
+			color_type = FreeImage_GetColorType(dib);
+			bIsGrey = (color_type == FIC_MINISBLACK) ? TRUE : FALSE;
+			break;
+		}
+
+	}
+	if (bIsGreyscale) {
+		*bIsGreyscale = bIsGrey;
+	}
+
+	return color_type;
+}
+
+/**
+Returns a pointer to an RGBA palette, created from the specified bitmap.
+The RGBA palette is a copy of the specified bitmap's palette, that, additionally
+contains the bitmap's transparency information in the rgbReserved member
+of the palette's RGBQUAD elements.
+@param dib A pointer to a FreeImage bitmap to create the RGBA palette from.
+@param buffer A pointer to the buffer to store the RGBA palette.
+@return A pointer to the newly created RGBA palette or NULL, if the specified
+bitmap is no palletized standard bitmap. If non-NULL, the returned value is
+actually the pointer passed in parameter 'buffer'.
+*/
+static inline RGBQUAD *
+GetRGBAPalette(FIBITMAP *dib, RGBQUAD * const buffer) {
+	// clone the palette
+	const unsigned ncolors = FreeImage_GetColorsUsed(dib);
+	if (ncolors == 0) {
+		return NULL;
+	}
+	memcpy(buffer, FreeImage_GetPalette(dib), ncolors * sizeof(RGBQUAD));
+	// merge the transparency table
+	const unsigned ntransp = MIN(ncolors, FreeImage_GetTransparencyCount(dib));
+	const BYTE * const tt = FreeImage_GetTransparencyTable(dib);
+	for (unsigned i = 0; i < ntransp; i++) {
+		buffer[i].rgbReserved = tt[i];
+	}
+	for (unsigned i = ntransp; i < ncolors; i++) {
+		buffer[i].rgbReserved = 255;
+	}
+	return buffer;
+}
+
+// --------------------------------------------------------------------------
+
+CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) {
+	double dWidth;
+	double dFScale;
+	const double dFilterWidth = pFilter->GetWidth();
+
+	// scale factor
+	const double dScale = double(uDstSize) / double(uSrcSize);
+
+	if(dScale < 1.0) {
+		// minification
+		dWidth = dFilterWidth / dScale; 
+		dFScale = dScale; 
+	} else {
+		// magnification
+		dWidth = dFilterWidth; 
+		dFScale = 1.0; 
+	}
+
+	// allocate a new line contributions structure
+	//
+	// window size is the number of sampled pixels
+	m_WindowSize = 2 * (int)ceil(dWidth) + 1; 
+	// length of dst line (no. of rows / cols) 
+	m_LineLength = uDstSize; 
+
+	 // allocate list of contributions 
+	m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
+	for(unsigned u = 0; u < m_LineLength; u++) {
+		// allocate contributions for every pixel
+		m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
+	}
+
+	// offset for discrete to continuous coordinate conversion
+	const double dOffset = (0.5 / dScale);
+
+	for(unsigned u = 0; u < m_LineLength; u++) {
+		// scan through line of contributions
+
+		// inverse mapping (discrete dst 'u' to continous src 'dCenter')
+		const double dCenter = (double)u / dScale + dOffset;
+
+		// find the significant edge points that affect the pixel
+		const int iLeft = MAX(0, (int)(dCenter - dWidth + 0.5));
+		const int iRight = MIN((int)(dCenter + dWidth + 0.5), int(uSrcSize));
+
+		m_WeightTable[u].Left = iLeft; 
+		m_WeightTable[u].Right = iRight;
+
+		double dTotalWeight = 0;  // sum of weights (initialized to zero)
+		for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
+			// calculate weights
+			const double weight = dFScale * pFilter->Filter(dFScale * ((double)iSrc + 0.5 - dCenter));
+			// assert((iSrc-iLeft) < m_WindowSize);
+			m_WeightTable[u].Weights[iSrc-iLeft] = weight;
+			dTotalWeight += weight;
+		}
+		if((dTotalWeight > 0) && (dTotalWeight != 1)) {
+			// normalize weight of neighbouring points
+			for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
+				// normalize point
+				m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight; 
+			}
+		}
+
+		// simplify the filter, discarding null weights at the right
+		{			
+			int iTrailing = iRight - iLeft - 1;
+			while(m_WeightTable[u].Weights[iTrailing] == 0) {
+				m_WeightTable[u].Right--;
+				iTrailing--;
+				if(m_WeightTable[u].Right == m_WeightTable[u].Left) {
+					break;
+				}
+			}
+			
+		}
+
+	} // next dst pixel
+}
+
+CWeightsTable::~CWeightsTable() {
+	for(unsigned u = 0; u < m_LineLength; u++) {
+		// free contributions for every pixel
+		free(m_WeightTable[u].Weights);
+	}
+	// free list of pixels contributions
+	free(m_WeightTable);
+}
+
+// --------------------------------------------------------------------------
+
+FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags) {
+
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+	const unsigned src_bpp = FreeImage_GetBPP(src);
+
+	// determine the image's color type
+	BOOL bIsGreyscale = FALSE;
+	FREE_IMAGE_COLOR_TYPE color_type;
+	if (src_bpp <= 8) {
+		color_type = GetExtendedColorType(src, &bIsGreyscale);
+	} else {
+		color_type = FIC_RGB;
+	}
+
+	// determine the required bit depth of the destination image
+	unsigned dst_bpp;
+	unsigned dst_bpp_s1 = 0;
+	if (color_type == FIC_PALETTE && !bIsGreyscale) {
+		// non greyscale FIC_PALETTE images require a high-color destination
+		// image (24- or 32-bits depending on the image's transparent state)
+		dst_bpp = FreeImage_IsTransparent(src) ? 32 : 24;
+	} else if (src_bpp <= 8) {
+		// greyscale images require an 8-bit destination image
+		// (or a 32-bit image if the image is transparent);
+		// however, if flag FI_RESCALE_TRUE_COLOR is set, we will return
+		// a true color (24 bpp) image
+		if (FreeImage_IsTransparent(src)) {
+			dst_bpp = 32;
+			// additionally, for transparent images we always need a
+			// palette including transparency information (an RGBA palette)
+			// so, set color_type accordingly
+			color_type = FIC_PALETTE;
+		} else {
+			dst_bpp = ((flags & FI_RESCALE_TRUE_COLOR) == FI_RESCALE_TRUE_COLOR) ? 24 : 8;
+			// in any case, we use a fast 8-bit temporary image for the
+			// first filter operation (stage 1, either horizontal or
+			// vertical) and implicitly convert to 24 bpp (if requested
+			// by flag FI_RESCALE_TRUE_COLOR) during the second filter
+			// operation
+			dst_bpp_s1 = 8;
+		}
+	} else if (src_bpp == 16 && image_type == FIT_BITMAP) {
+		// 16-bit 555 and 565 RGB images require a high-color destination
+		// image (fixed to 24 bits, since 16-bit RGBs don't support
+		// transparency in FreeImage)
+		dst_bpp = 24;
+	} else {
+		// bit depth remains unchanged for all other images
+		dst_bpp = src_bpp;
+	}
+
+	// make 'stage 1' bpp a copy of the destination bpp if it
+	// was not explicitly set
+	if (dst_bpp_s1 == 0) {
+		dst_bpp_s1 = dst_bpp;
+	}
+
+	// early exit if destination size is equal to source size
+	if ((src_width == dst_width) && (src_height == dst_height)) {
+		FIBITMAP *out = src;
+		FIBITMAP *tmp = src;
+		if ((src_width != FreeImage_GetWidth(src)) || (src_height != FreeImage_GetHeight(src))) {
+			out = FreeImage_Copy(tmp, src_left, src_top, src_left + src_width, src_top + src_height);
+			tmp = out;
+		}
+		if (src_bpp != dst_bpp) {
+			switch (dst_bpp) {
+				case 8:
+					out = FreeImage_ConvertToGreyscale(tmp);
+					break;
+				case 24:
+					out = FreeImage_ConvertTo24Bits(tmp);
+					break;
+				case 32:
+					out = FreeImage_ConvertTo32Bits(tmp);
+					break;
+				default:
+					break;
+			}
+			if (tmp != src) {
+				FreeImage_Unload(tmp);
+				tmp = NULL;
+			}
+		}
+
+		return (out != src) ? out : FreeImage_Clone(src);
+	}
+
+	RGBQUAD pal_buffer[256];
+	RGBQUAD *src_pal = NULL;
+
+	// provide the source image's palette to the rescaler for
+	// FIC_PALETTE type images (this includes palletized greyscale
+	// images with an unordered palette as well as transparent images)
+	if (color_type == FIC_PALETTE) {
+		if (dst_bpp == 32) {
+			// a 32-bit destination image signals transparency, so
+			// create an RGBA palette from the source palette
+			src_pal = GetRGBAPalette(src, pal_buffer);
+		} else {
+			src_pal = FreeImage_GetPalette(src);
+		}
+	}
+
+	// allocate the dst image
+	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, dst_bpp, 0, 0, 0);
+	if (!dst) {
+		return NULL;
+	}
+	
+	if (dst_bpp == 8) {
+		RGBQUAD * const dst_pal = FreeImage_GetPalette(dst);
+		if (color_type == FIC_MINISWHITE) {
+			// build an inverted greyscale palette
+			CREATE_GREYSCALE_PALETTE_REVERSE(dst_pal, 256);
+		} 
+		/*
+		else {
+			// build a default greyscale palette
+			// Currently, FreeImage_AllocateT already creates a default
+			// greyscale palette for 8 bpp images, so we can skip this here.
+			CREATE_GREYSCALE_PALETTE(dst_pal, 256);
+		}
+		*/
+	}
+
+	// calculate x and y offsets; since FreeImage uses bottom-up bitmaps, the
+	// value of src_offset_y is measured from the bottom of the image
+	unsigned src_offset_x = src_left;
+	unsigned src_offset_y = FreeImage_GetHeight(src) - src_height - src_top;
+
+	/*
+	Decide which filtering order (xy or yx) is faster for this mapping. 
+	--- The theory ---
+	Try to minimize calculations by counting the number of convolution multiplies
+	if(dst_width*src_height <= src_width*dst_height) {
+		// xy filtering
+	} else {
+		// yx filtering
+	}
+	--- The practice ---
+	Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
+	if(dst_width*dst_height <= src_width*dst_height) {
+		// xy filtering
+	} else {
+		// yx filtering
+	}
+	*/
+
+	if (dst_width <= src_width) {
+		// xy filtering
+		// -------------
+
+		FIBITMAP *tmp = NULL;
+
+		if (src_width != dst_width) {
+			// source and destination widths are different so, we must
+			// filter horizontally
+			if (src_height != dst_height) {
+				// source and destination heights are also different so, we need
+				// a temporary image
+				tmp = FreeImage_AllocateT(image_type, dst_width, src_height, dst_bpp_s1, 0, 0, 0);
+				if (!tmp) {
+					FreeImage_Unload(dst);
+					return NULL;
+				}
+			} else {
+				// source and destination heights are equal so, we can directly
+				// scale into destination image (second filter method will not
+				// be invoked)
+				tmp = dst;
+			}
+
+			// scale source image horizontally into temporary (or destination) image
+			horizontalFilter(src, src_height, src_width, src_offset_x, src_offset_y, src_pal, tmp, dst_width);
+
+			// set x and y offsets to zero for the second filter method
+			// invocation (the temporary image only contains the portion of
+			// the image to be rescaled with no offsets)
+			src_offset_x = 0;
+			src_offset_y = 0;
+
+			// also ensure, that the second filter method gets no source
+			// palette (the temporary image is palletized only, if it is
+			// greyscale; in that case, it is an 8-bit image with a linear
+			// palette so, the source palette is not needed or will even be
+			// mismatching, if the source palette is unordered)
+			src_pal = NULL;
+		} else {
+			// source and destination widths are equal so, just copy the
+			// image pointer
+			tmp = src;
+		}
+
+		if (src_height != dst_height) {
+			// source and destination heights are different so, scale
+			// temporary (or source) image vertically into destination image
+			verticalFilter(tmp, dst_width, src_height, src_offset_x, src_offset_y, src_pal, dst, dst_height);
+		}
+
+		// free temporary image, if not pointing to either src or dst
+		if (tmp != src && tmp != dst) {
+			FreeImage_Unload(tmp);
+		}
+
+	} else {
+		// yx filtering
+		// -------------
+
+		// Remark:
+		// The yx filtering branch could be more optimized by taking into,
+		// account that (src_width != dst_width) is always true, which
+		// follows from the above condition, which selects filtering order.
+		// Since (dst_width <= src_width) == TRUE selects xy filtering,
+		// both widths must be different when performing yx filtering.
+		// However, to make the code more robust, not depending on that
+		// condition and more symmetric to the xy filtering case, these
+		// (src_width != dst_width) conditions are still in place.
+
+		FIBITMAP *tmp = NULL;
+
+		if (src_height != dst_height) {
+			// source and destination heights are different so, we must
+			// filter vertically
+			if (src_width != dst_width) {
+				// source and destination widths are also different so, we need
+				// a temporary image
+				tmp = FreeImage_AllocateT(image_type, src_width, dst_height, dst_bpp_s1, 0, 0, 0);
+				if (!tmp) {
+					FreeImage_Unload(dst);
+					return NULL;
+				}
+			} else {
+				// source and destination widths are equal so, we can directly
+				// scale into destination image (second filter method will not
+				// be invoked)
+				tmp = dst;
+			}
+
+			// scale source image vertically into temporary (or destination) image
+			verticalFilter(src, src_width, src_height, src_offset_x, src_offset_y, src_pal, tmp, dst_height);
+
+			// set x and y offsets to zero for the second filter method
+			// invocation (the temporary image only contains the portion of
+			// the image to be rescaled with no offsets)
+			src_offset_x = 0;
+			src_offset_y = 0;
+
+			// also ensure, that the second filter method gets no source
+			// palette (the temporary image is palletized only, if it is
+			// greyscale; in that case, it is an 8-bit image with a linear
+			// palette so, the source palette is not needed or will even be
+			// mismatching, if the source palette is unordered)
+			src_pal = NULL;
+
+		} else {
+			// source and destination heights are equal so, just copy the
+			// image pointer
+			tmp = src;
+		}
+
+		if (src_width != dst_width) {
+			// source and destination heights are different so, scale
+			// temporary (or source) image horizontally into destination image
+			horizontalFilter(tmp, dst_height, src_width, src_offset_x, src_offset_y, src_pal, dst, dst_width);
+		}
+
+		// free temporary image, if not pointing to either src or dst
+		if (tmp != src && tmp != dst) {
+			FreeImage_Unload(tmp);
+		}
+	}
+
+	return dst;
+} 
+
+void CResizeEngine::horizontalFilter(FIBITMAP *const src, unsigned height, unsigned src_width, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_width) {
+
+	// allocate and calculate the contributions
+	CWeightsTable weightsTable(m_pFilter, dst_width, src_width);
+
+	// step through rows
+	switch(FreeImage_GetImageType(src)) {
+		case FIT_BITMAP:
+		{
+			switch(FreeImage_GetBPP(src)) {
+				case 1:
+				{
+					switch(FreeImage_GetBPP(dst)) {
+						case 8:
+						{
+							// transparently convert the 1-bit non-transparent greyscale image to 8 bpp
+							src_offset_x >>= 3;
+							if (src_pal) {
+								// we have got a palette
+								for (unsigned y = 0; y < height; y++) {
+									// scale each row
+									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+									for (unsigned x = 0; x < dst_width; x++) {
+										// loop through row
+										const unsigned iLeft = weightsTable.getLeftBoundary(x);		// retrieve left boundary
+										const unsigned iRight = weightsTable.getRightBoundary(x);	// retrieve right boundary
+										double value = 0;
+
+										for (unsigned i = iLeft; i < iRight; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+											value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
+										}
+
+										// clamp and place result in destination pixel
+										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+									}
+								}
+							} else {
+								// we do not have a palette
+								for (unsigned y = 0; y < height; y++) {
+									// scale each row
+									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+									for (unsigned x = 0; x < dst_width; x++) {
+										// loop through row
+										const unsigned iLeft = weightsTable.getLeftBoundary(x);		// retrieve left boundary
+										const unsigned iRight = weightsTable.getRightBoundary(x);	// retrieve right boundary
+										double value = 0;
+
+										for (unsigned i = iLeft; i < iRight; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+											value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
+										}
+										value *= 0xFF;
+
+										// clamp and place result in destination pixel
+										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+									}
+								}
+							}
+						}
+						break;
+
+						case 24:
+						{
+							// transparently convert the non-transparent 1-bit image to 24 bpp
+							src_offset_x >>= 3;
+							if (src_pal) {
+								// we have got a palette
+								for (unsigned y = 0; y < height; y++) {
+									// scale each row
+									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+									for (unsigned x = 0; x < dst_width; x++) {
+										// loop through row
+										const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
+										const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
+										double r = 0, g = 0, b = 0;
+
+										for (unsigned i = iLeft; i < iRight; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											const double weight = weightsTable.getWeight(x, i - iLeft);
+											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+											const BYTE * const entry = (BYTE *)&src_pal[pixel];
+											r += (weight * (double)entry[FI_RGBA_RED]);
+											g += (weight * (double)entry[FI_RGBA_GREEN]);
+											b += (weight * (double)entry[FI_RGBA_BLUE]);
+										}
+
+										// clamp and place result in destination pixel
+										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+										dst_bits += 3;
+									}
+								}
+							} else {
+								// we do not have a palette
+								for (unsigned y = 0; y < height; y++) {
+									// scale each row
+									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+									for (unsigned x = 0; x < dst_width; x++) {
+										// loop through row
+										const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
+										const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
+										double value = 0;
+
+										for (unsigned i = iLeft; i < iRight; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+											value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
+										}
+										value *= 0xFF;
+
+										// clamp and place result in destination pixel
+										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_RED]	= bval;
+										dst_bits[FI_RGBA_GREEN]	= bval;
+										dst_bits[FI_RGBA_BLUE]	= bval;
+										dst_bits += 3;
+									}
+								}
+							}
+						}
+						break;
+
+						case 32:
+						{
+							// transparently convert the transparent 1-bit image to 32 bpp; 
+							// we always have got a palette here
+							src_offset_x >>= 3;
+
+							for (unsigned y = 0; y < height; y++) {
+								// scale each row
+								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+								for (unsigned x = 0; x < dst_width; x++) {
+									// loop through row
+									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
+									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
+									double r = 0, g = 0, b = 0, a = 0;
+
+									for (unsigned i = iLeft; i < iRight; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const double weight = weightsTable.getWeight(x, i - iLeft);
+										const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+										const BYTE * const entry = (BYTE *)&src_pal[pixel];
+										r += (weight * (double)entry[FI_RGBA_RED]);
+										g += (weight * (double)entry[FI_RGBA_GREEN]);
+										b += (weight * (double)entry[FI_RGBA_BLUE]);
+										a += (weight * (double)entry[FI_RGBA_ALPHA]);
+									}
+
+									// clamp and place result in destination pixel
+									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+									dst_bits += 4;
+								}
+							}
+						}
+						break;
+					}
+				}
+				break;
+
+				case 4:
+				{
+					switch(FreeImage_GetBPP(dst)) {
+						case 8:
+						{
+							// transparently convert the non-transparent 4-bit greyscale image to 8 bpp; 
+							// we always have got a palette for 4-bit images
+							src_offset_x >>= 1;
+
+							for (unsigned y = 0; y < height; y++) {
+								// scale each row
+								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+								BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+								for (unsigned x = 0; x < dst_width; x++) {
+									// loop through row
+									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
+									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
+									double value = 0;
+
+									for (unsigned i = iLeft; i < iRight; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
+										value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
+									}
+
+									// clamp and place result in destination pixel
+									dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+								}
+							}
+						}
+						break;
+
+						case 24:
+						{
+							// transparently convert the non-transparent 4-bit image to 24 bpp; 
+							// we always have got a palette for 4-bit images
+							src_offset_x >>= 1;
+
+							for (unsigned y = 0; y < height; y++) {
+								// scale each row
+								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+								for (unsigned x = 0; x < dst_width; x++) {
+									// loop through row
+									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
+									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
+									double r = 0, g = 0, b = 0;
+
+									for (unsigned i = iLeft; i < iRight; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const double weight = weightsTable.getWeight(x, i - iLeft);
+										const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
+										const BYTE * const entry = (BYTE *)&src_pal[pixel];
+										r += (weight * (double)entry[FI_RGBA_RED]);
+										g += (weight * (double)entry[FI_RGBA_GREEN]);
+										b += (weight * (double)entry[FI_RGBA_BLUE]);
+									}
+
+									// clamp and place result in destination pixel
+									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+									dst_bits += 3;
+								}
+							}
+						}
+						break;
+
+						case 32:
+						{
+							// transparently convert the transparent 4-bit image to 32 bpp; 
+							// we always have got a palette for 4-bit images
+							src_offset_x >>= 1;
+
+							for (unsigned y = 0; y < height; y++) {
+								// scale each row
+								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+								for (unsigned x = 0; x < dst_width; x++) {
+									// loop through row
+									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
+									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
+									double r = 0, g = 0, b = 0, a = 0;
+
+									for (unsigned i = iLeft; i < iRight; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const double weight = weightsTable.getWeight(x, i - iLeft);
+										const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
+										const BYTE * const entry = (BYTE *)&src_pal[pixel];
+										r += (weight * (double)entry[FI_RGBA_RED]);
+										g += (weight * (double)entry[FI_RGBA_GREEN]);
+										b += (weight * (double)entry[FI_RGBA_BLUE]);
+										a += (weight * (double)entry[FI_RGBA_ALPHA]);
+									}
+
+									// clamp and place result in destination pixel
+									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+									dst_bits += 4;
+								}
+							}
+						}
+						break;
+					}
+				}
+				break;
+
+				case 8:
+				{
+					switch(FreeImage_GetBPP(dst)) {
+						case 8:
+						{
+							// scale the 8-bit non-transparent greyscale image
+							// into an 8 bpp destination image
+							if (src_pal) {
+								// we have got a palette
+								for (unsigned y = 0; y < height; y++) {
+									// scale each row
+									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+									for (unsigned x = 0; x < dst_width; x++) {
+										// loop through row
+										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+										const BYTE * const pixel = src_bits + iLeft;
+										double value = 0;
+
+										// for(i = iLeft to iRight)
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											value += (weightsTable.getWeight(x, i) * (double)*(BYTE *)&src_pal[pixel[i]]);
+										}
+
+										// clamp and place result in destination pixel
+										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+									}
+								}
+							} else {
+								// we do not have a palette
+								for (unsigned y = 0; y < height; y++) {
+									// scale each row
+									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+									for (unsigned x = 0; x < dst_width; x++) {
+										// loop through row
+										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+										const BYTE * const pixel = src_bits + iLeft;
+										double value = 0;
+
+										// for(i = iLeft to iRight)
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											value += (weightsTable.getWeight(x, i) * (double)pixel[i]);
+										}
+
+										// clamp and place result in destination pixel
+										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+									}
+								}
+							}
+						}
+						break;
+
+						case 24:
+						{
+							// transparently convert the non-transparent 8-bit image to 24 bpp
+							if (src_pal) {
+								// we have got a palette
+								for (unsigned y = 0; y < height; y++) {
+									// scale each row
+									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+									for (unsigned x = 0; x < dst_width; x++) {
+										// loop through row
+										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+										const BYTE * const pixel = src_bits + iLeft;
+										double r = 0, g = 0, b = 0;
+
+										// for(i = iLeft to iRight)
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											const double weight = weightsTable.getWeight(x, i);
+											const BYTE *const entry = (BYTE *)&src_pal[pixel[i]];
+											r += (weight * (double)entry[FI_RGBA_RED]);
+											g += (weight * (double)entry[FI_RGBA_GREEN]);
+											b += (weight * (double)entry[FI_RGBA_BLUE]);
+										}
+
+										// clamp and place result in destination pixel
+										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+										dst_bits += 3;
+									}
+								}
+							} else {
+								// we do not have a palette
+								for (unsigned y = 0; y < height; y++) {
+									// scale each row
+									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+									for (unsigned x = 0; x < dst_width; x++) {
+										// loop through row
+										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+										const BYTE * const pixel = src_bits + iLeft;
+										double value = 0;
+
+										// for(i = iLeft to iRight)
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											const double weight = weightsTable.getWeight(x, i);
+											value += (weight * (double)pixel[i]);
+										}
+
+										// clamp and place result in destination pixel
+										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_RED]	= bval;
+										dst_bits[FI_RGBA_GREEN]	= bval;
+										dst_bits[FI_RGBA_BLUE]	= bval;
+										dst_bits += 3;
+									}
+								}
+							}
+						}
+						break;
+
+						case 32:
+						{
+							// transparently convert the transparent 8-bit image to 32 bpp; 
+							// we always have got a palette here
+							for (unsigned y = 0; y < height; y++) {
+								// scale each row
+								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+								for (unsigned x = 0; x < dst_width; x++) {
+									// loop through row
+									const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+									const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+									const BYTE * const pixel = src_bits + iLeft;
+									double r = 0, g = 0, b = 0, a = 0;
+
+									// for(i = iLeft to iRight)
+									for (unsigned i = 0; i < iLimit; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const double weight = weightsTable.getWeight(x, i);
+										const BYTE * const entry = (BYTE *)&src_pal[pixel[i]];
+										r += (weight * (double)entry[FI_RGBA_RED]);
+										g += (weight * (double)entry[FI_RGBA_GREEN]);
+										b += (weight * (double)entry[FI_RGBA_BLUE]);
+										a += (weight * (double)entry[FI_RGBA_ALPHA]);
+									}
+
+									// clamp and place result in destination pixel
+									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+									dst_bits += 4;
+								}
+							}
+						}
+						break;
+					}
+				}
+				break;
+
+				case 16:
+				{
+					// transparently convert the 16-bit non-transparent image to 24 bpp
+					if (IS_FORMAT_RGB565(src)) {
+						// image has 565 format
+						for (unsigned y = 0; y < height; y++) {
+							// scale each row
+							const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+							BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+							for (unsigned x = 0; x < dst_width; x++) {
+								// loop through row
+								const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+								const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+								const WORD *pixel = src_bits + iLeft;
+								double r = 0, g = 0, b = 0;
+
+								// for(i = iLeft to iRight)
+								for (unsigned i = 0; i < iLimit; i++) {
+									// scan between boundaries
+									// accumulate weighted effect of each neighboring pixel
+									const double weight = weightsTable.getWeight(x, i);
+									r += (weight * (double)((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
+									g += (weight * (double)((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
+									b += (weight * (double)((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
+									pixel++;
+								}
+
+								// clamp and place result in destination pixel
+								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
+								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits += 3;
+							}
+						}
+					} else {
+						// image has 555 format
+						for (unsigned y = 0; y < height; y++) {
+							// scale each row
+							const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+							BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+							for (unsigned x = 0; x < dst_width; x++) {
+								// loop through row
+								const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+								const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+								const WORD *pixel = src_bits + iLeft;
+								double r = 0, g = 0, b = 0;
+
+								// for(i = iLeft to iRight)
+								for (unsigned i = 0; i < iLimit; i++) {
+									// scan between boundaries
+									// accumulate weighted effect of each neighboring pixel
+									const double weight = weightsTable.getWeight(x, i);
+									r += (weight * (double)((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
+									g += (weight * (double)((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
+									b += (weight * (double)((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
+									pixel++;
+								}
+
+								// clamp and place result in destination pixel
+								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits += 3;
+							}
+						}
+					}
+				}
+				break;
+
+				case 24:
+				{
+					// scale the 24-bit non-transparent image into a 24 bpp destination image
+					for (unsigned y = 0; y < height; y++) {
+						// scale each row
+						const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 3;
+						BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+						for (unsigned x = 0; x < dst_width; x++) {
+							// loop through row
+							const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+							const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+							const BYTE * pixel = src_bits + iLeft * 3;
+							double r = 0, g = 0, b = 0;
+
+							// for(i = iLeft to iRight)
+							for (unsigned i = 0; i < iLimit; i++) {
+								// scan between boundaries
+								// accumulate weighted effect of each neighboring pixel
+								const double weight = weightsTable.getWeight(x, i);
+								r += (weight * (double)pixel[FI_RGBA_RED]);
+								g += (weight * (double)pixel[FI_RGBA_GREEN]);
+								b += (weight * (double)pixel[FI_RGBA_BLUE]);
+								pixel += 3;
+							}
+
+							// clamp and place result in destination pixel
+							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+							dst_bits += 3;
+						}
+					}
+				}
+				break;
+
+				case 32:
+				{
+					// scale the 32-bit transparent image into a 32 bpp destination image
+					for (unsigned y = 0; y < height; y++) {
+						// scale each row
+						const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 4;
+						BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+						for (unsigned x = 0; x < dst_width; x++) {
+							// loop through row
+							const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+							const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+							const BYTE *pixel = src_bits + iLeft * 4;
+							double r = 0, g = 0, b = 0, a = 0;
+
+							// for(i = iLeft to iRight)
+							for (unsigned i = 0; i < iLimit; i++) {
+								// scan between boundaries
+								// accumulate weighted effect of each neighboring pixel
+								const double weight = weightsTable.getWeight(x, i);
+								r += (weight * (double)pixel[FI_RGBA_RED]);
+								g += (weight * (double)pixel[FI_RGBA_GREEN]);
+								b += (weight * (double)pixel[FI_RGBA_BLUE]);
+								a += (weight * (double)pixel[FI_RGBA_ALPHA]);
+								pixel += 4;
+							}
+
+							// clamp and place result in destination pixel
+							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+							dst_bits += 4;
+						}
+					}
+				}
+				break;
+			}
+		}
+		break;
+
+		case FIT_UINT16:
+		{
+			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+			const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
+
+			for (unsigned y = 0; y < height; y++) {
+				// scale each row
+				const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+
+				for (unsigned x = 0; x < dst_width; x++) {
+					// loop through row
+					const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+					const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+					const WORD *pixel = src_bits + iLeft * wordspp;
+					double value = 0;
+
+					// for(i = iLeft to iRight)
+					for (unsigned i = 0; i < iLimit; i++) {
+						// scan between boundaries
+						// accumulate weighted effect of each neighboring pixel
+						const double weight = weightsTable.getWeight(x, i);						
+						value += (weight * (double)pixel[0]);
+						pixel++;
+					}
+
+					// clamp and place result in destination pixel
+					dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
+					dst_bits += wordspp;
+				}
+			}
+		}
+		break;
+
+		case FIT_RGB16:
+		{
+			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+			const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
+
+			for (unsigned y = 0; y < height; y++) {
+				// scale each row
+				const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+
+				for (unsigned x = 0; x < dst_width; x++) {
+					// loop through row
+					const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+					const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+					const WORD *pixel = src_bits + iLeft * wordspp;
+					double r = 0, g = 0, b = 0;
+
+					// for(i = iLeft to iRight)
+					for (unsigned i = 0; i < iLimit; i++) {
+						// scan between boundaries
+						// accumulate weighted effect of each neighboring pixel
+						const double weight = weightsTable.getWeight(x, i);						
+						r += (weight * (double)pixel[0]);
+						g += (weight * (double)pixel[1]);
+						b += (weight * (double)pixel[2]);
+						pixel += wordspp;
+					}
+
+					// clamp and place result in destination pixel
+					dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+					dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+					dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+					dst_bits += wordspp;
+				}
+			}
+		}
+		break;
+
+		case FIT_RGBA16:
+		{
+			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+			const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
+
+			for (unsigned y = 0; y < height; y++) {
+				// scale each row
+				const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+
+				for (unsigned x = 0; x < dst_width; x++) {
+					// loop through row
+					const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
+					const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
+					const WORD *pixel = src_bits + iLeft * wordspp;
+					double r = 0, g = 0, b = 0, a = 0;
+
+					// for(i = iLeft to iRight)
+					for (unsigned i = 0; i < iLimit; i++) {
+						// scan between boundaries
+						// accumulate weighted effect of each neighboring pixel
+						const double weight = weightsTable.getWeight(x, i);						
+						r += (weight * (double)pixel[0]);
+						g += (weight * (double)pixel[1]);
+						b += (weight * (double)pixel[2]);
+						a += (weight * (double)pixel[3]);
+						pixel += wordspp;
+					}
+
+					// clamp and place result in destination pixel
+					dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+					dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+					dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+					dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
+					dst_bits += wordspp;
+				}
+			}
+		}
+		break;
+
+		case FIT_FLOAT:
+		case FIT_RGBF:
+		case FIT_RGBAF:
+		{
+			// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
+			const unsigned floatspp = (FreeImage_GetLine(src) / src_width) / sizeof(float);
+
+			for(unsigned y = 0; y < height; y++) {
+				// scale each row
+				const float *src_bits = (float*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(float);
+				float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
+
+				for(unsigned x = 0; x < dst_width; x++) {
+					// loop through row
+					const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
+					const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
+					double value[4] = {0, 0, 0, 0};                            // 4 = 128 bpp max
+
+					for(unsigned i = iLeft; i < iRight; i++) {
+						// scan between boundaries
+						// accumulate weighted effect of each neighboring pixel
+						const double weight = weightsTable.getWeight(x, i-iLeft);
+
+						unsigned index = i * floatspp;	// pixel index
+						for (unsigned j = 0; j < floatspp; j++) {
+							value[j] += (weight * (double)src_bits[index++]);
+						}
+					}
+
+					// place result in destination pixel
+					for (unsigned j = 0; j < floatspp; j++) {
+						dst_bits[j] = (float)value[j];
+					}
+
+					dst_bits += floatspp;
+				}
+			}
+		}
+		break;
+	}
+}
+
+/// Performs vertical image filtering
+void CResizeEngine::verticalFilter(FIBITMAP *const src, unsigned width, unsigned src_height, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_height) {
+
+	// allocate and calculate the contributions
+	CWeightsTable weightsTable(m_pFilter, dst_height, src_height);
+
+	// step through columns
+	switch(FreeImage_GetImageType(src)) {
+		case FIT_BITMAP:
+		{
+			const unsigned dst_pitch = FreeImage_GetPitch(dst);
+			BYTE * const dst_base = FreeImage_GetBits(dst);
+
+			switch(FreeImage_GetBPP(src)) {
+				case 1:
+				{
+					const unsigned src_pitch = FreeImage_GetPitch(src);
+					const BYTE * const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 3);
+
+					switch(FreeImage_GetBPP(dst)) {
+						case 8:
+						{
+							// transparently convert the 1-bit non-transparent greyscale image to 8 bpp
+							if (src_pal) {
+								// we have got a palette
+								for (unsigned x = 0; x < width; x++) {
+									// work on column x in dst
+									BYTE *dst_bits = dst_base + x;
+									const unsigned index = x >> 3;
+									const unsigned mask = 0x80 >> (x & 0x07);
+
+									// scale each column
+									for (unsigned y = 0; y < dst_height; y++) {
+										// loop through column
+										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+										double value = 0;
+
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											const unsigned pixel = (*src_bits & mask) != 0;
+											value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
+											src_bits += src_pitch;
+										}
+										value *= 0xFF;
+
+										// clamp and place result in destination pixel
+										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+										dst_bits += dst_pitch;
+									}
+								}
+							} else {
+								// we do not have a palette
+								for (unsigned x = 0; x < width; x++) {
+									// work on column x in dst
+									BYTE *dst_bits = dst_base + x;
+									const unsigned index = x >> 3;
+									const unsigned mask = 0x80 >> (x & 0x07);
+
+									// scale each column
+									for (unsigned y = 0; y < dst_height; y++) {
+										// loop through column
+										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+										double value = 0;
+
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
+											src_bits += src_pitch;
+										}
+										value *= 0xFF;
+
+										// clamp and place result in destination pixel
+										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+										dst_bits += dst_pitch;
+									}
+								}
+							}
+						}
+						break;
+
+						case 24:
+						{
+							// transparently convert the non-transparent 1-bit image to 24 bpp
+							if (src_pal) {
+								// we have got a palette
+								for (unsigned x = 0; x < width; x++) {
+									// work on column x in dst
+									BYTE *dst_bits = dst_base + x * 3;
+									const unsigned index = x >> 3;
+									const unsigned mask = 0x80 >> (x & 0x07);
+
+									// scale each column
+									for (unsigned y = 0; y < dst_height; y++) {
+										// loop through column
+										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+										double r = 0, g = 0, b = 0;
+
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											const double weight = weightsTable.getWeight(y, i);
+											const unsigned pixel = (*src_bits & mask) != 0;
+											const BYTE * const entry = (BYTE *)&src_pal[pixel];
+											r += (weight * (double)entry[FI_RGBA_RED]);
+											g += (weight * (double)entry[FI_RGBA_GREEN]);
+											b += (weight * (double)entry[FI_RGBA_BLUE]);
+											src_bits += src_pitch;
+										}
+
+										// clamp and place result in destination pixel
+										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+										dst_bits += dst_pitch;
+									}
+								}
+							} else {
+								// we do not have a palette
+								for (unsigned x = 0; x < width; x++) {
+									// work on column x in dst
+									BYTE *dst_bits = dst_base + x * 3;
+									const unsigned index = x >> 3;
+									const unsigned mask = 0x80 >> (x & 0x07);
+
+									// scale each column
+									for (unsigned y = 0; y < dst_height; y++) {
+										// loop through column
+										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+										double value = 0;
+
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
+											src_bits += src_pitch;
+										}
+										value *= 0xFF;
+
+										// clamp and place result in destination pixel
+										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_RED]	= bval;
+										dst_bits[FI_RGBA_GREEN]	= bval;
+										dst_bits[FI_RGBA_BLUE]	= bval;
+										dst_bits += dst_pitch;
+									}
+								}
+							}
+						}
+						break;
+
+						case 32:
+						{
+							// transparently convert the transparent 1-bit image to 32 bpp; 
+							// we always have got a palette here
+							for (unsigned x = 0; x < width; x++) {
+								// work on column x in dst
+								BYTE *dst_bits = dst_base + x * 4;
+								const unsigned index = x >> 3;
+								const unsigned mask = 0x80 >> (x & 0x07);
+
+								// scale each column
+								for (unsigned y = 0; y < dst_height; y++) {
+									// loop through column
+									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+									double r = 0, g = 0, b = 0, a = 0;
+
+									for (unsigned i = 0; i < iLimit; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const double weight = weightsTable.getWeight(y, i);
+										const unsigned pixel = (*src_bits & mask) != 0;
+										const BYTE * const entry = (BYTE *)&src_pal[pixel];
+										r += (weight * (double)entry[FI_RGBA_RED]);
+										g += (weight * (double)entry[FI_RGBA_GREEN]);
+										b += (weight * (double)entry[FI_RGBA_BLUE]);
+										a += (weight * (double)entry[FI_RGBA_ALPHA]);
+										src_bits += src_pitch;
+									}
+
+									// clamp and place result in destination pixel
+									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+									dst_bits += dst_pitch;
+								}
+							}
+						}
+						break;
+					}
+				}
+				break;
+
+				case 4:
+				{
+					const unsigned src_pitch = FreeImage_GetPitch(src);
+					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 1);
+
+					switch(FreeImage_GetBPP(dst)) {
+						case 8:
+						{
+							// transparently convert the non-transparent 4-bit greyscale image to 8 bpp; 
+							// we always have got a palette for 4-bit images
+							for (unsigned x = 0; x < width; x++) {
+								// work on column x in dst
+								BYTE *dst_bits = dst_base + x;
+								const unsigned index = x >> 1;
+
+								// scale each column
+								for (unsigned y = 0; y < dst_height; y++) {
+									// loop through column
+									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+									double value = 0;
+
+									for (unsigned i = 0; i < iLimit; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
+										value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
+										src_bits += src_pitch;
+									}
+
+									// clamp and place result in destination pixel
+									*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+									dst_bits += dst_pitch;
+								}
+							}
+						}
+						break;
+
+						case 24:
+						{
+							// transparently convert the non-transparent 4-bit image to 24 bpp; 
+							// we always have got a palette for 4-bit images
+							for (unsigned x = 0; x < width; x++) {
+								// work on column x in dst
+								BYTE *dst_bits = dst_base + x * 3;
+								const unsigned index = x >> 1;
+
+								// scale each column
+								for (unsigned y = 0; y < dst_height; y++) {
+									// loop through column
+									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+									double r = 0, g = 0, b = 0;
+
+									for (unsigned i = 0; i < iLimit; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const double weight = weightsTable.getWeight(y, i);
+										const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
+										const BYTE *const entry = (BYTE *)&src_pal[pixel];
+										r += (weight * (double)entry[FI_RGBA_RED]);
+										g += (weight * (double)entry[FI_RGBA_GREEN]);
+										b += (weight * (double)entry[FI_RGBA_BLUE]);
+										src_bits += src_pitch;
+									}
+
+									// clamp and place result in destination pixel
+									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+									dst_bits += dst_pitch;
+								}
+							}
+						}
+						break;
+
+						case 32:
+						{
+							// transparently convert the transparent 4-bit image to 32 bpp; 
+							// we always have got a palette for 4-bit images
+							for (unsigned x = 0; x < width; x++) {
+								// work on column x in dst
+								BYTE *dst_bits = dst_base + x * 4;
+								const unsigned index = x >> 1;
+
+								// scale each column
+								for (unsigned y = 0; y < dst_height; y++) {
+									// loop through column
+									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+									double r = 0, g = 0, b = 0, a = 0;
+
+									for (unsigned i = 0; i < iLimit; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const double weight = weightsTable.getWeight(y, i);
+										const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
+										const BYTE *const entry = (BYTE *)&src_pal[pixel];
+										r += (weight * (double)entry[FI_RGBA_RED]);
+										g += (weight * (double)entry[FI_RGBA_GREEN]);
+										b += (weight * (double)entry[FI_RGBA_BLUE]);
+										a += (weight * (double)entry[FI_RGBA_ALPHA]);
+										src_bits += src_pitch;
+									}
+
+									// clamp and place result in destination pixel
+									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+									dst_bits += dst_pitch;
+								}
+							}
+						}
+						break;
+					}
+				}
+				break;
+
+				case 8:
+				{
+					const unsigned src_pitch = FreeImage_GetPitch(src);
+					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
+
+					switch(FreeImage_GetBPP(dst)) {
+						case 8:
+						{
+							// scale the 8-bit non-transparent greyscale image into an 8 bpp destination image
+							if (src_pal) {
+								// we have got a palette
+								for (unsigned x = 0; x < width; x++) {
+									// work on column x in dst
+									BYTE *dst_bits = dst_base + x;
+
+									// scale each column
+									for (unsigned y = 0; y < dst_height; y++) {
+										// loop through column
+										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+										double value = 0;
+
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[*src_bits]);
+											src_bits += src_pitch;
+										}
+
+										// clamp and place result in destination pixel
+										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+										dst_bits += dst_pitch;
+									}
+								}
+							} else {
+								// we do not have a palette
+								for (unsigned x = 0; x < width; x++) {
+									// work on column x in dst
+									BYTE *dst_bits = dst_base + x;
+
+									// scale each column
+									for (unsigned y = 0; y < dst_height; y++) {
+										// loop through column
+										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+										double value = 0;
+
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											value += (weightsTable.getWeight(y, i) * (double)*src_bits);
+											src_bits += src_pitch;
+										}
+
+										// clamp and place result in destination pixel
+										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+										dst_bits += dst_pitch;
+									}
+								}
+							}
+						}
+						break;
+
+						case 24:
+						{
+							// transparently convert the non-transparent 8-bit image to 24 bpp
+							if (src_pal) {
+								// we have got a palette
+								for (unsigned x = 0; x < width; x++) {
+									// work on column x in dst
+									BYTE *dst_bits = dst_base + x * 3;
+
+									// scale each column
+									for (unsigned y = 0; y < dst_height; y++) {
+										// loop through column
+										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+										double r = 0, g = 0, b = 0;
+
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											const double weight = weightsTable.getWeight(y, i);
+											const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
+											r += (weight * (double)entry[FI_RGBA_RED]);
+											g += (weight * (double)entry[FI_RGBA_GREEN]);
+											b += (weight * (double)entry[FI_RGBA_BLUE]);
+											src_bits += src_pitch;
+										}
+
+										// clamp and place result in destination pixel
+										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+										dst_bits += dst_pitch;
+									}
+								}
+							} else {
+								// we do not have a palette
+								for (unsigned x = 0; x < width; x++) {
+									// work on column x in dst
+									BYTE *dst_bits = dst_base + x * 3;
+
+									// scale each column
+									for (unsigned y = 0; y < dst_height; y++) {
+										// loop through column
+										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+										double value = 0;
+
+										for (unsigned i = 0; i < iLimit; i++) {
+											// scan between boundaries
+											// accumulate weighted effect of each neighboring pixel
+											value += (weightsTable.getWeight(y, i) * (double)*src_bits);
+											src_bits += src_pitch;
+										}
+
+										// clamp and place result in destination pixel
+										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+										dst_bits[FI_RGBA_RED]	= bval;
+										dst_bits[FI_RGBA_GREEN]	= bval;
+										dst_bits[FI_RGBA_BLUE]	= bval;
+										dst_bits += dst_pitch;
+									}
+								}
+							}
+						}
+						break;
+
+						case 32:
+						{
+							// transparently convert the transparent 8-bit image to 32 bpp; 
+							// we always have got a palette here
+							for (unsigned x = 0; x < width; x++) {
+								// work on column x in dst
+								BYTE *dst_bits = dst_base + x * 4;
+
+								// scale each column
+								for (unsigned y = 0; y < dst_height; y++) {
+									// loop through column
+									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+									const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+									double r = 0, g = 0, b = 0, a = 0;
+
+									for (unsigned i = 0; i < iLimit; i++) {
+										// scan between boundaries
+										// accumulate weighted effect of each neighboring pixel
+										const double weight = weightsTable.getWeight(y, i);
+										const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
+										r += (weight * (double)entry[FI_RGBA_RED]);
+										g += (weight * (double)entry[FI_RGBA_GREEN]);
+										b += (weight * (double)entry[FI_RGBA_BLUE]);
+										a += (weight * (double)entry[FI_RGBA_ALPHA]);
+										src_bits += src_pitch;
+									}
+
+									// clamp and place result in destination pixel
+									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+									dst_bits += dst_pitch;
+								}
+							}
+						}
+						break;
+					}
+				}
+				break;
+
+				case 16:
+				{
+					// transparently convert the 16-bit non-transparent image to 24 bpp
+					const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+					const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
+
+					if (IS_FORMAT_RGB565(src)) {
+						// image has 565 format
+						for (unsigned x = 0; x < width; x++) {
+							// work on column x in dst
+							BYTE *dst_bits = dst_base + x * 3;
+
+							// scale each column
+							for (unsigned y = 0; y < dst_height; y++) {
+								// loop through column
+								const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+								const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+								const WORD *src_bits = src_base + iLeft * src_pitch + x;
+								double r = 0, g = 0, b = 0;
+
+								for (unsigned i = 0; i < iLimit; i++) {
+									// scan between boundaries
+									// accumulate weighted effect of each neighboring pixel
+									const double weight = weightsTable.getWeight(y, i);
+									r += (weight * (double)((*src_bits & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
+									g += (weight * (double)((*src_bits & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
+									b += (weight * (double)((*src_bits & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
+									src_bits += src_pitch;
+								}
+
+								// clamp and place result in destination pixel
+								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
+								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits += dst_pitch;
+							}
+						}
+					} else {
+						// image has 555 format
+						for (unsigned x = 0; x < width; x++) {
+							// work on column x in dst
+							BYTE *dst_bits = dst_base + x * 3;
+
+							// scale each column
+							for (unsigned y = 0; y < dst_height; y++) {
+								// loop through column
+								const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+								const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+								const WORD *src_bits = src_base + iLeft * src_pitch + x;
+								double r = 0, g = 0, b = 0;
+
+								for (unsigned i = 0; i < iLimit; i++) {
+									// scan between boundaries
+									// accumulate weighted effect of each neighboring pixel
+									const double weight = weightsTable.getWeight(y, i);
+									r += (weight * (double)((*src_bits & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
+									g += (weight * (double)((*src_bits & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
+									b += (weight * (double)((*src_bits & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
+									src_bits += src_pitch;
+								}
+
+								// clamp and place result in destination pixel
+								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+								dst_bits += dst_pitch;
+							}
+						}
+					}
+				}
+				break;
+
+				case 24:
+				{
+					// scale the 24-bit transparent image into a 24 bpp destination image
+					const unsigned src_pitch = FreeImage_GetPitch(src);
+					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 3;
+
+					for (unsigned x = 0; x < width; x++) {
+						// work on column x in dst
+						const unsigned index = x * 3;
+						BYTE *dst_bits = dst_base + index;
+
+						// scale each column
+						for (unsigned y = 0; y < dst_height; y++) {
+							// loop through column
+							const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+							const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+							const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+							double r = 0, g = 0, b = 0;
+
+							for (unsigned i = 0; i < iLimit; i++) {
+								// scan between boundaries
+								// accumulate weighted effect of each neighboring pixel
+								const double weight = weightsTable.getWeight(y, i);
+								r += (weight * (double)src_bits[FI_RGBA_RED]);
+								g += (weight * (double)src_bits[FI_RGBA_GREEN]);
+								b += (weight * (double)src_bits[FI_RGBA_BLUE]);
+								src_bits += src_pitch;
+							}
+
+							// clamp and place result in destination pixel
+							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
+							dst_bits += dst_pitch;
+						}
+					}
+				}
+				break;
+
+				case 32:
+				{
+					// scale the 32-bit transparent image into a 32 bpp destination image
+					const unsigned src_pitch = FreeImage_GetPitch(src);
+					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 4;
+
+					for (unsigned x = 0; x < width; x++) {
+						// work on column x in dst
+						const unsigned index = x * 4;
+						BYTE *dst_bits = dst_base + index;
+
+						// scale each column
+						for (unsigned y = 0; y < dst_height; y++) {
+							// loop through column
+							const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+							const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+							const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+							double r = 0, g = 0, b = 0, a = 0;
+
+							for (unsigned i = 0; i < iLimit; i++) {
+								// scan between boundaries
+								// accumulate weighted effect of each neighboring pixel
+								const double weight = weightsTable.getWeight(y, i);
+								r += (weight * (double)src_bits[FI_RGBA_RED]);
+								g += (weight * (double)src_bits[FI_RGBA_GREEN]);
+								b += (weight * (double)src_bits[FI_RGBA_BLUE]);
+								a += (weight * (double)src_bits[FI_RGBA_ALPHA]);
+								src_bits += src_pitch;
+							}
+
+							// clamp and place result in destination pixel
+							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
+							dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int) (a + 0.5), 0, 0xFF);
+							dst_bits += dst_pitch;
+						}
+					}
+				}
+				break;
+			}
+		}
+		break;
+
+		case FIT_UINT16:
+		{
+			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+			const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+			const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
+			WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
+
+			const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+			const WORD *const src_base = (WORD *)FreeImage_GetBits(src)	+ src_offset_y * src_pitch + src_offset_x * wordspp;
+
+			for (unsigned x = 0; x < width; x++) {
+				// work on column x in dst
+				const unsigned index = x * wordspp;	// pixel index
+				WORD *dst_bits = dst_base + index;
+
+				// scale each column
+				for (unsigned y = 0; y < dst_height; y++) {
+					// loop through column
+					const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+					const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+					const WORD *src_bits = src_base + iLeft * src_pitch + index;
+					double value = 0;
+
+					for (unsigned i = 0; i < iLimit; i++) {
+						// scan between boundaries
+						// accumulate weighted effect of each neighboring pixel
+						const double weight = weightsTable.getWeight(y, i);
+						value += (weight * (double)src_bits[0]);
+						src_bits += src_pitch;
+					}
+
+					// clamp and place result in destination pixel
+					dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
+
+					dst_bits += dst_pitch;
+				}
+			}
+		}
+		break;
+
+		case FIT_RGB16:
+		{
+			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+			const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+			const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
+			WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
+
+			const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+			const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
+
+			for (unsigned x = 0; x < width; x++) {
+				// work on column x in dst
+				const unsigned index = x * wordspp;	// pixel index
+				WORD *dst_bits = dst_base + index;
+
+				// scale each column
+				for (unsigned y = 0; y < dst_height; y++) {
+					// loop through column
+					const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+					const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+					const WORD *src_bits = src_base + iLeft * src_pitch + index;
+					double r = 0, g = 0, b = 0;
+
+					for (unsigned i = 0; i < iLimit; i++) {
+						// scan between boundaries
+						// accumulate weighted effect of each neighboring pixel
+						const double weight = weightsTable.getWeight(y, i);					
+						r += (weight * (double)src_bits[0]);
+						g += (weight * (double)src_bits[1]);
+						b += (weight * (double)src_bits[2]);
+
+						src_bits += src_pitch;
+					}
+
+					// clamp and place result in destination pixel
+					dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+					dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+					dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+
+					dst_bits += dst_pitch;
+				}
+			}
+		}
+		break;
+
+		case FIT_RGBA16:
+		{
+			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+			const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+			const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
+			WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
+
+			const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+			const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
+
+			for (unsigned x = 0; x < width; x++) {
+				// work on column x in dst
+				const unsigned index = x * wordspp;	// pixel index
+				WORD *dst_bits = dst_base + index;
+
+				// scale each column
+				for (unsigned y = 0; y < dst_height; y++) {
+					// loop through column
+					const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
+					const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
+					const WORD *src_bits = src_base + iLeft * src_pitch + index;
+					double r = 0, g = 0, b = 0, a = 0;
+
+					for (unsigned i = 0; i < iLimit; i++) {
+						// scan between boundaries
+						// accumulate weighted effect of each neighboring pixel
+						const double weight = weightsTable.getWeight(y, i);					
+						r += (weight * (double)src_bits[0]);
+						g += (weight * (double)src_bits[1]);
+						b += (weight * (double)src_bits[2]);
+						a += (weight * (double)src_bits[3]);
+
+						src_bits += src_pitch;
+					}
+
+					// clamp and place result in destination pixel
+					dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+					dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+					dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+					dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
+
+					dst_bits += dst_pitch;
+				}
+			}
+		}
+		break;
+
+		case FIT_FLOAT:
+		case FIT_RGBF:
+		case FIT_RGBAF:
+		{
+			// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
+			const unsigned floatspp = (FreeImage_GetLine(src) / width) / sizeof(float);
+
+			const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
+			float *const dst_base = (float *)FreeImage_GetBits(dst);
+
+			const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
+			const float *const src_base = (float *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * floatspp;
+
+			for (unsigned x = 0; x < width; x++) {
+				// work on column x in dst
+				const unsigned index = x * floatspp;	// pixel index
+				float *dst_bits = (float *)dst_base + index;
+
+				// scale each column
+				for (unsigned y = 0; y < dst_height; y++) {
+					// loop through column
+					const unsigned iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
+					const unsigned iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
+					const float *src_bits = src_base + iLeft * src_pitch + index;
+					double value[4] = {0, 0, 0, 0};                            // 4 = 128 bpp max
+
+					for (unsigned i = iLeft; i < iRight; i++) {
+						// scan between boundaries
+						// accumulate weighted effect of each neighboring pixel
+						const double weight = weightsTable.getWeight(y, i - iLeft);
+						for (unsigned j = 0; j < floatspp; j++) {
+							value[j] += (weight * (double)src_bits[j]);
+						}
+						src_bits += src_pitch;
+					}
+
+					// place result in destination pixel
+					for (unsigned j = 0; j < floatspp; j++) {
+						dst_bits[j] = (float)value[j];
+					}
+					dst_bits += dst_pitch;
+				}
+			}
+		}
+		break;
+	}
+}
diff --git a/files/Source/FreeImageToolkit/Resize.h b/files/Source/FreeImageToolkit/Resize.h
new file mode 100644
index 0000000..ce1d732
--- /dev/null
+++ b/files/Source/FreeImageToolkit/Resize.h
@@ -0,0 +1,196 @@
+// ==========================================================
+// Upsampling / downsampling classes
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.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!
+// ==========================================================
+
+#ifndef _RESIZE_H_
+#define _RESIZE_H_
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "Filters.h" 
+
+/**
+  Filter weights table.<br>
+  This class stores contribution information for an entire line (row or column).
+*/
+class CWeightsTable
+{
+/**
+  Sampled filter weight table.<br>
+  Contribution information for a single pixel
+*/
+typedef struct {
+	/// Normalized weights of neighboring pixels
+	double *Weights;
+	/// Bounds of source pixels window
+	unsigned Left, Right;
+} Contribution;
+
+private:
+	/// Row (or column) of contribution weights 
+	Contribution *m_WeightTable;
+	/// Filter window size (of affecting source pixels) 
+	unsigned m_WindowSize;
+	/// Length of line (no. of rows / cols) 
+	unsigned m_LineLength;
+
+public:
+	/** 
+	Constructor<br>
+	Allocate and compute the weights table
+	@param pFilter Filter used for upsampling or downsampling
+	@param uDstSize Length (in pixels) of the destination line buffer
+	@param uSrcSize Length (in pixels) of the source line buffer
+	*/
+	CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize);
+
+	/**
+	Destructor<br>
+	Destroy the weights table
+	*/
+	~CWeightsTable();
+
+	/** Retrieve a filter weight, given source and destination positions
+	@param dst_pos Pixel position in destination line buffer
+	@param src_pos Pixel position in source line buffer
+	@return Returns the filter weight
+	*/
+	double getWeight(unsigned dst_pos, unsigned src_pos) {
+		return m_WeightTable[dst_pos].Weights[src_pos];
+	}
+
+	/** Retrieve left boundary of source line buffer
+	@param dst_pos Pixel position in destination line buffer
+	@return Returns the left boundary of source line buffer
+	*/
+	unsigned getLeftBoundary(unsigned dst_pos) {
+		return m_WeightTable[dst_pos].Left;
+	}
+
+	/** Retrieve right boundary of source line buffer
+	@param dst_pos Pixel position in destination line buffer
+	@return Returns the right boundary of source line buffer
+	*/
+	unsigned getRightBoundary(unsigned dst_pos) {
+		return m_WeightTable[dst_pos].Right;
+	}
+};
+
+// ---------------------------------------------
+
+/**
+ CResizeEngine<br>
+ This class performs filtered zoom. It scales an image to the desired dimensions with 
+ any of the CGenericFilter derived filter class.<br>
+ It works with FIT_BITMAP buffers, WORD buffers (FIT_UINT16, FIT_RGB16, FIT_RGBA16) 
+ and float buffers (FIT_FLOAT, FIT_RGBF, FIT_RGBAF).<br><br>
+
+ <b>References</b> : <br>
+ [1] Paul Heckbert, C code to zoom raster images up or down, with nice filtering. 
+ UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
+ [2] Eran Yariv, Two Pass Scaling using Filters. The Code Project, December 1999. 
+ [online] http://www.codeproject.com/bitmap/2_pass_scaling.asp
+
+*/
+class CResizeEngine
+{
+private:
+	/// Pointer to the FIR / IIR filter
+	CGenericFilter* m_pFilter;
+
+public:
+
+	/**
+	Constructor
+	@param filter FIR /IIR filter to be used
+	*/
+	CResizeEngine(CGenericFilter* filter):m_pFilter(filter) {}
+
+	/// Destructor
+	virtual ~CResizeEngine() {}
+
+	/** Scale an image to the desired dimensions.
+
+	Method CResizeEngine::scale, as well as the two filtering methods
+	CResizeEngine::horizontalFilter and CResizeEngine::verticalFilter take
+	four additional parameters, that define a rectangle in the source
+	image to be rescaled.
+
+	These are src_left, src_top, src_width and src_height and should work
+	like these of function FreeImage_Copy. However, src_left and src_top are
+	actually named src_offset_x and src_offset_y in the filtering methods.
+
+	Additionally, since src_height and dst_height are always the same for
+	method horizontalFilter as src_width and dst_width are always the same
+	for verticalFilter, these have been stripped down to a single parameter
+	height and width for horizontalFilter and verticalFilter respectively.
+
+	Currently, method scale is called with the actual size of the source
+	image. However, in a future version, we could provide a new function
+	called FreeImage_RescaleRect that rescales only part of an image. 
+
+	@param src Pointer to the source image
+	@param dst_width Destination image width
+	@param dst_height Destination image height
+	@param src_left Left boundary of the source rectangle to be scaled
+	@param src_top Top boundary of the source rectangle to be scaled
+	@param src_width Width of the source rectangle to be scaled
+	@param src_height Height of the source rectangle to be scaled
+	@return Returns the scaled image if successful, returns NULL otherwise
+	*/
+	FIBITMAP* scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags);
+
+private:
+
+	/**
+	Performs horizontal image filtering
+
+	@param src Source image
+	@param height Source / Destination image height
+	@param src_width Source image width
+	@param src_offset_x
+	@param src_offset_y
+	@param src_pal
+	@param dst Destination image
+	@param dst_width Destination image width
+	*/
+	void horizontalFilter(FIBITMAP * const src, const unsigned height, const unsigned src_width,
+			const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
+			FIBITMAP * const dst, const unsigned dst_width);
+
+	/**
+	Performs vertical image filtering
+	@param src Source image
+	@param width Source / Destination image width
+	@param src_height Source image height
+	@param src_offset_x
+	@param src_offset_y
+	@param src_pal
+	@param dst Destination image
+	@param dst_height Destination image height
+	*/
+	void verticalFilter(FIBITMAP * const src, const unsigned width, const unsigned src_height,
+			const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
+			FIBITMAP * const dst, const unsigned dst_height);
+};
+
+#endif //   _RESIZE_H_
diff --git a/files/Source/LibMNG/libmng.h b/files/Source/LibMNG/libmng.h
new file mode 100644
index 0000000..b3b1ab1
--- /dev/null
+++ b/files/Source/LibMNG/libmng.h
@@ -0,0 +1,2932 @@
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * COPYRIGHT NOTICE:                                                      * */
+/* *                                                                        * */
+/* * Copyright (c) 2000-2007 Gerard Juyn                                    * */
+/* * [You may insert additional notices after this sentence if you modify   * */
+/* *  this source]                                                          * */
+/* *                                                                        * */
+/* * For the purposes of this copyright and license, "Contributing Authors" * */
+/* * is defined as the following set of individuals:                        * */
+/* *                                                                        * */
+/* *    Gerard Juyn                 - gjuyn :at: users.sourceforge.net      * */
+/* *    Glenn Randers-Pehrson       - glennrp :at: users.sourceforge.net    * */
+/* *    Raphael Assenat             - raph :at: raphnet.net                 * */
+/* *    John Stiles                 -                                       * */
+/* *                                                                        * */
+/* * The MNG Library is supplied "AS IS".  The Contributing Authors         * */
+/* * disclaim all warranties, expressed or implied, including, without      * */
+/* * limitation, the warranties of merchantability and of fitness for any   * */
+/* * purpose.  The Contributing Authors assume no liability for direct,     * */
+/* * indirect, incidental, special, exemplary, or consequential damages,    * */
+/* * which may result from the use of the MNG Library, even if advised of   * */
+/* * the possibility of such damage.                                        * */
+/* *                                                                        * */
+/* * Permission is hereby granted to use, copy, modify, and distribute this * */
+/* * source code, or portions hereof, for any purpose, without fee, subject * */
+/* * to the following restrictions:                                         * */
+/* *                                                                        * */
+/* * 1. The origin of this source code must not be misrepresented;          * */
+/* *    you must not claim that you wrote the original software.            * */
+/* *                                                                        * */
+/* * 2. Altered versions must be plainly marked as such and must not be     * */
+/* *    misrepresented as being the original source.                        * */
+/* *                                                                        * */
+/* * 3. This Copyright notice may not be removed or altered from any source * */
+/* *    or altered source distribution.                                     * */
+/* *                                                                        * */
+/* * The Contributing Authors specifically permit, without fee, and         * */
+/* * encourage the use of this source code as a component to supporting     * */
+/* * the MNG and JNG file format in commercial products.  If you use this   * */
+/* * source code in a product, acknowledgment would be highly appreciated.  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Parts of this software have been adapted from the libpng package.      * */
+/* * Although this library supports all features from the PNG specification * */
+/* * (as MNG descends from it) it does not require the libpng package.      * */
+/* * It does require the zlib library and optionally the IJG jpeg library,  * */
+/* * and/or the "little-cms" library by Marti Maria (depending on the       * */
+/* * inclusion of support for JNG and Full-Color-Management respectively.   * */
+/* *                                                                        * */
+/* * This library's function is primarily to read and display MNG           * */
+/* * animations. It is not meant as a full-featured image-editing           * */
+/* * component! It does however offer creation and editing functionality    * */
+/* * at the chunk level.                                                    * */
+/* * (future modifications may include some more support for creation       * */
+/* *  and or editing)                                                       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Version numbering                                                      * */
+/* *                                                                        * */
+/* * X.Y.Z : X = release (0 = initial build)                                * */
+/* *         Y = major version (uneven = test; even = production)           * */
+/* *         Z = minor version (bugfixes; 2 is older than 10)               * */
+/* *                                                                        * */
+/* * production versions only appear when a test-version is extensively     * */
+/* * tested and found stable or for intermediate bug-fixes (recognized by   * */
+/* * a change in the Z number)                                              * */
+/* *                                                                        * */
+/* * x.1.x      = test version                                              * */
+/* * x.2.x      = production version                                        * */
+/* * x.3.x      = test version                                              * */
+/* * x.4.x      = production version                                        * */
+/* *  etc.                                                                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Identifier naming conventions throughout this library                  * */
+/* *                                                                        * */
+/* * iXxxx      = an integer                                                * */
+/* * dXxxx      = a float                                                   * */
+/* * pXxxx      = a pointer                                                 * */
+/* * bXxxx      = a boolean                                                 * */
+/* * eXxxx      = an enumeration                                            * */
+/* * hXxxx      = a handle                                                  * */
+/* * zXxxx      = a zero-terminated string (pchar)                          * */
+/* * fXxxx      = a pointer to a function (callback)                        * */
+/* * aXxxx      = an array                                                  * */
+/* * sXxxx      = a structure                                               * */
+/* *                                                                        * */
+/* * Macros & defines are in all uppercase.                                 * */
+/* * Functions & typedefs in all lowercase.                                 * */
+/* * Exported stuff is prefixed with MNG_ or mng_ respectively.             * */
+/* *                                                                        * */
+/* * (I may have missed a couple; don't hesitate to let me know!)           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng.h                  copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : main application interface                                 * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : The main application interface. An application should not  * */
+/* *             need access to any of the other modules!                   * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/06/2000 - G.Juyn                                * */
+/* *             - changed chunk iteration function                         * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - added chunk access functions                             * */
+/* *             - added version control constants & functions              * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added set_outputprofile2 & set_srgbprofile2              * */
+/* *             - added empty-chunk put-routines                           * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - added version_dll & VERSION_DLL (for consistency)        * */
+/* *             - added version control explanatory text & samples         * */
+/* *             0.5.1 - 05/15/2000 - G.Juyn                                * */
+/* *             - added getimgdata & putimgdata functions                  * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/16/2000 - G.Juyn                                * */
+/* *             - changed the version parameters (obviously)               * */
+/* *             0.5.2 - 05/18/2000 - G.Juyn                                * */
+/* *             - complimented constants for chunk-property values         * */
+/* *             0.5.2 - 05/23/2000 - G.Juyn                                * */
+/* *             - fixed MNG_UINT_pHYg value                                * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - added support for get/set default zlib/IJG parms         * */
+/* *             0.5.2 - 06/02/2000 - G.Juyn                                * */
+/* *             - added MNG_BIGENDIAN_SUPPORT (contributed by Tim Rowley)  * */
+/* *             - separated configuration-options into "mng_conf.h"        * */
+/* *             - added RGB8_A8 canvasstyle                                * */
+/* *             - added getalphaline callback for RGB8_A8 canvasstyle      * */
+/* *             0.5.2 - 06/06/2000 - G.Juyn                                * */
+/* *             - moved errorcodes from "mng_error.h"                      * */
+/* *             - added mng_read_resume function to support                * */
+/* *               read-suspension                                          * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/16/2000 - G.Juyn                                * */
+/* *             - changed the version parameters (obviously)               * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added get/set for speedtype to facilitate testing        * */
+/* *             - added get for imagelevel during processtext callback     * */
+/* *             0.5.3 - 06/24/2000 - G.Juyn                                * */
+/* *             - fixed inclusion of IJG read/write code                   * */
+/* *             0.5.3 - 06/26/2000 - G.Juyn                                * */
+/* *             - changed userdata variable to mng_ptr                     * */
+/* *                                                                        * */
+/* *             0.9.0 - 06/30/2000 - G.Juyn                                * */
+/* *             - changed refresh parameters to 'x,y,width,height'         * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/06/2000 - G.Juyn                                * */
+/* *             - added MNG_NEEDTIMERWAIT errorcode                        * */
+/* *             - changed comments to indicate modified behavior for       * */
+/* *               timer & suspension breaks                                * */
+/* *             0.9.1 - 07/08/2000 - G.Juyn                                * */
+/* *             - added get routines for internal display variables        * */
+/* *             - added get/set routines for suspensionmode variable       * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added callbacks for SAVE/SEEK processing                 * */
+/* *             - added get/set routines for sectionbreak variable         * */
+/* *             - added NEEDSECTIONWAIT errorcode                          * */
+/* *             0.9.1 - 07/19/2000 - G.Juyn                                * */
+/* *             - added function to set frame-/layer-count & playtime      * */
+/* *             - added errorcode for updatemngheader if not a MNG         * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/31/2000 - G.Juyn                                * */
+/* *             - fixed problem with trace-functions improperly wrapped    * */
+/* *             - added status_xxxx functions                              * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *             - added function to set simplicity field                   * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/09/2000 - G.Juyn                                * */
+/* *             - added check for simplicity-bits in MHDR                  * */
+/* *             0.9.3 - 08/12/2000 - G.Juyn                                * */
+/* *             - added workaround for faulty PhotoShop iCCP chunk         * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *             0.9.3 - 10/10/2000 - G.Juyn                                * */
+/* *             - added support for alpha-depth prediction                 * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - fixed processing of unknown critical chunks              * */
+/* *             - removed test-MaGN                                        * */
+/* *             - added PNG/MNG spec version indicators                    * */
+/* *             - added support for nEED                                   * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added functions to retrieve PNG/JNG specific header-info * */
+/* *             - added JDAA chunk                                         * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added callback to process non-critical unknown chunks    * */
+/* *             0.9.3 - 10/20/2000 - G.Juyn                                * */
+/* *             - added errocode for delayed delta-processing              * */
+/* *             - added get/set for bKGD preference setting                * */
+/* *             0.9.3 - 10/21/2000 - G.Juyn                                * */
+/* *             - added get function for interlace/progressive display     * */
+/* *                                                                        * */
+/* *             0.9.4 - 01/18/2001 - G.Juyn                                * */
+/* *             - added errorcode for MAGN methods                         * */
+/* *             - removed test filter-methods 1 & 65                       * */
+/* *                                                                        * */
+/* *             1.0.0 - 02/05/2001 - G.Juyn                                * */
+/* *             - version numbers (obviously)                              * */
+/* *                                                                        * */
+/* *             1.0.1 - 02/08/2001 - G.Juyn                                * */
+/* *             - added MEND processing callback                           * */
+/* *             1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly)              * */
+/* *             - added BGRA8 canvas with premultiplied alpha              * */
+/* *             1.0.1 - 05/02/2001 - G.Juyn                                * */
+/* *             - added "default" sRGB generation (Thanks Marti!)          * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added optimization option for MNG-video playback         * */
+/* *             - added processterm callback                               * */
+/* *             1.0.2 - 06/25/2001 - G.Juyn                                * */
+/* *             - added late binding errorcode (not used internally)       * */
+/* *             - added option to turn off progressive refresh             * */
+/* *                                                                        * */
+/* *             1.0.3 - 08/06/2001 - G.Juyn                                * */
+/* *             - added get function for last processed BACK chunk         * */
+/* *                                                                        * */
+/* *             1.0.5 - 07/04/2002 - G.Juyn                                * */
+/* *             - added errorcode for extreme chunk-sizes                  * */
+/* *             1.0.5 - 08/07/2002 - G.Juyn                                * */
+/* *             - added test-option for PNG filter method 193 (=no filter) * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed PROM support                                   * */
+/* *             - completed delta-image support                            * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - added HLAPI function to copy chunks                      * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             - added 'supports' call to check function availability     * */
+/* *             1.0.5 - 09/15/2002 - G.Juyn                                * */
+/* *             - fixed LOOP iteration=0 special case                      * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 09/22/2002 - G.Juyn                                * */
+/* *             - added bgrx8 canvas (filler byte)                         * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - added check for TERM placement during create/write       * */
+/* *             - added beta version function & constant                   * */
+/* *             1.0.5 - 11/07/2002 - G.Juyn                                * */
+/* *             - added support to get totals after mng_read()             * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G. Randers-Pehrson                    * */
+/* *             - added support for reducing the footprint of libmng       * */
+/* *               by macros that optionally skip unused chunks, remove     * */
+/* *               16-bit sample support, remove Delta support, and         * */
+/* *               remove JNG support, to accomodate Mozilla/Firebird.      * */
+/* *             1.0.6 - 07/14/2003 - G. Randers-Pehrson                    * */
+/* *             - further optional removal of unused functions             * */
+/* *                                                                        * */
+/* *             1.0.7 - 11/27/2003 - R.A                                   * */
+/* *             - added CANVAS_RGB565 and CANVAS_BGR565                    * */
+/* *             1.0.7 - 12/06/2003 - R.A                                   * */
+/* *             - added CANVAS_RGBA565 and CANVAS_BGRA565                  * */
+/* *             1.0.7 - 01/25/2004 - J.S                                   * */
+/* *             - added premultiplied alpha canvas' for RGBA, ARGB, ABGR   * */
+/* *             1.0.7 - 03/07/2004 - G. Randers-Pehrson                    * */
+/* *             - put gamma, cms-related declarations inside #ifdef        * */
+/* *             1.0.7 - 03/10/2004 - G.R-P                                 * */
+/* *             - added conditionals around openstream/closestream         * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/02/2004 - G.Juyn                                * */
+/* *             - added CRC existence & checking flags                     * */
+/* *             1.0.8 - 04/12/2004 - G.Juyn                                * */
+/* *             - added data-push mechanisms for specialized decoders      * */
+/* *             1.0.8 - 06/05/2004 - G.R-P                                 * */
+/* *             - define MNG_INCLUDE_ZLIB when MNG_USE_ZLIB_CRC is defined * */
+/* *                                                                        * */
+/* *             1.0.9 - 10/03/2004 - G.Juyn                                * */
+/* *             - added function to retrieve current FRAM delay            * */
+/* *             1.0.9 - 10/14/2004 - G.Juyn                                * */
+/* *             - added bgr565_a8 canvas-style (thanks to J. Elvander)     * */
+/* *             1.0.9 - 10/17/2004 - G.Juyn                                * */
+/* *             - fixed PPLT getchunk/putchunk routines                    * */
+/* *                                                                        * */
+/* *             1.0.10 - 03/07/2006 - (thanks to W. Manthey)               * */
+/* *             - added CANVAS_RGB555 and CANVAS_BGR555                    * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_h_
+#define _libmng_h_
+
+/* ************************************************************************** */
+
+#include "libmng_conf.h"               /* user-specific configuration options */
+
+/* ************************************************************************** */
+
+#define MNG_CHECK_BAD_ICCP             /* let's catch that sucker !!! */
+
+#ifdef MNG_SUPPORT_READ                /* dependencies based on user-configuration */
+#define MNG_INCLUDE_READ_PROCS
+#endif
+
+#ifdef MNG_SUPPORT_WRITE
+#define MNG_INCLUDE_WRITE_PROCS
+#endif
+
+#ifdef MNG_USE_ZLIB_CRC
+#define MNG_INCLUDE_ZLIB
+#endif
+
+#ifdef MNG_SUPPORT_DISPLAY
+#define MNG_INCLUDE_FILTERS
+#define MNG_INCLUDE_INTERLACE
+#define MNG_INCLUDE_OBJECTS
+#define MNG_INCLUDE_DISPLAY_PROCS
+#define MNG_INCLUDE_TIMING_PROCS
+#define MNG_INCLUDE_ZLIB
+#endif
+
+#ifdef MNG_STORE_CHUNKS
+#define MNG_INCLUDE_ZLIB
+#endif
+
+#ifdef MNG_SUPPORT_IJG6B
+#define MNG_INCLUDE_JNG
+#define MNG_INCLUDE_IJG6B
+#define MNG_USE_SETJMP
+#endif
+
+#ifdef MNG_INCLUDE_JNG
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_ACCESS_CHUNKS)
+#define MNG_INCLUDE_JNG_READ
+#endif
+#if defined(MNG_SUPPORT_WRITE) || defined(MNG_ACCESS_CHUNKS)
+#define MNG_INCLUDE_JNG_WRITE
+#endif
+#endif
+
+#ifdef MNG_FULL_CMS
+#define MNG_INCLUDE_LCMS
+#endif
+
+#ifdef MNG_AUTO_DITHER
+#define MNG_INCLUDE_DITHERING
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+#define MNG_INCLUDE_TRACE_PROCS
+#ifdef MNG_TRACE_TELLTALE
+#define MNG_INCLUDE_TRACE_STRINGS
+#endif
+#endif
+
+#ifdef MNG_ERROR_TELLTALE
+#define MNG_INCLUDE_ERROR_STRINGS
+#endif
+
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_OPTIMIZE_CHUNKACCESS
+#define MNG_OPTIMIZE_CHUNKACCESS
+#endif
+#else
+#ifdef MNG_OPTIMIZE_CHUNKACCESS
+#undef MNG_OPTIMIZE_CHUNKACCESS
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#include "libmng_types.h"              /* platform-specific definitions
+                                          and other assorted stuff */
+
+/* ************************************************************************** */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Versioning control                                                    * */
+/* *                                                                        * */
+/* *  version_so and version_dll will NOT reflect version_major;            * */
+/* *  these will only change for binary incompatible changes (which will    * */
+/* *  hopefully never occur)                                                * */
+/* *  note: they will be set to 1 on the first public release !!!           * */
+/* *                                                                        * */
+/* *  first public release:                                                 * */
+/* *  #define MNG_VERSION_TEXT    "1.0.0"                                   * */
+/* *  #define MNG_VERSION_SO      1       eg. libmng.so.1                   * */
+/* *  #define MNG_VERSION_DLL     1       eg. libmng.dll                    * */
+/* *  #define MNG_VERSION_MAJOR   1                                         * */
+/* *  #define MNG_VERSION_MINOR   0                                         * */
+/* *  #define MNG_VERSION_RELEASE 0                                         * */
+/* *                                                                        * */
+/* *  bug fix & cosmetics :                                                 * */
+/* *  #define MNG_VERSION_TEXT    "1.0.1"                                   * */
+/* *  #define MNG_VERSION_SO      1       eg. libmng.so.1                   * */
+/* *  #define MNG_VERSION_DLL     1       eg. libmng.dll                    * */
+/* *  #define MNG_VERSION_MAJOR   1                                         * */
+/* *  #define MNG_VERSION_MINOR   0                                         * */
+/* *  #define MNG_VERSION_RELEASE 1                                         * */
+/* *                                                                        * */
+/* *  feature change :                                                      * */
+/* *  #define MNG_VERSION_TEXT    "1.2.0"                                   * */
+/* *  #define MNG_VERSION_SO      1       eg. libmng.so.1                   * */
+/* *  #define MNG_VERSION_DLL     1       eg. libmng.dll                    * */
+/* *  #define MNG_VERSION_MAJOR   1                                         * */
+/* *  #define MNG_VERSION_MINOR   2                                         * */
+/* *  #define MNG_VERSION_RELEASE 0                                         * */
+/* *                                                                        * */
+/* *  major rewrite (still binary compatible) :                             * */
+/* *  #define MNG_VERSION_TEXT    "2.0.0"                                   * */
+/* *  #define MNG_VERSION_SO      1       eg. libmng.so.1                   * */
+/* *  #define MNG_VERSION_DLL     1       eg. libmng.dll                    * */
+/* *  #define MNG_VERSION_MAJOR   2                                         * */
+/* *  #define MNG_VERSION_MINOR   0                                         * */
+/* *  #define MNG_VERSION_RELEASE 0                                         * */
+/* *                                                                        * */
+/* *  binary incompatible change:                                           * */
+/* *  #define MNG_VERSION_TEXT    "13.0.0"                                  * */
+/* *  #define MNG_VERSION_SO      2       eg. libmng.so.2                   * */
+/* *  #define MNG_VERSION_DLL     2       eg. libmng2.dll                   * */
+/* *  #define MNG_VERSION_MAJOR   13                                        * */
+/* *  #define MNG_VERSION_MINOR   0                                         * */
+/* *  #define MNG_VERSION_RELEASE 0                                         * */
+/* *                                                                        * */
+/* *  note that version_so & version_dll will always remain equal so it     * */
+/* *  doesn't matter which one is called to do version-checking; they are   * */
+/* *  just provided for their target platform                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_VERSION_TEXT    "1.0.10"
+#define MNG_VERSION_SO      1          /* eg. libmng.so.1  */
+#define MNG_VERSION_DLL     1          /* but: libmng.dll (!) */
+#define MNG_VERSION_MAJOR   1
+#define MNG_VERSION_MINOR   0
+#define MNG_VERSION_RELEASE 10
+#define MNG_VERSION_BETA    MNG_FALSE
+
+MNG_EXT mng_pchar MNG_DECL mng_version_text      (void);
+MNG_EXT mng_uint8 MNG_DECL mng_version_so        (void);
+MNG_EXT mng_uint8 MNG_DECL mng_version_dll       (void);
+MNG_EXT mng_uint8 MNG_DECL mng_version_major     (void);
+MNG_EXT mng_uint8 MNG_DECL mng_version_minor     (void);
+MNG_EXT mng_uint8 MNG_DECL mng_version_release   (void);
+MNG_EXT mng_bool  MNG_DECL mng_version_beta      (void);
+
+/* use the following call to check wether the version of libmng your app
+   is using supports the given function; this is useful in apps that dynamically
+   load the library to make sure a certain function will work; the result will
+   be MNG_TRUE if the given function is implemented in this version of the library;
+   Major/Minor/Version indicate the version the function became available;
+   (if these fields are zero the function is not yet implemented!) */
+#ifdef MNG_SUPPORT_FUNCQUERY
+MNG_EXT mng_bool  MNG_DECL mng_supports_func     (mng_pchar  zFunction,
+                                                  mng_uint8* iMajor,
+                                                  mng_uint8* iMinor,
+                                                  mng_uint8* iRelease);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  MNG/PNG specification level conformance                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_PNG_VERSION     "1.2"
+#define MNG_PNG_VERSION_MAJ 1
+#define MNG_PNG_VERSION_MIN 2
+
+#define MNG_MNG_VERSION     "1.1"
+#define MNG_MNG_VERSION_MAJ 1
+#define MNG_MNG_VERSION_MIN 1
+#define MNG_MNG_DRAFT       99         /* deprecated;
+                                          only used for nEED "MNG DRAFT nn" */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  High-level application functions                                      * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* library initialization function */
+/* must be the first called before anything can be done at all */
+/* initializes internal datastructure(s) */
+MNG_EXT mng_handle  MNG_DECL mng_initialize      (mng_ptr       pUserdata,
+                                                  mng_memalloc  fMemalloc,
+                                                  mng_memfree   fMemfree,
+                                                  mng_traceproc fTraceproc);
+
+/* library reset function */
+/* can be used to re-initialize the library, so another image can be
+   processed. there's absolutely no harm in calling it, even when it's not
+   really necessary */
+MNG_EXT mng_retcode MNG_DECL mng_reset           (mng_handle    hHandle);
+
+/* library cleanup function */
+/* must be the last called to clean up internal datastructure(s) */
+MNG_EXT mng_retcode MNG_DECL mng_cleanup         (mng_handle*   hHandle);
+
+/* high-level read functions */
+/* use mng_read if you simply want to read a Network Graphic */
+/* mng_read_resume is used in I/O-read-suspension scenarios, where the
+   "readdata" callback may return FALSE & length=0 indicating its buffer is
+   depleted or too short to supply the required bytes, and the buffer needs
+   to be refilled; libmng will return the errorcode MNG_NEEDMOREDATA telling
+   the app to refill its read-buffer after which it must call mng_read_resume
+   (or mng_display_resume if it also displaying the image simultaneously) */
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_retcode MNG_DECL mng_read            (mng_handle    hHandle);
+MNG_EXT mng_retcode MNG_DECL mng_read_resume     (mng_handle    hHandle);
+#endif
+
+/* high-level "data push" functions */
+/* these functions can be used in situations where data is streaming into the
+   application and needs to be buffered by libmng before it is actually
+   requested by libmng itself. the pushing complements the normal reading
+   mechanism, but applications can decide to always return "0 bytes read" to
+   make libmng go into suspension mode with the returncode MNG_NEEDMOREDATA */
+/* mng_read_pushdata can be used to push blobs of data of arbitrary size;
+   mng_read_pushsig and mng_read_pushchunk can be used if the application
+   has already done some low-level decoding (eg. at the chunk level) */
+/* the data being pushed into libmng with mng_read_pushdata *must* contain
+   the regular 4-byte chunklength, but *must not* contain it with
+   mng_read_pushchunk!!! */
+/* mng_read_pushsig is used to prevent libmng from trying to parse the regular
+   PNG/JNG/MNG signature bytes; the application must have done this itself
+   and *must* indicate the proper type in the function call or things will
+   go amiss!!
+   also you *must* call this first, so pretty much right after mng_initialize
+   and certainly before any call to mng_read or mng_readdisplay !!!! */
+/* IMPORTANT!!! data can only be safely pushed when libmng is in a
+   "wait" state; eg. during MNG_NEEDTIMERWAIT, MNG_NEEDSECTIONWAIT or
+   MNG_NEEDMOREDATA !!! this just means you can't have one thread displaying
+   and another thread pushing data !!! */
+/* if bOwnership = MNG_TRUE, libmng will retain the supplied pointer and
+   *will* expect the buffer to remain available until libmng is finished
+   with it; what happens then depends on whether or not you have set the
+   releasedata() callback; if this is set than the supplied buffer will
+   be returned through this callback and your application can take care of
+   cleaning it up, otherwise libmng will use its internal freeing mechanism
+   (which, depending on compile-options, will be the standard C free() call,
+   or the memfree() callback */
+/* if bOwnership = MNG_FALSE, libmng will just copy the data into its own
+   buffers and dispose of it in the normal way */
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_retcode MNG_DECL mng_read_pushdata   (mng_handle    hHandle,
+                                                  mng_ptr       pData,
+                                                  mng_size_t    iLength,
+                                                  mng_bool      bTakeownership);
+MNG_EXT mng_retcode MNG_DECL mng_read_pushsig    (mng_handle    hHandle,
+                                                  mng_imgtype   eSigtype);
+MNG_EXT mng_retcode MNG_DECL mng_read_pushchunk  (mng_handle    hHandle,
+                                                  mng_ptr       pChunk,
+                                                  mng_size_t    iLength,
+                                                  mng_bool      bTakeownership);
+#endif
+
+/* high-level write & create functions */
+/* use this if you want to write a previously read Network Graphic or
+   if you want to create a new graphic and write it */
+/* to write a previously read graphic you must have defined MNG_STORE_CHUNKS */
+/* to create a new graphic you'll also need access to the chunks
+   (eg. #define MNG_ACCESS_CHUNKS !) */
+#ifdef MNG_SUPPORT_WRITE
+MNG_EXT mng_retcode MNG_DECL mng_write           (mng_handle    hHandle);
+MNG_EXT mng_retcode MNG_DECL mng_create          (mng_handle    hHandle);
+#endif
+
+/* high-level display functions */
+/* use these to display a previously read or created graphic or
+   to read & display a graphic simultaneously */
+/* mng_display_resume should be called after a timer-interval
+   expires that was set through the settimer-callback, after a
+   read suspension-break, or, to resume an animation after a call
+   to mng_display_freeze/mng_display_reset */
+/* mng_display_freeze thru mng_display_gotime can be used to influence
+   the display of an image, BUT ONLY if it has been completely read! */
+#ifdef MNG_SUPPORT_DISPLAY
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_retcode MNG_DECL mng_readdisplay     (mng_handle    hHandle);
+#endif
+MNG_EXT mng_retcode MNG_DECL mng_display         (mng_handle    hHandle);
+MNG_EXT mng_retcode MNG_DECL mng_display_resume  (mng_handle    hHandle);
+MNG_EXT mng_retcode MNG_DECL mng_display_freeze  (mng_handle    hHandle);
+MNG_EXT mng_retcode MNG_DECL mng_display_reset   (mng_handle    hHandle);
+#ifndef MNG_NO_DISPLAY_GO_SUPPORTED
+MNG_EXT mng_retcode MNG_DECL mng_display_goframe (mng_handle    hHandle,
+                                                  mng_uint32    iFramenr);
+MNG_EXT mng_retcode MNG_DECL mng_display_golayer (mng_handle    hHandle,
+                                                  mng_uint32    iLayernr);
+MNG_EXT mng_retcode MNG_DECL mng_display_gotime  (mng_handle    hHandle,
+                                                  mng_uint32    iPlaytime);
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* event processing function */
+/* this needs to be called by the app when dynamic MNG is enabled and
+   a specific event occurs in the user-interface */
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG)
+MNG_EXT mng_retcode MNG_DECL mng_trapevent       (mng_handle    hHandle,
+                                                  mng_uint8     iEventtype,
+                                                  mng_int32     iX,
+                                                  mng_int32     iY);
+#endif
+
+/* error reporting function */
+/* use this if you need more detailed info on the last error */
+/* iExtra1 & iExtra2 may contain errorcodes from zlib, jpeg, etc... */
+/* zErrortext will only be filled if you #define MNG_ERROR_TELLTALE */
+MNG_EXT mng_retcode MNG_DECL mng_getlasterror    (mng_handle    hHandle,
+                                                  mng_int8*     iSeverity,
+                                                  mng_chunkid*  iChunkname,
+                                                  mng_uint32*   iChunkseq,
+                                                  mng_int32*    iExtra1,
+                                                  mng_int32*    iExtra2,
+                                                  mng_pchar*    zErrortext);
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Callback set functions                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* memory callbacks */
+/* called to allocate and release internal datastructures */
+#ifndef MNG_INTERNAL_MEMMNGMT
+MNG_EXT mng_retcode MNG_DECL mng_setcb_memalloc      (mng_handle        hHandle,
+                                                      mng_memalloc      fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_memfree       (mng_handle        hHandle,
+                                                      mng_memfree       fProc);
+#endif /* MNG_INTERNAL_MEMMNGMT */
+
+/* open- & close-stream callbacks */
+/* called to open & close streams for input or output */
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+MNG_EXT mng_retcode MNG_DECL mng_setcb_openstream    (mng_handle        hHandle,
+                                                      mng_openstream    fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_closestream   (mng_handle        hHandle,
+                                                      mng_closestream   fProc);
+#endif
+#endif
+
+/* read callback */
+/* called to get data from the inputstream */
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_retcode MNG_DECL mng_setcb_readdata      (mng_handle        hHandle,
+                                                      mng_readdata      fProc);
+#endif
+
+/* write callback */
+/* called to put data into the outputstream */
+#ifdef MNG_SUPPORT_WRITE
+MNG_EXT mng_retcode MNG_DECL mng_setcb_writedata     (mng_handle        hHandle,
+                                                      mng_writedata     fProc);
+#endif
+
+/* error callback */
+/* called when an error occurs */
+/* the application can determine if the error is recoverable,
+   and may inform the library by setting specific returncodes */
+MNG_EXT mng_retcode MNG_DECL mng_setcb_errorproc     (mng_handle        hHandle,
+                                                      mng_errorproc     fProc);
+
+/* trace callback */
+/* called to show the currently executing function */
+#ifdef MNG_SUPPORT_TRACE
+MNG_EXT mng_retcode MNG_DECL mng_setcb_traceproc     (mng_handle        hHandle,
+                                                      mng_traceproc     fProc);
+#endif
+
+/* callbacks for read processing */
+/* processheader is called when all header information has been gathered
+   from the inputstream */
+/* processtext is called for every tEXt, zTXt and iTXt chunk in the
+   inputstream (iType=0 for tEXt, 1 for zTXt and 2 for iTXt);
+   you can call get_imagelevel to check at what nesting-level the chunk is
+   encountered (eg. tEXt inside an embedded image inside a MNG -> level == 2;
+                in most other case -> level == 1) */
+/* processsave & processseek are called for SAVE/SEEK chunks */
+/* processneed is called for the nEED chunk; you should specify a callback
+   for this as the default behavior will be to abort processing, unless
+   the requirement is one of:
+   - a supported chunk
+   - the text "draft nn" where nn is a numeric value
+   - the text "MNG-1.0" or "MNG-1.1"
+   - the text "CACHEOFF" */
+/* processmend is called at the very end of the animation-stream;
+   note that this may not be the end of the animation though! */
+/* processterm is called when a TERM chunk is encountered; there can be only
+   1 in the stream (or none) */
+/* processunknown is called after reading each non-critical unknown chunk */
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processheader (mng_handle        hHandle,
+                                                      mng_processheader fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processtext   (mng_handle        hHandle,
+                                                      mng_processtext   fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processsave   (mng_handle        hHandle,
+                                                      mng_processsave   fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processseek   (mng_handle        hHandle,
+                                                      mng_processseek   fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processneed   (mng_handle        hHandle,
+                                                      mng_processneed   fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processmend   (mng_handle        hHandle,
+                                                      mng_processmend   fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processterm   (mng_handle        hHandle,
+                                                      mng_processterm   fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processunknown(mng_handle        hHandle,
+                                                      mng_processunknown fProc);
+#endif
+
+/* callbacks for display processing */
+/* getcanvasline is called to get an access-pointer to a line on the
+   drawing-canvas */
+/* getbkgdline is called to get an access-pointer to a line from the
+   background-canvas */
+/* refresh is called to inform the GUI to redraw the current canvas onto
+   its output device (eg. in Win32 this would mean sending an
+   invalidate message for the specified region */
+/* NOTE that the update-region is specified as x,y,width,height; eg. the
+   invalidate message for Windows requires left,top,right,bottom parameters
+   where the bottom-right is exclusive of the region!!
+   to get these correctly is as simple as:
+   left   = x;
+   top    = y;
+   right  = x + width;
+   bottom = y + height;
+   if your implementation requires inclusive points, simply subtract 1 from
+   both the right & bottom values calculated above.
+   */
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_EXT mng_retcode MNG_DECL mng_setcb_getcanvasline (mng_handle        hHandle,
+                                                      mng_getcanvasline fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_getbkgdline   (mng_handle        hHandle,
+                                                      mng_getbkgdline   fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_getalphaline  (mng_handle        hHandle,
+                                                      mng_getalphaline  fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_refresh       (mng_handle        hHandle,
+                                                      mng_refresh       fProc);
+
+/* timing callbacks */
+/* gettickcount is called to get the system tickcount (milliseconds);
+   this is used to determine the remaining interval between frames */
+/* settimer is called to inform the application that it should set a timer;
+   when the timer is triggered the app must call mng_display_resume */
+MNG_EXT mng_retcode MNG_DECL mng_setcb_gettickcount  (mng_handle        hHandle,
+                                                      mng_gettickcount  fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_settimer      (mng_handle        hHandle,
+                                                      mng_settimer      fProc);
+
+/* color management callbacks */
+/* called to transmit color management information to the application */
+/* these are only used when you #define MNG_APP_CMS */
+#ifdef MNG_APP_CMS
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processgamma  (mng_handle        hHandle,
+                                                      mng_processgamma  fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processchroma (mng_handle        hHandle,
+                                                      mng_processchroma fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processsrgb   (mng_handle        hHandle,
+                                                      mng_processsrgb   fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processiccp   (mng_handle        hHandle,
+                                                      mng_processiccp   fProc);
+MNG_EXT mng_retcode MNG_DECL mng_setcb_processarow   (mng_handle        hHandle,
+                                                      mng_processarow   fProc);
+#endif /* MNG_APP_CMS */
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* release push data callback */
+/* used when the app pushes data into libmng (as opposed to libmng pulling it)
+   and relinquishes ownership of the pushed data-buffer, but *does* want to
+   release (free) the buffer itself once libmng has finished processing it */
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_retcode MNG_DECL mng_setcb_releasedata   (mng_handle        hHandle,
+                                                      mng_releasedata   fProc);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Callback get functions                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* see _setcb_ */
+#ifndef MNG_INTERNAL_MEMMNGMT
+MNG_EXT mng_memalloc      MNG_DECL mng_getcb_memalloc      (mng_handle hHandle);
+MNG_EXT mng_memfree       MNG_DECL mng_getcb_memfree       (mng_handle hHandle);
+#endif
+
+/* see _setcb_ */
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_releasedata   MNG_DECL mng_getcb_releasedata   (mng_handle hHandle);
+#endif
+
+/* see _setcb_ */
+#if defined(MNG_SUPPORT_READ) || defined(MNG_WRITE_SUPPORT)
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+MNG_EXT mng_openstream    MNG_DECL mng_getcb_openstream    (mng_handle hHandle);
+MNG_EXT mng_closestream   MNG_DECL mng_getcb_closestream   (mng_handle hHandle);
+#endif
+#endif
+
+/* see _setcb_ */
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_readdata      MNG_DECL mng_getcb_readdata      (mng_handle hHandle);
+#endif
+
+/* see _setcb_ */
+#ifdef MNG_SUPPORT_WRITE
+MNG_EXT mng_writedata     MNG_DECL mng_getcb_writedata     (mng_handle hHandle);
+#endif
+
+/* see _setcb_ */
+MNG_EXT mng_errorproc     MNG_DECL mng_getcb_errorproc     (mng_handle hHandle);
+
+/* see _setcb_ */
+#ifdef MNG_SUPPORT_TRACE
+MNG_EXT mng_traceproc     MNG_DECL mng_getcb_traceproc     (mng_handle hHandle);
+#endif
+
+/* see _setcb_ */
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_processheader MNG_DECL mng_getcb_processheader (mng_handle hHandle);
+MNG_EXT mng_processtext   MNG_DECL mng_getcb_processtext   (mng_handle hHandle);
+MNG_EXT mng_processsave   MNG_DECL mng_getcb_processsave   (mng_handle hHandle);
+MNG_EXT mng_processseek   MNG_DECL mng_getcb_processseek   (mng_handle hHandle);
+MNG_EXT mng_processneed   MNG_DECL mng_getcb_processneed   (mng_handle hHandle);
+MNG_EXT mng_processunknown MNG_DECL mng_getcb_processunknown (mng_handle hHandle);
+MNG_EXT mng_processterm   MNG_DECL mng_getcb_processterm   (mng_handle hHandle);
+#endif
+
+/* see _setcb_ */
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_EXT mng_getcanvasline MNG_DECL mng_getcb_getcanvasline (mng_handle hHandle);
+MNG_EXT mng_getbkgdline   MNG_DECL mng_getcb_getbkgdline   (mng_handle hHandle);
+MNG_EXT mng_getalphaline  MNG_DECL mng_getcb_getalphaline  (mng_handle hHandle);
+MNG_EXT mng_refresh       MNG_DECL mng_getcb_refresh       (mng_handle hHandle);
+
+/* see _setcb_ */
+MNG_EXT mng_gettickcount  MNG_DECL mng_getcb_gettickcount  (mng_handle hHandle);
+MNG_EXT mng_settimer      MNG_DECL mng_getcb_settimer      (mng_handle hHandle);
+
+/* see _setcb_ */
+#ifdef MNG_APP_CMS
+MNG_EXT mng_processgamma  MNG_DECL mng_getcb_processgamma  (mng_handle hHandle);
+MNG_EXT mng_processchroma MNG_DECL mng_getcb_processchroma (mng_handle hHandle);
+MNG_EXT mng_processsrgb   MNG_DECL mng_getcb_processsrgb   (mng_handle hHandle);
+MNG_EXT mng_processiccp   MNG_DECL mng_getcb_processiccp   (mng_handle hHandle);
+MNG_EXT mng_processarow   MNG_DECL mng_getcb_processarow   (mng_handle hHandle);
+#endif /* MNG_APP_CMS */
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Property set functions                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* Application data pointer */
+/* provided for application use; not used by the library */
+MNG_EXT mng_retcode MNG_DECL mng_set_userdata        (mng_handle        hHandle,
+                                                      mng_ptr           pUserdata);
+
+/* The style of the drawing- & background-canvas */
+/* only used for displaying images */
+/* both are initially set to 24-bit RGB (eg. 8-bit per channel) */
+MNG_EXT mng_retcode MNG_DECL mng_set_canvasstyle     (mng_handle        hHandle,
+                                                      mng_uint32        iStyle);
+MNG_EXT mng_retcode MNG_DECL mng_set_bkgdstyle       (mng_handle        hHandle,
+                                                      mng_uint32        iStyle);
+
+/* The default background color */
+/* only used if the getbkgdline callback is not defined */
+/* for initially painting the canvas and restoring (part of) the background */
+MNG_EXT mng_retcode MNG_DECL mng_set_bgcolor         (mng_handle        hHandle,
+                                                      mng_uint16        iRed,
+                                                      mng_uint16        iGreen,
+                                                      mng_uint16        iBlue);
+
+/* Indicates preferred use of the bKGD chunk for PNG images */
+MNG_EXT mng_retcode MNG_DECL mng_set_usebkgd         (mng_handle        hHandle,
+                                                      mng_bool          bUseBKGD);
+
+/* Indicates storage of read chunks */
+/* only useful if you #define mng_store_chunks */
+/* can be used to dynamically change storage management */
+MNG_EXT mng_retcode MNG_DECL mng_set_storechunks     (mng_handle        hHandle,
+                                                      mng_bool          bStorechunks);
+
+/* Indicates breaks requested when processing SAVE/SEEK */
+/* set this to let the app handle section breaks; the library will return
+   MNG_NEEDSECTIONWAIT return-codes for each SEEK chunk */
+MNG_EXT mng_retcode MNG_DECL mng_set_sectionbreaks   (mng_handle        hHandle,
+                                                      mng_bool          bSectionbreaks);
+
+/* Indicates storage of playback info (ON by default!) */
+/* can be used to turn off caching of playback info; this is useful to
+   specifically optimize MNG-video playback; note that if caching is turned off
+   LOOP chunks will be flagged as errors! TERM chunks will be ignored and only
+   passed to the processterm() callback if it is defined by the app; also, this
+   feature can only be used with mng_readdisplay(); mng_read(),
+   mng_display_reset() and mng_display_goxxxx() will return an error;
+   once this option is turned off it can't be turned on for the same stream!!! */
+MNG_EXT mng_retcode MNG_DECL mng_set_cacheplayback   (mng_handle        hHandle,
+                                                      mng_bool          bCacheplayback);
+
+/* Indicates automatic progressive refreshes for large images (ON by default!) */
+/* turn this off if you do not want intermittent painting while a large image
+   is being read. useful if the input-stream comes from a fast medium, such
+   as a local harddisk */
+MNG_EXT mng_retcode MNG_DECL mng_set_doprogressive   (mng_handle        hHandle,
+                                                      mng_bool          bDoProgressive);
+
+/* Indicates existence and required checking of the CRC in input streams,
+   and generation in output streams */
+/* !!!! Use this ONLY if you know what you are doing !!!! */
+/* The value is a combination of the following flags:
+   0x0000001 = CRC is present in the input stream
+   0x0000002 = CRC must be generated in the output stream
+   0x0000010 = CRC should be checked for ancillary chunks
+   0x0000020 = a faulty CRC for ancillary chunks generates a warning only
+   0x0000040 = a faulty CRC for ancillary chunks generates an error
+   0x0000100 = CRC should be checked for critical chunks
+   0x0000200 = a faulty CRC for critical chunks generates a warning only
+   0x0000400 = a faulty CRC for critical chunks generates an error
+
+   The default is 0x00000533 = CRC present in input streams; should be checked;
+                               warning for ancillary chunks; error for critical
+                               chunks; generate CRC for output streams
+
+   Note that some combinations are meaningless; eg. if the CRC is not present
+   it won't do any good to turn the checking flags on; if a checking flag
+   is off, it doesn't do any good to ask for generation of warnings or errors.
+   Also libmng will generate either an error or a warning, not both,
+   so if you specify both the default will be to generate an error!
+   The only useful combinations for input are 331, 551, 351, 531, 0, 301, 501
+   and optionally 031 and 051, but only checking ancillary chunks and not
+   critical chunks is generally not a very good idea!!!
+   If you've also writing these values should be combined with 0x02 if
+   CRC's are required in the output stream
+   */
+MNG_EXT mng_retcode MNG_DECL mng_set_crcmode         (mng_handle        hHandle,
+                                                      mng_uint32        iCrcmode);
+
+/* Color-management necessaries */
+/*
+    *************************************************************************
+                 !!!!!!!! THIS NEXT BIT IS IMPORTANT !!!!!!!!!
+    *************************************************************************
+
+    If you have defined MNG_FULL_CMS (and are using lcms), you will have to
+    think hard about the following routines.
+
+    lcms requires 2 profiles to work off the differences in the input-image
+    and the output-device. The ICC profile for the input-image will be
+    embedded within it to reflect its color-characteristics, but the output
+    profile depends on the output-device, which is something only *YOU* know
+    about. sRGB (standard RGB) is common for x86 compatible environments
+    (eg. Windows, Linux and some others)
+
+    If you are compiling for a sRGB compliant system you probably won't have
+    to do anything special. (unless you want to of course)
+
+    If you are compiling for a non-sRGB compliant system
+    (eg. SGI, Mac, Next, others...)
+    you *MUST* define a proper ICC profile for the generic output-device
+    associated with that platform.
+
+    In either event, you may also want to offer an option to your users to
+    set the profile manually, or, if you know how, set it from a
+    system-defined default.
+
+    TO RECAP: for sRGB systems (Windows, Linux) no action required!
+              for non-sRGB systems (SGI, Mac, Next) ACTION REQUIRED!
+
+    Please visit http://www.srgb.com, http://www.color.org and
+    http://www.littlecms.com for more info.
+
+    *************************************************************************
+                 !!!!!!!! THE BIT ABOVE IS IMPORTANT !!!!!!!!!
+    *************************************************************************
+*/
+/* mng_set_srgb tells libmng if it's running on a sRGB compliant system or not
+   the default is already set to MNG_TRUE */
+/* mng_set_outputprofile, mng_set_outputprofile2, mng_set_outputsrgb
+   are used to set the default profile describing the output-device
+   by default it is already initialized with an sRGB profile */
+/* mng_set_srgbprofile, mng_set_srgbprofile2, mng_set_srgbimplicit
+   are used to set the default profile describing a standard sRGB device
+   this is used when the input-image is tagged only as being sRGB, but the
+   output-device is defined as not being sRGB compliant
+   by default it is already initialized with a standard sRGB profile */
+#if defined(MNG_SUPPORT_DISPLAY)
+MNG_EXT mng_retcode MNG_DECL mng_set_srgb            (mng_handle        hHandle,
+                                                      mng_bool          bIssRGB);
+MNG_EXT mng_retcode MNG_DECL mng_set_outputprofile   (mng_handle        hHandle,
+                                                      mng_pchar         zFilename);
+MNG_EXT mng_retcode MNG_DECL mng_set_outputprofile2  (mng_handle        hHandle,
+                                                      mng_uint32        iProfilesize,
+                                                      mng_ptr           pProfile);
+MNG_EXT mng_retcode MNG_DECL mng_set_outputsrgb      (mng_handle        hHandle);
+MNG_EXT mng_retcode MNG_DECL mng_set_srgbprofile     (mng_handle        hHandle,
+                                                      mng_pchar         zFilename);
+MNG_EXT mng_retcode MNG_DECL mng_set_srgbprofile2    (mng_handle        hHandle,
+                                                      mng_uint32        iProfilesize,
+                                                      mng_ptr           pProfile);
+MNG_EXT mng_retcode MNG_DECL mng_set_srgbimplicit    (mng_handle        hHandle);
+#endif
+
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+/* Gamma settings */
+/* ... blabla (explain gamma processing a little; eg. formula & stuff) ... */
+MNG_EXT mng_retcode MNG_DECL mng_set_viewgamma       (mng_handle        hHandle,
+                                                      mng_float         dGamma);
+MNG_EXT mng_retcode MNG_DECL mng_set_displaygamma    (mng_handle        hHandle,
+                                                      mng_float         dGamma);
+MNG_EXT mng_retcode MNG_DECL mng_set_dfltimggamma    (mng_handle        hHandle,
+                                                      mng_float         dGamma);
+MNG_EXT mng_retcode MNG_DECL mng_set_viewgammaint    (mng_handle        hHandle,
+                                                      mng_uint32        iGamma);
+MNG_EXT mng_retcode MNG_DECL mng_set_displaygammaint (mng_handle        hHandle,
+                                                      mng_uint32        iGamma);
+MNG_EXT mng_retcode MNG_DECL mng_set_dfltimggammaint (mng_handle        hHandle,
+                                                      mng_uint32        iGamma);
+#endif
+
+#ifndef MNG_SKIP_MAXCANVAS
+/* Ultimate clipping size */
+/* used to limit extreme graphics from overloading the system */
+/* if a graphic exceeds these limits a warning is issued, which can
+   be ignored by the app (using the errorproc callback). in that case
+   the library will use these settings to clip the input graphic, and
+   the app's canvas must account for this */
+MNG_EXT mng_retcode MNG_DECL mng_set_maxcanvaswidth  (mng_handle        hHandle,
+                                                      mng_uint32        iMaxwidth);
+MNG_EXT mng_retcode MNG_DECL mng_set_maxcanvasheight (mng_handle        hHandle,
+                                                      mng_uint32        iMaxheight);
+MNG_EXT mng_retcode MNG_DECL mng_set_maxcanvassize   (mng_handle        hHandle,
+                                                      mng_uint32        iMaxwidth,
+                                                      mng_uint32        iMaxheight);
+#endif
+
+/* ZLIB default compression parameters */
+/* these are used when writing out chunks */
+/* they are also used when compressing PNG image-data or JNG alpha-data;
+   in this case you can set them just before calling mng_putimgdata_ihdr */
+/* set to your liking; usually the defaults will suffice though! */
+/* check the documentation for ZLIB for details on these parameters */
+#ifdef MNG_INCLUDE_ZLIB
+MNG_EXT mng_retcode MNG_DECL mng_set_zlib_level      (mng_handle        hHandle,
+                                                      mng_int32         iZlevel);
+MNG_EXT mng_retcode MNG_DECL mng_set_zlib_method     (mng_handle        hHandle,
+                                                      mng_int32         iZmethod);
+MNG_EXT mng_retcode MNG_DECL mng_set_zlib_windowbits (mng_handle        hHandle,
+                                                      mng_int32         iZwindowbits);
+MNG_EXT mng_retcode MNG_DECL mng_set_zlib_memlevel   (mng_handle        hHandle,
+                                                      mng_int32         iZmemlevel);
+MNG_EXT mng_retcode MNG_DECL mng_set_zlib_strategy   (mng_handle        hHandle,
+                                                      mng_int32         iZstrategy);
+
+MNG_EXT mng_retcode MNG_DECL mng_set_zlib_maxidat    (mng_handle        hHandle,
+                                                      mng_uint32        iMaxIDAT);
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* JNG default compression parameters (based on IJG code) */
+/* these are used when compressing JNG image-data; so you can set them
+   just before calling mng_putimgdata_jhdr */
+/* set to your liking; usually the defaults will suffice though! */
+/* check the documentation for IJGSRC6B for details on these parameters */
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_INCLUDE_IJG6B
+MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_dctmethod  (mng_handle        hHandle,
+                                                      mngjpeg_dctmethod eJPEGdctmethod);
+#endif
+MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_quality    (mng_handle        hHandle,
+                                                      mng_int32         iJPEGquality);
+MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_smoothing  (mng_handle        hHandle,
+                                                      mng_int32         iJPEGsmoothing);
+MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_progressive(mng_handle        hHandle,
+                                                      mng_bool          bJPEGprogressive);
+MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_optimized  (mng_handle        hHandle,
+                                                      mng_bool          bJPEGoptimized);
+
+MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_maxjdat    (mng_handle        hHandle,
+                                                      mng_uint32        iMaxJDAT);
+#endif /* MNG_INCLUDE_JNG */
+
+/* Suspension-mode setting */
+/* use this to activate the internal suspension-buffer to improve
+   read-suspension processing */
+/* TODO: write-suspension ??? */   
+#if defined(MNG_SUPPORT_READ)
+MNG_EXT mng_retcode MNG_DECL mng_set_suspensionmode  (mng_handle        hHandle,
+                                                      mng_bool          bSuspensionmode);
+#endif
+
+/* Speed setting */
+/* use this to influence the display-speed of animations */
+#if defined(MNG_SUPPORT_DISPLAY)
+MNG_EXT mng_retcode MNG_DECL mng_set_speed           (mng_handle        hHandle,
+                                                      mng_speedtype     iSpeed);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Property get functions                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* see _set_ */
+MNG_EXT mng_ptr     MNG_DECL mng_get_userdata        (mng_handle        hHandle);
+
+/* Network Graphic header details */
+/* these get filled once the graphics header is processed,
+   so they are available in the processheader callback; before that
+   they are zeroed out and imagetype is set to it_unknown */
+/* this might be a good point for the app to initialize the drawing-canvas! */
+/* note that some fields are only set for the first(!) header-chunk:
+   MNG/MHDR (imagetype = mng_it_mng) - ticks thru simplicity
+   PNG/IHDR (imagetype = mng_it_png) - bitdepth thru interlace
+   JNG/JHDR (imagetype = mng_it_jng) - bitdepth thru compression &
+                                       interlace thru alphainterlace */
+MNG_EXT mng_imgtype MNG_DECL mng_get_sigtype         (mng_handle        hHandle);
+MNG_EXT mng_imgtype MNG_DECL mng_get_imagetype       (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_imagewidth      (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_imageheight     (mng_handle        hHandle);
+
+MNG_EXT mng_uint32  MNG_DECL mng_get_ticks           (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_framecount      (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_layercount      (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_playtime        (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_simplicity      (mng_handle        hHandle);
+
+MNG_EXT mng_uint8   MNG_DECL mng_get_bitdepth        (mng_handle        hHandle);
+MNG_EXT mng_uint8   MNG_DECL mng_get_colortype       (mng_handle        hHandle);
+MNG_EXT mng_uint8   MNG_DECL mng_get_compression     (mng_handle        hHandle);
+MNG_EXT mng_uint8   MNG_DECL mng_get_filter          (mng_handle        hHandle);
+MNG_EXT mng_uint8   MNG_DECL mng_get_interlace       (mng_handle        hHandle);
+MNG_EXT mng_uint8   MNG_DECL mng_get_alphabitdepth   (mng_handle        hHandle);
+MNG_EXT mng_uint8   MNG_DECL mng_get_alphacompression(mng_handle        hHandle);
+MNG_EXT mng_uint8   MNG_DECL mng_get_alphafilter     (mng_handle        hHandle);
+MNG_EXT mng_uint8   MNG_DECL mng_get_alphainterlace  (mng_handle        hHandle);
+
+/* indicates the predicted alpha-depth required to properly display the image */
+/* gets set once the graphics header is processed and is available in the
+   processheader callback for any type of input-image (PNG, JNG or MNG) */
+/* possible values are 0,1,2,4,8,16
+   0  = no transparency required
+   1  = on/off transparency required (alpha-values are 0 or 2^bit_depth-1)
+   2+ = semi-transparency required (values will be scaled to the bitdepth of the
+                                    canvasstyle supplied by the application) */
+MNG_EXT mng_uint8   MNG_DECL mng_get_alphadepth      (mng_handle        hHandle);
+
+/* defines whether a refresh() callback is called for an interlace pass (PNG)
+   or progressive scan (JNG) */
+/* returns the interlace pass number for PNG or a fabricated pass number for JNG;
+   returns 0 in all other cases */
+/* only useful if the image_type = mng_it_png or mng_it_jng and if the image
+   is actually interlaced (PNG) or progressive (JNG) */
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_EXT mng_uint8   MNG_DECL mng_get_refreshpass     (mng_handle        hHandle);
+#endif
+
+/* see _set_ */
+MNG_EXT mng_uint32  MNG_DECL mng_get_canvasstyle     (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_bkgdstyle       (mng_handle        hHandle);
+
+/* see _set_ */
+MNG_EXT mng_retcode MNG_DECL mng_get_bgcolor         (mng_handle        hHandle,
+                                                      mng_uint16*       iRed,
+                                                      mng_uint16*       iGreen,
+                                                      mng_uint16*       iBlue);
+
+/* see _set_ */
+MNG_EXT mng_bool    MNG_DECL mng_get_usebkgd         (mng_handle        hHandle);
+
+/* see _set_ */
+MNG_EXT mng_bool    MNG_DECL mng_get_storechunks     (mng_handle        hHandle);
+
+/* see _set_ */
+MNG_EXT mng_bool    MNG_DECL mng_get_sectionbreaks   (mng_handle        hHandle);
+
+/* see _set_ */
+MNG_EXT mng_bool    MNG_DECL mng_get_cacheplayback   (mng_handle        hHandle);
+
+/* see _set_ */
+MNG_EXT mng_bool    MNG_DECL mng_get_doprogressive   (mng_handle        hHandle);
+
+/* see _set_ */
+MNG_EXT mng_uint32  MNG_DECL mng_get_crcmode         (mng_handle        hHandle);
+
+/* see _set_ */
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_FULL_CMS)
+MNG_EXT mng_bool    MNG_DECL mng_get_srgb            (mng_handle        hHandle);
+#endif
+
+/* see _set_ */
+MNG_EXT mng_float   MNG_DECL mng_get_viewgamma       (mng_handle        hHandle);
+MNG_EXT mng_float   MNG_DECL mng_get_displaygamma    (mng_handle        hHandle);
+MNG_EXT mng_float   MNG_DECL mng_get_dfltimggamma    (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_viewgammaint    (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_displaygammaint (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_dfltimggammaint (mng_handle        hHandle);
+
+#ifndef MNG_SKIP_MAXCANVAS
+/* see _set_ */
+MNG_EXT mng_uint32  MNG_DECL mng_get_maxcanvaswidth  (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_maxcanvasheight (mng_handle        hHandle);
+#endif
+
+/* see _set_ */
+#ifdef MNG_INCLUDE_ZLIB
+MNG_EXT mng_int32   MNG_DECL mng_get_zlib_level      (mng_handle        hHandle);
+MNG_EXT mng_int32   MNG_DECL mng_get_zlib_method     (mng_handle        hHandle);
+MNG_EXT mng_int32   MNG_DECL mng_get_zlib_windowbits (mng_handle        hHandle);
+MNG_EXT mng_int32   MNG_DECL mng_get_zlib_memlevel   (mng_handle        hHandle);
+MNG_EXT mng_int32   MNG_DECL mng_get_zlib_strategy   (mng_handle        hHandle);
+
+MNG_EXT mng_uint32  MNG_DECL mng_get_zlib_maxidat    (mng_handle        hHandle);
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* see _set_ */
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_INCLUDE_IJG6B
+MNG_EXT mngjpeg_dctmethod
+                    MNG_DECL mng_get_jpeg_dctmethod  (mng_handle        hHandle);
+#endif
+MNG_EXT mng_int32   MNG_DECL mng_get_jpeg_quality    (mng_handle        hHandle);
+MNG_EXT mng_int32   MNG_DECL mng_get_jpeg_smoothing  (mng_handle        hHandle);
+MNG_EXT mng_bool    MNG_DECL mng_get_jpeg_progressive(mng_handle        hHandle);
+MNG_EXT mng_bool    MNG_DECL mng_get_jpeg_optimized  (mng_handle        hHandle);
+
+MNG_EXT mng_uint32  MNG_DECL mng_get_jpeg_maxjdat    (mng_handle        hHandle);
+#endif /* MNG_INCLUDE_JNG */
+
+/* see _set_  */
+#if defined(MNG_SUPPORT_READ)
+MNG_EXT mng_bool    MNG_DECL mng_get_suspensionmode  (mng_handle        hHandle);
+#endif
+
+/* see _set_  */
+#if defined(MNG_SUPPORT_DISPLAY)
+MNG_EXT mng_speedtype
+                    MNG_DECL mng_get_speed           (mng_handle        hHandle);
+#endif
+
+/* Image-level */
+/* this can be used inside the processtext callback to determine the level of
+   text of the image being processed; the value 1 is returned for top-level
+   texts, and the value 2 for a text inside an embedded image inside a MNG */
+MNG_EXT mng_uint32  MNG_DECL mng_get_imagelevel      (mng_handle        hHandle);
+
+/* BACK info */
+/* can be used to retrieve the color & mandatory values for the last processed
+   BACK chunk of a MNG (will fail for other image-types);
+   if no BACK chunk was processed yet, it will return all zeroes */
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_EXT mng_retcode MNG_DECL mng_get_lastbackchunk   (mng_handle        hHandle,
+                                                      mng_uint16*       iRed,
+                                                      mng_uint16*       iGreen,
+                                                      mng_uint16*       iBlue,
+                                                      mng_uint8*        iMandatory);
+#endif
+
+/* SEEK info */
+/* can be used to retrieve the segmentname of the last processed SEEK chunk;
+   if no SEEK chunk was processed or its segmentname was empty, the function
+   will return an empty string; the provided buffer must be at least 80 bytes!! */
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_EXT mng_retcode MNG_DECL mng_get_lastseekname    (mng_handle        hHandle,
+                                                      mng_pchar         zSegmentname);
+#endif
+
+/* FRAM info */
+/* can be used to retrieve the current FRAM delay; this may be useful when
+   retrieving a stream of frames with their corresponding delays by "fake"
+   reading and displaying the file */
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_EXT mng_uint32 MNG_DECL mng_get_currframdelay    (mng_handle        hHandle);
+#endif
+
+/* Display status variables */
+/* these get filled & updated during display processing */
+/* starttime is the tickcount at the start of displaying the animation */
+/* runtime is the actual number of millisecs since the start of the animation */
+/* currentframe, currentlayer & currentplaytime indicate the current
+   frame/layer/playtime(msecs) in the animation (these keep increasing;
+   even after the animation loops back to the TERM chunk) */
+/* totalframes, totallayers & totalplaytime are filled after a complete run
+   of an animation (eg. at MEND); they are also valid after just reading the MNG */
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_EXT mng_uint32  MNG_DECL mng_get_starttime       (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_runtime         (mng_handle        hHandle);
+#ifndef MNG_NO_CURRENT_INFO
+MNG_EXT mng_uint32  MNG_DECL mng_get_currentframe    (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_currentlayer    (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_currentplaytime (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_totalframes     (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_totallayers     (mng_handle        hHandle);
+MNG_EXT mng_uint32  MNG_DECL mng_get_totalplaytime   (mng_handle        hHandle);
+#endif
+#endif
+
+/* Status variables */
+/* these indicate the internal state of the library */
+/* most indicate exactly what you would expect -
+   status_error:        true if the last function call returned an errorcode
+   status_reading:      true if the library is (still) reading an image
+   status_suspendbreak: true if the library has suspended for "I/O"
+   status_creating:     true if the library is in the middle of creating an image
+   status_writing:      true if the library is in the middle of writing an image
+   status_displaying:   true if the library is displaying an image
+   status_running:      true if display processing is active (eg. not frozen or reset)
+   status_timerbreak:   true if the library has suspended for a "timer-break"
+   status_dynamic:      true if the library encountered an evNT chunk in the MNG
+   status_runningevent: true if the library is processing an external event */
+/* eg. mng_readdisplay() will turn the reading, displaying and running status on;
+   when EOF is reached the reading status will be turned off */   
+MNG_EXT mng_bool    MNG_DECL mng_status_error        (mng_handle        hHandle);
+#ifdef MNG_SUPPORT_READ
+MNG_EXT mng_bool    MNG_DECL mng_status_reading      (mng_handle        hHandle);
+MNG_EXT mng_bool    MNG_DECL mng_status_suspendbreak (mng_handle        hHandle);
+#endif
+#ifdef MNG_SUPPORT_WRITE
+MNG_EXT mng_bool    MNG_DECL mng_status_creating     (mng_handle        hHandle);
+MNG_EXT mng_bool    MNG_DECL mng_status_writing      (mng_handle        hHandle);
+#endif
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_EXT mng_bool    MNG_DECL mng_status_displaying   (mng_handle        hHandle);
+MNG_EXT mng_bool    MNG_DECL mng_status_running      (mng_handle        hHandle);
+MNG_EXT mng_bool    MNG_DECL mng_status_timerbreak   (mng_handle        hHandle);
+#endif
+#ifdef MNG_SUPPORT_DYNAMICMNG
+MNG_EXT mng_bool    MNG_DECL mng_status_dynamic      (mng_handle        hHandle);
+MNG_EXT mng_bool    MNG_DECL mng_status_runningevent (mng_handle        hHandle);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Chunk access functions                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_ACCESS_CHUNKS
+
+/* ************************************************************************** */
+
+/* use this to iterate the stored chunks */
+/* requires MNG_ACCESS_CHUNKS & MNG_STORE_CHUNKS */
+/* starts from the supplied chunk-index-nr; the first chunk has index 0!! */
+MNG_EXT mng_retcode MNG_DECL mng_iterate_chunks      (mng_handle       hHandle,
+                                                      mng_uint32       iChunkseq,
+                                                      mng_iteratechunk fProc);
+
+/* use the next function inside your 'iteratechunk' callback to copy
+   the given chunk to a new mng you are creating */
+/* the 'out' handle should be in 'create' status! */
+#ifdef MNG_SUPPORT_WRITE
+MNG_EXT mng_retcode MNG_DECL mng_copy_chunk          (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_handle       hHandleOut);
+#endif
+
+/* ************************************************************************** */
+
+/* use these to get chunk data from within the callback in iterate_chunks */
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_ihdr       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iWidth,
+                                                      mng_uint32       *iHeight,
+                                                      mng_uint8        *iBitdepth,
+                                                      mng_uint8        *iColortype,
+                                                      mng_uint8        *iCompression,
+                                                      mng_uint8        *iFilter,
+                                                      mng_uint8        *iInterlace);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_plte       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iCount,
+                                                      mng_palette8     *aPalette);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_idat       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iRawlen,
+                                                      mng_ptr          *pRawdata);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_trns       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_bool         *bGlobal,
+                                                      mng_uint8        *iType,
+                                                      mng_uint32       *iCount,
+                                                      mng_uint8arr     *aAlphas,
+                                                      mng_uint16       *iGray,
+                                                      mng_uint16       *iRed,
+                                                      mng_uint16       *iGreen,
+                                                      mng_uint16       *iBlue,
+                                                      mng_uint32       *iRawlen,
+                                                      mng_uint8arr     *aRawdata);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_gama       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint32       *iGamma);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_chrm       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint32       *iWhitepointx,
+                                                      mng_uint32       *iWhitepointy,
+                                                      mng_uint32       *iRedx,
+                                                      mng_uint32       *iRedy,
+                                                      mng_uint32       *iGreenx,
+                                                      mng_uint32       *iGreeny,
+                                                      mng_uint32       *iBluex,
+                                                      mng_uint32       *iBluey);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_srgb       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint8        *iRenderingintent);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_iccp       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint32       *iNamesize,
+                                                      mng_pchar        *zName,
+                                                      mng_uint8        *iCompression,
+                                                      mng_uint32       *iProfilesize,
+                                                      mng_ptr          *pProfile);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_text       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iKeywordsize,
+                                                      mng_pchar        *zKeyword,
+                                                      mng_uint32       *iTextsize,
+                                                      mng_pchar        *zText);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_ztxt       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iKeywordsize,
+                                                      mng_pchar        *zKeyword,
+                                                      mng_uint8        *iCompression,
+                                                      mng_uint32       *iTextsize,
+                                                      mng_pchar        *zText);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_itxt       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iKeywordsize,
+                                                      mng_pchar        *zKeyword,
+                                                      mng_uint8        *iCompressionflag,
+                                                      mng_uint8        *iCompressionmethod,
+                                                      mng_uint32       *iLanguagesize,
+                                                      mng_pchar        *zLanguage,
+                                                      mng_uint32       *iTranslationsize,
+                                                      mng_pchar        *zTranslation,
+                                                      mng_uint32       *iTextsize,
+                                                      mng_pchar        *zText);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_bkgd       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint8        *iType,
+                                                      mng_uint8        *iIndex,
+                                                      mng_uint16       *iGray,
+                                                      mng_uint16       *iRed,
+                                                      mng_uint16       *iGreen,
+                                                      mng_uint16       *iBlue);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_phys       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint32       *iSizex,
+                                                      mng_uint32       *iSizey,
+                                                      mng_uint8        *iUnit);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_sbit       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint8        *iType,
+                                                      mng_uint8arr4    *aBits);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_splt       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint32       *iNamesize,
+                                                      mng_pchar        *zName,
+                                                      mng_uint8        *iSampledepth,
+                                                      mng_uint32       *iEntrycount,
+                                                      mng_ptr          *pEntries);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_hist       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iEntrycount,
+                                                      mng_uint16arr    *aEntries);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_time       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iYear,
+                                                      mng_uint8        *iMonth,
+                                                      mng_uint8        *iDay,
+                                                      mng_uint8        *iHour,
+                                                      mng_uint8        *iMinute,
+                                                      mng_uint8        *iSecond);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_mhdr       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iWidth,
+                                                      mng_uint32       *iHeight,
+                                                      mng_uint32       *iTicks,
+                                                      mng_uint32       *iLayercount,
+                                                      mng_uint32       *iFramecount,
+                                                      mng_uint32       *iPlaytime,
+                                                      mng_uint32       *iSimplicity);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_loop       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint8        *iLevel,
+                                                      mng_uint32       *iRepeat,
+                                                      mng_uint8        *iTermination,
+                                                      mng_uint32       *iItermin,
+                                                      mng_uint32       *iItermax,
+                                                      mng_uint32       *iCount,
+                                                      mng_uint32p      *pSignals);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_endl       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint8        *iLevel);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_defi       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iObjectid,
+                                                      mng_uint8        *iDonotshow,
+                                                      mng_uint8        *iConcrete,
+                                                      mng_bool         *bHasloca,
+                                                      mng_int32        *iXlocation,
+                                                      mng_int32        *iYlocation,
+                                                      mng_bool         *bHasclip,
+                                                      mng_int32        *iLeftcb,
+                                                      mng_int32        *iRightcb,
+                                                      mng_int32        *iTopcb,
+                                                      mng_int32        *iBottomcb);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_basi       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iWidth,
+                                                      mng_uint32       *iHeight,
+                                                      mng_uint8        *iBitdepth,
+                                                      mng_uint8        *iColortype,
+                                                      mng_uint8        *iCompression,
+                                                      mng_uint8        *iFilter,
+                                                      mng_uint8        *iInterlace,
+                                                      mng_uint16       *iRed,
+                                                      mng_uint16       *iGreen,
+                                                      mng_uint16       *iBlue,
+                                                      mng_uint16       *iAlpha,
+                                                      mng_uint8        *iViewable);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_clon       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iSourceid,
+                                                      mng_uint16       *iCloneid,
+                                                      mng_uint8        *iClonetype,
+                                                      mng_uint8        *iDonotshow,
+                                                      mng_uint8        *iConcrete,
+                                                      mng_bool         *bHasloca,
+                                                      mng_uint8        *iLocationtype,
+                                                      mng_int32        *iLocationx,
+                                                      mng_int32        *iLocationy);
+
+#ifndef MNG_SKIPCHUNK_PAST
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_past       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iDestid,
+                                                      mng_uint8        *iTargettype,
+                                                      mng_int32        *iTargetx,
+                                                      mng_int32        *iTargety,
+                                                      mng_uint32       *iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_past_src   (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint16       *iSourceid,
+                                                      mng_uint8        *iComposition,
+                                                      mng_uint8        *iOrientation,
+                                                      mng_uint8        *iOffsettype,
+                                                      mng_int32        *iOffsetx,
+                                                      mng_int32        *iOffsety,
+                                                      mng_uint8        *iBoundarytype,
+                                                      mng_int32        *iBoundaryl,
+                                                      mng_int32        *iBoundaryr,
+                                                      mng_int32        *iBoundaryt,
+                                                      mng_int32        *iBoundaryb);
+#endif
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_disc       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iCount,
+                                                      mng_uint16p      *pObjectids);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_back       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iRed,
+                                                      mng_uint16       *iGreen,
+                                                      mng_uint16       *iBlue,
+                                                      mng_uint8        *iMandatory,
+                                                      mng_uint16       *iImageid,
+                                                      mng_uint8        *iTile);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_fram       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint8        *iMode,
+                                                      mng_uint32       *iNamesize,
+                                                      mng_pchar        *zName,
+                                                      mng_uint8        *iChangedelay,
+                                                      mng_uint8        *iChangetimeout,
+                                                      mng_uint8        *iChangeclipping,
+                                                      mng_uint8        *iChangesyncid,
+                                                      mng_uint32       *iDelay,
+                                                      mng_uint32       *iTimeout,
+                                                      mng_uint8        *iBoundarytype,
+                                                      mng_int32        *iBoundaryl,
+                                                      mng_int32        *iBoundaryr,
+                                                      mng_int32        *iBoundaryt,
+                                                      mng_int32        *iBoundaryb,
+                                                      mng_uint32       *iCount,
+                                                      mng_uint32p      *pSyncids);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_move       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iFirstid,
+                                                      mng_uint16       *iLastid,
+                                                      mng_uint8        *iMovetype,
+                                                      mng_int32        *iMovex,
+                                                      mng_int32        *iMovey);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_clip       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iFirstid,
+                                                      mng_uint16       *iLastid,
+                                                      mng_uint8        *iCliptype,
+                                                      mng_int32        *iClipl,
+                                                      mng_int32        *iClipr,
+                                                      mng_int32        *iClipt,
+                                                      mng_int32        *iClipb);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_show       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint16       *iFirstid,
+                                                      mng_uint16       *iLastid,
+                                                      mng_uint8        *iMode);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_term       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint8        *iTermaction,
+                                                      mng_uint8        *iIteraction,
+                                                      mng_uint32       *iDelay,
+                                                      mng_uint32       *iItermax);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_save       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint8        *iOffsettype,
+                                                      mng_uint32       *iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_save_entry (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint8        *iEntrytype,
+                                                      mng_uint32arr2   *iOffset,
+                                                      mng_uint32arr2   *iStarttime,
+                                                      mng_uint32       *iLayernr,
+                                                      mng_uint32       *iFramenr,
+                                                      mng_uint32       *iNamesize,
+                                                      mng_pchar        *zName);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_seek       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iNamesize,
+                                                      mng_pchar        *zName);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_expi       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iSnapshotid,
+                                                      mng_uint32       *iNamesize,
+                                                      mng_pchar        *zName);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_fpri       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint8        *iDeltatype,
+                                                      mng_uint8        *iPriority);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_need       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iKeywordssize,
+                                                      mng_pchar        *zKeywords);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_phyg       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_bool         *bEmpty,
+                                                      mng_uint32       *iSizex,
+                                                      mng_uint32       *iSizey,
+                                                      mng_uint8        *iUnit);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_jhdr       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iWidth,
+                                                      mng_uint32       *iHeight,
+                                                      mng_uint8        *iColortype,
+                                                      mng_uint8        *iImagesampledepth,
+                                                      mng_uint8        *iImagecompression,
+                                                      mng_uint8        *iImageinterlace,
+                                                      mng_uint8        *iAlphasampledepth,
+                                                      mng_uint8        *iAlphacompression,
+                                                      mng_uint8        *iAlphafilter,
+                                                      mng_uint8        *iAlphainterlace);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_jdat       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iRawlen,
+                                                      mng_ptr          *pRawdata);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_jdaa       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iRawlen,
+                                                      mng_ptr          *pRawdata);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_dhdr       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iObjectid,
+                                                      mng_uint8        *iImagetype,
+                                                      mng_uint8        *iDeltatype,
+                                                      mng_uint32       *iBlockwidth,
+                                                      mng_uint32       *iBlockheight,
+                                                      mng_uint32       *iBlockx,
+                                                      mng_uint32       *iBlocky);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_prom       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint8        *iColortype,
+                                                      mng_uint8        *iSampledepth,
+                                                      mng_uint8        *iFilltype);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_pplt       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint8        *iDeltatype,
+                                                      mng_uint32       *iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_pplt_entry (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint16       *iRed,
+                                                      mng_uint16       *iGreen,
+                                                      mng_uint16       *iBlue,
+                                                      mng_uint16       *iAlpha,
+                                                      mng_bool         *bUsed);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_drop       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iCount,
+                                                      mng_chunkidp     *pChunknames);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_dbyk       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_chunkid      *iChunkname,
+                                                      mng_uint8        *iPolarity,
+                                                      mng_uint32       *iKeywordssize,
+                                                      mng_pchar        *zKeywords);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_ordr       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_ordr_entry (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       iEntry,
+                                                      mng_chunkid      *iChunkname,
+                                                      mng_uint8        *iOrdertype);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_magn       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint16       *iFirstid,
+                                                      mng_uint16       *iLastid,
+                                                      mng_uint16       *iMethodX,
+                                                      mng_uint16       *iMX,
+                                                      mng_uint16       *iMY,
+                                                      mng_uint16       *iML,
+                                                      mng_uint16       *iMR,
+                                                      mng_uint16       *iMT,
+                                                      mng_uint16       *iMB,
+                                                      mng_uint16       *iMethodY);
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_mpng       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iFramewidth,
+                                                      mng_uint32       *iFrameheight,
+                                                      mng_uint16       *iNumplays,
+                                                      mng_uint16       *iTickspersec,
+                                                      mng_uint8        *iCompressionmethod,
+                                                      mng_uint32       *iCount);
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_mpng_frame (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint32       *iX,
+                                                      mng_uint32       *iY,
+                                                      mng_uint32       *iWidth,
+                                                      mng_uint32       *iHeight,
+                                                      mng_int32        *iXoffset,
+                                                      mng_int32        *iYoffset,
+                                                      mng_uint16       *iTicks);
+#endif
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_evnt       (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       *iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_evnt_entry (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint8        *iEventtype,
+                                                      mng_uint8        *iMasktype,
+                                                      mng_int32        *iLeft,
+                                                      mng_int32        *iRight,
+                                                      mng_int32        *iTop,
+                                                      mng_int32        *iBottom,
+                                                      mng_uint16       *iObjectid,
+                                                      mng_uint8        *iIndex,
+                                                      mng_uint32       *iSegmentnamesize,
+                                                      mng_pchar        *zSegmentname);
+
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_unknown    (mng_handle       hHandle,
+                                                      mng_handle       hChunk,
+                                                      mng_chunkid      *iChunkname,
+                                                      mng_uint32       *iRawlen,
+                                                      mng_ptr          *pRawdata);
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_WRITE_PROCS
+
+/* use these to create new chunks at the end of the chunk-list */
+/* requires at least MNG_ACCESS_CHUNKS (MNG_SUPPORT_WRITE may be nice too) */
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_ihdr       (mng_handle       hHandle,
+                                                      mng_uint32       iWidth,
+                                                      mng_uint32       iHeight,
+                                                      mng_uint8        iBitdepth,
+                                                      mng_uint8        iColortype,
+                                                      mng_uint8        iCompression,
+                                                      mng_uint8        iFilter,
+                                                      mng_uint8        iInterlace);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_plte       (mng_handle       hHandle,
+                                                      mng_uint32       iCount,
+                                                      mng_palette8     aPalette);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_idat       (mng_handle       hHandle,
+                                                      mng_uint32       iRawlen,
+                                                      mng_ptr          pRawdata);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_iend       (mng_handle       hHandle);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_trns       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_bool         bGlobal,
+                                                      mng_uint8        iType,
+                                                      mng_uint32       iCount,
+                                                      mng_uint8arr     aAlphas,
+                                                      mng_uint16       iGray,
+                                                      mng_uint16       iRed,
+                                                      mng_uint16       iGreen,
+                                                      mng_uint16       iBlue,
+                                                      mng_uint32       iRawlen,
+                                                      mng_uint8arr     aRawdata);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_gama       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint32       iGamma);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_chrm       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint32       iWhitepointx,
+                                                      mng_uint32       iWhitepointy,
+                                                      mng_uint32       iRedx,
+                                                      mng_uint32       iRedy,
+                                                      mng_uint32       iGreenx,
+                                                      mng_uint32       iGreeny,
+                                                      mng_uint32       iBluex,
+                                                      mng_uint32       iBluey);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_srgb       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint8        iRenderingintent);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_iccp       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint32       iNamesize,
+                                                      mng_pchar        zName,
+                                                      mng_uint8        iCompression,
+                                                      mng_uint32       iProfilesize,
+                                                      mng_ptr          pProfile);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_text       (mng_handle       hHandle,
+                                                      mng_uint32       iKeywordsize,
+                                                      mng_pchar        zKeyword,
+                                                      mng_uint32       iTextsize,
+                                                      mng_pchar        zText);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_ztxt       (mng_handle       hHandle,
+                                                      mng_uint32       iKeywordsize,
+                                                      mng_pchar        zKeyword,
+                                                      mng_uint8        iCompression,
+                                                      mng_uint32       iTextsize,
+                                                      mng_pchar        zText);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_itxt       (mng_handle       hHandle,
+                                                      mng_uint32       iKeywordsize,
+                                                      mng_pchar        zKeyword,
+                                                      mng_uint8        iCompressionflag,
+                                                      mng_uint8        iCompressionmethod,
+                                                      mng_uint32       iLanguagesize,
+                                                      mng_pchar        zLanguage,
+                                                      mng_uint32       iTranslationsize,
+                                                      mng_pchar        zTranslation,
+                                                      mng_uint32       iTextsize,
+                                                      mng_pchar        zText);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_bkgd       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint8        iType,
+                                                      mng_uint8        iIndex,
+                                                      mng_uint16       iGray,
+                                                      mng_uint16       iRed,
+                                                      mng_uint16       iGreen,
+                                                      mng_uint16       iBlue);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_phys       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint32       iSizex,
+                                                      mng_uint32       iSizey,
+                                                      mng_uint8        iUnit);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_sbit       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint8        iType,
+                                                      mng_uint8arr4    aBits);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_splt       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint32       iNamesize,
+                                                      mng_pchar        zName,
+                                                      mng_uint8        iSampledepth,
+                                                      mng_uint32       iEntrycount,
+                                                      mng_ptr          pEntries);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_hist       (mng_handle       hHandle,
+                                                      mng_uint32       iEntrycount,
+                                                      mng_uint16arr    aEntries);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_time       (mng_handle       hHandle,
+                                                      mng_uint16       iYear,
+                                                      mng_uint8        iMonth,
+                                                      mng_uint8        iDay,
+                                                      mng_uint8        iHour,
+                                                      mng_uint8        iMinute,
+                                                      mng_uint8        iSecond);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_mhdr       (mng_handle       hHandle,
+                                                      mng_uint32       iWidth,
+                                                      mng_uint32       iHeight,
+                                                      mng_uint32       iTicks,
+                                                      mng_uint32       iLayercount,
+                                                      mng_uint32       iFramecount,
+                                                      mng_uint32       iPlaytime,
+                                                      mng_uint32       iSimplicity);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_mend       (mng_handle       hHandle);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_loop       (mng_handle       hHandle,
+                                                      mng_uint8        iLevel,
+                                                      mng_uint32       iRepeat,
+                                                      mng_uint8        iTermination,
+                                                      mng_uint32       iItermin,
+                                                      mng_uint32       iItermax,
+                                                      mng_uint32       iCount,
+                                                      mng_uint32p      pSignals);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_endl       (mng_handle       hHandle,
+                                                      mng_uint8        iLevel);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_defi       (mng_handle       hHandle,
+                                                      mng_uint16       iObjectid,
+                                                      mng_uint8        iDonotshow,
+                                                      mng_uint8        iConcrete,
+                                                      mng_bool         bHasloca,
+                                                      mng_int32        iXlocation,
+                                                      mng_int32        iYlocation,
+                                                      mng_bool         bHasclip,
+                                                      mng_int32        iLeftcb,
+                                                      mng_int32        iRightcb,
+                                                      mng_int32        iTopcb,
+                                                      mng_int32        iBottomcb);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_basi       (mng_handle       hHandle,
+                                                      mng_uint32       iWidth,
+                                                      mng_uint32       iHeight,
+                                                      mng_uint8        iBitdepth,
+                                                      mng_uint8        iColortype,
+                                                      mng_uint8        iCompression,
+                                                      mng_uint8        iFilter,
+                                                      mng_uint8        iInterlace,
+                                                      mng_uint16       iRed,
+                                                      mng_uint16       iGreen,
+                                                      mng_uint16       iBlue,
+                                                      mng_uint16       iAlpha,
+                                                      mng_uint8        iViewable);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_clon       (mng_handle       hHandle,
+                                                      mng_uint16       iSourceid,
+                                                      mng_uint16       iCloneid,
+                                                      mng_uint8        iClonetype,
+                                                      mng_uint8        iDonotshow,
+                                                      mng_uint8        iConcrete,
+                                                      mng_bool         bHasloca,
+                                                      mng_uint8        iLocationtype,
+                                                      mng_int32        iLocationx,
+                                                      mng_int32        iLocationy);
+
+#ifndef MNG_SKIPCHUNK_PAST
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_past       (mng_handle       hHandle,
+                                                      mng_uint16       iDestid,
+                                                      mng_uint8        iTargettype,
+                                                      mng_int32        iTargetx,
+                                                      mng_int32        iTargety,
+                                                      mng_uint32       iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_past_src   (mng_handle       hHandle,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint16       iSourceid,
+                                                      mng_uint8        iComposition,
+                                                      mng_uint8        iOrientation,
+                                                      mng_uint8        iOffsettype,
+                                                      mng_int32        iOffsetx,
+                                                      mng_int32        iOffsety,
+                                                      mng_uint8        iBoundarytype,
+                                                      mng_int32        iBoundaryl,
+                                                      mng_int32        iBoundaryr,
+                                                      mng_int32        iBoundaryt,
+                                                      mng_int32        iBoundaryb);
+#endif
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_disc       (mng_handle       hHandle,
+                                                      mng_uint32       iCount,
+                                                      mng_uint16p      pObjectids);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_back       (mng_handle       hHandle,
+                                                      mng_uint16       iRed,
+                                                      mng_uint16       iGreen,
+                                                      mng_uint16       iBlue,
+                                                      mng_uint8        iMandatory,
+                                                      mng_uint16       iImageid,
+                                                      mng_uint8        iTile);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_fram       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint8        iMode,
+                                                      mng_uint32       iNamesize,
+                                                      mng_pchar        zName,
+                                                      mng_uint8        iChangedelay,
+                                                      mng_uint8        iChangetimeout,
+                                                      mng_uint8        iChangeclipping,
+                                                      mng_uint8        iChangesyncid,
+                                                      mng_uint32       iDelay,
+                                                      mng_uint32       iTimeout,
+                                                      mng_uint8        iBoundarytype,
+                                                      mng_int32        iBoundaryl,
+                                                      mng_int32        iBoundaryr,
+                                                      mng_int32        iBoundaryt,
+                                                      mng_int32        iBoundaryb,
+                                                      mng_uint32       iCount,
+                                                      mng_uint32p      pSyncids);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_move       (mng_handle       hHandle,
+                                                      mng_uint16       iFirstid,
+                                                      mng_uint16       iLastid,
+                                                      mng_uint8        iMovetype,
+                                                      mng_int32        iMovex,
+                                                      mng_int32        iMovey);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_clip       (mng_handle       hHandle,
+                                                      mng_uint16       iFirstid,
+                                                      mng_uint16       iLastid,
+                                                      mng_uint8        iCliptype,
+                                                      mng_int32        iClipl,
+                                                      mng_int32        iClipr,
+                                                      mng_int32        iClipt,
+                                                      mng_int32        iClipb);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_show       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint16       iFirstid,
+                                                      mng_uint16       iLastid,
+                                                      mng_uint8        iMode);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_term       (mng_handle       hHandle,
+                                                      mng_uint8        iTermaction,
+                                                      mng_uint8        iIteraction,
+                                                      mng_uint32       iDelay,
+                                                      mng_uint32       iItermax);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_save       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint8        iOffsettype,
+                                                      mng_uint32       iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_save_entry (mng_handle       hHandle,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint8        iEntrytype,
+                                                      mng_uint32arr2   iOffset,
+                                                      mng_uint32arr2   iStarttime,
+                                                      mng_uint32       iLayernr,
+                                                      mng_uint32       iFramenr,
+                                                      mng_uint32       iNamesize,
+                                                      mng_pchar        zName);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_seek       (mng_handle       hHandle,
+                                                      mng_uint32       iNamesize,
+                                                      mng_pchar        zName);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_expi       (mng_handle       hHandle,
+                                                      mng_uint16       iSnapshotid,
+                                                      mng_uint32       iNamesize,
+                                                      mng_pchar        zName);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_fpri       (mng_handle       hHandle,
+                                                      mng_uint8        iDeltatype,
+                                                      mng_uint8        iPriority);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_need       (mng_handle       hHandle,
+                                                      mng_uint32       iKeywordssize,
+                                                      mng_pchar        zKeywords);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_phyg       (mng_handle       hHandle,
+                                                      mng_bool         bEmpty,
+                                                      mng_uint32       iSizex,
+                                                      mng_uint32       iSizey,
+                                                      mng_uint8        iUnit);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_jhdr       (mng_handle       hHandle,
+                                                      mng_uint32       iWidth,
+                                                      mng_uint32       iHeight,
+                                                      mng_uint8        iColortype,
+                                                      mng_uint8        iImagesampledepth,
+                                                      mng_uint8        iImagecompression,
+                                                      mng_uint8        iImageinterlace,
+                                                      mng_uint8        iAlphasampledepth,
+                                                      mng_uint8        iAlphacompression,
+                                                      mng_uint8        iAlphafilter,
+                                                      mng_uint8        iAlphainterlace);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_jdat       (mng_handle       hHandle,
+                                                      mng_uint32       iRawlen,
+                                                      mng_ptr          pRawdata);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_jdaa       (mng_handle       hHandle,
+                                                      mng_uint32       iRawlen,
+                                                      mng_ptr          pRawdata);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_jsep       (mng_handle       hHandle);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_dhdr       (mng_handle       hHandle,
+                                                      mng_uint16       iObjectid,
+                                                      mng_uint8        iImagetype,
+                                                      mng_uint8        iDeltatype,
+                                                      mng_uint32       iBlockwidth,
+                                                      mng_uint32       iBlockheight,
+                                                      mng_uint32       iBlockx,
+                                                      mng_uint32       iBlocky);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_prom       (mng_handle       hHandle,
+                                                      mng_uint8        iColortype,
+                                                      mng_uint8        iSampledepth,
+                                                      mng_uint8        iFilltype);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_ipng       (mng_handle       hHandle);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_pplt       (mng_handle       hHandle,
+                                                      mng_uint8        iDeltatype,
+                                                      mng_uint32       iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_pplt_entry (mng_handle       hHandle,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint16       iRed,
+                                                      mng_uint16       iGreen,
+                                                      mng_uint16       iBlue,
+                                                      mng_uint16       iAlpha);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_jpng       (mng_handle       hHandle);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_drop       (mng_handle       hHandle,
+                                                      mng_uint32       iCount,
+                                                      mng_chunkidp     pChunknames);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_dbyk       (mng_handle       hHandle,
+                                                      mng_chunkid      iChunkname,
+                                                      mng_uint8        iPolarity,
+                                                      mng_uint32       iKeywordssize,
+                                                      mng_pchar        zKeywords);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_ordr       (mng_handle       hHandle,
+                                                      mng_uint32       iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_ordr_entry (mng_handle       hHandle,
+                                                      mng_uint32       iEntry,
+                                                      mng_chunkid      iChunkname,
+                                                      mng_uint8        iOrdertype);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_magn       (mng_handle       hHandle,
+                                                      mng_uint16       iFirstid,
+                                                      mng_uint16       iLastid,
+                                                      mng_uint16       iMethodX,
+                                                      mng_uint16       iMX,
+                                                      mng_uint16       iMY,
+                                                      mng_uint16       iML,
+                                                      mng_uint16       iMR,
+                                                      mng_uint16       iMT,
+                                                      mng_uint16       iMB,
+                                                      mng_uint16       iMethodY);
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_mpng       (mng_handle       hHandle,
+                                                      mng_uint32       iFramewidth,
+                                                      mng_uint32       iFrameheight,
+                                                      mng_uint16       iNumplays,
+                                                      mng_uint16       iTickspersec,
+                                                      mng_uint8        iCompressionmethod,
+                                                      mng_uint32       iCount);
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_mpng_frame (mng_handle       hHandle,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint32       iX,
+                                                      mng_uint32       iY,
+                                                      mng_uint32       iWidth,
+                                                      mng_uint32       iHeight,
+                                                      mng_int32        iXoffset,
+                                                      mng_int32        iYoffset,
+                                                      mng_uint16       iTicks);
+#endif
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_evnt       (mng_handle       hHandle,
+                                                      mng_uint32       iCount);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_evnt_entry (mng_handle       hHandle,
+                                                      mng_uint32       iEntry,
+                                                      mng_uint8        iEventtype,
+                                                      mng_uint8        iMasktype,
+                                                      mng_int32        iLeft,
+                                                      mng_int32        iRight,
+                                                      mng_int32        iTop,
+                                                      mng_int32        iBottom,
+                                                      mng_uint16       iObjectid,
+                                                      mng_uint8        iIndex,
+                                                      mng_uint32       iSegmentnamesize,
+                                                      mng_pchar        zSegmentname);
+
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_unknown    (mng_handle       hHandle,
+                                                      mng_chunkid      iChunkname,
+                                                      mng_uint32       iRawlen,
+                                                      mng_ptr          pRawdata);
+
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+
+/* ************************************************************************** */
+
+/* use these functions to access the actual image-data in stored chunks,
+   as opposed to the IDAT/JDAT data */
+/* to get accurate pixel-data the canvasstyle should seriously reflect the
+   bitdepth/colortype combination of the preceding IHDR/JHDR/BASI/DHDR;
+   all input can be converted to rgb(a)8 (rgb(a)16 for 16-bit images), but
+   there are only limited conversions back (see below for putimgdata)  */
+
+/* call this function if you want to extract the nth image from the list;
+   the first image is designated seqnr 0! */
+/* this function finds the IHDR/JHDR/BASI/DHDR with the appropriate seqnr,
+   starting from the beginning of the chunk-list; this may tend to get a little
+   slow for animations with a large number of chunks for images near the end */
+/* supplying a seqnr past the last image in the animation will return with
+   an errorcode */   
+MNG_EXT mng_retcode MNG_DECL mng_getimgdata_seq      (mng_handle        hHandle,
+                                                      mng_uint32        iSeqnr,
+                                                      mng_uint32        iCanvasstyle,
+                                                      mng_getcanvasline fGetcanvasline);
+
+/* both the following functions will search forward to find the first IDAT/JDAT,
+   and then traverse back to find the start of the image (IHDR,JHDR,DHDR,BASI);
+   note that this is very fast compared to decoding the IDAT/JDAT, so there's
+   not really a need for optimization; either can be called from the
+   iterate_chunks callback when a IHDR/JHDR is encountered; for BASI/DHDR there
+   may not be real image-data so it's wisest to keep iterating till the IEND,
+   and then call either of these functions if necessary (remember the correct seqnr!) */
+
+/* call this function if you want to extract the image starting at or after the nth
+   position in the chunk-list; this number is returned in the iterate_chunks callback */
+MNG_EXT mng_retcode MNG_DECL mng_getimgdata_chunkseq (mng_handle        hHandle,
+                                                      mng_uint32        iSeqnr,
+                                                      mng_uint32        iCanvasstyle,
+                                                      mng_getcanvasline fGetcanvasline);
+
+/* call this function if you want to extract the image starting at or after the
+   indicated chunk; the handle of a chunk is returned in the iterate_chunks callback */
+MNG_EXT mng_retcode MNG_DECL mng_getimgdata_chunk    (mng_handle        hHandle,
+                                                      mng_handle        hChunk,
+                                                      mng_uint32        iCanvasstyle,
+                                                      mng_getcanvasline fGetcanvasline);
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_WRITE_PROCS
+
+/* use the following functions to add image-data to the list of stored chunks */
+/* note that this only adds the IDAT or JDAT chunks and no others; you must call
+   one of these functions after you 'put' the initial chunks of the image and
+   before you 'put' the closing chunks */
+/* the canvasstyle should seriously reflect the bitdepth/colortype combination;
+   eg. bitdepth=16 would expect a 16-bit canvasstyle,
+   colortype=g or ga would expect a gray or gray+alpha style respectively
+   and so on, and so forth ...
+   (nb. the number of conversions will be extremely limited for the moment!) */
+
+MNG_EXT mng_retcode MNG_DECL mng_putimgdata_ihdr     (mng_handle        hHandle,
+                                                      mng_uint32        iWidth,
+                                                      mng_uint32        iHeight,
+                                                      mng_uint8         iColortype,
+                                                      mng_uint8         iBitdepth,
+                                                      mng_uint8         iCompression,
+                                                      mng_uint8         iFilter,
+                                                      mng_uint8         iInterlace,
+                                                      mng_uint32        iCanvasstyle,
+                                                      mng_getcanvasline fGetcanvasline);
+
+MNG_EXT mng_retcode MNG_DECL mng_putimgdata_jhdr     (mng_handle        hHandle,
+                                                      mng_uint32        iWidth,
+                                                      mng_uint32        iHeight,
+                                                      mng_uint8         iColortype,
+                                                      mng_uint8         iBitdepth,
+                                                      mng_uint8         iCompression,
+                                                      mng_uint8         iInterlace,
+                                                      mng_uint8         iAlphaBitdepth,
+                                                      mng_uint8         iAlphaCompression,
+                                                      mng_uint8         iAlphaFilter,
+                                                      mng_uint8         iAlphaInterlace,
+                                                      mng_uint32        iCanvasstyle,
+                                                      mng_getcanvasline fGetcanvasline);
+
+/* ************************************************************************** */
+
+/* use the following functions to set the framecount/layercount/playtime or
+   simplicity of an animation you are creating; this may be useful if these
+   variables are calculated during the creation-process */
+
+MNG_EXT mng_retcode MNG_DECL mng_updatemngheader     (mng_handle        hHandle,
+                                                      mng_uint32        iFramecount,
+                                                      mng_uint32        iLayercount,
+                                                      mng_uint32        iPlaytime);
+
+MNG_EXT mng_retcode MNG_DECL mng_updatemngsimplicity (mng_handle        hHandle,
+                                                      mng_uint32        iSimplicity);
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+
+#endif /* MNG_ACCESS_CHUNKS */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Error-code structure                                                   * */
+/* *                                                                        * */
+/* * 0b0000 00xx xxxx xxxx - basic errors; severity 9 (environment)         * */
+/* * 0b0000 01xx xxxx xxxx - chunk errors; severity 9 (image induced)       * */
+/* * 0b0000 10xx xxxx xxxx - severity 5 errors (application induced)        * */
+/* * 0b0001 00xx xxxx xxxx - severity 2 warnings (recoverable)              * */
+/* * 0b0010 00xx xxxx xxxx - severity 1 warnings (recoverable)              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_NOERROR          (mng_retcode)0    /* er.. indicates all's well   */
+
+#define MNG_OUTOFMEMORY      (mng_retcode)1    /* oops, buy some megabytes!   */
+#define MNG_INVALIDHANDLE    (mng_retcode)2    /* call mng_initialize first   */
+#define MNG_NOCALLBACK       (mng_retcode)3    /* set the callbacks please    */
+#define MNG_UNEXPECTEDEOF    (mng_retcode)4    /* what'd ya do with the data? */
+#define MNG_ZLIBERROR        (mng_retcode)5    /* zlib burped                 */
+#define MNG_JPEGERROR        (mng_retcode)6    /* jpglib complained           */
+#define MNG_LCMSERROR        (mng_retcode)7    /* little cms stressed out     */
+#define MNG_NOOUTPUTPROFILE  (mng_retcode)8    /* no output-profile defined   */
+#define MNG_NOSRGBPROFILE    (mng_retcode)9    /* no sRGB-profile defined     */
+#define MNG_BUFOVERFLOW      (mng_retcode)10   /* zlib output-buffer overflow */
+#define MNG_FUNCTIONINVALID  (mng_retcode)11   /* ay, totally inappropriate   */
+#define MNG_OUTPUTERROR      (mng_retcode)12   /* disk full ?                 */
+#define MNG_JPEGBUFTOOSMALL  (mng_retcode)13   /* can't handle buffer overflow*/
+#define MNG_NEEDMOREDATA     (mng_retcode)14   /* I'm hungry, give me more    */
+#define MNG_NEEDTIMERWAIT    (mng_retcode)15   /* Sleep a while then wake me  */
+#define MNG_NEEDSECTIONWAIT  (mng_retcode)16   /* just processed a SEEK       */
+#define MNG_LOOPWITHCACHEOFF (mng_retcode)17   /* LOOP when playback info off */
+
+#define MNG_DLLNOTLOADED     (mng_retcode)99   /* late binding failed         */
+
+#define MNG_APPIOERROR       (mng_retcode)901  /* application I/O error       */
+#define MNG_APPTIMERERROR    (mng_retcode)902  /* application timing error    */
+#define MNG_APPCMSERROR      (mng_retcode)903  /* application CMS error       */
+#define MNG_APPMISCERROR     (mng_retcode)904  /* application other error     */
+#define MNG_APPTRACEABORT    (mng_retcode)905  /* application aborts on trace */
+
+#define MNG_INTERNALERROR    (mng_retcode)999  /* internal inconsistancy      */
+
+#define MNG_INVALIDSIG       (mng_retcode)1025 /* invalid graphics file       */
+#define MNG_INVALIDCRC       (mng_retcode)1027 /* crc check failed            */
+#define MNG_INVALIDLENGTH    (mng_retcode)1028 /* chunklength mystifies me    */
+#define MNG_SEQUENCEERROR    (mng_retcode)1029 /* invalid chunk sequence      */
+#define MNG_CHUNKNOTALLOWED  (mng_retcode)1030 /* completely out-of-place     */
+#define MNG_MULTIPLEERROR    (mng_retcode)1031 /* only one occurence allowed  */
+#define MNG_PLTEMISSING      (mng_retcode)1032 /* indexed-color requires PLTE */
+#define MNG_IDATMISSING      (mng_retcode)1033 /* IHDR-block requires IDAT    */
+#define MNG_CANNOTBEEMPTY    (mng_retcode)1034 /* must contain some data      */
+#define MNG_GLOBALLENGTHERR  (mng_retcode)1035 /* global data incorrect       */
+#define MNG_INVALIDBITDEPTH  (mng_retcode)1036 /* bitdepth out-of-range       */
+#define MNG_INVALIDCOLORTYPE (mng_retcode)1037 /* colortype out-of-range      */
+#define MNG_INVALIDCOMPRESS  (mng_retcode)1038 /* compression method invalid  */
+#define MNG_INVALIDFILTER    (mng_retcode)1039 /* filter method invalid       */
+#define MNG_INVALIDINTERLACE (mng_retcode)1040 /* interlace method invalid    */
+#define MNG_NOTENOUGHIDAT    (mng_retcode)1041 /* ran out of compressed data  */
+#define MNG_PLTEINDEXERROR   (mng_retcode)1042 /* palette-index out-of-range  */
+#define MNG_NULLNOTFOUND     (mng_retcode)1043 /* couldn't find null-separator*/
+#define MNG_KEYWORDNULL      (mng_retcode)1044 /* keyword cannot be empty     */
+#define MNG_OBJECTUNKNOWN    (mng_retcode)1045 /* the object can't be found   */
+#define MNG_OBJECTEXISTS     (mng_retcode)1046 /* the object already exists   */
+#define MNG_TOOMUCHIDAT      (mng_retcode)1047 /* got too much compressed data*/
+#define MNG_INVSAMPLEDEPTH   (mng_retcode)1048 /* sampledepth out-of-range    */
+#define MNG_INVOFFSETSIZE    (mng_retcode)1049 /* invalid offset-size         */
+#define MNG_INVENTRYTYPE     (mng_retcode)1050 /* invalid entry-type          */
+#define MNG_ENDWITHNULL      (mng_retcode)1051 /* may not end with NULL       */
+#define MNG_INVIMAGETYPE     (mng_retcode)1052 /* invalid image_type          */
+#define MNG_INVDELTATYPE     (mng_retcode)1053 /* invalid delta_type          */
+#define MNG_INVALIDINDEX     (mng_retcode)1054 /* index-value invalid         */
+#define MNG_TOOMUCHJDAT      (mng_retcode)1055 /* got too much compressed data*/
+#define MNG_JPEGPARMSERR     (mng_retcode)1056 /* JHDR/JPEG parms do not match*/
+#define MNG_INVFILLMETHOD    (mng_retcode)1057 /* invalid fill_method         */
+#define MNG_OBJNOTCONCRETE   (mng_retcode)1058 /* object must be concrete     */
+#define MNG_TARGETNOALPHA    (mng_retcode)1059 /* object has no alpha-channel */
+#define MNG_MNGTOOCOMPLEX    (mng_retcode)1060 /* can't handle complexity     */
+#define MNG_UNKNOWNCRITICAL  (mng_retcode)1061 /* unknown critical chunk found*/
+#define MNG_UNSUPPORTEDNEED  (mng_retcode)1062 /* nEED requirement unsupported*/
+#define MNG_INVALIDDELTA     (mng_retcode)1063 /* Delta operation illegal     */
+#define MNG_INVALIDMETHOD    (mng_retcode)1064 /* invalid MAGN method         */
+#define MNG_IMPROBABLELENGTH (mng_retcode)1065 /* impropable chunk length     */
+#define MNG_INVALIDBLOCK     (mng_retcode)1066 /* invalid delta block         */
+#define MNG_INVALIDEVENT     (mng_retcode)1067 /* invalid event_type          */
+#define MNG_INVALIDMASK      (mng_retcode)1068 /* invalid mask_type           */
+#define MNG_NOMATCHINGLOOP   (mng_retcode)1069 /* ENDL without matching LOOP  */
+#define MNG_SEEKNOTFOUND     (mng_retcode)1070 /* EvNT points to unknown SEEK */
+#define MNG_OBJNOTABSTRACT   (mng_retcode)1071 /* object must be abstract     */
+#define MNG_TERMSEQERROR     (mng_retcode)1072 /* TERM in wrong place         */
+#define MNG_INVALIDFIELDVAL  (mng_retcode)1073 /* invalid fieldvalue (generic)*/
+#define MNG_INVALIDWIDTH     (mng_retcode)1074 /* invalid frame/image width   */
+#define MNG_INVALIDHEIGHT    (mng_retcode)1075 /* invalid frame/image height  */
+
+#define MNG_INVALIDCNVSTYLE  (mng_retcode)2049 /* can't make anything of this */
+#define MNG_WRONGCHUNK       (mng_retcode)2050 /* accessing the wrong chunk   */
+#define MNG_INVALIDENTRYIX   (mng_retcode)2051 /* accessing the wrong entry   */
+#define MNG_NOHEADER         (mng_retcode)2052 /* must have had header first  */
+#define MNG_NOCORRCHUNK      (mng_retcode)2053 /* can't find parent chunk     */
+#define MNG_NOMHDR           (mng_retcode)2054 /* no MNG header available     */
+
+#define MNG_IMAGETOOLARGE    (mng_retcode)4097 /* input-image way too big     */
+#define MNG_NOTANANIMATION   (mng_retcode)4098 /* file not a MNG              */
+#define MNG_FRAMENRTOOHIGH   (mng_retcode)4099 /* frame-nr out-of-range       */
+#define MNG_LAYERNRTOOHIGH   (mng_retcode)4100 /* layer-nr out-of-range       */
+#define MNG_PLAYTIMETOOHIGH  (mng_retcode)4101 /* playtime out-of-range       */
+#define MNG_FNNOTIMPLEMENTED (mng_retcode)4102 /* function not yet available  */
+
+#define MNG_IMAGEFROZEN      (mng_retcode)8193 /* stopped displaying          */
+
+#define MNG_LCMS_NOHANDLE    1                 /* LCMS returned NULL handle   */
+#define MNG_LCMS_NOMEM       2                 /* LCMS returned NULL gammatab */
+#define MNG_LCMS_NOTRANS     3                 /* LCMS returned NULL transform*/
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Canvas styles                                                         * */
+/* *                                                                        * */
+/* *  Note that the intentions are pretty darn good, but that the focus     * */
+/* *  is currently on 8-bit color support                                   * */
+/* *                                                                        * */
+/* *  The RGB8_A8 style is defined for apps that require a separate         * */
+/* *  canvas for the color-planes and the alpha-plane (eg. mozilla)         * */
+/* *  This requires for the app to supply the "getalphaline" callback!!!    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_CANVAS_RGB8      0x00000000L
+#define MNG_CANVAS_RGBA8     0x00001000L
+#define MNG_CANVAS_RGBA8_PM  0x00009000L
+#define MNG_CANVAS_ARGB8     0x00003000L
+#define MNG_CANVAS_ARGB8_PM  0x0000B000L
+#define MNG_CANVAS_RGB8_A8   0x00005000L
+#define MNG_CANVAS_BGR8      0x00000001L
+#define MNG_CANVAS_BGRX8     0x00010001L
+#define MNG_CANVAS_BGRA8     0x00001001L
+#define MNG_CANVAS_BGRA8PM   0x00009001L         /* backward compatibility */
+#define MNG_CANVAS_BGRA8_PM  0x00009001L
+#define MNG_CANVAS_ABGR8     0x00003001L
+#define MNG_CANVAS_ABGR8_PM  0x0000B001L
+#define MNG_CANVAS_RGB16     0x00000100L         /* not supported yet */
+#define MNG_CANVAS_RGBA16    0x00001100L         /* not supported yet */
+#define MNG_CANVAS_ARGB16    0x00003100L         /* not supported yet */
+#define MNG_CANVAS_BGR16     0x00000101L         /* not supported yet */
+#define MNG_CANVAS_BGRA16    0x00001101L         /* not supported yet */
+#define MNG_CANVAS_ABGR16    0x00003101L         /* not supported yet */
+#define MNG_CANVAS_GRAY8     0x00000002L         /* not supported yet */
+#define MNG_CANVAS_GRAY16    0x00000102L         /* not supported yet */
+#define MNG_CANVAS_GRAYA8    0x00001002L         /* not supported yet */
+#define MNG_CANVAS_GRAYA16   0x00001102L         /* not supported yet */
+#define MNG_CANVAS_AGRAY8    0x00003002L         /* not supported yet */
+#define MNG_CANVAS_AGRAY16   0x00003102L         /* not supported yet */
+#define MNG_CANVAS_DX15      0x00000003L         /* not supported yet */
+#define MNG_CANVAS_DX16      0x00000004L         /* not supported yet */
+
+#define MNG_CANVAS_RGB565    0x00000005L
+#define MNG_CANVAS_RGBA565   0x00001005L
+#define MNG_CANVAS_BGR565    0x00000006L
+#define MNG_CANVAS_BGRA565   0x00001006L
+#define MNG_CANVAS_BGR565_A8 0x00004006L
+
+#define MNG_CANVAS_RGB555    0x00000007L
+#define MNG_CANVAS_BGR555    0x00000008L
+
+#define MNG_CANVAS_PIXELTYPE(C)  (C & 0x000000FFL)
+#define MNG_CANVAS_BITDEPTH(C)   (C & 0x00000100L)
+#define MNG_CANVAS_HASALPHA(C)   (C & 0x00001000L)
+#define MNG_CANVAS_ALPHAFIRST(C) (C & 0x00002000L)
+#define MNG_CANVAS_ALPHASEPD(C)  (C & 0x00004000L)
+#define MNG_CANVAS_ALPHAPM(C)    (C & 0x00008000L)
+#define MNG_CANVAS_HASFILLER(C)  (C & 0x00010000L)
+
+#define MNG_CANVAS_RGB(C)        (MNG_CANVAS_PIXELTYPE (C) == 0)
+#define MNG_CANVAS_BGR(C)        (MNG_CANVAS_PIXELTYPE (C) == 1)
+#define MNG_CANVAS_GRAY(C)       (MNG_CANVAS_PIXELTYPE (C) == 2)
+#define MNG_CANVAS_DIRECTX15(C)  (MNG_CANVAS_PIXELTYPE (C) == 3)
+#define MNG_CANVAS_DIRECTX16(C)  (MNG_CANVAS_PIXELTYPE (C) == 4)
+#define MNG_CANVAS_RGB_565(C)    (MNG_CANVAS_PIXELTYPE (C) == 5)
+#define MNG_CANVAS_BGR_565(C)    (MNG_CANVAS_PIXELTYPE (C) == 6)
+#define MNG_CANVAS_8BIT(C)       (!MNG_CANVAS_BITDEPTH (C))
+#define MNG_CANVAS_16BIT(C)      (MNG_CANVAS_BITDEPTH (C))
+#define MNG_CANVAS_PIXELFIRST(C) (!MNG_CANVAS_ALPHAFIRST (C))
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Chunk names (idea adapted from libpng 1.1.0 - png.h)                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_UINT_HUH  0x40404040L
+
+#define MNG_UINT_BACK 0x4241434bL
+#define MNG_UINT_BASI 0x42415349L
+#define MNG_UINT_CLIP 0x434c4950L
+#define MNG_UINT_CLON 0x434c4f4eL
+#define MNG_UINT_DBYK 0x4442594bL
+#define MNG_UINT_DEFI 0x44454649L
+#define MNG_UINT_DHDR 0x44484452L
+#define MNG_UINT_DISC 0x44495343L
+#define MNG_UINT_DROP 0x44524f50L
+#define MNG_UINT_ENDL 0x454e444cL
+#define MNG_UINT_FRAM 0x4652414dL
+#define MNG_UINT_IDAT 0x49444154L
+#define MNG_UINT_IEND 0x49454e44L
+#define MNG_UINT_IHDR 0x49484452L
+#define MNG_UINT_IJNG 0x494a4e47L
+#define MNG_UINT_IPNG 0x49504e47L
+#define MNG_UINT_JDAA 0x4a444141L
+#define MNG_UINT_JDAT 0x4a444154L
+#define MNG_UINT_JHDR 0x4a484452L
+#define MNG_UINT_JSEP 0x4a534550L
+#define MNG_UINT_JdAA 0x4a644141L
+#define MNG_UINT_LOOP 0x4c4f4f50L
+#define MNG_UINT_MAGN 0x4d41474eL
+#define MNG_UINT_MEND 0x4d454e44L
+#define MNG_UINT_MHDR 0x4d484452L
+#define MNG_UINT_MOVE 0x4d4f5645L
+#define MNG_UINT_ORDR 0x4f524452L
+#define MNG_UINT_PAST 0x50415354L
+#define MNG_UINT_PLTE 0x504c5445L
+#define MNG_UINT_PPLT 0x50504c54L
+#define MNG_UINT_PROM 0x50524f4dL
+#define MNG_UINT_SAVE 0x53415645L
+#define MNG_UINT_SEEK 0x5345454bL
+#define MNG_UINT_SHOW 0x53484f57L
+#define MNG_UINT_TERM 0x5445524dL
+#define MNG_UINT_adAT 0x61644154L
+#define MNG_UINT_ahDR 0x61684452L
+#define MNG_UINT_bKGD 0x624b4744L
+#define MNG_UINT_cHRM 0x6348524dL
+#define MNG_UINT_eXPI 0x65585049L
+#define MNG_UINT_evNT 0x65764e54L
+#define MNG_UINT_fPRI 0x66505249L
+#define MNG_UINT_gAMA 0x67414d41L
+#define MNG_UINT_hIST 0x68495354L
+#define MNG_UINT_iCCP 0x69434350L
+#define MNG_UINT_iTXt 0x69545874L
+#define MNG_UINT_mpNG 0x6d704e47L
+#define MNG_UINT_nEED 0x6e454544L
+#define MNG_UINT_oFFs 0x6f464673L
+#define MNG_UINT_pCAL 0x7043414cL
+#define MNG_UINT_pHYg 0x70444167L
+#define MNG_UINT_pHYs 0x70485973L
+#define MNG_UINT_sBIT 0x73424954L
+#define MNG_UINT_sCAL 0x7343414cL
+#define MNG_UINT_sPLT 0x73504c54L
+#define MNG_UINT_sRGB 0x73524742L
+#define MNG_UINT_tEXt 0x74455874L
+#define MNG_UINT_tIME 0x74494d45L
+#define MNG_UINT_tRNS 0x74524e53L
+#define MNG_UINT_zTXt 0x7a545874L
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Chunk property values                                                 * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_BITDEPTH_1                   1       /* IHDR, BASI, JHDR, PROM */
+#define MNG_BITDEPTH_2                   2
+#define MNG_BITDEPTH_4                   4
+#define MNG_BITDEPTH_8                   8       /* sPLT */
+#define MNG_BITDEPTH_16                 16
+
+#define MNG_COLORTYPE_GRAY               0       /* IHDR, BASI, PROM */
+#define MNG_COLORTYPE_RGB                2
+#define MNG_COLORTYPE_INDEXED            3
+#define MNG_COLORTYPE_GRAYA              4
+#define MNG_COLORTYPE_RGBA               6
+
+#define MNG_COMPRESSION_DEFLATE          0       /* IHDR, zTXt, iTXt, iCCP,
+                                                    BASI, JHDR */
+
+#define MNG_FILTER_ADAPTIVE              0       /* IHDR, BASI, JHDR */
+/* #define MNG_FILTER_NO_ADAPTIVE           1
+#define MNG_FILTER_NO_DIFFERING          0
+#define MNG_FILTER_DIFFERING             0x40
+#define MNG_FILTER_MASK                  (MNG_FILTER_NO_ADAPTIVE | MNG_FILTER_DIFFERING) */
+#ifdef FILTER192
+#define MNG_FILTER_DIFFERING             0xC0
+#endif
+#ifdef FILTER193
+#define MNG_FILTER_NOFILTER              0xC1
+#endif
+
+#define MNG_INTERLACE_NONE               0       /* IHDR, BASI, JHDR */
+#define MNG_INTERLACE_ADAM7              1
+
+#define MNG_FILTER_NONE                  0       /* IDAT */
+#define MNG_FILTER_SUB                   1
+#define MNG_FILTER_UP                    2
+#define MNG_FILTER_AVERAGE               3
+#define MNG_FILTER_PAETH                 4
+
+#define MNG_INTENT_PERCEPTUAL            0       /* sRGB */
+#define MNG_INTENT_RELATIVECOLORIMETRIC  1
+#define MNG_INTENT_SATURATION            2
+#define MNG_INTENT_ABSOLUTECOLORIMETRIC  3
+                                                 /* tEXt, zTXt, iTXt */
+#define MNG_TEXT_TITLE                   "Title"
+#define MNG_TEXT_AUTHOR                  "Author"
+#define MNG_TEXT_DESCRIPTION             "Description"
+#define MNG_TEXT_COPYRIGHT               "Copyright"
+#define MNG_TEXT_CREATIONTIME            "Creation Time"
+#define MNG_TEXT_SOFTWARE                "Software"
+#define MNG_TEXT_DISCLAIMER              "Disclaimer"
+#define MNG_TEXT_WARNING                 "Warning"
+#define MNG_TEXT_SOURCE                  "Source"
+#define MNG_TEXT_COMMENT                 "Comment"
+
+#define MNG_FLAG_UNCOMPRESSED            0       /* iTXt */
+#define MNG_FLAG_COMPRESSED              1
+
+#define MNG_UNIT_UNKNOWN                 0       /* pHYs, pHYg */
+#define MNG_UNIT_METER                   1
+                                                 /* MHDR */
+#define MNG_SIMPLICITY_VALID             0x00000001
+#define MNG_SIMPLICITY_SIMPLEFEATURES    0x00000002
+#define MNG_SIMPLICITY_COMPLEXFEATURES   0x00000004
+#define MNG_SIMPLICITY_TRANSPARENCY      0x00000008
+#define MNG_SIMPLICITY_JNG               0x00000010
+#define MNG_SIMPLICITY_DELTAPNG          0x00000020
+
+#define MNG_TERMINATION_DECODER_NC       0       /* LOOP */
+#define MNG_TERMINATION_USER_NC          1
+#define MNG_TERMINATION_EXTERNAL_NC      2
+#define MNG_TERMINATION_DETERMINISTIC_NC 3
+#define MNG_TERMINATION_DECODER_C        4
+#define MNG_TERMINATION_USER_C           5
+#define MNG_TERMINATION_EXTERNAL_C       6
+#define MNG_TERMINATION_DETERMINISTIC_C  7
+
+#define MNG_DONOTSHOW_VISIBLE            0       /* DEFI */
+#define MNG_DONOTSHOW_NOTVISIBLE         1
+
+#define MNG_ABSTRACT                     0       /* DEFI */
+#define MNG_CONCRETE                     1
+
+#define MNG_NOTVIEWABLE                  0       /* BASI */
+#define MNG_VIEWABLE                     1
+
+#define MNG_FULL_CLONE                   0       /* CLON */
+#define MNG_PARTIAL_CLONE                1
+#define MNG_RENUMBER                     2
+
+#define MNG_CONCRETE_ASPARENT            0       /* CLON */
+#define MNG_CONCRETE_MAKEABSTRACT        1
+
+#define MNG_LOCATION_ABSOLUTE            0       /* CLON, MOVE */
+#define MNG_LOCATION_RELATIVE            1
+
+#ifndef MNG_SKIPCHUNK_PAST
+#define MNG_TARGET_ABSOLUTE              0       /* PAST */
+#define MNG_TARGET_RELATIVE_SAMEPAST     1
+#define MNG_TARGET_RELATIVE_PREVPAST     2
+
+#define MNG_COMPOSITE_OVER               0       /* PAST */
+#define MNG_COMPOSITE_REPLACE            1
+#define MNG_COMPOSITE_UNDER              2
+
+#define MNG_ORIENTATION_SAME             0       /* PAST */
+#define MNG_ORIENTATION_180DEG           2
+#define MNG_ORIENTATION_FLIPHORZ         4
+#define MNG_ORIENTATION_FLIPVERT         6
+#define MNG_ORIENTATION_TILED            8
+
+#define MNG_OFFSET_ABSOLUTE              0       /* PAST */
+#define MNG_OFFSET_RELATIVE              1
+#endif
+
+#define MNG_BOUNDARY_ABSOLUTE            0       /* PAST, FRAM */
+#define MNG_BOUNDARY_RELATIVE            1
+
+#define MNG_BACKGROUNDCOLOR_MANDATORY    0x01    /* BACK */
+#define MNG_BACKGROUNDIMAGE_MANDATORY    0x02    /* BACK */
+
+#define MNG_BACKGROUNDIMAGE_NOTILE       0       /* BACK */
+#define MNG_BACKGROUNDIMAGE_TILE         1
+
+#define MNG_FRAMINGMODE_NOCHANGE         0       /* FRAM */
+#define MNG_FRAMINGMODE_1                1
+#define MNG_FRAMINGMODE_2                2
+#define MNG_FRAMINGMODE_3                3
+#define MNG_FRAMINGMODE_4                4
+
+#define MNG_CHANGEDELAY_NO               0       /* FRAM */
+#define MNG_CHANGEDELAY_NEXTSUBFRAME     1
+#define MNG_CHANGEDELAY_DEFAULT          2
+
+#define MNG_CHANGETIMOUT_NO              0       /* FRAM */
+#define MNG_CHANGETIMOUT_DETERMINISTIC_1 1
+#define MNG_CHANGETIMOUT_DETERMINISTIC_2 2
+#define MNG_CHANGETIMOUT_DECODER_1       3
+#define MNG_CHANGETIMOUT_DECODER_2       4
+#define MNG_CHANGETIMOUT_USER_1          5
+#define MNG_CHANGETIMOUT_USER_2          6
+#define MNG_CHANGETIMOUT_EXTERNAL_1      7
+#define MNG_CHANGETIMOUT_EXTERNAL_2      8
+
+#define MNG_CHANGECLIPPING_NO            0       /* FRAM */
+#define MNG_CHANGECLIPPING_NEXTSUBFRAME  1
+#define MNG_CHANGECLIPPING_DEFAULT       2
+
+#define MNG_CHANGESYNCID_NO              0       /* FRAM */
+#define MNG_CHANGESYNCID_NEXTSUBFRAME    1
+#define MNG_CHANGESYNCID_DEFAULT         2
+
+#define MNG_CLIPPING_ABSOLUTE            0       /* CLIP */
+#define MNG_CLIPPING_RELATIVE            1
+
+#define MNG_SHOWMODE_0                   0       /* SHOW */
+#define MNG_SHOWMODE_1                   1
+#define MNG_SHOWMODE_2                   2
+#define MNG_SHOWMODE_3                   3
+#define MNG_SHOWMODE_4                   4
+#define MNG_SHOWMODE_5                   5
+#define MNG_SHOWMODE_6                   6
+#define MNG_SHOWMODE_7                   7
+
+#define MNG_TERMACTION_LASTFRAME         0       /* TERM */
+#define MNG_TERMACTION_CLEAR             1
+#define MNG_TERMACTION_FIRSTFRAME        2
+#define MNG_TERMACTION_REPEAT            3
+
+#define MNG_ITERACTION_LASTFRAME         0       /* TERM */
+#define MNG_ITERACTION_CLEAR             1
+#define MNG_ITERACTION_FIRSTFRAME        2
+
+#define MNG_SAVEOFFSET_4BYTE             4       /* SAVE */
+#define MNG_SAVEOFFSET_8BYTE             8
+
+#define MNG_SAVEENTRY_SEGMENTFULL        0       /* SAVE */
+#define MNG_SAVEENTRY_SEGMENT            1
+#define MNG_SAVEENTRY_SUBFRAME           2
+#define MNG_SAVEENTRY_EXPORTEDIMAGE      3
+
+#define MNG_PRIORITY_ABSOLUTE            0       /* fPRI */
+#define MNG_PRIORITY_RELATIVE            1
+
+#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 MNG_IMAGETYPE_UNKNOWN            0       /* DHDR */
+#define MNG_IMAGETYPE_PNG                1
+#define MNG_IMAGETYPE_JNG                2
+
+#define MNG_DELTATYPE_REPLACE            0       /* DHDR */
+#define MNG_DELTATYPE_BLOCKPIXELADD      1
+#define MNG_DELTATYPE_BLOCKALPHAADD      2
+#define MNG_DELTATYPE_BLOCKCOLORADD      3
+#define MNG_DELTATYPE_BLOCKPIXELREPLACE  4
+#define MNG_DELTATYPE_BLOCKALPHAREPLACE  5
+#define MNG_DELTATYPE_BLOCKCOLORREPLACE  6
+#define MNG_DELTATYPE_NOCHANGE           7
+
+#define MNG_FILLMETHOD_LEFTBITREPLICATE  0       /* PROM */
+#define MNG_FILLMETHOD_ZEROFILL          1
+
+#define MNG_DELTATYPE_REPLACERGB         0       /* PPLT */
+#define MNG_DELTATYPE_DELTARGB           1
+#define MNG_DELTATYPE_REPLACEALPHA       2
+#define MNG_DELTATYPE_DELTAALPHA         3
+#define MNG_DELTATYPE_REPLACERGBA        4
+#define MNG_DELTATYPE_DELTARGBA          5
+
+#define MNG_POLARITY_ONLY                0       /* DBYK */
+#define MNG_POLARITY_ALLBUT              1
+
+#define MNG_EVENT_NONE                   0       /* evNT */
+#define MNG_EVENT_MOUSEENTER             1
+#define MNG_EVENT_MOUSEMOVE              2
+#define MNG_EVENT_MOUSEEXIT              3
+#define MNG_EVENT_MOUSEDOWN              4
+#define MNG_EVENT_MOUSEUP                5
+
+#define MNG_MASK_NONE                    0       /* evNT */
+#define MNG_MASK_BOX                     1
+#define MNG_MASK_OBJECT                  2
+#define MNG_MASK_OBJECTIX                3
+#define MNG_MASK_BOXOBJECT               4
+#define MNG_MASK_BOXOBJECTIX             5
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Processtext callback types                                            * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_TYPE_TEXT 0
+#define MNG_TYPE_ZTXT 1
+#define MNG_TYPE_ITXT 2
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  CRC processing masks                                                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_CRC_INPUT              0x0000000f
+#define MNG_CRC_INPUT_NONE         0x00000000
+#define MNG_CRC_INPUT_PRESENT      0x00000001
+#define MNG_CRC_OUTPUT             0x000000f0
+#define MNG_CRC_OUTPUT_NONE        0x00000000
+#define MNG_CRC_OUTPUT_GENERATE    0x00000020
+#define MNG_CRC_OUTPUT_DUMMY       0x00000040
+#define MNG_CRC_ANCILLARY          0x00000f00
+#define MNG_CRC_ANCILLARY_IGNORE   0x00000000
+#define MNG_CRC_ANCILLARY_DISCARD  0x00000100
+#define MNG_CRC_ANCILLARY_WARNING  0x00000200
+#define MNG_CRC_ANCILLARY_ERROR    0x00000300
+#define MNG_CRC_CRITICAL           0x0000f000
+#define MNG_CRC_CRITICAL_IGNORE    0x00000000
+#define MNG_CRC_CRITICAL_WARNING   0x00002000
+#define MNG_CRC_CRITICAL_ERROR     0x00003000
+#define MNG_CRC_DEFAULT            0x00002121
+
+/* ************************************************************************** */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _libmng_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_callback_xs.c b/files/Source/LibMNG/libmng_callback_xs.c
new file mode 100644
index 0000000..ff1a22a
--- /dev/null
+++ b/files/Source/LibMNG/libmng_callback_xs.c
@@ -0,0 +1,1239 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_callback_xs.c      copyright (c) 2000-2004 G.Juyn   * */
+/* * version   : 1.0.9                                                      * */
+/* *                                                                        * */
+/* * purpose   : callback get/set interface (implementation)                * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the callback get/set functions           * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - fixed calling convention                                 * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/31/2000 - G.Juyn                                * */
+/* *             - fixed up punctuation (contribution by Tim Rowley)        * */
+/* *             0.5.2 - 06/02/2000 - G.Juyn                                * */
+/* *             - added getalphaline callback for RGB8_A8 canvasstyle      * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added callbacks for SAVE/SEEK processing                 * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - added support for nEED                                   * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added callback to process non-critical unknown chunks    * */
+/* *                                                                        * */
+/* *             1.0.1 - 02/08/2001 - G.Juyn                                * */
+/* *             - added MEND processing callback                           * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added processterm callback                               * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G. R-P                                * */
+/* *             - added SKIPCHUNK feature                                  * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/10/2004 - G.R-P                                 * */
+/* *             - added conditionals around openstream/closestream         * */
+/* *             1.0.7 - 03/19/2004 - G.R-P                                 * */
+/* *             - fixed typo (MNG_SKIPCHUNK_SAVE -> MNG_SKIPCHUNK_nEED     * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/10/2004 - G.Juyn                                * */
+/* *             - added data-push mechanisms for specialized decoders      * */
+/* *                                                                        * */
+/* *             1.0.9 - 09/18/2004 - G.R-P.                                * */
+/* *             - added two SKIPCHUNK_TERM conditionals                    * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Callback set functions                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_INTERNAL_MEMMNGMT
+mng_retcode MNG_DECL mng_setcb_memalloc (mng_handle   hHandle,
+                                         mng_memalloc fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_MEMALLOC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fMemalloc = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_MEMALLOC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INTERNAL_MEMMNGMT */
+
+/* ************************************************************************** */
+
+#ifndef MNG_INTERNAL_MEMMNGMT
+mng_retcode MNG_DECL mng_setcb_memfree (mng_handle  hHandle,
+                                        mng_memfree fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_MEMFREE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fMemfree = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_MEMFREE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INTERNAL_MEMMNGMT */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_setcb_releasedata (mng_handle      hHandle,
+                                            mng_releasedata fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_RELEASEDATA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fReleasedata = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_RELEASEDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+mng_retcode MNG_DECL mng_setcb_openstream (mng_handle     hHandle,
+                                           mng_openstream fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_OPENSTREAM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fOpenstream = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_OPENSTREAM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+mng_retcode MNG_DECL mng_setcb_closestream (mng_handle      hHandle,
+                                            mng_closestream fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_CLOSESTREAM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fClosestream = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_CLOSESTREAM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_setcb_readdata (mng_handle   hHandle,
+                                         mng_readdata fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_READDATA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fReaddata = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_READDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_setcb_writedata (mng_handle    hHandle,
+                                          mng_writedata fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_WRITEDATA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fWritedata = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_WRITEDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_setcb_errorproc (mng_handle    hHandle,
+                                          mng_errorproc fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_ERRORPROC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fErrorproc = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_ERRORPROC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_TRACE
+mng_retcode MNG_DECL mng_setcb_traceproc (mng_handle    hHandle,
+                                          mng_traceproc fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_TRACEPROC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fTraceproc = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_TRACEPROC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_TRACE */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_setcb_processheader (mng_handle        hHandle,
+                                              mng_processheader fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSHEADER, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcessheader = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSHEADER, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_tEXt
+mng_retcode MNG_DECL mng_setcb_processtext (mng_handle      hHandle,
+                                            mng_processtext fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSTEXT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcesstext = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSTEXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode MNG_DECL mng_setcb_processsave (mng_handle      hHandle,
+                                            mng_processsave fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSAVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcesssave = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_retcode MNG_DECL mng_setcb_processseek (mng_handle      hHandle,
+                                            mng_processseek fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSEEK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcessseek = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSEEK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_nEED
+mng_retcode MNG_DECL mng_setcb_processneed (mng_handle      hHandle,
+                                            mng_processneed fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSNEED, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcessneed = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSNEED, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_setcb_processmend (mng_handle      hHandle,
+                                            mng_processmend fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSMEND, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcessmend = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSMEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_setcb_processunknown (mng_handle         hHandle,
+                                               mng_processunknown fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSUNKNOWN, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcessunknown = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSUNKNOWN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_TERM
+mng_retcode MNG_DECL mng_setcb_processterm (mng_handle      hHandle,
+                                            mng_processterm fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSTERM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcessterm = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSTERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_setcb_getcanvasline (mng_handle        hHandle,
+                                              mng_getcanvasline fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETCANVASLINE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fGetcanvasline = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETCANVASLINE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_setcb_getbkgdline (mng_handle      hHandle,
+                                            mng_getbkgdline fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETBKGDLINE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fGetbkgdline = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETBKGDLINE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_setcb_getalphaline (mng_handle       hHandle,
+                                             mng_getalphaline fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETALPHALINE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fGetalphaline = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETALPHALINE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_setcb_refresh (mng_handle  hHandle,
+                                        mng_refresh fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_REFRESH, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fRefresh = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_REFRESH, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_setcb_gettickcount (mng_handle       hHandle,
+                                             mng_gettickcount fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETTICKCOUNT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fGettickcount = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETTICKCOUNT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_setcb_settimer (mng_handle   hHandle,
+                                         mng_settimer fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_SETTIMER, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fSettimer = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_SETTIMER, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+mng_retcode MNG_DECL mng_setcb_processgamma (mng_handle        hHandle,
+                                             mng_processgamma  fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSGAMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcessgamma = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSGAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+#ifndef MNG_SKIPCHUNK_cHRM
+mng_retcode MNG_DECL mng_setcb_processchroma (mng_handle        hHandle,
+                                              mng_processchroma fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSCHROMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcesschroma = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSCHROMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+mng_retcode MNG_DECL mng_setcb_processsrgb (mng_handle      hHandle,
+                                            mng_processsrgb fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSRGB, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcesssrgb = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+#ifndef MNG_SKIPCHUNK_iCCP
+mng_retcode MNG_DECL mng_setcb_processiccp (mng_handle      hHandle,
+                                            mng_processiccp fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSICCP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcessiccp = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSICCP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+mng_retcode MNG_DECL mng_setcb_processarow (mng_handle      hHandle,
+                                            mng_processarow fProc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSAROW, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->fProcessarow = fProc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSAROW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Callback get functions                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_INTERNAL_MEMMNGMT
+mng_memalloc MNG_DECL mng_getcb_memalloc (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_MEMALLOC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_MEMALLOC, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fMemalloc;
+}
+#endif /* MNG_INTERNAL_MEMMNGMT */
+
+/* ************************************************************************** */
+
+#ifndef MNG_INTERNAL_MEMMNGMT
+mng_memfree MNG_DECL mng_getcb_memfree (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_MEMFREE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_MEMFREE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fMemfree;
+}
+#endif /* MNG_INTERNAL_MEMMNGMT */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_releasedata MNG_DECL mng_getcb_releasedata (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_RELEASEDATA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_RELEASEDATA, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fReleasedata;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_readdata MNG_DECL mng_getcb_readdata (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_READDATA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_READDATA, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fReaddata;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+mng_openstream MNG_DECL mng_getcb_openstream (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_OPENSTREAM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_OPENSTREAM, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fOpenstream;
+}
+#endif
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+mng_closestream MNG_DECL mng_getcb_closestream (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_CLOSESTREAM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_CLOSESTREAM, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fClosestream;
+}
+#endif
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_WRITE
+mng_writedata MNG_DECL mng_getcb_writedata (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_WRITEDATA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_WRITEDATA, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fWritedata;
+}
+#endif /* MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+mng_errorproc MNG_DECL mng_getcb_errorproc (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_ERRORPROC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_ERRORPROC, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fErrorproc;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_TRACE
+mng_traceproc MNG_DECL mng_getcb_traceproc (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_TRACEPROC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_TRACEPROC, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fTraceproc;
+}
+#endif /* MNG_SUPPORT_TRACE */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_processheader MNG_DECL mng_getcb_processheader (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSHEADER, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSHEADER, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcessheader;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_tEXt
+mng_processtext MNG_DECL mng_getcb_processtext (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSTEXT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSTEXT, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcesstext;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_processsave MNG_DECL mng_getcb_processsave (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSAVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSAVE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcesssave;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_processseek MNG_DECL mng_getcb_processseek (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSEEK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSEEK, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcessseek;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_nEED
+mng_processneed MNG_DECL mng_getcb_processneed (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSNEED, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSNEED, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcessneed;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_processmend MNG_DECL mng_getcb_processmend (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSMEND, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSMEND, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcessmend;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_processunknown MNG_DECL mng_getcb_processunknown (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSUNKNOWN, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSUNKNOWN, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcessunknown;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+#ifndef MNG_SKIPCHUNK_TERM
+mng_processterm MNG_DECL mng_getcb_processterm (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSTERM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSTERM, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcessterm;
+}
+#endif
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_getcanvasline MNG_DECL mng_getcb_getcanvasline (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETCANVASLINE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETCANVASLINE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fGetcanvasline;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_getbkgdline MNG_DECL mng_getcb_getbkgdline (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETBKGDLINE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETBKGDLINE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fGetbkgdline;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_getalphaline MNG_DECL mng_getcb_getalphaline (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETALPHALINE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETALPHALINE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fGetalphaline;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_refresh MNG_DECL mng_getcb_refresh (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_REFRESH, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_REFRESH, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fRefresh;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_gettickcount MNG_DECL mng_getcb_gettickcount (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETTICKCOUNT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETTICKCOUNT, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fGettickcount;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_settimer MNG_DECL mng_getcb_settimer (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_SETTIMER, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_SETTIMER, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fSettimer;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+mng_processgamma MNG_DECL mng_getcb_processgamma (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSGAMMA, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcessgamma;
+}
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+#ifndef MNG_SKIPCHUNK_cHRM
+mng_processchroma MNG_DECL mng_getcb_processchroma (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSCHROMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSCHROMA, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcesschroma;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+mng_processsrgb MNG_DECL mng_getcb_processsrgb (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSRGB, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSRGB, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcesssrgb;
+}
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+#ifndef MNG_SKIPCHUNK_iCCP
+mng_processiccp MNG_DECL mng_getcb_processiccp (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSICCP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSICCP, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcessiccp;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS)
+mng_processarow MNG_DECL mng_getcb_processarow (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSAROW, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSAROW, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->fProcessarow;
+}
+#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
+
+
diff --git a/files/Source/LibMNG/libmng_chunk_descr.c b/files/Source/LibMNG/libmng_chunk_descr.c
new file mode 100644
index 0000000..e1004a5
--- /dev/null
+++ b/files/Source/LibMNG/libmng_chunk_descr.c
@@ -0,0 +1,6090 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_chunk_descr.c      copyright (c) 2005-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Chunk descriptor functions (implementation)                * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the chunk- anf field-descriptor          * */
+/* *             routines                                                   * */
+/* *                                                                        * */
+/* * changes   : 1.0.9 - 12/06/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKREADER               * */
+/* *             1.0.9 - 12/11/2004 - G.Juyn                                * */
+/* *             - made all constants 'static'                              * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *             1.0.9 - 01/17/2005 - G.Juyn                                * */
+/* *             - fixed problem with global PLTE/tRNS                      * */
+/* *                                                                        * */
+/* *             1.0.10 - 01/17/2005 - G.R-P.                               * */
+/* *             - added typecast to appease the compiler                   * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include <stddef.h>                    /* needed for offsetof() */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_memory.h"
+#include "libmng_objects.h"
+#include "libmng_chunks.h"
+#include "libmng_chunk_descr.h"
+#include "libmng_object_prc.h"
+#include "libmng_chunk_prc.h"
+#include "libmng_chunk_io.h"
+#include "libmng_display.h"
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+#include "libmng_pixels.h"
+#include "libmng_filter.h"
+#endif
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+#if defined(MNG_INCLUDE_READ_PROCS) || defined(MNG_INCLUDE_WRITE_PROCS)
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+/* PNG chunks */
+
+MNG_LOCAL mng_field_descriptor mng_fields_ihdr [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_NOHIGHBIT,
+     1, 0, 4, 4,
+     offsetof(mng_ihdr, iWidth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_NOHIGHBIT,
+     1, 0, 4, 4,
+     offsetof(mng_ihdr, iHeight), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 16, 1, 1,
+     offsetof(mng_ihdr, iBitdepth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 6, 1, 1,
+     offsetof(mng_ihdr, iColortype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 1, 1,
+     offsetof(mng_ihdr, iCompression), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 1, 1,
+     offsetof(mng_ihdr, iFilter), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_ihdr, iInterlace), MNG_NULL, MNG_NULL}
+  };
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_field_descriptor mng_fields_plte [] =
+  {
+    {mng_debunk_plte,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_field_descriptor mng_fields_idat [] =
+  {
+    {MNG_NULL,
+     MNG_NULL,
+     0, 0, 0, 0,
+     offsetof(mng_idat, pData), MNG_NULL, offsetof(mng_idat, iDatasize)}
+  };
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_field_descriptor mng_fields_trns [] =
+  {
+    {mng_debunk_trns,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_gAMA
+MNG_LOCAL mng_field_descriptor mng_fields_gama [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_gama, iGamma), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+MNG_LOCAL mng_field_descriptor mng_fields_chrm [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_chrm, iWhitepointx), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_chrm, iWhitepointy), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_chrm, iRedx), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_chrm, iRedy), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_chrm, iGreeny), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_chrm, iGreeny), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_chrm, iBluex), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_chrm, iBluey), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sRGB
+MNG_LOCAL mng_field_descriptor mng_fields_srgb [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 4, 1, 1,
+     offsetof(mng_srgb, iRenderingintent), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+MNG_LOCAL mng_field_descriptor mng_fields_iccp [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_TERMINATOR,
+     0, 0, 1, 79,
+     offsetof(mng_iccp, zName), MNG_NULL, offsetof(mng_iccp, iNamesize)},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 1, 1,
+     offsetof(mng_iccp, iCompression), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_DEFLATED,
+     0, 0, 0, 0,
+     offsetof(mng_iccp, pProfile), MNG_NULL, offsetof(mng_iccp, iProfilesize)}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tEXt
+MNG_LOCAL mng_field_descriptor mng_fields_text [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_TERMINATOR,
+     0, 0, 1, 79,
+     offsetof(mng_text, zKeyword), MNG_NULL, offsetof(mng_text, iKeywordsize)},
+    {MNG_NULL,
+     MNG_NULL,
+     0, 0, 0, 0,
+     offsetof(mng_text, zText), MNG_NULL, offsetof(mng_text, iTextsize)}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_zTXt
+MNG_LOCAL mng_field_descriptor mng_fields_ztxt [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_TERMINATOR,
+     0, 0, 1, 79,
+     offsetof(mng_ztxt, zKeyword), MNG_NULL, offsetof(mng_ztxt, iKeywordsize)},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 1, 1,
+     offsetof(mng_ztxt, iCompression), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_DEFLATED,
+     0, 0, 0, 0,
+     offsetof(mng_ztxt, zText), MNG_NULL, offsetof(mng_ztxt, iTextsize)}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iTXt
+MNG_LOCAL mng_field_descriptor mng_fields_itxt [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_TERMINATOR,
+     0, 0, 1, 79,
+     offsetof(mng_itxt, zKeyword), MNG_NULL, offsetof(mng_itxt, iKeywordsize)},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_itxt, iCompressionflag), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 1, 1,
+     offsetof(mng_itxt, iCompressionmethod), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_TERMINATOR,
+     0, 0, 0, 0,
+     offsetof(mng_itxt, zLanguage), MNG_NULL, offsetof(mng_itxt, iLanguagesize)},
+    {MNG_NULL,
+     MNG_FIELD_TERMINATOR,
+     0, 0, 0, 0,
+     offsetof(mng_itxt, zTranslation), MNG_NULL, offsetof(mng_itxt, iTranslationsize)},
+    {mng_deflate_itxt,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_bKGD
+MNG_LOCAL mng_field_descriptor mng_fields_bkgd [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_PUTIMGTYPE,
+     0, 0, 0, 0,
+     offsetof(mng_bkgd, iType), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE3,
+     0, 0xFF, 1, 1,
+     offsetof(mng_bkgd, iIndex), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE0 | MNG_FIELD_IFIMGTYPE4,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_bkgd, iGray), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE6,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_bkgd, iRed), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE6,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_bkgd, iGreen), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE6,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_bkgd, iBlue), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYs
+MNG_LOCAL mng_field_descriptor mng_fields_phys [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_phys, iSizex), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_phys, iSizey), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_phys, iUnit), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sBIT
+MNG_LOCAL mng_field_descriptor mng_fields_sbit [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_PUTIMGTYPE,
+     0, 0, 0, 0,
+     offsetof(mng_sbit, iType), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_IFIMGTYPES,
+     0, 0xFF, 1, 1,
+     offsetof(mng_sbit, aBits[0]), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE3 | MNG_FIELD_IFIMGTYPE4 | MNG_FIELD_IFIMGTYPE6,
+     0, 0xFF, 1, 1,
+     offsetof(mng_sbit, aBits[1]), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE3 | MNG_FIELD_IFIMGTYPE6,
+     0, 0xFF, 1, 1,
+     offsetof(mng_sbit, aBits[2]), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE6,
+     0, 0xFF, 1, 1,
+     offsetof(mng_sbit, aBits[3]), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+MNG_LOCAL mng_field_descriptor mng_fields_splt [] =
+  {
+    {MNG_NULL,
+     MNG_NULL,
+     0, 0, 1, 79,
+     offsetof(mng_splt, zName), MNG_NULL, offsetof(mng_splt, iNamesize)},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     8, 16, 1, 1,
+     offsetof(mng_splt, iSampledepth), MNG_NULL, MNG_NULL},
+    {mng_splt_entries,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_hIST
+MNG_LOCAL mng_field_descriptor mng_fields_hist [] =
+  {
+    {mng_hist_entries,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tIME
+MNG_LOCAL mng_field_descriptor mng_fields_time [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_time, iYear), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 12, 1, 1,
+     offsetof(mng_time, iMonth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 31, 1, 1,
+     offsetof(mng_time, iDay), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 24, 1, 1,
+     offsetof(mng_time, iHour), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 60, 1, 1,
+     offsetof(mng_time, iMinute), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 60, 1, 1,
+     offsetof(mng_time, iSecond), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+/* JNG chunks */
+
+#ifdef MNG_INCLUDE_JNG
+MNG_LOCAL mng_field_descriptor mng_fields_jhdr [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_NOHIGHBIT,
+     1, 0, 4, 4,
+     offsetof(mng_jhdr, iWidth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_NOHIGHBIT,
+     1, 0, 4, 4,
+     offsetof(mng_jhdr, iHeight), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     8, 16, 1, 1,
+     offsetof(mng_jhdr, iColortype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     8, 20, 1, 1,
+     offsetof(mng_jhdr, iImagesampledepth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     8, 8, 1, 1,
+     offsetof(mng_jhdr, iImagecompression), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 8, 1, 1,
+     offsetof(mng_jhdr, iImageinterlace), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 16, 1, 1,
+     offsetof(mng_jhdr, iAlphasampledepth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 8, 1, 1,
+     offsetof(mng_jhdr, iAlphacompression), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 1, 1,
+     offsetof(mng_jhdr, iAlphafilter), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_jhdr, iAlphainterlace), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#define mng_fields_jdaa mng_fields_idat
+#define mng_fields_jdat mng_fields_idat
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+/* MNG chunks */
+
+MNG_LOCAL mng_field_descriptor mng_fields_mhdr [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_mhdr, iWidth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_mhdr, iHeight), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_mhdr, iTicks), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_mhdr, iLayercount), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_mhdr, iFramecount), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_mhdr, iPlaytime), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_mhdr, iSimplicity), MNG_NULL, MNG_NULL}
+  };
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+MNG_LOCAL mng_field_descriptor mng_fields_loop [] =
+  {
+    {mng_debunk_loop,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+MNG_LOCAL mng_field_descriptor mng_fields_endl [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFF, 1, 1,
+     offsetof(mng_endl, iLevel), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DEFI
+MNG_LOCAL mng_field_descriptor mng_fields_defi [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_defi, iObjectid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 0xFF, 1, 1,
+     offsetof(mng_defi, iDonotshow), offsetof(mng_defi, bHasdonotshow), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 0xFF, 1, 1,
+     offsetof(mng_defi, iConcrete), offsetof(mng_defi, bHasconcrete), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0, 4, 4,
+     offsetof(mng_defi, iXlocation), offsetof(mng_defi, bHasloca), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0, 4, 4,
+     offsetof(mng_defi, iYlocation), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2,
+     0, 0, 4, 4,
+     offsetof(mng_defi, iLeftcb), offsetof(mng_defi, bHasclip), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2,
+     0, 0, 4, 4,
+     offsetof(mng_defi, iRightcb), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2,
+     0, 0, 4, 4,
+     offsetof(mng_defi, iTopcb), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2,
+     0, 0, 4, 4,
+     offsetof(mng_defi, iBottomcb), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BASI
+MNG_LOCAL mng_field_descriptor mng_fields_basi [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_basi, iWidth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_basi, iHeight), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 16, 1, 1,
+     offsetof(mng_basi, iBitdepth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 6, 1, 1,
+     offsetof(mng_basi, iColortype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 1, 1,
+     offsetof(mng_basi, iCompression), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 1, 1,
+     offsetof(mng_basi, iFilter), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_basi, iInterlace), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_basi, iRed), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_basi, iGreen), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_basi, iBlue), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_basi, iAlpha), offsetof(mng_basi, bHasalpha), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 1, 1, 1,
+     offsetof(mng_basi, iViewable), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLON
+MNG_LOCAL mng_field_descriptor mng_fields_clon [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_clon, iSourceid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_clon, iCloneid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 2, 1, 1,
+     offsetof(mng_clon, iClonetype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 1, 1, 1,
+     offsetof(mng_clon, iDonotshow), offsetof(mng_clon, bHasdonotshow), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 1, 1, 1,
+     offsetof(mng_clon, iConcrete), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 2, 1, 1,
+     offsetof(mng_clon, iLocationtype), offsetof(mng_clon, bHasloca), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0, 4, 4,
+     offsetof(mng_clon, iLocationx), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0, 4, 4,
+     offsetof(mng_clon, iLocationy), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+MNG_LOCAL mng_field_descriptor mng_fields_past [] =
+  {
+    {mng_debunk_past,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+MNG_LOCAL mng_field_descriptor mng_fields_disc [] =
+  {
+    {mng_disc_entries,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BACK
+MNG_LOCAL mng_field_descriptor mng_fields_back [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_back, iRed), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_back, iGreen), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_back, iBlue), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 3, 1, 1,
+     offsetof(mng_back, iMandatory), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_back, iImageid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 1, 1, 1,
+     offsetof(mng_back, iTile), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+MNG_LOCAL mng_field_descriptor mng_fields_fram [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 4, 1, 1,
+     offsetof(mng_fram, iMode), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_TERMINATOR | MNG_FIELD_OPTIONAL,
+     0, 0, 1, 79,
+     offsetof(mng_fram, zName), MNG_NULL, offsetof(mng_fram, iNamesize)},
+    {mng_fram_remainder,
+     MNG_FIELD_OPTIONAL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MOVE
+MNG_LOCAL mng_field_descriptor mng_fields_move [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_move, iFirstid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_move, iLastid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_move, iMovetype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_move, iMovex), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_move, iMovey), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLIP
+MNG_LOCAL mng_field_descriptor mng_fields_clip [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_clip, iFirstid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_clip, iLastid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_clip, iCliptype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_clip, iClipl), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_clip, iClipr), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_clip, iClipt), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_clip, iClipb), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SHOW
+MNG_LOCAL mng_field_descriptor mng_fields_show [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 0xFFFF, 2, 2,
+     offsetof(mng_show, iFirstid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     1, 0xFFFF, 2, 2,
+     offsetof(mng_show, iLastid), offsetof(mng_show, bHaslastid), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL,
+     0, 7, 1, 1,
+     offsetof(mng_show, iMode), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_TERM
+MNG_LOCAL mng_field_descriptor mng_fields_term [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 3, 1, 1,
+     offsetof(mng_term, iTermaction), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 2, 1, 1,
+     offsetof(mng_term, iIteraction), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0, 4, 4,
+     offsetof(mng_term, iDelay), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0, 4, 4,
+     offsetof(mng_term, iItermax), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+MNG_LOCAL mng_field_descriptor mng_fields_save [] =
+  {
+    {mng_save_entries,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+MNG_LOCAL mng_field_descriptor mng_fields_seek [] =
+  {
+    {MNG_NULL,
+     MNG_NULL,
+     0, 0, 1, 79,
+     offsetof(mng_seek, zName), MNG_NULL, offsetof(mng_seek, iNamesize)}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_eXPI
+MNG_LOCAL mng_field_descriptor mng_fields_expi [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_expi, iSnapshotid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_NULL,
+     0, 0, 1, 79,
+     offsetof(mng_expi, zName), MNG_NULL, offsetof(mng_expi, iNamesize)}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_fPRI
+MNG_LOCAL mng_field_descriptor mng_fields_fpri [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_fpri, iDeltatype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFF, 1, 1,
+     offsetof(mng_fpri, iPriority), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+MNG_LOCAL mng_field_descriptor mng_fields_need [] =
+  {
+    {MNG_NULL,
+     MNG_NULL,
+     0, 0, 1, 0,
+     offsetof(mng_need, zKeywords), MNG_NULL, offsetof(mng_need, iKeywordssize)}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYg
+#define mng_fields_phyg mng_fields_phys
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_LOCAL mng_field_descriptor mng_fields_dhdr [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_dhdr, iObjectid), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 2, 1, 1,
+     offsetof(mng_dhdr, iImagetype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 7, 1, 1,
+     offsetof(mng_dhdr, iDeltatype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0, 4, 4,
+     offsetof(mng_dhdr, iBlockwidth), offsetof(mng_dhdr, bHasblocksize), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1,
+     0, 0, 4, 4,
+     offsetof(mng_dhdr, iBlockheight), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2,
+     0, 0, 4, 4,
+     offsetof(mng_dhdr, iBlockx), offsetof(mng_dhdr, bHasblockloc), MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2,
+     0, 0, 4, 4,
+     offsetof(mng_dhdr, iBlocky), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_LOCAL mng_field_descriptor mng_fields_prom [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 14, 1, 1,
+     offsetof(mng_prom, iColortype), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 16, 1, 1,
+     offsetof(mng_prom, iSampledepth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_prom, iFilltype), MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_LOCAL mng_field_descriptor mng_fields_pplt [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 5, 1, 1,
+     offsetof(mng_pplt, iDeltatype), MNG_NULL, MNG_NULL},
+    {mng_pplt_entries,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_LOCAL mng_field_descriptor mng_fields_drop [] =
+  {
+    {mng_drop_entries,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+MNG_LOCAL mng_field_descriptor mng_fields_dbyk [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_dbyk, iChunkname), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_dbyk, iPolarity), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_NULL,
+     0, 0, 1, 0,
+     offsetof(mng_dbyk, zKeywords), MNG_NULL, offsetof(mng_dbyk, iKeywordssize)}
+  };
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+MNG_LOCAL mng_field_descriptor mng_fields_ordr [] =
+  {
+    {mng_drop_entries,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+MNG_LOCAL mng_field_descriptor mng_fields_magn [] =
+  {
+    {mng_debunk_magn,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+MNG_LOCAL mng_field_descriptor mng_fields_mpng [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 0, 4, 4,
+     offsetof(mng_mpng, iFramewidth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 0, 4, 4,
+     offsetof(mng_mpng, iFrameheight), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0xFFFF, 2, 2,
+     offsetof(mng_mpng, iNumplays), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 0xFFFF, 2, 2,
+     offsetof(mng_mpng, iTickspersec), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 1, 1,
+     offsetof(mng_mpng, iCompressionmethod), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_DEFLATED,
+     0, 0, 1, 0,
+     offsetof(mng_mpng, pFrames), MNG_NULL, offsetof(mng_mpng, iFramessize)}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+MNG_LOCAL mng_field_descriptor mng_fields_ahdr [] =
+  {
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 0, 4, 4,
+     offsetof(mng_ahdr, iNumframes), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_ahdr, iTickspersec), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 0, 4, 4,
+     offsetof(mng_ahdr, iNumplays), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 0, 4, 4,
+     offsetof(mng_ahdr, iTilewidth), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     1, 0, 4, 4,
+     offsetof(mng_ahdr, iTileheight), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_ahdr, iInterlace), MNG_NULL, MNG_NULL},
+    {MNG_NULL,
+     MNG_FIELD_INT,
+     0, 1, 1, 1,
+     offsetof(mng_ahdr, iStillused), MNG_NULL, MNG_NULL}
+  };
+
+MNG_LOCAL mng_field_descriptor mng_fields_adat [] =
+  {
+    {mng_adat_tiles,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_evNT
+MNG_LOCAL mng_field_descriptor mng_fields_evnt [] =
+  {
+    {mng_evnt_entries,
+     MNG_NULL,
+     0, 0, 0, 0,
+     MNG_NULL, MNG_NULL, MNG_NULL}
+  };
+#endif
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_field_descriptor mng_fields_unknown [] =
+  {
+    {MNG_NULL,
+     MNG_NULL,
+     0, 0, 1, 0,
+     offsetof(mng_unknown_chunk, pData), MNG_NULL, offsetof(mng_unknown_chunk, iDatasize)}
+  };
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+/* PNG chunks */
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ihdr =
+    {mng_it_png, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_ihdr,
+     mng_fields_ihdr, (sizeof(mng_fields_ihdr) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL,
+     MNG_NULL,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOJHDR | MNG_DESCR_NOBASI | MNG_DESCR_NOIDAT | MNG_DESCR_NOPLTE};
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_plte =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_plte, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_plte,
+     mng_fields_plte, (sizeof(mng_fields_plte) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_idat =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_idat, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_idat,
+     mng_fields_idat, (sizeof(mng_fields_idat) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_EMPTYEMBED,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOJSEP};
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_iend =
+    {mng_it_png, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_iend,
+     MNG_NULL, 0,
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED,
+     MNG_DESCR_GenHDR,
+     MNG_NULL};
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_trns =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_trns, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_trns,
+     mng_fields_trns, (sizeof(mng_fields_trns) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+
+#ifndef MNG_SKIPCHUNK_gAMA
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_gama =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_gama, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_gama,
+     mng_fields_gama, (sizeof(mng_fields_gama) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOPLTE | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+#endif
+
+#ifndef MNG_SKIPCHUNK_cHRM
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_chrm =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_chrm, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_chrm,
+     mng_fields_chrm, (sizeof(mng_fields_chrm) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOPLTE | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+#endif
+
+#ifndef MNG_SKIPCHUNK_sRGB
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_srgb =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_srgb, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_srgb,
+     mng_fields_srgb, (sizeof(mng_fields_srgb) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOPLTE | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+#endif
+
+#ifndef MNG_SKIPCHUNK_iCCP
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_iccp =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_iccp, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_iccp,
+     mng_fields_iccp, (sizeof(mng_fields_iccp) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOPLTE | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+#endif
+
+#ifndef MNG_SKIPCHUNK_tEXt
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_text =
+    {mng_it_png, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_text,
+     mng_fields_text, (sizeof(mng_fields_text) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_NULL};
+#endif
+
+#ifndef MNG_SKIPCHUNK_zTXt
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ztxt =
+    {mng_it_png, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_ztxt,
+     mng_fields_ztxt, (sizeof(mng_fields_ztxt) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_NULL};
+#endif
+
+#ifndef MNG_SKIPCHUNK_iTXt
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_itxt =
+    {mng_it_png, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_itxt,
+     mng_fields_itxt, (sizeof(mng_fields_itxt) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_NULL};
+#endif
+
+#ifndef MNG_SKIPCHUNK_bKGD
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_bkgd =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_bkgd, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_bkgd,
+     mng_fields_bkgd, (sizeof(mng_fields_bkgd) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+#endif
+
+#ifndef MNG_SKIPCHUNK_pHYs
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_phys =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_phys, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_phys,
+     mng_fields_phys, (sizeof(mng_fields_phys) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+#endif
+
+#ifndef MNG_SKIPCHUNK_sBIT
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_sbit =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_sbit, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_sbit,
+     mng_fields_sbit, (sizeof(mng_fields_sbit) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+#endif
+
+#ifndef MNG_SKIPCHUNK_sPLT
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_splt =
+    {mng_it_png, mng_create_none, 0, offsetof(mng_splt, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_splt,
+     mng_fields_splt, (sizeof(mng_fields_splt) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+#endif
+
+#ifndef MNG_SKIPCHUNK_hIST
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_hist =
+    {mng_it_png, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_hist,
+     mng_fields_hist, (sizeof(mng_fields_hist) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_GenHDR | MNG_DESCR_PLTE,
+     MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA};
+#endif
+
+#ifndef MNG_SKIPCHUNK_tIME
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_time =
+    {mng_it_png, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_time,
+     mng_fields_time, (sizeof(mng_fields_time) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_GLOBAL,
+     MNG_DESCR_GenHDR,
+     MNG_NULL};
+#endif
+
+/* ************************************************************************** */
+/* JNG chunks */
+
+#ifdef MNG_INCLUDE_JNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_jhdr =
+    {mng_it_jng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_jhdr,
+     mng_fields_jhdr, (sizeof(mng_fields_jhdr) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_NULL,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifdef MNG_INCLUDE_JNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_jdaa =
+    {mng_it_jng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_jdaa,
+     mng_fields_jdaa, (sizeof(mng_fields_jdaa) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_JngHDR,
+     MNG_DESCR_NOJSEP};
+#endif
+
+#ifdef MNG_INCLUDE_JNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_jdat =
+    {mng_it_jng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_jdat,
+     mng_fields_jdat, (sizeof(mng_fields_jdat) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_EMPTYEMBED,
+     MNG_DESCR_JngHDR,
+     MNG_NULL};
+#endif
+
+#ifdef MNG_INCLUDE_JNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_jsep =
+    {mng_it_jng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_jsep,
+     MNG_NULL, 0,
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED,
+     MNG_DESCR_JngHDR,
+     MNG_DESCR_NOJSEP};
+#endif
+
+/* ************************************************************************** */
+/* MNG chunks */
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_mhdr =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_mhdr,
+     mng_fields_mhdr, (sizeof(mng_fields_mhdr) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_NULL,
+     MNG_DESCR_NOMHDR | MNG_DESCR_NOIHDR | MNG_DESCR_NOJHDR};
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_mend =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_mend,
+     MNG_NULL, 0,
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_MHDR,
+     MNG_NULL};
+
+#ifndef MNG_SKIPCHUNK_LOOP
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_loop =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_loop,
+     mng_fields_loop, (sizeof(mng_fields_loop) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_endl =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_endl,
+     mng_fields_endl, (sizeof(mng_fields_endl) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_DEFI
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_defi =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_defi,
+     mng_fields_defi, (sizeof(mng_fields_defi) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_BASI
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_basi =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_basi,
+     mng_fields_basi, (sizeof(mng_fields_basi) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_CLON
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_clon =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_clon,
+     mng_fields_clon, (sizeof(mng_fields_clon) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_PAST
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_past =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_past,
+     mng_fields_past, (sizeof(mng_fields_past) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_DISC
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_disc =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_disc,
+     mng_fields_disc, (sizeof(mng_fields_disc) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_BACK
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_back =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_back,
+     mng_fields_back, (sizeof(mng_fields_back) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_FRAM
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_fram =
+    {mng_it_mng, mng_create_none, 0, offsetof(mng_fram, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_fram,
+     mng_fields_fram, (sizeof(mng_fields_fram) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_MOVE
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_move =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_move,
+     mng_fields_move, (sizeof(mng_fields_move) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_CLIP
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_clip =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_clip,
+     mng_fields_clip, (sizeof(mng_fields_clip) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_SHOW
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_show =
+    {mng_it_mng, mng_create_none, 0, offsetof(mng_show, bEmpty),
+     MNG_NULL, MNG_NULL, mng_special_show,
+     mng_fields_show, (sizeof(mng_fields_show) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_TERM
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_term =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_term,
+     mng_fields_term, (sizeof(mng_fields_term) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR | MNG_DESCR_NOTERM | MNG_DESCR_NOLOOP};
+#endif
+
+#ifndef MNG_SKIPCHUNK_SAVE
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_save =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_save,
+     mng_fields_save, (sizeof(mng_fields_save) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOSAVE | MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_SEEK
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_seek =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_seek,
+     mng_fields_seek, (sizeof(mng_fields_seek) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL,
+     MNG_DESCR_MHDR | MNG_DESCR_SAVE,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_eXPI
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_expi =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_expi,
+     mng_fields_expi, (sizeof(mng_fields_expi) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_fPRI
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_fpri =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_fpri,
+     mng_fields_fpri, (sizeof(mng_fields_fpri) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_nEED
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_need =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_need,
+     mng_fields_need, (sizeof(mng_fields_need) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_pHYg
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_phyg =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_phyg,
+     mng_fields_phyg, (sizeof(mng_fields_phyg) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_dhdr =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_dhdr,
+     mng_fields_dhdr, (sizeof(mng_fields_dhdr) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_prom =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_prom,
+     mng_fields_prom, (sizeof(mng_fields_prom) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR | MNG_DESCR_DHDR,
+     MNG_NULL};
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ipng =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_ipng,
+     MNG_NULL, 0,
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED,
+     MNG_DESCR_MHDR | MNG_DESCR_DHDR,
+     MNG_NULL};
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_pplt =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_pplt,
+     mng_fields_pplt, (sizeof(mng_fields_pplt) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR | MNG_DESCR_DHDR,
+     MNG_NULL};
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ijng =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_ijng,
+     MNG_NULL, 0,
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED,
+     MNG_DESCR_MHDR | MNG_DESCR_DHDR,
+     MNG_NULL};
+#endif
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_drop =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_drop,
+     mng_fields_drop, (sizeof(mng_fields_drop) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR | MNG_DESCR_DHDR,
+     MNG_NULL};
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_dbyk =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_dbyk,
+     mng_fields_dbyk, (sizeof(mng_fields_dbyk) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED,
+     MNG_DESCR_MHDR | MNG_DESCR_DHDR,
+     MNG_NULL};
+#endif
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ordr =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_ordr,
+     mng_fields_ordr, (sizeof(mng_fields_ordr) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR | MNG_DESCR_DHDR,
+     MNG_NULL};
+#endif
+#endif
+
+#ifndef MNG_SKIPCHUNK_MAGN
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_magn =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_magn,
+     mng_fields_magn, (sizeof(mng_fields_magn) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR};
+#endif
+
+#ifndef MNG_SKIPCHUNK_evNT
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_evnt =
+    {mng_it_mng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_evnt,
+     mng_fields_evnt, (sizeof(mng_fields_evnt) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_MHDR,
+     MNG_DESCR_NOSAVE};
+#endif
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_mpng =
+    {mng_it_mpng, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_mpng,
+     mng_fields_mpng, (sizeof(mng_fields_mpng) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_NULL,
+     MNG_DESCR_NOMHDR | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT};
+#endif
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ahdr =
+    {mng_it_ang, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_ahdr,
+     mng_fields_ahdr, (sizeof(mng_fields_ahdr) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_IHDR,
+     MNG_DESCR_NOMHDR | MNG_DESCR_NOJHDR | MNG_DESCR_NOIDAT};
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_adat =
+    {mng_it_ang, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_adat,
+     mng_fields_adat, (sizeof(mng_fields_adat) / sizeof(mng_field_descriptor)),
+     MNG_NULL,
+     MNG_DESCR_IHDR,
+     MNG_DESCR_NOMHDR | MNG_DESCR_NOJHDR};
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+/* the good ol' unknown babe */
+
+MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_unknown =
+    {mng_it_png, mng_create_none, 0, 0,
+     MNG_NULL, MNG_NULL, mng_special_unknown,
+     mng_fields_unknown, (sizeof(mng_fields_unknown) / sizeof(mng_field_descriptor)),
+     MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED,
+     MNG_NULL,
+     MNG_NULL};
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+MNG_LOCAL mng_chunk_header mng_chunk_unknown =
+    {MNG_UINT_HUH, mng_init_general, mng_free_unknown,
+     mng_read_general, mng_write_unknown, mng_assign_unknown,
+     0, 0, sizeof(mng_unknown_chunk), &mng_chunk_descr_unknown};
+
+/* ************************************************************************** */
+
+  /* the table-idea & binary search code was adapted from
+     libpng 1.1.0 (pngread.c) */
+  /* NOTE1: the table must remain sorted by chunkname, otherwise the binary
+     search will break !!! (ps. watch upper-/lower-case chunknames !!) */
+  /* NOTE2: the layout must remain equal to the header part of all the
+     chunk-structures (yes, that means even the pNext and pPrev fields;
+     it's wasting a bit of space, but hey, the code is a lot easier) */
+
+MNG_LOCAL mng_chunk_header mng_chunk_table [] =
+  {
+#ifndef MNG_SKIPCHUNK_BACK
+    {MNG_UINT_BACK, mng_init_general, mng_free_general, mng_read_general, mng_write_back, mng_assign_general, 0, 0, sizeof(mng_back), &mng_chunk_descr_back},
+#endif
+#ifndef MNG_SKIPCHUNK_BASI
+    {MNG_UINT_BASI, mng_init_general, mng_free_general, mng_read_general, mng_write_basi, mng_assign_general, 0, 0, sizeof(mng_basi), &mng_chunk_descr_basi},
+#endif
+#ifndef MNG_SKIPCHUNK_CLIP
+    {MNG_UINT_CLIP, mng_init_general, mng_free_general, mng_read_general, mng_write_clip, mng_assign_general, 0, 0, sizeof(mng_clip), &mng_chunk_descr_clip},
+#endif
+#ifndef MNG_SKIPCHUNK_CLON
+    {MNG_UINT_CLON, mng_init_general, mng_free_general, mng_read_general, mng_write_clon, mng_assign_general, 0, 0, sizeof(mng_clon), &mng_chunk_descr_clon},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+    {MNG_UINT_DBYK, mng_init_general, mng_free_dbyk,    mng_read_general, mng_write_dbyk, mng_assign_dbyk,    0, 0, sizeof(mng_dbyk), &mng_chunk_descr_dbyk},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_DEFI
+    {MNG_UINT_DEFI, mng_init_general, mng_free_general, mng_read_general, mng_write_defi, mng_assign_general, 0, 0, sizeof(mng_defi), &mng_chunk_descr_defi},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_UINT_DHDR, mng_init_general, mng_free_general, mng_read_general, mng_write_dhdr, mng_assign_general, 0, 0, sizeof(mng_dhdr), &mng_chunk_descr_dhdr},
+#endif
+#ifndef MNG_SKIPCHUNK_DISC
+    {MNG_UINT_DISC, mng_init_general, mng_free_disc,    mng_read_general, mng_write_disc, mng_assign_disc,    0, 0, sizeof(mng_disc), &mng_chunk_descr_disc},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DROP
+    {MNG_UINT_DROP, mng_init_general, mng_free_drop,    mng_read_general, mng_write_drop, mng_assign_drop,    0, 0, sizeof(mng_drop), &mng_chunk_descr_drop},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_LOOP
+    {MNG_UINT_ENDL, mng_init_general, mng_free_general, mng_read_general, mng_write_endl, mng_assign_general, 0, 0, sizeof(mng_endl), &mng_chunk_descr_endl},
+#endif
+#ifndef MNG_SKIPCHUNK_FRAM
+    {MNG_UINT_FRAM, mng_init_general, mng_free_fram,    mng_read_general, mng_write_fram, mng_assign_fram,    0, 0, sizeof(mng_fram), &mng_chunk_descr_fram},
+#endif
+    {MNG_UINT_IDAT, mng_init_general, mng_free_idat,    mng_read_general, mng_write_idat, mng_assign_idat,    0, 0, sizeof(mng_idat), &mng_chunk_descr_idat},  /* 12-th element! */
+    {MNG_UINT_IEND, mng_init_general, mng_free_general, mng_read_general, mng_write_iend, mng_assign_general, 0, 0, sizeof(mng_iend), &mng_chunk_descr_iend},
+    {MNG_UINT_IHDR, mng_init_general, mng_free_general, mng_read_general, mng_write_ihdr, mng_assign_general, 0, 0, sizeof(mng_ihdr), &mng_chunk_descr_ihdr},
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+    {MNG_UINT_IJNG, mng_init_general, mng_free_general, mng_read_general, mng_write_ijng, mng_assign_general, 0, 0, sizeof(mng_ijng), &mng_chunk_descr_ijng},
+#endif
+    {MNG_UINT_IPNG, mng_init_general, mng_free_general, mng_read_general, mng_write_ipng, mng_assign_general, 0, 0, sizeof(mng_ipng), &mng_chunk_descr_ipng},
+#endif
+#ifdef MNG_INCLUDE_JNG
+    {MNG_UINT_JDAA, mng_init_general, mng_free_jdaa,    mng_read_general, mng_write_jdaa, mng_assign_jdaa,    0, 0, sizeof(mng_jdaa), &mng_chunk_descr_jdaa},
+    {MNG_UINT_JDAT, mng_init_general, mng_free_jdat,    mng_read_general, mng_write_jdat, mng_assign_jdat,    0, 0, sizeof(mng_jdat), &mng_chunk_descr_jdat},
+    {MNG_UINT_JHDR, mng_init_general, mng_free_general, mng_read_general, mng_write_jhdr, mng_assign_general, 0, 0, sizeof(mng_jhdr), &mng_chunk_descr_jhdr},
+    {MNG_UINT_JSEP, mng_init_general, mng_free_general, mng_read_general, mng_write_jsep, mng_assign_general, 0, 0, sizeof(mng_jsep), &mng_chunk_descr_jsep},
+    {MNG_UINT_JdAA, mng_init_general, mng_free_jdaa,    mng_read_general, mng_write_jdaa, mng_assign_jdaa,    0, 0, sizeof(mng_jdaa), &mng_chunk_descr_jdaa},
+#endif
+#ifndef MNG_SKIPCHUNK_LOOP
+    {MNG_UINT_LOOP, mng_init_general, mng_free_loop,    mng_read_general, mng_write_loop, mng_assign_loop,    0, 0, sizeof(mng_loop), &mng_chunk_descr_loop},
+#endif
+#ifndef MNG_SKIPCHUNK_MAGN
+    {MNG_UINT_MAGN, mng_init_general, mng_free_general, mng_read_general, mng_write_magn, mng_assign_general, 0, 0, sizeof(mng_magn), &mng_chunk_descr_magn},
+#endif
+    {MNG_UINT_MEND, mng_init_general, mng_free_general, mng_read_general, mng_write_mend, mng_assign_general, 0, 0, sizeof(mng_mend), &mng_chunk_descr_mend},
+    {MNG_UINT_MHDR, mng_init_general, mng_free_general, mng_read_general, mng_write_mhdr, mng_assign_general, 0, 0, sizeof(mng_mhdr), &mng_chunk_descr_mhdr},
+#ifndef MNG_SKIPCHUNK_MOVE
+    {MNG_UINT_MOVE, mng_init_general, mng_free_general, mng_read_general, mng_write_move, mng_assign_general, 0, 0, sizeof(mng_move), &mng_chunk_descr_move},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+    {MNG_UINT_ORDR, mng_init_general, mng_free_ordr,    mng_read_general, mng_write_ordr, mng_assign_ordr,    0, 0, sizeof(mng_ordr), &mng_chunk_descr_ordr},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_UINT_PAST, mng_init_general, mng_free_past,    mng_read_general, mng_write_past, mng_assign_past,    0, 0, sizeof(mng_past), &mng_chunk_descr_past},
+#endif
+    {MNG_UINT_PLTE, mng_init_general, mng_free_general, mng_read_general, mng_write_plte, mng_assign_general, 0, 0, sizeof(mng_plte), &mng_chunk_descr_plte},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_UINT_PPLT, mng_init_general, mng_free_general, mng_read_general, mng_write_pplt, mng_assign_general, 0, 0, sizeof(mng_pplt), &mng_chunk_descr_pplt},
+    {MNG_UINT_PROM, mng_init_general, mng_free_general, mng_read_general, mng_write_prom, mng_assign_general, 0, 0, sizeof(mng_prom), &mng_chunk_descr_prom},
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+    {MNG_UINT_SAVE, mng_init_general, mng_free_save,    mng_read_general, mng_write_save, mng_assign_save,    0, 0, sizeof(mng_save), &mng_chunk_descr_save},
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+    {MNG_UINT_SEEK, mng_init_general, mng_free_seek,    mng_read_general, mng_write_seek, mng_assign_seek,    0, 0, sizeof(mng_seek), &mng_chunk_descr_seek},
+#endif
+#ifndef MNG_SKIPCHUNK_SHOW
+    {MNG_UINT_SHOW, mng_init_general, mng_free_general, mng_read_general, mng_write_show, mng_assign_general, 0, 0, sizeof(mng_show), &mng_chunk_descr_show},
+#endif
+#ifndef MNG_SKIPCHUNK_TERM
+    {MNG_UINT_TERM, mng_init_general, mng_free_general, mng_read_general, mng_write_term, mng_assign_general, 0, 0, sizeof(mng_term), &mng_chunk_descr_term},
+#endif
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+    {MNG_UINT_adAT, mng_init_general, mng_free_adat,    mng_read_general, mng_write_adat, mng_assign_adat,    0, 0, sizeof(mng_adat), &mng_chunk_descr_adat},
+    {MNG_UINT_ahDR, mng_init_general, mng_free_general, mng_read_general, mng_write_ahdr, mng_assign_ahdr,    0, 0, sizeof(mng_ahdr), &mng_chunk_descr_ahdr},
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+    {MNG_UINT_bKGD, mng_init_general, mng_free_general, mng_read_general, mng_write_bkgd, mng_assign_general, 0, 0, sizeof(mng_bkgd), &mng_chunk_descr_bkgd},
+#endif
+#ifndef MNG_SKIPCHUNK_cHRM
+    {MNG_UINT_cHRM, mng_init_general, mng_free_general, mng_read_general, mng_write_chrm, mng_assign_general, 0, 0, sizeof(mng_chrm), &mng_chunk_descr_chrm},
+#endif
+#ifndef MNG_SKIPCHUNK_eXPI
+    {MNG_UINT_eXPI, mng_init_general, mng_free_expi,    mng_read_general, mng_write_expi, mng_assign_expi,    0, 0, sizeof(mng_expi), &mng_chunk_descr_expi},
+#endif
+#ifndef MNG_SKIPCHUNK_evNT
+    {MNG_UINT_evNT, mng_init_general, mng_free_evnt,    mng_read_general, mng_write_evnt, mng_assign_evnt,    0, 0, sizeof(mng_evnt), &mng_chunk_descr_evnt},
+#endif
+#ifndef MNG_SKIPCHUNK_fPRI
+    {MNG_UINT_fPRI, mng_init_general, mng_free_general, mng_read_general, mng_write_fpri, mng_assign_general, 0, 0, sizeof(mng_fpri), &mng_chunk_descr_fpri},
+#endif
+#ifndef MNG_SKIPCHUNK_gAMA
+    {MNG_UINT_gAMA, mng_init_general, mng_free_general, mng_read_general, mng_write_gama, mng_assign_general, 0, 0, sizeof(mng_gama), &mng_chunk_descr_gama},
+#endif
+#ifndef MNG_SKIPCHUNK_hIST
+    {MNG_UINT_hIST, mng_init_general, mng_free_general, mng_read_general, mng_write_hist, mng_assign_general, 0, 0, sizeof(mng_hist), &mng_chunk_descr_hist},
+#endif
+#ifndef MNG_SKIPCHUNK_iCCP
+    {MNG_UINT_iCCP, mng_init_general, mng_free_iccp,    mng_read_general, mng_write_iccp, mng_assign_iccp,    0, 0, sizeof(mng_iccp), &mng_chunk_descr_iccp},
+#endif
+#ifndef MNG_SKIPCHUNK_iTXt
+    {MNG_UINT_iTXt, mng_init_general, mng_free_itxt,    mng_read_general, mng_write_itxt, mng_assign_itxt,    0, 0, sizeof(mng_itxt), &mng_chunk_descr_itxt},
+#endif
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_UINT_mpNG, mng_init_general, mng_free_mpng,    mng_read_general, mng_write_mpng, mng_assign_mpng,    0, 0, sizeof(mng_mpng), &mng_chunk_descr_mpng},
+#endif
+#ifndef MNG_SKIPCHUNK_nEED
+    {MNG_UINT_nEED, mng_init_general, mng_free_need,    mng_read_general, mng_write_need, mng_assign_need,    0, 0, sizeof(mng_need), &mng_chunk_descr_need},
+#endif
+/* TODO:     {MNG_UINT_oFFs, 0, 0, 0, 0, 0, 0},  */
+/* TODO:     {MNG_UINT_pCAL, 0, 0, 0, 0, 0, 0},  */
+#ifndef MNG_SKIPCHUNK_pHYg
+    {MNG_UINT_pHYg, mng_init_general, mng_free_general, mng_read_general, mng_write_phyg, mng_assign_general, 0, 0, sizeof(mng_phyg), &mng_chunk_descr_phyg},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYs
+    {MNG_UINT_pHYs, mng_init_general, mng_free_general, mng_read_general, mng_write_phys, mng_assign_general, 0, 0, sizeof(mng_phys), &mng_chunk_descr_phys},
+#endif
+#ifndef MNG_SKIPCHUNK_sBIT
+    {MNG_UINT_sBIT, mng_init_general, mng_free_general, mng_read_general, mng_write_sbit, mng_assign_general, 0, 0, sizeof(mng_sbit), &mng_chunk_descr_sbit},
+#endif
+/* TODO:     {MNG_UINT_sCAL, 0, 0, 0, 0, 0, 0},  */
+#ifndef MNG_SKIPCHUNK_sPLT
+    {MNG_UINT_sPLT, mng_init_general, mng_free_splt,    mng_read_general, mng_write_splt, mng_assign_splt,    0, 0, sizeof(mng_splt), &mng_chunk_descr_splt},
+#endif
+    {MNG_UINT_sRGB, mng_init_general, mng_free_general, mng_read_general, mng_write_srgb, mng_assign_general, 0, 0, sizeof(mng_srgb), &mng_chunk_descr_srgb},
+#ifndef MNG_SKIPCHUNK_tEXt
+    {MNG_UINT_tEXt, mng_init_general, mng_free_text,    mng_read_general, mng_write_text, mng_assign_text,    0, 0, sizeof(mng_text), &mng_chunk_descr_text},
+#endif
+#ifndef MNG_SKIPCHUNK_tIME
+    {MNG_UINT_tIME, mng_init_general, mng_free_general, mng_read_general, mng_write_time, mng_assign_general, 0, 0, sizeof(mng_time), &mng_chunk_descr_time},
+#endif
+    {MNG_UINT_tRNS, mng_init_general, mng_free_general, mng_read_general, mng_write_trns, mng_assign_general, 0, 0, sizeof(mng_trns), &mng_chunk_descr_trns},
+#ifndef MNG_SKIPCHUNK_zTXt
+    {MNG_UINT_zTXt, mng_init_general, mng_free_ztxt,    mng_read_general, mng_write_ztxt, mng_assign_ztxt,    0, 0, sizeof(mng_ztxt), &mng_chunk_descr_ztxt},
+#endif
+  };
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+void mng_get_chunkheader (mng_chunkid       iChunkname,
+                          mng_chunk_headerp pResult)
+{
+                                       /* binary search variables */
+  mng_int32         iTop, iLower, iUpper, iMiddle;
+  mng_chunk_headerp pEntry;            /* pointer to found entry */
+                                       /* determine max index of table */
+  iTop = (sizeof (mng_chunk_table) / sizeof (mng_chunk_table [0])) - 1;
+
+  /* binary search; with 54 chunks, worst-case is 7 comparisons */
+  iLower  = 0;
+#ifndef MNG_NO_DELTA_PNG
+  iMiddle = 11;                        /* start with the IDAT entry */
+#else
+  iMiddle = 8;
+#endif
+  iUpper  = iTop;
+  pEntry  = 0;                         /* no goods yet! */
+
+  do                                   /* the binary search itself */
+    {
+      if (mng_chunk_table [iMiddle].iChunkname < iChunkname)
+        iLower = iMiddle + 1;
+      else if (mng_chunk_table [iMiddle].iChunkname > iChunkname)
+        iUpper = iMiddle - 1;
+      else
+      {
+        pEntry = &mng_chunk_table [iMiddle];
+        break;
+      }
+      iMiddle = (iLower + iUpper) >> 1;
+    }
+  while (iLower <= iUpper);
+
+  if (!pEntry)                         /* unknown chunk ? */
+    pEntry = &mng_chunk_unknown;       /* make it so! */
+
+  MNG_COPY (pResult, pEntry, sizeof(mng_chunk_header));
+
+  return;
+}
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+/* PNG chunks */
+
+MNG_C_SPECIALFUNC (mng_special_ihdr)
+{
+  pData->bHasIHDR      = MNG_TRUE;     /* indicate IHDR is present */
+                                       /* and store interesting fields */
+  if ((!pData->bHasDHDR) || (pData->iDeltatype == MNG_DELTATYPE_NOCHANGE))
+  {
+    pData->iDatawidth  = ((mng_ihdrp)pChunk)->iWidth;
+    pData->iDataheight = ((mng_ihdrp)pChunk)->iHeight;
+  }
+
+  pData->iBitdepth     = ((mng_ihdrp)pChunk)->iBitdepth;
+  pData->iColortype    = ((mng_ihdrp)pChunk)->iColortype;
+  pData->iCompression  = ((mng_ihdrp)pChunk)->iCompression;
+  pData->iFilter       = ((mng_ihdrp)pChunk)->iFilter;
+  pData->iInterlace    = ((mng_ihdrp)pChunk)->iInterlace;
+
+#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT)
+  pData->iPNGmult = 1;
+  pData->iPNGdepth = pData->iBitdepth;
+#endif
+
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+  if (pData->iBitdepth < 8)
+      pData->iBitdepth = 8;
+#endif
+
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (pData->iBitdepth > 8)
+    {
+      pData->iBitdepth = 8;
+      pData->iPNGmult = 2;
+    }
+#endif
+
+  if ((pData->iBitdepth !=  8)      /* parameter validity checks */
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+      && (pData->iBitdepth !=  1) &&
+      (pData->iBitdepth !=  2) &&
+      (pData->iBitdepth !=  4)
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+      && (pData->iBitdepth != 16)   
+#endif
+      )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if ((pData->iColortype != MNG_COLORTYPE_GRAY   ) &&
+      (pData->iColortype != MNG_COLORTYPE_RGB    ) &&
+      (pData->iColortype != MNG_COLORTYPE_INDEXED) &&
+      (pData->iColortype != MNG_COLORTYPE_GRAYA  ) &&
+      (pData->iColortype != MNG_COLORTYPE_RGBA   )    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  if ((pData->iColortype == MNG_COLORTYPE_INDEXED) && (pData->iBitdepth > 8))
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if (((pData->iColortype == MNG_COLORTYPE_RGB    ) ||
+       (pData->iColortype == MNG_COLORTYPE_GRAYA  ) ||
+       (pData->iColortype == MNG_COLORTYPE_RGBA   )    ) &&
+      (pData->iBitdepth < 8                            )    )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if (pData->iCompression != MNG_COMPRESSION_DEFLATE)
+    MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+#if defined(FILTER192) || defined(FILTER193)
+  if ((pData->iFilter != MNG_FILTER_ADAPTIVE ) &&
+#if defined(FILTER192) && defined(FILTER193)
+      (pData->iFilter != MNG_FILTER_DIFFERING) &&
+      (pData->iFilter != MNG_FILTER_NOFILTER )    )
+#else
+#ifdef FILTER192
+      (pData->iFilter != MNG_FILTER_DIFFERING)    )
+#else
+      (pData->iFilter != MNG_FILTER_NOFILTER )    )
+#endif
+#endif
+    MNG_ERROR (pData, MNG_INVALIDFILTER);
+#else
+  if (pData->iFilter)
+    MNG_ERROR (pData, MNG_INVALIDFILTER);
+#endif
+
+  if ((pData->iInterlace != MNG_INTERLACE_NONE ) &&
+      (pData->iInterlace != MNG_INTERLACE_ADAM7)    )
+    MNG_ERROR (pData, MNG_INVALIDINTERLACE);
+
+#ifdef MNG_SUPPORT_DISPLAY 
+#ifndef MNG_NO_DELTA_PNG
+  if (pData->bHasDHDR)                 /* check the colortype for delta-images ! */
+  {
+    mng_imagedatap pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+
+    if (pData->iColortype != pBuf->iColortype)
+    {
+      if ( ( (pData->iColortype != MNG_COLORTYPE_INDEXED) ||
+             (pBuf->iColortype  == MNG_COLORTYPE_GRAY   )    ) &&
+           ( (pData->iColortype != MNG_COLORTYPE_GRAY   ) ||
+             (pBuf->iColortype  == MNG_COLORTYPE_INDEXED)    )    )
+        MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+    }
+  }
+#endif
+#endif
+
+  if (!pData->bHasheader)              /* first chunk ? */
+  {
+    pData->bHasheader = MNG_TRUE;      /* we've got a header */
+    pData->eImagetype = mng_it_png;    /* then this must be a PNG */
+    pData->iWidth     = pData->iDatawidth;
+    pData->iHeight    = pData->iDataheight;
+                                       /* predict alpha-depth ! */
+    if ((pData->iColortype == MNG_COLORTYPE_GRAYA  ) ||
+        (pData->iColortype == MNG_COLORTYPE_RGBA   )    )
+      pData->iAlphadepth = pData->iBitdepth;
+    else
+    if (pData->iColortype == MNG_COLORTYPE_INDEXED)
+      pData->iAlphadepth = 8;          /* worst case scenario */
+    else
+      pData->iAlphadepth = 1;  /* Possible tRNS cheap binary transparency */
+                                       /* fits on maximum canvas ? */
+    if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight))
+      MNG_WARNING (pData, MNG_IMAGETOOLARGE);
+
+#if !defined(MNG_INCLUDE_MPNG_PROPOSAL) || !defined(MNG_SUPPORT_DISPLAY)
+    if (pData->fProcessheader)         /* inform the app ? */
+      if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight))
+        MNG_ERROR (pData, MNG_APPMISCERROR);
+#endif
+  }
+
+  if (!pData->bHasDHDR)
+    pData->iImagelevel++;              /* one level deeper */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_process_display_ihdr (pData);
+#else
+  return MNG_NOERROR;                 
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+
+/* ************************************************************************** */
+
+MNG_F_SPECIALFUNC (mng_debunk_plte)
+{
+  mng_pltep  pPLTE    = (mng_pltep)pChunk;
+  mng_uint32 iRawlen  = *piRawlen;
+  mng_uint8p pRawdata = *ppRawdata;
+                                       /* length must be multiple of 3 */
+  if (((iRawlen % 3) != 0) || (iRawlen > 768))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                                       /* this is the exact length */
+  pPLTE->iEntrycount = iRawlen / 3;
+
+  MNG_COPY (pPLTE->aEntries, pRawdata, iRawlen);
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_C_SPECIALFUNC (mng_special_plte)
+{                                      /* multiple PLTE only inside BASI */
+  if ((pData->bHasPLTE) && (!pData->bHasBASI))
+    MNG_ERROR (pData, MNG_MULTIPLEERROR);
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {                                    /* only allowed for indexed-color or
+                                          rgb(a)-color! */
+    if ((pData->iColortype != MNG_COLORTYPE_RGB    ) &&
+        (pData->iColortype != MNG_COLORTYPE_INDEXED) &&
+        (pData->iColortype != MNG_COLORTYPE_RGBA   )   )
+      MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+                                       /* empty only allowed if global present */
+    if ((((mng_pltep)pChunk)->bEmpty) && (!pData->bHasglobalPLTE))
+      MNG_ERROR (pData, MNG_CANNOTBEEMPTY);
+  }
+  else
+  {
+    if (((mng_pltep)pChunk)->bEmpty)   /* cannot be empty as global! */
+      MNG_ERROR (pData, MNG_CANNOTBEEMPTY);
+  }
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+    pData->bHasPLTE = MNG_TRUE;        /* got it! */
+  else
+    pData->bHasglobalPLTE = MNG_TRUE;
+
+  pData->iPLTEcount = ((mng_pltep)pChunk)->iEntrycount;
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {
+    mng_imagep     pImage;
+    mng_imagedatap pBuf;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* processing delta-image ? */
+    {                                  /* store in object 0 !!! */
+      pImage           = (mng_imagep)pData->pObjzero;
+      pBuf             = pImage->pImgbuf;
+      pBuf->bHasPLTE   = MNG_TRUE;     /* it's definitely got a PLTE now */
+      pBuf->iPLTEcount = ((mng_pltep)pChunk)->iEntrycount;
+      MNG_COPY (pBuf->aPLTEentries, ((mng_pltep)pChunk)->aEntries,
+                sizeof (pBuf->aPLTEentries));
+    }
+    else
+#endif
+    {                                  /* get the current object */
+      pImage = (mng_imagep)pData->pCurrentobj;
+      if (!pImage)                     /* no object then dump it in obj 0 */
+        pImage = (mng_imagep)pData->pObjzero;
+
+      pBuf = pImage->pImgbuf;          /* address the object buffer */
+      pBuf->bHasPLTE = MNG_TRUE;       /* and tell it it's got a PLTE now */
+
+      if (((mng_pltep)pChunk)->bEmpty) /* if empty, inherit from global */
+      {
+        pBuf->iPLTEcount = pData->iGlobalPLTEcount;
+        MNG_COPY (pBuf->aPLTEentries, pData->aGlobalPLTEentries,
+                  sizeof (pBuf->aPLTEentries));
+
+        if (pData->bHasglobalTRNS)     /* also copy global tRNS ? */
+        {
+          mng_uint32 iRawlen2  = pData->iGlobalTRNSrawlen;
+          mng_uint8p pRawdata2 = (mng_uint8p)(pData->aGlobalTRNSrawdata);
+                                       /* indicate tRNS available */
+          pBuf->bHasTRNS = MNG_TRUE;
+                                       /* global length oke ? */
+          if ((iRawlen2 == 0) || (iRawlen2 > pBuf->iPLTEcount))
+            MNG_ERROR (pData, MNG_GLOBALLENGTHERR);
+                                       /* copy it */
+          pBuf->iTRNScount = iRawlen2;
+          MNG_COPY (pBuf->aTRNSentries, pRawdata2, iRawlen2);
+        }
+      }
+      else
+      {                                /* store fields for future reference */
+        pBuf->iPLTEcount = ((mng_pltep)pChunk)->iEntrycount;
+        MNG_COPY (pBuf->aPLTEentries, ((mng_pltep)pChunk)->aEntries,
+                  sizeof (pBuf->aPLTEentries));
+      }
+    }
+  }
+  else                                 /* store as global */
+  {
+    pData->iGlobalPLTEcount = ((mng_pltep)pChunk)->iEntrycount;
+    MNG_COPY (pData->aGlobalPLTEentries, ((mng_pltep)pChunk)->aEntries,
+              sizeof (pData->aGlobalPLTEentries));
+                                       /* create an animation object */
+    return mng_create_ani_plte (pData);
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+MNG_C_SPECIALFUNC (mng_special_idat)
+{
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasJHDR) &&
+      (pData->iJHDRalphacompression != MNG_COMPRESSION_DEFLATE))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+#endif
+                                       /* not allowed for deltatype NO_CHANGE */
+#ifndef MNG_NO_DELTA_PNG
+  if ((pData->bHasDHDR) && ((pData->iDeltatype == MNG_DELTATYPE_NOCHANGE)))
+    MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+#endif
+                                       /* can only be empty in BASI-block! */
+  if ((((mng_idatp)pChunk)->bEmpty) && (!pData->bHasBASI))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                                       /* indexed-color requires PLTE */
+  if ((pData->bHasIHDR) && (pData->iColortype == 3) && (!pData->bHasPLTE))
+    MNG_ERROR (pData, MNG_PLTEMISSING);
+
+  pData->bHasIDAT = MNG_TRUE;          /* got some IDAT now, don't we */
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+MNG_C_SPECIALFUNC (mng_special_iend)
+{                                      /* IHDR-block requires IDAT */
+  if ((pData->bHasIHDR) && (!pData->bHasIDAT))
+    MNG_ERROR (pData, MNG_IDATMISSING);
+
+  pData->iImagelevel--;                /* one level up */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* create an animation object */
+    mng_retcode iRetcode = mng_create_ani_image (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* display processing */
+    iRetcode = mng_process_display_iend (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if (!pData->bTimerset)               /* reset only if not broken !!! */
+  {
+#endif
+                                       /* IEND signals the end for most ... */
+    pData->bHasIHDR         = MNG_FALSE;
+    pData->bHasBASI         = MNG_FALSE;
+    pData->bHasDHDR         = MNG_FALSE;
+#ifdef MNG_INCLUDE_JNG
+    pData->bHasJHDR         = MNG_FALSE;
+    pData->bHasJSEP         = MNG_FALSE;
+    pData->bHasJDAA         = MNG_FALSE;
+    pData->bHasJDAT         = MNG_FALSE;
+#endif
+    pData->bHasPLTE         = MNG_FALSE;
+    pData->bHasTRNS         = MNG_FALSE;
+    pData->bHasGAMA         = MNG_FALSE;
+    pData->bHasCHRM         = MNG_FALSE;
+    pData->bHasSRGB         = MNG_FALSE;
+    pData->bHasICCP         = MNG_FALSE;
+    pData->bHasBKGD         = MNG_FALSE;
+    pData->bHasIDAT         = MNG_FALSE;
+#ifdef MNG_SUPPORT_DISPLAY
+  }
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+MNG_F_SPECIALFUNC (mng_debunk_trns)
+{
+  mng_trnsp  pTRNS    = (mng_trnsp)pChunk;
+  mng_uint32 iRawlen  = *piRawlen;
+  mng_uint8p pRawdata = *ppRawdata;
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {                                  /* not global! */
+    pTRNS->bGlobal = MNG_FALSE;
+    pTRNS->iType   = pData->iColortype;
+
+    if (iRawlen != 0)
+    {
+      switch (pData->iColortype)     /* store fields */
+      {
+        case 0: {                    /* gray */
+                  if (iRawlen != 2)
+                    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                  pTRNS->iGray  = mng_get_uint16 (pRawdata);
+                  break;
+                }
+        case 2: {                    /* rgb */
+                  if (iRawlen != 6)
+                    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                  pTRNS->iRed   = mng_get_uint16 (pRawdata);
+                  pTRNS->iGreen = mng_get_uint16 (pRawdata+2);
+                  pTRNS->iBlue  = mng_get_uint16 (pRawdata+4);
+                  break;
+                }
+        case 3: {                    /* indexed */
+                  if (iRawlen > 256)
+                    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                  pTRNS->iCount = iRawlen;
+                  MNG_COPY (pTRNS->aEntries, pRawdata, iRawlen);
+                  break;
+                }
+      }
+    }
+  }
+  else                               /* it's global! */
+  {
+    if (iRawlen == 0)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+    pTRNS->bGlobal = MNG_TRUE;
+    pTRNS->iType   = 0;
+    pTRNS->iRawlen = iRawlen;
+    MNG_COPY (pTRNS->aRawdata, pRawdata, iRawlen);
+
+    pData->iGlobalTRNSrawlen = iRawlen;
+    MNG_COPY (pData->aGlobalTRNSrawdata, pRawdata, iRawlen);
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_C_SPECIALFUNC (mng_special_trns)
+{                                      /* multiple tRNS only inside BASI */
+  if ((pData->bHasTRNS) && (!pData->bHasBASI))
+    MNG_ERROR (pData, MNG_MULTIPLEERROR);
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {                                    /* not allowed with full alpha-channel */
+    if ((pData->iColortype == 4) || (pData->iColortype == 6))
+      MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+
+    if (!((mng_trnsp)pChunk)->bEmpty)  /* filled ? */
+    {                                
+#ifdef MNG_SUPPORT_DISPLAY
+      if (pData->iColortype == 3)
+      {
+        mng_imagep     pImage = (mng_imagep)pData->pCurrentobj;
+        mng_imagedatap pBuf;
+
+        if (!pImage)                   /* no object then check obj 0 */
+          pImage = (mng_imagep)pData->pObjzero;
+
+        pBuf = pImage->pImgbuf;        /* address object buffer */
+
+        if (((mng_trnsp)pChunk)->iCount > pBuf->iPLTEcount)
+          MNG_ERROR (pData, MNG_INVALIDLENGTH);
+      }
+#endif
+    }
+    else                               /* if empty there must be global stuff! */
+    {
+      if (!pData->bHasglobalTRNS)
+        MNG_ERROR (pData, MNG_CANNOTBEEMPTY);
+    }
+  }
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+    pData->bHasTRNS = MNG_TRUE;        /* indicate tRNS available */
+  else
+    pData->bHasglobalTRNS = MNG_TRUE;
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {
+    mng_imagep     pImage;
+    mng_imagedatap pBuf;
+    mng_uint8p     pRawdata2;
+    mng_uint32     iRawlen2;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* processing delta-image ? */
+    {                                  /* store in object 0 !!! */
+#if defined(MNG_NO_1_2_4BIT_SUPPORT)
+      mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1,0,0,0,0,0,0,0,1};
+#endif
+      pImage = (mng_imagep)pData->pObjzero;
+      pBuf   = pImage->pImgbuf;        /* address object buffer */
+      pBuf->bHasTRNS   = MNG_TRUE;     /* tell it it's got a tRNS now */
+      pBuf->iTRNSgray  = 0;
+      pBuf->iTRNSred   = 0;
+      pBuf->iTRNSgreen = 0;
+      pBuf->iTRNSblue  = 0;
+      pBuf->iTRNScount = 0;
+
+      switch (pData->iColortype)       /* store fields for future reference */
+      {
+        case 0: {                      /* gray */
+                  pBuf->iTRNSgray  = ((mng_trnsp)pChunk)->iGray;
+#if defined(MNG_NO_1_2_4BIT_SUPPORT)
+                  pBuf->iTRNSgray *= multiplier[pData->iPNGdepth];
+#endif
+#if defined(MNG_NO_16BIT_SUPPORT)
+                  if (pData->iPNGmult == 2)
+                     pBuf->iTRNSgray >>= 8;
+#endif
+                  break;
+                }
+        case 2: {                      /* rgb */
+                  pBuf->iTRNSred   = ((mng_trnsp)pChunk)->iRed;
+                  pBuf->iTRNSgreen = ((mng_trnsp)pChunk)->iGreen;
+                  pBuf->iTRNSblue  = ((mng_trnsp)pChunk)->iBlue;
+#if defined(MNG_NO_16BIT_SUPPORT)
+                  if (pData->iPNGmult == 2)
+                  {
+                     pBuf->iTRNSred   >>= 8;
+                     pBuf->iTRNSgreen >>= 8;
+                     pBuf->iTRNSblue  >>= 8;
+                  }
+#endif
+                  break;
+                }
+        case 3: {                      /* indexed */
+                  pBuf->iTRNScount = ((mng_trnsp)pChunk)->iCount;
+                  MNG_COPY (pBuf->aTRNSentries,
+                            ((mng_trnsp)pChunk)->aEntries,
+                            ((mng_trnsp)pChunk)->iCount);
+                  break;
+                }
+      }
+    }
+    else
+#endif
+    {                                  /* address current object */
+      pImage = (mng_imagep)pData->pCurrentobj;
+
+      if (!pImage)                     /* no object then dump it in obj 0 */
+        pImage = (mng_imagep)pData->pObjzero;
+
+      pBuf = pImage->pImgbuf;          /* address object buffer */
+      pBuf->bHasTRNS   = MNG_TRUE;     /* and tell it it's got a tRNS now */
+      pBuf->iTRNSgray  = 0;
+      pBuf->iTRNSred   = 0;
+      pBuf->iTRNSgreen = 0;
+      pBuf->iTRNSblue  = 0;
+      pBuf->iTRNScount = 0;
+
+      if (((mng_trnsp)pChunk)->bEmpty) /* if empty, inherit from global */
+      {
+        iRawlen2  = pData->iGlobalTRNSrawlen;
+        pRawdata2 = (mng_ptr)(pData->aGlobalTRNSrawdata);
+                                       /* global length oke ? */
+        if ((pData->iColortype == 0) && (iRawlen2 != 2))
+          MNG_ERROR (pData, MNG_GLOBALLENGTHERR);
+
+        if ((pData->iColortype == 2) && (iRawlen2 != 6))
+          MNG_ERROR (pData, MNG_GLOBALLENGTHERR);
+
+        if ((pData->iColortype == 3) && ((iRawlen2 == 0) || (iRawlen2 > pBuf->iPLTEcount)))
+          MNG_ERROR (pData, MNG_GLOBALLENGTHERR);
+
+        switch (pData->iColortype)     /* store fields for future reference */
+        {
+          case 0: {                    /* gray */
+                    pBuf->iTRNSgray = mng_get_uint16 (pRawdata2);
+#if defined(MNG_NO_16BIT_SUPPORT)
+                    if (pData->iPNGmult == 2)
+                       pBuf->iTRNSgray >>= 8;
+#endif
+                    break;
+                  }
+          case 2: {                    /* rgb */
+                    pBuf->iTRNSred   = mng_get_uint16 (pRawdata2);
+                    pBuf->iTRNSgreen = mng_get_uint16 (pRawdata2+2);
+                    pBuf->iTRNSblue  = mng_get_uint16 (pRawdata2+4);
+#if defined(MNG_NO_16BIT_SUPPORT)
+                    if (pData->iPNGmult == 2)
+                    {
+                       pBuf->iTRNSred   >>= 8;
+                       pBuf->iTRNSgreen >>= 8;
+                       pBuf->iTRNSblue  >>= 8;
+                    }
+#endif
+                    break;
+                  }
+          case 3: {                    /* indexed */
+                    pBuf->iTRNScount = iRawlen2;
+                    MNG_COPY (pBuf->aTRNSentries, pRawdata2, iRawlen2);
+                    break;
+                  }
+        }
+      }
+      else
+      {
+        switch (pData->iColortype)     /* store fields for future reference */
+        {
+          case 0: {                    /* gray */
+                    pBuf->iTRNSgray = ((mng_trnsp)pChunk)->iGray;
+#if defined(MNG_NO_16BIT_SUPPORT)
+                    if (pData->iPNGmult == 2)
+                       pBuf->iTRNSgray >>= 8;
+#endif
+                    break;
+                  }
+          case 2: {                    /* rgb */
+                    pBuf->iTRNSred   = ((mng_trnsp)pChunk)->iRed;
+                    pBuf->iTRNSgreen = ((mng_trnsp)pChunk)->iGreen;
+                    pBuf->iTRNSblue  = ((mng_trnsp)pChunk)->iBlue;
+#if defined(MNG_NO_16BIT_SUPPORT)
+                    if (pData->iPNGmult == 2)
+                    {
+                       pBuf->iTRNSred   >>= 8;
+                       pBuf->iTRNSgreen >>= 8;
+                       pBuf->iTRNSblue  >>= 8;
+                    }
+#endif
+                    break;
+                  }
+          case 3: {                    /* indexed */
+                    pBuf->iTRNScount = ((mng_trnsp)pChunk)->iCount;
+                    MNG_COPY (pBuf->aTRNSentries,
+                              ((mng_trnsp)pChunk)->aEntries,
+                              ((mng_trnsp)pChunk)->iCount);
+                    break;
+                  }
+        }
+      }
+    }
+  }
+  else
+  {                                    /* create an animation object */
+    return mng_create_ani_trns (pData);
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+MNG_C_SPECIALFUNC (mng_special_gama)
+{
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    pData->bHasGAMA = MNG_TRUE;        /* indicate we've got it */
+  else
+    pData->bHasglobalGAMA = (mng_bool)!((mng_gamap)pChunk)->bEmpty;
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+  {
+    mng_imagep pImage;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* update delta image ? */
+      pImage = (mng_imagep)pData->pObjzero;
+    else
+#endif
+    {
+      pImage = (mng_imagep)pData->pCurrentobj;
+      if (!pImage)                     /* no object then dump it in obj 0 */
+        pImage = (mng_imagep)pData->pObjzero;
+    }
+                                       /* store for color-processing routines */
+    pImage->pImgbuf->iGamma   = ((mng_gamap)pChunk)->iGamma;
+    pImage->pImgbuf->bHasGAMA = MNG_TRUE;
+  }
+  else
+  {                                    /* store as global */
+    if (!((mng_gamap)pChunk)->bEmpty)
+      pData->iGlobalGamma = ((mng_gamap)pChunk)->iGamma;
+                                       /* create an animation object */
+    return mng_create_ani_gama (pData, pChunk);
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+MNG_C_SPECIALFUNC (mng_special_chrm)
+{
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    pData->bHasCHRM = MNG_TRUE;        /* indicate we've got it */
+  else
+    pData->bHasglobalCHRM = (mng_bool)!((mng_chrmp)pChunk)->bEmpty;
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+#ifdef MNG_INCLUDE_JNG
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    {
+      mng_imagep     pImage;
+      mng_imagedatap pBuf;
+
+#ifndef MNG_NO_DELTA_PNG
+      if (pData->bHasDHDR)             /* update delta image ? */
+        pImage = (mng_imagep)pData->pObjzero;
+      else
+#endif
+      {
+        pImage = (mng_imagep)pData->pCurrentobj;
+        if (!pImage)                   /* no object then dump it in obj 0 */
+          pImage = (mng_imagep)pData->pObjzero;
+      }
+
+      pBuf = pImage->pImgbuf;          /* address object buffer */
+      pBuf->bHasCHRM = MNG_TRUE;       /* and tell it it's got a CHRM now */
+                                       /* store for color-processing routines */
+      pBuf->iWhitepointx   = ((mng_chrmp)pChunk)->iWhitepointx;
+      pBuf->iWhitepointy   = ((mng_chrmp)pChunk)->iWhitepointy;
+      pBuf->iPrimaryredx   = ((mng_chrmp)pChunk)->iRedx;
+      pBuf->iPrimaryredy   = ((mng_chrmp)pChunk)->iRedy;
+      pBuf->iPrimarygreenx = ((mng_chrmp)pChunk)->iGreenx;
+      pBuf->iPrimarygreeny = ((mng_chrmp)pChunk)->iGreeny;
+      pBuf->iPrimarybluex  = ((mng_chrmp)pChunk)->iBluex;
+      pBuf->iPrimarybluey  = ((mng_chrmp)pChunk)->iBluey;
+    }
+    else
+    {                                  /* store as global */
+      if (!((mng_chrmp)pChunk)->bEmpty)
+      {
+        pData->iGlobalWhitepointx   = ((mng_chrmp)pChunk)->iWhitepointx;
+        pData->iGlobalWhitepointy   = ((mng_chrmp)pChunk)->iWhitepointy;
+        pData->iGlobalPrimaryredx   = ((mng_chrmp)pChunk)->iRedx;
+        pData->iGlobalPrimaryredy   = ((mng_chrmp)pChunk)->iRedy;
+        pData->iGlobalPrimarygreenx = ((mng_chrmp)pChunk)->iGreenx;
+        pData->iGlobalPrimarygreeny = ((mng_chrmp)pChunk)->iGreeny;
+        pData->iGlobalPrimarybluex  = ((mng_chrmp)pChunk)->iBluex;
+        pData->iGlobalPrimarybluey  = ((mng_chrmp)pChunk)->iBluey;
+      }
+                                       /* create an animation object */
+      return mng_create_ani_chrm (pData, pChunk);
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+MNG_C_SPECIALFUNC (mng_special_srgb)
+{
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    pData->bHasSRGB = MNG_TRUE;        /* indicate we've got it */
+  else
+    pData->bHasglobalSRGB = (mng_bool)!((mng_srgbp)pChunk)->bEmpty;
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+  {
+    mng_imagep pImage;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* update delta image ? */
+      pImage = (mng_imagep)pData->pObjzero;
+    else
+#endif
+    {
+      pImage = (mng_imagep)pData->pCurrentobj;
+      if (!pImage)                     /* no object then dump it in obj 0 */
+        pImage = (mng_imagep)pData->pObjzero;
+    }
+                                       /* store for color-processing routines */
+    pImage->pImgbuf->iRenderingintent = ((mng_srgbp)pChunk)->iRenderingintent;
+    pImage->pImgbuf->bHasSRGB         = MNG_TRUE;
+  }
+  else
+  {                                    /* store as global */
+    if (!((mng_srgbp)pChunk)->bEmpty)
+      pData->iGlobalRendintent = ((mng_srgbp)pChunk)->iRenderingintent;
+                                       /* create an animation object */
+    return mng_create_ani_srgb (pData, pChunk);
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+MNG_C_SPECIALFUNC (mng_special_iccp)
+{
+  mng_retcode       iRetcode;
+  mng_chunk_headerp pDummy;
+
+#ifdef MNG_CHECK_BAD_ICCP              /* Check for bad iCCP chunk */
+  if (!strncmp (((mng_iccpp)pChunk)->zName, "Photoshop ICC profile", 21))
+  {
+    if (((mng_iccpp)pChunk)->iProfilesize == 2615) /* is it the sRGB profile ? */
+    {
+      mng_chunk_header chunk_srgb;
+      mng_get_chunkheader (MNG_UINT_sRGB, &chunk_srgb);
+                                       /* pretend it's an sRGB chunk then ! */
+      iRetcode = mng_read_general (pData, &chunk_srgb, 1, (mng_ptr)"0", &pDummy);
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+
+      pDummy->fCleanup (pData, pDummy);  
+    }
+  }
+  else
+  {
+#endif /* MNG_CHECK_BAD_ICCP */
+
+#ifdef MNG_INCLUDE_JNG
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+      pData->bHasICCP = MNG_TRUE;      /* indicate we've got it */
+    else
+      pData->bHasglobalICCP = (mng_bool)!((mng_iccpp)pChunk)->bEmpty;
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifdef MNG_INCLUDE_JNG
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    {
+      mng_imagep pImage;
+
+#ifndef MNG_NO_DELTA_PNG
+      if (pData->bHasDHDR)             /* update delta image ? */
+      {                                /* store in object 0 ! */
+        pImage = (mng_imagep)pData->pObjzero;
+
+        if (pImage->pImgbuf->pProfile) /* profile existed ? */
+          MNG_FREEX (pData, pImage->pImgbuf->pProfile, pImage->pImgbuf->iProfilesize);
+                                       /* allocate a buffer & copy it */
+        MNG_ALLOC (pData, pImage->pImgbuf->pProfile, ((mng_iccpp)pChunk)->iProfilesize);
+        MNG_COPY  (pImage->pImgbuf->pProfile, ((mng_iccpp)pChunk)->pProfile, ((mng_iccpp)pChunk)->iProfilesize);
+                                       /* store its length as well */
+        pImage->pImgbuf->iProfilesize = ((mng_iccpp)pChunk)->iProfilesize;
+        pImage->pImgbuf->bHasICCP     = MNG_TRUE;
+      }
+      else
+#endif
+      {
+        pImage = (mng_imagep)pData->pCurrentobj;
+
+        if (!pImage)                   /* no object then dump it in obj 0 */
+          pImage = (mng_imagep)pData->pObjzero;
+
+        if (pImage->pImgbuf->pProfile) /* profile existed ? */
+          MNG_FREEX (pData, pImage->pImgbuf->pProfile, pImage->pImgbuf->iProfilesize);
+                                       /* allocate a buffer & copy it */
+        MNG_ALLOC (pData, pImage->pImgbuf->pProfile, ((mng_iccpp)pChunk)->iProfilesize);
+        MNG_COPY  (pImage->pImgbuf->pProfile, ((mng_iccpp)pChunk)->pProfile, ((mng_iccpp)pChunk)->iProfilesize);
+                                       /* store its length as well */
+        pImage->pImgbuf->iProfilesize = ((mng_iccpp)pChunk)->iProfilesize;
+        pImage->pImgbuf->bHasICCP     = MNG_TRUE;
+      }
+    }
+    else
+    {                                  /* store as global */
+      if (pData->pGlobalProfile)     /* did we have a global profile ? */
+        MNG_FREEX (pData, pData->pGlobalProfile, pData->iGlobalProfilesize);
+
+      if (((mng_iccpp)pChunk)->bEmpty) /* empty chunk ? */
+      {
+        pData->iGlobalProfilesize = 0; /* reset to null */
+        pData->pGlobalProfile     = MNG_NULL;
+      }
+      else
+      {                                /* allocate a global buffer & copy it */
+        MNG_ALLOC (pData, pData->pGlobalProfile, ((mng_iccpp)pChunk)->iProfilesize);
+        MNG_COPY  (pData->pGlobalProfile, ((mng_iccpp)pChunk)->pProfile, ((mng_iccpp)pChunk)->iProfilesize);
+                                       /* store its length as well */
+        pData->iGlobalProfilesize = ((mng_iccpp)pChunk)->iProfilesize;
+      }
+                                       /* create an animation object */
+      return mng_create_ani_iccp (pData, pChunk);
+    }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_CHECK_BAD_ICCP
+  }
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tEXt
+MNG_C_SPECIALFUNC (mng_special_text)
+{
+  if (pData->fProcesstext)             /* inform the application ? */
+  {
+    mng_bool bOke = pData->fProcesstext ((mng_handle)pData, MNG_TYPE_TEXT,
+                                         ((mng_textp)pChunk)->zKeyword,
+                                         ((mng_textp)pChunk)->zText, 0, 0);
+    if (!bOke)
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_zTXt
+MNG_C_SPECIALFUNC (mng_special_ztxt)
+{
+  if (pData->fProcesstext)             /* inform the application ? */
+  {
+    mng_bool bOke = pData->fProcesstext ((mng_handle)pData, MNG_TYPE_ZTXT,
+                                         ((mng_ztxtp)pChunk)->zKeyword,
+                                         ((mng_ztxtp)pChunk)->zText, 0, 0);
+    if (!bOke)
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iTXt
+MNG_F_SPECIALFUNC (mng_deflate_itxt)
+{
+  mng_itxtp  pITXT    = (mng_itxtp)pChunk;
+  mng_uint32 iBufsize = 0;
+  mng_uint8p pBuf     = 0;
+  mng_uint32 iTextlen = 0;
+
+  if (pITXT->iCompressionflag)         /* decompress the text ? */
+  {
+    mng_retcode iRetcode = mng_inflate_buffer (pData, *ppRawdata, *piRawlen,
+                                               &pBuf, &iBufsize, &iTextlen);
+
+    if (iRetcode)                      /* on error bail out */
+    {                                  /* don't forget to drop the temp buffer */
+      MNG_FREEX (pData, pBuf, iBufsize);
+      return iRetcode;
+    }
+
+    MNG_ALLOC (pData, pITXT->zText, iTextlen+1);
+    MNG_COPY (pITXT->zText, pBuf, iTextlen);
+
+    pITXT->iTextsize = iTextlen;
+
+    MNG_FREEX (pData, pBuf, iBufsize);
+
+  } else {
+
+    MNG_ALLOC (pData, pITXT->zText, (*piRawlen)+1);
+    MNG_COPY (pITXT->zText, *ppRawdata, *piRawlen);
+
+    pITXT->iTextsize = *piRawlen;
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iTXt
+MNG_C_SPECIALFUNC (mng_special_itxt)
+{
+  if (pData->fProcesstext)             /* inform the application ? */
+  {
+    mng_bool bOke = pData->fProcesstext ((mng_handle)pData, MNG_TYPE_ITXT,
+                                         ((mng_itxtp)pChunk)->zKeyword,
+                                         ((mng_itxtp)pChunk)->zText,
+                                         ((mng_itxtp)pChunk)->zLanguage,
+                                         ((mng_itxtp)pChunk)->zTranslation);
+    if (!bOke)
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_bKGD
+MNG_C_SPECIALFUNC (mng_special_bkgd)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  mng_imagep     pImage = (mng_imagep)pData->pCurrentobj;
+  mng_imagedatap pBuf;
+#endif
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    pData->bHasBKGD = MNG_TRUE;        /* indicate bKGD available */
+  else
+    pData->bHasglobalBKGD = (mng_bool)!(((mng_bkgdp)pChunk)->bEmpty);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if (!pImage)                         /* if no object dump it in obj 0 */
+    pImage = (mng_imagep)pData->pObjzero;
+  pBuf = pImage->pImgbuf;              /* address object buffer */
+
+#ifdef MNG_INCLUDE_JNG
+  if (pData->bHasJHDR)
+  {
+    pBuf->bHasBKGD = MNG_TRUE;         /* tell the object it's got bKGD now */
+
+    switch (pData->iJHDRcolortype)     /* store fields for future reference */
+    {
+      case  8 : ;                      /* gray */
+      case 12 : {                      /* graya */
+                  pBuf->iBKGDgray  = ((mng_bkgdp)pChunk)->iGray;
+                  break;
+                }
+      case 10 : ;                      /* rgb */
+      case 14 : {                      /* rgba */
+                  pBuf->iBKGDred   = ((mng_bkgdp)pChunk)->iRed;
+                  pBuf->iBKGDgreen = ((mng_bkgdp)pChunk)->iGreen;
+                  pBuf->iBKGDblue  = ((mng_bkgdp)pChunk)->iBlue;
+                  break;
+                }
+    }
+  }
+  else
+#endif /* MNG_INCLUDE_JNG */
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {
+    pBuf->bHasBKGD = MNG_TRUE;         /* tell the object it's got bKGD now */
+
+    switch (pData->iColortype)         /* store fields for future reference */
+    {
+      case 0 : ;                        /* gray */
+      case 4 : {                        /* graya */
+                 pBuf->iBKGDgray  = ((mng_bkgdp)pChunk)->iGray;
+                 break;
+               }
+      case 2 : ;                        /* rgb */
+      case 6 : {                        /* rgba */
+                 pBuf->iBKGDred   = ((mng_bkgdp)pChunk)->iRed;
+                 pBuf->iBKGDgreen = ((mng_bkgdp)pChunk)->iGreen;
+                 pBuf->iBKGDblue  = ((mng_bkgdp)pChunk)->iBlue;
+                 break;
+               }
+      case 3 : {                        /* indexed */
+                 pBuf->iBKGDindex = ((mng_bkgdp)pChunk)->iIndex;
+                 break;
+               }
+    }
+  }
+  else                                 /* store as global */
+  {
+    if (!(((mng_bkgdp)pChunk)->bEmpty))
+    {
+      pData->iGlobalBKGDred   = ((mng_bkgdp)pChunk)->iRed;
+      pData->iGlobalBKGDgreen = ((mng_bkgdp)pChunk)->iGreen;
+      pData->iGlobalBKGDblue  = ((mng_bkgdp)pChunk)->iBlue;
+    }
+                                       /* create an animation object */
+    return mng_create_ani_bkgd (pData);
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYs
+MNG_C_SPECIALFUNC (mng_special_phys)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sBIT
+MNG_C_SPECIALFUNC (mng_special_sbit)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+MNG_F_SPECIALFUNC (mng_splt_entries)
+{
+  mng_spltp  pSPLT    = (mng_spltp)pChunk;
+  mng_uint32 iRawlen  = *piRawlen;
+  mng_uint8p pRawdata = *ppRawdata;
+
+  if ((pSPLT->iSampledepth != MNG_BITDEPTH_8 ) &&
+      (pSPLT->iSampledepth != MNG_BITDEPTH_16)   )
+    MNG_ERROR (pData, MNG_INVSAMPLEDEPTH);
+                                       /* check remaining length */
+  if ( ((pSPLT->iSampledepth == MNG_BITDEPTH_8 ) && (iRawlen %  6 != 0)) ||
+       ((pSPLT->iSampledepth == MNG_BITDEPTH_16) && (iRawlen % 10 != 0))    )
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  if (pSPLT->iSampledepth == MNG_BITDEPTH_8)
+    pSPLT->iEntrycount = iRawlen / 6;
+  else
+    pSPLT->iEntrycount = iRawlen / 10;
+
+  if (iRawlen)
+  {
+    MNG_ALLOC (pData, pSPLT->pEntries, iRawlen);
+    MNG_COPY (pSPLT->pEntries, pRawdata, iRawlen);
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+MNG_C_SPECIALFUNC (mng_special_splt)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_hIST
+MNG_F_SPECIALFUNC (mng_hist_entries)
+{
+  mng_histp  pHIST    = (mng_histp)pChunk;
+  mng_uint32 iRawlen  = *piRawlen;
+  mng_uint8p pRawdata = *ppRawdata;
+  mng_uint32 iX;
+
+  if ( ((iRawlen & 0x01) != 0) || ((iRawlen >> 1) != pData->iPLTEcount) )
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pHIST->iEntrycount = iRawlen >> 1;
+
+  for (iX = 0; iX < pHIST->iEntrycount; iX++)
+  {
+    pHIST->aEntries[iX] = mng_get_uint16 (pRawdata);
+    pRawdata += 2;
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_hIST
+MNG_C_SPECIALFUNC (mng_special_hist)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tIME
+MNG_C_SPECIALFUNC (mng_special_time)
+{
+/*  if (pData->fProcesstime) */            /* inform the application ? */
+/*  {
+
+    pData->fProcesstime ((mng_handle)pData, );
+  } */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+/* JNG chunks */
+
+#ifdef MNG_INCLUDE_JNG
+MNG_C_SPECIALFUNC (mng_special_jhdr)
+{
+  if ((pData->eSigtype == mng_it_jng) && (pData->iChunkseq > 1))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* inside a JHDR-IEND block now */
+  pData->bHasJHDR              = MNG_TRUE;
+                                       /* and store interesting fields */
+  pData->iDatawidth            = ((mng_jhdrp)pChunk)->iWidth;
+  pData->iDataheight           = ((mng_jhdrp)pChunk)->iHeight;
+  pData->iJHDRcolortype        = ((mng_jhdrp)pChunk)->iColortype;
+  pData->iJHDRimgbitdepth      = ((mng_jhdrp)pChunk)->iImagesampledepth;
+  pData->iJHDRimgcompression   = ((mng_jhdrp)pChunk)->iImagecompression;
+  pData->iJHDRimginterlace     = ((mng_jhdrp)pChunk)->iImageinterlace;
+  pData->iJHDRalphabitdepth    = ((mng_jhdrp)pChunk)->iAlphasampledepth;
+  pData->iJHDRalphacompression = ((mng_jhdrp)pChunk)->iAlphacompression;
+  pData->iJHDRalphafilter      = ((mng_jhdrp)pChunk)->iAlphafilter;
+  pData->iJHDRalphainterlace   = ((mng_jhdrp)pChunk)->iAlphainterlace;
+
+#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT)
+  pData->iPNGmult = 1;
+  pData->iPNGdepth = pData->iJHDRalphabitdepth;
+#endif
+
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+  if (pData->iJHDRalphabitdepth < 8)
+    pData->iJHDRalphabitdepth = 8;
+#endif
+
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (pData->iJHDRalphabitdepth > 8)
+  {
+    pData->iPNGmult = 2;
+    pData->iJHDRalphabitdepth = 8;
+  }
+#endif
+                                       /* parameter validity checks */
+  if ((pData->iJHDRcolortype != MNG_COLORTYPE_JPEGGRAY  ) &&
+      (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGCOLOR ) &&
+      (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGGRAYA ) &&
+      (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGCOLORA)    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  if ((pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG8     ) &&
+      (pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG12    ) &&
+      (pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG8AND12)    )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) ||
+      (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    )
+  {
+    if ((pData->iJHDRalphabitdepth != MNG_BITDEPTH_8 )
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+        && (pData->iJHDRalphabitdepth != MNG_BITDEPTH_1 ) &&
+        (pData->iJHDRalphabitdepth != MNG_BITDEPTH_2 ) &&
+        (pData->iJHDRalphabitdepth != MNG_BITDEPTH_4 )
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+        && (pData->iJHDRalphabitdepth != MNG_BITDEPTH_16)
+#endif
+        )
+      MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+    if ((pData->iJHDRalphacompression != MNG_COMPRESSION_DEFLATE     ) &&
+        (pData->iJHDRalphacompression != MNG_COMPRESSION_BASELINEJPEG)    )
+      MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+    if ((pData->iJHDRalphacompression == MNG_COMPRESSION_BASELINEJPEG) &&
+        (pData->iJHDRalphabitdepth    !=  MNG_BITDEPTH_8             )    )
+      MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+#if defined(FILTER192) || defined(FILTER193)
+    if ((pData->iJHDRalphafilter != MNG_FILTER_ADAPTIVE ) &&
+#if defined(FILTER192) && defined(FILTER193)
+        (pData->iJHDRalphafilter != MNG_FILTER_DIFFERING) &&
+        (pData->iJHDRalphafilter != MNG_FILTER_NOFILTER )    )
+#else
+#ifdef FILTER192
+        (pData->iJHDRalphafilter != MNG_FILTER_DIFFERING)    )
+#else
+        (pData->iJHDRalphafilter != MNG_FILTER_NOFILTER )    )
+#endif
+#endif
+      MNG_ERROR (pData, MNG_INVALIDFILTER);
+#else
+    if (pData->iJHDRalphafilter)
+      MNG_ERROR (pData, MNG_INVALIDFILTER);
+#endif
+
+  }
+  else
+  {
+    if (pData->iJHDRalphabitdepth)
+      MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+    if (pData->iJHDRalphacompression)
+      MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+    if (pData->iJHDRalphafilter)
+      MNG_ERROR (pData, MNG_INVALIDFILTER);
+    if (pData->iJHDRalphainterlace)
+      MNG_ERROR (pData, MNG_INVALIDINTERLACE);
+  }
+
+  if (!pData->bHasheader)              /* first chunk ? */
+  {
+    pData->bHasheader = MNG_TRUE;      /* we've got a header */
+    pData->eImagetype = mng_it_jng;    /* then this must be a JNG */
+    pData->iWidth     = ((mng_jhdrp)pChunk)->iWidth;
+    pData->iHeight    = ((mng_jhdrp)pChunk)->iHeight;
+                                       /* predict alpha-depth ! */
+    if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) ||
+        (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    )
+      pData->iAlphadepth = pData->iJHDRalphabitdepth;
+    else
+      pData->iAlphadepth = 0;
+                                       /* fits on maximum canvas ? */
+    if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight))
+      MNG_WARNING (pData, MNG_IMAGETOOLARGE);
+
+    if (pData->fProcessheader)         /* inform the app ? */
+      if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight))
+        MNG_ERROR (pData, MNG_APPMISCERROR);
+
+  }
+
+  pData->iColortype = 0;               /* fake grayscale for other routines */
+  pData->iImagelevel++;                /* one level deeper */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode = mng_process_display_jhdr (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (((mng_jhdrp)pChunk)->iAlphasampledepth > 8)
+    ((mng_jhdrp)pChunk)->iAlphasampledepth = 8;
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+MNG_C_SPECIALFUNC (mng_special_jdaa)
+{
+  if (pData->iJHDRalphacompression != MNG_COMPRESSION_BASELINEJPEG)
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  pData->bHasJDAA = MNG_TRUE;          /* got some JDAA now, don't we */
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+MNG_C_SPECIALFUNC (mng_special_jdat)
+{
+  pData->bHasJDAT = MNG_TRUE;          /* got some JDAT now, don't we */
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+MNG_C_SPECIALFUNC (mng_special_jsep)
+{
+  pData->bHasJSEP = MNG_TRUE;          /* indicate we've had the 8-/12-bit separator */
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+/* MNG chunks */
+
+MNG_C_SPECIALFUNC (mng_special_mhdr)
+{
+  if (pData->bHasheader)               /* can only be the first chunk! */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  pData->bHasMHDR     = MNG_TRUE;      /* oh boy, a real MNG */
+  pData->bHasheader   = MNG_TRUE;      /* we've got a header */
+  pData->eImagetype   = mng_it_mng;    /* fill header fields */
+  pData->iWidth       = ((mng_mhdrp)pChunk)->iWidth;
+  pData->iHeight      = ((mng_mhdrp)pChunk)->iHeight;
+  pData->iTicks       = ((mng_mhdrp)pChunk)->iTicks;
+  pData->iLayercount  = ((mng_mhdrp)pChunk)->iLayercount;
+  pData->iFramecount  = ((mng_mhdrp)pChunk)->iFramecount;
+  pData->iPlaytime    = ((mng_mhdrp)pChunk)->iPlaytime;
+  pData->iSimplicity  = ((mng_mhdrp)pChunk)->iSimplicity;
+#ifndef MNG_NO_OLD_VERSIONS
+  pData->bPreDraft48  = MNG_FALSE;
+#endif
+                                       /* predict alpha-depth */
+  if ((pData->iSimplicity & 0x00000001) == 0)
+#ifndef MNG_NO_16BIT_SUPPORT
+    pData->iAlphadepth = 16;           /* no indicators = assume the worst */
+#else
+    pData->iAlphadepth = 8;            /* anything else = assume the worst */
+#endif
+  else
+  if ((pData->iSimplicity & 0x00000008) == 0)
+    pData->iAlphadepth = 0;            /* no transparency at all */
+  else
+  if ((pData->iSimplicity & 0x00000140) == 0x00000040)
+    pData->iAlphadepth = 1;            /* no semi-transparency guaranteed */
+  else
+#ifndef MNG_NO_16BIT_SUPPORT
+    pData->iAlphadepth = 16;           /* anything else = assume the worst */
+#else
+    pData->iAlphadepth = 8;            /* anything else = assume the worst */
+#endif
+
+#ifdef MNG_INCLUDE_JNG                 /* can we handle the complexity ? */
+  if (pData->iSimplicity & 0x0000FC00)
+#else
+  if (pData->iSimplicity & 0x0000FC10)
+#endif
+    MNG_ERROR (pData, MNG_MNGTOOCOMPLEX);
+                                       /* fits on maximum canvas ? */
+  if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight))
+    MNG_WARNING (pData, MNG_IMAGETOOLARGE);
+
+  if (pData->fProcessheader)           /* inform the app ? */
+    if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight))
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+
+  pData->iImagelevel++;                /* one level deeper */
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+MNG_C_SPECIALFUNC (mng_special_mend)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* do something */
+    mng_retcode iRetcode = mng_process_display_mend (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    if (!pData->iTotalframes)          /* save totals */
+      pData->iTotalframes   = pData->iFrameseq;
+    if (!pData->iTotallayers)
+      pData->iTotallayers   = pData->iLayerseq;
+    if (!pData->iTotalplaytime)
+      pData->iTotalplaytime = pData->iFrametime;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  pData->bHasMHDR = MNG_FALSE;         /* end of the line, bro! */
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+MNG_F_SPECIALFUNC (mng_debunk_loop)
+{
+  mng_loopp  pLOOP    = (mng_loopp)pChunk;
+  mng_uint32 iRawlen  = *piRawlen;
+  mng_uint8p pRawdata = *ppRawdata;
+
+  if (iRawlen >= 5)                    /* length checks */
+  {
+    if (iRawlen >= 6)
+    {
+      if ((iRawlen - 6) % 4 != 0)
+        MNG_ERROR (pData, MNG_INVALIDLENGTH);
+    }
+  }
+  else
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  if (iRawlen >= 5)                  /* store the fields */
+  {
+    pLOOP->iLevel  = *pRawdata;
+
+#ifndef MNG_NO_OLD_VERSIONS
+    if (pData->bPreDraft48)
+    {
+      pLOOP->iTermination = *(pRawdata+1);
+      pLOOP->iRepeat = mng_get_uint32 (pRawdata+2);
+    }
+    else
+#endif
+    {
+      pLOOP->iRepeat = mng_get_uint32 (pRawdata+1);
+    }
+
+    if (iRawlen >= 6)
+    {
+#ifndef MNG_NO_OLD_VERSIONS
+      if (!pData->bPreDraft48)
+#endif
+        pLOOP->iTermination = *(pRawdata+5);
+
+      if (iRawlen >= 10)
+      {
+        pLOOP->iItermin = mng_get_uint32 (pRawdata+6);
+
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+        if (iRawlen >= 14)
+        {
+          pLOOP->iItermax = mng_get_uint32 (pRawdata+10);
+          pLOOP->iCount   = (iRawlen - 14) / 4;
+
+          if (pLOOP->iCount)
+          {
+            MNG_ALLOC (pData, pLOOP->pSignals, pLOOP->iCount << 2);
+
+#ifndef MNG_BIGENDIAN_SUPPORTED
+            {
+              mng_uint32  iX;
+              mng_uint8p  pIn  = pRawdata + 14;
+              mng_uint32p pOut = (mng_uint32p)pLOOP->pSignals;
+
+              for (iX = 0; iX < pLOOP->iCount; iX++)
+              {
+                *pOut++ = mng_get_uint32 (pIn);
+                pIn += 4;
+              }
+            }
+#else
+            MNG_COPY (pLOOP->pSignals, pRawdata + 14, pLOOP->iCount << 2);
+#endif /* !MNG_BIGENDIAN_SUPPORTED */
+          }
+        }
+#endif
+      }
+    }
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+MNG_C_SPECIALFUNC (mng_special_loop)
+{
+  if (!pData->bCacheplayback)          /* must store playback info to work!! */
+    MNG_ERROR (pData, MNG_LOOPWITHCACHEOFF);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode;
+
+    pData->bHasLOOP = MNG_TRUE;        /* indicate we're inside a loop */
+                                       /* create the LOOP ani-object */
+    iRetcode = mng_create_ani_loop (pData, pChunk);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* skip till matching ENDL if iteration=0 */
+    if ((!pData->bSkipping) && (((mng_loopp)pChunk)->iRepeat == 0))
+      pData->bSkipping = MNG_TRUE;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+MNG_C_SPECIALFUNC (mng_special_endl)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  if (pData->bHasLOOP)                 /* are we really processing a loop ? */
+  {
+    mng_uint8 iLevel = ((mng_endlp)pChunk)->iLevel;
+                                       /* create an ENDL animation object */
+    return mng_create_ani_endl (pData, iLevel);
+  }
+  else
+    MNG_ERROR (pData, MNG_NOMATCHINGLOOP);
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DEFI
+MNG_C_SPECIALFUNC (mng_special_defi)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  mng_retcode iRetcode;
+
+  pData->iDEFIobjectid     = ((mng_defip)pChunk)->iObjectid;
+  pData->bDEFIhasdonotshow = ((mng_defip)pChunk)->bHasdonotshow;
+  pData->iDEFIdonotshow    = ((mng_defip)pChunk)->iDonotshow;
+  pData->bDEFIhasconcrete  = ((mng_defip)pChunk)->bHasconcrete;
+  pData->iDEFIconcrete     = ((mng_defip)pChunk)->iConcrete;
+  pData->bDEFIhasloca      = ((mng_defip)pChunk)->bHasloca;
+  pData->iDEFIlocax        = ((mng_defip)pChunk)->iXlocation;
+  pData->iDEFIlocay        = ((mng_defip)pChunk)->iYlocation;
+  pData->bDEFIhasclip      = ((mng_defip)pChunk)->bHasclip;
+  pData->iDEFIclipl        = ((mng_defip)pChunk)->iLeftcb;
+  pData->iDEFIclipr        = ((mng_defip)pChunk)->iRightcb;
+  pData->iDEFIclipt        = ((mng_defip)pChunk)->iTopcb;
+  pData->iDEFIclipb        = ((mng_defip)pChunk)->iBottomcb;
+                                       /* create an animation object */
+  iRetcode = mng_create_ani_defi (pData);
+  if (!iRetcode)                       /* do display processing */
+    iRetcode = mng_process_display_defi (pData);
+  return iRetcode;
+#else
+  return MNG_NOERROR;                  /* done */
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BASI
+MNG_C_SPECIALFUNC (mng_special_basi)
+{
+  pData->bHasBASI     = MNG_TRUE;      /* inside a BASI-IEND block now */
+                                       /* store interesting fields */
+  pData->iDatawidth   = ((mng_basip)pChunk)->iWidth;
+  pData->iDataheight  = ((mng_basip)pChunk)->iHeight;
+  pData->iBitdepth    = ((mng_basip)pChunk)->iBitdepth;   
+  pData->iColortype   = ((mng_basip)pChunk)->iColortype;
+  pData->iCompression = ((mng_basip)pChunk)->iCompression;
+  pData->iFilter      = ((mng_basip)pChunk)->iFilter;
+  pData->iInterlace   = ((mng_basip)pChunk)->iInterlace;
+
+#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT)
+  pData->iPNGmult = 1;
+  pData->iPNGdepth = pData->iBitdepth;
+#endif
+
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+  if (pData->iBitdepth < 8)
+    pData->iBitdepth = 8;
+#endif
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (pData->iBitdepth > 8)
+    {
+      pData->iBitdepth = 8;
+      pData->iPNGmult = 2;
+    }
+#endif
+
+  if ((pData->iBitdepth !=  8)      /* parameter validity checks */
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+      && (pData->iBitdepth !=  1) &&
+      (pData->iBitdepth !=  2) &&
+      (pData->iBitdepth !=  4)
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+      && (pData->iBitdepth != 16)
+#endif
+      )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if ((pData->iColortype != MNG_COLORTYPE_GRAY   ) &&
+      (pData->iColortype != MNG_COLORTYPE_RGB    ) &&
+      (pData->iColortype != MNG_COLORTYPE_INDEXED) &&
+      (pData->iColortype != MNG_COLORTYPE_GRAYA  ) &&
+      (pData->iColortype != MNG_COLORTYPE_RGBA   )    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  if ((pData->iColortype == MNG_COLORTYPE_INDEXED) && (pData->iBitdepth > 8))
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if (((pData->iColortype == MNG_COLORTYPE_RGB    ) ||
+       (pData->iColortype == MNG_COLORTYPE_GRAYA  ) ||
+       (pData->iColortype == MNG_COLORTYPE_RGBA   )    ) &&
+      (pData->iBitdepth < 8                            )    )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+#if defined(FILTER192) || defined(FILTER193)
+  if ((pData->iFilter != MNG_FILTER_ADAPTIVE ) &&
+#if defined(FILTER192) && defined(FILTER193)
+      (pData->iFilter != MNG_FILTER_DIFFERING) &&
+      (pData->iFilter != MNG_FILTER_NOFILTER )    )
+#else
+#ifdef FILTER192
+      (pData->iFilter != MNG_FILTER_DIFFERING)    )
+#else
+      (pData->iFilter != MNG_FILTER_NOFILTER )    )
+#endif
+#endif
+    MNG_ERROR (pData, MNG_INVALIDFILTER);
+#else
+  if (pData->iFilter)
+    MNG_ERROR (pData, MNG_INVALIDFILTER);
+#endif
+
+  pData->iImagelevel++;                /* one level deeper */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* create an animation object */
+    mng_retcode iRetcode = mng_create_ani_basi (pData, pChunk);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (((mng_basip)pChunk)->iBitdepth > 8)
+    ((mng_basip)pChunk)->iBitdepth = 8;
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLON
+MNG_C_SPECIALFUNC (mng_special_clon)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_ani_clon (pData, pChunk);
+#else
+  return MNG_NOERROR;                  /* done */
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+MNG_F_SPECIALFUNC (mng_debunk_past)
+{
+  mng_pastp        pPAST    = (mng_pastp)pChunk;
+  mng_uint32       iRawlen  = *piRawlen;
+  mng_uint8p       pRawdata = *ppRawdata;
+  mng_uint32       iSize;
+  mng_uint32       iX;
+  mng_past_sourcep pSource;
+                                       /* check the length */
+  if ((iRawlen < 41) || (((iRawlen - 11) % 30) != 0))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pPAST->iDestid     = mng_get_uint16 (pRawdata);
+  pPAST->iTargettype = *(pRawdata+2);
+  pPAST->iTargetx    = mng_get_int32  (pRawdata+3);
+  pPAST->iTargety    = mng_get_int32  (pRawdata+7);
+  pPAST->iCount      = ((iRawlen - 11) / 30); /* how many entries again? */
+  iSize              = pPAST->iCount * sizeof (mng_past_source);
+
+  pRawdata += 11;
+                                       /* get a buffer for all the source blocks */
+  MNG_ALLOC (pData, pPAST->pSources, iSize);
+
+  pSource = (mng_past_sourcep)(pPAST->pSources);
+
+  for (iX = pPAST->iCount; iX > 0; iX--)
+  {                                    /* now copy the source blocks */
+    pSource->iSourceid     = mng_get_uint16 (pRawdata);
+    pSource->iComposition  = *(pRawdata+2);
+    pSource->iOrientation  = *(pRawdata+3);
+    pSource->iOffsettype   = *(pRawdata+4);
+    pSource->iOffsetx      = mng_get_int32 (pRawdata+5);
+    pSource->iOffsety      = mng_get_int32 (pRawdata+9);
+    pSource->iBoundarytype = *(pRawdata+13);
+    pSource->iBoundaryl    = mng_get_int32 (pRawdata+14);
+    pSource->iBoundaryr    = mng_get_int32 (pRawdata+18);
+    pSource->iBoundaryt    = mng_get_int32 (pRawdata+22);
+    pSource->iBoundaryb    = mng_get_int32 (pRawdata+26);
+
+    pSource++;
+    pRawdata += 30;
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+MNG_C_SPECIALFUNC (mng_special_past)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_ani_past (pData, pChunk);
+#else
+  return MNG_NOERROR;
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+MNG_F_SPECIALFUNC (mng_disc_entries)
+{
+  mng_discp   pDISC    = (mng_discp)pChunk;
+  mng_uint32  iRawlen  = *piRawlen;
+  mng_uint8p  pRawdata = *ppRawdata;
+
+  if ((iRawlen % 2) != 0)              /* check the length */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pDISC->iCount = (iRawlen / sizeof (mng_uint16));
+
+  if (pDISC->iCount)
+  {
+    MNG_ALLOC (pData, pDISC->pObjectids, iRawlen);
+
+#ifndef MNG_BIGENDIAN_SUPPORTED
+    {
+      mng_uint32  iX;
+      mng_uint8p  pIn  = pRawdata;
+      mng_uint16p pOut = pDISC->pObjectids;
+
+      for (iX = pDISC->iCount; iX > 0; iX--)
+      {
+        *pOut++ = mng_get_uint16 (pIn);
+        pIn += 2;
+      }
+    }
+#else
+    MNG_COPY (pDISC->pObjectids, pRawdata, iRawlen);
+#endif /* !MNG_BIGENDIAN_SUPPORTED */
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+MNG_C_SPECIALFUNC (mng_special_disc)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_ani_disc (pData, pChunk);
+#else
+  return MNG_NOERROR;                  
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BACK
+MNG_C_SPECIALFUNC (mng_special_back)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+                                       /* retrieve the fields */
+  pData->bHasBACK       = MNG_TRUE;
+  pData->iBACKred       = ((mng_backp)pChunk)->iRed;
+  pData->iBACKgreen     = ((mng_backp)pChunk)->iGreen;
+  pData->iBACKblue      = ((mng_backp)pChunk)->iBlue;
+  pData->iBACKmandatory = ((mng_backp)pChunk)->iMandatory;
+  pData->iBACKimageid   = ((mng_backp)pChunk)->iImageid;
+  pData->iBACKtile      = ((mng_backp)pChunk)->iTile;
+
+  return mng_create_ani_back (pData);
+#else
+  return MNG_NOERROR;
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+MNG_F_SPECIALFUNC (mng_fram_remainder)
+{
+  mng_framp  pFRAM     = (mng_framp)pChunk;
+  mng_uint32 iRawlen   = *piRawlen;
+  mng_uint8p pRawdata  = *ppRawdata;
+  mng_uint32 iRequired = 0;
+
+  if (iRawlen < 4)                     /* must have at least 4 bytes */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  iRequired = 4;                       /* calculate and check required remaining length */
+
+  pFRAM->iChangedelay    = *pRawdata;
+  pFRAM->iChangetimeout  = *(pRawdata+1);
+  pFRAM->iChangeclipping = *(pRawdata+2);
+  pFRAM->iChangesyncid   = *(pRawdata+3);
+
+  if (pFRAM->iChangedelay   ) { iRequired +=  4; }
+  if (pFRAM->iChangetimeout ) { iRequired +=  4; }
+  if (pFRAM->iChangeclipping) { iRequired += 17; }
+
+  if (pFRAM->iChangesyncid)
+  {
+    if ((iRawlen - iRequired) % 4 != 0)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+  else
+  {
+    if (iRawlen != iRequired)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+
+  pRawdata += 4;
+
+  if (pFRAM->iChangedelay)              /* delay changed ? */
+  {
+    pFRAM->iDelay = mng_get_uint32 (pRawdata);
+    pRawdata += 4;
+  }
+
+  if (pFRAM->iChangetimeout)            /* timeout changed ? */
+  {
+    pFRAM->iTimeout = mng_get_uint32 (pRawdata);
+    pRawdata += 4;
+  }
+
+  if (pFRAM->iChangeclipping)           /* clipping changed ? */
+  {
+    pFRAM->iBoundarytype = *pRawdata;
+    pFRAM->iBoundaryl    = mng_get_int32 (pRawdata+1);
+    pFRAM->iBoundaryr    = mng_get_int32 (pRawdata+5);
+    pFRAM->iBoundaryt    = mng_get_int32 (pRawdata+9);
+    pFRAM->iBoundaryb    = mng_get_int32 (pRawdata+13);
+    pRawdata += 17;
+  }
+
+  if (pFRAM->iChangesyncid)
+  {
+    pFRAM->iCount    = (iRawlen - iRequired) / 4;
+
+    if (pFRAM->iCount)
+    {
+      MNG_ALLOC (pData, pFRAM->pSyncids, pFRAM->iCount * 4);
+
+#ifndef MNG_BIGENDIAN_SUPPORTED
+      {
+        mng_uint32 iX;
+        mng_uint32p pOut = pFRAM->pSyncids;
+
+        for (iX = pFRAM->iCount; iX > 0; iX--)
+        {
+          *pOut++ = mng_get_uint32 (pRawdata);
+          pRawdata += 4;
+        }
+      }
+#else
+      MNG_COPY (pFRAM->pSyncids, pRawdata, pFRAM->iCount * 4);
+#endif /* !MNG_BIGENDIAN_SUPPORTED */
+    }
+  }
+
+#ifndef MNG_NO_OLD_VERSIONS
+  if (pData->bPreDraft48)              /* old style input-stream ? */
+  {
+    switch (pFRAM->iMode)              /* fix the framing mode then */
+    {
+      case  0: { break; }
+      case  1: { pFRAM->iMode = 3; break; }
+      case  2: { pFRAM->iMode = 4; break; }
+      case  3: { pFRAM->iMode = 1; break; }
+      case  4: { pFRAM->iMode = 1; break; }
+      case  5: { pFRAM->iMode = 2; break; }
+      default: { pFRAM->iMode = 1; break; }
+    }
+  }
+#endif
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+MNG_C_SPECIALFUNC (mng_special_fram)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_ani_fram (pData, pChunk);
+#else
+  return MNG_NOERROR;
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MOVE
+MNG_C_SPECIALFUNC (mng_special_move)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_ani_move (pData, pChunk);
+#else
+  return MNG_NOERROR;
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLIP
+MNG_C_SPECIALFUNC (mng_special_clip)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_ani_clip (pData, pChunk);
+#else
+  return MNG_NOERROR;                  
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SHOW
+MNG_C_SPECIALFUNC (mng_special_show)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  mng_retcode iRetcode;
+
+  if (!((mng_showp)pChunk)->bEmpty)    /* any data ? */
+  {
+    if (!((mng_showp)pChunk)->bHaslastid)
+      ((mng_showp)pChunk)->iLastid = ((mng_showp)pChunk)->iFirstid;
+
+    pData->iSHOWfromid = ((mng_showp)pChunk)->iFirstid;
+    pData->iSHOWtoid   = ((mng_showp)pChunk)->iLastid;
+    pData->iSHOWmode   = ((mng_showp)pChunk)->iMode;
+  }
+  else                                 /* use defaults then */
+  {
+    pData->iSHOWfromid = 1;
+    pData->iSHOWtoid   = 65535;
+    pData->iSHOWmode   = 2;
+  }
+                                       /* create a SHOW animation object */
+  iRetcode = mng_create_ani_show (pData);
+  if (!iRetcode)                       /* go and do it! */
+    iRetcode = mng_process_display_show (pData);
+
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_TERM
+MNG_C_SPECIALFUNC (mng_special_term)
+{
+                                       /* should be behind MHDR or SAVE !! */
+  if ((!pData->bHasSAVE) && (pData->iChunkseq > 2))
+  {
+    pData->bMisplacedTERM = MNG_TRUE;  /* indicate we found a misplaced TERM */
+                                       /* and send a warning signal!!! */
+    MNG_WARNING (pData, MNG_SEQUENCEERROR);
+  }
+
+  pData->bHasTERM = MNG_TRUE;
+
+  if (pData->fProcessterm)             /* inform the app ? */
+    if (!pData->fProcessterm (((mng_handle)pData),
+                              ((mng_termp)pChunk)->iTermaction,
+                              ((mng_termp)pChunk)->iIteraction,
+                              ((mng_termp)pChunk)->iDelay,
+                              ((mng_termp)pChunk)->iItermax))
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* create the TERM ani-object */
+    mng_retcode iRetcode = mng_create_ani_term (pData, pChunk);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* save for future reference */
+    pData->pTermaniobj = pData->pLastaniobj;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+MNG_F_SPECIALFUNC (mng_save_entries)
+{
+  mng_savep       pSAVE     = (mng_savep)pChunk;
+  mng_uint32      iRawlen   = *piRawlen;
+  mng_uint8p      pRawdata  = *ppRawdata;
+  mng_save_entryp pEntry    = MNG_NULL;
+  mng_uint32      iCount    = 0;
+  mng_uint8       iOtype    = *pRawdata;
+  mng_uint8       iEtype;
+  mng_uint8p      pTemp;
+  mng_uint8p      pNull;
+  mng_uint32      iLen;
+  mng_uint32      iOffset[2];
+  mng_uint32      iStarttime[2];
+  mng_uint32      iFramenr;
+  mng_uint32      iLayernr;
+  mng_uint32      iX;
+  mng_uint32      iNamesize;
+
+  if ((iOtype != 4) && (iOtype != 8))
+    MNG_ERROR (pData, MNG_INVOFFSETSIZE);
+
+  pSAVE->iOffsettype = iOtype;
+
+  for (iX = 0; iX < 2; iX++)       /* do this twice to get the count first ! */
+  {
+    pTemp = pRawdata + 1;
+    iLen  = iRawlen  - 1;
+
+    if (iX)                        /* second run ? */
+    {
+      MNG_ALLOC (pData, pEntry, (iCount * sizeof (mng_save_entry)));
+
+      pSAVE->iCount   = iCount;
+      pSAVE->pEntries = pEntry;
+    }
+
+    while (iLen)                   /* anything left ? */
+    {
+      iEtype = *pTemp;             /* entrytype */
+
+      if ((iEtype != 0) && (iEtype != 1) && (iEtype != 2) && (iEtype != 3))
+        MNG_ERROR (pData, MNG_INVENTRYTYPE);
+
+      pTemp++;
+
+      if (iEtype > 1)
+      {
+        iOffset    [0] = 0;
+        iOffset    [1] = 0;
+        iStarttime [0] = 0;
+        iStarttime [1] = 0;
+        iLayernr       = 0;
+        iFramenr       = 0;
+      }
+      else
+      {
+        if (iOtype == 4)
+        {
+          iOffset [0] = 0;
+          iOffset [1] = mng_get_uint32 (pTemp);
+
+          pTemp += 4;
+        }
+        else
+        {
+          iOffset [0] = mng_get_uint32 (pTemp);
+          iOffset [1] = mng_get_uint32 (pTemp+4);
+
+          pTemp += 8;
+        }
+
+        if (iEtype > 0)
+        {
+          iStarttime [0] = 0;
+          iStarttime [1] = 0;
+          iLayernr       = 0;
+          iFramenr       = 0;
+        }
+        else
+        {
+          if (iOtype == 4)
+          {
+            iStarttime [0] = 0;
+            iStarttime [1] = mng_get_uint32 (pTemp+0);
+            iLayernr       = mng_get_uint32 (pTemp+4);
+            iFramenr       = mng_get_uint32 (pTemp+8);
+
+            pTemp += 12;
+          }
+          else
+          {
+            iStarttime [0] = mng_get_uint32 (pTemp+0);
+            iStarttime [1] = mng_get_uint32 (pTemp+4);
+            iLayernr       = mng_get_uint32 (pTemp+8);
+            iFramenr       = mng_get_uint32 (pTemp+12);
+
+            pTemp += 16;
+          }
+        }
+      }
+
+      pNull = pTemp;               /* get the name length */
+      while (*pNull)
+        pNull++;
+
+      if ((pNull - pRawdata) > (mng_int32)iRawlen)
+      {
+        iNamesize = iLen;          /* no null found; so end of SAVE */
+        iLen      = 0;
+      }
+      else
+      {
+        iNamesize = pNull - pTemp; /* should be another entry */
+        iLen     -= iNamesize;
+
+        if (!iLen)                 /* must not end with a null ! */
+          MNG_ERROR (pData, MNG_ENDWITHNULL);
+      }
+
+      if (!pEntry)
+      {
+        iCount++;
+      }
+      else
+      {
+        pEntry->iEntrytype     = iEtype;
+        pEntry->iOffset    [0] = iOffset    [0];
+        pEntry->iOffset    [1] = iOffset    [1];
+        pEntry->iStarttime [0] = iStarttime [0];
+        pEntry->iStarttime [1] = iStarttime [1];
+        pEntry->iLayernr       = iLayernr;
+        pEntry->iFramenr       = iFramenr;
+        pEntry->iNamesize      = iNamesize;
+
+        if (iNamesize)
+        {
+          MNG_ALLOC (pData, pEntry->zName, iNamesize+1);
+          MNG_COPY (pEntry->zName, pTemp, iNamesize);
+        }
+
+        pEntry++;
+      }
+
+      pTemp += iNamesize;
+    }
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+MNG_C_SPECIALFUNC (mng_special_save)
+{
+  pData->bHasSAVE = MNG_TRUE;
+
+  if (pData->fProcesssave)             /* inform the application ? */
+  {
+    mng_bool bOke = pData->fProcesssave ((mng_handle)pData);
+    if (!bOke)
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode;
+
+    /* TODO: something with the parameters */
+
+                                       /* create a SAVE animation object */
+    iRetcode = mng_create_ani_save (pData);
+    if (!iRetcode)                     /* process it */
+      iRetcode = mng_process_display_save (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+MNG_C_SPECIALFUNC (mng_special_seek)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_DISPLAY
+                                       /* create a SEEK animation object */
+  iRetcode = mng_create_ani_seek (pData, pChunk);
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  if (pData->fProcessseek)             /* inform the app ? */
+    if (!pData->fProcessseek ((mng_handle)pData, ((mng_seekp)pChunk)->zName))
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_process_display_seek (pData);
+#else
+  return MNG_NOERROR;                  
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_eXPI
+MNG_C_SPECIALFUNC (mng_special_expi)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_fPRI
+MNG_C_SPECIALFUNC (mng_special_fpri)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+MNG_LOCAL mng_bool CheckKeyword (mng_datap  pData,
+                                 mng_uint8p pKeyword)
+{
+  mng_chunkid handled_chunks [] =
+  {
+    MNG_UINT_BACK,                     /* keep it sorted !!!! */
+    MNG_UINT_BASI,
+    MNG_UINT_CLIP,
+    MNG_UINT_CLON,
+#ifndef MNG_NO_DELTA_PNG
+/* TODO:    MNG_UINT_DBYK,  */
+#endif
+    MNG_UINT_DEFI,
+#ifndef MNG_NO_DELTA_PNG
+    MNG_UINT_DHDR,
+#endif
+    MNG_UINT_DISC,
+#ifndef MNG_NO_DELTA_PNG
+/* TODO:    MNG_UINT_DROP,  */
+#endif
+    MNG_UINT_ENDL,
+    MNG_UINT_FRAM,
+    MNG_UINT_IDAT,
+    MNG_UINT_IEND,
+    MNG_UINT_IHDR,
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+    MNG_UINT_IJNG,
+#endif    
+    MNG_UINT_IPNG,
+#endif
+#ifdef MNG_INCLUDE_JNG
+    MNG_UINT_JDAA,
+    MNG_UINT_JDAT,
+    MNG_UINT_JHDR,
+/* TODO:    MNG_UINT_JSEP,  */
+    MNG_UINT_JdAA,
+#endif
+    MNG_UINT_LOOP,
+    MNG_UINT_MAGN,
+    MNG_UINT_MEND,
+    MNG_UINT_MHDR,
+    MNG_UINT_MOVE,
+/* TODO:    MNG_UINT_ORDR,  */
+    MNG_UINT_PAST,
+    MNG_UINT_PLTE,
+#ifndef MNG_NO_DELTA_PNG
+    MNG_UINT_PPLT,
+    MNG_UINT_PROM,
+#endif
+    MNG_UINT_SAVE,
+    MNG_UINT_SEEK,
+    MNG_UINT_SHOW,
+    MNG_UINT_TERM,
+    MNG_UINT_bKGD,
+    MNG_UINT_cHRM,
+/* TODO:    MNG_UINT_eXPI,  */
+    MNG_UINT_evNT,
+/* TODO:    MNG_UINT_fPRI,  */
+    MNG_UINT_gAMA,
+/* TODO:    MNG_UINT_hIST,  */
+    MNG_UINT_iCCP,
+    MNG_UINT_iTXt,
+    MNG_UINT_nEED,
+/* TODO:    MNG_UINT_oFFs,  */
+/* TODO:    MNG_UINT_pCAL,  */
+/* TODO:    MNG_UINT_pHYg,  */
+/* TODO:    MNG_UINT_pHYs,  */
+/* TODO:    MNG_UINT_sBIT,  */
+/* TODO:    MNG_UINT_sCAL,  */
+/* TODO:    MNG_UINT_sPLT,  */
+    MNG_UINT_sRGB,
+    MNG_UINT_tEXt,
+    MNG_UINT_tIME,
+    MNG_UINT_tRNS,
+    MNG_UINT_zTXt,
+  };
+
+  mng_bool bOke = MNG_FALSE;
+
+  if (pData->fProcessneed)             /* does the app handle it ? */
+    bOke = pData->fProcessneed ((mng_handle)pData, (mng_pchar)pKeyword);
+
+  if (!bOke)
+  {                                    /* find the keyword length */
+    mng_uint8p pNull = pKeyword;
+    while (*pNull)
+      pNull++;
+
+    if ((pNull - pKeyword) == 4)       /* test a chunk ? */
+    {                                  /* get the chunk-id */
+      mng_chunkid iChunkid = (*pKeyword     << 24) + (*(pKeyword+1) << 16) +
+                             (*(pKeyword+2) <<  8) + (*(pKeyword+3)      );
+                                       /* binary search variables */
+      mng_int32   iTop, iLower, iUpper, iMiddle;
+                                       /* determine max index of table */
+      iTop = (sizeof (handled_chunks) / sizeof (handled_chunks [0])) - 1;
+
+      /* binary search; with 52 chunks, worst-case is 7 comparisons */
+      iLower  = 0;
+      iMiddle = iTop >> 1;
+      iUpper  = iTop;
+
+      do                                   /* the binary search itself */
+        {
+          if (handled_chunks [iMiddle] < iChunkid)
+            iLower = iMiddle + 1;
+          else if (handled_chunks [iMiddle] > iChunkid)
+            iUpper = iMiddle - 1;
+          else
+          {
+            bOke = MNG_TRUE;
+            break;
+          }
+
+          iMiddle = (iLower + iUpper) >> 1;
+        }
+      while (iLower <= iUpper);
+    }
+                                       /* test draft ? */
+    if ((!bOke) && ((pNull - pKeyword) == 8) &&
+        (*pKeyword     == 'd') && (*(pKeyword+1) == 'r') &&
+        (*(pKeyword+2) == 'a') && (*(pKeyword+3) == 'f') &&
+        (*(pKeyword+4) == 't') && (*(pKeyword+5) == ' '))
+    {
+      mng_uint32 iDraft;
+
+      iDraft = (*(pKeyword+6) - '0') * 10 + (*(pKeyword+7) - '0');
+      bOke   = (mng_bool)(iDraft <= MNG_MNG_DRAFT);
+    }
+                                       /* test MNG 1.0/1.1 ? */
+    if ((!bOke) && ((pNull - pKeyword) == 7) &&
+        (*pKeyword     == 'M') && (*(pKeyword+1) == 'N') &&
+        (*(pKeyword+2) == 'G') && (*(pKeyword+3) == '-') &&
+        (*(pKeyword+4) == '1') && (*(pKeyword+5) == '.') &&
+        ((*(pKeyword+6) == '0') || (*(pKeyword+6) == '1')))
+      bOke   = MNG_TRUE;
+                                       /* test CACHEOFF ? */
+    if ((!bOke) && ((pNull - pKeyword) == 8) &&
+        (*pKeyword     == 'C') && (*(pKeyword+1) == 'A') &&
+        (*(pKeyword+2) == 'C') && (*(pKeyword+3) == 'H') &&
+        (*(pKeyword+4) == 'E') && (*(pKeyword+5) == 'O') &&
+        (*(pKeyword+6) == 'F') && (*(pKeyword+7) == 'F'))
+    {
+      if (!pData->pFirstaniobj)        /* only if caching hasn't started yet ! */
+      {
+        bOke                  = MNG_TRUE;
+        pData->bCacheplayback = MNG_FALSE;
+        pData->bStorechunks   = MNG_FALSE;
+      }
+    }
+  }
+
+  return bOke;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+MNG_C_SPECIALFUNC (mng_special_need)
+{
+                                       /* let's check it */
+  mng_bool   bOke = MNG_TRUE;
+  mng_uint8p pNull, pTemp, pMax;
+
+  pTemp = (mng_uint8p)((mng_needp)pChunk)->zKeywords;
+  pMax  = (mng_uint8p)(pTemp + ((mng_needp)pChunk)->iKeywordssize);
+  pNull = pTemp;
+  while (*pNull)
+    pNull++;
+
+  while ((bOke) && (pNull < pMax))
+  {
+    bOke  = CheckKeyword (pData, pTemp);
+    pTemp = pNull + 1;
+    pNull = pTemp;
+    while (*pNull)
+      pNull++;
+  }
+
+  if (bOke)
+    bOke = CheckKeyword (pData, pTemp);
+
+  if (!bOke)
+    MNG_ERROR (pData, MNG_UNSUPPORTEDNEED);
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYg
+MNG_C_SPECIALFUNC (mng_special_phyg)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_C_SPECIALFUNC (mng_special_dhdr)
+{
+  if ((((mng_dhdrp)pChunk)->iDeltatype == MNG_DELTATYPE_REPLACE) && (((mng_dhdrp)pChunk)->bHasblockloc))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  if ((((mng_dhdrp)pChunk)->iDeltatype == MNG_DELTATYPE_NOCHANGE) && (((mng_dhdrp)pChunk)->bHasblocksize))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pData->bHasDHDR   = MNG_TRUE;        /* inside a DHDR-IEND block now */
+  pData->iDeltatype = ((mng_dhdrp)pChunk)->iDeltatype;
+
+  pData->iImagelevel++;                /* one level deeper */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_ani_dhdr (pData, pChunk);
+#else
+  return MNG_NOERROR;                  
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_C_SPECIALFUNC (mng_special_prom)
+{
+  if ((((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_GRAY   ) &&
+      (((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_RGB    ) &&
+      (((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_INDEXED) &&
+      (((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_GRAYA  ) &&
+      (((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_RGBA   )    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (((mng_promp)pChunk)->iSampledepth == MNG_BITDEPTH_16 )
+      ((mng_promp)pChunk)->iSampledepth = MNG_BITDEPTH_8;
+#endif
+
+  if ((((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_1 ) &&
+      (((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_2 ) &&
+      (((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_4 ) &&
+      (((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_8 )
+#ifndef MNG_NO_16BIT_SUPPORT
+      && (((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_16)
+#endif
+    )
+    MNG_ERROR (pData, MNG_INVSAMPLEDEPTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode = mng_create_ani_prom (pData, pChunk);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_C_SPECIALFUNC (mng_special_ipng)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  mng_retcode iRetcode = mng_create_ani_ipng (pData);
+  if (!iRetcode)                       /* process it */
+    iRetcode = mng_process_display_ipng (pData);
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_F_SPECIALFUNC (mng_pplt_entries)
+{
+  mng_ppltp     pPPLT      = (mng_ppltp)pChunk;
+  mng_uint32    iRawlen    = *piRawlen;
+  mng_uint8p    pRawdata   = *ppRawdata;
+  mng_uint8     iDeltatype = pPPLT->iDeltatype;
+  mng_uint32    iMax       = 0;
+  mng_int32     iX, iY, iM;
+  mng_rgbpaltab aIndexentries;
+  mng_uint8arr  aAlphaentries;
+  mng_uint8arr  aUsedentries;
+                                       /* must be indexed color ! */
+  if (pData->iColortype != MNG_COLORTYPE_INDEXED)
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  for (iY = 255; iY >= 0; iY--)        /* reset arrays */
+  {
+    aIndexentries [iY].iRed   = 0;
+    aIndexentries [iY].iGreen = 0;
+    aIndexentries [iY].iBlue  = 0;
+    aAlphaentries [iY]        = 255;
+    aUsedentries  [iY]        = 0;
+  }
+
+  while (iRawlen)                      /* as long as there are entries left ... */
+  {
+    mng_uint32 iDiff;
+
+    if (iRawlen < 2)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    iX = (mng_int32)(*pRawdata);      /* get start and end index */
+    iM = (mng_int32)(*(pRawdata+1));
+
+    if (iM < iX)
+      MNG_ERROR (pData, MNG_INVALIDINDEX);
+
+    if (iM >= (mng_int32) iMax)       /* determine highest used index */
+      iMax = iM + 1;
+
+    pRawdata += 2;
+    iRawlen  -= 2;
+    iDiff = (iM - iX + 1);
+    if ((iDeltatype == MNG_DELTATYPE_REPLACERGB  ) ||
+        (iDeltatype == MNG_DELTATYPE_DELTARGB    )    )
+      iDiff = iDiff * 3;
+    else
+    if ((iDeltatype == MNG_DELTATYPE_REPLACERGBA) ||
+        (iDeltatype == MNG_DELTATYPE_DELTARGBA  )    )
+      iDiff = iDiff * 4;
+
+    if (iRawlen < iDiff)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((iDeltatype == MNG_DELTATYPE_REPLACERGB  ) ||
+        (iDeltatype == MNG_DELTATYPE_DELTARGB    )    )
+    {
+      for (iY = iX; iY <= iM; iY++)
+      {
+        aIndexentries [iY].iRed   = *pRawdata;
+        aIndexentries [iY].iGreen = *(pRawdata+1);
+        aIndexentries [iY].iBlue  = *(pRawdata+2);
+        aUsedentries  [iY]        = 1;
+
+        pRawdata += 3;
+        iRawlen  -= 3;
+      }
+    }
+    else
+    if ((iDeltatype == MNG_DELTATYPE_REPLACEALPHA) ||
+        (iDeltatype == MNG_DELTATYPE_DELTAALPHA  )    )
+    {
+      for (iY = iX; iY <= iM; iY++)
+      {
+        aAlphaentries [iY]        = *pRawdata;
+        aUsedentries  [iY]        = 1;
+
+        pRawdata++;
+        iRawlen--;
+      }
+    }
+    else
+    {
+      for (iY = iX; iY <= iM; iY++)
+      {
+        aIndexentries [iY].iRed   = *pRawdata;
+        aIndexentries [iY].iGreen = *(pRawdata+1);
+        aIndexentries [iY].iBlue  = *(pRawdata+2);
+        aAlphaentries [iY]        = *(pRawdata+3);
+        aUsedentries  [iY]        = 1;
+
+        pRawdata += 4;
+        iRawlen  -= 4;
+      }
+    }
+  }
+
+  switch (pData->iBitdepth)            /* check maximum allowed entries for bitdepth */
+  {
+    case MNG_BITDEPTH_1 : {
+                            if (iMax > 2)
+                              MNG_ERROR (pData, MNG_INVALIDINDEX);
+                            break;
+                          }
+    case MNG_BITDEPTH_2 : {
+                            if (iMax > 4)
+                              MNG_ERROR (pData, MNG_INVALIDINDEX);
+                            break;
+                          }
+    case MNG_BITDEPTH_4 : {
+                            if (iMax > 16)
+                              MNG_ERROR (pData, MNG_INVALIDINDEX);
+                            break;
+                          }
+  }
+
+  pPPLT->iCount = iMax;
+
+  for (iY = 255; iY >= 0; iY--)        
+  {
+    pPPLT->aEntries [iY].iRed   = aIndexentries [iY].iRed;
+    pPPLT->aEntries [iY].iGreen = aIndexentries [iY].iGreen;
+    pPPLT->aEntries [iY].iBlue  = aIndexentries [iY].iBlue;
+    pPPLT->aEntries [iY].iAlpha = aAlphaentries [iY];
+    pPPLT->aEntries [iY].bUsed  = (mng_bool)(aUsedentries [iY]);
+  }
+
+  {                                    /* create animation object */
+    mng_retcode iRetcode = mng_create_ani_pplt (pData, iDeltatype, iMax,
+                                                aIndexentries, aAlphaentries,
+                                                aUsedentries);
+    if (iRetcode)
+      return iRetcode;
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_C_SPECIALFUNC (mng_special_pplt)
+{
+  return MNG_NOERROR;                 
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+MNG_C_SPECIALFUNC (mng_special_ijng)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  mng_retcode iRetcode = mng_create_ani_ijng (pData);
+  if (!iRetcode)                       /* process it */
+    iRetcode = mng_process_display_ijng (pData);
+  return iRetcode;
+#else
+  return MNG_NOERROR;                  /* done */
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_F_SPECIALFUNC (mng_drop_entries)
+{
+  mng_dropp   pDROP    = (mng_dropp)pChunk;
+  mng_uint32  iRawlen  = *piRawlen;
+  mng_uint8p  pRawdata = *ppRawdata;
+  mng_uint32  iX;
+  mng_uint32p pEntry;
+                                       /* check length */
+  if ((iRawlen < 4) || ((iRawlen % 4) != 0))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  MNG_ALLOC (pData, pEntry, iRawlen);
+  pDROP->iCount      = iRawlen / 4;
+  pDROP->pChunknames = (mng_ptr)pEntry;
+
+  for (iX = pDROP->iCount; iX > 0; iX--)
+  {
+    *pEntry++ = mng_get_uint32 (pRawdata);
+    pRawdata += 4;
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+MNG_C_SPECIALFUNC (mng_special_drop)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+MNG_C_SPECIALFUNC (mng_special_dbyk)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+MNG_F_SPECIALFUNC (mng_ordr_entries)
+{
+  mng_ordrp       pORDR    = (mng_ordrp)pChunk;
+  mng_uint32      iRawlen  = *piRawlen;
+  mng_uint8p      pRawdata = *ppRawdata;
+  mng_uint32      iX;
+  mng_ordr_entryp pEntry;
+                                       /* check length */
+  if ((iRawlen < 5) || ((iRawlen % 5) != 0))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  MNG_ALLOC (pData, pEntry, iRawlen);
+  pORDR->iCount   = iRawlen / 5;
+  pORDR->pEntries = (mng_ptr)pEntry;
+
+  for (iX = pORDR->iCount; iX > 0; iX--)
+  {
+    pEntry->iChunkname = mng_get_uint32 (pRawdata);
+    pEntry->iOrdertype = *(pRawdata+4);
+    pEntry++;
+    pRawdata += 5;
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+MNG_C_SPECIALFUNC (mng_special_ordr)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+MNG_F_SPECIALFUNC (mng_debunk_magn)
+{
+  mng_magnp  pMAGN    = (mng_magnp)pChunk;
+  mng_uint32 iRawlen  = *piRawlen;
+  mng_uint8p pRawdata = *ppRawdata;
+  mng_bool   bFaulty;
+                                       /* check length */
+  if (iRawlen > 20)
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  /* following is an ugly hack to allow faulty layout caused by previous
+     versions of libmng and MNGeye, which wrote MAGN with a 16-bit
+     MethodX/MethodY (as opposed to the proper 8-bit as defined in the spec!) */
+
+  if ((iRawlen ==  6) || (iRawlen ==  8) || (iRawlen == 10) || (iRawlen == 12) ||
+      (iRawlen == 14) || (iRawlen == 16) || (iRawlen == 20))
+    bFaulty = MNG_TRUE;                /* these lengths are all wrong */
+  else                                 /* length 18 can be right or wrong !!! */
+  if ((iRawlen ==  18) && (mng_get_uint16 (pRawdata+4) <= 5) &&
+      (mng_get_uint16 (pRawdata+6)  < 256) &&
+      (mng_get_uint16 (pRawdata+8)  < 256) &&
+      (mng_get_uint16 (pRawdata+10) < 256) &&
+      (mng_get_uint16 (pRawdata+12) < 256) &&
+      (mng_get_uint16 (pRawdata+14) < 256) &&
+      (mng_get_uint16 (pRawdata+16) < 256))
+    bFaulty = MNG_TRUE;                /* this is very likely the wrong layout */
+  else
+    bFaulty = MNG_FALSE;               /* all other cases are handled as right */
+
+  if (bFaulty)                         /* wrong layout ? */
+  {
+    if (iRawlen > 0)                   /* get the fields */
+      pMAGN->iFirstid = mng_get_uint16 (pRawdata);
+    else
+      pMAGN->iFirstid = 0;
+
+    if (iRawlen > 2)
+      pMAGN->iLastid  = mng_get_uint16 (pRawdata+2);
+    else
+      pMAGN->iLastid  = pMAGN->iFirstid;
+
+    if (iRawlen > 4)
+      pMAGN->iMethodX = (mng_uint8)(mng_get_uint16 (pRawdata+4));
+    else
+      pMAGN->iMethodX = 0;
+
+    if (iRawlen > 6)
+      pMAGN->iMX      = mng_get_uint16 (pRawdata+6);
+    else
+      pMAGN->iMX      = 1;
+
+    if (iRawlen > 8)
+      pMAGN->iMY      = mng_get_uint16 (pRawdata+8);
+    else
+      pMAGN->iMY      = pMAGN->iMX;
+
+    if (iRawlen > 10)
+      pMAGN->iML      = mng_get_uint16 (pRawdata+10);
+    else
+      pMAGN->iML      = pMAGN->iMX;
+
+    if (iRawlen > 12)
+      pMAGN->iMR      = mng_get_uint16 (pRawdata+12);
+    else
+      pMAGN->iMR      = pMAGN->iMX;
+
+    if (iRawlen > 14)
+      pMAGN->iMT      = mng_get_uint16 (pRawdata+14);
+    else
+      pMAGN->iMT      = pMAGN->iMY;
+
+    if (iRawlen > 16)
+      pMAGN->iMB      = mng_get_uint16 (pRawdata+16);
+    else
+      pMAGN->iMB      = pMAGN->iMY;
+
+    if (iRawlen > 18)
+      pMAGN->iMethodY = (mng_uint8)(mng_get_uint16 (pRawdata+18));
+    else
+      pMAGN->iMethodY = pMAGN->iMethodX;
+  }
+  else                                 /* proper layout !!!! */
+  {
+    if (iRawlen > 0)                   /* get the fields */
+      pMAGN->iFirstid = mng_get_uint16 (pRawdata);
+    else
+      pMAGN->iFirstid = 0;
+
+    if (iRawlen > 2)
+      pMAGN->iLastid  = mng_get_uint16 (pRawdata+2);
+    else
+      pMAGN->iLastid  = pMAGN->iFirstid;
+
+    if (iRawlen > 4)
+      pMAGN->iMethodX = *(pRawdata+4);
+    else
+      pMAGN->iMethodX = 0;
+
+    if (iRawlen > 5)
+      pMAGN->iMX      = mng_get_uint16 (pRawdata+5);
+    else
+      pMAGN->iMX      = 1;
+
+    if (iRawlen > 7)
+      pMAGN->iMY      = mng_get_uint16 (pRawdata+7);
+    else
+      pMAGN->iMY      = pMAGN->iMX;
+
+    if (iRawlen > 9)
+      pMAGN->iML      = mng_get_uint16 (pRawdata+9);
+    else
+      pMAGN->iML      = pMAGN->iMX;
+
+    if (iRawlen > 11)
+      pMAGN->iMR      = mng_get_uint16 (pRawdata+11);
+    else
+      pMAGN->iMR      = pMAGN->iMX;
+
+    if (iRawlen > 13)
+      pMAGN->iMT      = mng_get_uint16 (pRawdata+13);
+    else
+      pMAGN->iMT      = pMAGN->iMY;
+
+    if (iRawlen > 15)
+      pMAGN->iMB      = mng_get_uint16 (pRawdata+15);
+    else
+      pMAGN->iMB      = pMAGN->iMY;
+
+    if (iRawlen > 17)
+      pMAGN->iMethodY = *(pRawdata+17);
+    else
+      pMAGN->iMethodY = pMAGN->iMethodX;
+  }
+                                       /* check field validity */
+  if ((pMAGN->iMethodX > 5) || (pMAGN->iMethodY > 5))
+    MNG_ERROR (pData, MNG_INVALIDMETHOD);
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+MNG_C_SPECIALFUNC (mng_special_magn)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_ani_magn (pData, pChunk);
+#else
+  return MNG_NOERROR;                  
+#endif /* MNG_SUPPORT_DISPLAY */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_evNT
+MNG_F_SPECIALFUNC (mng_evnt_entries)
+{
+  mng_evntp       pEVNT = (mng_evntp)pChunk;
+  mng_uint32      iRawlen;
+  mng_uint8p      pRawdata;
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG)
+  mng_retcode     iRetcode;
+#endif
+  mng_uint8p      pNull;
+  mng_uint8       iEventtype;
+  mng_uint8       iMasktype;
+  mng_int32       iLeft;
+  mng_int32       iRight;
+  mng_int32       iTop;
+  mng_int32       iBottom;
+  mng_uint16      iObjectid;
+  mng_uint8       iIndex;
+  mng_uint32      iNamesize;
+  mng_uint32      iCount = 0;
+  mng_evnt_entryp pEntry = MNG_NULL;
+  mng_uint32      iX;
+
+  for (iX = 0; iX < 2; iX++)
+  {
+    iRawlen  = *piRawlen;
+    pRawdata = *ppRawdata;
+
+    if (iX)                            /* second run ? */
+    {
+      MNG_ALLOC (pData, pEntry, (iCount * sizeof (mng_evnt_entry)));
+      pEVNT->iCount   = iCount;
+      pEVNT->pEntries = pEntry;
+    }
+
+    while (iRawlen)                    /* anything left ? */
+    {
+      if (iRawlen < 2)                 /* must have at least 2 bytes ! */
+        MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+      iEventtype = *pRawdata;          /* eventtype */
+      if (iEventtype > 5)
+        MNG_ERROR (pData, MNG_INVALIDEVENT);
+
+      pRawdata++;
+
+      iMasktype  = *pRawdata;          /* masktype */
+      if (iMasktype > 5)
+        MNG_ERROR (pData, MNG_INVALIDMASK);
+
+      pRawdata++;
+      iRawlen -= 2;
+
+      iLeft     = 0;
+      iRight    = 0;
+      iTop      = 0;
+      iBottom   = 0;
+      iObjectid = 0;
+      iIndex    = 0;
+
+      switch (iMasktype)
+      {
+        case 1 :
+          {
+            if (iRawlen > 16)
+            {
+              iLeft     = mng_get_int32 (pRawdata);
+              iRight    = mng_get_int32 (pRawdata+4);
+              iTop      = mng_get_int32 (pRawdata+8);
+              iBottom   = mng_get_int32 (pRawdata+12);
+              pRawdata += 16;
+              iRawlen -= 16;
+            }
+            else
+              MNG_ERROR (pData, MNG_INVALIDLENGTH);
+            break;
+          }
+        case 2 :
+          {
+            if (iRawlen > 2)
+            {
+              iObjectid = mng_get_uint16 (pRawdata);
+              pRawdata += 2;
+              iRawlen -= 2;
+            }
+            else
+              MNG_ERROR (pData, MNG_INVALIDLENGTH);
+            break;
+          }
+        case 3 :
+          {
+            if (iRawlen > 3)
+            {
+              iObjectid = mng_get_uint16 (pRawdata);
+              iIndex    = *(pRawdata+2);
+              pRawdata += 3;
+              iRawlen -= 3;
+            }
+            else
+              MNG_ERROR (pData, MNG_INVALIDLENGTH);
+            break;
+          }
+        case 4 :
+          {
+            if (iRawlen > 18)
+            {
+              iLeft     = mng_get_int32 (pRawdata);
+              iRight    = mng_get_int32 (pRawdata+4);
+              iTop      = mng_get_int32 (pRawdata+8);
+              iBottom   = mng_get_int32 (pRawdata+12);
+              iObjectid = mng_get_uint16 (pRawdata+16);
+              pRawdata += 18;
+              iRawlen -= 18;
+            }
+            else
+              MNG_ERROR (pData, MNG_INVALIDLENGTH);
+            break;
+          }
+        case 5 :
+          {
+            if (iRawlen > 19)
+            {
+              iLeft     = mng_get_int32 (pRawdata);
+              iRight    = mng_get_int32 (pRawdata+4);
+              iTop      = mng_get_int32 (pRawdata+8);
+              iBottom   = mng_get_int32 (pRawdata+12);
+              iObjectid = mng_get_uint16 (pRawdata+16);
+              iIndex    = *(pRawdata+18);
+              pRawdata += 19;
+              iRawlen -= 19;
+            }
+            else
+              MNG_ERROR (pData, MNG_INVALIDLENGTH);
+            break;
+          }
+      }
+
+      pNull = pRawdata;                /* get the name length */
+      while (*pNull)
+        pNull++;
+
+      if ((pNull - pRawdata) > (mng_int32)iRawlen)
+      {
+        iNamesize = iRawlen;           /* no null found; so end of evNT */
+        iRawlen   = 0;
+      }
+      else
+      {
+        iNamesize = pNull - pRawdata;  /* should be another entry */
+        iRawlen   = iRawlen - iNamesize - 1;
+
+        if (!iRawlen)                  /* must not end with a null ! */
+          MNG_ERROR (pData, MNG_ENDWITHNULL);
+      }
+
+      if (!iX)
+      {
+        iCount++;
+      }
+      else
+      {
+        pEntry->iEventtype       = iEventtype;
+        pEntry->iMasktype        = iMasktype;
+        pEntry->iLeft            = iLeft;
+        pEntry->iRight           = iRight;
+        pEntry->iTop             = iTop;
+        pEntry->iBottom          = iBottom;
+        pEntry->iObjectid        = iObjectid;
+        pEntry->iIndex           = iIndex;
+        pEntry->iSegmentnamesize = iNamesize;
+
+        if (iNamesize)
+        {
+          MNG_ALLOC (pData, pEntry->zSegmentname, iNamesize+1);
+          MNG_COPY (pEntry->zSegmentname, pRawdata, iNamesize);
+        }
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG)
+        iRetcode = mng_create_event (pData, (mng_ptr)pEntry);
+        if (iRetcode)                    /* on error bail out */
+          return iRetcode;
+#endif
+
+        pEntry++;
+      }
+
+      pRawdata = pRawdata + iNamesize + 1;
+    }
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_evNT
+MNG_C_SPECIALFUNC (mng_special_evnt)
+{
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+MNG_C_SPECIALFUNC (mng_special_mpng)
+{
+  if ((pData->eImagetype != mng_it_png) && (pData->eImagetype != mng_it_jng))
+    MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+    
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_mpng_obj (pData, pChunk);
+#else
+  return MNG_NOERROR;
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+MNG_C_SPECIALFUNC (mng_special_ahdr)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  return mng_create_ang_obj (pData, pChunk);
+#else
+  return MNG_NOERROR;
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+MNG_F_SPECIALFUNC (mng_adat_tiles)
+{
+  if ((pData->eImagetype != mng_it_ang) || (!pData->pANG))
+    MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+
+  {
+    mng_adatp      pADAT = (mng_adatp)pChunk;
+    mng_ang_objp   pANG  = (mng_ang_objp)pData->pANG;
+    mng_uint32     iRawlen  = *piRawlen;
+    mng_uint8p     pRawdata = *ppRawdata;
+    mng_retcode    iRetcode;
+    mng_uint8p     pBuf;
+    mng_uint32     iBufsize;
+    mng_uint32     iRealsize;
+    mng_uint8p     pTemp;
+    mng_uint8p     pTemp2;
+    mng_int32      iX;
+    mng_int32      iSize;
+
+#ifdef MNG_SUPPORT_DISPLAY
+    mng_imagep     pImage;
+    mng_int32      iTemplen;
+    mng_uint8p     pSwap;
+
+    mng_processobject pProcess;
+
+    mng_uint32     iSavedatawidth;
+    mng_uint32     iSavedataheight;
+
+    mng_fptr       fSaveinitrowproc;
+    mng_fptr       fSavestorerow;
+    mng_fptr       fSaveprocessrow;
+    mng_fptr       fSavedifferrow;
+    mng_imagep     fSavestoreobj;
+    mng_imagedatap fSavestorebuf;
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+    png_imgtype    eSavepngimgtype;
+#endif
+
+    mng_uint8      iSaveinterlace;
+    mng_int8       iSavepass;
+    mng_int32      iSaverow;
+    mng_int32      iSaverowinc;
+    mng_int32      iSavecol;
+    mng_int32      iSavecolinc;
+    mng_int32      iSaverowsamples;
+    mng_int32      iSavesamplemul;
+    mng_int32      iSavesampleofs;
+    mng_int32      iSavesamplediv;
+    mng_int32      iSaverowsize;
+    mng_int32      iSaverowmax;
+    mng_int32      iSavefilterofs;
+    mng_int32      iSavepixelofs;
+    mng_uint32     iSavelevel0;
+    mng_uint32     iSavelevel1;
+    mng_uint32     iSavelevel2;
+    mng_uint32     iSavelevel3;
+    mng_uint8p     pSaveworkrow;
+    mng_uint8p     pSaveprevrow;
+    mng_uint8p     pSaverGBArow;
+    mng_bool       bSaveisRGBA16;
+    mng_bool       bSaveisOpaque;
+    mng_int32      iSavefilterbpp;
+
+    mng_int32      iSavedestl;
+    mng_int32      iSavedestt;
+    mng_int32      iSavedestr;
+    mng_int32      iSavedestb;
+    mng_int32      iSavesourcel;
+    mng_int32      iSavesourcet;
+    mng_int32      iSavesourcer;
+    mng_int32      iSavesourceb;
+#endif /* MNG_SUPPORT_DISPLAY */
+
+    iRetcode = mng_inflate_buffer (pData, pRawdata, iRawlen,
+                                   &pBuf, &iBufsize, &iRealsize);
+    if (iRetcode)                      /* on error bail out */
+    {                                  /* don't forget to drop the temp buffer */
+      MNG_FREEX (pData, pBuf, iBufsize);
+      return iRetcode;
+    }
+                                       /* get buffer for tile info in ADAT chunk */
+    pADAT->iTilessize = pANG->iNumframes * sizeof(mng_adat_tile);
+    MNG_ALLOCX (pData, pADAT->pTiles, pADAT->iTilessize);
+    if (!pADAT->pTiles)
+    {
+      pADAT->iTilessize = 0;
+      MNG_FREEX (pData, pBuf, iBufsize);
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+
+    pTemp  = pBuf;
+    pTemp2 = (mng_uint8p)pADAT->pTiles;
+
+    if (!pANG->iStillused)
+      iSize = 12;
+    else
+      iSize = 13;
+
+    for (iX = 0; iX < pANG->iNumframes; iX++)
+    {
+      MNG_COPY (pTemp2, pTemp, iSize);
+      pTemp  += iSize;
+      pTemp2 += sizeof(mng_adat_tile);
+    }
+
+#ifdef MNG_SUPPORT_DISPLAY
+                                       /* get buffer for tile info in ANG object */
+    pANG->iTilessize = pADAT->iTilessize;
+    MNG_ALLOCX (pData, pANG->pTiles, pANG->iTilessize);
+    if (!pANG->pTiles)
+    {
+      pANG->iTilessize = 0;
+      MNG_FREEX (pData, pBuf, iBufsize);
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+                                       /* copy it from the ADAT object */
+    MNG_COPY (pANG->pTiles, pADAT->pTiles, pANG->iTilessize);
+
+                                       /* save IDAT work-parms */
+    fSaveinitrowproc    = pData->fInitrowproc;
+    fSavestorerow       = pData->fDisplayrow;
+    fSaveprocessrow     = pData->fProcessrow;
+    fSavedifferrow      = pData->fDifferrow;
+    fSavestoreobj       = pData->pStoreobj;
+    fSavestorebuf       = pData->pStorebuf;
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+    eSavepngimgtype     = pData->ePng_imgtype;
+#endif
+
+    iSavedatawidth      = pData->iDatawidth;
+    iSavedataheight     = pData->iDataheight;
+    iSaveinterlace      = pData->iInterlace;
+    iSavepass           = pData->iPass;
+    iSaverow            = pData->iRow;
+    iSaverowinc         = pData->iRowinc;
+    iSavecol            = pData->iCol;
+    iSavecolinc         = pData->iColinc;
+    iSaverowsamples     = pData->iRowsamples;
+    iSavesamplemul      = pData->iSamplemul;
+    iSavesampleofs      = pData->iSampleofs;
+    iSavesamplediv      = pData->iSamplediv;
+    iSaverowsize        = pData->iRowsize;
+    iSaverowmax         = pData->iRowmax;
+    iSavefilterofs      = pData->iFilterofs;
+    iSavepixelofs       = pData->iPixelofs;
+    iSavelevel0         = pData->iLevel0;
+    iSavelevel1         = pData->iLevel1;
+    iSavelevel2         = pData->iLevel2;
+    iSavelevel3         = pData->iLevel3;
+    pSaveworkrow        = pData->pWorkrow;
+    pSaveprevrow        = pData->pPrevrow;
+    pSaverGBArow        = pData->pRGBArow;
+    bSaveisRGBA16       = pData->bIsRGBA16;
+    bSaveisOpaque       = pData->bIsOpaque;
+    iSavefilterbpp      = pData->iFilterbpp;
+    iSavedestl          = pData->iDestl;
+    iSavedestt          = pData->iDestt;
+    iSavedestr          = pData->iDestr;
+    iSavedestb          = pData->iDestb;
+    iSavesourcel        = pData->iSourcel;
+    iSavesourcet        = pData->iSourcet;
+    iSavesourcer        = pData->iSourcer;
+    iSavesourceb        = pData->iSourceb;
+
+    pData->iDatawidth   = pANG->iTilewidth;
+    pData->iDataheight  = pANG->iTileheight;
+
+    pData->iDestl       = 0;
+    pData->iDestt       = 0;
+    pData->iDestr       = pANG->iTilewidth;
+    pData->iDestb       = pANG->iTileheight;
+    pData->iSourcel     = 0;
+    pData->iSourcet     = 0;
+    pData->iSourcer     = pANG->iTilewidth;
+    pData->iSourceb     = pANG->iTileheight;
+
+    pData->fInitrowproc = MNG_NULL;
+    pData->fStorerow    = MNG_NULL;
+    pData->fProcessrow  = MNG_NULL;
+    pData->fDifferrow   = MNG_NULL;
+
+    /* clone image object to store the pixel-data from object 0 */
+    iRetcode = mng_clone_imageobject (pData, 1, MNG_FALSE, MNG_FALSE, MNG_FALSE,
+                                      MNG_FALSE, 0, 0, 0, pData->pObjzero, &pImage);
+    if (iRetcode)                      /* on error, drop temp buffer and bail */
+    {
+      MNG_FREEX (pData, pBuf, iBufsize);
+      return iRetcode;
+    }
+
+    /* make sure we got the right dimensions and interlacing */
+    iRetcode = mng_reset_object_details (pData, pImage, pANG->iTilewidth, pANG->iTileheight,
+                                         pImage->pImgbuf->iBitdepth, pImage->pImgbuf->iColortype,
+                                         pImage->pImgbuf->iCompression, pImage->pImgbuf->iFilter,
+                                         pANG->iInterlace, MNG_FALSE);
+    if (iRetcode)                      /* on error, drop temp buffer and bail */
+    {
+      MNG_FREEX (pData, pBuf, iBufsize);
+      return iRetcode;
+    }
+
+    pData->pStoreobj    = pImage;
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+    pData->fInitrowproc = (mng_fptr)mng_init_rowproc;
+    pData->ePng_imgtype = mng_png_imgtype(pData->iColortype,pData->iBitdepth);
+#else
+    switch (pData->iColortype)         /* determine row initialization routine */
+    {
+      case 0 : {                       /* gray */
+                 switch (pData->iBitdepth)
+                 {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                   case  1 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g1_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g1_i;
+
+                               break;
+                             }
+                   case  2 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g2_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g2_i;
+
+                               break;
+                             }
+                   case  4 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g4_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g4_i;
+                               break;
+                             }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g8_i;
+
+                               break;
+                             }
+#ifndef MNG_NO_16BIT_SUPPORT
+                   case 16 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g16_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g16_i;
+
+                               break;
+                             }
+#endif
+                 }
+
+                 break;
+               }
+      case 2 : {                       /* rgb */
+                 switch (pData->iBitdepth)
+                 {
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgb8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgb8_i;
+                               break;
+                             }
+#ifndef MNG_NO_16BIT_SUPPORT
+                   case 16 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgb16_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgb16_i;
+
+                               break;
+                             }
+#endif
+                 }
+
+                 break;
+               }
+      case 3 : {                       /* indexed */
+                 switch (pData->iBitdepth)
+                 {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                   case  1 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx1_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx1_i;
+
+                               break;
+                             }
+                   case  2 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx2_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx2_i;
+
+                               break;
+                             }
+                   case  4 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx4_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx4_i;
+
+                               break;
+                             }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx8_i;
+
+                               break;
+                             }
+                 }
+
+                 break;
+               }
+      case 4 : {                       /* gray+alpha */
+                 switch (pData->iBitdepth)
+                 {
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_ga8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_ga8_i;
+
+                               break;
+                             }
+#ifndef MNG_NO_16BIT_SUPPORT
+                   case 16 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_ga16_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_ga16_i;
+                               break;
+                             }
+#endif
+                 }
+
+                 break;
+               }
+      case 6 : {                       /* rgb+alpha */
+                 switch (pData->iBitdepth)
+                 {
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgba8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgba8_i;
+
+                               break;
+                             }
+#ifndef MNG_NO_16BIT_SUPPORT
+                   case 16 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgba16_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgba16_i;
+
+                               break;
+                             }
+#endif
+                 }
+
+                 break;
+               }
+    }
+#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */
+
+    pData->iFilterofs = 0;             /* determine filter characteristics */
+    pData->iLevel0    = 0;             /* default levels */
+    pData->iLevel1    = 0;    
+    pData->iLevel2    = 0;
+    pData->iLevel3    = 0;
+
+#ifdef FILTER192                       /* leveling & differing ? */
+    if (pData->iFilter == MNG_FILTER_DIFFERING)
+    {
+      switch (pData->iColortype)
+      {
+        case 0 : {
+                   if (pData->iBitdepth <= 8)
+                     pData->iFilterofs = 1;
+                   else
+                     pData->iFilterofs = 2;
+
+                   break;
+                 }
+        case 2 : {
+                   if (pData->iBitdepth <= 8)
+                     pData->iFilterofs = 3;
+                   else
+                     pData->iFilterofs = 6;
+
+                   break;
+                 }
+        case 3 : {
+                   pData->iFilterofs = 1;
+                   break;
+                 }
+        case 4 : {
+                   if (pData->iBitdepth <= 8)
+                     pData->iFilterofs = 2;
+                   else
+                     pData->iFilterofs = 4;
+
+                   break;
+                 }
+        case 6 : {
+                   if (pData->iBitdepth <= 8)
+                     pData->iFilterofs = 4;
+                   else
+                     pData->iFilterofs = 8;
+
+                   break;
+                 }
+      }
+    }
+#endif
+
+#ifdef FILTER193                       /* no adaptive filtering ? */
+    if (pData->iFilter == MNG_FILTER_NOFILTER)
+      pData->iPixelofs = pData->iFilterofs;
+    else
+#endif
+      pData->iPixelofs = pData->iFilterofs + 1;
+
+    if (pData->fInitrowproc)           /* need to initialize row processing? */
+    {
+      iRetcode = ((mng_initrowproc)pData->fInitrowproc) (pData);
+      if (iRetcode)
+      {
+         MNG_FREEX (pData, pBuf, iBufsize);
+         return iRetcode;
+      }
+    }
+                                       /* calculate remainder of buffer */
+    pTemp    = pBuf + (mng_int32)(pANG->iNumframes * iSize);
+    iTemplen = iRealsize - (mng_int32)(pANG->iNumframes * iSize);
+
+    do
+    {
+      if (iTemplen > pData->iRowmax)   /* get a pixel-row from the temp buffer */
+      {
+        MNG_COPY (pData->pWorkrow, pTemp, pData->iRowmax);
+      }
+      else
+      {
+        MNG_COPY (pData->pWorkrow, pTemp, iTemplen);
+      }
+
+      {                                /* image not completed yet ? */
+        if (pData->iRow < (mng_int32)pData->iDataheight)
+        {
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+          if (pData->iPNGdepth == 1)
+          {
+            /* Inflate Workrow to 8-bit */
+            mng_int32  iX;
+            mng_uint8p pSrc = pData->pWorkrow+1;
+            mng_uint8p pDest = pSrc + pData->iRowsize - (pData->iRowsize+7)/8;
+
+            for (iX = ((pData->iRowsize+7)/8) ; iX > 0 ; iX--)
+              *pDest++ = *pSrc++;
+
+            pDest = pData->pWorkrow+1;
+            pSrc = pDest + pData->iRowsize - (pData->iRowsize+7)/8;
+            for (iX = pData->iRowsize; ;)
+            {
+              *pDest++ = (((*pSrc)>>7)&1);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)>>6)&1);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)>>5)&1);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)>>4)&1);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)>>3)&1);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)>>2)&1);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)>>1)&1);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)   )&1);
+              if (iX-- <= 0)
+                break;
+              pSrc++;
+            }
+          }
+          else if (pData->iPNGdepth == 2)
+          {
+            /* Inflate Workrow to 8-bit */
+            mng_int32  iX;
+            mng_uint8p pSrc = pData->pWorkrow+1;
+            mng_uint8p pDest = pSrc + pData->iRowsize - (2*pData->iRowsize+7)/8;
+
+            for (iX = ((2*pData->iRowsize+7)/8) ; iX > 0 ; iX--)
+               *pDest++ = *pSrc++;
+
+            pDest = pData->pWorkrow+1;
+            pSrc = pDest + pData->iRowsize - (2*pData->iRowsize+7)/8;
+            for (iX = pData->iRowsize; ;)
+            {
+              *pDest++ = (((*pSrc)>>6)&3);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)>>4)&3);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)>>2)&3);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)   )&3);
+              if (iX-- <= 0)
+                break;
+              pSrc++;
+            }
+          }
+          else if (pData->iPNGdepth == 4)
+          {
+            /* Inflate Workrow to 8-bit */
+            mng_int32  iX;
+            mng_uint8p pSrc = pData->pWorkrow+1;
+            mng_uint8p pDest = pSrc + pData->iRowsize - (4*pData->iRowsize+7)/8;
+
+            for (iX = ((4*pData->iRowsize+7)/8) ; iX > 0 ; iX--)
+               *pDest++ = *pSrc++;
+
+            pDest = pData->pWorkrow+1;
+            pSrc = pDest + pData->iRowsize - (4*pData->iRowsize+7)/8;
+            for (iX = pData->iRowsize; ;)
+            {
+              *pDest++ = (((*pSrc)>>4)&0x0f);
+              if (iX-- <= 0)
+                break;
+              *pDest++ = (((*pSrc)   )&0x0f);
+              if (iX-- <= 0)
+                break;
+              pSrc++;
+            }
+          }
+          if (pData->iPNGdepth < 8 && pData->iColortype == 0)
+          {
+            /* Expand samples to 8-bit by LBR */
+            mng_int32  iX;
+            mng_uint8p pSrc = pData->pWorkrow+1;
+            mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1};
+
+            for (iX = pData->iRowsize; iX > 0; iX--)
+                *pSrc++ *= multiplier[pData->iPNGdepth];
+          }
+#endif
+#ifdef MNG_NO_16BIT_SUPPORT
+          if (pData->iPNGdepth > 8)
+          {
+            /* Reduce Workrow to 8-bit */
+            mng_int32  iX;
+            mng_uint8p pSrc = pData->pWorkrow+1;
+            mng_uint8p pDest = pSrc;
+
+            for (iX = pData->iRowsize; iX > 0; iX--)
+            {
+              *pDest = *pSrc;
+              pDest++;
+              pSrc+=2;
+            }
+          }
+#endif
+
+#ifdef FILTER192                       /* has leveling info ? */
+          if (pData->iFilterofs == MNG_FILTER_DIFFERING)
+            iRetcode = init_rowdiffering (pData);
+          else
+#endif
+            iRetcode = MNG_NOERROR;
+                                       /* filter the row if necessary */
+          if ((!iRetcode) && (pData->iFilterofs < pData->iPixelofs  ) &&
+                             (*(pData->pWorkrow + pData->iFilterofs))    )
+            iRetcode = mng_filter_a_row (pData);
+
+                                       /* additional leveling/differing ? */
+          if ((!iRetcode) && (pData->fDifferrow))
+          {
+            iRetcode = ((mng_differrow)pData->fDifferrow) (pData);
+
+            pSwap           = pData->pWorkrow;
+            pData->pWorkrow = pData->pPrevrow;
+            pData->pPrevrow = pSwap;   /* make sure we're processing the right data */
+          }
+
+          if (!iRetcode)
+          {
+            {                          /* process this row */
+              if ((!iRetcode) && (pData->fProcessrow))
+                iRetcode = ((mng_processrow)pData->fProcessrow) (pData);
+                                       /* store in object ? */
+              if ((!iRetcode) && (pData->fStorerow))
+                iRetcode = ((mng_storerow)pData->fStorerow)     (pData);
+            }
+          }
+
+          if (iRetcode)                   /* on error bail out */
+          {
+            MNG_FREEX (pData, pBuf, iBufsize);
+            MNG_ERROR (pData, iRetcode);
+          }
+
+          if (!pData->fDifferrow)      /* swap row-pointers */
+          {
+            pSwap           = pData->pWorkrow;
+            pData->pWorkrow = pData->pPrevrow;
+            pData->pPrevrow = pSwap;   /* so prev points to the processed row! */
+          }
+                                       /* adjust variables for next row */
+          iRetcode = mng_next_row (pData);
+
+          if (iRetcode)                   /* on error bail out */
+          {
+            MNG_FREEX (pData, pBuf, iBufsize);
+            MNG_ERROR (pData, iRetcode);
+          }
+        }
+      }
+
+      pTemp    += pData->iRowmax;
+      iTemplen -= pData->iRowmax;
+    }                                  /* until some error or EOI
+                                          or all pixels received */
+    while ( (iTemplen > 0)  &&
+            ( (pData->iRow < (mng_int32)pData->iDataheight) ||
+              ( (pData->iPass >= 0) && (pData->iPass < 7) )    )    );
+
+    mng_cleanup_rowproc (pData);       /* cleanup row processing buffers !! */
+
+                                       /* restore saved work-parms */
+    pData->iDatawidth   = iSavedatawidth;
+    pData->iDataheight  = iSavedataheight;
+
+    pData->fInitrowproc = fSaveinitrowproc;
+    pData->fDisplayrow  = fSavestorerow;
+    pData->fProcessrow  = fSaveprocessrow;
+    pData->fDifferrow   = fSavedifferrow;
+    pData->pStoreobj    = fSavestoreobj;
+    pData->pStorebuf    = fSavestorebuf;
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+    pData->ePng_imgtype = eSavepngimgtype;
+#endif
+
+    pData->iInterlace   = iSaveinterlace;
+    pData->iPass        = iSavepass;
+    pData->iRow         = iSaverow;
+    pData->iRowinc      = iSaverowinc;
+    pData->iCol         = iSavecol;
+    pData->iColinc      = iSavecolinc;
+    pData->iRowsamples  = iSaverowsamples;
+    pData->iSamplemul   = iSavesamplemul;
+    pData->iSampleofs   = iSavesampleofs;
+    pData->iSamplediv   = iSavesamplediv;
+    pData->iRowsize     = iSaverowsize;
+    pData->iRowmax      = iSaverowmax;
+    pData->iFilterofs   = iSavefilterofs;
+    pData->iPixelofs    = iSavepixelofs;
+    pData->iLevel0      = iSavelevel0;
+    pData->iLevel1      = iSavelevel1;
+    pData->iLevel2      = iSavelevel2;
+    pData->iLevel3      = iSavelevel3;
+    pData->pWorkrow     = pSaveworkrow;
+    pData->pPrevrow     = pSaveprevrow;
+    pData->pRGBArow     = pSaverGBArow;
+    pData->bIsRGBA16    = bSaveisRGBA16;
+    pData->bIsOpaque    = bSaveisOpaque;
+    pData->iFilterbpp   = iSavefilterbpp;
+    pData->iDestl       = iSavedestl;
+    pData->iDestt       = iSavedestt;
+    pData->iDestr       = iSavedestr;
+    pData->iDestb       = iSavedestb;
+    pData->iSourcel     = iSavesourcel;
+    pData->iSourcet     = iSavesourcet;
+    pData->iSourcer     = iSavesourcer;
+    pData->iSourceb     = iSavesourceb;
+
+                                       /* create the animation directives ! */
+    pProcess = (mng_processobject)pANG->sHeader.fProcess;
+    iRetcode = pProcess (pData, (mng_objectp)pData->pANG);
+    if (iRetcode)
+      return iRetcode;
+
+#endif /* MNG_SUPPORT_DISPLAY */
+
+    MNG_FREE (pData, pBuf, iBufsize);  /* always free the temp buffer ! */
+  }
+
+  *piRawlen = 0;
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+MNG_C_SPECIALFUNC (mng_special_adat)
+{
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+MNG_C_SPECIALFUNC (mng_special_unknown)
+{
+                                       /* critical chunk ? */
+  if ((((mng_uint32)pData->iChunkname & 0x20000000) == 0)
+#ifdef MNG_SKIPCHUNK_SAVE
+    && (pData->iChunkname != MNG_UINT_SAVE)
+#endif
+#ifdef MNG_SKIPCHUNK_SEEK
+    && (pData->iChunkname != MNG_UINT_SEEK)
+#endif
+#ifdef MNG_SKIPCHUNK_DBYK
+    && (pData->iChunkname != MNG_UINT_DBYK)
+#endif
+#ifdef MNG_SKIPCHUNK_ORDR
+    && (pData->iChunkname != MNG_UINT_ORDR)
+#endif
+      )
+    MNG_ERROR (pData, MNG_UNKNOWNCRITICAL);
+
+  if (pData->fProcessunknown)          /* let the app handle it ? */
+  {
+    mng_bool bOke = pData->fProcessunknown ((mng_handle)pData, pData->iChunkname,
+                                            ((mng_unknown_chunkp)pChunk)->iDatasize,
+                                            ((mng_unknown_chunkp)pChunk)->pData);
+    if (!bOke)
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_READ_PROCS || MNG_INCLUDE_WRITE_PROCS */
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
+
+
+
+
diff --git a/files/Source/LibMNG/libmng_chunk_descr.h b/files/Source/LibMNG/libmng_chunk_descr.h
new file mode 100644
index 0000000..3781ab0
--- /dev/null
+++ b/files/Source/LibMNG/libmng_chunk_descr.h
@@ -0,0 +1,146 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_chunk_descr.h      copyright (c) 2007 G.Juyn        * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Chunk descriptor functions (implementation)                * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : definition of the chunk- anf field-descriptor routines     * */
+/* *                                                                        * */
+/* * changes   : 1.0.9 - 12/06/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKREADER               * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_chunk_descr_h_
+#define _libmng_chunk_descr_h_
+
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+#if defined(MNG_INCLUDE_READ_PROCS) || defined(MNG_INCLUDE_WRITE_PROCS)
+
+/* ************************************************************************** */
+
+void mng_get_chunkheader (mng_chunkid       iChunkname,
+                          mng_chunk_headerp pResult);
+
+/* ************************************************************************** */
+
+#define MNG_F_SPECIALFUNC(n) mng_retcode n (mng_datap   pData,    \
+                                            mng_chunkp  pChunk,   \
+                                            mng_uint32* piRawlen, \
+                                            mng_uint8p* ppRawdata)
+
+MNG_F_SPECIALFUNC (mng_debunk_plte) ;
+MNG_F_SPECIALFUNC (mng_debunk_trns) ;
+MNG_F_SPECIALFUNC (mng_deflate_itxt) ;
+MNG_F_SPECIALFUNC (mng_splt_entries) ;
+MNG_F_SPECIALFUNC (mng_hist_entries) ;
+
+MNG_F_SPECIALFUNC (mng_debunk_loop) ;
+MNG_F_SPECIALFUNC (mng_debunk_past) ;
+MNG_F_SPECIALFUNC (mng_disc_entries) ;
+MNG_F_SPECIALFUNC (mng_fram_remainder) ;
+MNG_F_SPECIALFUNC (mng_save_entries) ;
+MNG_F_SPECIALFUNC (mng_pplt_entries) ;
+MNG_F_SPECIALFUNC (mng_drop_entries) ;
+MNG_F_SPECIALFUNC (mng_ordr_entries) ;
+MNG_F_SPECIALFUNC (mng_debunk_magn) ;
+MNG_F_SPECIALFUNC (mng_evnt_entries) ;
+MNG_F_SPECIALFUNC (mng_adat_tiles) ;
+
+/* ************************************************************************** */
+
+#define MNG_C_SPECIALFUNC(n) mng_retcode n (mng_datap  pData,   \
+                                            mng_chunkp pChunk)
+
+MNG_C_SPECIALFUNC (mng_special_ihdr) ;
+MNG_C_SPECIALFUNC (mng_special_plte) ;
+MNG_C_SPECIALFUNC (mng_special_idat) ;
+MNG_C_SPECIALFUNC (mng_special_iend) ;
+MNG_C_SPECIALFUNC (mng_special_trns) ;
+MNG_C_SPECIALFUNC (mng_special_gama) ;
+MNG_C_SPECIALFUNC (mng_special_chrm) ;
+MNG_C_SPECIALFUNC (mng_special_srgb) ;
+MNG_C_SPECIALFUNC (mng_special_iccp) ;
+MNG_C_SPECIALFUNC (mng_special_text) ;
+MNG_C_SPECIALFUNC (mng_special_ztxt) ;
+MNG_C_SPECIALFUNC (mng_special_itxt) ;
+MNG_C_SPECIALFUNC (mng_special_bkgd) ;
+MNG_C_SPECIALFUNC (mng_special_phys) ;
+MNG_C_SPECIALFUNC (mng_special_sbit) ;
+MNG_C_SPECIALFUNC (mng_special_splt) ;
+MNG_C_SPECIALFUNC (mng_special_hist) ;
+MNG_C_SPECIALFUNC (mng_special_time) ;
+
+MNG_C_SPECIALFUNC (mng_special_jhdr) ;
+MNG_C_SPECIALFUNC (mng_special_jdaa) ;
+MNG_C_SPECIALFUNC (mng_special_jdat) ;
+MNG_C_SPECIALFUNC (mng_special_jsep) ;
+
+MNG_C_SPECIALFUNC (mng_special_mhdr) ;
+MNG_C_SPECIALFUNC (mng_special_mend) ;
+MNG_C_SPECIALFUNC (mng_special_loop) ;
+MNG_C_SPECIALFUNC (mng_special_endl) ;
+MNG_C_SPECIALFUNC (mng_special_defi) ;
+MNG_C_SPECIALFUNC (mng_special_basi) ;
+MNG_C_SPECIALFUNC (mng_special_clon) ;
+MNG_C_SPECIALFUNC (mng_special_past) ;
+MNG_C_SPECIALFUNC (mng_special_disc) ;
+MNG_C_SPECIALFUNC (mng_special_back) ;
+MNG_C_SPECIALFUNC (mng_special_fram) ;
+MNG_C_SPECIALFUNC (mng_special_move) ;
+MNG_C_SPECIALFUNC (mng_special_clip) ;
+MNG_C_SPECIALFUNC (mng_special_show) ;
+MNG_C_SPECIALFUNC (mng_special_term) ;
+MNG_C_SPECIALFUNC (mng_special_save) ;
+MNG_C_SPECIALFUNC (mng_special_seek) ;
+MNG_C_SPECIALFUNC (mng_special_expi) ;
+MNG_C_SPECIALFUNC (mng_special_fpri) ;
+MNG_C_SPECIALFUNC (mng_special_need) ;
+MNG_C_SPECIALFUNC (mng_special_phyg) ;
+
+MNG_C_SPECIALFUNC (mng_special_dhdr) ;
+MNG_C_SPECIALFUNC (mng_special_prom) ;
+MNG_C_SPECIALFUNC (mng_special_ipng) ;
+MNG_C_SPECIALFUNC (mng_special_pplt) ;
+MNG_C_SPECIALFUNC (mng_special_ijng) ;
+MNG_C_SPECIALFUNC (mng_special_drop) ;
+MNG_C_SPECIALFUNC (mng_special_dbyk) ;
+MNG_C_SPECIALFUNC (mng_special_ordr) ;
+
+MNG_C_SPECIALFUNC (mng_special_magn) ;
+MNG_C_SPECIALFUNC (mng_special_evnt) ;
+MNG_C_SPECIALFUNC (mng_special_mpng) ;
+MNG_C_SPECIALFUNC (mng_special_ahdr) ;
+MNG_C_SPECIALFUNC (mng_special_adat) ;
+MNG_C_SPECIALFUNC (mng_special_unknown) ;
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_READ_PROCS) || MNG_INCLUDE_WRITE_PROCS */
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+
+/* ************************************************************************** */
+
+#endif /* _libmng_chunk_descr_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_chunk_io.c b/files/Source/LibMNG/libmng_chunk_io.c
new file mode 100644
index 0000000..eb18099
--- /dev/null
+++ b/files/Source/LibMNG/libmng_chunk_io.c
@@ -0,0 +1,10740 @@
+/** ************************************************************************* */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_chunk_io.c         copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Chunk I/O routines (implementation)                        * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of chunk input/output routines              * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/01/2000 - G.Juyn                                * */
+/* *             - cleaned up left-over teststuff in the BACK chunk routine * */
+/* *             0.5.1 - 05/04/2000 - G.Juyn                                * */
+/* *             - changed CRC initialization to use dynamic structure      * */
+/* *               (wasn't thread-safe the old way !)                       * */
+/* *             0.5.1 - 05/06/2000 - G.Juyn                                * */
+/* *             - filled in many missing sequence&length checks            * */
+/* *             - filled in many missing chunk-store snippets              * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - added checks for running animations                      * */
+/* *             - filled some write routines                               * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/10/2000 - G.Juyn                                * */
+/* *             - filled some more write routines                          * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - filled remaining write routines                          * */
+/* *             - fixed read_pplt with regard to deltatype                 * */
+/* *             - added callback error-reporting support                   * */
+/* *             - added pre-draft48 support (short MHDR, frame_mode, LOOP) * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *             - fixed chunk-storage bit in several routines              * */
+/* *             0.5.1 - 05/13/2000 - G.Juyn                                * */
+/* *             - added eMNGma hack (will be removed in 1.0.0 !!!)         * */
+/* *             - added TERM animation object pointer (easier reference)   * */
+/* *             - supplemented the SAVE & SEEK display processing          * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/18/2000 - G.Juyn                                * */
+/* *             - B004 - fixed problem with MNG_SUPPORT_WRITE not defined  * */
+/* *               also for MNG_SUPPORT_WRITE without MNG_INCLUDE_JNG       * */
+/* *             0.5.2 - 05/19/2000 - G.Juyn                                * */
+/* *             - cleaned up some code regarding mixed support             * */
+/* *             0.5.2 - 05/20/2000 - G.Juyn                                * */
+/* *             - implemented JNG support                                  * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - added support for global color-chunks in animation       * */
+/* *             - added support for global PLTE,tRNS,bKGD in animation     * */
+/* *             - added support for SAVE & SEEK in animation               * */
+/* *             0.5.2 - 05/29/2000 - G.Juyn                                * */
+/* *             - changed ani_create calls not returning object pointer    * */
+/* *             - create ani objects always (not just inside TERM/LOOP)    * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added support for delta-image processing                 * */
+/* *             0.5.2 - 05/31/2000 - G.Juyn                                * */
+/* *             - fixed up punctuation (contributed by Tim Rowley)         * */
+/* *             0.5.2 - 06/02/2000 - G.Juyn                                * */
+/* *             - changed SWAP_ENDIAN to BIGENDIAN_SUPPORTED               * */
+/* *             0.5.2 - 06/03/2000 - G.Juyn                                * */
+/* *             - fixed makeup for Linux gcc compile                       * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/12/2000 - G.Juyn                                * */
+/* *             - added processing of color-info on delta-image            * */
+/* *             0.5.3 - 06/13/2000 - G.Juyn                                * */
+/* *             - fixed handling of empty SAVE chunk                       * */
+/* *             0.5.3 - 06/17/2000 - G.Juyn                                * */
+/* *             - changed to support delta-images                          * */
+/* *             - added extra checks for delta-images                      * */
+/* *             0.5.3 - 06/20/2000 - G.Juyn                                * */
+/* *             - fixed possible trouble if IEND display-process got       * */
+/* *               broken up                                                * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added processing of PLTE & tRNS for delta-images         * */
+/* *             - added administration of imagelevel parameter             * */
+/* *             0.5.3 - 06/22/2000 - G.Juyn                                * */
+/* *             - implemented support for PPLT chunk                       * */
+/* *             0.5.3 - 06/26/2000 - G.Juyn                                * */
+/* *             - added precaution against faulty iCCP chunks from PS      * */
+/* *             0.5.3 - 06/29/2000 - G.Juyn                                * */
+/* *             - fixed some 64-bit warnings                               * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/14/2000 - G.Juyn                                * */
+/* *             - changed pre-draft48 frame_mode=3 to frame_mode=1         * */
+/* *             0.9.1 - 07/16/2000 - G.Juyn                                * */
+/* *             - fixed storage of images during mng_read()                * */
+/* *             - fixed support for mng_display() after mng_read()         * */
+/* *             0.9.1 - 07/19/2000 - G.Juyn                                * */
+/* *             - fixed several chunk-writing routines                     * */
+/* *             0.9.1 - 07/24/2000 - G.Juyn                                * */
+/* *             - fixed reading of still-images                            * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/07/2000 - G.Juyn                                * */
+/* *             - B111300 - fixup for improved portability                 * */
+/* *             0.9.3 - 08/08/2000 - G.Juyn                                * */
+/* *             - fixed compiler-warnings from Mozilla                     * */
+/* *             0.9.3 - 08/09/2000 - G.Juyn                                * */
+/* *             - added check for simplicity-bits in MHDR                  * */
+/* *             0.9.3 - 08/12/2000 - G.Juyn                                * */
+/* *             - fixed check for simplicity-bits in MHDR (JNG)            * */
+/* *             0.9.3 - 08/12/2000 - G.Juyn                                * */
+/* *             - added workaround for faulty PhotoShop iCCP chunk         * */
+/* *             0.9.3 - 08/22/2000 - G.Juyn                                * */
+/* *             - fixed write-code for zTXt & iTXt                         * */
+/* *             - fixed read-code for iTXt                                 * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *             0.9.3 - 09/10/2000 - G.Juyn                                * */
+/* *             - fixed DEFI behavior                                      * */
+/* *             0.9.3 - 10/02/2000 - G.Juyn                                * */
+/* *             - fixed simplicity-check in compliance with draft 81/0.98a * */
+/* *             0.9.3 - 10/10/2000 - G.Juyn                                * */
+/* *             - added support for alpha-depth prediction                 * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - added support for nEED                                   * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added support for JDAA                                   * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - fixed support for MAGN                                   * */
+/* *             - implemented nEED "xxxx" (where "xxxx" is a chunkid)      * */
+/* *             - added callback to process non-critical unknown chunks    * */
+/* *             - fixed support for bKGD                                   * */
+/* *             0.9.3 - 10/23/2000 - G.Juyn                                * */
+/* *             - fixed bug in empty PLTE handling                         * */
+/* *                                                                        * */
+/* *             0.9.4 - 11/20/2000 - G.Juyn                                * */
+/* *             - changed IHDR filter_method check for PNGs                * */
+/* *             0.9.4 -  1/18/2001 - G.Juyn                                * */
+/* *             - added errorchecking for MAGN methods                     * */
+/* *             - removed test filter-methods 1 & 65                       * */
+/* *                                                                        * */
+/* *             0.9.5 -  1/25/2001 - G.Juyn                                * */
+/* *             - fixed some small compiler warnings (thanks Nikki)        * */
+/* *                                                                        * */
+/* *             1.0.2 - 05/05/2000 - G.Juyn                                * */
+/* *             - B421427 - writes wrong format in bKGD and tRNS           * */
+/* *             1.0.2 - 06/20/2000 - G.Juyn                                * */
+/* *             - B434583 - compiler-warning if MNG_STORE_CHUNKS undefined * */
+/* *                                                                        * */
+/* *             1.0.5 - 07/08/2002 - G.Juyn                                * */
+/* *             - B578572 - removed eMNGma hack (thanks Dimitri!)          * */
+/* *             1.0.5 - 08/07/2002 - G.Juyn                                * */
+/* *             - added test-option for PNG filter method 193 (=no filter) * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed PROM support                                   * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/07/2002 - G.Juyn                                * */
+/* *             - fixed reading of FRAM with just frame_mode and name      * */
+/* *             1.0.5 - 09/13/2002 - G.Juyn                                * */
+/* *             - fixed read/write of MAGN chunk                           * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 09/15/2002 - G.Juyn                                * */
+/* *             - fixed LOOP iteration=0 special case                      * */
+/* *             1.0.5 - 09/19/2002 - G.Juyn                                * */
+/* *             - misplaced TERM is now treated as warning                 * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 10/03/2002 - G.Juyn                                * */
+/* *             - fixed chunk-storage for evNT chunk                       * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - fixed DISC support                                       * */
+/* *             - added another fix for misplaced TERM chunk               * */
+/* *             1.0.5 - 10/17/2002 - G.Juyn                                * */
+/* *             - fixed initializtion of pIds in dISC read routine         * */
+/* *             1.0.5 - 11/06/2002 - G.Juyn                                * */
+/* *             - added support for nEED "MNG 1.1"                         * */
+/* *             - added support for nEED "CACHEOFF"                        * */
+/* *                                                                        * */
+/* *             1.0.6 - 05/25/2003 - G.R-P                                 * */
+/* *             - added MNG_SKIPCHUNK_cHNK footprint optimizations         * */
+/* *             1.0.6 - 06/02/2003 - G.R-P                                 * */
+/* *             - removed some redundant checks for iRawlen==0             * */
+/* *             1.0.6 - 06/22/2003 - G.R-P                                 * */
+/* *             - added MNG_NO_16BIT_SUPPORT, MNG_NO_DELTA_PNG reductions  * */
+/* *             - optionally use zlib's crc32 function instead of          * */
+/* *               local mng_update_crc                                     * */
+/* *             1.0.6 - 07/14/2003 - G.R-P                                 * */
+/* *             - added MNG_NO_LOOP_SIGNALS_SUPPORTED conditional          * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *             1.0.6 - 08/17/2003 - G.R-P                                 * */
+/* *             - added conditionals around non-VLC chunk support          * */
+/* *                                                                        * */
+/* *             1.0.7 - 10/29/2003 - G.R-P                                 * */
+/* *             - revised JDAA and JDAT readers to avoid compiler bug      * */
+/* *             1.0.7 - 01/25/2004 - J.S                                   * */
+/* *             - added premultiplied alpha canvas' for RGBA, ARGB, ABGR   * */
+/* *             1.0.7 - 01/27/2004 - J.S                                   * */
+/* *             - fixed inclusion of IJNG chunk for non-JNG use            * */
+/* *             1.0.7 - 02/26/2004 - G.Juyn                                * */
+/* *             - fixed bug in chunk-storage of SHOW chunk (from == to)    * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/02/2004 - G.Juyn                                * */
+/* *             - added CRC existence & checking flags                     * */
+/* *             1.0.8 - 07/07/2004 - G.R-P                                 * */
+/* *             - change worst-case iAlphadepth to 1 for standalone PNGs   * */
+/* *                                                                        * */
+/* *             1.0.9 - 09/28/2004 - G.R-P                                 * */
+/* *             - improved handling of cheap transparency when 16-bit      * */
+/* *               support is disabled                                      * */
+/* *             1.0.9 - 10/04/2004 - G.Juyn                                * */
+/* *             - fixed bug in writing sBIT for indexed color              * */
+/* *             1.0.9 - 10/10/2004 - G.R-P.                                * */
+/* *             - added MNG_NO_1_2_4BIT_SUPPORT                            * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKINITFREE             * */
+/* *             1.0.9 - 12/06/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKASSIGN               * */
+/* *             1.0.9 - 12/07/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKREADER               * */
+/* *             1.0.9 - 12/11/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_DISPLAYCALLS              * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *             1.0.9 - 01/17/2005 - G.Juyn                                * */
+/* *             - fixed problem with global PLTE/tRNS                      * */
+/* *                                                                        * */
+/* *             1.0.10 - 02/07/2005 - G.Juyn                               * */
+/* *             - fixed display routines called twice for FULL_MNG         * */
+/* *               support in mozlibmngconf.h                               * */
+/* *             1.0.10 - 12/04/2005 - G.R-P.                               * */
+/* *             - #ifdef out use of mng_inflate_buffer when it is not      * */
+/* *               available.                                               * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *             1.0.10 - 05/02/2007 - G.Juyn                               * */
+/* *             - fixed inflate_buffer for extreme compression ratios      * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_objects.h"
+#include "libmng_object_prc.h"
+#include "libmng_chunks.h"
+#ifdef MNG_CHECK_BAD_ICCP
+#include "libmng_chunk_prc.h"
+#endif
+#include "libmng_memory.h"
+#include "libmng_display.h"
+#include "libmng_zlib.h"
+#include "libmng_pixels.h"
+#include "libmng_chunk_io.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * CRC - Cyclic Redundancy Check                                          * */
+/* *                                                                        * */
+/* * The code below is taken directly from the sample provided with the     * */
+/* * PNG specification.                                                     * */
+/* * (it is only adapted to the library's internal data-definitions)        * */
+/* *                                                                        * */
+/* ************************************************************************** */
+/* Make the table for a fast CRC. */
+#ifndef MNG_USE_ZLIB_CRC
+MNG_LOCAL void make_crc_table (mng_datap pData)
+{
+  mng_uint32 iC;
+  mng_int32  iN, iK;
+
+  for (iN = 0; iN < 256; iN++)
+  {
+    iC = (mng_uint32) iN;
+
+    for (iK = 0; iK < 8; iK++)
+    {
+      if (iC & 1)
+        iC = 0xedb88320U ^ (iC >> 1);
+      else
+        iC = iC >> 1;
+    }
+
+    pData->aCRCtable [iN] = iC;
+  }
+
+  pData->bCRCcomputed = MNG_TRUE;
+}
+#endif
+
+/* Update a running CRC with the bytes buf[0..len-1]--the CRC
+   should be initialized to all 1's, and the transmitted value
+   is the 1's complement of the final running CRC (see the
+   crc() routine below). */
+
+MNG_LOCAL mng_uint32 update_crc (mng_datap  pData,
+                                 mng_uint32 iCrc,
+                                 mng_uint8p pBuf,
+                                 mng_int32  iLen)
+{
+#ifdef MNG_USE_ZLIB_CRC
+  return crc32 (iCrc, pBuf, iLen);
+#else
+  mng_uint32 iC = iCrc;
+  mng_int32 iN;
+
+  if (!pData->bCRCcomputed)
+    make_crc_table (pData);
+
+  for (iN = 0; iN < iLen; iN++)
+    iC = pData->aCRCtable [(iC ^ pBuf [iN]) & 0xff] ^ (iC >> 8);
+
+  return iC;
+#endif
+}
+
+/* Return the CRC of the bytes buf[0..len-1]. */
+mng_uint32 mng_crc (mng_datap  pData,
+                    mng_uint8p pBuf,
+                    mng_int32  iLen)
+{
+#ifdef MNG_USE_ZLIB_CRC
+  return update_crc (pData, 0, pBuf, iLen);
+#else
+  return update_crc (pData, 0xffffffffU, pBuf, iLen) ^ 0xffffffffU;
+#endif
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Routines for swapping byte-order from and to graphic files             * */
+/* * (This code is adapted from the libpng package)                         * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_BIGENDIAN_SUPPORTED
+
+/* ************************************************************************** */
+
+mng_uint32 mng_get_uint32 (mng_uint8p pBuf)
+{
+   mng_uint32 i = ((mng_uint32)(*pBuf)       << 24) +
+                  ((mng_uint32)(*(pBuf + 1)) << 16) +
+                  ((mng_uint32)(*(pBuf + 2)) <<  8) +
+                   (mng_uint32)(*(pBuf + 3));
+   return (i);
+}
+
+/* ************************************************************************** */
+
+mng_int32 mng_get_int32 (mng_uint8p pBuf)
+{
+   mng_int32 i = ((mng_int32)(*pBuf)       << 24) +
+                 ((mng_int32)(*(pBuf + 1)) << 16) +
+                 ((mng_int32)(*(pBuf + 2)) <<  8) +
+                  (mng_int32)(*(pBuf + 3));
+   return (i);
+}
+
+/* ************************************************************************** */
+
+mng_uint16 mng_get_uint16 (mng_uint8p pBuf)
+{
+   mng_uint16 i = (mng_uint16)(((mng_uint16)(*pBuf) << 8) +
+                                (mng_uint16)(*(pBuf + 1)));
+   return (i);
+}
+
+/* ************************************************************************** */
+
+void mng_put_uint32 (mng_uint8p pBuf,
+                     mng_uint32 i)
+{
+   *pBuf     = (mng_uint8)((i >> 24) & 0xff);
+   *(pBuf+1) = (mng_uint8)((i >> 16) & 0xff);
+   *(pBuf+2) = (mng_uint8)((i >> 8) & 0xff);
+   *(pBuf+3) = (mng_uint8)(i & 0xff);
+}
+
+/* ************************************************************************** */
+
+void mng_put_int32 (mng_uint8p pBuf,
+                    mng_int32  i)
+{
+   *pBuf     = (mng_uint8)((i >> 24) & 0xff);
+   *(pBuf+1) = (mng_uint8)((i >> 16) & 0xff);
+   *(pBuf+2) = (mng_uint8)((i >> 8) & 0xff);
+   *(pBuf+3) = (mng_uint8)(i & 0xff);
+}
+
+/* ************************************************************************** */
+
+void mng_put_uint16 (mng_uint8p pBuf,
+                     mng_uint16 i)
+{
+   *pBuf     = (mng_uint8)((i >> 8) & 0xff);
+   *(pBuf+1) = (mng_uint8)(i & 0xff);
+}
+
+/* ************************************************************************** */
+
+#endif /* !MNG_BIGENDIAN_SUPPORTED */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Helper routines to simplify chunk-data extraction                      * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_READ_PROCS
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+MNG_LOCAL mng_uint8p find_null (mng_uint8p pIn)
+{
+  mng_uint8p pOut = pIn;
+  while (*pOut)                        /* the read_graphic routine has made sure there's */
+    pOut++;                            /* always at least 1 zero-byte in the buffer */
+  return pOut;
+}
+#endif
+
+/* ************************************************************************** */
+
+#if !defined(MNG_SKIPCHUNK_iCCP) || !defined(MNG_SKIPCHUNK_zTXt) || \
+    !defined(MNG_SKIPCHUNK_iTXt) || defined(MNG_INCLUDE_MPNG_PROPOSAL) || \
+    defined(MNG_INCLUDE_ANG_PROPOSAL)
+mng_retcode mng_inflate_buffer (mng_datap  pData,
+                                mng_uint8p pInbuf,
+                                mng_uint32 iInsize,
+                                mng_uint8p *pOutbuf,
+                                mng_uint32 *iOutsize,
+                                mng_uint32 *iRealsize)
+{
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INFLATE_BUFFER, MNG_LC_START);
+#endif
+
+  if (iInsize)                         /* anything to do ? */
+  {
+    *iOutsize = iInsize * 3;           /* estimate uncompressed size */
+                                       /* and allocate a temporary buffer */
+    MNG_ALLOC (pData, *pOutbuf, *iOutsize);
+
+    do
+    {
+      mngzlib_inflateinit (pData);     /* initialize zlib */
+                                       /* let zlib know where to store the output */
+      pData->sZlib.next_out  = *pOutbuf;
+                                       /* "size - 1" so we've got space for the
+                                          zero-termination of a possible string */
+      pData->sZlib.avail_out = *iOutsize - 1;
+                                       /* ok; let's inflate... */
+      iRetcode = mngzlib_inflatedata (pData, iInsize, pInbuf);
+                                       /* determine actual output size */
+      *iRealsize = (mng_uint32)pData->sZlib.total_out;
+
+      mngzlib_inflatefree (pData);     /* zlib's done */
+
+      if (iRetcode == MNG_BUFOVERFLOW) /* not enough space ? */
+      {                                /* then get some more */
+        MNG_FREEX (pData, *pOutbuf, *iOutsize);
+        *iOutsize = *iOutsize + *iOutsize;
+        MNG_ALLOC (pData, *pOutbuf, *iOutsize);
+      }
+    }                                  /* repeat if we didn't have enough space */
+    while ((iRetcode == MNG_BUFOVERFLOW) &&
+           (*iOutsize < 200 * iInsize));
+
+    if (!iRetcode)                     /* if oke ? */
+      *((*pOutbuf) + *iRealsize) = 0;  /* then put terminator zero */
+
+  }
+  else
+  {
+    *pOutbuf   = 0;                    /* nothing to do; then there's no output */
+    *iOutsize  = 0;
+    *iRealsize = 0;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INFLATE_BUFFER, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_READ_PROCS */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Helper routines to simplify chunk writing                              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+#ifdef MNG_INCLUDE_WRITE_PROCS
+/* ************************************************************************** */
+
+#if !defined(MNG_SKIPCHUNK_iCCP) || !defined(MNG_SKIPCHUNK_zTXt) || !defined(MNG_SKIPCHUNK_iTXt)
+MNG_LOCAL mng_retcode deflate_buffer (mng_datap  pData,
+                                      mng_uint8p pInbuf,
+                                      mng_uint32 iInsize,
+                                      mng_uint8p *pOutbuf,
+                                      mng_uint32 *iOutsize,
+                                      mng_uint32 *iRealsize)
+{
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DEFLATE_BUFFER, MNG_LC_START);
+#endif
+
+  if (iInsize)                         /* anything to do ? */
+  {
+    *iOutsize = (iInsize * 5) >> 2;    /* estimate compressed size */
+                                       /* and allocate a temporary buffer */
+    MNG_ALLOC (pData, *pOutbuf, *iOutsize);
+
+    do
+    {
+      mngzlib_deflateinit (pData);     /* initialize zlib */
+                                       /* let zlib know where to store the output */
+      pData->sZlib.next_out  = *pOutbuf;
+      pData->sZlib.avail_out = *iOutsize;
+                                       /* ok; let's deflate... */
+      iRetcode = mngzlib_deflatedata (pData, iInsize, pInbuf);
+                                       /* determine actual output size */
+      *iRealsize = pData->sZlib.total_out;
+
+      mngzlib_deflatefree (pData);     /* zlib's done */
+
+      if (iRetcode == MNG_BUFOVERFLOW) /* not enough space ? */
+      {                                /* then get some more */
+        MNG_FREEX (pData, *pOutbuf, *iOutsize);
+        *iOutsize = *iOutsize + (iInsize >> 1);
+        MNG_ALLOC (pData, *pOutbuf, *iOutsize);
+      }
+    }                                  /* repeat if we didn't have enough space */
+    while (iRetcode == MNG_BUFOVERFLOW);
+  }
+  else
+  {
+    *pOutbuf   = 0;                    /* nothing to do; then there's no output */
+    *iOutsize  = 0;
+    *iRealsize = 0;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DEFLATE_BUFFER, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode write_raw_chunk (mng_datap   pData,
+                                       mng_chunkid iChunkname,
+                                       mng_uint32  iRawlen,
+                                       mng_uint8p  pRawdata)
+{
+  mng_uint32 iCrc;
+  mng_uint32 iWritten;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_RAW_CHUNK, MNG_LC_START);
+#endif
+                                       /* temporary buffer ? */
+  if ((pRawdata != 0) && (pRawdata != pData->pWritebuf+8))
+  {                                    /* store length & chunktype in default buffer */
+    mng_put_uint32 (pData->pWritebuf,   iRawlen);
+    mng_put_uint32 (pData->pWritebuf+4, (mng_uint32)iChunkname);
+
+    if (pData->iCrcmode & MNG_CRC_OUTPUT)
+    {
+      if ((pData->iCrcmode & MNG_CRC_OUTPUT) == MNG_CRC_OUTPUT_GENERATE)
+      {                                /* calculate the crc */
+        iCrc = update_crc (pData, 0xffffffffL, pData->pWritebuf+4, 4);
+        iCrc = update_crc (pData, iCrc, pRawdata, iRawlen) ^ 0xffffffffL;
+      } else {
+        iCrc = 0;                      /* dummy crc */
+      }                                /* store in default buffer */
+      mng_put_uint32 (pData->pWritebuf+8, iCrc);
+    }
+                                       /* write the length & chunktype */
+    if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf, 8, &iWritten))
+      MNG_ERROR (pData, MNG_APPIOERROR);
+
+    if (iWritten != 8)                 /* disk full ? */
+      MNG_ERROR (pData, MNG_OUTPUTERROR);
+                                       /* write the temporary buffer */
+    if (!pData->fWritedata ((mng_handle)pData, pRawdata, iRawlen, &iWritten))
+      MNG_ERROR (pData, MNG_APPIOERROR);
+
+    if (iWritten != iRawlen)           /* disk full ? */
+      MNG_ERROR (pData, MNG_OUTPUTERROR);
+
+    if (pData->iCrcmode & MNG_CRC_OUTPUT)
+    {                                  /* write the crc */
+      if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf+8, 4, &iWritten))
+        MNG_ERROR (pData, MNG_APPIOERROR);
+
+      if (iWritten != 4)               /* disk full ? */
+        MNG_ERROR (pData, MNG_OUTPUTERROR);
+    }
+  }
+  else
+  {                                    /* prefix with length & chunktype */
+    mng_put_uint32 (pData->pWritebuf,   iRawlen);
+    mng_put_uint32 (pData->pWritebuf+4, (mng_uint32)iChunkname);
+
+    if (pData->iCrcmode & MNG_CRC_OUTPUT)
+    {
+      if ((pData->iCrcmode & MNG_CRC_OUTPUT) == MNG_CRC_OUTPUT_GENERATE)
+                                       /* calculate the crc */
+        iCrc = mng_crc (pData, pData->pWritebuf+4, iRawlen + 4);
+      else
+        iCrc = 0;                      /* dummy crc */
+                                       /* add it to the buffer */
+      mng_put_uint32 (pData->pWritebuf + iRawlen + 8, iCrc);
+                                       /* write it in a single pass */
+      if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf, iRawlen + 12, &iWritten))
+        MNG_ERROR (pData, MNG_APPIOERROR);
+
+      if (iWritten != iRawlen + 12)    /* disk full ? */
+        MNG_ERROR (pData, MNG_OUTPUTERROR);
+    } else {
+      if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf, iRawlen + 8, &iWritten))
+        MNG_ERROR (pData, MNG_APPIOERROR);
+
+      if (iWritten != iRawlen + 8)     /* disk full ? */
+        MNG_ERROR (pData, MNG_OUTPUTERROR);
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_RAW_CHUNK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* B004 */
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+/* B004 */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * chunk read functions                                                   * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_READ_PROCS
+
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode create_chunk_storage (mng_datap       pData,
+                                            mng_chunkp      pHeader,
+                                            mng_uint32      iRawlen,
+                                            mng_uint8p      pRawdata,
+                                            mng_field_descp pField,
+                                            mng_uint16      iFields,
+                                            mng_chunkp*     ppChunk,
+                                            mng_bool        bWorkcopy)
+{
+  mng_field_descp pTempfield  = pField;
+  mng_uint16      iFieldcount = iFields;
+  mng_uint8p      pTempdata   = pRawdata;
+  mng_uint32      iTemplen    = iRawlen;
+  mng_uint16      iLastgroup  = 0;
+  mng_uint8p      pChunkdata;
+  mng_uint32      iDatalen;
+  mng_uint8       iColortype;
+  mng_bool        bProcess;
+                                       /* initialize storage */
+  mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  if (((mng_chunk_headerp)(*ppChunk))->iChunkname == MNG_UINT_HUH)
+    ((mng_chunk_headerp)(*ppChunk))->iChunkname = pData->iChunkname;
+
+  if ((!bWorkcopy) ||
+      ((((mng_chunk_headerp)pHeader)->iChunkname != MNG_UINT_IDAT) &&
+       (((mng_chunk_headerp)pHeader)->iChunkname != MNG_UINT_JDAT) &&
+       (((mng_chunk_headerp)pHeader)->iChunkname != MNG_UINT_JDAA)   ))
+  {
+    pChunkdata = (mng_uint8p)(*ppChunk);
+
+#ifdef MNG_INCLUDE_JNG                 /* determine current colortype */
+    if (pData->bHasJHDR)
+      iColortype = (mng_uint8)(pData->iJHDRcolortype - 8);
+    else
+#endif /* MNG_INCLUDE_JNG */
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+      iColortype = pData->iColortype;
+    else
+      iColortype = 6;
+
+    if (iTemplen)                      /* not empty ? */
+    {                                  /* then go fill the fields */
+      while ((iFieldcount) && (iTemplen))
+      {
+        if (pTempfield->iOffsetchunk)
+        {
+          if (pTempfield->iFlags & MNG_FIELD_PUTIMGTYPE)
+          {
+            *(pChunkdata+pTempfield->iOffsetchunk) = iColortype;
+            bProcess = MNG_FALSE;
+          }
+          else
+          if (pTempfield->iFlags & MNG_FIELD_IFIMGTYPES)
+            bProcess = (mng_bool)(((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE0) && (iColortype == 0)) ||
+                                  ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE2) && (iColortype == 2)) ||
+                                  ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE3) && (iColortype == 3)) ||
+                                  ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE4) && (iColortype == 4)) ||
+                                  ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE6) && (iColortype == 6))   );
+          else
+            bProcess = MNG_TRUE;
+
+          if (bProcess)
+          {
+            iLastgroup = (mng_uint16)(pTempfield->iFlags & MNG_FIELD_GROUPMASK);
+                                      /* numeric field ? */
+            if (pTempfield->iFlags & MNG_FIELD_INT)
+            {
+              if (iTemplen < pTempfield->iLengthmax)
+                MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+              switch (pTempfield->iLengthmax)
+              {
+                case 1 : { mng_uint8 iNum = *pTempdata;
+                           if (((mng_uint16)iNum < pTempfield->iMinvalue) ||
+                               ((mng_uint16)iNum > pTempfield->iMaxvalue)    )
+                             MNG_ERROR (pData, MNG_INVALIDFIELDVAL);
+                           *(pChunkdata+pTempfield->iOffsetchunk) = iNum;
+                           break; }
+                case 2 : { mng_uint16 iNum = mng_get_uint16 (pTempdata);
+                           if ((iNum < pTempfield->iMinvalue) || (iNum > pTempfield->iMaxvalue))
+                             MNG_ERROR (pData, MNG_INVALIDFIELDVAL);
+                           *((mng_uint16p)(pChunkdata+pTempfield->iOffsetchunk)) = iNum;
+                           break; }
+                case 4 : { mng_uint32 iNum = mng_get_uint32 (pTempdata);
+                           if ((iNum < pTempfield->iMinvalue) ||
+                               ((pTempfield->iFlags & MNG_FIELD_NOHIGHBIT) && (iNum & 0x80000000)) )
+                             MNG_ERROR (pData, MNG_INVALIDFIELDVAL);
+                           *((mng_uint32p)(pChunkdata+pTempfield->iOffsetchunk)) = iNum;
+                           break; }
+              }
+
+              pTempdata += pTempfield->iLengthmax;
+              iTemplen  -= pTempfield->iLengthmax;
+
+            } else {                   /* not numeric so it's a bunch of bytes */
+
+              if (!pTempfield->iOffsetchunklen)    /* big fat NONO */
+                MNG_ERROR (pData, MNG_INTERNALERROR);
+                                       /* with terminating 0 ? */
+              if (pTempfield->iFlags & MNG_FIELD_TERMINATOR)
+              {
+                mng_uint8p pWork = pTempdata;
+                while (*pWork)         /* find the zero */
+                  pWork++;
+                iDatalen = (mng_uint32)(pWork - pTempdata);
+              } else {                 /* no terminator, so everything that's left ! */
+                iDatalen = iTemplen;
+              }
+
+              if ((pTempfield->iLengthmax) && (iDatalen > pTempfield->iLengthmax))
+                MNG_ERROR (pData, MNG_INVALIDLENGTH);
+#if !defined(MNG_SKIPCHUNK_iCCP) || !defined(MNG_SKIPCHUNK_zTXt) || \
+    !defined(MNG_SKIPCHUNK_iTXt) || defined(MNG_INCLUDE_MPNG_PROPOSAL) || \
+    defined(MNG_INCLUDE_ANG_PROPOSAL)
+                                       /* needs decompression ? */
+              if (pTempfield->iFlags & MNG_FIELD_DEFLATED)
+              {
+                mng_uint8p pBuf = 0;
+                mng_uint32 iBufsize = 0;
+                mng_uint32 iRealsize;
+                mng_ptr    pWork;
+
+                iRetcode = mng_inflate_buffer (pData, pTempdata, iDatalen,
+                                               &pBuf, &iBufsize, &iRealsize);
+
+#ifdef MNG_CHECK_BAD_ICCP              /* Check for bad iCCP chunk */
+                if ((iRetcode) && (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_iCCP))
+                {
+                  *((mng_ptr *)(pChunkdata+pTempfield->iOffsetchunk))      = MNG_NULL;
+                  *((mng_uint32p)(pChunkdata+pTempfield->iOffsetchunklen)) = iDatalen;
+                }
+                else
+#endif
+                {
+                  if (iRetcode)
+                    return iRetcode;
+
+#if defined(MNG_INCLUDE_MPNG_PROPOSAL) || defined(MNG_INCLUDE_ANG_PROPOSAL)
+                  if ( (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_mpNG) ||
+                       (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_adAT)    )
+                  {
+                    MNG_ALLOC (pData, pWork, iRealsize);
+                  }
+                  else
+                  {
+#endif
+                                       /* don't forget to generate null terminator */
+                    MNG_ALLOC (pData, pWork, iRealsize+1);
+#if defined(MNG_INCLUDE_MPNG_PROPOSAL) || defined(MNG_INCLUDE_ANG_PROPOSAL)
+                  }
+#endif
+                  MNG_COPY (pWork, pBuf, iRealsize);
+
+                  *((mng_ptr *)(pChunkdata+pTempfield->iOffsetchunk))      = pWork;
+                  *((mng_uint32p)(pChunkdata+pTempfield->iOffsetchunklen)) = iRealsize;
+                }
+
+                if (pBuf)              /* free the temporary buffer */
+                  MNG_FREEX (pData, pBuf, iBufsize);
+
+              } else
+#endif
+                     {                 /* no decompression, so just copy */
+
+                mng_ptr pWork;
+                                       /* don't forget to generate null terminator */
+                MNG_ALLOC (pData, pWork, iDatalen+1);
+                MNG_COPY (pWork, pTempdata, iDatalen);
+
+                *((mng_ptr *)(pChunkdata+pTempfield->iOffsetchunk))      = pWork;
+                *((mng_uint32p)(pChunkdata+pTempfield->iOffsetchunklen)) = iDatalen;
+              }
+
+              if (pTempfield->iFlags & MNG_FIELD_TERMINATOR)
+                iDatalen++;            /* skip the terminating zero as well !!! */
+
+              iTemplen  -= iDatalen;
+              pTempdata += iDatalen;
+            }
+                                       /* need to set an indicator ? */
+            if (pTempfield->iOffsetchunkind)
+              *((mng_uint8p)(pChunkdata+pTempfield->iOffsetchunkind)) = MNG_TRUE;
+          }
+        }
+
+        if (pTempfield->pSpecialfunc)  /* special function required ? */
+        {
+          iRetcode = pTempfield->pSpecialfunc(pData, *ppChunk, &iTemplen, &pTempdata);
+          if (iRetcode)                /* on error bail out */
+            return iRetcode;
+        }
+
+        pTempfield++;                  /* Neeeeeeexxxtt */
+        iFieldcount--;
+      }
+
+      if (iTemplen)                    /* extra data ??? */
+        MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+      while (iFieldcount)              /* not enough data ??? */
+      {
+        if (pTempfield->iFlags & MNG_FIELD_IFIMGTYPES)
+          bProcess = (mng_bool)(((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE0) && (iColortype == 0)) ||
+                                ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE2) && (iColortype == 2)) ||
+                                ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE3) && (iColortype == 3)) ||
+                                ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE4) && (iColortype == 4)) ||
+                                ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE6) && (iColortype == 6))   );
+        else
+          bProcess = MNG_TRUE;
+
+        if (bProcess)
+        {
+          if (!(pTempfield->iFlags & MNG_FIELD_OPTIONAL))
+            MNG_ERROR (pData, MNG_INVALIDLENGTH);
+          if ((pTempfield->iFlags & MNG_FIELD_GROUPMASK) &&
+              ((mng_uint16)(pTempfield->iFlags & MNG_FIELD_GROUPMASK) == iLastgroup))
+            MNG_ERROR (pData, MNG_INVALIDLENGTH);
+        }
+
+        pTempfield++;
+        iFieldcount--;
+      }
+    }
+  }
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+READ_CHUNK (mng_read_general)
+{
+  mng_retcode     iRetcode = MNG_NOERROR;
+  mng_chunk_descp pDescr   = ((mng_chunk_headerp)pHeader)->pChunkdescr;
+  mng_field_descp pField;
+  mng_uint16      iFields;
+
+  if (!pDescr)                         /* this is a bad booboo !!! */
+    MNG_ERROR (pData, MNG_INTERNALERROR);
+
+  pField  = pDescr->pFielddesc;
+  iFields = pDescr->iFielddesc;
+                                       /* check chunk against signature */
+  if ((pDescr->eImgtype == mng_it_mng) && (pData->eSigtype != mng_it_mng))
+    MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+
+  if ((pDescr->eImgtype == mng_it_jng) && (pData->eSigtype == mng_it_png))
+    MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+                                       /* empties allowed ? */
+  if ((iRawlen == 0) && (!(pDescr->iAllowed & MNG_DESCR_EMPTY)))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  if ((pData->eImagetype != mng_it_mng) || (!(pDescr->iAllowed & MNG_DESCR_GLOBAL)))
+  {                                    /* *a* header required ? */
+    if ((pDescr->iMusthaves & MNG_DESCR_GenHDR) &&
+#ifdef MNG_INCLUDE_JNG
+        (!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+        (!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR))
+#endif
+      MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+    if ((pDescr->iMusthaves & MNG_DESCR_JngHDR) &&
+        (!pData->bHasDHDR) && (!pData->bHasJHDR))
+      MNG_ERROR (pData, MNG_SEQUENCEERROR);
+#endif
+  }
+                                       /* specific chunk pre-requisite ? */
+  if (((pDescr->iMusthaves & MNG_DESCR_IHDR) && (!pData->bHasIHDR)) ||
+#ifdef MNG_INCLUDE_JNG
+      ((pDescr->iMusthaves & MNG_DESCR_JHDR) && (!pData->bHasJHDR)) ||
+#endif
+      ((pDescr->iMusthaves & MNG_DESCR_DHDR) && (!pData->bHasDHDR)) ||
+      ((pDescr->iMusthaves & MNG_DESCR_LOOP) && (!pData->bHasLOOP)) ||
+      ((pDescr->iMusthaves & MNG_DESCR_PLTE) && (!pData->bHasPLTE)) ||
+      ((pDescr->iMusthaves & MNG_DESCR_MHDR) && (!pData->bHasMHDR)) ||
+      ((pDescr->iMusthaves & MNG_DESCR_SAVE) && (!pData->bHasSAVE))   )
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* specific chunk undesired ? */
+  if (((pDescr->iMustNOThaves & MNG_DESCR_NOIHDR) && (pData->bHasIHDR)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOBASI) && (pData->bHasBASI)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NODHDR) && (pData->bHasDHDR)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOIDAT) && (pData->bHasIDAT)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOPLTE) && (pData->bHasPLTE)) ||
+#ifdef MNG_INCLUDE_JNG
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOJHDR) && (pData->bHasJHDR)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOJDAT) && (pData->bHasJDAT)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOJDAA) && (pData->bHasJDAA)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOJSEP) && (pData->bHasJSEP)) ||
+#endif
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOMHDR) && (pData->bHasMHDR)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOLOOP) && (pData->bHasLOOP)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOTERM) && (pData->bHasTERM)) ||
+      ((pDescr->iMustNOThaves & MNG_DESCR_NOSAVE) && (pData->bHasSAVE))   )
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (pData->eSigtype == mng_it_mng)   /* check global and embedded empty chunks */
+  {
+#ifdef MNG_INCLUDE_JNG
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    {
+      if ((iRawlen == 0) && (!(pDescr->iAllowed & MNG_DESCR_EMPTYEMBED)))
+        MNG_ERROR (pData, MNG_INVALIDLENGTH);
+    } else {
+      if ((iRawlen == 0) && (!(pDescr->iAllowed & MNG_DESCR_EMPTYGLOBAL)))
+        MNG_ERROR (pData, MNG_INVALIDLENGTH);
+    }
+  }
+
+  if (pDescr->pSpecialfunc)            /* need special processing ? */
+  {
+    iRetcode = create_chunk_storage (pData, pHeader, iRawlen, pRawdata,
+                                     pField, iFields, ppChunk, MNG_TRUE);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* empty indicator ? */
+    if ((!iRawlen) && (pDescr->iOffsetempty))
+      *(((mng_uint8p)*ppChunk)+pDescr->iOffsetempty) = MNG_TRUE;
+
+    iRetcode = pDescr->pSpecialfunc(pData, *ppChunk);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    if ((((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_IDAT) ||
+        (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAT) ||
+        (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAA)    )
+    {
+      iRetcode = ((mng_chunk_headerp)*ppChunk)->fCleanup (pData, *ppChunk);
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+      *ppChunk = MNG_NULL;
+    } else {
+#ifdef MNG_STORE_CHUNKS
+      if (!pData->bStorechunks)
+#endif
+      {
+        iRetcode = ((mng_chunk_headerp)*ppChunk)->fCleanup (pData, *ppChunk);
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+        *ppChunk = MNG_NULL;
+      }
+    }
+  }
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if (iRawlen)
+  {
+#ifdef MNG_OPTIMIZE_DISPLAYCALLS
+    pData->iRawlen  = iRawlen;
+    pData->pRawdata = pRawdata;
+#endif
+
+                                       /* display processing */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_IDAT)
+      iRetcode = mng_process_display_idat (pData, iRawlen, pRawdata);
+#ifdef MNG_INCLUDE_JNG
+    else
+    if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAT)
+      iRetcode = mng_process_display_jdat (pData, iRawlen, pRawdata);
+    else
+    if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAA)
+      iRetcode = mng_process_display_jdaa (pData, iRawlen, pRawdata);
+#endif
+#else
+    if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_IDAT)
+      iRetcode = mng_process_display_idat (pData);
+#ifdef MNG_INCLUDE_JNG
+    else
+    if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAT)
+      iRetcode = mng_process_display_jdat (pData);
+    else
+    if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAA)
+      iRetcode = mng_process_display_jdaa (pData);
+#endif
+#endif
+
+    if (iRetcode)
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if ((pData->bStorechunks) && (!(*ppChunk)))
+  {
+    iRetcode = create_chunk_storage (pData, pHeader, iRawlen, pRawdata,
+                                     pField, iFields, ppChunk, MNG_FALSE);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* empty indicator ? */
+    if ((!iRawlen) && (pDescr->iOffsetempty))
+      *(((mng_uint8p)*ppChunk)+pDescr->iOffsetempty) = MNG_TRUE;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_ihdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IHDR, MNG_LC_START);
+#endif
+
+  if (iRawlen != 13)                   /* length oke ? */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                                       /* only allowed inside PNG or MNG */
+  if ((pData->eSigtype != mng_it_png) && (pData->eSigtype != mng_it_mng))
+    MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+                                       /* sequence checks */
+  if ((pData->eSigtype == mng_it_png) && (pData->iChunkseq > 1))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasIDAT) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasIDAT))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  pData->bHasIHDR      = MNG_TRUE;     /* indicate IHDR is present */
+                                       /* and store interesting fields */
+  if ((!pData->bHasDHDR) || (pData->iDeltatype == MNG_DELTATYPE_NOCHANGE))
+  {
+    pData->iDatawidth  = mng_get_uint32 (pRawdata);
+    pData->iDataheight = mng_get_uint32 (pRawdata+4);
+  }
+
+  pData->iBitdepth     = *(pRawdata+8);
+  pData->iColortype    = *(pRawdata+9);
+  pData->iCompression  = *(pRawdata+10);
+  pData->iFilter       = *(pRawdata+11);
+  pData->iInterlace    = *(pRawdata+12);
+
+#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT)
+  pData->iPNGmult = 1;
+  pData->iPNGdepth = pData->iBitdepth;
+#endif
+
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+  if (pData->iBitdepth < 8)
+      pData->iBitdepth = 8;
+#endif
+
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (pData->iBitdepth > 8)
+    {
+      pData->iBitdepth = 8;
+      pData->iPNGmult = 2;
+    }
+#endif
+
+  if ((pData->iBitdepth !=  8)      /* parameter validity checks */
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+      && (pData->iBitdepth !=  1) &&
+      (pData->iBitdepth !=  2) &&
+      (pData->iBitdepth !=  4)
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+      && (pData->iBitdepth != 16)   
+#endif
+      )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if ((pData->iColortype != MNG_COLORTYPE_GRAY   ) &&
+      (pData->iColortype != MNG_COLORTYPE_RGB    ) &&
+      (pData->iColortype != MNG_COLORTYPE_INDEXED) &&
+      (pData->iColortype != MNG_COLORTYPE_GRAYA  ) &&
+      (pData->iColortype != MNG_COLORTYPE_RGBA   )    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  if ((pData->iColortype == MNG_COLORTYPE_INDEXED) && (pData->iBitdepth > 8))
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if (((pData->iColortype == MNG_COLORTYPE_RGB    ) ||
+       (pData->iColortype == MNG_COLORTYPE_GRAYA  ) ||
+       (pData->iColortype == MNG_COLORTYPE_RGBA   )    ) &&
+      (pData->iBitdepth < 8                            )    )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if (pData->iCompression != MNG_COMPRESSION_DEFLATE)
+    MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+#if defined(FILTER192) || defined(FILTER193)
+  if ((pData->iFilter != MNG_FILTER_ADAPTIVE ) &&
+#if defined(FILTER192) && defined(FILTER193)
+      (pData->iFilter != MNG_FILTER_DIFFERING) &&
+      (pData->iFilter != MNG_FILTER_NOFILTER )    )
+#else
+#ifdef FILTER192
+      (pData->iFilter != MNG_FILTER_DIFFERING)    )
+#else
+      (pData->iFilter != MNG_FILTER_NOFILTER )    )
+#endif
+#endif
+    MNG_ERROR (pData, MNG_INVALIDFILTER);
+#else
+  if (pData->iFilter)
+    MNG_ERROR (pData, MNG_INVALIDFILTER);
+#endif
+
+  if ((pData->iInterlace != MNG_INTERLACE_NONE ) &&
+      (pData->iInterlace != MNG_INTERLACE_ADAM7)    )
+    MNG_ERROR (pData, MNG_INVALIDINTERLACE);
+
+#ifdef MNG_SUPPORT_DISPLAY 
+#ifndef MNG_NO_DELTA_PNG
+  if (pData->bHasDHDR)                 /* check the colortype for delta-images ! */
+  {
+    mng_imagedatap pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+
+    if (pData->iColortype != pBuf->iColortype)
+    {
+      if ( ( (pData->iColortype != MNG_COLORTYPE_INDEXED) ||
+             (pBuf->iColortype  == MNG_COLORTYPE_GRAY   )    ) &&
+           ( (pData->iColortype != MNG_COLORTYPE_GRAY   ) ||
+             (pBuf->iColortype  == MNG_COLORTYPE_INDEXED)    )    )
+        MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+    }
+  }
+#endif
+#endif
+
+  if (!pData->bHasheader)              /* first chunk ? */
+  {
+    pData->bHasheader = MNG_TRUE;      /* we've got a header */
+    pData->eImagetype = mng_it_png;    /* then this must be a PNG */
+    pData->iWidth     = pData->iDatawidth;
+    pData->iHeight    = pData->iDataheight;
+                                       /* predict alpha-depth ! */
+    if ((pData->iColortype == MNG_COLORTYPE_GRAYA  ) ||
+        (pData->iColortype == MNG_COLORTYPE_RGBA   )    )
+      pData->iAlphadepth = pData->iBitdepth;
+    else
+    if (pData->iColortype == MNG_COLORTYPE_INDEXED)
+      pData->iAlphadepth = 8;          /* worst case scenario */
+    else
+      pData->iAlphadepth = 1;  /* Possible tRNS cheap binary transparency */
+                                       /* fits on maximum canvas ? */
+    if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight))
+      MNG_WARNING (pData, MNG_IMAGETOOLARGE);
+
+#if !defined(MNG_INCLUDE_MPNG_PROPOSAL) || !defined(MNG_SUPPORT_DISPLAY)
+    if (pData->fProcessheader)         /* inform the app ? */
+      if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight))
+        MNG_ERROR (pData, MNG_APPMISCERROR);
+#endif        
+  }
+
+  if (!pData->bHasDHDR)
+    pData->iImagelevel++;              /* one level deeper */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode = mng_process_display_ihdr (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* fill the fields */
+    ((mng_ihdrp)*ppChunk)->iWidth       = mng_get_uint32 (pRawdata);
+    ((mng_ihdrp)*ppChunk)->iHeight      = mng_get_uint32 (pRawdata+4);
+    ((mng_ihdrp)*ppChunk)->iBitdepth    = pData->iBitdepth;
+    ((mng_ihdrp)*ppChunk)->iColortype   = pData->iColortype;
+    ((mng_ihdrp)*ppChunk)->iCompression = pData->iCompression;
+    ((mng_ihdrp)*ppChunk)->iFilter      = pData->iFilter;
+    ((mng_ihdrp)*ppChunk)->iInterlace   = pData->iInterlace;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_plte)
+{
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS)
+  mng_uint32  iX;
+  mng_uint8p  pRawdata2;
+#endif
+#ifdef MNG_SUPPORT_DISPLAY
+  mng_uint32  iRawlen2;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PLTE, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIDAT) || (pData->bHasJHDR))
+#else
+  if (pData->bHasIDAT)
+#endif  
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* multiple PLTE only inside BASI */
+  if ((pData->bHasPLTE) && (!pData->bHasBASI))
+    MNG_ERROR (pData, MNG_MULTIPLEERROR);
+                                       /* length must be multiple of 3 */
+  if (((iRawlen % 3) != 0) || (iRawlen > 768))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {                                    /* only allowed for indexed-color or
+                                          rgb(a)-color! */
+    if ((pData->iColortype != 2) && (pData->iColortype != 3) && (pData->iColortype != 6))
+      MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+                                       /* empty only allowed if global present */
+    if ((iRawlen == 0) && (!pData->bHasglobalPLTE))
+        MNG_ERROR (pData, MNG_CANNOTBEEMPTY);
+  }
+  else
+  {
+    if (iRawlen == 0)                  /* cannot be empty as global! */
+      MNG_ERROR (pData, MNG_CANNOTBEEMPTY);
+  }
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+    pData->bHasPLTE = MNG_TRUE;        /* got it! */
+  else
+    pData->bHasglobalPLTE = MNG_TRUE;
+
+  pData->iPLTEcount = iRawlen / 3;  
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {
+    mng_imagep     pImage;
+    mng_imagedatap pBuf;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* processing delta-image ? */
+    {                                  /* store in object 0 !!! */
+      pImage           = (mng_imagep)pData->pObjzero;
+      pBuf             = pImage->pImgbuf;
+      pBuf->bHasPLTE   = MNG_TRUE;     /* it's definitely got a PLTE now */
+      pBuf->iPLTEcount = iRawlen / 3;  /* this is the exact length */
+      pRawdata2        = pRawdata;     /* copy the entries */
+
+      for (iX = 0; iX < iRawlen / 3; iX++)
+      {
+        pBuf->aPLTEentries[iX].iRed   = *pRawdata2;
+        pBuf->aPLTEentries[iX].iGreen = *(pRawdata2+1);
+        pBuf->aPLTEentries[iX].iBlue  = *(pRawdata2+2);
+
+        pRawdata2 += 3;
+      }
+    }
+    else
+#endif
+    {                                  /* get the current object */
+      pImage = (mng_imagep)pData->pCurrentobj;
+
+      if (!pImage)                     /* no object then dump it in obj 0 */
+        pImage = (mng_imagep)pData->pObjzero;
+
+      pBuf = pImage->pImgbuf;          /* address the object buffer */
+      pBuf->bHasPLTE = MNG_TRUE;       /* and tell it it's got a PLTE now */
+
+      if (!iRawlen)                    /* if empty, inherit from global */
+      {
+        pBuf->iPLTEcount = pData->iGlobalPLTEcount;
+        MNG_COPY (pBuf->aPLTEentries, pData->aGlobalPLTEentries,
+                  sizeof (pBuf->aPLTEentries));
+
+        if (pData->bHasglobalTRNS)     /* also copy global tRNS ? */
+        {                              /* indicate tRNS available */
+          pBuf->bHasTRNS = MNG_TRUE;
+
+          iRawlen2  = pData->iGlobalTRNSrawlen;
+          pRawdata2 = (mng_uint8p)(pData->aGlobalTRNSrawdata);
+                                       /* global length oke ? */
+          if ((iRawlen2 == 0) || (iRawlen2 > pBuf->iPLTEcount))
+            MNG_ERROR (pData, MNG_GLOBALLENGTHERR);
+                                       /* copy it */
+          pBuf->iTRNScount = iRawlen2;
+          MNG_COPY (pBuf->aTRNSentries, pRawdata2, iRawlen2);
+        }
+      }
+      else
+      {                                /* store fields for future reference */
+        pBuf->iPLTEcount = iRawlen / 3;
+        pRawdata2        = pRawdata;
+
+        for (iX = 0; iX < pBuf->iPLTEcount; iX++)
+        {
+          pBuf->aPLTEentries[iX].iRed   = *pRawdata2;
+          pBuf->aPLTEentries[iX].iGreen = *(pRawdata2+1);
+          pBuf->aPLTEentries[iX].iBlue  = *(pRawdata2+2);
+
+          pRawdata2 += 3;
+        }
+      }
+    }
+  }
+  else                                 /* store as global */
+  {
+    pData->iGlobalPLTEcount = iRawlen / 3;
+    pRawdata2               = pRawdata;
+
+    for (iX = 0; iX < pData->iGlobalPLTEcount; iX++)
+    {
+      pData->aGlobalPLTEentries[iX].iRed   = *pRawdata2;
+      pData->aGlobalPLTEentries[iX].iGreen = *(pRawdata2+1);
+      pData->aGlobalPLTEentries[iX].iBlue  = *(pRawdata2+2);
+
+      pRawdata2 += 3;
+    }
+
+    {                                  /* create an animation object */
+      mng_retcode iRetcode = mng_create_ani_plte (pData, pData->iGlobalPLTEcount,
+                                                  pData->aGlobalPLTEentries);
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_pltep)*ppChunk)->bEmpty      = (mng_bool)(iRawlen == 0);
+    ((mng_pltep)*ppChunk)->iEntrycount = iRawlen / 3;
+    pRawdata2                          = pRawdata;
+
+    for (iX = 0; iX < ((mng_pltep)*ppChunk)->iEntrycount; iX++)
+    {
+      ((mng_pltep)*ppChunk)->aEntries[iX].iRed   = *pRawdata2;
+      ((mng_pltep)*ppChunk)->aEntries[iX].iGreen = *(pRawdata2+1);
+      ((mng_pltep)*ppChunk)->aEntries[iX].iBlue  = *(pRawdata2+2);
+
+      pRawdata2 += 3;
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PLTE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_idat)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IDAT, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_JNG                 /* sequence checks */
+  if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasJHDR) &&
+      (pData->iJHDRalphacompression != MNG_COMPRESSION_DEFLATE))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (pData->bHasJSEP)
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+#endif
+                                       /* not allowed for deltatype NO_CHANGE */
+#ifndef MNG_NO_DELTA_PNG
+  if ((pData->bHasDHDR) && ((pData->iDeltatype == MNG_DELTATYPE_NOCHANGE)))
+    MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+#endif
+                                       /* can only be empty in BASI-block! */
+  if ((iRawlen == 0) && (!pData->bHasBASI))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                                       /* indexed-color requires PLTE */
+  if ((pData->bHasIHDR) && (pData->iColortype == 3) && (!pData->bHasPLTE))
+    MNG_ERROR (pData, MNG_PLTEMISSING);
+
+  pData->bHasIDAT = MNG_TRUE;          /* got some IDAT now, don't we */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if (iRawlen)
+  {                                    /* display processing */
+    mng_retcode iRetcode = mng_process_display_idat (pData, iRawlen, pRawdata);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_idatp)*ppChunk)->bEmpty    = (mng_bool)(iRawlen == 0);
+    ((mng_idatp)*ppChunk)->iDatasize = iRawlen;
+
+    if (iRawlen != 0)                  /* is there any data ? */
+    {
+      MNG_ALLOC (pData, ((mng_idatp)*ppChunk)->pData, iRawlen);
+      MNG_COPY  (((mng_idatp)*ppChunk)->pData, pRawdata, iRawlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_iend)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IEND, MNG_LC_START);
+#endif
+
+  if (iRawlen > 0)                     /* must not contain data! */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_INCLUDE_JNG                 /* sequence checks */
+  if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* IHDR-block requires IDAT */
+  if ((pData->bHasIHDR) && (!pData->bHasIDAT))
+    MNG_ERROR (pData, MNG_IDATMISSING);
+
+  pData->iImagelevel--;                /* one level up */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* create an animation object */
+    mng_retcode iRetcode = mng_create_ani_image (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* display processing */
+    iRetcode = mng_process_display_iend (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if (!pData->bTimerset)               /* reset only if not broken !!! */
+  {
+#endif
+                                       /* IEND signals the end for most ... */
+    pData->bHasIHDR         = MNG_FALSE;
+    pData->bHasBASI         = MNG_FALSE;
+    pData->bHasDHDR         = MNG_FALSE;
+#ifdef MNG_INCLUDE_JNG
+    pData->bHasJHDR         = MNG_FALSE;
+    pData->bHasJSEP         = MNG_FALSE;
+    pData->bHasJDAA         = MNG_FALSE;
+    pData->bHasJDAT         = MNG_FALSE;
+#endif
+    pData->bHasPLTE         = MNG_FALSE;
+    pData->bHasTRNS         = MNG_FALSE;
+    pData->bHasGAMA         = MNG_FALSE;
+    pData->bHasCHRM         = MNG_FALSE;
+    pData->bHasSRGB         = MNG_FALSE;
+    pData->bHasICCP         = MNG_FALSE;
+    pData->bHasBKGD         = MNG_FALSE;
+    pData->bHasIDAT         = MNG_FALSE;
+#ifdef MNG_SUPPORT_DISPLAY
+  }
+#endif
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_trns)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_TRNS, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIDAT) || (pData->bHasJHDR))
+#else
+  if (pData->bHasIDAT)
+#endif  
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* multiple tRNS only inside BASI */
+  if ((pData->bHasTRNS) && (!pData->bHasBASI))
+    MNG_ERROR (pData, MNG_MULTIPLEERROR);
+
+  if (iRawlen > 256)                   /* it just can't be bigger than that! */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {                                    /* not allowed with full alpha-channel */
+    if ((pData->iColortype == 4) || (pData->iColortype == 6))
+      MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+
+    if (iRawlen != 0)                  /* filled ? */
+    {                                  /* length checks */
+      if ((pData->iColortype == 0) && (iRawlen != 2))
+        MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+      if ((pData->iColortype == 2) && (iRawlen != 6))
+        MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+      if (pData->iColortype == 3)
+      {
+        mng_imagep     pImage = (mng_imagep)pData->pCurrentobj;
+        mng_imagedatap pBuf;
+
+        if (!pImage)                   /* no object then check obj 0 */
+          pImage = (mng_imagep)pData->pObjzero;
+
+        pBuf = pImage->pImgbuf;        /* address object buffer */
+
+        if (iRawlen > pBuf->iPLTEcount)
+          MNG_ERROR (pData, MNG_INVALIDLENGTH);
+      }
+#endif
+    }
+    else                               /* if empty there must be global stuff! */
+    {
+      if (!pData->bHasglobalTRNS)
+        MNG_ERROR (pData, MNG_CANNOTBEEMPTY);
+    }
+  }
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+    pData->bHasTRNS = MNG_TRUE;        /* indicate tRNS available */
+  else
+    pData->bHasglobalTRNS = MNG_TRUE;
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {
+    mng_imagep     pImage;
+    mng_imagedatap pBuf;
+    mng_uint8p     pRawdata2;
+    mng_uint32     iRawlen2;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* processing delta-image ? */
+    {                                  /* store in object 0 !!! */
+      pImage = (mng_imagep)pData->pObjzero;
+      pBuf   = pImage->pImgbuf;        /* address object buffer */
+
+      switch (pData->iColortype)       /* store fields for future reference */
+      {
+        case 0: {                      /* gray */
+#if defined(MNG_NO_1_2_4BIT_SUPPORT)
+                  mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1,
+                                          0,0,0,0,0,0,0,1};
+#endif
+                  pBuf->iTRNSgray  = mng_get_uint16 (pRawdata);
+                  pBuf->iTRNSred   = 0;
+                  pBuf->iTRNSgreen = 0;
+                  pBuf->iTRNSblue  = 0;
+                  pBuf->iTRNScount = 0;
+#if defined(MNG_NO_1_2_4BIT_SUPPORT)
+                  pBuf->iTRNSgray *= multiplier[pData->iPNGdepth];
+#endif
+#if defined(MNG_NO_16BIT_SUPPORT)
+                  if (pData->iPNGmult == 2)
+                     pBuf->iTRNSgray >>= 8;
+#endif
+                  break;
+                }
+        case 2: {                      /* rgb */
+                  pBuf->iTRNSgray  = 0;
+                  pBuf->iTRNSred   = mng_get_uint16 (pRawdata);
+                  pBuf->iTRNSgreen = mng_get_uint16 (pRawdata+2);
+                  pBuf->iTRNSblue  = mng_get_uint16 (pRawdata+4);
+                  pBuf->iTRNScount = 0;
+#if defined(MNG_NO_16BIT_SUPPORT)
+                  if (pData->iPNGmult == 2)
+                  {
+                     pBuf->iTRNSred   >>= 8;
+                     pBuf->iTRNSgreen >>= 8;
+                     pBuf->iTRNSblue  >>= 8;
+                  }
+#endif
+                  break;
+                }
+        case 3: {                      /* indexed */
+                  pBuf->iTRNSgray  = 0;
+                  pBuf->iTRNSred   = 0;
+                  pBuf->iTRNSgreen = 0;
+                  pBuf->iTRNSblue  = 0;
+                  pBuf->iTRNScount = iRawlen;
+                  MNG_COPY (pBuf->aTRNSentries, pRawdata, iRawlen);
+                  break;
+                }
+      }
+
+      pBuf->bHasTRNS = MNG_TRUE;       /* tell it it's got a tRNS now */
+    }
+    else
+#endif
+    {                                  /* address current object */
+      pImage = (mng_imagep)pData->pCurrentobj;
+
+      if (!pImage)                     /* no object then dump it in obj 0 */
+        pImage = (mng_imagep)pData->pObjzero;
+
+      pBuf = pImage->pImgbuf;          /* address object buffer */
+      pBuf->bHasTRNS = MNG_TRUE;       /* and tell it it's got a tRNS now */
+
+      if (iRawlen == 0)                /* if empty, inherit from global */
+      {
+        iRawlen2  = pData->iGlobalTRNSrawlen;
+        pRawdata2 = (mng_ptr)(pData->aGlobalTRNSrawdata);
+                                         /* global length oke ? */
+        if ((pData->iColortype == 0) && (iRawlen2 != 2))
+          MNG_ERROR (pData, MNG_GLOBALLENGTHERR);
+
+        if ((pData->iColortype == 2) && (iRawlen2 != 6))
+          MNG_ERROR (pData, MNG_GLOBALLENGTHERR);
+
+        if ((pData->iColortype == 3) && ((iRawlen2 == 0) || (iRawlen2 > pBuf->iPLTEcount)))
+          MNG_ERROR (pData, MNG_GLOBALLENGTHERR);
+      }
+      else
+      {
+        iRawlen2  = iRawlen;
+        pRawdata2 = pRawdata;
+      }
+
+      switch (pData->iColortype)        /* store fields for future reference */
+      {
+        case 0: {                      /* gray */
+                  pBuf->iTRNSgray  = mng_get_uint16 (pRawdata2);
+                  pBuf->iTRNSred   = 0;
+                  pBuf->iTRNSgreen = 0;
+                  pBuf->iTRNSblue  = 0;
+                  pBuf->iTRNScount = 0;
+#if defined(MNG_NO_16BIT_SUPPORT)
+                  if (pData->iPNGmult == 2)
+                     pBuf->iTRNSgray >>= 8;
+#endif
+                  break;
+                }
+        case 2: {                      /* rgb */
+                  pBuf->iTRNSgray  = 0;
+                  pBuf->iTRNSred   = mng_get_uint16 (pRawdata2);
+                  pBuf->iTRNSgreen = mng_get_uint16 (pRawdata2+2);
+                  pBuf->iTRNSblue  = mng_get_uint16 (pRawdata2+4);
+                  pBuf->iTRNScount = 0;
+#if defined(MNG_NO_16BIT_SUPPORT)
+                  if (pData->iPNGmult == 2)
+                  {
+                     pBuf->iTRNSred   >>= 8;
+                     pBuf->iTRNSgreen >>= 8;
+                     pBuf->iTRNSblue  >>= 8;
+                  }
+#endif
+                  break;
+                }
+        case 3: {                      /* indexed */
+                  pBuf->iTRNSgray  = 0;
+                  pBuf->iTRNSred   = 0;
+                  pBuf->iTRNSgreen = 0;
+                  pBuf->iTRNSblue  = 0;
+                  pBuf->iTRNScount = iRawlen2;
+                  MNG_COPY (pBuf->aTRNSentries, pRawdata2, iRawlen2);
+                  break;
+                }
+      }
+    }  
+  }
+  else                                 /* store as global */
+  {
+    pData->iGlobalTRNSrawlen = iRawlen;
+    MNG_COPY (pData->aGlobalTRNSrawdata, pRawdata, iRawlen);
+
+    {                                  /* create an animation object */
+      mng_retcode iRetcode = mng_create_ani_trns (pData, pData->iGlobalTRNSrawlen,
+                                                  pData->aGlobalTRNSrawdata);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+    {                                  /* not global! */
+      ((mng_trnsp)*ppChunk)->bGlobal  = MNG_FALSE;
+      ((mng_trnsp)*ppChunk)->iType    = pData->iColortype;
+
+      if (iRawlen == 0)                /* if empty, indicate so */
+        ((mng_trnsp)*ppChunk)->bEmpty = MNG_TRUE;
+      else
+      {
+        ((mng_trnsp)*ppChunk)->bEmpty = MNG_FALSE;
+
+        switch (pData->iColortype)     /* store fields */
+        {
+          case 0: {                    /* gray */
+                    ((mng_trnsp)*ppChunk)->iGray  = mng_get_uint16 (pRawdata);
+                    break;
+                  }
+          case 2: {                    /* rgb */
+                    ((mng_trnsp)*ppChunk)->iRed   = mng_get_uint16 (pRawdata);
+                    ((mng_trnsp)*ppChunk)->iGreen = mng_get_uint16 (pRawdata+2);
+                    ((mng_trnsp)*ppChunk)->iBlue  = mng_get_uint16 (pRawdata+4);
+                    break;
+                  }
+          case 3: {                    /* indexed */
+                    ((mng_trnsp)*ppChunk)->iCount = iRawlen;
+                    MNG_COPY (((mng_trnsp)*ppChunk)->aEntries, pRawdata, iRawlen);
+                    break;
+                  }
+        }
+      }
+    }
+    else                               /* it's global! */
+    {
+      ((mng_trnsp)*ppChunk)->bEmpty  = (mng_bool)(iRawlen == 0);
+      ((mng_trnsp)*ppChunk)->bGlobal = MNG_TRUE;
+      ((mng_trnsp)*ppChunk)->iType   = 0;
+      ((mng_trnsp)*ppChunk)->iRawlen = iRawlen;
+
+      MNG_COPY (((mng_trnsp)*ppChunk)->aRawdata, pRawdata, iRawlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_TRNS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_gama)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_GAMA, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIDAT) || (pData->bHasPLTE) || (pData->bHasJDAT) || (pData->bHasJDAA))
+#else
+  if ((pData->bHasIDAT) || (pData->bHasPLTE))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+  {                                    /* length must be exactly 4 */
+    if (iRawlen != 4)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+  else
+  {                                    /* length must be empty or exactly 4 */
+    if ((iRawlen != 0) && (iRawlen != 4))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    pData->bHasGAMA = MNG_TRUE;        /* indicate we've got it */
+  else
+    pData->bHasglobalGAMA = (mng_bool)(iRawlen != 0);
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+  {
+    mng_imagep pImage;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* update delta image ? */
+    {                                  /* store in object 0 ! */
+      pImage = (mng_imagep)pData->pObjzero;
+                                       /* store for color-processing routines */
+      pImage->pImgbuf->iGamma   = mng_get_uint32 (pRawdata);
+      pImage->pImgbuf->bHasGAMA = MNG_TRUE;
+    }
+    else
+#endif
+    {
+      pImage = (mng_imagep)pData->pCurrentobj;
+
+      if (!pImage)                     /* no object then dump it in obj 0 */
+        pImage = (mng_imagep)pData->pObjzero;
+                                       /* store for color-processing routines */
+      pImage->pImgbuf->iGamma   = mng_get_uint32 (pRawdata);
+      pImage->pImgbuf->bHasGAMA = MNG_TRUE;
+    }
+  }
+  else
+  {                                    /* store as global */
+    if (iRawlen != 0)
+      pData->iGlobalGamma = mng_get_uint32 (pRawdata);
+
+    {                                  /* create an animation object */
+      mng_retcode iRetcode = mng_create_ani_gama (pData, (mng_bool)(iRawlen == 0),
+                                                  pData->iGlobalGamma);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_gamap)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)
+      ((mng_gamap)*ppChunk)->iGamma = mng_get_uint32 (pRawdata);
+
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_GAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_cHRM
+READ_CHUNK (mng_read_chrm)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CHRM, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIDAT) || (pData->bHasPLTE) || (pData->bHasJDAT) || (pData->bHasJDAA))
+#else
+  if ((pData->bHasIDAT) || (pData->bHasPLTE))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+  {                                    /* length must be exactly 32 */
+    if (iRawlen != 32)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+  else
+  {                                    /* length must be empty or exactly 32 */
+    if ((iRawlen != 0) && (iRawlen != 32))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    pData->bHasCHRM = MNG_TRUE;        /* indicate we've got it */
+  else
+    pData->bHasglobalCHRM = (mng_bool)(iRawlen != 0);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_uint32 iWhitepointx,   iWhitepointy;
+    mng_uint32 iPrimaryredx,   iPrimaryredy;
+    mng_uint32 iPrimarygreenx, iPrimarygreeny;
+    mng_uint32 iPrimarybluex,  iPrimarybluey;
+
+    iWhitepointx   = mng_get_uint32 (pRawdata);
+    iWhitepointy   = mng_get_uint32 (pRawdata+4);
+    iPrimaryredx   = mng_get_uint32 (pRawdata+8);
+    iPrimaryredy   = mng_get_uint32 (pRawdata+12);
+    iPrimarygreenx = mng_get_uint32 (pRawdata+16);
+    iPrimarygreeny = mng_get_uint32 (pRawdata+20);
+    iPrimarybluex  = mng_get_uint32 (pRawdata+24);
+    iPrimarybluey  = mng_get_uint32 (pRawdata+28);
+
+#ifdef MNG_INCLUDE_JNG
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    {
+      mng_imagep     pImage;
+      mng_imagedatap pBuf;
+
+#ifndef MNG_NO_DELTA_PNG
+      if (pData->bHasDHDR)             /* update delta image ? */
+      {                                /* store it in object 0 ! */
+        pImage = (mng_imagep)pData->pObjzero;
+
+        pBuf = pImage->pImgbuf;        /* address object buffer */
+        pBuf->bHasCHRM = MNG_TRUE;     /* and tell it it's got a CHRM now */
+                                       /* store for color-processing routines */
+        pBuf->iWhitepointx   = iWhitepointx;
+        pBuf->iWhitepointy   = iWhitepointy;
+        pBuf->iPrimaryredx   = iPrimaryredx;
+        pBuf->iPrimaryredy   = iPrimaryredy;
+        pBuf->iPrimarygreenx = iPrimarygreenx;
+        pBuf->iPrimarygreeny = iPrimarygreeny;
+        pBuf->iPrimarybluex  = iPrimarybluex;
+        pBuf->iPrimarybluey  = iPrimarybluey;
+      }
+      else
+#endif
+      {
+        pImage = (mng_imagep)pData->pCurrentobj;
+
+        if (!pImage)                   /* no object then dump it in obj 0 */
+          pImage = (mng_imagep)pData->pObjzero;
+
+        pBuf = pImage->pImgbuf;        /* address object buffer */
+        pBuf->bHasCHRM = MNG_TRUE;     /* and tell it it's got a CHRM now */
+                                       /* store for color-processing routines */
+        pBuf->iWhitepointx   = iWhitepointx;
+        pBuf->iWhitepointy   = iWhitepointy;
+        pBuf->iPrimaryredx   = iPrimaryredx;
+        pBuf->iPrimaryredy   = iPrimaryredy;
+        pBuf->iPrimarygreenx = iPrimarygreenx;
+        pBuf->iPrimarygreeny = iPrimarygreeny;
+        pBuf->iPrimarybluex  = iPrimarybluex;
+        pBuf->iPrimarybluey  = iPrimarybluey;
+      }
+    }
+    else
+    {                                  /* store as global */
+      if (iRawlen != 0)
+      {
+        pData->iGlobalWhitepointx   = iWhitepointx;
+        pData->iGlobalWhitepointy   = iWhitepointy;
+        pData->iGlobalPrimaryredx   = iPrimaryredx;
+        pData->iGlobalPrimaryredy   = iPrimaryredy;
+        pData->iGlobalPrimarygreenx = iPrimarygreenx;
+        pData->iGlobalPrimarygreeny = iPrimarygreeny;
+        pData->iGlobalPrimarybluex  = iPrimarybluex;
+        pData->iGlobalPrimarybluey  = iPrimarybluey;
+      }
+
+      {                                /* create an animation object */
+        mng_retcode iRetcode = mng_create_ani_chrm (pData, (mng_bool)(iRawlen == 0),
+                                                    iWhitepointx,   iWhitepointy,
+                                                    iPrimaryredx,   iPrimaryredy,
+                                                    iPrimarygreenx, iPrimarygreeny,
+                                                    iPrimarybluex,  iPrimarybluey);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+      }
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_chrmp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)
+    {
+      ((mng_chrmp)*ppChunk)->iWhitepointx = mng_get_uint32 (pRawdata);
+      ((mng_chrmp)*ppChunk)->iWhitepointy = mng_get_uint32 (pRawdata+4);
+      ((mng_chrmp)*ppChunk)->iRedx        = mng_get_uint32 (pRawdata+8);
+      ((mng_chrmp)*ppChunk)->iRedy        = mng_get_uint32 (pRawdata+12);
+      ((mng_chrmp)*ppChunk)->iGreenx      = mng_get_uint32 (pRawdata+16);
+      ((mng_chrmp)*ppChunk)->iGreeny      = mng_get_uint32 (pRawdata+20);
+      ((mng_chrmp)*ppChunk)->iBluex       = mng_get_uint32 (pRawdata+24);
+      ((mng_chrmp)*ppChunk)->iBluey       = mng_get_uint32 (pRawdata+28);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CHRM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_srgb)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SRGB, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIDAT) || (pData->bHasPLTE) || (pData->bHasJDAT) || (pData->bHasJDAA))
+#else
+  if ((pData->bHasIDAT) || (pData->bHasPLTE))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+  {                                    /* length must be exactly 1 */
+    if (iRawlen != 1)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+  else
+  {                                    /* length must be empty or exactly 1 */
+    if ((iRawlen != 0) && (iRawlen != 1))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    pData->bHasSRGB = MNG_TRUE;        /* indicate we've got it */
+  else
+    pData->bHasglobalSRGB = (mng_bool)(iRawlen != 0);
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+  {
+    mng_imagep pImage;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* update delta image ? */
+    {                                  /* store in object 0 ! */
+      pImage = (mng_imagep)pData->pObjzero;
+                                       /* store for color-processing routines */
+      pImage->pImgbuf->iRenderingintent = *pRawdata;
+      pImage->pImgbuf->bHasSRGB         = MNG_TRUE;
+    }
+    else
+#endif
+    {
+      pImage = (mng_imagep)pData->pCurrentobj;
+
+      if (!pImage)                     /* no object then dump it in obj 0 */
+        pImage = (mng_imagep)pData->pObjzero;
+                                       /* store for color-processing routines */
+      pImage->pImgbuf->iRenderingintent = *pRawdata;
+      pImage->pImgbuf->bHasSRGB         = MNG_TRUE;
+    }
+  }
+  else
+  {                                    /* store as global */
+    if (iRawlen != 0)
+      pData->iGlobalRendintent = *pRawdata;
+
+    {                                  /* create an animation object */
+      mng_retcode iRetcode = mng_create_ani_srgb (pData, (mng_bool)(iRawlen == 0),
+                                                  pData->iGlobalRendintent);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_srgbp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)
+      ((mng_srgbp)*ppChunk)->iRenderingintent = *pRawdata;
+
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_iCCP
+READ_CHUNK (mng_read_iccp)
+{
+  mng_retcode iRetcode;
+  mng_uint8p  pTemp;
+  mng_uint32  iCompressedsize;
+  mng_uint32  iProfilesize;
+  mng_uint32  iBufsize = 0;
+  mng_uint8p  pBuf = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ICCP, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIDAT) || (pData->bHasPLTE) || (pData->bHasJDAT) || (pData->bHasJDAA))
+#else
+  if ((pData->bHasIDAT) || (pData->bHasPLTE))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+  {                                    /* length must be at least 2 */
+    if (iRawlen < 2)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+  else
+  {                                    /* length must be empty or at least 2 */
+    if ((iRawlen != 0) && (iRawlen < 2))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+
+  pTemp = find_null (pRawdata);        /* find null-separator */
+                                       /* not found inside input-data ? */
+  if ((pTemp - pRawdata) > (mng_int32)iRawlen)
+    MNG_ERROR (pData, MNG_NULLNOTFOUND);
+                                       /* determine size of compressed profile */
+  iCompressedsize = (mng_uint32)(iRawlen - (pTemp - pRawdata) - 2);
+                                       /* decompress the profile */
+  iRetcode = mng_inflate_buffer (pData, pTemp+2, iCompressedsize,
+                                 &pBuf, &iBufsize, &iProfilesize);
+
+#ifdef MNG_CHECK_BAD_ICCP              /* Check for bad iCCP chunk */
+  if ((iRetcode) && (!strncmp ((char *)pRawdata, "Photoshop ICC profile", 21)))
+  {
+    if (iRawlen == 2615)               /* is it the sRGB profile ? */
+    {
+      mng_chunk_header chunk_srgb =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+        {MNG_UINT_sRGB, mng_init_general, mng_free_general, mng_read_srgb, mng_write_srgb, mng_assign_general, 0, 0, sizeof(mng_srgb)};
+#else
+        {MNG_UINT_sRGB, mng_init_srgb, mng_free_srgb, mng_read_srgb, mng_write_srgb, mng_assign_srgb, 0, 0};
+#endif
+                                       /* pretend it's an sRGB chunk then ! */
+      iRetcode = mng_read_srgb (pData, &chunk_srgb, 1, (mng_ptr)"0", ppChunk);
+
+      if (iRetcode)                    /* on error bail out */
+      {                                /* don't forget to drop the temp buffer */
+        MNG_FREEX (pData, pBuf, iBufsize);
+        return iRetcode;
+      }
+    }
+  }
+  else
+  {
+#endif /* MNG_CHECK_BAD_ICCP */
+
+    if (iRetcode)                      /* on error bail out */
+    {                                  /* don't forget to drop the temp buffer */
+      MNG_FREEX (pData, pBuf, iBufsize);
+      return iRetcode;
+    }
+
+#ifdef MNG_INCLUDE_JNG
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+      pData->bHasICCP = MNG_TRUE;      /* indicate we've got it */
+    else
+      pData->bHasglobalICCP = (mng_bool)(iRawlen != 0);
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifdef MNG_INCLUDE_JNG
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+    if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    {
+      mng_imagep pImage;
+
+#ifndef MNG_NO_DELTA_PNG
+      if (pData->bHasDHDR)             /* update delta image ? */
+      {                                /* store in object 0 ! */
+        pImage = (mng_imagep)pData->pObjzero;
+
+        if (pImage->pImgbuf->pProfile) /* profile existed ? */
+          MNG_FREEX (pData, pImage->pImgbuf->pProfile, pImage->pImgbuf->iProfilesize);
+                                       /* allocate a buffer & copy it */
+        MNG_ALLOC (pData, pImage->pImgbuf->pProfile, iProfilesize);
+        MNG_COPY  (pImage->pImgbuf->pProfile, pBuf, iProfilesize);
+                                       /* store its length as well */
+        pImage->pImgbuf->iProfilesize = iProfilesize;
+        pImage->pImgbuf->bHasICCP     = MNG_TRUE;
+      }
+      else
+#endif
+      {
+        pImage = (mng_imagep)pData->pCurrentobj;
+
+        if (!pImage)                   /* no object then dump it in obj 0 */
+          pImage = (mng_imagep)pData->pObjzero;
+
+        if (pImage->pImgbuf->pProfile) /* profile existed ? */
+          MNG_FREEX (pData, pImage->pImgbuf->pProfile, pImage->pImgbuf->iProfilesize);
+                                       /* allocate a buffer & copy it */
+        MNG_ALLOC (pData, pImage->pImgbuf->pProfile, iProfilesize);
+        MNG_COPY  (pImage->pImgbuf->pProfile, pBuf, iProfilesize);
+                                       /* store its length as well */
+        pImage->pImgbuf->iProfilesize = iProfilesize;
+        pImage->pImgbuf->bHasICCP     = MNG_TRUE;
+      }
+    }
+    else
+    {                                  /* store as global */
+      if (iRawlen == 0)                /* empty chunk ? */
+      {
+        if (pData->pGlobalProfile)     /* did we have a global profile ? */
+          MNG_FREEX (pData, pData->pGlobalProfile, pData->iGlobalProfilesize);
+
+        pData->iGlobalProfilesize = 0; /* reset to null */
+        pData->pGlobalProfile     = MNG_NULL;
+      }
+      else
+      {                                /* allocate a global buffer & copy it */
+        MNG_ALLOC (pData, pData->pGlobalProfile, iProfilesize);
+        MNG_COPY  (pData->pGlobalProfile, pBuf, iProfilesize);
+                                       /* store its length as well */
+        pData->iGlobalProfilesize = iProfilesize;
+      }
+
+                                       /* create an animation object */
+      iRetcode = mng_create_ani_iccp (pData, (mng_bool)(iRawlen == 0),
+                                      pData->iGlobalProfilesize,
+                                      pData->pGlobalProfile);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+    if (pData->bStorechunks)
+    {                                  /* initialize storage */
+      iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+      if (iRetcode)                    /* on error bail out */
+      {                                /* don't forget to drop the temp buffer */
+        MNG_FREEX (pData, pBuf, iBufsize);
+        return iRetcode;
+      }
+                                       /* store the fields */
+      ((mng_iccpp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+
+      if (iRawlen)                     /* not empty ? */
+      {
+        if (!pBuf)                     /* hasn't been unpuzzled it yet ? */
+        {                              /* find null-separator */
+          pTemp = find_null (pRawdata);
+                                       /* not found inside input-data ? */
+          if ((pTemp - pRawdata) > (mng_int32)iRawlen)
+            MNG_ERROR (pData, MNG_NULLNOTFOUND);
+                                       /* determine size of compressed profile */
+          iCompressedsize = iRawlen - (pTemp - pRawdata) - 2;
+                                       /* decompress the profile */
+          iRetcode = mng_inflate_buffer (pData, pTemp+2, iCompressedsize,
+                                         &pBuf, &iBufsize, &iProfilesize);
+
+          if (iRetcode)                /* on error bail out */
+          {                            /* don't forget to drop the temp buffer */
+            MNG_FREEX (pData, pBuf, iBufsize);
+            return iRetcode;
+          }
+        }
+
+        ((mng_iccpp)*ppChunk)->iNamesize = (mng_uint32)(pTemp - pRawdata);
+
+        if (((mng_iccpp)*ppChunk)->iNamesize)
+        {
+          MNG_ALLOC (pData, ((mng_iccpp)*ppChunk)->zName,
+                            ((mng_iccpp)*ppChunk)->iNamesize + 1);
+          MNG_COPY  (((mng_iccpp)*ppChunk)->zName, pRawdata,
+                     ((mng_iccpp)*ppChunk)->iNamesize);
+        }
+
+        ((mng_iccpp)*ppChunk)->iCompression = *(pTemp+1);
+        ((mng_iccpp)*ppChunk)->iProfilesize = iProfilesize;
+
+        MNG_ALLOC (pData, ((mng_iccpp)*ppChunk)->pProfile, iProfilesize);
+        MNG_COPY  (((mng_iccpp)*ppChunk)->pProfile, pBuf, iProfilesize);
+      }
+    }
+#endif /* MNG_STORE_CHUNKS */
+
+    if (pBuf)                          /* free the temporary buffer */
+      MNG_FREEX (pData, pBuf, iBufsize);
+
+#ifdef MNG_CHECK_BAD_ICCP
+  }
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ICCP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_tEXt
+READ_CHUNK (mng_read_text)
+{
+  mng_uint32 iKeywordlen, iTextlen;
+  mng_pchar  zKeyword, zText;
+  mng_uint8p pTemp;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_TEXT, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen < 2)                     /* length must be at least 2 */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pTemp = find_null (pRawdata);        /* find the null separator */
+                                       /* not found inside input-data ? */
+  if ((pTemp - pRawdata) > (mng_int32)iRawlen)
+    MNG_ERROR (pData, MNG_NULLNOTFOUND);
+
+  if (pTemp == pRawdata)               /* there must be at least 1 char for keyword */
+    MNG_ERROR (pData, MNG_KEYWORDNULL);
+
+  iKeywordlen = (mng_uint32)(pTemp - pRawdata);
+  iTextlen    = iRawlen - iKeywordlen - 1;
+
+  if (pData->fProcesstext)             /* inform the application ? */
+  {
+    mng_bool bOke;
+
+    MNG_ALLOC (pData, zKeyword, iKeywordlen + 1);
+    MNG_COPY  (zKeyword, pRawdata, iKeywordlen);
+
+    MNG_ALLOCX (pData, zText, iTextlen + 1);
+
+    if (!zText)                        /* on error bail out */
+    {
+      MNG_FREEX (pData, zKeyword, iKeywordlen + 1);
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+
+    if (iTextlen)
+      MNG_COPY (zText, pTemp+1, iTextlen);
+
+    bOke = pData->fProcesstext ((mng_handle)pData, MNG_TYPE_TEXT, zKeyword, zText, 0, 0);
+
+    MNG_FREEX (pData, zText, iTextlen + 1);
+    MNG_FREEX (pData, zKeyword, iKeywordlen + 1);
+
+    if (!bOke)
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+
+  }
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_textp)*ppChunk)->iKeywordsize = iKeywordlen;
+    ((mng_textp)*ppChunk)->iTextsize    = iTextlen;
+
+    if (iKeywordlen)
+    {
+      MNG_ALLOC (pData, ((mng_textp)*ppChunk)->zKeyword, iKeywordlen+1);
+      MNG_COPY  (((mng_textp)*ppChunk)->zKeyword, pRawdata, iKeywordlen);
+    }
+
+    if (iTextlen)
+    {
+      MNG_ALLOC (pData, ((mng_textp)*ppChunk)->zText, iTextlen+1);
+      MNG_COPY  (((mng_textp)*ppChunk)->zText, pTemp+1, iTextlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_TEXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_zTXt
+READ_CHUNK (mng_read_ztxt)
+{
+  mng_retcode iRetcode;
+  mng_uint32  iKeywordlen, iTextlen;
+  mng_pchar   zKeyword;
+  mng_uint8p  pTemp;
+  mng_uint32  iCompressedsize;
+  mng_uint32  iBufsize;
+  mng_uint8p  pBuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ZTXT, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen < 3)                     /* length must be at least 3 */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pTemp = find_null (pRawdata);        /* find the null separator */
+                                       /* not found inside input-data ? */
+  if ((pTemp - pRawdata) > (mng_int32)iRawlen)
+    MNG_ERROR (pData, MNG_NULLNOTFOUND);
+
+  if (pTemp == pRawdata)               /* there must be at least 1 char for keyword */
+    MNG_ERROR (pData, MNG_KEYWORDNULL);
+
+  if (*(pTemp+1) != 0)                 /* only deflate compression-method allowed */
+    MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+  iKeywordlen     = (mng_uint32)(pTemp - pRawdata);
+  iCompressedsize = (mng_uint32)(iRawlen - iKeywordlen - 2);
+
+  zKeyword        = 0;                 /* there's no keyword buffer yet */
+  pBuf            = 0;                 /* or a temporary buffer ! */
+
+  if (pData->fProcesstext)             /* inform the application ? */
+  {                                    /* decompress the text */
+    iRetcode = mng_inflate_buffer (pData, pTemp+2, iCompressedsize,
+                                   &pBuf, &iBufsize, &iTextlen);
+
+    if (iRetcode)                      /* on error bail out */
+    {                                  /* don't forget to drop the temp buffers */
+      MNG_FREEX (pData, pBuf, iBufsize);
+      return iRetcode;
+    }
+
+    MNG_ALLOCX (pData, zKeyword, iKeywordlen+1);
+
+    if (!zKeyword)                     /* on error bail out */
+    {                                  /* don't forget to drop the temp buffers */
+      MNG_FREEX (pData, pBuf, iBufsize);
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+
+    MNG_COPY (zKeyword, pRawdata, iKeywordlen);
+
+    if (!pData->fProcesstext ((mng_handle)pData, MNG_TYPE_ZTXT, zKeyword, (mng_pchar)pBuf, 0, 0))
+    {                                  /* don't forget to drop the temp buffers */
+      MNG_FREEX (pData, pBuf, iBufsize);
+      MNG_FREEX (pData, zKeyword, iKeywordlen+1);
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+    }
+  }
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+    {                                  /* don't forget to drop the temp buffers */
+      MNG_FREEX (pData, pBuf, iBufsize);
+      MNG_FREEX (pData, zKeyword, iKeywordlen+1);
+      return iRetcode;
+    }
+                                       /* store the fields */
+    ((mng_ztxtp)*ppChunk)->iKeywordsize = iKeywordlen;
+    ((mng_ztxtp)*ppChunk)->iCompression = *(pTemp+1);
+
+    if ((!pBuf) && (iCompressedsize))  /* did we not get a text-buffer yet ? */
+    {                                  /* decompress the text */
+      iRetcode = mng_inflate_buffer (pData, pTemp+2, iCompressedsize,
+                                     &pBuf, &iBufsize, &iTextlen);
+
+      if (iRetcode)                    /* on error bail out */
+      {                                /* don't forget to drop the temp buffers */
+        MNG_FREEX (pData, pBuf, iBufsize);
+        MNG_FREEX (pData, zKeyword, iKeywordlen+1);
+        return iRetcode;
+      }
+    }
+
+    MNG_ALLOCX (pData, ((mng_ztxtp)*ppChunk)->zKeyword, iKeywordlen + 1);
+                                       /* on error bail out */
+    if (!((mng_ztxtp)*ppChunk)->zKeyword)
+    {                                  /* don't forget to drop the temp buffers */
+      MNG_FREEX (pData, pBuf, iBufsize);
+      MNG_FREEX (pData, zKeyword, iKeywordlen+1);
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+
+    MNG_COPY (((mng_ztxtp)*ppChunk)->zKeyword, pRawdata, iKeywordlen);
+
+    ((mng_ztxtp)*ppChunk)->iTextsize = iTextlen;
+
+    if (iCompressedsize)
+    {
+      MNG_ALLOCX (pData, ((mng_ztxtp)*ppChunk)->zText, iTextlen + 1);
+                                       /* on error bail out */
+      if (!((mng_ztxtp)*ppChunk)->zText)
+      {                                /* don't forget to drop the temp buffers */
+        MNG_FREEX (pData, pBuf, iBufsize);
+        MNG_FREEX (pData, zKeyword, iKeywordlen+1);
+        MNG_ERROR (pData, MNG_OUTOFMEMORY);
+      }
+
+      MNG_COPY (((mng_ztxtp)*ppChunk)->zText, pBuf, iTextlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+  MNG_FREEX (pData, pBuf, iBufsize);   /* free the temporary buffers */
+  MNG_FREEX (pData, zKeyword, iKeywordlen+1);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ZTXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_iTXt
+READ_CHUNK (mng_read_itxt)
+{
+  mng_retcode iRetcode;
+  mng_uint32  iKeywordlen, iTextlen, iLanguagelen, iTranslationlen;
+  mng_pchar   zKeyword, zLanguage, zTranslation;
+  mng_uint8p  pNull1, pNull2, pNull3;
+  mng_uint32  iCompressedsize;
+  mng_uint8   iCompressionflag;
+  mng_uint32  iBufsize;
+  mng_uint8p  pBuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ITXT, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen < 6)                     /* length must be at least 6 */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pNull1 = find_null (pRawdata);       /* find the null separators */
+  pNull2 = find_null (pNull1+3);
+  pNull3 = find_null (pNull2+1);
+                                       /* not found inside input-data ? */
+  if (((pNull1 - pRawdata) > (mng_int32)iRawlen) ||
+      ((pNull2 - pRawdata) > (mng_int32)iRawlen) ||
+      ((pNull3 - pRawdata) > (mng_int32)iRawlen)    )
+    MNG_ERROR (pData, MNG_NULLNOTFOUND);
+
+  if (pNull1 == pRawdata)              /* there must be at least 1 char for keyword */
+    MNG_ERROR (pData, MNG_KEYWORDNULL);
+                                       /* compression or not ? */
+  if ((*(pNull1+1) != 0) && (*(pNull1+1) != 1))
+    MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+  if (*(pNull1+2) != 0)                /* only deflate compression-method allowed */
+    MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+  iKeywordlen      = (mng_uint32)(pNull1 - pRawdata);
+  iLanguagelen     = (mng_uint32)(pNull2 - pNull1 - 3);
+  iTranslationlen  = (mng_uint32)(pNull3 - pNull2 - 1);
+  iCompressedsize  = (mng_uint32)(iRawlen - iKeywordlen - iLanguagelen - iTranslationlen - 5);
+  iCompressionflag = *(pNull1+1);
+
+  zKeyword     = 0;                    /* no buffers acquired yet */
+  zLanguage    = 0;
+  zTranslation = 0;
+  pBuf         = 0;
+  iTextlen     = 0;
+
+  if (pData->fProcesstext)             /* inform the application ? */
+  {
+    if (iCompressionflag)              /* decompress the text ? */
+    {
+      iRetcode = mng_inflate_buffer (pData, pNull3+1, iCompressedsize,
+                                     &pBuf, &iBufsize, &iTextlen);
+
+      if (iRetcode)                    /* on error bail out */
+      {                                /* don't forget to drop the temp buffer */
+        MNG_FREEX (pData, pBuf, iBufsize);
+        return iRetcode;
+      }
+    }
+    else
+    {
+      iTextlen = iCompressedsize;
+      iBufsize = iTextlen+1;           /* plus 1 for terminator byte!!! */
+
+      MNG_ALLOC (pData, pBuf, iBufsize);
+      MNG_COPY  (pBuf, pNull3+1, iTextlen);
+    }
+
+    MNG_ALLOCX (pData, zKeyword,     iKeywordlen     + 1);
+    MNG_ALLOCX (pData, zLanguage,    iLanguagelen    + 1);
+    MNG_ALLOCX (pData, zTranslation, iTranslationlen + 1);
+                                       /* on error bail out */
+    if ((!zKeyword) || (!zLanguage) || (!zTranslation))
+    {                                  /* don't forget to drop the temp buffers */
+      MNG_FREEX (pData, zTranslation, iTranslationlen + 1);
+      MNG_FREEX (pData, zLanguage,    iLanguagelen    + 1);
+      MNG_FREEX (pData, zKeyword,     iKeywordlen     + 1);
+      MNG_FREEX (pData, pBuf, iBufsize);
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+
+    MNG_COPY (zKeyword,     pRawdata, iKeywordlen);
+    MNG_COPY (zLanguage,    pNull1+3, iLanguagelen);
+    MNG_COPY (zTranslation, pNull2+1, iTranslationlen);
+
+    if (!pData->fProcesstext ((mng_handle)pData, MNG_TYPE_ITXT, zKeyword, (mng_pchar)pBuf,
+                                                                zLanguage, zTranslation))
+    {                                  /* don't forget to drop the temp buffers */
+      MNG_FREEX (pData, zTranslation, iTranslationlen + 1);
+      MNG_FREEX (pData, zLanguage,    iLanguagelen    + 1);
+      MNG_FREEX (pData, zKeyword,     iKeywordlen     + 1);
+      MNG_FREEX (pData, pBuf,         iBufsize);
+
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+    }
+  }
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+    {                                  /* don't forget to drop the temp buffers */
+      MNG_FREEX (pData, zTranslation, iTranslationlen + 1);
+      MNG_FREEX (pData, zLanguage,    iLanguagelen    + 1);
+      MNG_FREEX (pData, zKeyword,     iKeywordlen     + 1);
+      MNG_FREEX (pData, pBuf,         iBufsize);
+      return iRetcode;
+    }
+                                       /* store the fields */
+    ((mng_itxtp)*ppChunk)->iKeywordsize       = iKeywordlen;
+    ((mng_itxtp)*ppChunk)->iLanguagesize      = iLanguagelen;
+    ((mng_itxtp)*ppChunk)->iTranslationsize   = iTranslationlen;
+    ((mng_itxtp)*ppChunk)->iCompressionflag   = *(pNull1+1);
+    ((mng_itxtp)*ppChunk)->iCompressionmethod = *(pNull1+2);
+
+    if ((!pBuf) && (iCompressedsize))  /* did we not get a text-buffer yet ? */
+    {
+      if (iCompressionflag)            /* decompress the text ? */
+      {
+        iRetcode = mng_inflate_buffer (pData, pNull3+1, iCompressedsize,
+                                       &pBuf, &iBufsize, &iTextlen);
+
+        if (iRetcode)                  /* on error bail out */
+        {                              /* don't forget to drop the temp buffers */
+          MNG_FREEX (pData, zTranslation, iTranslationlen + 1);
+          MNG_FREEX (pData, zLanguage,    iLanguagelen    + 1);
+          MNG_FREEX (pData, zKeyword,     iKeywordlen     + 1);
+          MNG_FREEX (pData, pBuf,         iBufsize);
+          return iRetcode;
+        }
+      }
+      else
+      {
+        iTextlen = iCompressedsize;
+        iBufsize = iTextlen+1;         /* plus 1 for terminator byte!!! */
+
+        MNG_ALLOC (pData, pBuf, iBufsize);
+        MNG_COPY  (pBuf, pNull3+1, iTextlen);
+      }
+    }
+
+    MNG_ALLOCX (pData, ((mng_itxtp)*ppChunk)->zKeyword,     iKeywordlen     + 1);
+    MNG_ALLOCX (pData, ((mng_itxtp)*ppChunk)->zLanguage,    iLanguagelen    + 1);
+    MNG_ALLOCX (pData, ((mng_itxtp)*ppChunk)->zTranslation, iTranslationlen + 1);
+                                       /* on error bail out */
+    if ((!((mng_itxtp)*ppChunk)->zKeyword    ) ||
+        (!((mng_itxtp)*ppChunk)->zLanguage   ) ||
+        (!((mng_itxtp)*ppChunk)->zTranslation)    )
+    {                                  /* don't forget to drop the temp buffers */
+      MNG_FREEX (pData, zTranslation, iTranslationlen + 1);
+      MNG_FREEX (pData, zLanguage,    iLanguagelen    + 1);
+      MNG_FREEX (pData, zKeyword,     iKeywordlen     + 1);
+      MNG_FREEX (pData, pBuf,         iBufsize);
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+
+    MNG_COPY (((mng_itxtp)*ppChunk)->zKeyword,     pRawdata, iKeywordlen);
+    MNG_COPY (((mng_itxtp)*ppChunk)->zLanguage,    pNull1+3, iLanguagelen);
+    MNG_COPY (((mng_itxtp)*ppChunk)->zTranslation, pNull2+1, iTranslationlen);
+
+    ((mng_itxtp)*ppChunk)->iTextsize = iTextlen;
+
+    if (iTextlen)
+    {
+      MNG_ALLOCX (pData, ((mng_itxtp)*ppChunk)->zText, iTextlen + 1);
+
+      if (!((mng_itxtp)*ppChunk)->zText)
+      {                                /* don't forget to drop the temp buffers */
+        MNG_FREEX (pData, zTranslation, iTranslationlen + 1);
+        MNG_FREEX (pData, zLanguage,    iLanguagelen    + 1);
+        MNG_FREEX (pData, zKeyword,     iKeywordlen     + 1);
+        MNG_FREEX (pData, pBuf,         iBufsize);
+        MNG_ERROR (pData, MNG_OUTOFMEMORY);
+      }
+
+      MNG_COPY  (((mng_itxtp)*ppChunk)->zText, pBuf, iTextlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+                                       /* free the temporary buffers */
+  MNG_FREEX (pData, zTranslation, iTranslationlen + 1);
+  MNG_FREEX (pData, zLanguage,    iLanguagelen    + 1);
+  MNG_FREEX (pData, zKeyword,     iKeywordlen     + 1);
+  MNG_FREEX (pData, pBuf,         iBufsize);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ITXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_bKGD
+READ_CHUNK (mng_read_bkgd)
+{
+#ifdef MNG_SUPPORT_DISPLAY
+  mng_imagep     pImage = (mng_imagep)pData->pCurrentobj;
+  mng_imagedatap pBuf;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_BKGD, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIDAT) || (pData->bHasJDAT) || (pData->bHasJDAA))
+#else
+  if (pData->bHasIDAT)
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen > 6)                     /* it just can't be bigger than that! */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_INCLUDE_JNG                 /* length checks */
+  if (pData->bHasJHDR)
+  {
+    if (((pData->iJHDRcolortype == 8) || (pData->iJHDRcolortype == 12)) && (iRawlen != 2))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if (((pData->iJHDRcolortype == 10) || (pData->iJHDRcolortype == 14)) && (iRawlen != 6))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+  else
+#endif /* MNG_INCLUDE_JNG */
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {
+    if (((pData->iColortype == 0) || (pData->iColortype == 4)) && (iRawlen != 2))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if (((pData->iColortype == 2) || (pData->iColortype == 6)) && (iRawlen != 6))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((pData->iColortype == 3) && (iRawlen != 1))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+  else
+  {
+    if (iRawlen != 6)                  /* global is always 16-bit RGB ! */
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    pData->bHasBKGD = MNG_TRUE;        /* indicate bKGD available */
+  else
+    pData->bHasglobalBKGD = (mng_bool)(iRawlen != 0);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if (!pImage)                         /* if no object dump it in obj 0 */
+    pImage = (mng_imagep)pData->pObjzero;
+
+  pBuf = pImage->pImgbuf;              /* address object buffer */
+
+#ifdef MNG_INCLUDE_JNG
+  if (pData->bHasJHDR)
+  {
+    pBuf->bHasBKGD = MNG_TRUE;         /* tell the object it's got bKGD now */
+
+    switch (pData->iJHDRcolortype)     /* store fields for future reference */
+    {
+      case  8 : ;                      /* gray */
+      case 12 : {                      /* graya */
+                  pBuf->iBKGDgray  = mng_get_uint16 (pRawdata);
+                  break;
+                }
+      case 10 : ;                      /* rgb */
+      case 14 : {                      /* rgba */
+                  pBuf->iBKGDred   = mng_get_uint16 (pRawdata);
+                  pBuf->iBKGDgreen = mng_get_uint16 (pRawdata+2);
+                  pBuf->iBKGDblue  = mng_get_uint16 (pRawdata+4);
+                  break;
+                }
+    }
+  }
+  else
+#endif /* MNG_INCLUDE_JNG */
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {
+    pBuf->bHasBKGD = MNG_TRUE;         /* tell the object it's got bKGD now */
+
+    switch (pData->iColortype)         /* store fields for future reference */
+    {
+      case 0 : ;                        /* gray */
+      case 4 : {                        /* graya */
+                 pBuf->iBKGDgray  = mng_get_uint16 (pRawdata);
+                 break;
+               }
+      case 2 : ;                        /* rgb */
+      case 6 : {                        /* rgba */
+                 pBuf->iBKGDred   = mng_get_uint16 (pRawdata);
+                 pBuf->iBKGDgreen = mng_get_uint16 (pRawdata+2);
+                 pBuf->iBKGDblue  = mng_get_uint16 (pRawdata+4);
+                 break;
+               }
+      case 3 : {                        /* indexed */
+                 pBuf->iBKGDindex = *pRawdata;
+                 break;
+               }
+    }
+  }
+  else                                 /* store as global */
+  {
+    if (iRawlen)
+    {
+      pData->iGlobalBKGDred   = mng_get_uint16 (pRawdata);
+      pData->iGlobalBKGDgreen = mng_get_uint16 (pRawdata+2);
+      pData->iGlobalBKGDblue  = mng_get_uint16 (pRawdata+4);
+    }
+
+    {                                  /* create an animation object */
+      mng_retcode iRetcode = mng_create_ani_bkgd (pData, pData->iGlobalBKGDred,
+                                                  pData->iGlobalBKGDgreen,
+                                                  pData->iGlobalBKGDblue);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_bkgdp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+    ((mng_bkgdp)*ppChunk)->iType  = pData->iColortype;
+
+    if (iRawlen)
+    {
+      switch (iRawlen)                 /* guess from length */
+      {
+        case 1 : {                     /* indexed */
+                   ((mng_bkgdp)*ppChunk)->iType  = 3;
+                   ((mng_bkgdp)*ppChunk)->iIndex = *pRawdata;
+                   break;
+                 }
+        case 2 : {                     /* gray */
+                   ((mng_bkgdp)*ppChunk)->iType  = 0;
+                   ((mng_bkgdp)*ppChunk)->iGray  = mng_get_uint16 (pRawdata);
+                   break;
+                 }
+        case 6 : {                     /* rgb */
+                   ((mng_bkgdp)*ppChunk)->iType  = 2;
+                   ((mng_bkgdp)*ppChunk)->iRed   = mng_get_uint16 (pRawdata);
+                   ((mng_bkgdp)*ppChunk)->iGreen = mng_get_uint16 (pRawdata+2);
+                   ((mng_bkgdp)*ppChunk)->iBlue  = mng_get_uint16 (pRawdata+4);
+                   break;
+                 }
+      }
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_pHYs
+READ_CHUNK (mng_read_phys)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PHYS, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIDAT) || (pData->bHasJDAT) || (pData->bHasJDAA))
+#else
+  if (pData->bHasIDAT)
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* it's 9 bytes or empty; no more, no less! */
+  if ((iRawlen != 9) && (iRawlen != 0))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_physp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)
+    {
+      ((mng_physp)*ppChunk)->iSizex = mng_get_uint32 (pRawdata);
+      ((mng_physp)*ppChunk)->iSizey = mng_get_uint32 (pRawdata+4);
+      ((mng_physp)*ppChunk)->iUnit  = *(pRawdata+8);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PHYS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_sBIT
+READ_CHUNK (mng_read_sbit)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SBIT, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasPLTE) || (pData->bHasIDAT) || (pData->bHasJDAT) || (pData->bHasJDAA))
+#else
+  if ((pData->bHasPLTE) || (pData->bHasIDAT))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen > 4)                     /* it just can't be bigger than that! */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_INCLUDE_JNG                 /* length checks */
+  if (pData->bHasJHDR)
+  {
+    if ((pData->iJHDRcolortype ==  8) && (iRawlen != 1))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((pData->iJHDRcolortype == 10) && (iRawlen != 3))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((pData->iJHDRcolortype == 12) && (iRawlen != 2))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((pData->iJHDRcolortype == 14) && (iRawlen != 4))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+  else
+#endif /* MNG_INCLUDE_JNG */
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+  {
+    if ((pData->iColortype == 0) && (iRawlen != 1))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((pData->iColortype == 2) && (iRawlen != 3))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((pData->iColortype == 3) && (iRawlen != 3))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((pData->iColortype == 4) && (iRawlen != 2))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((pData->iColortype == 6) && (iRawlen != 4))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+  else
+  {                                    /* global = empty or RGBA */
+    if ((iRawlen != 0) && (iRawlen != 4))
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+  }
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_sbitp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)
+    {
+#ifdef MNG_INCLUDE_JNG
+      if (pData->bHasJHDR)
+        ((mng_sbitp)*ppChunk)->iType = pData->iJHDRcolortype;
+      else
+#endif
+      if (pData->bHasIHDR)
+        ((mng_sbitp)*ppChunk)->iType = pData->iColortype;
+      else                             /* global ! */
+        ((mng_sbitp)*ppChunk)->iType = 6;
+
+      if (iRawlen > 0)
+        ((mng_sbitp)*ppChunk)->aBits [0] = *pRawdata;
+      if (iRawlen > 1)
+        ((mng_sbitp)*ppChunk)->aBits [1] = *(pRawdata+1);
+      if (iRawlen > 2)
+        ((mng_sbitp)*ppChunk)->aBits [2] = *(pRawdata+2);
+      if (iRawlen > 3)
+        ((mng_sbitp)*ppChunk)->aBits [3] = *(pRawdata+3);
+
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SBIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_sPLT
+READ_CHUNK (mng_read_splt)
+{
+  mng_uint8p pTemp;
+  mng_uint32 iNamelen;
+  mng_uint8  iSampledepth;
+  mng_uint32 iRemain;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SPLT, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (pData->bHasIDAT)
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen)
+  {
+    pTemp = find_null (pRawdata);      /* find null-separator */
+                                       /* not found inside input-data ? */
+    if ((pTemp - pRawdata) > (mng_int32)iRawlen)
+      MNG_ERROR (pData, MNG_NULLNOTFOUND);
+
+    iNamelen     = (mng_uint32)(pTemp - pRawdata);
+    iSampledepth = *(pTemp+1);
+    iRemain      = (iRawlen - 2 - iNamelen);
+
+    if ((iSampledepth != 1) && (iSampledepth != 2))
+      MNG_ERROR (pData, MNG_INVSAMPLEDEPTH);
+                                       /* check remaining length */
+    if ( ((iSampledepth == 1) && (iRemain %  6 != 0)) ||
+         ((iSampledepth == 2) && (iRemain % 10 != 0))    )
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  }
+  else
+  {
+    pTemp        = MNG_NULL;
+    iNamelen     = 0;
+    iSampledepth = 0;
+    iRemain      = 0;
+  }
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_spltp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)
+    {
+      ((mng_spltp)*ppChunk)->iNamesize    = iNamelen;
+      ((mng_spltp)*ppChunk)->iSampledepth = iSampledepth;
+
+      if (iSampledepth == 1)
+        ((mng_spltp)*ppChunk)->iEntrycount = iRemain / 6;
+      else
+        ((mng_spltp)*ppChunk)->iEntrycount = iRemain / 10;
+
+      if (iNamelen)
+      {
+        MNG_ALLOC (pData, ((mng_spltp)*ppChunk)->zName, iNamelen+1);
+        MNG_COPY (((mng_spltp)*ppChunk)->zName, pRawdata, iNamelen);
+      }
+
+      if (iRemain)
+      {
+        MNG_ALLOC (pData, ((mng_spltp)*ppChunk)->pEntries, iRemain);
+        MNG_COPY (((mng_spltp)*ppChunk)->pEntries, pTemp+2, iRemain);
+      }
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_hIST
+READ_CHUNK (mng_read_hist)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_HIST, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if ((!pData->bHasPLTE) || (pData->bHasIDAT))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* length oke ? */
+  if ( ((iRawlen & 0x01) != 0) || ((iRawlen >> 1) != pData->iPLTEcount) )
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {
+    mng_uint32 iX;
+                                       /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_histp)*ppChunk)->iEntrycount = iRawlen >> 1;
+
+    for (iX = 0; iX < (iRawlen >> 1); iX++)
+    {
+      ((mng_histp)*ppChunk)->aEntries [iX] = mng_get_uint16 (pRawdata);
+      pRawdata += 2;
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_HIST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_tIME
+READ_CHUNK (mng_read_time)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_TIME, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 7)                    /* length must be exactly 7 */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+/*  if (pData->fProcesstime) */            /* inform the application ? */
+/*  {
+
+    pData->fProcesstime ((mng_handle)pData, );
+  } */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_timep)*ppChunk)->iYear   = mng_get_uint16 (pRawdata);
+    ((mng_timep)*ppChunk)->iMonth  = *(pRawdata+2);
+    ((mng_timep)*ppChunk)->iDay    = *(pRawdata+3);
+    ((mng_timep)*ppChunk)->iHour   = *(pRawdata+4);
+    ((mng_timep)*ppChunk)->iMinute = *(pRawdata+5);
+    ((mng_timep)*ppChunk)->iSecond = *(pRawdata+6);
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_TIME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_mhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MHDR, MNG_LC_START);
+#endif
+
+  if (pData->eSigtype != mng_it_mng)   /* sequence checks */
+    MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+
+  if (pData->bHasheader)               /* can only be the first chunk! */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* correct length ? */
+#ifndef MNG_NO_OLD_VERSIONS
+  if ((iRawlen != 28) && (iRawlen != 12))
+#else
+  if ((iRawlen != 28))
+#endif
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pData->bHasMHDR       = MNG_TRUE;    /* oh boy, a real MNG */
+  pData->bHasheader     = MNG_TRUE;    /* we've got a header */
+  pData->eImagetype     = mng_it_mng;  /* fill header fields */
+  pData->iWidth         = mng_get_uint32 (pRawdata);
+  pData->iHeight        = mng_get_uint32 (pRawdata+4);
+  pData->iTicks         = mng_get_uint32 (pRawdata+8);
+
+#ifndef MNG_NO_OLD_VERSIONS
+  if (iRawlen == 28)                   /* proper MHDR ? */
+  {
+#endif
+    pData->iLayercount  = mng_get_uint32 (pRawdata+12);
+    pData->iFramecount  = mng_get_uint32 (pRawdata+16);
+    pData->iPlaytime    = mng_get_uint32 (pRawdata+20);
+    pData->iSimplicity  = mng_get_uint32 (pRawdata+24);
+
+#ifndef MNG_NO_OLD_VERSIONS
+    pData->bPreDraft48  = MNG_FALSE;
+  }
+  else                                 /* probably pre-draft48 then */
+  {
+    pData->iLayercount  = 0;
+    pData->iFramecount  = 0;
+    pData->iPlaytime    = 0;
+    pData->iSimplicity  = 0;
+
+    pData->bPreDraft48  = MNG_TRUE;
+  }
+#endif
+                                       /* predict alpha-depth */
+  if ((pData->iSimplicity & 0x00000001) == 0)
+#ifndef MNG_NO_16BIT_SUPPORT
+    pData->iAlphadepth = 16;           /* no indicators = assume the worst */
+#else
+    pData->iAlphadepth = 8;            /* anything else = assume the worst */
+#endif
+  else
+  if ((pData->iSimplicity & 0x00000008) == 0)
+    pData->iAlphadepth = 0;            /* no transparency at all */
+  else
+  if ((pData->iSimplicity & 0x00000140) == 0x00000040)
+    pData->iAlphadepth = 1;            /* no semi-transparency guaranteed */
+  else
+#ifndef MNG_NO_16BIT_SUPPORT
+    pData->iAlphadepth = 16;           /* anything else = assume the worst */
+#else
+    pData->iAlphadepth = 8;            /* anything else = assume the worst */
+#endif
+
+#ifdef MNG_INCLUDE_JNG                 /* can we handle the complexity ? */
+  if (pData->iSimplicity & 0x0000FC00)
+#else
+  if (pData->iSimplicity & 0x0000FC10)
+#endif
+    MNG_ERROR (pData, MNG_MNGTOOCOMPLEX);
+                                       /* fits on maximum canvas ? */
+  if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight))
+    MNG_WARNING (pData, MNG_IMAGETOOLARGE);
+
+  if (pData->fProcessheader)           /* inform the app ? */
+    if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight))
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+
+  pData->iImagelevel++;                /* one level deeper */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_mhdrp)*ppChunk)->iWidth      = pData->iWidth;
+    ((mng_mhdrp)*ppChunk)->iHeight     = pData->iHeight;
+    ((mng_mhdrp)*ppChunk)->iTicks      = pData->iTicks;
+    ((mng_mhdrp)*ppChunk)->iLayercount = pData->iLayercount;
+    ((mng_mhdrp)*ppChunk)->iFramecount = pData->iFramecount;
+    ((mng_mhdrp)*ppChunk)->iPlaytime   = pData->iPlaytime;
+    ((mng_mhdrp)*ppChunk)->iSimplicity = pData->iSimplicity;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_mend)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MEND, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen > 0)                     /* must not contain data! */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* do something */
+    mng_retcode iRetcode = mng_process_display_mend (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    if (!pData->iTotalframes)          /* save totals */
+      pData->iTotalframes   = pData->iFrameseq;
+    if (!pData->iTotallayers)
+      pData->iTotallayers   = pData->iLayerseq;
+    if (!pData->iTotalplaytime)
+      pData->iTotalplaytime = pData->iFrametime;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  pData->bHasMHDR = MNG_FALSE;         /* end of the line, bro! */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_LOOP
+READ_CHUNK (mng_read_loop)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_LOOP, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (!pData->bCacheplayback)          /* must store playback info to work!! */
+    MNG_ERROR (pData, MNG_LOOPWITHCACHEOFF);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen >= 5)                    /* length checks */
+  {
+    if (iRawlen >= 6)
+    {
+      if ((iRawlen - 6) % 4 != 0)
+        MNG_ERROR (pData, MNG_INVALIDLENGTH);
+    }
+  }
+  else
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_uint8   iLevel;
+    mng_uint32  iRepeat;
+    mng_uint8   iTermination = 0;
+    mng_uint32  iItermin     = 1;
+    mng_uint32  iItermax     = 0x7fffffffL;
+    mng_retcode iRetcode;
+
+    pData->bHasLOOP = MNG_TRUE;        /* indicate we're inside a loop */
+
+    iLevel = *pRawdata;                /* determine the fields for processing */
+
+#ifndef MNG_NO_OLD_VERSIONS
+    if (pData->bPreDraft48)
+    {
+      iTermination = *(pRawdata+1);
+
+      iRepeat = mng_get_uint32 (pRawdata+2);
+    }
+    else
+#endif
+      iRepeat = mng_get_uint32 (pRawdata+1);
+
+    if (iRawlen >= 6)
+    {
+#ifndef MNG_NO_OLD_VERSIONS
+      if (!pData->bPreDraft48)
+#endif
+        iTermination = *(pRawdata+5);
+
+      if (iRawlen >= 10)
+      {
+        iItermin = mng_get_uint32 (pRawdata+6);
+
+        if (iRawlen >= 14)
+        {
+          iItermax = mng_get_uint32 (pRawdata+10);
+
+          /* TODO: process signals */
+
+        }
+      }
+    }
+                                       /* create the LOOP ani-object */
+    iRetcode = mng_create_ani_loop (pData, iLevel, iRepeat, iTermination,
+                                           iItermin, iItermax, 0, 0);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* skip till matching ENDL if iteration=0 */
+    if ((!pData->bSkipping) && (iRepeat == 0))
+      pData->bSkipping = MNG_TRUE;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    if (iRawlen >= 5)                  /* store the fields */
+    {
+      ((mng_loopp)*ppChunk)->iLevel  = *pRawdata;
+
+#ifndef MNG_NO_OLD_VERSIONS
+      if (pData->bPreDraft48)
+      {
+        ((mng_loopp)*ppChunk)->iTermination = *(pRawdata+1);
+        ((mng_loopp)*ppChunk)->iRepeat = mng_get_uint32 (pRawdata+2);
+      }
+      else
+#endif
+      {
+        ((mng_loopp)*ppChunk)->iRepeat = mng_get_uint32 (pRawdata+1);
+      }
+
+      if (iRawlen >= 6)
+      {
+#ifndef MNG_NO_OLD_VERSIONS
+        if (!pData->bPreDraft48)
+#endif
+          ((mng_loopp)*ppChunk)->iTermination = *(pRawdata+5);
+
+        if (iRawlen >= 10)
+        {
+          ((mng_loopp)*ppChunk)->iItermin = mng_get_uint32 (pRawdata+6);
+
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+          if (iRawlen >= 14)
+          {
+            ((mng_loopp)*ppChunk)->iItermax = mng_get_uint32 (pRawdata+10);
+            ((mng_loopp)*ppChunk)->iCount   = (iRawlen - 14) / 4;
+
+            if (((mng_loopp)*ppChunk)->iCount)
+            {
+              MNG_ALLOC (pData, ((mng_loopp)*ppChunk)->pSignals,
+                                ((mng_loopp)*ppChunk)->iCount << 2);
+
+#ifndef MNG_BIGENDIAN_SUPPORTED
+              {
+                mng_uint32  iX;
+                mng_uint8p  pIn  = pRawdata + 14;
+                mng_uint32p pOut = (mng_uint32p)((mng_loopp)*ppChunk)->pSignals;
+
+                for (iX = 0; iX < ((mng_loopp)*ppChunk)->iCount; iX++)
+                {
+                  *pOut++ = mng_get_uint32 (pIn);
+                  pIn += 4;
+                }
+              }
+#else
+              MNG_COPY (((mng_loopp)*ppChunk)->pSignals, pRawdata + 14,
+                        ((mng_loopp)*ppChunk)->iCount << 2);
+#endif /* !MNG_BIGENDIAN_SUPPORTED */
+            }
+          }
+#endif
+        }
+      }
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_LOOP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_LOOP
+READ_CHUNK (mng_read_endl)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ENDL, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 1)                    /* length must be exactly 1 */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    if (pData->bHasLOOP)               /* are we really processing a loop ? */
+    {
+      mng_uint8 iLevel = *pRawdata;    /* get the nest level */
+                                       /* create an ENDL animation object */
+      mng_retcode iRetcode = mng_create_ani_endl (pData, iLevel);
+                                 
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+
+/*      {
+        mng_ani_endlp pENDL = (mng_ani_endlp)pData->pLastaniobj;
+
+        iRetcode = pENDL->sHeader.fProcess (pData, pENDL);
+
+        if (iRetcode)
+          return iRetcode;
+      } */
+    }
+    else
+      MNG_ERROR (pData, MNG_NOMATCHINGLOOP);
+      
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_endlp)*ppChunk)->iLevel = *pRawdata;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ENDL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_DEFI
+READ_CHUNK (mng_read_defi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DEFI, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* check the length */
+  if ((iRawlen != 2) && (iRawlen != 3) && (iRawlen != 4) &&
+      (iRawlen != 12) && (iRawlen != 28))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode;
+
+    pData->iDEFIobjectid       = mng_get_uint16 (pRawdata);
+
+    if (iRawlen > 2)
+    {
+      pData->bDEFIhasdonotshow = MNG_TRUE;
+      pData->iDEFIdonotshow    = *(pRawdata+2);
+    }
+    else
+    {
+      pData->bDEFIhasdonotshow = MNG_FALSE;
+      pData->iDEFIdonotshow    = 0;
+    }
+
+    if (iRawlen > 3)
+    {
+      pData->bDEFIhasconcrete  = MNG_TRUE;
+      pData->iDEFIconcrete     = *(pRawdata+3);
+    }
+    else
+    {
+      pData->bDEFIhasconcrete  = MNG_FALSE;
+      pData->iDEFIconcrete     = 0;
+    }
+
+    if (iRawlen > 4)
+    {
+      pData->bDEFIhasloca      = MNG_TRUE;
+      pData->iDEFIlocax        = mng_get_int32 (pRawdata+4);
+      pData->iDEFIlocay        = mng_get_int32 (pRawdata+8);
+    }
+    else
+    {
+      pData->bDEFIhasloca      = MNG_FALSE;
+      pData->iDEFIlocax        = 0;
+      pData->iDEFIlocay        = 0;
+    }
+
+    if (iRawlen > 12)
+    {
+      pData->bDEFIhasclip      = MNG_TRUE;
+      pData->iDEFIclipl        = mng_get_int32 (pRawdata+12);
+      pData->iDEFIclipr        = mng_get_int32 (pRawdata+16);
+      pData->iDEFIclipt        = mng_get_int32 (pRawdata+20);
+      pData->iDEFIclipb        = mng_get_int32 (pRawdata+24);
+    }
+    else
+    {
+      pData->bDEFIhasclip      = MNG_FALSE;
+      pData->iDEFIclipl        = 0;
+      pData->iDEFIclipr        = 0;
+      pData->iDEFIclipt        = 0;
+      pData->iDEFIclipb        = 0;
+    }
+                                       /* create an animation object */
+    iRetcode = mng_create_ani_defi (pData);
+                   
+    if (!iRetcode)                     /* do display processing */
+      iRetcode = mng_process_display_defi (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_defip)*ppChunk)->iObjectid       = mng_get_uint16 (pRawdata);
+
+    if (iRawlen > 2)
+    {
+      ((mng_defip)*ppChunk)->bHasdonotshow = MNG_TRUE;
+      ((mng_defip)*ppChunk)->iDonotshow    = *(pRawdata+2);
+    }
+    else
+      ((mng_defip)*ppChunk)->bHasdonotshow = MNG_FALSE;
+
+    if (iRawlen > 3)
+    {
+      ((mng_defip)*ppChunk)->bHasconcrete  = MNG_TRUE;
+      ((mng_defip)*ppChunk)->iConcrete     = *(pRawdata+3);
+    }
+    else
+      ((mng_defip)*ppChunk)->bHasconcrete  = MNG_FALSE;
+
+    if (iRawlen > 4)
+    {
+      ((mng_defip)*ppChunk)->bHasloca      = MNG_TRUE;
+      ((mng_defip)*ppChunk)->iXlocation    = mng_get_int32 (pRawdata+4);
+      ((mng_defip)*ppChunk)->iYlocation    = mng_get_int32 (pRawdata+8);
+    }
+    else
+      ((mng_defip)*ppChunk)->bHasloca      = MNG_FALSE;
+
+    if (iRawlen > 12)
+    {
+      ((mng_defip)*ppChunk)->bHasclip      = MNG_TRUE;
+      ((mng_defip)*ppChunk)->iLeftcb       = mng_get_int32 (pRawdata+12);
+      ((mng_defip)*ppChunk)->iRightcb      = mng_get_int32 (pRawdata+16);
+      ((mng_defip)*ppChunk)->iTopcb        = mng_get_int32 (pRawdata+20);
+      ((mng_defip)*ppChunk)->iBottomcb     = mng_get_int32 (pRawdata+24);
+    }
+    else
+      ((mng_defip)*ppChunk)->bHasclip      = MNG_FALSE;
+
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_BASI
+READ_CHUNK (mng_read_basi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_BASI, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* check the length */
+  if ((iRawlen != 13) && (iRawlen != 19) && (iRawlen != 21) && (iRawlen != 22))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pData->bHasBASI     = MNG_TRUE;      /* inside a BASI-IEND block now */
+                                       /* store interesting fields */
+  pData->iDatawidth   = mng_get_uint32 (pRawdata);
+  pData->iDataheight  = mng_get_uint32 (pRawdata+4);
+  pData->iBitdepth    = *(pRawdata+8);
+  pData->iColortype   = *(pRawdata+9);
+  pData->iCompression = *(pRawdata+10);
+  pData->iFilter      = *(pRawdata+11);
+  pData->iInterlace   = *(pRawdata+12);
+
+
+#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT)
+  pData->iPNGmult = 1;
+  pData->iPNGdepth = pData->iBitdepth;
+#endif
+
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+  if (pData->iBitdepth < 8)
+    pData->iBitdepth = 8;
+#endif
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (pData->iBitdepth > 8)
+    {
+      pData->iBitdepth = 8;
+      pData->iPNGmult = 2;
+    }
+#endif
+
+  if ((pData->iBitdepth !=  8)      /* parameter validity checks */
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+      && (pData->iBitdepth !=  1) &&
+      (pData->iBitdepth !=  2) &&
+      (pData->iBitdepth !=  4)
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+      && (pData->iBitdepth != 16)
+#endif
+      )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if ((pData->iColortype != MNG_COLORTYPE_GRAY   ) &&
+      (pData->iColortype != MNG_COLORTYPE_RGB    ) &&
+      (pData->iColortype != MNG_COLORTYPE_INDEXED) &&
+      (pData->iColortype != MNG_COLORTYPE_GRAYA  ) &&
+      (pData->iColortype != MNG_COLORTYPE_RGBA   )    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  if ((pData->iColortype == MNG_COLORTYPE_INDEXED) && (pData->iBitdepth > 8))
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if (((pData->iColortype == MNG_COLORTYPE_RGB    ) ||
+       (pData->iColortype == MNG_COLORTYPE_GRAYA  ) ||
+       (pData->iColortype == MNG_COLORTYPE_RGBA   )    ) &&
+      (pData->iBitdepth < 8                            )    )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if (pData->iCompression != MNG_COMPRESSION_DEFLATE)
+    MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+#if defined(FILTER192) || defined(FILTER193)
+  if ((pData->iFilter != MNG_FILTER_ADAPTIVE ) &&
+#if defined(FILTER192) && defined(FILTER193)
+      (pData->iFilter != MNG_FILTER_DIFFERING) &&
+      (pData->iFilter != MNG_FILTER_NOFILTER )    )
+#else
+#ifdef FILTER192
+      (pData->iFilter != MNG_FILTER_DIFFERING)    )
+#else
+      (pData->iFilter != MNG_FILTER_NOFILTER )    )
+#endif
+#endif
+    MNG_ERROR (pData, MNG_INVALIDFILTER);
+#else
+  if (pData->iFilter)
+    MNG_ERROR (pData, MNG_INVALIDFILTER);
+#endif
+
+  if ((pData->iInterlace != MNG_INTERLACE_NONE ) &&
+      (pData->iInterlace != MNG_INTERLACE_ADAM7)    )
+    MNG_ERROR (pData, MNG_INVALIDINTERLACE);
+
+  pData->iImagelevel++;                /* one level deeper */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_uint16  iRed      = 0;
+    mng_uint16  iGreen    = 0;
+    mng_uint16  iBlue     = 0;
+    mng_bool    bHasalpha = MNG_FALSE;
+    mng_uint16  iAlpha    = 0xFFFF;
+    mng_uint8   iViewable = 0;
+    mng_retcode iRetcode;
+
+    if (iRawlen > 13)                  /* get remaining fields, if any */
+    {
+      iRed      = mng_get_uint16 (pRawdata+13);
+      iGreen    = mng_get_uint16 (pRawdata+15);
+      iBlue     = mng_get_uint16 (pRawdata+17);
+    }
+
+    if (iRawlen > 19)
+    {
+      bHasalpha = MNG_TRUE;
+      iAlpha    = mng_get_uint16 (pRawdata+19);
+    }
+
+    if (iRawlen > 21)
+      iViewable = *(pRawdata+21);
+                                       /* create an animation object */
+    iRetcode = mng_create_ani_basi (pData, iRed, iGreen, iBlue,
+                                    bHasalpha, iAlpha, iViewable);
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_basi (pData, iRed, iGreen, iBlue,
+                                           bHasalpha, iAlpha, iViewable); */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_basip)*ppChunk)->iWidth       = mng_get_uint32 (pRawdata);
+    ((mng_basip)*ppChunk)->iHeight      = mng_get_uint32 (pRawdata+4);
+#ifdef MNG_NO_16BIT_SUPPORT
+    if (*(pRawdata+8) > 8)
+      ((mng_basip)*ppChunk)->iBitdepth    = 8;
+    else
+#endif
+      ((mng_basip)*ppChunk)->iBitdepth    = *(pRawdata+8);
+    ((mng_basip)*ppChunk)->iColortype   = *(pRawdata+9);
+    ((mng_basip)*ppChunk)->iCompression = *(pRawdata+10);
+    ((mng_basip)*ppChunk)->iFilter      = *(pRawdata+11);
+    ((mng_basip)*ppChunk)->iInterlace   = *(pRawdata+12);
+
+    if (iRawlen > 13)
+    {
+      ((mng_basip)*ppChunk)->iRed       = mng_get_uint16 (pRawdata+13);
+      ((mng_basip)*ppChunk)->iGreen     = mng_get_uint16 (pRawdata+15);
+      ((mng_basip)*ppChunk)->iBlue      = mng_get_uint16 (pRawdata+17);
+    }
+
+    if (iRawlen > 19)
+      ((mng_basip)*ppChunk)->iAlpha     = mng_get_uint16 (pRawdata+19);
+
+    if (iRawlen > 21)
+      ((mng_basip)*ppChunk)->iViewable  = *(pRawdata+21);
+
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_BASI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_CLON
+READ_CHUNK (mng_read_clon)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CLON, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* check the length */
+  if ((iRawlen != 4) && (iRawlen != 5) && (iRawlen != 6) &&
+      (iRawlen != 7) && (iRawlen != 16))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_uint16  iSourceid, iCloneid;
+    mng_uint8   iClonetype    = 0;
+    mng_bool    bHasdonotshow = MNG_FALSE;
+    mng_uint8   iDonotshow    = 0;
+    mng_uint8   iConcrete     = 0;
+    mng_bool    bHasloca      = MNG_FALSE;
+    mng_uint8   iLocationtype = 0;
+    mng_int32   iLocationx    = 0;
+    mng_int32   iLocationy    = 0;
+    mng_retcode iRetcode;
+
+    iSourceid       = mng_get_uint16 (pRawdata);
+    iCloneid        = mng_get_uint16 (pRawdata+2);
+
+    if (iRawlen > 4)
+      iClonetype    = *(pRawdata+4);
+
+    if (iRawlen > 5)
+    {
+      bHasdonotshow = MNG_TRUE;
+      iDonotshow    = *(pRawdata+5);
+    }
+
+    if (iRawlen > 6)
+      iConcrete     = *(pRawdata+6);
+
+    if (iRawlen > 7)
+    {
+      bHasloca      = MNG_TRUE;
+      iLocationtype = *(pRawdata+7);
+      iLocationx    = mng_get_int32 (pRawdata+8);
+      iLocationy    = mng_get_int32 (pRawdata+12);
+    }
+
+    iRetcode = mng_create_ani_clon (pData, iSourceid, iCloneid, iClonetype,
+                                    bHasdonotshow, iDonotshow, iConcrete,
+                                    bHasloca, iLocationtype, iLocationx, iLocationy);
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_clon (pData, iSourceid, iCloneid, iClonetype,
+                                           bHasdonotshow, iDonotshow, iConcrete,
+                                           bHasloca, iLocationtype, iLocationx,
+                                           iLocationy); */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_clonp)*ppChunk)->iSourceid       = mng_get_uint16 (pRawdata);
+    ((mng_clonp)*ppChunk)->iCloneid        = mng_get_uint16 (pRawdata+2);
+
+    if (iRawlen > 4)
+      ((mng_clonp)*ppChunk)->iClonetype    = *(pRawdata+4);
+
+    if (iRawlen > 5)
+      ((mng_clonp)*ppChunk)->iDonotshow    = *(pRawdata+5);
+
+    if (iRawlen > 6)
+      ((mng_clonp)*ppChunk)->iConcrete     = *(pRawdata+6);
+
+    if (iRawlen > 7)
+    {
+      ((mng_clonp)*ppChunk)->bHasloca      = MNG_TRUE;
+      ((mng_clonp)*ppChunk)->iLocationtype = *(pRawdata+7);
+      ((mng_clonp)*ppChunk)->iLocationx    = mng_get_int32 (pRawdata+8);
+      ((mng_clonp)*ppChunk)->iLocationy    = mng_get_int32 (pRawdata+12);
+    }
+    else
+    {
+      ((mng_clonp)*ppChunk)->bHasloca      = MNG_FALSE;
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CLON, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_PAST
+READ_CHUNK (mng_read_past)
+{
+#if defined(MNG_STORE_CHUNKS) || defined(MNG_SUPPORT_DISPLAY)
+  mng_retcode      iRetcode;
+  mng_uint16       iTargetid;
+  mng_uint8        iTargettype;
+  mng_int32        iTargetx;
+  mng_int32        iTargety;
+  mng_uint32       iCount;
+  mng_uint32       iSize;
+  mng_ptr          pSources;
+  mng_uint32       iX;
+  mng_past_sourcep pSource;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PAST, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+                                       /* check the length */
+  if ((iRawlen < 41) || (((iRawlen - 11) % 30) != 0))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#if defined(MNG_STORE_CHUNKS) || defined(MNG_SUPPORT_DISPLAY)
+  iTargetid   = mng_get_uint16 (pRawdata);
+  iTargettype = *(pRawdata+2);
+  iTargetx    = mng_get_int32  (pRawdata+3);
+  iTargety    = mng_get_int32  (pRawdata+7);
+  iCount      = ((iRawlen - 11) / 30); /* how many entries again? */
+  iSize       = iCount * sizeof (mng_past_source);
+
+  pRawdata += 11;
+                                       /* get a buffer for all the source blocks */
+  MNG_ALLOC (pData, pSources, iSize);
+
+  pSource = (mng_past_sourcep)pSources;
+
+  for (iX = 0; iX < iCount; iX++)      /* now copy the source blocks */
+  {
+    pSource->iSourceid     = mng_get_uint16 (pRawdata);
+    pSource->iComposition  = *(pRawdata+2);
+    pSource->iOrientation  = *(pRawdata+3);
+    pSource->iOffsettype   = *(pRawdata+4);
+    pSource->iOffsetx      = mng_get_int32 (pRawdata+5);
+    pSource->iOffsety      = mng_get_int32 (pRawdata+9);
+    pSource->iBoundarytype = *(pRawdata+13);
+    pSource->iBoundaryl    = mng_get_int32 (pRawdata+14);
+    pSource->iBoundaryr    = mng_get_int32 (pRawdata+18);
+    pSource->iBoundaryt    = mng_get_int32 (pRawdata+22);
+    pSource->iBoundaryb    = mng_get_int32 (pRawdata+26);
+
+    pSource++;
+    pRawdata += 30;
+  }
+#endif
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* create playback object */
+    iRetcode = mng_create_ani_past (pData, iTargetid, iTargettype, iTargetx,
+                                    iTargety, iCount, pSources);
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_past (pData, iTargetid, iTargettype, iTargetx,
+                                           iTargety, iCount, pSources); */
+
+    if (iRetcode)                      /* on error bail out */
+    {
+      MNG_FREEX (pData, pSources, iSize);
+      return iRetcode;
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+    {
+      MNG_FREEX (pData, pSources, iSize);
+      return iRetcode;
+    }
+                                       /* store the fields */
+    ((mng_pastp)*ppChunk)->iDestid     = iTargetid;
+    ((mng_pastp)*ppChunk)->iTargettype = iTargettype;
+    ((mng_pastp)*ppChunk)->iTargetx    = iTargetx;
+    ((mng_pastp)*ppChunk)->iTargety    = iTargety;
+    ((mng_pastp)*ppChunk)->iCount      = iCount;
+                                       /* get a buffer & copy the source blocks */
+    MNG_ALLOC (pData, ((mng_pastp)*ppChunk)->pSources, iSize);
+    MNG_COPY (((mng_pastp)*ppChunk)->pSources, pSources, iSize);
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#if defined(MNG_STORE_CHUNKS) || defined(MNG_SUPPORT_DISPLAY)
+                                       /* free the source block buffer */
+  MNG_FREEX (pData, pSources, iSize);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PAST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_DISC
+READ_CHUNK (mng_read_disc)
+{
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS)
+  mng_uint32  iCount;
+  mng_uint16p pIds = MNG_NULL;
+  mng_retcode iRetcode;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DISC, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if ((iRawlen % 2) != 0)              /* check the length */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS)
+  iCount = (iRawlen / sizeof (mng_uint16));
+
+  if (iCount)
+  {
+    MNG_ALLOC (pData, pIds, iRawlen);
+
+#ifndef MNG_BIGENDIAN_SUPPORTED
+    {
+      mng_uint32  iX;
+      mng_uint8p  pIn  = pRawdata;
+      mng_uint16p pOut = pIds;
+
+      for (iX = 0; iX < iCount; iX++)
+      {
+        *pOut++ = mng_get_uint16 (pIn);
+        pIn += 2;
+      }
+    }
+#else
+    MNG_COPY (pIds, pRawdata, iRawlen);
+#endif /* !MNG_BIGENDIAN_SUPPORTED */
+  }
+#endif
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* create playback object */
+    iRetcode = mng_create_ani_disc (pData, iCount, pIds);
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_disc (pData, iCount, pIds); */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_discp)*ppChunk)->iCount = iCount;
+
+    if (iRawlen)
+    {
+      MNG_ALLOC (pData, ((mng_discp)*ppChunk)->pObjectids, iRawlen);
+      MNG_COPY (((mng_discp)*ppChunk)->pObjectids, pIds, iRawlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS)
+  if (iRawlen)
+    MNG_FREEX (pData, pIds, iRawlen);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DISC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_BACK
+READ_CHUNK (mng_read_back)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_BACK, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* check the length */
+  if ((iRawlen != 6) && (iRawlen != 7) && (iRawlen != 9) && (iRawlen != 10))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode;
+                                       /* retrieve the fields */
+    pData->bHasBACK         = MNG_TRUE;
+    pData->iBACKred         = mng_get_uint16 (pRawdata);
+    pData->iBACKgreen       = mng_get_uint16 (pRawdata+2);
+    pData->iBACKblue        = mng_get_uint16 (pRawdata+4);
+
+    if (iRawlen > 6)
+      pData->iBACKmandatory = *(pRawdata+6);
+    else
+      pData->iBACKmandatory = 0;
+
+    if (iRawlen > 7)
+      pData->iBACKimageid   = mng_get_uint16 (pRawdata+7);
+    else
+      pData->iBACKimageid   = 0;
+
+    if (iRawlen > 9)
+      pData->iBACKtile      = *(pRawdata+9);
+    else
+      pData->iBACKtile      = 0;
+
+    iRetcode = mng_create_ani_back (pData, pData->iBACKred, pData->iBACKgreen,
+                                    pData->iBACKblue, pData->iBACKmandatory,
+                                    pData->iBACKimageid, pData->iBACKtile);
+
+    if (iRetcode)                    /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_backp)*ppChunk)->iRed         = mng_get_uint16 (pRawdata);
+    ((mng_backp)*ppChunk)->iGreen       = mng_get_uint16 (pRawdata+2);
+    ((mng_backp)*ppChunk)->iBlue        = mng_get_uint16 (pRawdata+4);
+
+    if (iRawlen > 6)
+      ((mng_backp)*ppChunk)->iMandatory = *(pRawdata+6);
+
+    if (iRawlen > 7)
+      ((mng_backp)*ppChunk)->iImageid   = mng_get_uint16 (pRawdata+7);
+
+    if (iRawlen > 9)
+      ((mng_backp)*ppChunk)->iTile      = *(pRawdata+9);
+
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_BACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_FRAM
+READ_CHUNK (mng_read_fram)
+{
+  mng_uint8p pTemp;
+#ifdef MNG_STORE_CHUNKS
+  mng_uint32 iNamelen;
+#endif
+  mng_uint32 iRemain;
+  mng_uint32 iRequired = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_FRAM, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen <= 1)                    /* only framing-mode ? */
+  {
+#ifdef MNG_STORE_CHUNKS
+    iNamelen = 0;                      /* indicate so */
+#endif
+    iRemain  = 0;
+    pTemp    = MNG_NULL;
+  }
+  else
+  {
+    pTemp = find_null (pRawdata+1);    /* find null-separator */
+                                       /* not found inside input-data ? */
+    if ((pTemp - pRawdata) > (mng_int32)iRawlen)
+      pTemp  = pRawdata + iRawlen;     /* than remainder is name */
+
+#ifdef MNG_STORE_CHUNKS
+    iNamelen = (mng_uint32)((pTemp - pRawdata) - 1);
+#endif
+    iRemain  = (mng_uint32)(iRawlen - (pTemp - pRawdata));
+
+    if (iRemain)                       /* if there is remaining data it's less 1 byte */
+      iRemain--;
+
+    if ((iRemain) && (iRemain < 4))    /* remains must be empty or at least 4 bytes */
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if (iRemain)
+    {
+      iRequired = 4;                   /* calculate and check required remaining length */
+
+      if (*(pTemp+1)) { iRequired += 4; }
+      if (*(pTemp+2)) { iRequired += 4; }
+      if (*(pTemp+3)) { iRequired += 17; }
+
+      if (*(pTemp+4))
+      {
+        if ((iRemain - iRequired) % 4 != 0)
+          MNG_ERROR (pData, MNG_INVALIDLENGTH);
+      }
+      else
+      {
+        if (iRemain != iRequired)
+          MNG_ERROR (pData, MNG_INVALIDLENGTH);
+      }
+    }
+  }
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_uint8p  pWork           = pTemp;
+    mng_uint8   iFramemode      = 0;
+    mng_uint8   iChangedelay    = 0;
+    mng_uint32  iDelay          = 0;
+    mng_uint8   iChangetimeout  = 0;
+    mng_uint32  iTimeout        = 0;
+    mng_uint8   iChangeclipping = 0;
+    mng_uint8   iCliptype       = 0;
+    mng_int32   iClipl          = 0;
+    mng_int32   iClipr          = 0;
+    mng_int32   iClipt          = 0;
+    mng_int32   iClipb          = 0;
+    mng_retcode iRetcode;
+
+    if (iRawlen)                       /* any data specified ? */
+    {
+      if (*(pRawdata))                 /* save the new framing mode ? */
+      {
+        iFramemode = *(pRawdata);
+
+#ifndef MNG_NO_OLD_VERSIONS
+        if (pData->bPreDraft48)        /* old style input-stream ? */
+        {
+          switch (iFramemode)
+          {
+            case  0: { break; }
+            case  1: { iFramemode = 3; break; }
+            case  2: { iFramemode = 4; break; }
+            case  3: { iFramemode = 1; break; }
+            case  4: { iFramemode = 1; break; }
+            case  5: { iFramemode = 2; break; }
+            default: { iFramemode = 1; break; }
+          }
+        }
+#endif
+      }
+
+      if (iRemain)
+      {
+        iChangedelay    = *(pWork+1);
+        iChangetimeout  = *(pWork+2);
+        iChangeclipping = *(pWork+3);
+        pWork += 5;
+
+        if (iChangedelay)              /* delay changed ? */
+        {
+          iDelay = mng_get_uint32 (pWork);
+          pWork += 4;
+        }
+
+        if (iChangetimeout)            /* timeout changed ? */
+        {
+          iTimeout = mng_get_uint32 (pWork);
+          pWork += 4;
+        }
+
+        if (iChangeclipping)           /* clipping changed ? */
+        {
+          iCliptype = *pWork;
+          iClipl    = mng_get_int32 (pWork+1);
+          iClipr    = mng_get_int32 (pWork+5);
+          iClipt    = mng_get_int32 (pWork+9);
+          iClipb    = mng_get_int32 (pWork+13);
+        }
+      }
+    }
+
+    iRetcode = mng_create_ani_fram (pData, iFramemode, iChangedelay, iDelay,
+                                    iChangetimeout, iTimeout,
+                                    iChangeclipping, iCliptype,
+                                    iClipl, iClipr, iClipt, iClipb);
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_fram (pData, iFramemode, iChangedelay, iDelay,
+                                           iChangetimeout, iTimeout,
+                                           iChangeclipping, iCliptype,
+                                           iClipl, iClipr, iClipt, iClipb); */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_framp)*ppChunk)->bEmpty              = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)
+    {
+      mng_uint8 iFramemode = *(pRawdata);
+
+#ifndef MNG_NO_OLD_VERSIONS
+      if (pData->bPreDraft48)          /* old style input-stream ? */
+      {
+        switch (iFramemode)
+        {
+          case  1: { iFramemode = 3; break; }
+          case  2: { iFramemode = 4; break; }
+          case  3: { iFramemode = 5; break; }    /* TODO: provision for mode=5 ??? */
+          case  4: { iFramemode = 1; break; }
+          case  5: { iFramemode = 2; break; }
+          default: { iFramemode = 1; break; }
+        }
+      }
+#endif
+
+      ((mng_framp)*ppChunk)->iMode             = iFramemode;
+      ((mng_framp)*ppChunk)->iNamesize         = iNamelen;
+
+      if (iNamelen)
+      {
+        MNG_ALLOC (pData, ((mng_framp)*ppChunk)->zName, iNamelen+1);
+        MNG_COPY (((mng_framp)*ppChunk)->zName, pRawdata+1, iNamelen);
+      }
+
+      if (iRemain)
+      {
+        ((mng_framp)*ppChunk)->iChangedelay    = *(pTemp+1);
+        ((mng_framp)*ppChunk)->iChangetimeout  = *(pTemp+2);
+        ((mng_framp)*ppChunk)->iChangeclipping = *(pTemp+3);
+        ((mng_framp)*ppChunk)->iChangesyncid   = *(pTemp+4);
+
+        pTemp += 5;
+
+        if (((mng_framp)*ppChunk)->iChangedelay)
+        {
+          ((mng_framp)*ppChunk)->iDelay        = mng_get_uint32 (pTemp);
+          pTemp += 4;
+        }
+
+        if (((mng_framp)*ppChunk)->iChangetimeout)
+        {
+          ((mng_framp)*ppChunk)->iTimeout      = mng_get_uint32 (pTemp);
+          pTemp += 4;
+        }
+
+        if (((mng_framp)*ppChunk)->iChangeclipping)
+        {
+          ((mng_framp)*ppChunk)->iBoundarytype = *pTemp;
+          ((mng_framp)*ppChunk)->iBoundaryl    = mng_get_int32 (pTemp+1);
+          ((mng_framp)*ppChunk)->iBoundaryr    = mng_get_int32 (pTemp+5);
+          ((mng_framp)*ppChunk)->iBoundaryt    = mng_get_int32 (pTemp+9);
+          ((mng_framp)*ppChunk)->iBoundaryb    = mng_get_int32 (pTemp+13);
+          pTemp += 17;
+        }
+
+        if (((mng_framp)*ppChunk)->iChangesyncid)
+        {
+          ((mng_framp)*ppChunk)->iCount        = (iRemain - iRequired) / 4;
+
+          if (((mng_framp)*ppChunk)->iCount)
+          {
+            MNG_ALLOC (pData, ((mng_framp)*ppChunk)->pSyncids,
+                              ((mng_framp)*ppChunk)->iCount * 4);
+
+#ifndef MNG_BIGENDIAN_SUPPORTED
+            {
+              mng_uint32 iX;
+              mng_uint32p pOut = ((mng_framp)*ppChunk)->pSyncids;
+
+              for (iX = 0; iX < ((mng_framp)*ppChunk)->iCount; iX++)
+              {
+                *pOut++ = mng_get_uint32 (pTemp);
+                pTemp += 4;
+              }
+            }
+#else
+            MNG_COPY (((mng_framp)*ppChunk)->pSyncids, pTemp,
+                      ((mng_framp)*ppChunk)->iCount * 4);
+#endif /* !MNG_BIGENDIAN_SUPPORTED */
+          }
+        }
+      }
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_FRAM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_MOVE
+READ_CHUNK (mng_read_move)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MOVE, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 13)                   /* check the length */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode;
+                                       /* create a MOVE animation object */
+    iRetcode = mng_create_ani_move (pData, mng_get_uint16 (pRawdata),
+                                           mng_get_uint16 (pRawdata+2),
+                                           *(pRawdata+4),
+                                           mng_get_int32 (pRawdata+5),
+                                           mng_get_int32 (pRawdata+9));
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_move (pData,
+                                           mng_get_uint16 (pRawdata),
+                                           mng_get_uint16 (pRawdata+2),
+                                           *(pRawdata+4),
+                                           mng_get_int32 (pRawdata+5),
+                                           mng_get_int32 (pRawdata+9)); */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_movep)*ppChunk)->iFirstid  = mng_get_uint16 (pRawdata);
+    ((mng_movep)*ppChunk)->iLastid   = mng_get_uint16 (pRawdata+2);
+    ((mng_movep)*ppChunk)->iMovetype = *(pRawdata+4);
+    ((mng_movep)*ppChunk)->iMovex    = mng_get_int32 (pRawdata+5);
+    ((mng_movep)*ppChunk)->iMovey    = mng_get_int32 (pRawdata+9);
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MOVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_CLIP
+READ_CHUNK (mng_read_clip)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CLIP, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 21)                   /* check the length */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode;
+                                       /* create a CLIP animation object */
+    iRetcode = mng_create_ani_clip (pData, mng_get_uint16 (pRawdata),
+                                           mng_get_uint16 (pRawdata+2),
+                                           *(pRawdata+4),
+                                           mng_get_int32 (pRawdata+5),
+                                           mng_get_int32 (pRawdata+9),
+                                           mng_get_int32 (pRawdata+13),
+                                           mng_get_int32 (pRawdata+17));
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_clip (pData,
+                                           mng_get_uint16 (pRawdata),
+                                           mng_get_uint16 (pRawdata+2),
+                                           *(pRawdata+4),
+                                           mng_get_int32 (pRawdata+5),
+                                           mng_get_int32 (pRawdata+9),
+                                           mng_get_int32 (pRawdata+13),
+                                           mng_get_int32 (pRawdata+17)); */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_clipp)*ppChunk)->iFirstid  = mng_get_uint16 (pRawdata);
+    ((mng_clipp)*ppChunk)->iLastid   = mng_get_uint16 (pRawdata+2);
+    ((mng_clipp)*ppChunk)->iCliptype = *(pRawdata+4);
+    ((mng_clipp)*ppChunk)->iClipl    = mng_get_int32 (pRawdata+5);
+    ((mng_clipp)*ppChunk)->iClipr    = mng_get_int32 (pRawdata+9);
+    ((mng_clipp)*ppChunk)->iClipt    = mng_get_int32 (pRawdata+13);
+    ((mng_clipp)*ppChunk)->iClipb    = mng_get_int32 (pRawdata+17);
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CLIP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_SHOW
+READ_CHUNK (mng_read_show)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SHOW, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* check the length */
+  if ((iRawlen != 0) && (iRawlen != 2) && (iRawlen != 4) && (iRawlen != 5))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode;
+
+    if (iRawlen)                       /* determine parameters if any */
+    {
+      pData->iSHOWfromid = mng_get_uint16 (pRawdata);
+
+      if (iRawlen > 2)
+        pData->iSHOWtoid = mng_get_uint16 (pRawdata+2);
+      else
+        pData->iSHOWtoid = pData->iSHOWfromid;
+
+      if (iRawlen > 4)
+        pData->iSHOWmode = *(pRawdata+4);
+      else
+        pData->iSHOWmode = 0;
+    }
+    else                               /* use defaults then */
+    {
+      pData->iSHOWmode   = 2;
+      pData->iSHOWfromid = 1;
+      pData->iSHOWtoid   = 65535;
+    }
+                                       /* create a SHOW animation object */
+    iRetcode = mng_create_ani_show (pData, pData->iSHOWfromid,
+                                    pData->iSHOWtoid, pData->iSHOWmode);
+
+    if (!iRetcode)
+      iRetcode = mng_process_display_show (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_showp)*ppChunk)->bEmpty      = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)
+    {
+      ((mng_showp)*ppChunk)->iFirstid  = mng_get_uint16 (pRawdata);
+
+      if (iRawlen > 2)
+        ((mng_showp)*ppChunk)->iLastid = mng_get_uint16 (pRawdata+2);
+      else
+        ((mng_showp)*ppChunk)->iLastid = ((mng_showp)*ppChunk)->iFirstid;
+
+      if (iRawlen > 4)
+        ((mng_showp)*ppChunk)->iMode   = *(pRawdata+4);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SHOW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_TERM
+READ_CHUNK (mng_read_term)
+{
+  mng_uint8   iTermaction;
+  mng_uint8   iIteraction = 0;
+  mng_uint32  iDelay      = 0;
+  mng_uint32  iItermax    = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_TERM, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+                                       /* should be behind MHDR or SAVE !! */
+  if ((!pData->bHasSAVE) && (pData->iChunkseq > 2))
+  {
+    pData->bMisplacedTERM = MNG_TRUE;  /* indicate we found a misplaced TERM */
+                                       /* and send a warning signal!!! */
+    MNG_WARNING (pData, MNG_SEQUENCEERROR);
+  }
+
+  if (pData->bHasLOOP)                 /* no way, jose! */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (pData->bHasTERM)                 /* only 1 allowed! */
+    MNG_ERROR (pData, MNG_MULTIPLEERROR);
+                                       /* check the length */
+  if ((iRawlen != 1) && (iRawlen != 10))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pData->bHasTERM = MNG_TRUE;
+
+  iTermaction = *pRawdata;             /* get the fields */
+
+  if (iRawlen > 1)
+  {
+    iIteraction = *(pRawdata+1);
+    iDelay      = mng_get_uint32 (pRawdata+2);
+    iItermax    = mng_get_uint32 (pRawdata+6);
+  }
+
+  if (pData->fProcessterm)             /* inform the app ? */
+    if (!pData->fProcessterm (((mng_handle)pData), iTermaction, iIteraction,
+                                                   iDelay, iItermax))
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* create the TERM ani-object */
+    mng_retcode iRetcode = mng_create_ani_term (pData, iTermaction, iIteraction,
+                                                iDelay, iItermax);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* save for future reference */
+    pData->pTermaniobj = pData->pLastaniobj;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_termp)*ppChunk)->iTermaction = iTermaction;
+    ((mng_termp)*ppChunk)->iIteraction = iIteraction;
+    ((mng_termp)*ppChunk)->iDelay      = iDelay;
+    ((mng_termp)*ppChunk)->iItermax    = iItermax;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_TERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_SAVE
+READ_CHUNK (mng_read_save)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SAVE, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) || (pData->bHasSAVE))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  pData->bHasSAVE = MNG_TRUE;
+
+  if (pData->fProcesssave)             /* inform the application ? */
+  {
+    mng_bool bOke = pData->fProcesssave ((mng_handle)pData);
+
+    if (!bOke)
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode;
+
+
+    /* TODO: something with the parameters */
+
+
+                                       /* create a SAVE animation object */
+    iRetcode = mng_create_ani_save (pData);
+
+    if (!iRetcode)
+      iRetcode = mng_process_display_save (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+      
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_savep)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)                       /* not empty ? */
+    {
+      mng_uint8       iOtype = *pRawdata;
+      mng_uint8       iEtype;
+      mng_uint32      iCount = 0;
+      mng_uint8p      pTemp;
+      mng_uint8p      pNull;
+      mng_uint32      iLen;
+      mng_uint32      iOffset[2];
+      mng_uint32      iStarttime[2];
+      mng_uint32      iFramenr;
+      mng_uint32      iLayernr;
+      mng_uint32      iX;
+      mng_save_entryp pEntry = MNG_NULL;
+      mng_uint32      iNamesize;
+
+      if ((iOtype != 4) && (iOtype != 8))
+        MNG_ERROR (pData, MNG_INVOFFSETSIZE);
+
+      ((mng_savep)*ppChunk)->iOffsettype = iOtype;
+
+      for (iX = 0; iX < 2; iX++)       /* do this twice to get the count first ! */
+      {
+        pTemp = pRawdata + 1;
+        iLen  = iRawlen  - 1;
+
+        if (iX)                        /* second run ? */
+        {
+          MNG_ALLOC (pData, pEntry, (iCount * sizeof (mng_save_entry)));
+
+          ((mng_savep)*ppChunk)->iCount   = iCount;
+          ((mng_savep)*ppChunk)->pEntries = pEntry;
+        }
+
+        while (iLen)                   /* anything left ? */
+        {
+          iEtype = *pTemp;             /* entrytype */
+
+          if ((iEtype != 0) && (iEtype != 1) && (iEtype != 2) && (iEtype != 3))
+            MNG_ERROR (pData, MNG_INVENTRYTYPE);
+
+          pTemp++;
+
+          if (iEtype > 1)
+          {
+            iOffset    [0] = 0;
+            iOffset    [1] = 0;
+            iStarttime [0] = 0;
+            iStarttime [1] = 0;
+            iLayernr       = 0;
+            iFramenr       = 0;
+          }
+          else
+          {
+            if (iOtype == 4)
+            {
+              iOffset [0] = 0;
+              iOffset [1] = mng_get_uint32 (pTemp);
+
+              pTemp += 4;
+            }
+            else
+            {
+              iOffset [0] = mng_get_uint32 (pTemp);
+              iOffset [1] = mng_get_uint32 (pTemp+4);
+
+              pTemp += 8;
+            }
+
+            if (iEtype > 0)
+            {
+              iStarttime [0] = 0;
+              iStarttime [1] = 0;
+              iLayernr       = 0;
+              iFramenr       = 0;
+            }
+            else
+            {
+              if (iOtype == 4)
+              {
+                iStarttime [0] = 0;
+                iStarttime [1] = mng_get_uint32 (pTemp+0);
+                iLayernr       = mng_get_uint32 (pTemp+4);
+                iFramenr       = mng_get_uint32 (pTemp+8);
+
+                pTemp += 12;
+              }
+              else
+              {
+                iStarttime [0] = mng_get_uint32 (pTemp+0);
+                iStarttime [1] = mng_get_uint32 (pTemp+4);
+                iLayernr       = mng_get_uint32 (pTemp+8);
+                iFramenr       = mng_get_uint32 (pTemp+12);
+
+                pTemp += 16;
+              }
+            }
+          }
+
+          pNull = find_null (pTemp);   /* get the name length */
+
+          if ((pNull - pRawdata) > (mng_int32)iRawlen)
+          {
+            iNamesize = iLen;          /* no null found; so end of SAVE */
+            iLen      = 0;
+          }
+          else
+          {
+            iNamesize = pNull - pTemp; /* should be another entry */
+            iLen     -= iNamesize;
+
+            if (!iLen)                 /* must not end with a null ! */
+              MNG_ERROR (pData, MNG_ENDWITHNULL);
+          }
+
+          if (!pEntry)
+          {
+            iCount++;
+          }
+          else
+          {
+            pEntry->iEntrytype     = iEtype;
+            pEntry->iOffset    [0] = iOffset    [0];
+            pEntry->iOffset    [1] = iOffset    [1];
+            pEntry->iStarttime [0] = iStarttime [0];
+            pEntry->iStarttime [1] = iStarttime [1];
+            pEntry->iLayernr       = iLayernr;
+            pEntry->iFramenr       = iFramenr;
+            pEntry->iNamesize      = iNamesize;
+
+            if (iNamesize)
+            {
+              MNG_ALLOC (pData, pEntry->zName, iNamesize+1);
+              MNG_COPY (pEntry->zName, pTemp, iNamesize);
+            }
+
+            pEntry++;
+          }
+
+          pTemp += iNamesize;
+        }
+      }
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_SEEK
+READ_CHUNK (mng_read_seek)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SEEK, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) || (!pData->bHasSAVE))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_SUPPORT_DISPLAY
+                                       /* create a SEEK animation object */
+  iRetcode = mng_create_ani_seek (pData, iRawlen, (mng_pchar)pRawdata);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+    
+#endif /* MNG_SUPPORT_DISPLAY */
+
+  if (pData->fProcessseek)             /* inform the app ? */
+  {
+    mng_bool  bOke;
+    mng_pchar zName;
+
+    MNG_ALLOC (pData, zName, iRawlen + 1);
+
+    if (iRawlen)
+      MNG_COPY (zName, pRawdata, iRawlen);
+
+    bOke = pData->fProcessseek ((mng_handle)pData, zName);
+
+    MNG_FREEX (pData, zName, iRawlen + 1);
+
+    if (!bOke)
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+#ifdef MNG_SUPPORT_DISPLAY
+                                       /* do display processing of the SEEK */
+  iRetcode = mng_process_display_seek (pData);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_seekp)*ppChunk)->iNamesize = iRawlen;
+
+    if (iRawlen)
+    {
+      MNG_ALLOC (pData, ((mng_seekp)*ppChunk)->zName, iRawlen+1);
+      MNG_COPY (((mng_seekp)*ppChunk)->zName, pRawdata, iRawlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_SEEK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_eXPI
+READ_CHUNK (mng_read_expi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_EXPI, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen < 3)                     /* check the length */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_expip)*ppChunk)->iSnapshotid = mng_get_uint16 (pRawdata);
+    ((mng_expip)*ppChunk)->iNamesize   = iRawlen - 2;
+
+    if (((mng_expip)*ppChunk)->iNamesize)
+    {
+      MNG_ALLOC (pData, ((mng_expip)*ppChunk)->zName,
+                        ((mng_expip)*ppChunk)->iNamesize + 1);
+      MNG_COPY (((mng_expip)*ppChunk)->zName, pRawdata+2,
+                ((mng_expip)*ppChunk)->iNamesize);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_EXPI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_fPRI
+READ_CHUNK (mng_read_fpri)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_FPRI, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 2)                    /* must be two bytes long */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_fprip)*ppChunk)->iDeltatype = *pRawdata;
+    ((mng_fprip)*ppChunk)->iPriority  = *(pRawdata+1);
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_FPRI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_nEED
+MNG_LOCAL mng_bool CheckKeyword (mng_datap  pData,
+                                 mng_uint8p pKeyword)
+{
+  mng_chunkid handled_chunks [] =
+  {
+    MNG_UINT_BACK,                     /* keep it sorted !!!! */
+    MNG_UINT_BASI,
+    MNG_UINT_CLIP,
+    MNG_UINT_CLON,
+#ifndef MNG_NO_DELTA_PNG
+/* TODO:    MNG_UINT_DBYK,  */
+#endif
+    MNG_UINT_DEFI,
+#ifndef MNG_NO_DELTA_PNG
+    MNG_UINT_DHDR,
+#endif
+    MNG_UINT_DISC,
+#ifndef MNG_NO_DELTA_PNG
+/* TODO:    MNG_UINT_DROP,  */
+#endif
+    MNG_UINT_ENDL,
+    MNG_UINT_FRAM,
+    MNG_UINT_IDAT,
+    MNG_UINT_IEND,
+    MNG_UINT_IHDR,
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+    MNG_UINT_IJNG,
+#endif    
+    MNG_UINT_IPNG,
+#endif
+#ifdef MNG_INCLUDE_JNG
+    MNG_UINT_JDAA,
+    MNG_UINT_JDAT,
+    MNG_UINT_JHDR,
+/* TODO:    MNG_UINT_JSEP,  */
+    MNG_UINT_JdAA,
+#endif
+    MNG_UINT_LOOP,
+    MNG_UINT_MAGN,
+    MNG_UINT_MEND,
+    MNG_UINT_MHDR,
+    MNG_UINT_MOVE,
+/* TODO:    MNG_UINT_ORDR,  */
+    MNG_UINT_PAST,
+    MNG_UINT_PLTE,
+#ifndef MNG_NO_DELTA_PNG
+    MNG_UINT_PPLT,
+    MNG_UINT_PROM,
+#endif
+    MNG_UINT_SAVE,
+    MNG_UINT_SEEK,
+    MNG_UINT_SHOW,
+    MNG_UINT_TERM,
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+    MNG_UINT_adAT,
+    MNG_UINT_ahDR,
+#endif
+    MNG_UINT_bKGD,
+    MNG_UINT_cHRM,
+/* TODO:    MNG_UINT_eXPI,  */
+    MNG_UINT_evNT,
+/* TODO:    MNG_UINT_fPRI,  */
+    MNG_UINT_gAMA,
+/* TODO:    MNG_UINT_hIST,  */
+    MNG_UINT_iCCP,
+    MNG_UINT_iTXt,
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    MNG_UINT_mpNG,
+#endif
+    MNG_UINT_nEED,
+/* TODO:    MNG_UINT_oFFs,  */
+/* TODO:    MNG_UINT_pCAL,  */
+/* TODO:    MNG_UINT_pHYg,  */
+/* TODO:    MNG_UINT_pHYs,  */
+/* TODO:    MNG_UINT_sBIT,  */
+/* TODO:    MNG_UINT_sCAL,  */
+/* TODO:    MNG_UINT_sPLT,  */
+    MNG_UINT_sRGB,
+    MNG_UINT_tEXt,
+    MNG_UINT_tIME,
+    MNG_UINT_tRNS,
+    MNG_UINT_zTXt,
+  };
+
+  mng_bool bOke = MNG_FALSE;
+
+  if (pData->fProcessneed)             /* does the app handle it ? */
+    bOke = pData->fProcessneed ((mng_handle)pData, (mng_pchar)pKeyword);
+
+  if (!bOke)
+  {                                    /* find the keyword length */
+    mng_uint8p pNull = find_null (pKeyword);
+
+    if (pNull - pKeyword == 4)         /* test a chunk ? */
+    {                                  /* get the chunk-id */
+      mng_chunkid iChunkid = (*pKeyword     << 24) + (*(pKeyword+1) << 16) +
+                             (*(pKeyword+2) <<  8) + (*(pKeyword+3)      );
+                                       /* binary search variables */
+      mng_int32   iTop, iLower, iUpper, iMiddle;
+                                       /* determine max index of table */
+      iTop = (sizeof (handled_chunks) / sizeof (handled_chunks [0])) - 1;
+
+      /* binary search; with 52 chunks, worst-case is 7 comparisons */
+      iLower  = 0;
+      iMiddle = iTop >> 1;
+      iUpper  = iTop;
+
+      do                                   /* the binary search itself */
+        {
+          if (handled_chunks [iMiddle] < iChunkid)
+            iLower = iMiddle + 1;
+          else if (handled_chunks [iMiddle] > iChunkid)
+            iUpper = iMiddle - 1;
+          else
+          {
+            bOke = MNG_TRUE;
+            break;
+          }
+
+          iMiddle = (iLower + iUpper) >> 1;
+        }
+      while (iLower <= iUpper);
+    }
+                                       /* test draft ? */
+    if ((!bOke) && (pNull - pKeyword == 8) &&
+        (*pKeyword     == 'd') && (*(pKeyword+1) == 'r') &&
+        (*(pKeyword+2) == 'a') && (*(pKeyword+3) == 'f') &&
+        (*(pKeyword+4) == 't') && (*(pKeyword+5) == ' '))
+    {
+      mng_uint32 iDraft;
+
+      iDraft = (*(pKeyword+6) - '0') * 10 + (*(pKeyword+7) - '0');
+      bOke   = (mng_bool)(iDraft <= MNG_MNG_DRAFT);
+    }
+                                       /* test MNG 1.0/1.1 ? */
+    if ((!bOke) && (pNull - pKeyword == 7) &&
+        (*pKeyword     == 'M') && (*(pKeyword+1) == 'N') &&
+        (*(pKeyword+2) == 'G') && (*(pKeyword+3) == '-') &&
+        (*(pKeyword+4) == '1') && (*(pKeyword+5) == '.') &&
+        ((*(pKeyword+6) == '0') || (*(pKeyword+6) == '1')))
+      bOke   = MNG_TRUE;
+                                       /* test CACHEOFF ? */
+    if ((!bOke) && (pNull - pKeyword == 8) &&
+        (*pKeyword     == 'C') && (*(pKeyword+1) == 'A') &&
+        (*(pKeyword+2) == 'C') && (*(pKeyword+3) == 'H') &&
+        (*(pKeyword+4) == 'E') && (*(pKeyword+5) == 'O') &&
+        (*(pKeyword+6) == 'F') && (*(pKeyword+7) == 'F'))
+    {
+      if (!pData->pFirstaniobj)        /* only if caching hasn't started yet ! */
+      {
+        bOke                  = MNG_TRUE;
+        pData->bCacheplayback = MNG_FALSE;
+        pData->bStorechunks   = MNG_FALSE;
+      }
+    }
+  }
+
+  return bOke;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_nEED
+READ_CHUNK (mng_read_need)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_NEED, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen < 1)                     /* check the length */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  {                                    /* let's check it */
+    mng_bool   bOke = MNG_TRUE;
+    mng_pchar  zKeywords;
+    mng_uint8p pNull, pTemp;
+
+    MNG_ALLOC (pData, zKeywords, iRawlen + 1);
+
+    if (iRawlen)
+      MNG_COPY (zKeywords, pRawdata, iRawlen);
+
+    pTemp = (mng_uint8p)zKeywords;
+    pNull = find_null (pTemp);
+
+    while ((bOke) && (pNull < (mng_uint8p)zKeywords + iRawlen))
+    {
+      bOke  = CheckKeyword (pData, pTemp);
+      pTemp = pNull + 1;
+      pNull = find_null (pTemp);
+    }
+
+    if (bOke)
+      bOke = CheckKeyword (pData, pTemp);
+
+    MNG_FREEX (pData, zKeywords, iRawlen + 1);
+
+    if (!bOke)
+      MNG_ERROR (pData, MNG_UNSUPPORTEDNEED);
+  }
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_needp)*ppChunk)->iKeywordssize = iRawlen;
+
+    if (iRawlen)
+    {
+      MNG_ALLOC (pData, ((mng_needp)*ppChunk)->zKeywords, iRawlen+1);
+      MNG_COPY (((mng_needp)*ppChunk)->zKeywords, pRawdata, iRawlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_NEED, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_pHYg
+READ_CHUNK (mng_read_phyg)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PHYG, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* it's 9 bytes or empty; no more, no less! */
+  if ((iRawlen != 9) && (iRawlen != 0))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_phygp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0);
+
+    if (iRawlen)
+    {
+      ((mng_phygp)*ppChunk)->iSizex = mng_get_uint32 (pRawdata);
+      ((mng_phygp)*ppChunk)->iSizey = mng_get_uint32 (pRawdata+4);
+      ((mng_phygp)*ppChunk)->iUnit  = *(pRawdata+8);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PHYG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_INCLUDE_JNG
+READ_CHUNK (mng_read_jhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_JHDR, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((pData->eSigtype != mng_it_jng) && (pData->eSigtype != mng_it_mng))
+    MNG_ERROR (pData, MNG_CHUNKNOTALLOWED);
+
+  if ((pData->eSigtype == mng_it_jng) && (pData->iChunkseq > 1))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 16)                   /* length oke ? */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                                       /* inside a JHDR-IEND block now */
+  pData->bHasJHDR              = MNG_TRUE;
+                                       /* and store interesting fields */
+  pData->iDatawidth            = mng_get_uint32 (pRawdata);
+  pData->iDataheight           = mng_get_uint32 (pRawdata+4);
+  pData->iJHDRcolortype        = *(pRawdata+8);
+  pData->iJHDRimgbitdepth      = *(pRawdata+9);
+  pData->iJHDRimgcompression   = *(pRawdata+10);
+  pData->iJHDRimginterlace     = *(pRawdata+11);
+  pData->iJHDRalphabitdepth    = *(pRawdata+12);
+  pData->iJHDRalphacompression = *(pRawdata+13);
+  pData->iJHDRalphafilter      = *(pRawdata+14);
+  pData->iJHDRalphainterlace   = *(pRawdata+15);
+
+
+#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT)
+  pData->iPNGmult = 1;
+  pData->iPNGdepth = pData->iJHDRalphabitdepth;
+#endif
+
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+  if (pData->iJHDRalphabitdepth < 8)
+    pData->iJHDRalphabitdepth = 8;
+#endif
+
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (pData->iJHDRalphabitdepth > 8)
+  {
+    pData->iPNGmult = 2;
+    pData->iJHDRalphabitdepth = 8;
+  }
+#endif
+                                       /* parameter validity checks */
+  if ((pData->iJHDRcolortype != MNG_COLORTYPE_JPEGGRAY  ) &&
+      (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGCOLOR ) &&
+      (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGGRAYA ) &&
+      (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGCOLORA)    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  if ((pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG8     ) &&
+      (pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG12    ) &&
+      (pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG8AND12)    )
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if (pData->iJHDRimgcompression != MNG_COMPRESSION_BASELINEJPEG)
+    MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+  if ((pData->iJHDRimginterlace != MNG_INTERLACE_SEQUENTIAL ) &&
+      (pData->iJHDRimginterlace != MNG_INTERLACE_PROGRESSIVE)    )
+    MNG_ERROR (pData, MNG_INVALIDINTERLACE);
+
+  if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) ||
+      (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    )
+  {
+    if ((pData->iJHDRalphabitdepth != MNG_BITDEPTH_8 )
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+        && (pData->iJHDRalphabitdepth != MNG_BITDEPTH_1 ) &&
+        (pData->iJHDRalphabitdepth != MNG_BITDEPTH_2 ) &&
+        (pData->iJHDRalphabitdepth != MNG_BITDEPTH_4 )
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+        && (pData->iJHDRalphabitdepth != MNG_BITDEPTH_16)
+#endif
+        )
+      MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+    if ((pData->iJHDRalphacompression != MNG_COMPRESSION_DEFLATE     ) &&
+        (pData->iJHDRalphacompression != MNG_COMPRESSION_BASELINEJPEG)    )
+      MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+    if ((pData->iJHDRalphacompression == MNG_COMPRESSION_BASELINEJPEG) &&
+        (pData->iJHDRalphabitdepth    !=  MNG_BITDEPTH_8             )    )
+      MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+#if defined(FILTER192) || defined(FILTER193)
+    if ((pData->iJHDRalphafilter != MNG_FILTER_ADAPTIVE ) &&
+#if defined(FILTER192) && defined(FILTER193)
+        (pData->iJHDRalphafilter != MNG_FILTER_DIFFERING) &&
+        (pData->iJHDRalphafilter != MNG_FILTER_NOFILTER )    )
+#else
+#ifdef FILTER192
+        (pData->iJHDRalphafilter != MNG_FILTER_DIFFERING)    )
+#else
+        (pData->iJHDRalphafilter != MNG_FILTER_NOFILTER )    )
+#endif
+#endif
+      MNG_ERROR (pData, MNG_INVALIDFILTER);
+#else
+    if (pData->iJHDRalphafilter)
+      MNG_ERROR (pData, MNG_INVALIDFILTER);
+#endif
+
+    if ((pData->iJHDRalphainterlace != MNG_INTERLACE_NONE ) &&
+        (pData->iJHDRalphainterlace != MNG_INTERLACE_ADAM7)    )
+      MNG_ERROR (pData, MNG_INVALIDINTERLACE);
+
+  }
+  else
+  {
+    if (pData->iJHDRalphabitdepth)
+      MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+    if (pData->iJHDRalphacompression)
+      MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+    if (pData->iJHDRalphafilter)
+      MNG_ERROR (pData, MNG_INVALIDFILTER);
+
+    if (pData->iJHDRalphainterlace)
+      MNG_ERROR (pData, MNG_INVALIDINTERLACE);
+
+  }
+
+  if (!pData->bHasheader)              /* first chunk ? */
+  {
+    pData->bHasheader = MNG_TRUE;      /* we've got a header */
+    pData->eImagetype = mng_it_jng;    /* then this must be a JNG */
+    pData->iWidth     = mng_get_uint32 (pRawdata);
+    pData->iHeight    = mng_get_uint32 (pRawdata+4);
+                                       /* predict alpha-depth ! */
+  if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) ||
+      (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    )
+      pData->iAlphadepth = pData->iJHDRalphabitdepth;
+    else
+      pData->iAlphadepth = 0;
+                                       /* fits on maximum canvas ? */
+    if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight))
+      MNG_WARNING (pData, MNG_IMAGETOOLARGE);
+
+    if (pData->fProcessheader)         /* inform the app ? */
+      if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight))
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+
+  }
+
+  pData->iColortype = 0;               /* fake grayscale for other routines */
+  pData->iImagelevel++;                /* one level deeper */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode = mng_process_display_jhdr (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_jhdrp)*ppChunk)->iWidth            = mng_get_uint32 (pRawdata);
+    ((mng_jhdrp)*ppChunk)->iHeight           = mng_get_uint32 (pRawdata+4);
+    ((mng_jhdrp)*ppChunk)->iColortype        = *(pRawdata+8);
+    ((mng_jhdrp)*ppChunk)->iImagesampledepth = *(pRawdata+9);
+    ((mng_jhdrp)*ppChunk)->iImagecompression = *(pRawdata+10);
+    ((mng_jhdrp)*ppChunk)->iImageinterlace   = *(pRawdata+11);
+    ((mng_jhdrp)*ppChunk)->iAlphasampledepth = *(pRawdata+12);
+#ifdef MNG_NO_16BIT_SUPPORT
+    if (*(pRawdata+12) > 8)
+        ((mng_jhdrp)*ppChunk)->iAlphasampledepth = 8;
+#endif
+    ((mng_jhdrp)*ppChunk)->iAlphacompression = *(pRawdata+13);
+    ((mng_jhdrp)*ppChunk)->iAlphafilter      = *(pRawdata+14);
+    ((mng_jhdrp)*ppChunk)->iAlphainterlace   = *(pRawdata+15);
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_JHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#else
+#define read_jhdr 0
+#endif /* MNG_INCLUDE_JNG */
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_INCLUDE_JNG
+READ_CHUNK (mng_read_jdaa)
+{
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS)
+  volatile mng_retcode iRetcode;
+
+  iRetcode=MNG_NOERROR;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_JDAA, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasJHDR) && (!pData->bHasDHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (pData->bHasJSEP)
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+    
+  if (pData->iJHDRalphacompression != MNG_COMPRESSION_BASELINEJPEG)
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen == 0)                    /* can never be empty */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pData->bHasJDAA = MNG_TRUE;          /* got some JDAA now, don't we */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  iRetcode = mng_process_display_jdaa (pData, iRawlen, pRawdata);
+
+  if (iRetcode)                      /* on error bail out */
+    return iRetcode;
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_jdaap)*ppChunk)->bEmpty    = (mng_bool)(iRawlen == 0);
+    ((mng_jdaap)*ppChunk)->iDatasize = iRawlen;
+
+    if (iRawlen != 0)                  /* is there any data ? */
+    {
+      MNG_ALLOC (pData, ((mng_jdaap)*ppChunk)->pData, iRawlen);
+      MNG_COPY  (((mng_jdaap)*ppChunk)->pData, pRawdata, iRawlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_JDAA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#else
+#define read_jdaa 0
+#endif /* MNG_INCLUDE_JNG */
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_INCLUDE_JNG
+READ_CHUNK (mng_read_jdat)
+{
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS)
+  volatile mng_retcode iRetcode;
+
+  iRetcode=MNG_NOERROR;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_JDAT, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasJHDR) && (!pData->bHasDHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen == 0)                    /* can never be empty */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pData->bHasJDAT = MNG_TRUE;          /* got some JDAT now, don't we */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  iRetcode = mng_process_display_jdat (pData, iRawlen, pRawdata);
+
+  if (iRetcode)                      /* on error bail out */
+    return iRetcode;
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_jdatp)*ppChunk)->bEmpty    = (mng_bool)(iRawlen == 0);
+    ((mng_jdatp)*ppChunk)->iDatasize = iRawlen;
+
+    if (iRawlen != 0)                  /* is there any data ? */
+    {
+      MNG_ALLOC (pData, ((mng_jdatp)*ppChunk)->pData, iRawlen);
+      MNG_COPY  (((mng_jdatp)*ppChunk)->pData, pRawdata, iRawlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_JDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#else
+#define read_jdat 0
+#endif /* MNG_INCLUDE_JNG */
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_INCLUDE_JNG
+READ_CHUNK (mng_read_jsep)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_JSEP, MNG_LC_START);
+#endif
+
+  if (!pData->bHasJHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 0)                    /* must be empty ! */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pData->bHasJSEP = MNG_TRUE;          /* indicate we've had the 8-/12-bit separator */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_JSEP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#else
+#define read_jsep 0
+#endif /* MNG_INCLUDE_JNG */
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_NO_DELTA_PNG
+READ_CHUNK (mng_read_dhdr)
+{
+  mng_uint8 iImagetype, iDeltatype;
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DHDR, MNG_LC_START);
+#endif
+
+  if (!pData->bHasMHDR)                /* sequence checks */
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* check for valid length */
+  if ((iRawlen != 4) && (iRawlen != 12) && (iRawlen != 20))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  iImagetype = *(pRawdata+2);          /* check fields for validity */
+  iDeltatype = *(pRawdata+3);
+
+  if (iImagetype > MNG_IMAGETYPE_JNG)
+    MNG_ERROR (pData, MNG_INVIMAGETYPE);
+
+  if (iDeltatype > MNG_DELTATYPE_NOCHANGE)
+    MNG_ERROR (pData, MNG_INVDELTATYPE);
+
+  if ((iDeltatype == MNG_DELTATYPE_REPLACE) && (iRawlen > 12))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  if ((iDeltatype == MNG_DELTATYPE_NOCHANGE) && (iRawlen > 4))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  pData->bHasDHDR   = MNG_TRUE;        /* inside a DHDR-IEND block now */
+  pData->iDeltatype = iDeltatype;
+
+  pData->iImagelevel++;                /* one level deeper */
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_uint16  iObjectid    = mng_get_uint16 (pRawdata);
+    mng_uint32  iBlockwidth  = 0;
+    mng_uint32  iBlockheight = 0;
+    mng_uint32  iBlockx      = 0;
+    mng_uint32  iBlocky      = 0;
+    mng_retcode iRetcode;
+
+    if (iRawlen > 4)
+    {
+      iBlockwidth  = mng_get_uint32 (pRawdata+4);
+      iBlockheight = mng_get_uint32 (pRawdata+8);
+    }
+
+    if (iRawlen > 12)
+    {
+      iBlockx      = mng_get_uint32 (pRawdata+12);
+      iBlocky      = mng_get_uint32 (pRawdata+16);
+    }
+
+    iRetcode = mng_create_ani_dhdr (pData, iObjectid, iImagetype, iDeltatype,
+                                    iBlockwidth, iBlockheight, iBlockx, iBlocky);
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_dhdr (pData, iObjectid, iImagetype, iDeltatype,
+                                           iBlockwidth, iBlockheight, iBlockx, iBlocky); */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_dhdrp)*ppChunk)->iObjectid      = mng_get_uint16 (pRawdata);
+    ((mng_dhdrp)*ppChunk)->iImagetype     = iImagetype;
+    ((mng_dhdrp)*ppChunk)->iDeltatype     = iDeltatype;
+
+    if (iRawlen > 4)
+    {
+      ((mng_dhdrp)*ppChunk)->iBlockwidth  = mng_get_uint32 (pRawdata+4);
+      ((mng_dhdrp)*ppChunk)->iBlockheight = mng_get_uint32 (pRawdata+8);
+    }
+
+    if (iRawlen > 12)
+    {
+      ((mng_dhdrp)*ppChunk)->iBlockx      = mng_get_uint32 (pRawdata+12);
+      ((mng_dhdrp)*ppChunk)->iBlocky      = mng_get_uint32 (pRawdata+16);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_NO_DELTA_PNG
+READ_CHUNK (mng_read_prom)
+{
+  mng_uint8 iColortype;
+  mng_uint8 iSampledepth;
+  mng_uint8 iFilltype;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PROM, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) || (!pData->bHasDHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 3)                    /* gotta be exactly 3 bytes */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  iColortype   = *pRawdata;            /* check fields for validity */
+  iSampledepth = *(pRawdata+1);
+  iFilltype    = *(pRawdata+2);
+
+  if ((iColortype != MNG_COLORTYPE_GRAY   ) &&
+      (iColortype != MNG_COLORTYPE_RGB    ) &&
+      (iColortype != MNG_COLORTYPE_INDEXED) &&
+      (iColortype != MNG_COLORTYPE_GRAYA  ) &&
+      (iColortype != MNG_COLORTYPE_RGBA   )    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (iSampledepth == MNG_BITDEPTH_16 )
+      iSampledepth = MNG_BITDEPTH_8;
+#endif
+
+  if ((iSampledepth != MNG_BITDEPTH_1 ) &&
+      (iSampledepth != MNG_BITDEPTH_2 ) &&
+      (iSampledepth != MNG_BITDEPTH_4 ) &&
+      (iSampledepth != MNG_BITDEPTH_8 )
+#ifndef MNG_NO_16BIT_SUPPORT
+      && (iSampledepth != MNG_BITDEPTH_16)
+#endif
+    )
+    MNG_ERROR (pData, MNG_INVSAMPLEDEPTH);
+
+  if ((iFilltype != MNG_FILLMETHOD_LEFTBITREPLICATE) &&
+      (iFilltype != MNG_FILLMETHOD_ZEROFILL        )    )
+    MNG_ERROR (pData, MNG_INVFILLMETHOD);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode = mng_create_ani_prom (pData, iSampledepth,
+                                                iColortype, iFilltype);
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_prom (pData, iSampledepth,
+                                           iColortype, iFilltype); */
+                                           
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_promp)*ppChunk)->iColortype   = iColortype;
+    ((mng_promp)*ppChunk)->iSampledepth = iSampledepth;
+    ((mng_promp)*ppChunk)->iFilltype    = iFilltype;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PROM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_NO_DELTA_PNG
+READ_CHUNK (mng_read_ipng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IPNG, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) || (!pData->bHasDHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 0)                    /* gotta be empty */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode = mng_create_ani_ipng (pData);
+
+    if (!iRetcode)
+      iRetcode = mng_process_display_ipng (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_NO_DELTA_PNG
+READ_CHUNK (mng_read_pplt)
+{
+  mng_uint8     iDeltatype;
+  mng_uint8p    pTemp;
+  mng_uint32    iLen;
+  mng_uint8     iX, iM;
+  mng_uint32    iY;
+  mng_uint32    iMax;
+  mng_rgbpaltab aIndexentries;
+  mng_uint8arr  aAlphaentries;
+  mng_uint8arr  aUsedentries;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PPLT, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) && (!pData->bHasDHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen < 1)                     /* must have at least 1 byte */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  iDeltatype = *pRawdata;
+                                       /* valid ? */
+  if (iDeltatype > MNG_DELTATYPE_DELTARGBA)
+    MNG_ERROR (pData, MNG_INVDELTATYPE);
+                                       /* must be indexed color ! */
+  if (pData->iColortype != MNG_COLORTYPE_INDEXED)
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  pTemp = pRawdata + 1;
+  iLen  = iRawlen - 1;
+  iMax  = 0;
+
+  for (iY = 0; iY < 256; iY++)         /* reset arrays */
+  {
+    aIndexentries [iY].iRed   = 0;
+    aIndexentries [iY].iGreen = 0;
+    aIndexentries [iY].iBlue  = 0;
+    aAlphaentries [iY]        = 255;
+    aUsedentries  [iY]        = 0;
+  }
+
+  while (iLen)                         /* as long as there are entries left ... */
+  {
+    mng_uint32 iDiff;
+
+    if (iLen < 2)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    iX = *pTemp;                       /* get start and end index */
+    iM = *(pTemp+1);
+
+    if (iM < iX)
+      MNG_ERROR (pData, MNG_INVALIDINDEX);
+
+    if ((mng_uint32)iM >= iMax)        /* determine highest used index */
+      iMax = (mng_uint32)iM + 1;
+
+    pTemp += 2;
+    iLen  -= 2;
+    iDiff = (iM - iX + 1);
+    if ((iDeltatype == MNG_DELTATYPE_REPLACERGB  ) ||
+        (iDeltatype == MNG_DELTATYPE_DELTARGB    )    )
+      iDiff = iDiff * 3;
+    else
+    if ((iDeltatype == MNG_DELTATYPE_REPLACERGBA) ||
+        (iDeltatype == MNG_DELTATYPE_DELTARGBA  )    )
+      iDiff = iDiff * 4;
+
+    if (iLen < iDiff)
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+    if ((iDeltatype == MNG_DELTATYPE_REPLACERGB  ) ||
+        (iDeltatype == MNG_DELTATYPE_DELTARGB    )    )
+    {
+      for (iY = (mng_uint32)iX; iY <= (mng_uint32)iM; iY++)
+      {
+        aIndexentries [iY].iRed   = *pTemp;
+        aIndexentries [iY].iGreen = *(pTemp+1);
+        aIndexentries [iY].iBlue  = *(pTemp+2);
+        aUsedentries  [iY]        = 1;
+
+        pTemp += 3;
+        iLen  -= 3;
+      }
+    }
+    else
+    if ((iDeltatype == MNG_DELTATYPE_REPLACEALPHA) ||
+        (iDeltatype == MNG_DELTATYPE_DELTAALPHA  )    )
+    {
+      for (iY = (mng_uint32)iX; iY <= (mng_uint32)iM; iY++)
+      {
+        aAlphaentries [iY]        = *pTemp;
+        aUsedentries  [iY]        = 1;
+
+        pTemp++;
+        iLen--;
+      }
+    }
+    else
+    {
+      for (iY = (mng_uint32)iX; iY <= (mng_uint32)iM; iY++)
+      {
+        aIndexentries [iY].iRed   = *pTemp;
+        aIndexentries [iY].iGreen = *(pTemp+1);
+        aIndexentries [iY].iBlue  = *(pTemp+2);
+        aAlphaentries [iY]        = *(pTemp+3);
+        aUsedentries  [iY]        = 1;
+
+        pTemp += 4;
+        iLen  -= 4;
+      }
+    }
+  }
+
+  switch (pData->iBitdepth)            /* check maximum allowed entries for bitdepth */
+  {
+    case MNG_BITDEPTH_1 : {
+                            if (iMax > 2)
+                              MNG_ERROR (pData, MNG_INVALIDINDEX);
+                            break;
+                          }
+    case MNG_BITDEPTH_2 : {
+                            if (iMax > 4)
+                              MNG_ERROR (pData, MNG_INVALIDINDEX);
+                            break;
+                          }
+    case MNG_BITDEPTH_4 : {
+                            if (iMax > 16)
+                              MNG_ERROR (pData, MNG_INVALIDINDEX);
+                            break;
+                          }
+  }
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {                                    /* create animation object */
+    mng_retcode iRetcode = mng_create_ani_pplt (pData, iDeltatype, iMax,
+                                                aIndexentries, aAlphaentries,
+                                                aUsedentries);
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_pplt (pData, iDeltatype, iMax, aIndexentries,
+                                           aAlphaentries, aUsedentries); */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_ppltp)*ppChunk)->iDeltatype = iDeltatype;
+    ((mng_ppltp)*ppChunk)->iCount     = iMax;
+
+    for (iY = 0; iY < 256; iY++)
+    {
+      ((mng_ppltp)*ppChunk)->aEntries [iY].iRed   = aIndexentries [iY].iRed;
+      ((mng_ppltp)*ppChunk)->aEntries [iY].iGreen = aIndexentries [iY].iGreen;
+      ((mng_ppltp)*ppChunk)->aEntries [iY].iBlue  = aIndexentries [iY].iBlue;
+      ((mng_ppltp)*ppChunk)->aEntries [iY].iAlpha = aAlphaentries [iY];
+      ((mng_ppltp)*ppChunk)->aEntries [iY].bUsed  = (mng_bool)(aUsedentries [iY]);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_PPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+READ_CHUNK (mng_read_ijng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IJNG, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) || (!pData->bHasDHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen != 0)                    /* gotta be empty */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode = mng_create_ani_ijng (pData);
+
+    if (!iRetcode)
+      iRetcode = mng_process_display_ijng (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_IJNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_NO_DELTA_PNG
+READ_CHUNK (mng_read_drop)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DROP, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) || (!pData->bHasDHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* check length */
+  if ((iRawlen < 4) || ((iRawlen % 4) != 0))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_dropp)*ppChunk)->iCount = iRawlen / 4;
+
+    if (iRawlen)
+    {
+      mng_uint32      iX;
+      mng_uint8p      pTemp = pRawdata;
+      mng_uint32p     pEntry;
+
+      MNG_ALLOC (pData, pEntry, iRawlen);
+
+      ((mng_dropp)*ppChunk)->pChunknames = (mng_ptr)pEntry;
+
+      for (iX = 0; iX < iRawlen / 4; iX++)
+      {
+        *pEntry = mng_get_uint32 (pTemp);
+
+        pTemp  += 4;
+        pEntry++;
+      }
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DROP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+READ_CHUNK (mng_read_dbyk)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DBYK, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) || (!pData->bHasDHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen < 6)                     /* must be at least 6 long */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_dbykp)*ppChunk)->iChunkname    = mng_get_uint32 (pRawdata);
+    ((mng_dbykp)*ppChunk)->iPolarity     = *(pRawdata+4);
+    ((mng_dbykp)*ppChunk)->iKeywordssize = iRawlen - 5;
+
+    if (iRawlen > 5)
+    {
+      MNG_ALLOC (pData, ((mng_dbykp)*ppChunk)->zKeywords, iRawlen-4);
+      MNG_COPY (((mng_dbykp)*ppChunk)->zKeywords, pRawdata+5, iRawlen-5);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DBYK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+READ_CHUNK (mng_read_ordr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ORDR, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) || (!pData->bHasDHDR))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* check length */
+  if ((iRawlen < 5) || ((iRawlen % 5) != 0))
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+
+
+    /* TODO: something !!! */
+
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_ordrp)*ppChunk)->iCount = iRawlen / 5;
+
+    if (iRawlen)
+    {
+      mng_uint32      iX;
+      mng_ordr_entryp pEntry;
+      mng_uint8p      pTemp = pRawdata;
+      
+      MNG_ALLOC (pData, pEntry, iRawlen);
+
+      ((mng_ordrp)*ppChunk)->pEntries = pEntry;
+
+      for (iX = 0; iX < iRawlen / 5; iX++)
+      {
+        pEntry->iChunkname = mng_get_uint32 (pTemp);
+        pEntry->iOrdertype = *(pTemp+4);
+
+        pTemp += 5;
+        pEntry++;
+      }
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_ORDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_MAGN
+READ_CHUNK (mng_read_magn)
+{
+  mng_uint16 iFirstid, iLastid;
+  mng_uint8  iMethodX, iMethodY;
+  mng_uint16 iMX, iMY, iML, iMR, iMT, iMB;
+  mng_bool   bFaulty;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MAGN, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_SUPPORT_JNG
+  if ((!pData->bHasMHDR) || (pData->bHasIHDR) || (pData->bHasDHDR) || (pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) || (pData->bHasIHDR) || (pData->bHasDHDR))
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* check length */
+  if (iRawlen > 20)
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  /* following is an ugly hack to allow faulty layout caused by previous
+     versions of libmng and MNGeye, which wrote MAGN with a 16-bit
+     MethodX/MethodY (as opposed to the proper 8-bit as defined in the spec!) */
+
+  if ((iRawlen ==  6) || (iRawlen ==  8) || (iRawlen == 10) || (iRawlen == 12) ||
+      (iRawlen == 14) || (iRawlen == 16) || (iRawlen == 20))
+    bFaulty = MNG_TRUE;                /* these lengths are all wrong */
+  else                                 /* length 18 can be right or wrong !!! */
+  if ((iRawlen ==  18) && (mng_get_uint16 (pRawdata+4) <= 5) &&
+      (mng_get_uint16 (pRawdata+6)  < 256) &&
+      (mng_get_uint16 (pRawdata+8)  < 256) &&
+      (mng_get_uint16 (pRawdata+10) < 256) &&
+      (mng_get_uint16 (pRawdata+12) < 256) &&
+      (mng_get_uint16 (pRawdata+14) < 256) &&
+      (mng_get_uint16 (pRawdata+16) < 256))
+    bFaulty = MNG_TRUE;                /* this is very likely the wrong layout */
+  else
+    bFaulty = MNG_FALSE;               /* all other cases are handled as right */
+
+  if (bFaulty)                         /* wrong layout ? */
+  {
+    if (iRawlen > 0)                   /* get the fields */
+      iFirstid = mng_get_uint16 (pRawdata);
+    else
+      iFirstid = 0;
+
+    if (iRawlen > 2)
+      iLastid  = mng_get_uint16 (pRawdata+2);
+    else
+      iLastid  = iFirstid;
+
+    if (iRawlen > 4)
+      iMethodX = (mng_uint8)(mng_get_uint16 (pRawdata+4));
+    else
+      iMethodX = 0;
+
+    if (iRawlen > 6)
+      iMX      = mng_get_uint16 (pRawdata+6);
+    else
+      iMX      = 1;
+
+    if (iRawlen > 8)
+      iMY      = mng_get_uint16 (pRawdata+8);
+    else
+      iMY      = iMX;
+
+    if (iRawlen > 10)
+      iML      = mng_get_uint16 (pRawdata+10);
+    else
+      iML      = iMX;
+
+    if (iRawlen > 12)
+      iMR      = mng_get_uint16 (pRawdata+12);
+    else
+      iMR      = iMX;
+
+    if (iRawlen > 14)
+      iMT      = mng_get_uint16 (pRawdata+14);
+    else
+      iMT      = iMY;
+
+    if (iRawlen > 16)
+      iMB      = mng_get_uint16 (pRawdata+16);
+    else
+      iMB      = iMY;
+
+    if (iRawlen > 18)
+      iMethodY = (mng_uint8)(mng_get_uint16 (pRawdata+18));
+    else
+      iMethodY = iMethodX;
+  }
+  else                                 /* proper layout !!!! */
+  {
+    if (iRawlen > 0)                   /* get the fields */
+      iFirstid = mng_get_uint16 (pRawdata);
+    else
+      iFirstid = 0;
+
+    if (iRawlen > 2)
+      iLastid  = mng_get_uint16 (pRawdata+2);
+    else
+      iLastid  = iFirstid;
+
+    if (iRawlen > 4)
+      iMethodX = *(pRawdata+4);
+    else
+      iMethodX = 0;
+
+    if (iRawlen > 5)
+      iMX      = mng_get_uint16 (pRawdata+5);
+    else
+      iMX      = 1;
+
+    if (iRawlen > 7)
+      iMY      = mng_get_uint16 (pRawdata+7);
+    else
+      iMY      = iMX;
+
+    if (iRawlen > 9)
+      iML      = mng_get_uint16 (pRawdata+9);
+    else
+      iML      = iMX;
+
+    if (iRawlen > 11)
+      iMR      = mng_get_uint16 (pRawdata+11);
+    else
+      iMR      = iMX;
+
+    if (iRawlen > 13)
+      iMT      = mng_get_uint16 (pRawdata+13);
+    else
+      iMT      = iMY;
+
+    if (iRawlen > 15)
+      iMB      = mng_get_uint16 (pRawdata+15);
+    else
+      iMB      = iMY;
+
+    if (iRawlen > 17)
+      iMethodY = *(pRawdata+17);
+    else
+      iMethodY = iMethodX;
+  }
+                                       /* check field validity */
+  if ((iMethodX > 5) || (iMethodY > 5))
+    MNG_ERROR (pData, MNG_INVALIDMETHOD);
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    mng_retcode iRetcode;
+
+    iRetcode = mng_create_ani_magn (pData, iFirstid, iLastid, iMethodX,
+                                    iMX, iMY, iML, iMR, iMT, iMB, iMethodY);
+
+/*    if (!iRetcode)
+      iRetcode = mng_process_display_magn (pData, iFirstid, iLastid, iMethodX,
+                                           iMX, iMY, iML, iMR, iMT, iMB, iMethodY); */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_magnp)*ppChunk)->iFirstid = iFirstid;
+    ((mng_magnp)*ppChunk)->iLastid  = iLastid;
+    ((mng_magnp)*ppChunk)->iMethodX = iMethodX;
+    ((mng_magnp)*ppChunk)->iMX      = iMX;
+    ((mng_magnp)*ppChunk)->iMY      = iMY;
+    ((mng_magnp)*ppChunk)->iML      = iML;
+    ((mng_magnp)*ppChunk)->iMR      = iMR;
+    ((mng_magnp)*ppChunk)->iMT      = iMT;
+    ((mng_magnp)*ppChunk)->iMB      = iMB;
+    ((mng_magnp)*ppChunk)->iMethodY = iMethodY;
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MAGN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+READ_CHUNK (mng_read_mpng)
+{
+  mng_uint32  iFramewidth;
+  mng_uint32  iFrameheight;
+  mng_uint16  iTickspersec;
+  mng_uint32  iFramessize;
+  mng_uint32  iCompressedsize;
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS)
+  mng_retcode iRetcode;
+  mng_uint16  iNumplays;
+  mng_uint32  iBufsize;
+  mng_uint8p  pBuf = 0;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MPNG, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if (!pData->bHasIHDR)
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen < 41)                    /* length must be at least 41 */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+  iFramewidth     = mng_get_int32 (pRawdata);
+  if (iFramewidth == 0)                /* frame_width must not be zero */
+    MNG_ERROR (pData, MNG_INVALIDWIDTH);
+
+  iFrameheight    = mng_get_int32 (pRawdata+4);
+  if (iFrameheight == 0)               /* frame_height must not be zero */
+    MNG_ERROR (pData, MNG_INVALIDHEIGHT);
+
+  iTickspersec    = mng_get_uint16 (pRawdata+10);
+  if (iTickspersec == 0)               /* delay_den must not be zero */
+    MNG_ERROR (pData, MNG_INVALIDFIELDVAL);
+
+  if (*(pRawdata+12) != 0)             /* only deflate compression-method allowed */
+    MNG_ERROR (pData, MNG_INVALIDCOMPRESS);
+
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS)
+  iNumplays       = mng_get_uint16 (pRawdata+8);
+  iCompressedsize = (mng_uint32)(iRawlen - 13);
+#endif
+
+#ifdef MNG_SUPPORT_DISPLAY
+  {
+    iRetcode = mng_inflate_buffer (pData, pRawdata+13, iCompressedsize,
+                                   &pBuf, &iBufsize, &iFramessize);
+    if (iRetcode)                    /* on error bail out */
+    {
+      MNG_FREEX (pData, pBuf, iBufsize);
+      return iRetcode;
+    }
+
+    if (iFramessize % 26)
+    {
+      MNG_FREEX (pData, pBuf, iBufsize);
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+    }
+
+    iRetcode = mng_create_mpng_obj (pData, iFramewidth, iFrameheight, iNumplays,
+                                    iTickspersec, iFramessize, pBuf);
+    if (iRetcode)                      /* on error bail out */
+    {
+      MNG_FREEX (pData, pBuf, iBufsize);
+      return iRetcode;
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the fields */
+    ((mng_mpngp)*ppChunk)->iFramewidth        = iFramewidth;
+    ((mng_mpngp)*ppChunk)->iFrameheight       = iFrameheight;
+    ((mng_mpngp)*ppChunk)->iNumplays          = iNumplays;
+    ((mng_mpngp)*ppChunk)->iTickspersec       = iTickspersec;
+    ((mng_mpngp)*ppChunk)->iCompressionmethod = *(pRawdata+14);
+
+#ifndef MNG_SUPPORT_DISPLAY
+    iRetcode = mng_inflate_buffer (pData, pRawdata+13, iCompressedsize,
+                                   &pBuf, &iBufsize, &iFramessize);
+    if (iRetcode)                    /* on error bail out */
+    {
+      MNG_FREEX (pData, pBuf, iBufsize);
+      return iRetcode;
+    }
+
+    if (iFramessize % 26)
+    {
+      MNG_FREEX (pData, pBuf, iBufsize);
+      MNG_ERROR (pData, MNG_INVALIDLENGTH);
+    }
+#endif
+
+    if (iFramessize)
+    {
+      MNG_ALLOCX (pData, ((mng_mpngp)*ppChunk)->pFrames, iFramessize);
+      if (((mng_mpngp)*ppChunk)->pFrames == 0)
+      {
+        MNG_FREEX (pData, pBuf, iBufsize);
+        MNG_ERROR (pData, MNG_OUTOFMEMORY);
+      }
+
+      ((mng_mpngp)*ppChunk)->iFramessize = iFramessize;
+      MNG_COPY (((mng_mpngp)*ppChunk)->pFrames, pBuf, iFramessize);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS)
+  MNG_FREEX (pData, pBuf, iBufsize);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_MPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifndef MNG_SKIPCHUNK_evNT
+READ_CHUNK (mng_read_evnt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_EVNT, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+  if ((!pData->bHasMHDR) || (pData->bHasSAVE))
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+
+  if (iRawlen < 2)                     /* must have at least 1 entry ! */
+    MNG_ERROR (pData, MNG_INVALIDLENGTH);
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG)
+  {
+    if (iRawlen)                       /* not empty ? */
+    {
+      mng_retcode iRetcode;
+      mng_uint8p  pTemp;
+      mng_uint8p  pNull;
+      mng_uint32  iLen;
+      mng_uint8   iEventtype;
+      mng_uint8   iMasktype;
+      mng_int32   iLeft;
+      mng_int32   iRight;
+      mng_int32   iTop;
+      mng_int32   iBottom;
+      mng_uint16  iObjectid;
+      mng_uint8   iIndex;
+      mng_uint32  iNamesize;
+
+      pTemp = pRawdata;
+      iLen  = iRawlen;
+
+      while (iLen)                   /* anything left ? */
+      {
+        iEventtype = *pTemp;         /* eventtype */
+        if (iEventtype > 5)
+          MNG_ERROR (pData, MNG_INVALIDEVENT);
+
+        pTemp++;
+
+        iMasktype  = *pTemp;         /* masktype */
+        if (iMasktype > 5)
+          MNG_ERROR (pData, MNG_INVALIDMASK);
+
+        pTemp++;
+        iLen -= 2;
+
+        iLeft     = 0;
+        iRight    = 0;
+        iTop      = 0;
+        iBottom   = 0;
+        iObjectid = 0;
+        iIndex    = 0;
+
+        switch (iMasktype)
+        {
+          case 1 :
+            {
+              if (iLen > 16)
+              {
+                iLeft     = mng_get_int32 (pTemp);
+                iRight    = mng_get_int32 (pTemp+4);
+                iTop      = mng_get_int32 (pTemp+8);
+                iBottom   = mng_get_int32 (pTemp+12);
+                pTemp += 16;
+                iLen -= 16;
+              }
+              else
+                MNG_ERROR (pData, MNG_INVALIDLENGTH);
+              break;
+            }
+          case 2 :
+            {
+              if (iLen > 2)
+              {
+                iObjectid = mng_get_uint16 (pTemp);
+                pTemp += 2;
+                iLen -= 2;
+              }
+              else
+                MNG_ERROR (pData, MNG_INVALIDLENGTH);
+              break;
+            }
+          case 3 :
+            {
+              if (iLen > 3)
+              {
+                iObjectid = mng_get_uint16 (pTemp);
+                iIndex    = *(pTemp+2);
+                pTemp += 3;
+                iLen -= 3;
+              }
+              else
+                MNG_ERROR (pData, MNG_INVALIDLENGTH);
+              break;
+            }
+          case 4 :
+            {
+              if (iLen > 18)
+              {
+                iLeft     = mng_get_int32 (pTemp);
+                iRight    = mng_get_int32 (pTemp+4);
+                iTop      = mng_get_int32 (pTemp+8);
+                iBottom   = mng_get_int32 (pTemp+12);
+                iObjectid = mng_get_uint16 (pTemp+16);
+                pTemp += 18;
+                iLen -= 18;
+              }
+              else
+                MNG_ERROR (pData, MNG_INVALIDLENGTH);
+              break;
+            }
+          case 5 :
+            {
+              if (iLen > 19)
+              {
+                iLeft     = mng_get_int32 (pTemp);
+                iRight    = mng_get_int32 (pTemp+4);
+                iTop      = mng_get_int32 (pTemp+8);
+                iBottom   = mng_get_int32 (pTemp+12);
+                iObjectid = mng_get_uint16 (pTemp+16);
+                iIndex    = *(pTemp+18);
+                pTemp += 19;
+                iLen -= 19;
+              }
+              else
+                MNG_ERROR (pData, MNG_INVALIDLENGTH);
+              break;
+            }
+        }
+
+        pNull = find_null (pTemp);   /* get the name length */
+
+        if ((pNull - pTemp) > (mng_int32)iLen)
+        {
+          iNamesize = iLen;          /* no null found; so end of evNT */
+          iLen      = 0;
+        }
+        else
+        {
+          iNamesize = pNull - pTemp; /* should be another entry */
+          iLen      = iLen - iNamesize - 1;
+
+          if (!iLen)                 /* must not end with a null ! */
+            MNG_ERROR (pData, MNG_ENDWITHNULL);
+        }
+
+        iRetcode = mng_create_event (pData, iEventtype, iMasktype, iLeft, iRight,
+                                            iTop, iBottom, iObjectid, iIndex,
+                                            iNamesize, (mng_pchar)pTemp);
+
+        if (iRetcode)                 /* on error bail out */
+          return iRetcode;
+
+        pTemp = pTemp + iNamesize + 1;
+      }
+    }
+  }
+#endif /* MNG_SUPPORT_DISPLAY && MNG_SUPPORT_DYNAMICMNG */
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    if (iRawlen)                       /* not empty ? */
+    {
+      mng_uint32      iX;
+      mng_uint32      iCount = 0;
+      mng_uint8p      pTemp;
+      mng_uint8p      pNull;
+      mng_uint32      iLen;
+      mng_uint8       iEventtype;
+      mng_uint8       iMasktype;
+      mng_int32       iLeft;
+      mng_int32       iRight;
+      mng_int32       iTop;
+      mng_int32       iBottom;
+      mng_uint16      iObjectid;
+      mng_uint8       iIndex;
+      mng_uint32      iNamesize;
+      mng_evnt_entryp pEntry = MNG_NULL;
+
+      for (iX = 0; iX < 2; iX++)       /* do this twice to get the count first ! */
+      {
+        pTemp = pRawdata;
+        iLen  = iRawlen;
+
+        if (iX)                        /* second run ? */
+        {
+          MNG_ALLOC (pData, pEntry, (iCount * sizeof (mng_evnt_entry)));
+
+          ((mng_evntp)*ppChunk)->iCount   = iCount;
+          ((mng_evntp)*ppChunk)->pEntries = pEntry;
+        }
+
+        while (iLen)                   /* anything left ? */
+        {
+          iEventtype = *pTemp;         /* eventtype */
+          if (iEventtype > 5)
+            MNG_ERROR (pData, MNG_INVALIDEVENT);
+
+          pTemp++;
+
+          iMasktype  = *pTemp;         /* masktype */
+          if (iMasktype > 5)
+            MNG_ERROR (pData, MNG_INVALIDMASK);
+
+          pTemp++;
+          iLen -= 2;
+
+          iLeft     = 0;
+          iRight    = 0;
+          iTop      = 0;
+          iBottom   = 0;
+          iObjectid = 0;
+          iIndex    = 0;
+
+          switch (iMasktype)
+          {
+            case 1 :
+              {
+                if (iLen > 16)
+                {
+                  iLeft     = mng_get_int32 (pTemp);
+                  iRight    = mng_get_int32 (pTemp+4);
+                  iTop      = mng_get_int32 (pTemp+8);
+                  iBottom   = mng_get_int32 (pTemp+12);
+                  pTemp += 16;
+                  iLen -= 16;
+                }
+                else
+                  MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                break;
+              }
+            case 2 :
+              {
+                if (iLen > 2)
+                {
+                  iObjectid = mng_get_uint16 (pTemp);
+                  pTemp += 2;
+                  iLen -= 2;
+                }
+                else
+                  MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                break;
+              }
+            case 3 :
+              {
+                if (iLen > 3)
+                {
+                  iObjectid = mng_get_uint16 (pTemp);
+                  iIndex    = *(pTemp+2);
+                  pTemp += 3;
+                  iLen -= 3;
+                }
+                else
+                  MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                break;
+              }
+            case 4 :
+              {
+                if (iLen > 18)
+                {
+                  iLeft     = mng_get_int32 (pTemp);
+                  iRight    = mng_get_int32 (pTemp+4);
+                  iTop      = mng_get_int32 (pTemp+8);
+                  iBottom   = mng_get_int32 (pTemp+12);
+                  iObjectid = mng_get_uint16 (pTemp+16);
+                  pTemp += 18;
+                  iLen -= 18;
+                }
+                else
+                  MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                break;
+              }
+            case 5 :
+              {
+                if (iLen > 19)
+                {
+                  iLeft     = mng_get_int32 (pTemp);
+                  iRight    = mng_get_int32 (pTemp+4);
+                  iTop      = mng_get_int32 (pTemp+8);
+                  iBottom   = mng_get_int32 (pTemp+12);
+                  iObjectid = mng_get_uint16 (pTemp+16);
+                  iIndex    = *(pTemp+18);
+                  pTemp += 19;
+                  iLen -= 19;
+                }
+                else
+                  MNG_ERROR (pData, MNG_INVALIDLENGTH);
+                break;
+              }
+          }
+
+          pNull = find_null (pTemp);   /* get the name length */
+
+          if ((pNull - pTemp) > (mng_int32)iLen)
+          {
+            iNamesize = iLen;          /* no null found; so end of evNT */
+            iLen      = 0;
+          }
+          else
+          {
+            iNamesize = pNull - pTemp; /* should be another entry */
+            iLen      = iLen - iNamesize - 1;
+
+            if (!iLen)                 /* must not end with a null ! */
+              MNG_ERROR (pData, MNG_ENDWITHNULL);
+          }
+
+          if (!iX)
+          {
+            iCount++;
+          }
+          else
+          {
+            pEntry->iEventtype       = iEventtype;
+            pEntry->iMasktype        = iMasktype;
+            pEntry->iLeft            = iLeft;
+            pEntry->iRight           = iRight;
+            pEntry->iTop             = iTop;
+            pEntry->iBottom          = iBottom;
+            pEntry->iObjectid        = iObjectid;
+            pEntry->iIndex           = iIndex;
+            pEntry->iSegmentnamesize = iNamesize;
+
+            if (iNamesize)
+            {
+              MNG_ALLOC (pData, pEntry->zSegmentname, iNamesize+1);
+              MNG_COPY (pEntry->zSegmentname, pTemp, iNamesize);
+            }
+
+            pEntry++;
+          }
+
+          pTemp = pTemp + iNamesize + 1;
+        }
+      }
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_EVNT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_unknown)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_UNKNOWN, MNG_LC_START);
+#endif
+                                       /* sequence checks */
+#ifdef MNG_INCLUDE_JNG
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) &&
+      (!pData->bHasBASI) && (!pData->bHasDHDR)    )
+#endif
+    MNG_ERROR (pData, MNG_SEQUENCEERROR);
+                                       /* critical chunk ? */
+  if ((((mng_uint32)pData->iChunkname & 0x20000000) == 0)
+#ifdef MNG_SKIPCHUNK_SAVE
+    && (pData->iChunkname != MNG_UINT_SAVE)
+#endif
+#ifdef MNG_SKIPCHUNK_SEEK
+    && (pData->iChunkname != MNG_UINT_SEEK)
+#endif
+#ifdef MNG_SKIPCHUNK_DBYK
+    && (pData->iChunkname != MNG_UINT_DBYK)
+#endif
+#ifdef MNG_SKIPCHUNK_ORDR
+    && (pData->iChunkname != MNG_UINT_ORDR)
+#endif
+      )
+    MNG_ERROR (pData, MNG_UNKNOWNCRITICAL);
+
+  if (pData->fProcessunknown)          /* let the app handle it ? */
+  {
+    mng_bool bOke = pData->fProcessunknown ((mng_handle)pData, pData->iChunkname,
+                                            iRawlen, (mng_ptr)pRawdata);
+
+    if (!bOke)
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+#ifdef MNG_STORE_CHUNKS
+  if (pData->bStorechunks)
+  {                                    /* initialize storage */
+    mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* store the length */
+    ((mng_chunk_headerp)*ppChunk)->iChunkname = pData->iChunkname;
+    ((mng_unknown_chunkp)*ppChunk)->iDatasize = iRawlen;
+
+    if (iRawlen == 0)                  /* any data at all ? */
+      ((mng_unknown_chunkp)*ppChunk)->pData = 0;
+    else
+    {                                  /* then store it */
+      MNG_ALLOC (pData, ((mng_unknown_chunkp)*ppChunk)->pData, iRawlen);
+      MNG_COPY (((mng_unknown_chunkp)*ppChunk)->pData, pRawdata, iRawlen);
+    }
+  }
+#endif /* MNG_STORE_CHUNKS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_UNKNOWN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+#endif
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_READ_PROCS */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * chunk write functions                                                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_WRITE_PROCS
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_ihdr)
+{
+  mng_ihdrp   pIHDR;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IHDR, MNG_LC_START);
+#endif
+
+  pIHDR    = (mng_ihdrp)pChunk;        /* address the proper chunk */
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 13;
+                                       /* fill the output buffer */
+  mng_put_uint32 (pRawdata,   pIHDR->iWidth);
+  mng_put_uint32 (pRawdata+4, pIHDR->iHeight);
+
+  *(pRawdata+8)  = pIHDR->iBitdepth;
+  *(pRawdata+9)  = pIHDR->iColortype;
+  *(pRawdata+10) = pIHDR->iCompression;
+  *(pRawdata+11) = pIHDR->iFilter;
+  *(pRawdata+12) = pIHDR->iInterlace;
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pIHDR->sHeader.iChunkname, iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IHDR, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_plte)
+{
+  mng_pltep   pPLTE;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint8p  pTemp;
+  mng_uint32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PLTE, MNG_LC_START);
+#endif
+
+  pPLTE    = (mng_pltep)pChunk;        /* address the proper chunk */
+
+  if (pPLTE->bEmpty)                   /* write empty chunk ? */
+    iRetcode = write_raw_chunk (pData, pPLTE->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = pPLTE->iEntrycount * 3;
+                                       /* fill the output buffer */
+    pTemp = pRawdata;
+
+    for (iX = 0; iX < pPLTE->iEntrycount; iX++)
+    {
+      *pTemp     = pPLTE->aEntries [iX].iRed;
+      *(pTemp+1) = pPLTE->aEntries [iX].iGreen;
+      *(pTemp+2) = pPLTE->aEntries [iX].iBlue;
+
+      pTemp += 3;
+    }
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pPLTE->sHeader.iChunkname, iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PLTE, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_idat)
+{
+  mng_idatp   pIDAT;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IDAT, MNG_LC_START);
+#endif
+
+  pIDAT = (mng_idatp)pChunk;           /* address the proper chunk */
+
+  if (pIDAT->bEmpty)                   /* and write it */
+    iRetcode = write_raw_chunk (pData, pIDAT->sHeader.iChunkname, 0, 0);
+  else
+    iRetcode = write_raw_chunk (pData, pIDAT->sHeader.iChunkname,
+                                pIDAT->iDatasize, pIDAT->pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IDAT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_iend)
+{
+  mng_iendp   pIEND;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IEND, MNG_LC_START);
+#endif
+
+  pIEND = (mng_iendp)pChunk;           /* address the proper chunk */
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pIEND->sHeader.iChunkname, 0, 0);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IEND, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_trns)
+{
+  mng_trnsp   pTRNS;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint8p  pTemp;
+  mng_uint32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_TRNS, MNG_LC_START);
+#endif
+
+  pTRNS = (mng_trnsp)pChunk;           /* address the proper chunk */
+
+  if (pTRNS->bEmpty)                   /* write empty chunk ? */
+    iRetcode = write_raw_chunk (pData, pTRNS->sHeader.iChunkname, 0, 0);
+  else
+  if (pTRNS->bGlobal)                  /* write global chunk ? */
+    iRetcode = write_raw_chunk (pData, pTRNS->sHeader.iChunkname,
+                                pTRNS->iRawlen, (mng_uint8p)pTRNS->aRawdata);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer */
+    iRawlen  = 0;                      /* and default size */
+
+    switch (pTRNS->iType)
+    {
+      case 0: {
+                iRawlen   = 2;         /* fill the size & output buffer */
+                mng_put_uint16 (pRawdata, pTRNS->iGray);
+
+                break;
+              }
+      case 2: {
+                iRawlen       = 6;     /* fill the size & output buffer */
+                mng_put_uint16 (pRawdata,   pTRNS->iRed);
+                mng_put_uint16 (pRawdata+2, pTRNS->iGreen);
+                mng_put_uint16 (pRawdata+4, pTRNS->iBlue);
+
+                break;
+              }
+      case 3: {                        /* init output buffer size */
+                iRawlen = pTRNS->iCount;
+
+                pTemp   = pRawdata;    /* fill the output buffer */
+
+                for (iX = 0; iX < pTRNS->iCount; iX++)
+                {
+                  *pTemp = pTRNS->aEntries[iX];
+                  pTemp++;
+                }
+
+                break;
+              }
+    }
+                                       /* write the chunk */
+    iRetcode = write_raw_chunk (pData, pTRNS->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_TRNS, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_gama)
+{
+  mng_gamap   pGAMA;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_GAMA, MNG_LC_START);
+#endif
+
+  pGAMA = (mng_gamap)pChunk;           /* address the proper chunk */
+
+  if (pGAMA->bEmpty)                   /* write empty ? */
+    iRetcode = write_raw_chunk (pData, pGAMA->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 4;
+                                       /* fill the buffer */
+    mng_put_uint32 (pRawdata, pGAMA->iGamma);
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pGAMA->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_GAMA, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+WRITE_CHUNK (mng_write_chrm)
+{
+  mng_chrmp   pCHRM;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_CHRM, MNG_LC_START);
+#endif
+
+  pCHRM = (mng_chrmp)pChunk;           /* address the proper chunk */
+
+  if (pCHRM->bEmpty)                   /* write empty ? */
+    iRetcode = write_raw_chunk (pData, pCHRM->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 32;
+                                       /* fill the buffer */
+    mng_put_uint32 (pRawdata,    pCHRM->iWhitepointx);
+    mng_put_uint32 (pRawdata+4,  pCHRM->iWhitepointy);
+    mng_put_uint32 (pRawdata+8,  pCHRM->iRedx);
+    mng_put_uint32 (pRawdata+12, pCHRM->iRedy);
+    mng_put_uint32 (pRawdata+16, pCHRM->iGreenx);
+    mng_put_uint32 (pRawdata+20, pCHRM->iGreeny);
+    mng_put_uint32 (pRawdata+24, pCHRM->iBluex);
+    mng_put_uint32 (pRawdata+28, pCHRM->iBluey);
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pCHRM->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_CHRM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_srgb)
+{
+  mng_srgbp   pSRGB;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SRGB, MNG_LC_START);
+#endif
+
+  pSRGB = (mng_srgbp)pChunk;           /* address the proper chunk */
+
+  if (pSRGB->bEmpty)                   /* write empty ? */
+    iRetcode = write_raw_chunk (pData, pSRGB->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 1;
+                                       /* fill the buffer */
+    *pRawdata = pSRGB->iRenderingintent;
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pSRGB->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SRGB, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+WRITE_CHUNK (mng_write_iccp)
+{
+  mng_iccpp   pICCP;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint8p  pTemp;
+  mng_uint8p  pBuf = 0;
+  mng_uint32  iBuflen;
+  mng_uint32  iReallen;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ICCP, MNG_LC_START);
+#endif
+
+  pICCP = (mng_iccpp)pChunk;           /* address the proper chunk */
+
+  if (pICCP->bEmpty)                   /* write empty ? */
+    iRetcode = write_raw_chunk (pData, pICCP->sHeader.iChunkname, 0, 0);
+  else
+  {                                    /* compress the profile */
+    iRetcode = deflate_buffer (pData, pICCP->pProfile, pICCP->iProfilesize,
+                               &pBuf, &iBuflen, &iReallen);
+
+    if (!iRetcode)                     /* still oke ? */
+    {
+      pRawdata = pData->pWritebuf+8;   /* init output buffer & size */
+      iRawlen  = pICCP->iNamesize + 2 + iReallen;
+                                       /* requires large buffer ? */
+      if (iRawlen > pData->iWritebufsize)
+        MNG_ALLOC (pData, pRawdata, iRawlen);
+
+      pTemp = pRawdata;                /* fill the buffer */
+
+      if (pICCP->iNamesize)
+      {
+        MNG_COPY (pTemp, pICCP->zName, pICCP->iNamesize);
+        pTemp += pICCP->iNamesize;
+      }
+
+      *pTemp     = 0;
+      *(pTemp+1) = pICCP->iCompression;
+      pTemp += 2;
+
+      if (iReallen)
+        MNG_COPY (pTemp, pBuf, iReallen);
+                                       /* and write it */
+      iRetcode = write_raw_chunk (pData, pICCP->sHeader.iChunkname,
+                                  iRawlen, pRawdata);
+                                       /* drop the temp buffer ? */
+      if (iRawlen > pData->iWritebufsize)
+        MNG_FREEX (pData, pRawdata, iRawlen);
+
+    }
+
+    MNG_FREEX (pData, pBuf, iBuflen);  /* always drop the extra buffer */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ICCP, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tEXt
+WRITE_CHUNK (mng_write_text)
+{
+  mng_textp   pTEXT;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint8p  pTemp;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_TEXT, MNG_LC_START);
+#endif
+
+  pTEXT = (mng_textp)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = pTEXT->iKeywordsize + 1 + pTEXT->iTextsize;
+                                       /* requires large buffer ? */
+  if (iRawlen > pData->iWritebufsize)
+    MNG_ALLOC (pData, pRawdata, iRawlen);
+
+  pTemp = pRawdata;                    /* fill the buffer */
+
+  if (pTEXT->iKeywordsize)
+  {
+    MNG_COPY (pTemp, pTEXT->zKeyword, pTEXT->iKeywordsize);
+    pTemp += pTEXT->iKeywordsize;
+  }
+
+  *pTemp = 0;
+  pTemp += 1;
+
+  if (pTEXT->iTextsize)
+    MNG_COPY (pTemp, pTEXT->zText, pTEXT->iTextsize);
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pTEXT->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+  if (iRawlen > pData->iWritebufsize)  /* drop the temp buffer ? */
+    MNG_FREEX (pData, pRawdata, iRawlen);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_TEXT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_zTXt
+WRITE_CHUNK (mng_write_ztxt)
+{
+  mng_ztxtp   pZTXT;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint8p  pTemp;
+  mng_uint8p  pBuf = 0;
+  mng_uint32  iBuflen;
+  mng_uint32  iReallen;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ZTXT, MNG_LC_START);
+#endif
+
+  pZTXT = (mng_ztxtp)pChunk;           /* address the proper chunk */
+                                       /* compress the text */
+  iRetcode = deflate_buffer (pData, (mng_uint8p)pZTXT->zText, pZTXT->iTextsize,
+                             &pBuf, &iBuflen, &iReallen);
+
+  if (!iRetcode)                       /* all ok ? */
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = pZTXT->iKeywordsize + 2 + iReallen;
+                                       /* requires large buffer ? */
+    if (iRawlen > pData->iWritebufsize)
+      MNG_ALLOC (pData, pRawdata, iRawlen);
+
+    pTemp = pRawdata;                  /* fill the buffer */
+
+    if (pZTXT->iKeywordsize)
+    {
+      MNG_COPY (pTemp, pZTXT->zKeyword, pZTXT->iKeywordsize);
+      pTemp += pZTXT->iKeywordsize;
+    }
+
+    *pTemp = 0;                        /* terminator zero */
+    pTemp++;
+    *pTemp = 0;                        /* compression type */
+    pTemp++;
+
+    if (iReallen)
+      MNG_COPY (pTemp, pBuf, iReallen);
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pZTXT->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+                                       /* drop the temp buffer ? */
+    if (iRawlen > pData->iWritebufsize)
+      MNG_FREEX (pData, pRawdata, iRawlen);
+
+  }
+
+  MNG_FREEX (pData, pBuf, iBuflen);    /* always drop the compression buffer */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ZTXT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iTXt
+WRITE_CHUNK (mng_write_itxt)
+{
+  mng_itxtp   pITXT;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint8p  pTemp;
+  mng_uint8p  pBuf = 0;
+  mng_uint32  iBuflen;
+  mng_uint32  iReallen;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ITXT, MNG_LC_START);
+#endif
+
+  pITXT = (mng_itxtp)pChunk;           /* address the proper chunk */
+
+  if (pITXT->iCompressionflag)         /* compress the text */
+    iRetcode = deflate_buffer (pData, (mng_uint8p)pITXT->zText, pITXT->iTextsize,
+                               &pBuf, &iBuflen, &iReallen);
+  else
+    iRetcode = MNG_NOERROR;
+
+  if (!iRetcode)                       /* all ok ? */
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = pITXT->iKeywordsize + pITXT->iLanguagesize +
+               pITXT->iTranslationsize + 5;
+
+    if (pITXT->iCompressionflag)
+      iRawlen = iRawlen + iReallen;
+    else
+      iRawlen = iRawlen + pITXT->iTextsize;
+                                       /* requires large buffer ? */
+    if (iRawlen > pData->iWritebufsize)
+      MNG_ALLOC (pData, pRawdata, iRawlen);
+
+    pTemp = pRawdata;                  /* fill the buffer */
+
+    if (pITXT->iKeywordsize)
+    {
+      MNG_COPY (pTemp, pITXT->zKeyword, pITXT->iKeywordsize);
+      pTemp += pITXT->iKeywordsize;
+    }
+
+    *pTemp = 0;
+    pTemp++;
+    *pTemp = pITXT->iCompressionflag;
+    pTemp++;
+    *pTemp = pITXT->iCompressionmethod;
+    pTemp++;
+
+    if (pITXT->iLanguagesize)
+    {
+      MNG_COPY (pTemp, pITXT->zLanguage, pITXT->iLanguagesize);
+      pTemp += pITXT->iLanguagesize;
+    }
+
+    *pTemp = 0;
+    pTemp++;
+
+    if (pITXT->iTranslationsize)
+    {
+      MNG_COPY (pTemp, pITXT->zTranslation, pITXT->iTranslationsize);
+      pTemp += pITXT->iTranslationsize;
+    }
+
+    *pTemp = 0;
+    pTemp++;
+
+    if (pITXT->iCompressionflag)
+    {
+      if (iReallen)
+        MNG_COPY (pTemp, pBuf, iReallen);
+    }
+    else
+    {
+      if (pITXT->iTextsize)
+        MNG_COPY (pTemp, pITXT->zText, pITXT->iTextsize);
+    }
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pITXT->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+                                       /* drop the temp buffer ? */
+    if (iRawlen > pData->iWritebufsize)
+      MNG_FREEX (pData, pRawdata, iRawlen);
+
+  }
+
+  MNG_FREEX (pData, pBuf, iBuflen);    /* always drop the compression buffer */
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ITXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_bKGD
+WRITE_CHUNK (mng_write_bkgd)
+{
+  mng_bkgdp   pBKGD;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_BKGD, MNG_LC_START);
+#endif
+
+  pBKGD = (mng_bkgdp)pChunk;           /* address the proper chunk */
+
+  if (pBKGD->bEmpty)                   /* write empty ? */
+    iRetcode = write_raw_chunk (pData, pBKGD->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 0;                      /* and default size */
+
+    switch (pBKGD->iType)
+    {
+      case 0: {                        /* gray */
+                iRawlen = 2;           /* fill the size & output buffer */
+                mng_put_uint16 (pRawdata, pBKGD->iGray);
+
+                break;
+              }
+      case 2: {                        /* rgb */
+                iRawlen = 6;           /* fill the size & output buffer */
+                mng_put_uint16 (pRawdata,   pBKGD->iRed);
+                mng_put_uint16 (pRawdata+2, pBKGD->iGreen);
+                mng_put_uint16 (pRawdata+4, pBKGD->iBlue);
+
+                break;
+              }
+      case 3: {                        /* indexed */
+                iRawlen   = 1;         /* fill the size & output buffer */
+                *pRawdata = pBKGD->iIndex;
+
+                break;
+              }
+    }
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pBKGD->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_BKGD, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYs
+WRITE_CHUNK (mng_write_phys)
+{
+  mng_physp   pPHYS;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PHYS, MNG_LC_START);
+#endif
+
+  pPHYS = (mng_physp)pChunk;           /* address the proper chunk */
+
+  if (pPHYS->bEmpty)                   /* write empty ? */
+    iRetcode = write_raw_chunk (pData, pPHYS->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 9;
+                                       /* fill the output buffer */
+    mng_put_uint32 (pRawdata,   pPHYS->iSizex);
+    mng_put_uint32 (pRawdata+4, pPHYS->iSizey);
+
+    *(pRawdata+8) = pPHYS->iUnit;
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pPHYS->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PHYS, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sBIT
+WRITE_CHUNK (mng_write_sbit)
+{
+  mng_sbitp   pSBIT;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SBIT, MNG_LC_START);
+#endif
+
+  pSBIT = (mng_sbitp)pChunk;           /* address the proper chunk */
+
+  if (pSBIT->bEmpty)                   /* write empty ? */
+    iRetcode = write_raw_chunk (pData, pSBIT->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 0;                      /* and default size */
+
+    switch (pSBIT->iType)
+    {
+      case  0: {                       /* gray */
+                 iRawlen       = 1;    /* fill the size & output buffer */
+                 *pRawdata     = pSBIT->aBits[0];
+
+                 break;
+               }
+      case  2: {                       /* rgb */
+                 iRawlen       = 3;    /* fill the size & output buffer */
+                 *pRawdata     = pSBIT->aBits[0];
+                 *(pRawdata+1) = pSBIT->aBits[1];
+                 *(pRawdata+2) = pSBIT->aBits[2];
+
+                 break;
+               }
+      case  3: {                       /* indexed */
+                 iRawlen       = 3;    /* fill the size & output buffer */
+                 *pRawdata     = pSBIT->aBits[0];
+                 *pRawdata     = pSBIT->aBits[1];
+                 *pRawdata     = pSBIT->aBits[2];
+
+                 break;
+               }
+      case  4: {                       /* gray + alpha */
+                 iRawlen       = 2;    /* fill the size & output buffer */
+                 *pRawdata     = pSBIT->aBits[0];
+                 *(pRawdata+1) = pSBIT->aBits[1];
+
+                 break;
+               }
+      case  6: {                       /* rgb + alpha */
+                 iRawlen       = 4;    /* fill the size & output buffer */
+                 *pRawdata     = pSBIT->aBits[0];
+                 *(pRawdata+1) = pSBIT->aBits[1];
+                 *(pRawdata+2) = pSBIT->aBits[2];
+                 *(pRawdata+3) = pSBIT->aBits[3];
+
+                 break;
+               }
+      case 10: {                       /* jpeg gray */
+                 iRawlen       = 1;    /* fill the size & output buffer */
+                 *pRawdata     = pSBIT->aBits[0];
+
+                 break;
+               }
+      case 12: {                       /* jpeg rgb */
+                 iRawlen       = 3;    /* fill the size & output buffer */
+                 *pRawdata     = pSBIT->aBits[0];
+                 *(pRawdata+1) = pSBIT->aBits[1];
+                 *(pRawdata+2) = pSBIT->aBits[2];
+
+                 break;
+               }
+      case 14: {                       /* jpeg gray + alpha */
+                 iRawlen       = 2;    /* fill the size & output buffer */
+                 *pRawdata     = pSBIT->aBits[0];
+                 *(pRawdata+1) = pSBIT->aBits[1];
+
+                 break;
+               }
+      case 16: {                       /* jpeg rgb + alpha */
+                 iRawlen       = 4;    /* fill the size & output buffer */
+                 *pRawdata     = pSBIT->aBits[0];
+                 *(pRawdata+1) = pSBIT->aBits[1];
+                 *(pRawdata+2) = pSBIT->aBits[2];
+                 *(pRawdata+3) = pSBIT->aBits[3];
+
+                 break;
+               }
+    }
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pSBIT->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SBIT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+WRITE_CHUNK (mng_write_splt)
+{
+  mng_spltp   pSPLT;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint32  iEntrieslen;
+  mng_uint8p  pTemp;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SPLT, MNG_LC_START);
+#endif
+
+  pSPLT = (mng_spltp)pChunk;           /* address the proper chunk */
+
+  pRawdata    = pData->pWritebuf+8;    /* init output buffer & size */
+  iEntrieslen = ((pSPLT->iSampledepth >> 3) * 4 + 2) * pSPLT->iEntrycount;
+  iRawlen     = pSPLT->iNamesize + 2 + iEntrieslen;
+                                       /* requires large buffer ? */
+  if (iRawlen > pData->iWritebufsize)
+    MNG_ALLOC (pData, pRawdata, iRawlen);
+
+  pTemp = pRawdata;                    /* fill the buffer */
+
+  if (pSPLT->iNamesize)
+  {
+    MNG_COPY (pTemp, pSPLT->zName, pSPLT->iNamesize);
+    pTemp += pSPLT->iNamesize;
+  }
+
+  *pTemp     = 0;
+  *(pTemp+1) = pSPLT->iSampledepth;
+  pTemp += 2;
+
+  if (pSPLT->iEntrycount)
+    MNG_COPY (pTemp, pSPLT->pEntries, iEntrieslen);
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pSPLT->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+  if (iRawlen > pData->iWritebufsize)  /* drop the temp buffer ? */
+    MNG_FREEX (pData, pRawdata, iRawlen);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SPLT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_hIST
+WRITE_CHUNK (mng_write_hist)
+{
+  mng_histp   pHIST;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint8p  pTemp;
+  mng_uint32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_HIST, MNG_LC_START);
+#endif
+
+  pHIST = (mng_histp)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = pHIST->iEntrycount << 1;
+
+  pTemp    = pRawdata;                 /* fill the output buffer */
+
+  for (iX = 0; iX < pHIST->iEntrycount; iX++)
+  {
+    mng_put_uint16 (pTemp, pHIST->aEntries [iX]);
+    pTemp += 2;
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pHIST->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_HIST, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tIME
+WRITE_CHUNK (mng_write_time)
+{
+  mng_timep   pTIME;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_TIME, MNG_LC_START);
+#endif
+
+  pTIME = (mng_timep)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 7;
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata, pTIME->iYear);
+
+  *(pRawdata+2) = pTIME->iMonth;
+  *(pRawdata+3) = pTIME->iDay;
+  *(pRawdata+4) = pTIME->iHour;
+  *(pRawdata+5) = pTIME->iMinute;
+  *(pRawdata+6) = pTIME->iSecond;
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pTIME->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_TIME, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_mhdr)
+{
+  mng_mhdrp   pMHDR;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MHDR, MNG_LC_START);
+#endif
+
+  pMHDR = (mng_mhdrp)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 28;
+                                       /* fill the output buffer */
+  mng_put_uint32 (pRawdata,    pMHDR->iWidth);
+  mng_put_uint32 (pRawdata+4,  pMHDR->iHeight);
+  mng_put_uint32 (pRawdata+8,  pMHDR->iTicks);
+  mng_put_uint32 (pRawdata+12, pMHDR->iLayercount);
+  mng_put_uint32 (pRawdata+16, pMHDR->iFramecount);
+  mng_put_uint32 (pRawdata+20, pMHDR->iPlaytime);
+  mng_put_uint32 (pRawdata+24, pMHDR->iSimplicity);
+
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pMHDR->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MHDR, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_mend)
+{
+  mng_mendp   pMEND;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MEND, MNG_LC_START);
+#endif
+
+  pMEND = (mng_mendp)pChunk;           /* address the proper chunk */
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pMEND->sHeader.iChunkname, 0, 0);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MEND, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_loop)
+{
+  mng_loopp   pLOOP;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+  mng_uint8p  pTemp1;
+  mng_uint32p pTemp2;
+  mng_uint32  iX;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_LOOP, MNG_LC_START);
+#endif
+
+  pLOOP = (mng_loopp)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 5;
+                                       /* fill the output buffer */
+  *pRawdata = pLOOP->iLevel;
+  mng_put_uint32 (pRawdata+1,  pLOOP->iRepeat);
+
+  if (pLOOP->iTermination)
+  {
+    iRawlen++;
+    *(pRawdata+5) = pLOOP->iTermination;
+
+    if ((pLOOP->iCount) ||
+        (pLOOP->iItermin != 1) || (pLOOP->iItermax != 0x7FFFFFFFL))
+    {
+      iRawlen += 8;
+
+      mng_put_uint32 (pRawdata+6,  pLOOP->iItermin);
+      mng_put_uint32 (pRawdata+10, pLOOP->iItermax);
+
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+      if (pLOOP->iCount)
+      {
+        iRawlen += pLOOP->iCount * 4;
+
+        pTemp1 = pRawdata+14;
+        pTemp2 = pLOOP->pSignals;
+
+        for (iX = 0; iX < pLOOP->iCount; iX++)
+        {
+          mng_put_uint32 (pTemp1, *pTemp2);
+
+          pTemp1 += 4;
+          pTemp2++;
+        }
+      }
+#endif
+    }
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pLOOP->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_LOOP, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_endl)
+{
+  mng_endlp   pENDL;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ENDL, MNG_LC_START);
+#endif
+
+  pENDL     = (mng_endlp)pChunk;       /* address the proper chunk */
+
+  pRawdata  = pData->pWritebuf+8;      /* init output buffer & size */
+  iRawlen   = 1;
+
+  *pRawdata = pENDL->iLevel;           /* fill the output buffer */
+                                       /* and write it */
+  iRetcode  = write_raw_chunk (pData, pENDL->sHeader.iChunkname,
+                               iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ENDL, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_defi)
+{
+  mng_defip   pDEFI;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DEFI, MNG_LC_START);
+#endif
+
+  pDEFI = (mng_defip)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 2;
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata, pDEFI->iObjectid);
+
+  if ((pDEFI->iDonotshow) || (pDEFI->iConcrete) || (pDEFI->bHasloca) || (pDEFI->bHasclip))
+  {
+    iRawlen++;
+    *(pRawdata+2) = pDEFI->iDonotshow;
+
+    if ((pDEFI->iConcrete) || (pDEFI->bHasloca) || (pDEFI->bHasclip))
+    {
+      iRawlen++;
+      *(pRawdata+3) = pDEFI->iConcrete;
+
+      if ((pDEFI->bHasloca) || (pDEFI->bHasclip))
+      {
+        iRawlen += 8;
+
+        mng_put_uint32 (pRawdata+4, pDEFI->iXlocation);
+        mng_put_uint32 (pRawdata+8, pDEFI->iYlocation);
+
+        if (pDEFI->bHasclip)
+        {
+          iRawlen += 16;
+
+          mng_put_uint32 (pRawdata+12, pDEFI->iLeftcb);
+          mng_put_uint32 (pRawdata+16, pDEFI->iRightcb);
+          mng_put_uint32 (pRawdata+20, pDEFI->iTopcb);
+          mng_put_uint32 (pRawdata+24, pDEFI->iBottomcb);
+        }
+      }
+    }
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pDEFI->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DEFI, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_basi)
+{
+  mng_basip   pBASI;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_bool    bOpaque;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_BASI, MNG_LC_START);
+#endif
+
+  pBASI = (mng_basip)pChunk;           /* address the proper chunk */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+  if (pBASI->iBitdepth <= 8)           /* determine opacity alpha-field */
+#endif
+    bOpaque = (mng_bool)(pBASI->iAlpha == 0xFF);
+#ifndef MNG_NO_16BIT_SUPPORT
+  else
+    bOpaque = (mng_bool)(pBASI->iAlpha == 0xFFFF);
+#endif
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 13;
+                                       /* fill the output buffer */
+  mng_put_uint32 (pRawdata,   pBASI->iWidth);
+  mng_put_uint32 (pRawdata+4, pBASI->iHeight);
+
+  *(pRawdata+8)  = pBASI->iBitdepth;
+  *(pRawdata+9)  = pBASI->iColortype;
+  *(pRawdata+10) = pBASI->iCompression;
+  *(pRawdata+11) = pBASI->iFilter;
+  *(pRawdata+12) = pBASI->iInterlace;
+
+  if ((pBASI->iRed) || (pBASI->iGreen) || (pBASI->iBlue) ||
+      (!bOpaque) || (pBASI->iViewable))
+  {
+    iRawlen += 6;
+    mng_put_uint16 (pRawdata+13, pBASI->iRed);
+    mng_put_uint16 (pRawdata+15, pBASI->iGreen);
+    mng_put_uint16 (pRawdata+17, pBASI->iBlue);
+
+    if ((!bOpaque) || (pBASI->iViewable))
+    {
+      iRawlen += 2;
+      mng_put_uint16 (pRawdata+19, pBASI->iAlpha);
+
+      if (pBASI->iViewable)
+      {
+        iRawlen++;
+        *(pRawdata+21) = pBASI->iViewable;
+      }
+    }
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pBASI->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_BASI, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_clon)
+{
+  mng_clonp   pCLON;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_CLON, MNG_LC_START);
+#endif
+
+  pCLON = (mng_clonp)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 4;
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata,   pCLON->iSourceid);
+  mng_put_uint16 (pRawdata+2, pCLON->iCloneid);
+
+  if ((pCLON->iClonetype) || (pCLON->iDonotshow) || (pCLON->iConcrete) || (pCLON->bHasloca))
+  {
+    iRawlen++;
+    *(pRawdata+4) = pCLON->iClonetype;
+
+    if ((pCLON->iDonotshow) || (pCLON->iConcrete) || (pCLON->bHasloca))
+    {
+      iRawlen++;
+      *(pRawdata+5) = pCLON->iDonotshow;
+
+      if ((pCLON->iConcrete) || (pCLON->bHasloca))
+      {
+        iRawlen++;
+        *(pRawdata+6) = pCLON->iConcrete;
+
+        if (pCLON->bHasloca)
+        {
+          iRawlen += 9;
+          *(pRawdata+7) = pCLON->iLocationtype;
+          mng_put_int32 (pRawdata+8,  pCLON->iLocationx);
+          mng_put_int32 (pRawdata+12, pCLON->iLocationy);
+        }
+      }
+    }
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pCLON->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_CLON, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+WRITE_CHUNK (mng_write_past)
+{
+  mng_pastp        pPAST;
+  mng_uint8p       pRawdata;
+  mng_uint32       iRawlen;
+  mng_retcode      iRetcode;
+  mng_past_sourcep pSource;
+  mng_uint32       iX;
+  mng_uint8p       pTemp;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PAST, MNG_LC_START);
+#endif
+
+  pPAST = (mng_pastp)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 11 + (30 * pPAST->iCount);
+                                       /* requires large buffer ? */
+  if (iRawlen > pData->iWritebufsize)
+    MNG_ALLOC (pData, pRawdata, iRawlen);
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata,   pPAST->iDestid);
+
+  *(pRawdata+2) = pPAST->iTargettype;
+
+  mng_put_int32  (pRawdata+3, pPAST->iTargetx);
+  mng_put_int32  (pRawdata+7, pPAST->iTargety);
+
+  pTemp   = pRawdata+11;
+  pSource = pPAST->pSources;
+
+  for (iX = 0; iX < pPAST->iCount; iX++)
+  {
+    mng_put_uint16 (pTemp,    pSource->iSourceid);
+
+    *(pTemp+2)  = pSource->iComposition;
+    *(pTemp+3)  = pSource->iOrientation;
+    *(pTemp+4)  = pSource->iOffsettype;
+
+    mng_put_int32  (pTemp+5,  pSource->iOffsetx);
+    mng_put_int32  (pTemp+9,  pSource->iOffsety);
+
+    *(pTemp+13) = pSource->iBoundarytype;
+
+    mng_put_int32  (pTemp+14, pSource->iBoundaryl);
+    mng_put_int32  (pTemp+18, pSource->iBoundaryr);
+    mng_put_int32  (pTemp+22, pSource->iBoundaryt);
+    mng_put_int32  (pTemp+26, pSource->iBoundaryb);
+
+    pSource++;
+    pTemp += 30;
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pPAST->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+                                       /* free temporary buffer ? */
+  if (iRawlen > pData->iWritebufsize)
+    MNG_FREEX (pData, pRawdata, iRawlen);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PAST, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_disc)
+{
+  mng_discp        pDISC;
+  mng_uint8p       pRawdata;
+  mng_uint32       iRawlen;
+  mng_retcode      iRetcode;
+  mng_uint32       iX;
+  mng_uint8p       pTemp1;
+  mng_uint16p      pTemp2;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DISC, MNG_LC_START);
+#endif
+
+  pDISC    = (mng_discp)pChunk;        /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = pDISC->iCount << 1;
+
+  pTemp1   = pRawdata;                 /* fill the output buffer */
+  pTemp2   = pDISC->pObjectids;
+
+  for (iX = 0; iX < pDISC->iCount; iX++)
+  {
+    mng_put_uint16 (pTemp1, *pTemp2);
+
+    pTemp2++;
+    pTemp1 += 2;
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pDISC->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DISC, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_back)
+{
+  mng_backp   pBACK;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_BACK, MNG_LC_START);
+#endif
+
+  pBACK = (mng_backp)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 6;
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata,   pBACK->iRed);
+  mng_put_uint16 (pRawdata+2, pBACK->iGreen);
+  mng_put_uint16 (pRawdata+4, pBACK->iBlue);
+
+  if ((pBACK->iMandatory) || (pBACK->iImageid) || (pBACK->iTile))
+  {
+    iRawlen++;
+    *(pRawdata+6) = pBACK->iMandatory;
+
+    if ((pBACK->iImageid) || (pBACK->iTile))
+    {
+      iRawlen += 2;
+      mng_put_uint16 (pRawdata+7, pBACK->iImageid);
+
+      if (pBACK->iTile)
+      {
+        iRawlen++;
+        *(pRawdata+9) = pBACK->iTile;
+      }
+    }
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pBACK->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_BACK, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_fram)
+{
+  mng_framp   pFRAM;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint8p  pTemp;
+  mng_uint32p pTemp2;
+  mng_uint32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_FRAM, MNG_LC_START);
+#endif
+
+  pFRAM = (mng_framp)pChunk;           /* address the proper chunk */
+
+  if (pFRAM->bEmpty)                   /* empty ? */
+    iRetcode = write_raw_chunk (pData, pFRAM->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 1;
+                                       /* fill the output buffer */
+    *pRawdata = pFRAM->iMode;
+
+    if ((pFRAM->iNamesize      ) ||
+        (pFRAM->iChangedelay   ) || (pFRAM->iChangetimeout) ||
+        (pFRAM->iChangeclipping) || (pFRAM->iChangesyncid )    )
+    {
+      if (pFRAM->iNamesize)
+        MNG_COPY (pRawdata+1, pFRAM->zName, pFRAM->iNamesize);
+
+      iRawlen += pFRAM->iNamesize;
+      pTemp = pRawdata + pFRAM->iNamesize + 1;
+
+      if ((pFRAM->iChangedelay   ) || (pFRAM->iChangetimeout) ||
+          (pFRAM->iChangeclipping) || (pFRAM->iChangesyncid )    )
+      {
+        *pTemp     = 0;
+        *(pTemp+1) = pFRAM->iChangedelay;
+        *(pTemp+2) = pFRAM->iChangetimeout;
+        *(pTemp+3) = pFRAM->iChangeclipping;
+        *(pTemp+4) = pFRAM->iChangesyncid;
+
+        iRawlen += 5;
+        pTemp   += 5;
+
+        if (pFRAM->iChangedelay)
+        {
+          mng_put_uint32 (pTemp, pFRAM->iDelay);
+          iRawlen += 4;
+          pTemp   += 4;
+        }
+
+        if (pFRAM->iChangetimeout)
+        {
+          mng_put_uint32 (pTemp, pFRAM->iTimeout);
+          iRawlen += 4;
+          pTemp   += 4;
+        }
+
+        if (pFRAM->iChangeclipping)
+        {
+          *pTemp = pFRAM->iBoundarytype;
+
+          mng_put_uint32 (pTemp+1,  pFRAM->iBoundaryl);
+          mng_put_uint32 (pTemp+5,  pFRAM->iBoundaryr);
+          mng_put_uint32 (pTemp+9,  pFRAM->iBoundaryt);
+          mng_put_uint32 (pTemp+13, pFRAM->iBoundaryb);
+
+          iRawlen += 17;
+          pTemp   += 17;
+        }
+
+        if (pFRAM->iChangesyncid)
+        {
+          iRawlen += pFRAM->iCount * 4;
+          pTemp2 = pFRAM->pSyncids;
+
+          for (iX = 0; iX < pFRAM->iCount; iX++)
+          {
+            mng_put_uint32 (pTemp, *pTemp2);
+
+            pTemp2++;
+            pTemp += 4;
+          }  
+        }
+      }
+    }
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pFRAM->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_FRAM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_move)
+{
+  mng_movep   pMOVE;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MOVE, MNG_LC_START);
+#endif
+
+  pMOVE = (mng_movep)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 13;
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata,   pMOVE->iFirstid);
+  mng_put_uint16 (pRawdata+2, pMOVE->iLastid);
+
+  *(pRawdata+4) = pMOVE->iMovetype;
+
+  mng_put_int32  (pRawdata+5, pMOVE->iMovex);
+  mng_put_int32  (pRawdata+9, pMOVE->iMovey);
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pMOVE->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MOVE, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_clip)
+{
+  mng_clipp   pCLIP;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_CLIP, MNG_LC_START);
+#endif
+
+  pCLIP = (mng_clipp)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 21;
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata,    pCLIP->iFirstid);
+  mng_put_uint16 (pRawdata+2,  pCLIP->iLastid);
+
+  *(pRawdata+4) = pCLIP->iCliptype;
+
+  mng_put_int32  (pRawdata+5,  pCLIP->iClipl);
+  mng_put_int32  (pRawdata+9,  pCLIP->iClipr);
+  mng_put_int32  (pRawdata+13, pCLIP->iClipt);
+  mng_put_int32  (pRawdata+17, pCLIP->iClipb);
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pCLIP->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_CLIP, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_show)
+{
+  mng_showp   pSHOW;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SHOW, MNG_LC_START);
+#endif
+
+  pSHOW = (mng_showp)pChunk;           /* address the proper chunk */
+
+  if (pSHOW->bEmpty)                   /* empty ? */
+    iRetcode = write_raw_chunk (pData, pSHOW->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 2;
+                                       /* fill the output buffer */
+    mng_put_uint16 (pRawdata, pSHOW->iFirstid);
+
+    if ((pSHOW->iLastid != pSHOW->iFirstid) || (pSHOW->iMode))
+    {
+      iRawlen += 2;
+      mng_put_uint16 (pRawdata+2, pSHOW->iLastid);
+
+      if (pSHOW->iMode)
+      {
+        iRawlen++;
+        *(pRawdata+4) = pSHOW->iMode;
+      }
+    }
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pSHOW->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SHOW, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_term)
+{
+  mng_termp   pTERM;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_TERM, MNG_LC_START);
+#endif
+
+  pTERM     = (mng_termp)pChunk;       /* address the proper chunk */
+
+  pRawdata  = pData->pWritebuf+8;      /* init output buffer & size */
+  iRawlen   = 1;
+
+  *pRawdata = pTERM->iTermaction;      /* fill the output buffer */
+
+  if (pTERM->iTermaction == 3)
+  {
+    iRawlen       = 10;
+    *(pRawdata+1) = pTERM->iIteraction;
+
+    mng_put_uint32 (pRawdata+2, pTERM->iDelay);
+    mng_put_uint32 (pRawdata+6, pTERM->iItermax);
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pTERM->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_TERM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+WRITE_CHUNK (mng_write_save)
+{
+  mng_savep       pSAVE;
+  mng_uint8p      pRawdata;
+  mng_uint32      iRawlen;
+  mng_retcode     iRetcode;
+  mng_save_entryp pEntry;
+  mng_uint32      iEntrysize;
+  mng_uint8p      pTemp;
+  mng_uint32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SAVE, MNG_LC_START);
+#endif
+
+  pSAVE = (mng_savep)pChunk;           /* address the proper chunk */
+
+  if (pSAVE->bEmpty)                   /* empty ? */
+    iRetcode = write_raw_chunk (pData, pSAVE->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata  = pData->pWritebuf+8;    /* init output buffer & size */
+    iRawlen   = 1;
+
+    *pRawdata = pSAVE->iOffsettype;    /* fill the output buffer */
+
+    if (pSAVE->iOffsettype == 16)
+      iEntrysize = 25;
+    else
+      iEntrysize = 17;
+
+    pTemp  = pRawdata+1;
+    pEntry = pSAVE->pEntries;
+
+    for (iX = 0; iX < pSAVE->iCount; iX++)
+    {
+      if (iX)                          /* put separator null-byte, except the first */
+      {
+        *pTemp = 0;
+        pTemp++;
+        iRawlen++;
+      }
+
+      iRawlen += iEntrysize + pEntry->iNamesize;
+      *pTemp = pEntry->iEntrytype;
+
+      if (pSAVE->iOffsettype == 16)
+      {
+        mng_put_uint32 (pTemp+1,  pEntry->iOffset[0]);
+        mng_put_uint32 (pTemp+5,  pEntry->iOffset[1]);
+        mng_put_uint32 (pTemp+9,  pEntry->iStarttime[0]);
+        mng_put_uint32 (pTemp+13, pEntry->iStarttime[1]);
+        mng_put_uint32 (pTemp+17, pEntry->iLayernr);
+        mng_put_uint32 (pTemp+21, pEntry->iFramenr);
+
+        pTemp += 25;
+      }
+      else
+      {
+        mng_put_uint32 (pTemp+1,  pEntry->iOffset[1]);
+        mng_put_uint32 (pTemp+5,  pEntry->iStarttime[1]);
+        mng_put_uint32 (pTemp+9,  pEntry->iLayernr);
+        mng_put_uint32 (pTemp+13, pEntry->iFramenr);
+
+        pTemp += 17;
+      }
+
+      if (pEntry->iNamesize)
+      {
+        MNG_COPY (pTemp, pEntry->zName, pEntry->iNamesize);
+        pTemp += pEntry->iNamesize;
+      }
+
+      pEntry++;  
+    }
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pSAVE->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SAVE, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+WRITE_CHUNK (mng_write_seek)
+{
+  mng_seekp   pSEEK;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SEEK, MNG_LC_START);
+#endif
+
+  pSEEK    = (mng_seekp)pChunk;        /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = pSEEK->iNamesize;
+
+  if (iRawlen)                         /* fill the output buffer */
+    MNG_COPY (pRawdata, pSEEK->zName, iRawlen);
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pSEEK->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_SEEK, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_eXPI
+WRITE_CHUNK (mng_write_expi)
+{
+  mng_expip   pEXPI;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_EXPI, MNG_LC_START);
+#endif
+
+  pEXPI    = (mng_expip)pChunk;        /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 2 + pEXPI->iNamesize;
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata, pEXPI->iSnapshotid);
+
+  if (pEXPI->iNamesize)
+    MNG_COPY (pRawdata+2, pEXPI->zName, pEXPI->iNamesize);
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pEXPI->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_EXPI, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_fPRI
+WRITE_CHUNK (mng_write_fpri)
+{
+  mng_fprip   pFPRI;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_FPRI, MNG_LC_START);
+#endif
+
+  pFPRI         = (mng_fprip)pChunk;   /* address the proper chunk */
+
+  pRawdata      = pData->pWritebuf+8;  /* init output buffer & size */
+  iRawlen       = 2;
+
+  *pRawdata     = pFPRI->iDeltatype;   /* fill the output buffer */
+  *(pRawdata+1) = pFPRI->iPriority;
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pFPRI->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_FPRI, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+WRITE_CHUNK (mng_write_need)
+{
+  mng_needp   pNEED;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_NEED, MNG_LC_START);
+#endif
+
+  pNEED    = (mng_needp)pChunk;        /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = pNEED->iKeywordssize;
+                                       /* fill the output buffer */
+  if (pNEED->iKeywordssize)
+    MNG_COPY (pRawdata, pNEED->zKeywords, pNEED->iKeywordssize);
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pNEED->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_NEED, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYg
+WRITE_CHUNK (mng_write_phyg)
+{
+  mng_phygp   pPHYG;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PHYG, MNG_LC_START);
+#endif
+
+  pPHYG = (mng_phygp)pChunk;           /* address the proper chunk */
+
+  if (pPHYG->bEmpty)                   /* write empty ? */
+    iRetcode = write_raw_chunk (pData, pPHYG->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 9;
+                                       /* fill the output buffer */
+    mng_put_uint32 (pRawdata,   pPHYG->iSizex);
+    mng_put_uint32 (pRawdata+4, pPHYG->iSizey);
+
+    *(pRawdata+8) = pPHYG->iUnit;
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pPHYG->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PHYG, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+/* B004 */
+#ifdef MNG_INCLUDE_JNG
+/* B004 */
+WRITE_CHUNK (mng_write_jhdr)
+{
+  mng_jhdrp   pJHDR;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_JHDR, MNG_LC_START);
+#endif
+
+  pJHDR    = (mng_jhdrp)pChunk;        /* address the proper chunk */
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 16;
+                                       /* fill the output buffer */
+  mng_put_uint32 (pRawdata,   pJHDR->iWidth);
+  mng_put_uint32 (pRawdata+4, pJHDR->iHeight);
+
+  *(pRawdata+8)  = pJHDR->iColortype;
+  *(pRawdata+9)  = pJHDR->iImagesampledepth;
+  *(pRawdata+10) = pJHDR->iImagecompression;
+  *(pRawdata+11) = pJHDR->iImageinterlace;
+  *(pRawdata+12) = pJHDR->iAlphasampledepth;
+  *(pRawdata+13) = pJHDR->iAlphacompression;
+  *(pRawdata+14) = pJHDR->iAlphafilter;
+  *(pRawdata+15) = pJHDR->iAlphainterlace;
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pJHDR->sHeader.iChunkname, iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_JHDR, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#else
+#define write_jhdr 0
+/* B004 */
+#endif /* MNG_INCLUDE_JNG */
+/* B004 */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+WRITE_CHUNK (mng_write_jdaa)
+{
+  mng_jdatp   pJDAA;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_JDAA, MNG_LC_START);
+#endif
+
+  pJDAA = (mng_jdaap)pChunk;           /* address the proper chunk */
+
+  if (pJDAA->bEmpty)                   /* and write it */
+    iRetcode = write_raw_chunk (pData, pJDAA->sHeader.iChunkname, 0, 0);
+  else
+    iRetcode = write_raw_chunk (pData, pJDAA->sHeader.iChunkname,
+                                pJDAA->iDatasize, pJDAA->pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_JDAA, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#else
+#define write_jdaa 0
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+/* B004 */
+#ifdef MNG_INCLUDE_JNG
+/* B004 */
+WRITE_CHUNK (mng_write_jdat)
+{
+  mng_jdatp   pJDAT;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_JDAT, MNG_LC_START);
+#endif
+
+  pJDAT = (mng_jdatp)pChunk;           /* address the proper chunk */
+
+  if (pJDAT->bEmpty)                   /* and write it */
+    iRetcode = write_raw_chunk (pData, pJDAT->sHeader.iChunkname, 0, 0);
+  else
+    iRetcode = write_raw_chunk (pData, pJDAT->sHeader.iChunkname,
+                                pJDAT->iDatasize, pJDAT->pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_JDAT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#else
+#define write_jdat 0
+/* B004 */
+#endif /* MNG_INCLUDE_JNG */
+/* B004 */
+
+/* ************************************************************************** */
+
+/* B004 */
+#ifdef MNG_INCLUDE_JNG
+/* B004 */
+WRITE_CHUNK (mng_write_jsep)
+{
+  mng_jsepp   pJSEP;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_JSEP, MNG_LC_START);
+#endif
+
+  pJSEP = (mng_jsepp)pChunk;           /* address the proper chunk */
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pJSEP->sHeader.iChunkname, 0, 0);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_JSEP, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#else
+#define write_jsep 0
+/* B004 */
+#endif /* MNG_INCLUDE_JNG */
+/* B004 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+WRITE_CHUNK (mng_write_dhdr)
+{
+  mng_dhdrp   pDHDR;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DHDR, MNG_LC_START);
+#endif
+
+  pDHDR    = (mng_dhdrp)pChunk;        /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 4;
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata, pDHDR->iObjectid);
+
+  *(pRawdata+2) = pDHDR->iImagetype;
+  *(pRawdata+3) = pDHDR->iDeltatype;
+
+  if (pDHDR->iDeltatype != 7)
+  {
+    iRawlen += 8;
+    mng_put_uint32 (pRawdata+4, pDHDR->iBlockwidth);
+    mng_put_uint32 (pRawdata+8, pDHDR->iBlockheight);
+
+    if (pDHDR->iDeltatype != 0)
+    {
+      iRawlen += 8;
+      mng_put_uint32 (pRawdata+12, pDHDR->iBlockx);
+      mng_put_uint32 (pRawdata+16, pDHDR->iBlocky);
+    }
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pDHDR->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DHDR, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+WRITE_CHUNK (mng_write_prom)
+{
+  mng_promp   pPROM;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PROM, MNG_LC_START);
+#endif
+
+  pPROM    = (mng_promp)pChunk;        /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 3;
+
+  *pRawdata     = pPROM->iColortype;   /* fill the output buffer */
+  *(pRawdata+1) = pPROM->iSampledepth;
+  *(pRawdata+2) = pPROM->iFilltype;
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pPROM->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PROM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+WRITE_CHUNK (mng_write_ipng)
+{
+  mng_ipngp   pIPNG;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IPNG, MNG_LC_START);
+#endif
+
+  pIPNG = (mng_ipngp)pChunk;           /* address the proper chunk */
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pIPNG->sHeader.iChunkname, 0, 0);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IPNG, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+WRITE_CHUNK (mng_write_pplt)
+{
+  mng_ppltp       pPPLT;
+  mng_uint8p      pRawdata;
+  mng_uint32      iRawlen;
+  mng_retcode     iRetcode;
+  mng_pplt_entryp pEntry;
+  mng_uint8p      pTemp;
+  mng_uint32      iX;
+  mng_bool        bHasgroup;
+  mng_uint8p      pLastid = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PPLT, MNG_LC_START);
+#endif
+
+  pPPLT = (mng_ppltp)pChunk;           /* address the proper chunk */
+
+  pRawdata  = pData->pWritebuf+8;      /* init output buffer & size */
+  iRawlen   = 1;
+
+  *pRawdata = pPPLT->iDeltatype;       /* fill the output buffer */
+
+  pTemp     = pRawdata+1;
+  bHasgroup = MNG_FALSE;
+
+  for (iX = 0; iX < pPPLT->iCount; iX++)
+  {
+    pEntry = &pPPLT->aEntries[iX];
+    
+    if (pEntry->bUsed)                 /* valid entry ? */
+    {
+      if (!bHasgroup)                  /* start a new group ? */
+      {
+        bHasgroup  = MNG_TRUE;
+        pLastid    = pTemp+1;
+
+        *pTemp     = (mng_uint8)iX;
+        *(pTemp+1) = 0;
+
+        pTemp += 2;
+        iRawlen += 2;
+      }
+
+      switch (pPPLT->iDeltatype)       /* add group-entry depending on type */
+      {
+        case 0: ;
+        case 1: {
+                  *pTemp     = pEntry->iRed;
+                  *(pTemp+1) = pEntry->iGreen;
+                  *(pTemp+2) = pEntry->iBlue;
+
+                  pTemp += 3;
+                  iRawlen += 3;
+
+                  break;
+                }
+
+        case 2: ;
+        case 3: {
+                  *pTemp     = pEntry->iAlpha;
+
+                  pTemp++;
+                  iRawlen++;
+
+                  break;
+                }
+
+        case 4: ;
+        case 5: {
+                  *pTemp     = pEntry->iRed;
+                  *(pTemp+1) = pEntry->iGreen;
+                  *(pTemp+2) = pEntry->iBlue;
+                  *(pTemp+3) = pEntry->iAlpha;
+
+                  pTemp += 4;
+                  iRawlen += 4;
+
+                  break;
+                }
+
+      }
+    }
+    else
+    {
+      if (bHasgroup)                   /* finish off a group ? */
+        *pLastid = (mng_uint8)(iX-1);
+
+      bHasgroup = MNG_FALSE;
+    }
+  }
+
+  if (bHasgroup)                       /* last group unfinished ? */
+    *pLastid = (mng_uint8)(pPPLT->iCount-1);
+                                       /* write the output buffer */
+  iRetcode = write_raw_chunk (pData, pPPLT->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_PPLT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+WRITE_CHUNK (mng_write_ijng)
+{
+  mng_ijngp   pIJNG;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IJNG, MNG_LC_START);
+#endif
+
+  pIJNG = (mng_ijngp)pChunk;           /* address the proper chunk */
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pIJNG->sHeader.iChunkname, 0, 0);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_IJNG, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+WRITE_CHUNK (mng_write_drop)
+{
+  mng_dropp        pDROP;
+  mng_uint8p       pRawdata;
+  mng_uint32       iRawlen;
+  mng_retcode      iRetcode;
+  mng_uint32       iX;
+  mng_uint8p       pTemp1;
+  mng_chunkidp     pTemp2;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DROP, MNG_LC_START);
+#endif
+
+  pDROP    = (mng_dropp)pChunk;        /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = pDROP->iCount << 2;
+
+  pTemp1   = pRawdata;                 /* fill the output buffer */
+  pTemp2   = pDROP->pChunknames;
+
+  for (iX = 0; iX < pDROP->iCount; iX++)
+  {
+    mng_put_uint32 (pTemp1, (mng_uint32)*pTemp2);
+
+    pTemp2++;
+    pTemp1 += 4;
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pDROP->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DROP, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+WRITE_CHUNK (mng_write_dbyk)
+{
+  mng_dbykp   pDBYK;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DBYK, MNG_LC_START);
+#endif
+
+  pDBYK = (mng_dbykp)pChunk;           /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 5 + pDBYK->iKeywordssize;
+                                       /* fill the output buffer */
+  mng_put_uint32 (pRawdata, pDBYK->iChunkname);
+  *(pRawdata+4) = pDBYK->iPolarity;
+
+  if (pDBYK->iKeywordssize)
+    MNG_COPY (pRawdata+5, pDBYK->zKeywords, pDBYK->iKeywordssize);
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pDBYK->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_DBYK, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+WRITE_CHUNK (mng_write_ordr)
+{
+  mng_ordrp       pORDR;
+  mng_uint8p      pRawdata;
+  mng_uint32      iRawlen;
+  mng_retcode     iRetcode;
+  mng_uint8p      pTemp;
+  mng_ordr_entryp pEntry;
+  mng_uint32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ORDR, MNG_LC_START);
+#endif
+
+  pORDR    = (mng_ordrp)pChunk;        /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = pORDR->iCount * 5;
+
+  pTemp    = pRawdata;                 /* fill the output buffer */
+  pEntry   = pORDR->pEntries;
+
+  for (iX = 0; iX < pORDR->iCount; iX++)
+  {
+    mng_put_uint32 (pTemp, pEntry->iChunkname);
+    *(pTemp+4) = pEntry->iOrdertype;
+    pTemp += 5;
+    pEntry++;
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pORDR->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_ORDR, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_magn)
+{
+  mng_magnp   pMAGN;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MAGN, MNG_LC_START);
+#endif
+
+  pMAGN    = (mng_magnp)pChunk;        /* address the proper chunk */
+
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 18;
+                                       /* fill the output buffer */
+  mng_put_uint16 (pRawdata,    pMAGN->iFirstid);
+  mng_put_uint16 (pRawdata+2,  pMAGN->iLastid);
+  *(pRawdata+4) = pMAGN->iMethodX;
+  mng_put_uint16 (pRawdata+5,  pMAGN->iMX);
+  mng_put_uint16 (pRawdata+7,  pMAGN->iMY);
+  mng_put_uint16 (pRawdata+9,  pMAGN->iML);
+  mng_put_uint16 (pRawdata+11, pMAGN->iMR);
+  mng_put_uint16 (pRawdata+13, pMAGN->iMT);
+  mng_put_uint16 (pRawdata+15, pMAGN->iMB);
+  *(pRawdata+17) = pMAGN->iMethodY;
+                                       /* optimize length */
+  if (pMAGN->iMethodY == pMAGN->iMethodX)
+  {
+    iRawlen--;
+
+    if (pMAGN->iMB == pMAGN->iMY)
+    {
+      iRawlen -= 2;
+
+      if (pMAGN->iMT == pMAGN->iMY)
+      {
+        iRawlen -= 2;
+
+        if (pMAGN->iMR == pMAGN->iMX)
+        {
+          iRawlen -= 2;
+
+          if (pMAGN->iML == pMAGN->iMX)
+          {
+            iRawlen -= 2;
+
+            if (pMAGN->iMY == pMAGN->iMX)
+            {
+              iRawlen -= 2;
+
+              if (pMAGN->iMX == 1)
+              {
+                iRawlen -= 2;
+
+                if (pMAGN->iMethodX == 0)
+                {
+                  iRawlen--;
+
+                  if (pMAGN->iLastid == pMAGN->iFirstid)
+                  {
+                    iRawlen -= 2;
+
+                    if (pMAGN->iFirstid == 0)
+                      iRawlen = 0;
+
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pMAGN->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MAGN, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+WRITE_CHUNK (mng_write_mpng)
+{
+  mng_mpngp   pMPNG;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+  mng_uint8p  pBuf = 0;
+  mng_uint32  iBuflen;
+  mng_uint32  iReallen;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MPNG, MNG_LC_START);
+#endif
+
+  pMPNG = (mng_mpngp)pChunk;           /* address the proper chunk */
+                                       /* compress the frame structures */
+  iRetcode = deflate_buffer (pData, (mng_uint8p)pMPNG->pFrames, pMPNG->iFramessize,
+                             &pBuf, &iBuflen, &iReallen);
+
+  if (!iRetcode)                       /* all ok ? */
+  {
+    pRawdata = pData->pWritebuf+8;     /* init output buffer & size */
+    iRawlen  = 15 + iReallen;
+                                       /* requires large buffer ? */
+    if (iRawlen > pData->iWritebufsize)
+      MNG_ALLOC (pData, pRawdata, iRawlen);
+                                       /* fill the buffer */
+    mng_put_uint32 (pRawdata,    pMPNG->iFramewidth);
+    mng_put_uint32 (pRawdata+4,  pMPNG->iFrameheight);
+    mng_put_uint16 (pRawdata+8,  pMPNG->iNumplays);
+    mng_put_uint16 (pRawdata+10, pMPNG->iTickspersec);
+    *(pRawdata+12) = pMPNG->iCompressionmethod;
+
+    if (iReallen)
+      MNG_COPY (pRawdata+13, pBuf, iReallen);
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pMPNG->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+                                       /* drop the temp buffer ? */
+    if (iRawlen > pData->iWritebufsize)
+      MNG_FREEX (pData, pRawdata, iRawlen);
+  }
+
+  MNG_FREEX (pData, pBuf, iBuflen);    /* always drop the compression buffer */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_MPNG, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+WRITE_CHUNK (mng_write_ahdr)
+{
+  mng_ahdrp   pAHDR;
+  mng_uint8p  pRawdata;
+  mng_uint32  iRawlen;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_AHDR, MNG_LC_START);
+#endif
+
+  pAHDR    = (mng_ahdrp)pChunk;        /* address the proper chunk */
+  pRawdata = pData->pWritebuf+8;       /* init output buffer & size */
+  iRawlen  = 22;
+                                       /* fill the buffer */
+  mng_put_uint32 (pRawdata,    pAHDR->iNumframes);
+  mng_put_uint32 (pRawdata+4,  pAHDR->iTickspersec);
+  mng_put_uint32 (pRawdata+8,  pAHDR->iNumplays);
+  mng_put_uint32 (pRawdata+12, pAHDR->iTilewidth);
+  mng_put_uint32 (pRawdata+16, pAHDR->iTileheight);
+  *(pRawdata+20) = pAHDR->iInterlace;
+  *(pRawdata+21) = pAHDR->iStillused;
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pAHDR->sHeader.iChunkname,
+                              iRawlen, pRawdata);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_AHDR, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+WRITE_CHUNK (mng_write_adat)
+{
+
+  /* TODO: something */
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_evNT
+WRITE_CHUNK (mng_write_evnt)
+{
+  mng_evntp       pEVNT;
+  mng_uint8p      pRawdata;
+  mng_uint32      iRawlen;
+  mng_retcode     iRetcode;
+  mng_evnt_entryp pEntry;
+  mng_uint8p      pTemp;
+  mng_uint32      iX;
+  mng_uint32      iNamesize;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_EVNT, MNG_LC_START);
+#endif
+
+  pEVNT = (mng_evntp)pChunk;           /* address the proper chunk */
+
+  if (!pEVNT->iCount)                  /* empty ? */
+    iRetcode = write_raw_chunk (pData, pEVNT->sHeader.iChunkname, 0, 0);
+  else
+  {
+    pRawdata  = pData->pWritebuf+8;    /* init output buffer & size */
+    iRawlen   = 0;
+    pTemp     = pRawdata;
+    pEntry    = pEVNT->pEntries;
+
+    for (iX = 0; iX < pEVNT->iCount; iX++)
+    {
+      if (iX)                          /* put separator null-byte, except the first */
+      {
+        *pTemp = 0;
+        pTemp++;
+        iRawlen++;
+      }
+
+      *pTemp     = pEntry->iEventtype;
+      *(pTemp+1) = pEntry->iMasktype;
+      pTemp   += 2;
+      iRawlen += 2;
+
+      switch (pEntry->iMasktype)
+      {
+        case 1 :
+          {
+            mng_put_int32 (pTemp, pEntry->iLeft);
+            mng_put_int32 (pTemp+4, pEntry->iRight);
+            mng_put_int32 (pTemp+8, pEntry->iTop);
+            mng_put_int32 (pTemp+12, pEntry->iBottom);
+            pTemp   += 16;
+            iRawlen += 16;
+            break;
+          }
+        case 2 :
+          {
+            mng_put_uint16 (pTemp, pEntry->iObjectid);
+            pTemp   += 2;
+            iRawlen += 2;
+            break;
+          }
+        case 3 :
+          {
+            mng_put_uint16 (pTemp, pEntry->iObjectid);
+            *(pTemp+2) = pEntry->iIndex;
+            pTemp   += 3;
+            iRawlen += 3;
+            break;
+          }
+        case 4 :
+          {
+            mng_put_int32 (pTemp, pEntry->iLeft);
+            mng_put_int32 (pTemp+4, pEntry->iRight);
+            mng_put_int32 (pTemp+8, pEntry->iTop);
+            mng_put_int32 (pTemp+12, pEntry->iBottom);
+            mng_put_uint16 (pTemp+16, pEntry->iObjectid);
+            pTemp   += 18;
+            iRawlen += 18;
+            break;
+          }
+        case 5 :
+          {
+            mng_put_int32 (pTemp, pEntry->iLeft);
+            mng_put_int32 (pTemp+4, pEntry->iRight);
+            mng_put_int32 (pTemp+8, pEntry->iTop);
+            mng_put_int32 (pTemp+12, pEntry->iBottom);
+            mng_put_uint16 (pTemp+16, pEntry->iObjectid);
+            *(pTemp+18) = pEntry->iIndex;
+            pTemp   += 19;
+            iRawlen += 19;
+            break;
+          }
+      }
+
+      iNamesize = pEntry->iSegmentnamesize;
+
+      if (iNamesize)
+      {
+        MNG_COPY (pTemp, pEntry->zSegmentname, iNamesize);
+        pTemp   += iNamesize;
+        iRawlen += iNamesize;
+      }
+
+      pEntry++;  
+    }
+                                       /* and write it */
+    iRetcode = write_raw_chunk (pData, pEVNT->sHeader.iChunkname,
+                                iRawlen, pRawdata);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_EVNT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+WRITE_CHUNK (mng_write_unknown)
+{
+  mng_unknown_chunkp pUnknown;
+  mng_retcode        iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_UNKNOWN, MNG_LC_START);
+#endif
+                                       /* address the proper chunk */
+  pUnknown = (mng_unknown_chunkp)pChunk;
+                                       /* and write it */
+  iRetcode = write_raw_chunk (pData, pUnknown->sHeader.iChunkname,
+                              pUnknown->iDatasize, pUnknown->pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_UNKNOWN, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_chunk_io.h b/files/Source/LibMNG/libmng_chunk_io.h
new file mode 100644
index 0000000..f8505ba
--- /dev/null
+++ b/files/Source/LibMNG/libmng_chunk_io.h
@@ -0,0 +1,415 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_chunk_io.h         copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.109                                                      * */
+/* *                                                                        * */
+/* * purpose   : Chunk I/O routines (definition)                            * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the chunk input/output routines              * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/04/2000 - G.Juyn                                * */
+/* *             - changed CRC initialization to use dynamic structure      * */
+/* *               (wasn't thread-safe the old way !)                       * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed write routines definition                        * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added support for JDAA                                   * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added SKIP_CHUNK and NO_DELTA_PNG support                * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/24/2004 - G.R-P                                 * */
+/* *             - fixed SKIPCHUNK_itXT and SKIPCHUNK_ztXT typos            * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/07/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKREADER               * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_chunk_io_h_
+#define _libmng_chunk_io_h_
+
+/* ************************************************************************** */
+
+mng_uint32 mng_crc (mng_datap  pData,
+                    mng_uint8p buf,
+                    mng_int32  len);
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_READ_PROCS
+
+/* ************************************************************************** */
+
+mng_retcode mng_inflate_buffer (mng_datap  pData,
+                                mng_uint8p pInbuf,
+                                mng_uint32 iInsize,
+                                mng_uint8p *pOutbuf,
+                                mng_uint32 *iOutsize,
+                                mng_uint32 *iRealsize);
+
+/* ************************************************************************** */
+
+#define READ_CHUNK(n) mng_retcode n (mng_datap   pData,    \
+                                     mng_chunkp  pHeader,  \
+                                     mng_uint32  iRawlen,  \
+                                     mng_uint8p  pRawdata, \
+                                     mng_chunkp* ppChunk)
+
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+READ_CHUNK (mng_read_general) ;
+#endif
+
+READ_CHUNK (mng_read_ihdr) ;
+READ_CHUNK (mng_read_plte) ;
+READ_CHUNK (mng_read_idat) ;
+READ_CHUNK (mng_read_iend) ;
+READ_CHUNK (mng_read_trns) ;
+READ_CHUNK (mng_read_gama) ;
+READ_CHUNK (mng_read_chrm) ;
+READ_CHUNK (mng_read_srgb) ;
+#ifndef MNG_SKIPCHUNK_iCCP
+READ_CHUNK (mng_read_iccp) ;
+#endif
+#ifndef MNG_SKIPCHUNK_tEXt
+READ_CHUNK (mng_read_text) ;
+#endif
+#ifndef MNG_SKIPCHUNK_zTXt
+READ_CHUNK (mng_read_ztxt) ;
+#endif
+#ifndef MNG_SKIPCHUNK_iTXt
+READ_CHUNK (mng_read_itxt) ;
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+READ_CHUNK (mng_read_bkgd) ;
+#endif
+#ifndef MNG_SKIPCHUNK_pHYs
+READ_CHUNK (mng_read_phys) ;
+#endif
+#ifndef MNG_SKIPCHUNK_sBIT
+READ_CHUNK (mng_read_sbit) ;
+#endif
+#ifndef MNG_SKIPCHUNK_sPLT
+READ_CHUNK (mng_read_splt) ;
+#endif
+#ifndef MNG_SKIPCHUNK_hIST
+READ_CHUNK (mng_read_hist) ;
+#endif
+#ifndef MNG_SKIPCHUNK_tIME
+READ_CHUNK (mng_read_time) ;
+#endif
+READ_CHUNK (mng_read_mhdr) ;
+READ_CHUNK (mng_read_mend) ;
+READ_CHUNK (mng_read_loop) ;
+READ_CHUNK (mng_read_endl) ;
+READ_CHUNK (mng_read_defi) ;
+READ_CHUNK (mng_read_basi) ;
+READ_CHUNK (mng_read_clon) ;
+#ifndef MNG_SKIPCHUNK_PAST
+READ_CHUNK (mng_read_past) ;
+#endif
+READ_CHUNK (mng_read_disc) ;
+READ_CHUNK (mng_read_back) ;
+READ_CHUNK (mng_read_fram) ;
+READ_CHUNK (mng_read_move) ;
+READ_CHUNK (mng_read_clip) ;
+READ_CHUNK (mng_read_show) ;
+READ_CHUNK (mng_read_term) ;
+READ_CHUNK (mng_read_save) ;
+READ_CHUNK (mng_read_seek) ;
+#ifndef MNG_SKIPCHUNK_eXPI
+READ_CHUNK (mng_read_expi) ;
+#endif
+#ifndef MNG_SKIPCHUNK_fPRI
+READ_CHUNK (mng_read_fpri) ;
+#endif
+#ifndef MNG_SKIPCHUNK_pHYg
+READ_CHUNK (mng_read_phyg) ;
+#endif
+#ifdef MNG_INCLUDE_JNG
+READ_CHUNK (mng_read_jhdr) ;
+READ_CHUNK (mng_read_jdaa) ;
+READ_CHUNK (mng_read_jdat) ;
+READ_CHUNK (mng_read_jsep) ;
+#endif
+#ifndef MNG_NO_DELTA_PNG
+READ_CHUNK (mng_read_dhdr) ;
+READ_CHUNK (mng_read_prom) ;
+READ_CHUNK (mng_read_ipng) ;
+READ_CHUNK (mng_read_pplt) ;
+#ifdef MNG_INCLUDE_JNG
+READ_CHUNK (mng_read_ijng) ;
+#endif
+READ_CHUNK (mng_read_drop) ;
+READ_CHUNK (mng_read_dbyk) ;
+READ_CHUNK (mng_read_ordr) ;
+#endif
+READ_CHUNK (mng_read_magn) ;
+#ifndef MNG_SKIPCHUNK_nEED
+READ_CHUNK (mng_read_need) ;
+#endif
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+READ_CHUNK (mng_read_mpng) ;
+#endif
+#ifndef MNG_SKIPCHUNK_evNT
+READ_CHUNK (mng_read_evnt) ;
+#endif
+READ_CHUNK (mng_read_unknown) ;
+
+/* ************************************************************************** */
+
+#else /* MNG_INCLUDE_READ_PROCS */
+#define mng_read_ihdr 0
+#define mng_read_plte 0
+#define mng_read_idat 0
+#define mng_read_iend 0
+#define mng_read_trns 0
+#define mng_read_gama 0
+#define mng_read_chrm 0
+#define mng_read_srgb 0
+#define mng_read_iccp 0
+#define mng_read_text 0
+#define mng_read_ztxt 0
+#define mng_read_itxt 0
+#define mng_read_bkgd 0
+#define mng_read_phys 0
+#define mng_read_sbit 0
+#define mng_read_splt 0
+#define mng_read_hist 0
+#define mng_read_time 0
+#define mng_read_mhdr 0
+#define mng_read_mend 0
+#define mng_read_loop 0
+#define mng_read_endl 0
+#define mng_read_defi 0
+#define mng_read_basi 0
+#define mng_read_clon 0
+#ifndef MNG_SKIPCHUNK_PAST
+#define mng_read_past 0
+#endif
+#define mng_read_disc 0
+#define mng_read_back 0
+#define mng_read_fram 0
+#define mng_read_move 0
+#define mng_read_clip 0
+#define mng_read_show 0
+#define mng_read_term 0
+#define mng_read_save 0
+#define mng_read_seek 0
+#define mng_read_expi 0
+#define mng_read_fpri 0
+#define mng_read_phyg 0
+#ifdef MNG_INCLUDE_JNG
+#define mng_read_jhdr 0
+#define mng_read_jdaa 0
+#define mng_read_jdat 0
+#define mng_read_jsep 0
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#define mng_read_dhdr 0
+#define mng_read_prom 0
+#define mng_read_ipng 0
+#define mng_read_pplt 0
+#ifdef MNG_INCLUDE_JNG
+#define mng_read_ijng 0
+#endif
+#define mng_read_drop 0
+#define mng_read_dbyk 0
+#define mng_read_ordr 0
+#endif
+#define mng_read_magn 0
+#define mng_read_need 0
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+#define mng_read_mpng 0
+#endif
+#define mng_read_evnt 0
+#define mng_read_unknown 0
+#endif /* MNG_INCLUDE_READ_PROCS */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_WRITE_PROCS
+
+#define WRITE_CHUNK(n) mng_retcode n (mng_datap  pData,   \
+                                      mng_chunkp pChunk)
+
+WRITE_CHUNK (mng_write_ihdr) ;
+WRITE_CHUNK (mng_write_plte) ;
+WRITE_CHUNK (mng_write_idat) ;
+WRITE_CHUNK (mng_write_iend) ;
+WRITE_CHUNK (mng_write_trns) ;
+WRITE_CHUNK (mng_write_gama) ;
+WRITE_CHUNK (mng_write_chrm) ;
+WRITE_CHUNK (mng_write_srgb) ;
+WRITE_CHUNK (mng_write_iccp) ;
+WRITE_CHUNK (mng_write_text) ;
+WRITE_CHUNK (mng_write_ztxt) ;
+WRITE_CHUNK (mng_write_itxt) ;
+WRITE_CHUNK (mng_write_bkgd) ;
+WRITE_CHUNK (mng_write_phys) ;
+WRITE_CHUNK (mng_write_sbit) ;
+WRITE_CHUNK (mng_write_splt) ;
+WRITE_CHUNK (mng_write_hist) ;
+WRITE_CHUNK (mng_write_time) ;
+WRITE_CHUNK (mng_write_mhdr) ;
+WRITE_CHUNK (mng_write_mend) ;
+WRITE_CHUNK (mng_write_loop) ;
+WRITE_CHUNK (mng_write_endl) ;
+WRITE_CHUNK (mng_write_defi) ;
+WRITE_CHUNK (mng_write_basi) ;
+WRITE_CHUNK (mng_write_clon) ;
+#ifndef MNG_SKIPCHUNK_PAST
+WRITE_CHUNK (mng_write_past) ;
+#endif
+WRITE_CHUNK (mng_write_disc) ;
+WRITE_CHUNK (mng_write_back) ;
+WRITE_CHUNK (mng_write_fram) ;
+WRITE_CHUNK (mng_write_move) ;
+WRITE_CHUNK (mng_write_clip) ;
+WRITE_CHUNK (mng_write_show) ;
+WRITE_CHUNK (mng_write_term) ;
+WRITE_CHUNK (mng_write_save) ;
+WRITE_CHUNK (mng_write_seek) ;
+WRITE_CHUNK (mng_write_expi) ;
+WRITE_CHUNK (mng_write_fpri) ;
+WRITE_CHUNK (mng_write_phyg) ;
+#ifdef MNG_INCLUDE_JNG
+WRITE_CHUNK (mng_write_jhdr) ;
+WRITE_CHUNK (mng_write_jdaa) ;
+WRITE_CHUNK (mng_write_jdat) ;
+WRITE_CHUNK (mng_write_jsep) ;
+#endif
+#ifndef MNG_NO_DELTA_PNG
+WRITE_CHUNK (mng_write_dhdr) ;
+WRITE_CHUNK (mng_write_prom) ;
+WRITE_CHUNK (mng_write_ipng) ;
+WRITE_CHUNK (mng_write_pplt) ;
+#ifdef MNG_INCLUDE_JNG
+WRITE_CHUNK (mng_write_ijng) ;
+#endif
+WRITE_CHUNK (mng_write_drop) ;
+WRITE_CHUNK (mng_write_dbyk) ;
+WRITE_CHUNK (mng_write_ordr) ;
+#endif
+WRITE_CHUNK (mng_write_magn) ;
+WRITE_CHUNK (mng_write_need) ;
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+WRITE_CHUNK (mng_write_mpng) ;
+#endif
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+WRITE_CHUNK (mng_write_ahdr) ;
+WRITE_CHUNK (mng_write_adat) ;
+#endif
+WRITE_CHUNK (mng_write_evnt) ;
+WRITE_CHUNK (mng_write_unknown) ;
+
+/* ************************************************************************** */
+
+#else /* MNG_INCLUDE_WRITE_PROCS */
+#define mng_write_ihdr 0
+#define mng_write_plte 0
+#define mng_write_idat 0
+#define mng_write_iend 0
+#define mng_write_trns 0
+#define mng_write_gama 0
+#define mng_write_chrm 0
+#define mng_write_srgb 0
+#define mng_write_iccp 0
+#define mng_write_text 0
+#define mng_write_ztxt 0
+#define mng_write_itxt 0
+#define mng_write_bkgd 0
+#define mng_write_phys 0
+#define mng_write_sbit 0
+#define mng_write_splt 0
+#define mng_write_hist 0
+#define mng_write_time 0
+#define mng_write_mhdr 0
+#define mng_write_mend 0
+#define mng_write_loop 0
+#define mng_write_endl 0
+#define mng_write_defi 0
+#define mng_write_basi 0
+#define mng_write_clon 0
+#ifndef MNG_SKIPCHUNK_PAST
+#define mng_write_past 0
+#endif
+#define mng_write_disc 0
+#define mng_write_back 0
+#define mng_write_fram 0
+#define mng_write_move 0
+#define mng_write_clip 0
+#define mng_write_show 0
+#define mng_write_term 0
+#define mng_write_save 0
+#define mng_write_seek 0
+#define mng_write_expi 0
+#define mng_write_fpri 0
+#define mng_write_phyg 0
+#ifdef MNG_INCLUDE_JNG
+#define mng_write_jhdr 0
+#define mng_write_jdaa 0
+#define mng_write_jdat 0
+#define mng_write_jsep 0
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#define mng_write_dhdr 0
+#define mng_write_prom 0
+#define mng_write_ipng 0
+#define mng_write_pplt 0
+#ifdef MNG_INCLUDE_JNG
+#define mng_write_ijng 0
+#endif
+#define mng_write_drop 0
+#define mng_write_dbyk 0
+#define mng_write_ordr 0
+#endif
+#define mng_write_magn 0
+#define mng_write_need 0
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+#define mng_write_mpng 0
+#endif
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+#define mng_write_adat 0
+#define mng_write_ahdr 0
+#endif
+#define mng_write_evnt 0
+#define mng_write_unknown 0
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+
+/* ************************************************************************** */
+
+#endif /* _libmng_chunk_io_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_chunk_prc.c b/files/Source/LibMNG/libmng_chunk_prc.c
new file mode 100644
index 0000000..e633e7e
--- /dev/null
+++ b/files/Source/LibMNG/libmng_chunk_prc.c
@@ -0,0 +1,4452 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_chunk_prc.c        copyright (c) 2000-2005 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Chunk initialization & cleanup (implementation)            * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the chunk initialization & cleanup       * */
+/* *             routines                                                   * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/19/2000 - G.Juyn                                * */
+/* *             - fixed creation-code                                      * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/31/2000 - G.Juyn                                * */
+/* *             - put add_chunk() inside MNG_INCLUDE_WRITE_PROCS wrapper   * */
+/* *             0.9.2 - 08/01/2000 - G.Juyn                                * */
+/* *             - wrapper for add_chunk() changed                          * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added support for JDAA                                   * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             - added HLAPI function to copy chunks                      * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 10/04/2002 - G.Juyn                                * */
+/* *             - fixed chunk-storage for evNT chunk                       * */
+/* *             1.0.5 - 10/17/2002 - G.Juyn                                * */
+/* *             - fixed issue in freeing evNT chunk                        * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added MNG_SKIPCHUNK_cHNK footprint optimizations         * */
+/* *             - added MNG_NO_DELTA_PNG reduction feature                 * */
+/* *             1.0.6 - 07/14/2003 - G.R-P                                 * */
+/* *             - added MNG_NO_LOOP_SIGNALS_SUPPORTED conditional          * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *             1.0.6 - 08/17/2003 - G.R-P                                 * */
+/* *             - added conditionals around non-VLC chunk support          * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/24/2004 - G.R-P                                 * */
+/* *             - fixed SKIPCHUNK_eXPI -> fPRI typo                        * */
+/* *                                                                        * */
+/* *             1.0.9 - 09/25/2004 - G.Juyn                                * */
+/* *             - replaced MNG_TWEAK_LARGE_FILES with permanent solution   * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKINITFREE             * */
+/* *             1.0.9 - 12/06/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKASSIGN               * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* *             1.0.10 - 07/30/2005 - G.Juyn                               * */
+/* *             - fixed problem with CLON object during readdisplay()      * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_memory.h"
+#include "libmng_chunks.h"
+#include "libmng_chunk_prc.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * General chunk routines                                                 * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+void mng_add_chunk (mng_datap  pData,
+                    mng_chunkp pChunk)
+{
+  if (!pData->pFirstchunk)             /* list is still empty ? */
+  {
+    pData->pFirstchunk      = pChunk;  /* then this becomes the first */
+    
+#ifdef MNG_SUPPORT_WRITE
+    if (!pData->iFirstchunkadded)
+    {
+      pData->iFirstchunkadded = ((mng_chunk_headerp)pChunk)->iChunkname;
+#endif
+
+      if (((mng_chunk_headerp)pChunk)->iChunkname == MNG_UINT_IHDR)
+        pData->eImagetype     = mng_it_png;
+      else
+#ifdef MNG_INCLUDE_JNG
+      if (((mng_chunk_headerp)pChunk)->iChunkname == MNG_UINT_JHDR)
+        pData->eImagetype     = mng_it_jng;
+      else
+#endif
+        pData->eImagetype     = mng_it_mng;
+
+      pData->eSigtype         = pData->eImagetype;
+#ifdef MNG_SUPPORT_WRITE
+    }
+#endif
+  }
+  else
+  {                                    /* else we make appropriate links */
+    ((mng_chunk_headerp)pChunk)->pPrev = pData->pLastchunk;
+    ((mng_chunk_headerp)pData->pLastchunk)->pNext = pChunk;
+  }
+
+  pData->pLastchunk = pChunk;          /* and it's always the last */
+
+  return;
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Chunk specific initialization routines                                 * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+INIT_CHUNK_HDR (mng_init_general)
+{
+  MNG_ALLOC (pData, *ppChunk, ((mng_chunk_headerp)pHeader)->iChunksize);
+  MNG_COPY (*ppChunk, pHeader, sizeof (mng_chunk_header));
+  return MNG_NOERROR;
+}
+
+#else /* MNG_OPTIMIZE_CHUNKINITFREE */
+
+/* ************************************************************************** */
+
+INIT_CHUNK_HDR (mng_init_ihdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IHDR, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_ihdr));
+  ((mng_ihdrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+INIT_CHUNK_HDR (mng_init_plte)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PLTE, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_plte));
+  ((mng_pltep)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PLTE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+INIT_CHUNK_HDR (mng_init_idat)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDAT, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_idat));
+  ((mng_idatp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+INIT_CHUNK_HDR (mng_init_iend)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IEND, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_iend));
+  ((mng_iendp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+INIT_CHUNK_HDR (mng_init_trns)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_TRNS, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_trns));
+  ((mng_trnsp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_TRNS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_gAMA
+INIT_CHUNK_HDR (mng_init_gama)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GAMA, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_gama));
+  ((mng_gamap)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+INIT_CHUNK_HDR (mng_init_chrm)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_CHRM, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_chrm));
+  ((mng_chrmp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_CHRM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sRGB
+INIT_CHUNK_HDR (mng_init_srgb)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SRGB, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_srgb));
+  ((mng_srgbp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+INIT_CHUNK_HDR (mng_init_iccp)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ICCP, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_iccp));
+  ((mng_iccpp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ICCP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tEXt
+INIT_CHUNK_HDR (mng_init_text)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_TEXT, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_text));
+  ((mng_textp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_TEXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_zTXt
+INIT_CHUNK_HDR (mng_init_ztxt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ZTXT, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_ztxt));
+  ((mng_ztxtp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ZTXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iTXt
+INIT_CHUNK_HDR (mng_init_itxt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ITXT, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_itxt));
+  ((mng_itxtp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ITXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_bKGD
+INIT_CHUNK_HDR (mng_init_bkgd)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_BKGD, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_bkgd));
+  ((mng_bkgdp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYs
+INIT_CHUNK_HDR (mng_init_phys)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PHYS, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_phys));
+  ((mng_physp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PHYS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sBIT
+INIT_CHUNK_HDR (mng_init_sbit)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SBIT, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_sbit));
+  ((mng_sbitp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SBIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+INIT_CHUNK_HDR (mng_init_splt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SPLT, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_splt));
+  ((mng_spltp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_hIST
+INIT_CHUNK_HDR (mng_init_hist)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_HIST, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_hist));
+  ((mng_histp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_HIST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tIME
+INIT_CHUNK_HDR (mng_init_time)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_TIME, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_time));
+  ((mng_timep)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_TIME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+INIT_CHUNK_HDR (mng_init_mhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_MHDR, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_mhdr));
+  ((mng_mhdrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_MHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+INIT_CHUNK_HDR (mng_init_mend)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_MEND, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_mend));
+  ((mng_mendp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_MEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+INIT_CHUNK_HDR (mng_init_loop)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_LOOP, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_loop));
+  ((mng_loopp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_LOOP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+INIT_CHUNK_HDR (mng_init_endl)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ENDL, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_endl));
+  ((mng_endlp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ENDL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DEFI
+INIT_CHUNK_HDR (mng_init_defi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DEFI, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_defi));
+  ((mng_defip)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BASI
+INIT_CHUNK_HDR (mng_init_basi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_BASI, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_basi));
+  ((mng_basip)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_BASI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLON
+INIT_CHUNK_HDR (mng_init_clon)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_CLON, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_clon));
+  ((mng_clonp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_CLON, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+INIT_CHUNK_HDR (mng_init_past)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PAST, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_past));
+  ((mng_pastp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PAST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+INIT_CHUNK_HDR (mng_init_disc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DISC, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_disc));
+  ((mng_discp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DISC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BACK
+INIT_CHUNK_HDR (mng_init_back)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_BACK, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_back));
+  ((mng_backp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_BACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+INIT_CHUNK_HDR (mng_init_fram)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_FRAM, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_fram));
+  ((mng_framp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_FRAM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MOVE
+INIT_CHUNK_HDR (mng_init_move)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_MOVE, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_move));
+  ((mng_movep)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_MOVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLIP
+INIT_CHUNK_HDR (mng_init_clip)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_CLIP, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_clip));
+  ((mng_clipp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_CLIP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SHOW
+INIT_CHUNK_HDR (mng_init_show)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SHOW, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_show));
+  ((mng_showp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SHOW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_TERM
+INIT_CHUNK_HDR (mng_init_term)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_TERM, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_term));
+  ((mng_termp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_TERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+INIT_CHUNK_HDR (mng_init_save)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SAVE, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_save));
+  ((mng_savep)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+INIT_CHUNK_HDR (mng_init_seek)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SEEK, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_seek));
+  ((mng_seekp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_SEEK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_eXPI
+INIT_CHUNK_HDR (mng_init_expi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_EXPI, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_expi));
+  ((mng_expip)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_EXPI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_fPRI
+INIT_CHUNK_HDR (mng_init_fpri)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_FPRI, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_fpri));
+  ((mng_fprip)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_FPRI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+INIT_CHUNK_HDR (mng_init_need)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_NEED, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_need));
+  ((mng_needp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_NEED, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYg
+INIT_CHUNK_HDR (mng_init_phyg)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PHYG, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_phyg));
+  ((mng_phygp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PHYG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+INIT_CHUNK_HDR (mng_init_jhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JHDR, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_jhdr));
+  ((mng_jhdrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+INIT_CHUNK_HDR (mng_init_jdaa)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JDAA, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_jdaa));
+  ((mng_jdaap)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JDAA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+INIT_CHUNK_HDR (mng_init_jdat)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JDAT, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_jdat));
+  ((mng_jdatp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+INIT_CHUNK_HDR (mng_init_jsep)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JSEP, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_jsep));
+  ((mng_jsepp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JSEP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+INIT_CHUNK_HDR (mng_init_dhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DHDR, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_dhdr));
+  ((mng_dhdrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+INIT_CHUNK_HDR (mng_init_prom)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PROM, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_prom));
+  ((mng_promp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PROM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+INIT_CHUNK_HDR (mng_init_ipng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IPNG, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_ipng));
+  ((mng_ipngp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+INIT_CHUNK_HDR (mng_init_pplt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PPLT, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_pplt));
+  ((mng_ppltp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_PPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+INIT_CHUNK_HDR (mng_init_ijng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IJNG, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_ijng));
+  ((mng_ijngp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IJNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+INIT_CHUNK_HDR (mng_init_drop)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DROP, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_drop));
+  ((mng_dropp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DROP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+INIT_CHUNK_HDR (mng_init_dbyk)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DBYK, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_dbyk));
+  ((mng_dbykp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_DBYK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+INIT_CHUNK_HDR (mng_init_ordr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ORDR, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_ordr));
+  ((mng_ordrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ORDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+INIT_CHUNK_HDR (mng_init_magn)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_MAGN, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_magn));
+  ((mng_magnp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_MAGN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_evNT
+INIT_CHUNK_HDR (mng_init_evnt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_EVNT, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_evnt));
+  ((mng_evntp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_EVNT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+INIT_CHUNK_HDR (mng_init_unknown)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_UNKNOWN, MNG_LC_START);
+#endif
+
+  MNG_ALLOC (pData, *ppChunk, sizeof (mng_unknown_chunk));
+  ((mng_unknown_chunkp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_UNKNOWN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_OPTIMIZE_CHUNKINITFREE */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Chunk specific cleanup routines                                        * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+FREE_CHUNK_HDR (mng_free_general)
+{
+  MNG_FREEX (pData, pHeader, ((mng_chunk_headerp)pHeader)->iChunksize);
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+FREE_CHUNK_HDR (mng_free_ihdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IHDR, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_ihdr));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+FREE_CHUNK_HDR (mng_free_plte)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PLTE, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_plte));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PLTE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+FREE_CHUNK_HDR (mng_free_idat)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IDAT, MNG_LC_START);
+#endif
+
+  if (((mng_idatp)pHeader)->iDatasize)
+    MNG_FREEX (pData, ((mng_idatp)pHeader)->pData,
+                      ((mng_idatp)pHeader)->iDatasize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_idat));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IDAT, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+FREE_CHUNK_HDR (mng_free_iend)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IEND, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_iend));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+FREE_CHUNK_HDR (mng_free_trns)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_TRNS, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_trns));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_TRNS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_gAMA
+FREE_CHUNK_HDR (mng_free_gama)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_GAMA, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_gama));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_GAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_cHRM
+FREE_CHUNK_HDR (mng_free_chrm)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_CHRM, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_chrm));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_CHRM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_sRGB
+FREE_CHUNK_HDR (mng_free_srgb)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SRGB, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_srgb));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+FREE_CHUNK_HDR (mng_free_iccp)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ICCP, MNG_LC_START);
+#endif
+
+  if (((mng_iccpp)pHeader)->iNamesize)
+    MNG_FREEX (pData, ((mng_iccpp)pHeader)->zName,
+                      ((mng_iccpp)pHeader)->iNamesize + 1);
+
+  if (((mng_iccpp)pHeader)->iProfilesize)
+    MNG_FREEX (pData, ((mng_iccpp)pHeader)->pProfile,
+                      ((mng_iccpp)pHeader)->iProfilesize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_iccp));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ICCP, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tEXt
+FREE_CHUNK_HDR (mng_free_text)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_TEXT, MNG_LC_START);
+#endif
+
+  if (((mng_textp)pHeader)->iKeywordsize)
+    MNG_FREEX (pData, ((mng_textp)pHeader)->zKeyword,
+                      ((mng_textp)pHeader)->iKeywordsize + 1);
+
+  if (((mng_textp)pHeader)->iTextsize)
+    MNG_FREEX (pData, ((mng_textp)pHeader)->zText,
+                      ((mng_textp)pHeader)->iTextsize + 1);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_text));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_TEXT, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_zTXt
+FREE_CHUNK_HDR (mng_free_ztxt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ZTXT, MNG_LC_START);
+#endif
+
+  if (((mng_ztxtp)pHeader)->iKeywordsize)
+    MNG_FREEX (pData, ((mng_ztxtp)pHeader)->zKeyword,
+                      ((mng_ztxtp)pHeader)->iKeywordsize + 1);
+
+  if (((mng_ztxtp)pHeader)->iTextsize)
+    MNG_FREEX (pData, ((mng_ztxtp)pHeader)->zText,
+                      ((mng_ztxtp)pHeader)->iTextsize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_ztxt));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ZTXT, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+#ifndef MNG_SKIPCHUNK_iTXt
+FREE_CHUNK_HDR (mng_free_itxt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ITXT, MNG_LC_START);
+#endif
+
+  if (((mng_itxtp)pHeader)->iKeywordsize)
+    MNG_FREEX (pData, ((mng_itxtp)pHeader)->zKeyword,
+                      ((mng_itxtp)pHeader)->iKeywordsize + 1);
+
+  if (((mng_itxtp)pHeader)->iLanguagesize)
+    MNG_FREEX (pData, ((mng_itxtp)pHeader)->zLanguage,
+                      ((mng_itxtp)pHeader)->iLanguagesize + 1);
+
+  if (((mng_itxtp)pHeader)->iTranslationsize)
+    MNG_FREEX (pData, ((mng_itxtp)pHeader)->zTranslation,
+                      ((mng_itxtp)pHeader)->iTranslationsize + 1);
+
+  if (((mng_itxtp)pHeader)->iTextsize)
+    MNG_FREEX (pData, ((mng_itxtp)pHeader)->zText,
+                      ((mng_itxtp)pHeader)->iTextsize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_itxt));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ITXT, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+FREE_CHUNK_HDR (mng_free_mpng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MPNG, MNG_LC_START);
+#endif
+
+  if (((mng_mpngp)pHeader)->iFramessize)
+    MNG_FREEX (pData, ((mng_mpngp)pHeader)->pFrames,
+                      ((mng_mpngp)pHeader)->iFramessize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_mpng));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MPNG, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+FREE_CHUNK_HDR (mng_free_adat)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ADAT, MNG_LC_START);
+#endif
+
+  if (((mng_adatp)pHeader)->iTilessize)
+    MNG_FREEX (pData, ((mng_adatp)pHeader)->pTiles, ((mng_adatp)pHeader)->iTilessize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_adat));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ADAT, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_bKGD
+FREE_CHUNK_HDR (mng_free_bkgd)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_BKGD, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_bkgd));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_pHYs
+FREE_CHUNK_HDR (mng_free_phys)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PHYS, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_phys));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PHYS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_sBIT
+FREE_CHUNK_HDR (mng_free_sbit)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SBIT, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_sbit));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SBIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+FREE_CHUNK_HDR (mng_free_splt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SPLT, MNG_LC_START);
+#endif
+
+  if (((mng_spltp)pHeader)->iNamesize)
+    MNG_FREEX (pData, ((mng_spltp)pHeader)->zName,
+                      ((mng_spltp)pHeader)->iNamesize + 1);
+
+  if (((mng_spltp)pHeader)->iEntrycount)
+    MNG_FREEX (pData, ((mng_spltp)pHeader)->pEntries,
+                      ((mng_spltp)pHeader)->iEntrycount *
+                      (((mng_spltp)pHeader)->iSampledepth * 3 + sizeof (mng_uint16)) );
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_splt));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SPLT, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_hIST
+FREE_CHUNK_HDR (mng_free_hist)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_HIST, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_hist));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_HIST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_tIME
+FREE_CHUNK_HDR (mng_free_time)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_TIME, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_time));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_TIME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+FREE_CHUNK_HDR (mng_free_mhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MHDR, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_mhdr));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+FREE_CHUNK_HDR (mng_free_mend)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MEND, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_mend));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+FREE_CHUNK_HDR (mng_free_loop)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_LOOP, MNG_LC_START);
+#endif
+
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+  if (((mng_loopp)pHeader)->iCount)
+    MNG_FREEX (pData, ((mng_loopp)pHeader)->pSignals,
+                      ((mng_loopp)pHeader)->iCount * sizeof (mng_uint32) );
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_loop));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_LOOP, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+FREE_CHUNK_HDR (mng_free_endl)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ENDL, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_endl));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ENDL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_DEFI
+FREE_CHUNK_HDR (mng_free_defi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DEFI, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_defi));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_BASI
+FREE_CHUNK_HDR (mng_free_basi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_BASI, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_basi));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_BASI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_CLON
+FREE_CHUNK_HDR (mng_free_clon)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_CLON, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_clon));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_CLON, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+FREE_CHUNK_HDR (mng_free_past)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PAST, MNG_LC_START);
+#endif
+
+  if (((mng_pastp)pHeader)->iCount)
+    MNG_FREEX (pData, ((mng_pastp)pHeader)->pSources,
+                      ((mng_pastp)pHeader)->iCount * sizeof (mng_past_source) );
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_past));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PAST, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+FREE_CHUNK_HDR (mng_free_disc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DISC, MNG_LC_START);
+#endif
+
+  if (((mng_discp)pHeader)->iCount)
+    MNG_FREEX (pData, ((mng_discp)pHeader)->pObjectids,
+                      ((mng_discp)pHeader)->iCount * sizeof (mng_uint16) );
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_disc));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DISC, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_BACK
+FREE_CHUNK_HDR (mng_free_back)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_BACK, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_back));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_BACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+FREE_CHUNK_HDR (mng_free_fram)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_FRAM, MNG_LC_START);
+#endif
+
+  if (((mng_framp)pHeader)->iNamesize)
+    MNG_FREEX (pData, ((mng_framp)pHeader)->zName,
+                      ((mng_framp)pHeader)->iNamesize + 1);
+
+  if (((mng_framp)pHeader)->iCount)
+    MNG_FREEX (pData, ((mng_framp)pHeader)->pSyncids,
+                      ((mng_framp)pHeader)->iCount * sizeof (mng_uint32) );
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_fram));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_FRAM, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_MOVE
+FREE_CHUNK_HDR (mng_free_move)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MOVE, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_move));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MOVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_CLIP
+FREE_CHUNK_HDR (mng_free_clip)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_CLIP, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_clip));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_CLIP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_SHOW
+FREE_CHUNK_HDR (mng_free_show)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SHOW, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_show));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SHOW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_TERM
+FREE_CHUNK_HDR (mng_free_term)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_TERM, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_term));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_TERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+FREE_CHUNK_HDR (mng_free_save)
+{
+  mng_save_entryp pEntry = ((mng_savep)pHeader)->pEntries;
+  mng_uint32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SAVE, MNG_LC_START);
+#endif
+
+  for (iX = 0; iX < ((mng_savep)pHeader)->iCount; iX++)
+  {
+    if (pEntry->iNamesize)
+      MNG_FREEX (pData, pEntry->zName, pEntry->iNamesize);
+
+    pEntry = pEntry + sizeof (mng_save_entry);
+  }
+
+  if (((mng_savep)pHeader)->iCount)
+    MNG_FREEX (pData, ((mng_savep)pHeader)->pEntries,
+                      ((mng_savep)pHeader)->iCount * sizeof (mng_save_entry) );
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_save));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SAVE, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+FREE_CHUNK_HDR (mng_free_seek)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SEEK, MNG_LC_START);
+#endif
+
+  if (((mng_seekp)pHeader)->iNamesize)
+    MNG_FREEX (pData, ((mng_seekp)pHeader)->zName,
+                      ((mng_seekp)pHeader)->iNamesize + 1);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_seek));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_SEEK, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_eXPI
+FREE_CHUNK_HDR (mng_free_expi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_EXPI, MNG_LC_START);
+#endif
+
+  if (((mng_expip)pHeader)->iNamesize)
+    MNG_FREEX (pData, ((mng_expip)pHeader)->zName,
+                      ((mng_expip)pHeader)->iNamesize + 1);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_expi));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_EXPI, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_fPRI
+FREE_CHUNK_HDR (mng_free_fpri)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_FPRI, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_fpri));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_FPRI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+FREE_CHUNK_HDR (mng_free_need)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_NEED, MNG_LC_START);
+#endif
+
+  if (((mng_needp)pHeader)->iKeywordssize)
+    MNG_FREEX (pData, ((mng_needp)pHeader)->zKeywords,
+                      ((mng_needp)pHeader)->iKeywordssize + 1);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_need));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_NEED, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_pHYg
+FREE_CHUNK_HDR (mng_free_phyg)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PHYG, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_phyg));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PHYG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifdef MNG_INCLUDE_JNG
+FREE_CHUNK_HDR (mng_free_jhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_JHDR, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_jhdr));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_JHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+FREE_CHUNK_HDR (mng_free_jdaa)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_JDAA, MNG_LC_START);
+#endif
+
+  if (((mng_jdaap)pHeader)->iDatasize)
+    MNG_FREEX (pData, ((mng_jdaap)pHeader)->pData,
+                      ((mng_jdaap)pHeader)->iDatasize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_jdaa));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_JDAA, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+FREE_CHUNK_HDR (mng_free_jdat)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_JDAT, MNG_LC_START);
+#endif
+
+  if (((mng_jdatp)pHeader)->iDatasize)
+    MNG_FREEX (pData, ((mng_jdatp)pHeader)->pData,
+                      ((mng_jdatp)pHeader)->iDatasize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_jdat));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_JDAT, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifdef MNG_INCLUDE_JNG
+FREE_CHUNK_HDR (mng_free_jsep)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_JSEP, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_jsep));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_JSEP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_NO_DELTA_PNG
+FREE_CHUNK_HDR (mng_free_dhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DHDR, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_dhdr));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_NO_DELTA_PNG
+FREE_CHUNK_HDR (mng_free_prom)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PROM, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_prom));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PROM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_NO_DELTA_PNG
+FREE_CHUNK_HDR (mng_free_ipng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IPNG, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_ipng));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_NO_DELTA_PNG
+FREE_CHUNK_HDR (mng_free_pplt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PPLT, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_pplt));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_PPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+FREE_CHUNK_HDR (mng_free_ijng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IJNG, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_ijng));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IJNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+FREE_CHUNK_HDR (mng_free_drop)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DROP, MNG_LC_START);
+#endif
+
+  if (((mng_dropp)pHeader)->iCount)
+    MNG_FREEX (pData, ((mng_dropp)pHeader)->pChunknames,
+                      ((mng_dropp)pHeader)->iCount * sizeof (mng_chunkid) );
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_drop));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DROP, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+FREE_CHUNK_HDR (mng_free_dbyk)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DBYK, MNG_LC_START);
+#endif
+
+  if (((mng_dbykp)pHeader)->iKeywordssize)
+    MNG_FREEX (pData, ((mng_dbykp)pHeader)->zKeywords,
+                      ((mng_dbykp)pHeader)->iKeywordssize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_dbyk));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_DBYK, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+FREE_CHUNK_HDR (mng_free_ordr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ORDR, MNG_LC_START);
+#endif
+
+  if (((mng_ordrp)pHeader)->iCount)
+    MNG_FREEX (pData, ((mng_ordrp)pHeader)->pEntries,
+                      ((mng_ordrp)pHeader)->iCount * sizeof (mng_ordr_entry) );
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_ordr));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ORDR, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+#ifndef MNG_SKIPCHUNK_MAGN
+FREE_CHUNK_HDR (mng_free_magn)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MAGN, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pHeader, sizeof (mng_magn));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MAGN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_evNT
+FREE_CHUNK_HDR (mng_free_evnt)
+{
+  mng_evnt_entryp pEntry = ((mng_evntp)pHeader)->pEntries;
+  mng_uint32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_EVNT, MNG_LC_START);
+#endif
+
+  for (iX = 0; iX < ((mng_evntp)pHeader)->iCount; iX++)
+  {
+    if (pEntry->iSegmentnamesize)
+      MNG_FREEX (pData, pEntry->zSegmentname, pEntry->iSegmentnamesize+1);
+
+    pEntry++;
+  }
+
+  if (((mng_evntp)pHeader)->iCount)
+    MNG_FREEX (pData, ((mng_evntp)pHeader)->pEntries,
+                      ((mng_evntp)pHeader)->iCount * sizeof (mng_evnt_entry) );
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_evnt));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_EVNT, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+FREE_CHUNK_HDR (mng_free_unknown)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_UNKNOWN, MNG_LC_START);
+#endif
+
+  if (((mng_unknown_chunkp)pHeader)->iDatasize)
+    MNG_FREEX (pData, ((mng_unknown_chunkp)pHeader)->pData,
+                      ((mng_unknown_chunkp)pHeader)->iDatasize);
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  MNG_FREEX (pData, pHeader, sizeof (mng_unknown_chunk));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_UNKNOWN, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKINITFREE
+  return MNG_NOERROR;
+#else
+  return mng_free_general(pData, pHeader);
+#endif
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Chunk specific copy routines                                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_WRITE_PROCS
+
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_CHUNKASSIGN
+ASSIGN_CHUNK_HDR (mng_assign_general)
+{
+  mng_ptr    pSrc = (mng_uint8p)pChunkfrom + sizeof (mng_chunk_header);
+  mng_ptr    pDst = (mng_uint8p)pChunkto   + sizeof (mng_chunk_header);
+  mng_size_t iLen = ((mng_chunk_headerp)pChunkfrom)->iChunksize - sizeof (mng_chunk_header);
+
+  MNG_COPY (pDst, pSrc, iLen);
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+ASSIGN_CHUNK_HDR (mng_assign_ihdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IHDR, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IHDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_ihdrp)pChunkto)->iWidth       = ((mng_ihdrp)pChunkfrom)->iWidth;
+  ((mng_ihdrp)pChunkto)->iHeight      = ((mng_ihdrp)pChunkfrom)->iHeight;
+  ((mng_ihdrp)pChunkto)->iBitdepth    = ((mng_ihdrp)pChunkfrom)->iBitdepth;
+  ((mng_ihdrp)pChunkto)->iColortype   = ((mng_ihdrp)pChunkfrom)->iColortype;
+  ((mng_ihdrp)pChunkto)->iCompression = ((mng_ihdrp)pChunkfrom)->iCompression;
+  ((mng_ihdrp)pChunkto)->iFilter      = ((mng_ihdrp)pChunkfrom)->iFilter;
+  ((mng_ihdrp)pChunkto)->iInterlace   = ((mng_ihdrp)pChunkfrom)->iInterlace;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+ASSIGN_CHUNK_HDR (mng_assign_plte)
+{
+  mng_uint32 iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PLTE, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_PLTE)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_pltep)pChunkto)->bEmpty      = ((mng_pltep)pChunkfrom)->bEmpty;
+  ((mng_pltep)pChunkto)->iEntrycount = ((mng_pltep)pChunkfrom)->iEntrycount;
+
+  for (iX = 0; iX < ((mng_pltep)pChunkto)->iEntrycount; iX++)
+    ((mng_pltep)pChunkto)->aEntries [iX] = ((mng_pltep)pChunkfrom)->aEntries [iX];
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PLTE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+ASSIGN_CHUNK_HDR (mng_assign_idat)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IDAT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IDAT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_idatp)pChunkto)->bEmpty    = ((mng_idatp)pChunkfrom)->bEmpty;
+  ((mng_idatp)pChunkto)->iDatasize = ((mng_idatp)pChunkfrom)->iDatasize;
+
+  if (((mng_idatp)pChunkto)->iDatasize)
+  {
+    MNG_ALLOC (pData, ((mng_idatp)pChunkto)->pData, ((mng_idatp)pChunkto)->iDatasize);
+    MNG_COPY  (((mng_idatp)pChunkto)->pData, ((mng_idatp)pChunkfrom)->pData,
+               ((mng_idatp)pChunkto)->iDatasize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+ASSIGN_CHUNK_HDR (mng_assign_iend)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IEND, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IEND)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+ASSIGN_CHUNK_HDR (mng_assign_trns)
+{
+  mng_uint32 iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_TRNS, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_tRNS)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_trnsp)pChunkto)->bEmpty  = ((mng_trnsp)pChunkfrom)->bEmpty;
+  ((mng_trnsp)pChunkto)->bGlobal = ((mng_trnsp)pChunkfrom)->bGlobal;
+  ((mng_trnsp)pChunkto)->iType   = ((mng_trnsp)pChunkfrom)->iType;
+  ((mng_trnsp)pChunkto)->iCount  = ((mng_trnsp)pChunkfrom)->iCount;
+  ((mng_trnsp)pChunkto)->iGray   = ((mng_trnsp)pChunkfrom)->iGray;
+  ((mng_trnsp)pChunkto)->iRed    = ((mng_trnsp)pChunkfrom)->iRed;
+  ((mng_trnsp)pChunkto)->iGreen  = ((mng_trnsp)pChunkfrom)->iGreen;
+  ((mng_trnsp)pChunkto)->iBlue   = ((mng_trnsp)pChunkfrom)->iBlue;
+  ((mng_trnsp)pChunkto)->iRawlen = ((mng_trnsp)pChunkfrom)->iRawlen;
+
+  for (iX = 0; iX < ((mng_trnsp)pChunkto)->iCount; iX++)
+    ((mng_trnsp)pChunkto)->aEntries [iX] = ((mng_trnsp)pChunkfrom)->aEntries [iX];
+
+  for (iX = 0; iX < ((mng_trnsp)pChunkto)->iRawlen; iX++)
+    ((mng_trnsp)pChunkto)->aRawdata [iX] = ((mng_trnsp)pChunkfrom)->aRawdata [iX];
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_TRNS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_gAMA
+ASSIGN_CHUNK_HDR (mng_assign_gama)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_GAMA, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_gAMA)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_gamap)pChunkto)->bEmpty = ((mng_gamap)pChunkfrom)->bEmpty;
+  ((mng_gamap)pChunkto)->iGamma = ((mng_gamap)pChunkfrom)->iGamma;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_GAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_cHRM
+ASSIGN_CHUNK_HDR (mng_assign_chrm)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_CHRM, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_cHRM)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_chrmp)pChunkto)->bEmpty       = ((mng_chrmp)pChunkfrom)->bEmpty;
+  ((mng_chrmp)pChunkto)->iWhitepointx = ((mng_chrmp)pChunkfrom)->iWhitepointx;
+  ((mng_chrmp)pChunkto)->iWhitepointy = ((mng_chrmp)pChunkfrom)->iWhitepointy;
+  ((mng_chrmp)pChunkto)->iRedx        = ((mng_chrmp)pChunkfrom)->iRedx;
+  ((mng_chrmp)pChunkto)->iRedy        = ((mng_chrmp)pChunkfrom)->iRedy;
+  ((mng_chrmp)pChunkto)->iGreenx      = ((mng_chrmp)pChunkfrom)->iGreenx;
+  ((mng_chrmp)pChunkto)->iGreeny      = ((mng_chrmp)pChunkfrom)->iGreeny;
+  ((mng_chrmp)pChunkto)->iBluex       = ((mng_chrmp)pChunkfrom)->iBluex;
+  ((mng_chrmp)pChunkto)->iBluey       = ((mng_chrmp)pChunkfrom)->iBluey;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_CHRM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_sRGB
+ASSIGN_CHUNK_HDR (mng_assign_srgb)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SRGB, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_sRGB)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_srgbp)pChunkto)->iRenderingintent = ((mng_srgbp)pChunkfrom)->iRenderingintent;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+ASSIGN_CHUNK_HDR (mng_assign_iccp)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ICCP, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_iCCP)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_iccpp)pChunkto)->bEmpty       = ((mng_iccpp)pChunkfrom)->bEmpty;
+  ((mng_iccpp)pChunkto)->iNamesize    = ((mng_iccpp)pChunkfrom)->iNamesize;
+  ((mng_iccpp)pChunkto)->iCompression = ((mng_iccpp)pChunkfrom)->iCompression;
+  ((mng_iccpp)pChunkto)->iProfilesize = ((mng_iccpp)pChunkfrom)->iProfilesize;
+
+  if (((mng_iccpp)pChunkto)->iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_iccpp)pChunkto)->zName, ((mng_iccpp)pChunkto)->iNamesize);
+    MNG_COPY  (((mng_iccpp)pChunkto)->zName, ((mng_iccpp)pChunkfrom)->zName,
+               ((mng_iccpp)pChunkto)->iNamesize);
+  }
+
+  if (((mng_iccpp)pChunkto)->iProfilesize)
+  {
+    MNG_ALLOC (pData, ((mng_iccpp)pChunkto)->pProfile, ((mng_iccpp)pChunkto)->iProfilesize);
+    MNG_COPY  (((mng_iccpp)pChunkto)->pProfile, ((mng_iccpp)pChunkfrom)->pProfile,
+               ((mng_iccpp)pChunkto)->iProfilesize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ICCP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tEXt
+ASSIGN_CHUNK_HDR (mng_assign_text)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_TEXT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_tEXt)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_textp)pChunkto)->iKeywordsize = ((mng_textp)pChunkfrom)->iKeywordsize;
+  ((mng_textp)pChunkto)->iTextsize    = ((mng_textp)pChunkfrom)->iTextsize;
+
+  if (((mng_textp)pChunkto)->iKeywordsize)
+  {
+    MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zKeyword, ((mng_textp)pChunkto)->iKeywordsize);
+    MNG_COPY  (((mng_itxtp)pChunkto)->zKeyword, ((mng_textp)pChunkfrom)->zKeyword,
+               ((mng_itxtp)pChunkto)->iKeywordsize);
+  }
+
+  if (((mng_textp)pChunkto)->iTextsize)
+  {
+    MNG_ALLOC (pData, ((mng_textp)pChunkto)->zText, ((mng_textp)pChunkto)->iTextsize);
+    MNG_COPY  (((mng_textp)pChunkto)->zText, ((mng_textp)pChunkfrom)->zText,
+               ((mng_textp)pChunkto)->iTextsize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_TEXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_zTXt
+ASSIGN_CHUNK_HDR (mng_assign_ztxt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ZTXT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_zTXt)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_ztxtp)pChunkto)->iKeywordsize = ((mng_ztxtp)pChunkfrom)->iKeywordsize;
+  ((mng_ztxtp)pChunkto)->iCompression = ((mng_ztxtp)pChunkfrom)->iCompression;
+  ((mng_ztxtp)pChunkto)->iTextsize    = ((mng_ztxtp)pChunkfrom)->iTextsize;
+
+  if (((mng_ztxtp)pChunkto)->iKeywordsize)
+  {
+    MNG_ALLOC (pData, ((mng_ztxtp)pChunkto)->zKeyword, ((mng_ztxtp)pChunkto)->iKeywordsize);
+    MNG_COPY  (((mng_ztxtp)pChunkto)->zKeyword, ((mng_ztxtp)pChunkfrom)->zKeyword,
+               ((mng_ztxtp)pChunkto)->iKeywordsize);
+  }
+
+  if (((mng_ztxtp)pChunkto)->iTextsize)
+  {
+    MNG_ALLOC (pData, ((mng_ztxtp)pChunkto)->zText, ((mng_ztxtp)pChunkto)->iTextsize);
+    MNG_COPY  (((mng_ztxtp)pChunkto)->zText, ((mng_ztxtp)pChunkfrom)->zText,
+               ((mng_ztxtp)pChunkto)->iTextsize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ZTXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iTXt
+ASSIGN_CHUNK_HDR (mng_assign_itxt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ITXT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_iTXt)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_itxtp)pChunkto)->iKeywordsize       = ((mng_itxtp)pChunkfrom)->iKeywordsize;
+  ((mng_itxtp)pChunkto)->iCompressionflag   = ((mng_itxtp)pChunkfrom)->iCompressionflag;
+  ((mng_itxtp)pChunkto)->iCompressionmethod = ((mng_itxtp)pChunkfrom)->iCompressionmethod;
+  ((mng_itxtp)pChunkto)->iLanguagesize      = ((mng_itxtp)pChunkfrom)->iLanguagesize;
+  ((mng_itxtp)pChunkto)->iTranslationsize   = ((mng_itxtp)pChunkfrom)->iTranslationsize;
+  ((mng_itxtp)pChunkto)->iTextsize          = ((mng_itxtp)pChunkfrom)->iTextsize;
+
+  if (((mng_itxtp)pChunkto)->iKeywordsize)
+  {
+    MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zKeyword, ((mng_itxtp)pChunkto)->iKeywordsize);
+    MNG_COPY  (((mng_itxtp)pChunkto)->zKeyword, ((mng_itxtp)pChunkfrom)->zKeyword,
+               ((mng_itxtp)pChunkto)->iKeywordsize);
+  }
+
+  if (((mng_itxtp)pChunkto)->iTextsize)
+  {
+    MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zLanguage, ((mng_itxtp)pChunkto)->iLanguagesize);
+    MNG_COPY  (((mng_itxtp)pChunkto)->zLanguage, ((mng_itxtp)pChunkfrom)->zLanguage,
+               ((mng_itxtp)pChunkto)->iLanguagesize);
+  }
+
+  if (((mng_itxtp)pChunkto)->iTextsize)
+  {
+    MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zTranslation, ((mng_itxtp)pChunkto)->iTranslationsize);
+    MNG_COPY  (((mng_itxtp)pChunkto)->zTranslation, ((mng_itxtp)pChunkfrom)->zTranslation,
+               ((mng_itxtp)pChunkto)->iTranslationsize);
+  }
+
+  if (((mng_itxtp)pChunkto)->iTextsize)
+  {
+    MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zText, ((mng_itxtp)pChunkto)->iTextsize);
+    MNG_COPY  (((mng_itxtp)pChunkto)->zText, ((mng_itxtp)pChunkfrom)->zText,
+               ((mng_itxtp)pChunkto)->iTextsize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ITXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_bKGD
+ASSIGN_CHUNK_HDR (mng_assign_bkgd)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_BKGD, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_bKGD)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_bkgdp)pChunkto)->bEmpty = ((mng_bkgdp)pChunkfrom)->bEmpty;
+  ((mng_bkgdp)pChunkto)->iType  = ((mng_bkgdp)pChunkfrom)->iType;
+  ((mng_bkgdp)pChunkto)->iIndex = ((mng_bkgdp)pChunkfrom)->iIndex;
+  ((mng_bkgdp)pChunkto)->iGray  = ((mng_bkgdp)pChunkfrom)->iGray;
+  ((mng_bkgdp)pChunkto)->iRed   = ((mng_bkgdp)pChunkfrom)->iRed;
+  ((mng_bkgdp)pChunkto)->iGreen = ((mng_bkgdp)pChunkfrom)->iGreen;
+  ((mng_bkgdp)pChunkto)->iBlue  = ((mng_bkgdp)pChunkfrom)->iBlue;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_pHYs
+ASSIGN_CHUNK_HDR (mng_assign_phys)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PHYS, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_pHYs)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_physp)pChunkto)->bEmpty = ((mng_physp)pChunkfrom)->bEmpty;
+  ((mng_physp)pChunkto)->iSizex = ((mng_physp)pChunkfrom)->iSizex;
+  ((mng_physp)pChunkto)->iSizey = ((mng_physp)pChunkfrom)->iSizey;
+  ((mng_physp)pChunkto)->iUnit  = ((mng_physp)pChunkfrom)->iUnit;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PHYS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_sBIT
+ASSIGN_CHUNK_HDR (mng_assign_sbit)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SBIT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_sBIT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_sbitp)pChunkto)->bEmpty    = ((mng_sbitp)pChunkfrom)->bEmpty;
+  ((mng_sbitp)pChunkto)->iType     = ((mng_sbitp)pChunkfrom)->iType;
+  ((mng_sbitp)pChunkto)->aBits [0] = ((mng_sbitp)pChunkfrom)->aBits [0];
+  ((mng_sbitp)pChunkto)->aBits [1] = ((mng_sbitp)pChunkfrom)->aBits [1];
+  ((mng_sbitp)pChunkto)->aBits [2] = ((mng_sbitp)pChunkfrom)->aBits [2];
+  ((mng_sbitp)pChunkto)->aBits [3] = ((mng_sbitp)pChunkfrom)->aBits [3];
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SBIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+ASSIGN_CHUNK_HDR (mng_assign_splt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SPLT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_sPLT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_spltp)pChunkto)->bEmpty       = ((mng_spltp)pChunkfrom)->bEmpty;
+  ((mng_spltp)pChunkto)->iNamesize    = ((mng_spltp)pChunkfrom)->iNamesize;
+  ((mng_spltp)pChunkto)->iSampledepth = ((mng_spltp)pChunkfrom)->iSampledepth;
+  ((mng_spltp)pChunkto)->iEntrycount  = ((mng_spltp)pChunkfrom)->iEntrycount;
+  ((mng_spltp)pChunkto)->pEntries     = ((mng_spltp)pChunkfrom)->pEntries;
+
+  if (((mng_spltp)pChunkto)->iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_spltp)pChunkto)->zName, ((mng_spltp)pChunkto)->iNamesize);
+    MNG_COPY  (((mng_spltp)pChunkto)->zName, ((mng_spltp)pChunkfrom)->zName,
+               ((mng_spltp)pChunkto)->iNamesize);
+  }
+
+  if (((mng_spltp)pChunkto)->iEntrycount)
+  {
+    mng_uint32 iLen = ((mng_spltp)pChunkto)->iEntrycount *
+                      (((mng_spltp)pChunkto)->iSampledepth * 3 + sizeof (mng_uint16));
+
+    MNG_ALLOC (pData, ((mng_spltp)pChunkto)->pEntries, iLen);
+    MNG_COPY  (((mng_spltp)pChunkto)->pEntries, ((mng_spltp)pChunkfrom)->pEntries, iLen);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_hIST
+ASSIGN_CHUNK_HDR (mng_assign_hist)
+{
+  mng_uint32 iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_HIST, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_hIST)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_histp)pChunkto)->iEntrycount = ((mng_histp)pChunkfrom)->iEntrycount;
+
+  for (iX = 0; iX < ((mng_histp)pChunkto)->iEntrycount; iX++)
+    ((mng_histp)pChunkto)->aEntries [iX] = ((mng_histp)pChunkfrom)->aEntries [iX];
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_HIST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_tIME
+ASSIGN_CHUNK_HDR (mng_assign_time)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_TIME, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_tIME)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_timep)pChunkto)->iYear   = ((mng_timep)pChunkfrom)->iYear;
+  ((mng_timep)pChunkto)->iMonth  = ((mng_timep)pChunkfrom)->iMonth;
+  ((mng_timep)pChunkto)->iDay    = ((mng_timep)pChunkfrom)->iDay;
+  ((mng_timep)pChunkto)->iHour   = ((mng_timep)pChunkfrom)->iHour;
+  ((mng_timep)pChunkto)->iMinute = ((mng_timep)pChunkfrom)->iMinute;
+  ((mng_timep)pChunkto)->iSecond = ((mng_timep)pChunkfrom)->iSecond;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_TIME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+ASSIGN_CHUNK_HDR (mng_assign_mhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MHDR, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_mhdrp)pChunkto)->iWidth      = ((mng_mhdrp)pChunkfrom)->iWidth;
+  ((mng_mhdrp)pChunkto)->iHeight     = ((mng_mhdrp)pChunkfrom)->iHeight;
+  ((mng_mhdrp)pChunkto)->iTicks      = ((mng_mhdrp)pChunkfrom)->iTicks;
+  ((mng_mhdrp)pChunkto)->iLayercount = ((mng_mhdrp)pChunkfrom)->iLayercount;
+  ((mng_mhdrp)pChunkto)->iFramecount = ((mng_mhdrp)pChunkfrom)->iFramecount;
+  ((mng_mhdrp)pChunkto)->iPlaytime   = ((mng_mhdrp)pChunkfrom)->iPlaytime;
+  ((mng_mhdrp)pChunkto)->iSimplicity = ((mng_mhdrp)pChunkfrom)->iSimplicity;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+ASSIGN_CHUNK_HDR (mng_assign_mend)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MEND, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_MEND)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+ASSIGN_CHUNK_HDR (mng_assign_loop)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_LOOP, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_LOOP)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_loopp)pChunkto)->iLevel       = ((mng_loopp)pChunkfrom)->iLevel;
+  ((mng_loopp)pChunkto)->iRepeat      = ((mng_loopp)pChunkfrom)->iRepeat;
+  ((mng_loopp)pChunkto)->iTermination = ((mng_loopp)pChunkfrom)->iTermination;
+  ((mng_loopp)pChunkto)->iItermin     = ((mng_loopp)pChunkfrom)->iItermin;
+  ((mng_loopp)pChunkto)->iItermax     = ((mng_loopp)pChunkfrom)->iItermax;
+  ((mng_loopp)pChunkto)->iCount       = ((mng_loopp)pChunkfrom)->iCount;
+
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+  if (((mng_loopp)pChunkto)->iCount)
+  {
+    mng_uint32 iLen = ((mng_loopp)pChunkto)->iCount * sizeof (mng_uint32);
+    MNG_ALLOC (pData, ((mng_loopp)pChunkto)->pSignals, iLen);
+    MNG_COPY  (((mng_loopp)pChunkto)->pSignals, ((mng_loopp)pChunkfrom)->pSignals, iLen);
+  }
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_LOOP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+ASSIGN_CHUNK_HDR (mng_assign_endl)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ENDL, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_ENDL)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_endlp)pChunkto)->iLevel = ((mng_endlp)pChunkfrom)->iLevel;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ENDL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_DEFI
+ASSIGN_CHUNK_HDR (mng_assign_defi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DEFI, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DEFI)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_defip)pChunkto)->iObjectid     = ((mng_defip)pChunkfrom)->iObjectid;
+  ((mng_defip)pChunkto)->bHasdonotshow = ((mng_defip)pChunkfrom)->bHasdonotshow;
+  ((mng_defip)pChunkto)->iDonotshow    = ((mng_defip)pChunkfrom)->iDonotshow;
+  ((mng_defip)pChunkto)->bHasconcrete  = ((mng_defip)pChunkfrom)->bHasconcrete;
+  ((mng_defip)pChunkto)->iConcrete     = ((mng_defip)pChunkfrom)->iConcrete;
+  ((mng_defip)pChunkto)->bHasloca      = ((mng_defip)pChunkfrom)->bHasloca;
+  ((mng_defip)pChunkto)->iXlocation    = ((mng_defip)pChunkfrom)->iXlocation;
+  ((mng_defip)pChunkto)->iYlocation    = ((mng_defip)pChunkfrom)->iYlocation;
+  ((mng_defip)pChunkto)->bHasclip      = ((mng_defip)pChunkfrom)->bHasclip;
+  ((mng_defip)pChunkto)->iLeftcb       = ((mng_defip)pChunkfrom)->iLeftcb;
+  ((mng_defip)pChunkto)->iRightcb      = ((mng_defip)pChunkfrom)->iRightcb;
+  ((mng_defip)pChunkto)->iTopcb        = ((mng_defip)pChunkfrom)->iTopcb;
+  ((mng_defip)pChunkto)->iBottomcb     = ((mng_defip)pChunkfrom)->iBottomcb;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_BASI
+ASSIGN_CHUNK_HDR (mng_assign_basi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_BASI, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_BASI)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_basip)pChunkto)->iWidth       = ((mng_basip)pChunkfrom)->iWidth;
+  ((mng_basip)pChunkto)->iHeight      = ((mng_basip)pChunkfrom)->iHeight;
+  ((mng_basip)pChunkto)->iBitdepth    = ((mng_basip)pChunkfrom)->iBitdepth;
+  ((mng_basip)pChunkto)->iColortype   = ((mng_basip)pChunkfrom)->iColortype;
+  ((mng_basip)pChunkto)->iCompression = ((mng_basip)pChunkfrom)->iCompression;
+  ((mng_basip)pChunkto)->iFilter      = ((mng_basip)pChunkfrom)->iFilter;
+  ((mng_basip)pChunkto)->iInterlace   = ((mng_basip)pChunkfrom)->iInterlace;
+  ((mng_basip)pChunkto)->iRed         = ((mng_basip)pChunkfrom)->iRed;
+  ((mng_basip)pChunkto)->iGreen       = ((mng_basip)pChunkfrom)->iGreen;
+  ((mng_basip)pChunkto)->iBlue        = ((mng_basip)pChunkfrom)->iBlue;
+  ((mng_basip)pChunkto)->iAlpha       = ((mng_basip)pChunkfrom)->iAlpha;
+  ((mng_basip)pChunkto)->iViewable    = ((mng_basip)pChunkfrom)->iViewable;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_BASI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_CLON
+ASSIGN_CHUNK_HDR (mng_assign_clon)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_CLON, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_CLON)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_clonp)pChunkto)->iSourceid     = ((mng_clonp)pChunkfrom)->iSourceid;
+  ((mng_clonp)pChunkto)->iCloneid      = ((mng_clonp)pChunkfrom)->iCloneid;
+  ((mng_clonp)pChunkto)->iClonetype    = ((mng_clonp)pChunkfrom)->iClonetype;
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+  ((mng_clonp)pChunkto)->bHasdonotshow = ((mng_clonp)pChunkfrom)->bHasdonotshow;
+#endif
+  ((mng_clonp)pChunkto)->iDonotshow    = ((mng_clonp)pChunkfrom)->iDonotshow;
+  ((mng_clonp)pChunkto)->iConcrete     = ((mng_clonp)pChunkfrom)->iConcrete;
+  ((mng_clonp)pChunkto)->bHasloca      = ((mng_clonp)pChunkfrom)->bHasloca;
+  ((mng_clonp)pChunkto)->iLocationtype = ((mng_clonp)pChunkfrom)->iLocationtype;
+  ((mng_clonp)pChunkto)->iLocationx    = ((mng_clonp)pChunkfrom)->iLocationx;
+  ((mng_clonp)pChunkto)->iLocationy    = ((mng_clonp)pChunkfrom)->iLocationy;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_CLON, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+ASSIGN_CHUNK_HDR (mng_assign_past)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PAST, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_PAST)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_pastp)pChunkto)->iDestid     = ((mng_pastp)pChunkfrom)->iDestid;
+  ((mng_pastp)pChunkto)->iTargettype = ((mng_pastp)pChunkfrom)->iTargettype;
+  ((mng_pastp)pChunkto)->iTargetx    = ((mng_pastp)pChunkfrom)->iTargetx;
+  ((mng_pastp)pChunkto)->iTargety    = ((mng_pastp)pChunkfrom)->iTargety;
+  ((mng_pastp)pChunkto)->iCount      = ((mng_pastp)pChunkfrom)->iCount;
+
+  if (((mng_pastp)pChunkto)->iCount)
+  {
+    mng_uint32 iLen = ((mng_pastp)pChunkto)->iCount * sizeof (mng_past_source);
+
+    MNG_ALLOC (pData, ((mng_pastp)pChunkto)->pSources, iLen);
+    MNG_COPY  (((mng_pastp)pChunkto)->pSources, ((mng_pastp)pChunkfrom)->pSources, iLen);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PAST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+ASSIGN_CHUNK_HDR (mng_assign_disc)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DISC, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DISC)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_discp)pChunkto)->iCount = ((mng_discp)pChunkfrom)->iCount;
+
+  if (((mng_discp)pChunkto)->iCount)
+  {
+    mng_uint32 iLen = ((mng_discp)pChunkto)->iCount * sizeof (mng_uint16);
+
+    MNG_ALLOC (pData, ((mng_discp)pChunkto)->pObjectids, iLen);
+    MNG_COPY  (((mng_discp)pChunkto)->pObjectids, ((mng_discp)pChunkfrom)->pObjectids, iLen);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DISC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_BACK
+ASSIGN_CHUNK_HDR (mng_assign_back)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_BACK, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_BACK)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_backp)pChunkto)->iRed       = ((mng_backp)pChunkfrom)->iRed;
+  ((mng_backp)pChunkto)->iGreen     = ((mng_backp)pChunkfrom)->iGreen;
+  ((mng_backp)pChunkto)->iBlue      = ((mng_backp)pChunkfrom)->iBlue;
+  ((mng_backp)pChunkto)->iMandatory = ((mng_backp)pChunkfrom)->iMandatory;
+  ((mng_backp)pChunkto)->iImageid   = ((mng_backp)pChunkfrom)->iImageid;
+  ((mng_backp)pChunkto)->iTile      = ((mng_backp)pChunkfrom)->iTile;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_BACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+ASSIGN_CHUNK_HDR (mng_assign_fram)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_FRAM, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_FRAM)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_framp)pChunkto)->bEmpty          = ((mng_framp)pChunkfrom)->bEmpty;
+  ((mng_framp)pChunkto)->iMode           = ((mng_framp)pChunkfrom)->iMode;
+  ((mng_framp)pChunkto)->iNamesize       = ((mng_framp)pChunkfrom)->iNamesize;
+  ((mng_framp)pChunkto)->iChangedelay    = ((mng_framp)pChunkfrom)->iChangedelay;
+  ((mng_framp)pChunkto)->iChangetimeout  = ((mng_framp)pChunkfrom)->iChangetimeout;
+  ((mng_framp)pChunkto)->iChangeclipping = ((mng_framp)pChunkfrom)->iChangeclipping;
+  ((mng_framp)pChunkto)->iChangesyncid   = ((mng_framp)pChunkfrom)->iChangesyncid;
+  ((mng_framp)pChunkto)->iDelay          = ((mng_framp)pChunkfrom)->iDelay;
+  ((mng_framp)pChunkto)->iTimeout        = ((mng_framp)pChunkfrom)->iTimeout;
+  ((mng_framp)pChunkto)->iBoundarytype   = ((mng_framp)pChunkfrom)->iBoundarytype;
+  ((mng_framp)pChunkto)->iBoundaryl      = ((mng_framp)pChunkfrom)->iBoundaryl;
+  ((mng_framp)pChunkto)->iBoundaryr      = ((mng_framp)pChunkfrom)->iBoundaryr;
+  ((mng_framp)pChunkto)->iBoundaryt      = ((mng_framp)pChunkfrom)->iBoundaryt;
+  ((mng_framp)pChunkto)->iBoundaryb      = ((mng_framp)pChunkfrom)->iBoundaryb;
+  ((mng_framp)pChunkto)->iCount          = ((mng_framp)pChunkfrom)->iCount;
+
+  if (((mng_framp)pChunkto)->iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_framp)pChunkto)->zName, ((mng_framp)pChunkto)->iNamesize);
+    MNG_COPY  (((mng_framp)pChunkto)->zName, ((mng_framp)pChunkfrom)->zName,
+               ((mng_framp)pChunkto)->iNamesize);
+  }
+
+  if (((mng_framp)pChunkto)->iCount)
+  {
+    mng_uint32 iLen = ((mng_framp)pChunkto)->iCount * sizeof (mng_uint32);
+
+    MNG_ALLOC (pData, ((mng_framp)pChunkto)->pSyncids, iLen);
+    MNG_COPY  (((mng_framp)pChunkto)->pSyncids, ((mng_framp)pChunkfrom)->pSyncids, iLen);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_FRAM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_MOVE
+ASSIGN_CHUNK_HDR (mng_assign_move)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MOVE, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_MOVE)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_movep)pChunkto)->iFirstid  = ((mng_movep)pChunkfrom)->iFirstid;
+  ((mng_movep)pChunkto)->iLastid   = ((mng_movep)pChunkfrom)->iLastid;
+  ((mng_movep)pChunkto)->iMovetype = ((mng_movep)pChunkfrom)->iMovetype;
+  ((mng_movep)pChunkto)->iMovex    = ((mng_movep)pChunkfrom)->iMovex;
+  ((mng_movep)pChunkto)->iMovey    = ((mng_movep)pChunkfrom)->iMovey;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MOVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_CLIP
+ASSIGN_CHUNK_HDR (mng_assign_clip)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_CLIP, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_CLIP)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_clipp)pChunkto)->iFirstid  = ((mng_clipp)pChunkfrom)->iFirstid;
+  ((mng_clipp)pChunkto)->iLastid   = ((mng_clipp)pChunkfrom)->iLastid;
+  ((mng_clipp)pChunkto)->iCliptype = ((mng_clipp)pChunkfrom)->iCliptype;
+  ((mng_clipp)pChunkto)->iClipl    = ((mng_clipp)pChunkfrom)->iClipl;
+  ((mng_clipp)pChunkto)->iClipr    = ((mng_clipp)pChunkfrom)->iClipr;
+  ((mng_clipp)pChunkto)->iClipt    = ((mng_clipp)pChunkfrom)->iClipt;
+  ((mng_clipp)pChunkto)->iClipb    = ((mng_clipp)pChunkfrom)->iClipb;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_CLIP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_SHOW
+ASSIGN_CHUNK_HDR (mng_assign_show)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SHOW, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_SHOW)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_showp)pChunkto)->bEmpty   = ((mng_showp)pChunkfrom)->bEmpty;
+  ((mng_showp)pChunkto)->iFirstid = ((mng_showp)pChunkfrom)->iFirstid;
+  ((mng_showp)pChunkto)->iLastid  = ((mng_showp)pChunkfrom)->iLastid;
+  ((mng_showp)pChunkto)->iMode    = ((mng_showp)pChunkfrom)->iMode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SHOW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_TERM
+ASSIGN_CHUNK_HDR (mng_assign_term)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_TERM, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_TERM)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_termp)pChunkto)->iTermaction = ((mng_termp)pChunkfrom)->iTermaction;
+  ((mng_termp)pChunkto)->iIteraction = ((mng_termp)pChunkfrom)->iIteraction;
+  ((mng_termp)pChunkto)->iDelay      = ((mng_termp)pChunkfrom)->iDelay;
+  ((mng_termp)pChunkto)->iItermax    = ((mng_termp)pChunkfrom)->iItermax;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_TERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+ASSIGN_CHUNK_HDR (mng_assign_save)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SAVE, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_SAVE)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_savep)pChunkto)->bEmpty      = ((mng_savep)pChunkfrom)->bEmpty;
+  ((mng_savep)pChunkto)->iOffsettype = ((mng_savep)pChunkfrom)->iOffsettype;
+  ((mng_savep)pChunkto)->iCount      = ((mng_savep)pChunkfrom)->iCount;
+
+  if (((mng_savep)pChunkto)->iCount)
+  {
+    mng_uint32      iX;
+    mng_save_entryp pEntry;
+    mng_uint32      iLen = ((mng_savep)pChunkto)->iCount * sizeof (mng_save_entry);
+
+    MNG_ALLOC (pData, ((mng_savep)pChunkto)->pEntries, iLen);
+    MNG_COPY  (((mng_savep)pChunkto)->pEntries, ((mng_savep)pChunkfrom)->pEntries, iLen);
+
+    pEntry = ((mng_savep)pChunkto)->pEntries;
+
+    for (iX = 0; iX < ((mng_savep)pChunkto)->iCount; iX++)
+    {
+      if (pEntry->iNamesize)
+      {
+        mng_pchar pTemp = pEntry->zName;
+
+        MNG_ALLOC (pData, pEntry->zName, pEntry->iNamesize);
+        MNG_COPY  (pEntry->zName, pTemp, pEntry->iNamesize);
+      }
+      else
+      {
+        pEntry->zName = MNG_NULL;
+      }
+
+      pEntry++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+ASSIGN_CHUNK_HDR (mng_assign_seek)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SEEK, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_SEEK)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_seekp)pChunkto)->iNamesize = ((mng_seekp)pChunkfrom)->iNamesize;
+
+  if (((mng_seekp)pChunkto)->iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_seekp)pChunkto)->zName, ((mng_seekp)pChunkto)->iNamesize);
+    MNG_COPY  (((mng_seekp)pChunkto)->zName, ((mng_seekp)pChunkfrom)->zName,
+               ((mng_seekp)pChunkto)->iNamesize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_SEEK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_eXPI
+ASSIGN_CHUNK_HDR (mng_assign_expi)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_EXPI, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_eXPI)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_expip)pChunkto)->iSnapshotid = ((mng_expip)pChunkfrom)->iSnapshotid;
+  ((mng_expip)pChunkto)->iNamesize   = ((mng_expip)pChunkfrom)->iNamesize;
+
+  if (((mng_expip)pChunkto)->iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_expip)pChunkto)->zName, ((mng_expip)pChunkto)->iNamesize);
+    MNG_COPY  (((mng_expip)pChunkto)->zName, ((mng_expip)pChunkfrom)->zName,
+               ((mng_expip)pChunkto)->iNamesize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_EXPI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_fPRI
+ASSIGN_CHUNK_HDR (mng_assign_fpri)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_FPRI, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_fPRI)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_fprip)pChunkto)->iDeltatype = ((mng_fprip)pChunkfrom)->iDeltatype;
+  ((mng_fprip)pChunkto)->iPriority  = ((mng_fprip)pChunkfrom)->iPriority;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_FPRI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+ASSIGN_CHUNK_HDR (mng_assign_need)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_NEED, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_nEED)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_needp)pChunkto)->iKeywordssize = ((mng_needp)pChunkfrom)->iKeywordssize;
+
+  if (((mng_needp)pChunkto)->iKeywordssize)
+  {
+    MNG_ALLOC (pData, ((mng_needp)pChunkto)->zKeywords, ((mng_needp)pChunkto)->iKeywordssize);
+    MNG_COPY  (((mng_needp)pChunkto)->zKeywords, ((mng_needp)pChunkfrom)->zKeywords,
+               ((mng_needp)pChunkto)->iKeywordssize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_NEED, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_pHYg
+ASSIGN_CHUNK_HDR (mng_assign_phyg)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PHYG, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_pHYg)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_phygp)pChunkto)->bEmpty = ((mng_phygp)pChunkfrom)->bEmpty;
+  ((mng_phygp)pChunkto)->iSizex = ((mng_phygp)pChunkfrom)->iSizex;
+  ((mng_phygp)pChunkto)->iSizey = ((mng_phygp)pChunkfrom)->iSizey;
+  ((mng_phygp)pChunkto)->iUnit  = ((mng_phygp)pChunkfrom)->iUnit;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PHYG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifdef MNG_INCLUDE_JNG
+ASSIGN_CHUNK_HDR (mng_assign_jhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_JHDR, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_JHDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_jhdrp)pChunkto)->iWidth            = ((mng_jhdrp)pChunkfrom)->iWidth;
+  ((mng_jhdrp)pChunkto)->iHeight           = ((mng_jhdrp)pChunkfrom)->iHeight;
+  ((mng_jhdrp)pChunkto)->iColortype        = ((mng_jhdrp)pChunkfrom)->iColortype;
+  ((mng_jhdrp)pChunkto)->iImagesampledepth = ((mng_jhdrp)pChunkfrom)->iImagesampledepth;
+  ((mng_jhdrp)pChunkto)->iImagecompression = ((mng_jhdrp)pChunkfrom)->iImagecompression;
+  ((mng_jhdrp)pChunkto)->iImageinterlace   = ((mng_jhdrp)pChunkfrom)->iImageinterlace;
+  ((mng_jhdrp)pChunkto)->iAlphasampledepth = ((mng_jhdrp)pChunkfrom)->iAlphasampledepth;
+  ((mng_jhdrp)pChunkto)->iAlphacompression = ((mng_jhdrp)pChunkfrom)->iAlphacompression;
+  ((mng_jhdrp)pChunkto)->iAlphafilter      = ((mng_jhdrp)pChunkfrom)->iAlphafilter;
+  ((mng_jhdrp)pChunkto)->iAlphainterlace   = ((mng_jhdrp)pChunkfrom)->iAlphainterlace;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_JHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+ASSIGN_CHUNK_HDR (mng_assign_jdaa)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_JDAA, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_JDAA)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_jdaap)pChunkto)->bEmpty    = ((mng_jdaap)pChunkfrom)->bEmpty;
+  ((mng_jdaap)pChunkto)->iDatasize = ((mng_jdaap)pChunkfrom)->iDatasize;
+
+  if (((mng_jdaap)pChunkto)->iDatasize)
+  {
+    MNG_ALLOC (pData, ((mng_jdaap)pChunkto)->pData, ((mng_jdaap)pChunkto)->iDatasize);
+    MNG_COPY  (((mng_jdaap)pChunkto)->pData, ((mng_jdaap)pChunkfrom)->pData,
+               ((mng_jdaap)pChunkto)->iDatasize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_JDAA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+ASSIGN_CHUNK_HDR (mng_assign_jdat)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_JDAT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_JDAT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_jdatp)pChunkto)->bEmpty    = ((mng_jdatp)pChunkfrom)->bEmpty;
+  ((mng_jdatp)pChunkto)->iDatasize = ((mng_jdatp)pChunkfrom)->iDatasize;
+
+  if (((mng_jdatp)pChunkto)->iDatasize)
+  {
+    MNG_ALLOC (pData, ((mng_jdatp)pChunkto)->pData, ((mng_jdatp)pChunkto)->iDatasize);
+    MNG_COPY  (((mng_jdatp)pChunkto)->pData, ((mng_jdatp)pChunkfrom)->pData,
+               ((mng_jdatp)pChunkto)->iDatasize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_JDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifdef MNG_INCLUDE_JNG
+ASSIGN_CHUNK_HDR (mng_assign_jsep)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_JSEP, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_JSEP)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_JSEP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_NO_DELTA_PNG
+ASSIGN_CHUNK_HDR (mng_assign_dhdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DHDR, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DHDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_dhdrp)pChunkto)->iObjectid    = ((mng_dhdrp)pChunkfrom)->iObjectid;
+  ((mng_dhdrp)pChunkto)->iImagetype   = ((mng_dhdrp)pChunkfrom)->iImagetype;
+  ((mng_dhdrp)pChunkto)->iDeltatype   = ((mng_dhdrp)pChunkfrom)->iDeltatype;
+  ((mng_dhdrp)pChunkto)->iBlockwidth  = ((mng_dhdrp)pChunkfrom)->iBlockwidth;
+  ((mng_dhdrp)pChunkto)->iBlockheight = ((mng_dhdrp)pChunkfrom)->iBlockheight;
+  ((mng_dhdrp)pChunkto)->iBlockx      = ((mng_dhdrp)pChunkfrom)->iBlockx;
+  ((mng_dhdrp)pChunkto)->iBlocky      = ((mng_dhdrp)pChunkfrom)->iBlocky;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_NO_DELTA_PNG
+ASSIGN_CHUNK_HDR (mng_assign_prom)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PROM, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_PROM)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_promp)pChunkto)->iColortype   = ((mng_promp)pChunkfrom)->iColortype;
+  ((mng_promp)pChunkto)->iSampledepth = ((mng_promp)pChunkfrom)->iSampledepth;
+  ((mng_promp)pChunkto)->iFilltype    = ((mng_promp)pChunkfrom)->iFilltype;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PROM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_NO_DELTA_PNG
+ASSIGN_CHUNK_HDR (mng_assign_ipng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IPNG, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IPNG)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_NO_DELTA_PNG
+ASSIGN_CHUNK_HDR (mng_assign_pplt)
+{
+  mng_uint32 iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PPLT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_PPLT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_ppltp)pChunkto)->iDeltatype = ((mng_ppltp)pChunkfrom)->iDeltatype;
+  ((mng_ppltp)pChunkto)->iCount     = ((mng_ppltp)pChunkfrom)->iCount;
+
+  for (iX = 0; iX < ((mng_ppltp)pChunkto)->iCount; iX++)
+    ((mng_ppltp)pChunkto)->aEntries [iX] = ((mng_ppltp)pChunkfrom)->aEntries [iX];
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_PPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+ASSIGN_CHUNK_HDR (mng_assign_ijng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IJNG, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IJNG)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_IJNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+ASSIGN_CHUNK_HDR (mng_assign_drop)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DROP, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DROP)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_dropp)pChunkto)->iCount = ((mng_dropp)pChunkfrom)->iCount;
+
+  if (((mng_dropp)pChunkto)->iCount)
+  {
+    mng_uint32 iLen = ((mng_dropp)pChunkto)->iCount * sizeof (mng_uint32);
+
+    MNG_ALLOC (pData, ((mng_dropp)pChunkto)->pChunknames, iLen);
+    MNG_COPY  (((mng_dropp)pChunkto)->pChunknames, ((mng_dropp)pChunkfrom)->pChunknames, iLen);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DROP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+ASSIGN_CHUNK_HDR (mng_assign_dbyk)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DBYK, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DBYK)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_dbykp)pChunkto)->iChunkname    = ((mng_dbykp)pChunkfrom)->iChunkname;
+  ((mng_dbykp)pChunkto)->iPolarity     = ((mng_dbykp)pChunkfrom)->iPolarity;
+  ((mng_dbykp)pChunkto)->iKeywordssize = ((mng_dbykp)pChunkfrom)->iKeywordssize;
+
+  if (((mng_dbykp)pChunkto)->iKeywordssize)
+  {
+    MNG_ALLOC (pData, ((mng_dbykp)pChunkto)->zKeywords, ((mng_dbykp)pChunkto)->iKeywordssize);
+    MNG_COPY  (((mng_dbykp)pChunkto)->zKeywords, ((mng_dbykp)pChunkfrom)->zKeywords,
+               ((mng_dbykp)pChunkto)->iKeywordssize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_DBYK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+ASSIGN_CHUNK_HDR (mng_assign_ordr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ORDR, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_ORDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_ordrp)pChunkto)->iCount = ((mng_ordrp)pChunkfrom)->iCount;
+
+  if (((mng_ordrp)pChunkto)->iCount)
+  {
+    mng_uint32 iLen = ((mng_ordrp)pChunkto)->iCount * sizeof (mng_ordr_entry);
+
+    MNG_ALLOC (pData, ((mng_ordrp)pChunkto)->pEntries, iLen);
+    MNG_COPY  (((mng_ordrp)pChunkto)->pEntries, ((mng_ordrp)pChunkfrom)->pEntries, iLen);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ORDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKASSIGN
+#ifndef MNG_SKIPCHUNK_MAGN
+ASSIGN_CHUNK_HDR (mng_assign_magn)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MAGN, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_MAGN)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_magnp)pChunkto)->iFirstid = ((mng_magnp)pChunkfrom)->iFirstid;
+  ((mng_magnp)pChunkto)->iLastid  = ((mng_magnp)pChunkfrom)->iLastid;
+  ((mng_magnp)pChunkto)->iMethodX = ((mng_magnp)pChunkfrom)->iMethodX;
+  ((mng_magnp)pChunkto)->iMX      = ((mng_magnp)pChunkfrom)->iMX;
+  ((mng_magnp)pChunkto)->iMY      = ((mng_magnp)pChunkfrom)->iMY;
+  ((mng_magnp)pChunkto)->iML      = ((mng_magnp)pChunkfrom)->iML;
+  ((mng_magnp)pChunkto)->iMR      = ((mng_magnp)pChunkfrom)->iMR;
+  ((mng_magnp)pChunkto)->iMT      = ((mng_magnp)pChunkfrom)->iMT;
+  ((mng_magnp)pChunkto)->iMB      = ((mng_magnp)pChunkfrom)->iMB;
+  ((mng_magnp)pChunkto)->iMethodY = ((mng_magnp)pChunkfrom)->iMethodY;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MAGN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+ASSIGN_CHUNK_HDR (mng_assign_mpng)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MPNG, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_mpNG)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_mpngp)pChunkto)->iFramewidth        = ((mng_mpngp)pChunkfrom)->iFramewidth;
+  ((mng_mpngp)pChunkto)->iFrameheight       = ((mng_mpngp)pChunkfrom)->iFrameheight;
+  ((mng_mpngp)pChunkto)->iNumplays          = ((mng_mpngp)pChunkfrom)->iNumplays;
+  ((mng_mpngp)pChunkto)->iTickspersec       = ((mng_mpngp)pChunkfrom)->iTickspersec;
+  ((mng_mpngp)pChunkto)->iCompressionmethod = ((mng_mpngp)pChunkfrom)->iCompressionmethod;
+  ((mng_mpngp)pChunkto)->iFramessize        = ((mng_mpngp)pChunkfrom)->iFramessize;
+
+  if (((mng_mpngp)pChunkto)->iFramessize)
+  {
+    MNG_ALLOC (pData, ((mng_mpngp)pChunkto)->pFrames, ((mng_mpngp)pChunkto)->iFramessize);
+    MNG_COPY  (((mng_mpngp)pChunkto)->pFrames, ((mng_mpngp)pChunkfrom)->pFrames,
+               ((mng_mpngp)pChunkto)->iFramessize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_MPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+ASSIGN_CHUNK_HDR (mng_assign_ahdr)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_AHDR, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_ahDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_ahdrp)pChunkto)->iNumframes   = ((mng_ahdrp)pChunkfrom)->iNumframes;
+  ((mng_ahdrp)pChunkto)->iTickspersec = ((mng_ahdrp)pChunkfrom)->iTickspersec;
+  ((mng_ahdrp)pChunkto)->iNumplays    = ((mng_ahdrp)pChunkfrom)->iNumplays;
+  ((mng_ahdrp)pChunkto)->iTilewidth   = ((mng_ahdrp)pChunkfrom)->iTilewidth;
+  ((mng_ahdrp)pChunkto)->iTileheight  = ((mng_ahdrp)pChunkfrom)->iTileheight;
+  ((mng_ahdrp)pChunkto)->iInterlace   = ((mng_ahdrp)pChunkfrom)->iInterlace;
+  ((mng_ahdrp)pChunkto)->iStillused   = ((mng_ahdrp)pChunkfrom)->iStillused;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_AHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+ASSIGN_CHUNK_HDR (mng_assign_adat)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ADAT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_adAT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_adatp)pChunkto)->iTilessize = ((mng_adatp)pChunkfrom)->iTilessize;
+
+  if (((mng_adatp)pChunkto)->iTilessize)
+  {
+    MNG_ALLOC (pData, ((mng_adatp)pChunkto)->pTiles, ((mng_adatp)pChunkto)->iTilessize);
+    MNG_COPY  (((mng_adatp)pChunkto)->pTiles, ((mng_adatp)pChunkfrom)->pTiles,
+               ((mng_adatp)pChunkto)->iTilessize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_ADAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_evNT
+ASSIGN_CHUNK_HDR (mng_assign_evnt)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_EVNT, MNG_LC_START);
+#endif
+
+  if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_evNT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */
+
+  ((mng_evntp)pChunkto)->iCount = ((mng_evntp)pChunkfrom)->iCount;
+
+  if (((mng_evntp)pChunkto)->iCount)
+  {
+    mng_uint32      iX;
+    mng_evnt_entryp pEntry;
+    mng_uint32      iLen = ((mng_evntp)pChunkto)->iCount * sizeof (mng_evnt_entry);
+
+    MNG_ALLOC (pData, ((mng_evntp)pChunkto)->pEntries, iLen);
+    MNG_COPY  (((mng_evntp)pChunkto)->pEntries, ((mng_evntp)pChunkfrom)->pEntries, iLen);
+
+    pEntry = ((mng_evntp)pChunkto)->pEntries;
+
+    for (iX = 0; iX < ((mng_evntp)pChunkto)->iCount; iX++)
+    {
+      if (pEntry->iSegmentnamesize)
+      {
+        mng_pchar pTemp = pEntry->zSegmentname;
+
+        MNG_ALLOC (pData, pEntry->zSegmentname, pEntry->iSegmentnamesize+1);
+        MNG_COPY  (pEntry->zSegmentname, pTemp, pEntry->iSegmentnamesize);
+      }
+      else
+      {
+        pEntry->zSegmentname = MNG_NULL;
+      }
+
+      pEntry++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_EVNT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+ASSIGN_CHUNK_HDR (mng_assign_unknown)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_UNKNOWN, MNG_LC_START);
+#endif
+
+  ((mng_unknown_chunkp)pChunkto)->iDatasize = ((mng_unknown_chunkp)pChunkfrom)->iDatasize;
+
+  if (((mng_unknown_chunkp)pChunkto)->iDatasize)
+  {
+    MNG_ALLOC (pData, ((mng_unknown_chunkp)pChunkto)->pData, ((mng_unknown_chunkp)pChunkto)->iDatasize);
+    MNG_COPY  (((mng_unknown_chunkp)pChunkto)->pData, ((mng_unknown_chunkp)pChunkfrom)->pData,
+               ((mng_unknown_chunkp)pChunkto)->iDatasize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ASSIGN_UNKNOWN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_chunk_prc.h b/files/Source/LibMNG/libmng_chunk_prc.h
new file mode 100644
index 0000000..0cf0f3c
--- /dev/null
+++ b/files/Source/LibMNG/libmng_chunk_prc.h
@@ -0,0 +1,381 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_chunk_prc.h        copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Chunk initialization & cleanup (definition)                * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : definition of the chunk initialization & cleanup routines  * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added support for JDAA                                   * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added NO_DELTA_PNG support                               * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKINITFREE             * */
+/* *             1.0.9 - 12/06/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKASSIGN               * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_chunk_prc_h_
+#define _libmng_chunk_prc_h_
+
+/* ************************************************************************** */
+
+void mng_add_chunk (mng_datap  pData,
+                    mng_chunkp pChunk);
+
+/* ************************************************************************** */
+
+#define INIT_CHUNK_HDR(n) mng_retcode n (mng_datap   pData,    \
+                                         mng_chunkp  pHeader,  \
+                                         mng_chunkp* ppChunk)
+
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+INIT_CHUNK_HDR (mng_init_general) ;
+#else
+INIT_CHUNK_HDR (mng_init_ihdr) ;
+INIT_CHUNK_HDR (mng_init_plte) ;
+INIT_CHUNK_HDR (mng_init_idat) ;
+INIT_CHUNK_HDR (mng_init_iend) ;
+INIT_CHUNK_HDR (mng_init_trns) ;
+INIT_CHUNK_HDR (mng_init_gama) ;
+INIT_CHUNK_HDR (mng_init_chrm) ;
+INIT_CHUNK_HDR (mng_init_srgb) ;
+INIT_CHUNK_HDR (mng_init_iccp) ;
+INIT_CHUNK_HDR (mng_init_text) ;
+INIT_CHUNK_HDR (mng_init_ztxt) ;
+INIT_CHUNK_HDR (mng_init_itxt) ;
+INIT_CHUNK_HDR (mng_init_bkgd) ;
+INIT_CHUNK_HDR (mng_init_phys) ;
+INIT_CHUNK_HDR (mng_init_sbit) ;
+INIT_CHUNK_HDR (mng_init_splt) ;
+INIT_CHUNK_HDR (mng_init_hist) ;
+INIT_CHUNK_HDR (mng_init_time) ;
+INIT_CHUNK_HDR (mng_init_mhdr) ;
+INIT_CHUNK_HDR (mng_init_mend) ;
+INIT_CHUNK_HDR (mng_init_loop) ;
+INIT_CHUNK_HDR (mng_init_endl) ;
+INIT_CHUNK_HDR (mng_init_defi) ;
+INIT_CHUNK_HDR (mng_init_basi) ;
+INIT_CHUNK_HDR (mng_init_clon) ;
+#ifndef MNG_SKIPCHUNK_PAST
+INIT_CHUNK_HDR (mng_init_past) ;
+#endif
+INIT_CHUNK_HDR (mng_init_disc) ;
+INIT_CHUNK_HDR (mng_init_back) ;
+INIT_CHUNK_HDR (mng_init_fram) ;
+INIT_CHUNK_HDR (mng_init_move) ;
+INIT_CHUNK_HDR (mng_init_clip) ;
+INIT_CHUNK_HDR (mng_init_show) ;
+INIT_CHUNK_HDR (mng_init_term) ;
+INIT_CHUNK_HDR (mng_init_save) ;
+INIT_CHUNK_HDR (mng_init_seek) ;
+INIT_CHUNK_HDR (mng_init_expi) ;
+INIT_CHUNK_HDR (mng_init_fpri) ;
+INIT_CHUNK_HDR (mng_init_need) ;
+INIT_CHUNK_HDR (mng_init_phyg) ;
+#ifdef MNG_INCLUDE_JNG
+INIT_CHUNK_HDR (mng_init_jhdr) ;
+INIT_CHUNK_HDR (mng_init_jdaa) ;
+INIT_CHUNK_HDR (mng_init_jdat) ;
+INIT_CHUNK_HDR (mng_init_jsep) ;
+#endif
+#ifndef MNG_NO_DELTA_PNG
+INIT_CHUNK_HDR (mng_init_dhdr) ;
+INIT_CHUNK_HDR (mng_init_prom) ;
+INIT_CHUNK_HDR (mng_init_ipng) ;
+INIT_CHUNK_HDR (mng_init_pplt) ;
+#ifdef MNG_INCLUDE_JNG
+INIT_CHUNK_HDR (mng_init_ijng) ;
+#endif
+INIT_CHUNK_HDR (mng_init_drop) ;
+INIT_CHUNK_HDR (mng_init_dbyk) ;
+INIT_CHUNK_HDR (mng_init_ordr) ;
+#endif
+INIT_CHUNK_HDR (mng_init_magn) ;
+INIT_CHUNK_HDR (mng_init_evnt) ;
+INIT_CHUNK_HDR (mng_init_unknown) ;
+#endif /* MNG_OPTIMIZE_CHUNKINITFREE */
+
+/* ************************************************************************** */
+
+#define FREE_CHUNK_HDR(n) mng_retcode n (mng_datap   pData,    \
+                                         mng_chunkp  pHeader)
+
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+FREE_CHUNK_HDR (mng_free_general) ;
+#else /* MNG_OPTIMIZE_CHUNKINITFREE */
+FREE_CHUNK_HDR (mng_free_ihdr) ;
+FREE_CHUNK_HDR (mng_free_plte) ;
+FREE_CHUNK_HDR (mng_free_iend) ;
+FREE_CHUNK_HDR (mng_free_trns) ;
+FREE_CHUNK_HDR (mng_free_gama) ;
+FREE_CHUNK_HDR (mng_free_chrm) ;
+FREE_CHUNK_HDR (mng_free_srgb) ;
+FREE_CHUNK_HDR (mng_free_bkgd) ;
+FREE_CHUNK_HDR (mng_free_phys) ;
+FREE_CHUNK_HDR (mng_free_sbit) ;
+FREE_CHUNK_HDR (mng_free_hist) ;
+FREE_CHUNK_HDR (mng_free_time) ;
+FREE_CHUNK_HDR (mng_free_mhdr) ;
+FREE_CHUNK_HDR (mng_free_mend) ;
+FREE_CHUNK_HDR (mng_free_endl) ;
+FREE_CHUNK_HDR (mng_free_defi) ;
+FREE_CHUNK_HDR (mng_free_basi) ;
+FREE_CHUNK_HDR (mng_free_clon) ;
+FREE_CHUNK_HDR (mng_free_back) ;
+FREE_CHUNK_HDR (mng_free_move) ;
+FREE_CHUNK_HDR (mng_free_clip) ;
+FREE_CHUNK_HDR (mng_free_show) ;
+FREE_CHUNK_HDR (mng_free_term) ;
+FREE_CHUNK_HDR (mng_free_fpri) ;
+FREE_CHUNK_HDR (mng_free_phyg) ;
+#ifdef MNG_INCLUDE_JNG
+FREE_CHUNK_HDR (mng_free_jhdr) ;
+FREE_CHUNK_HDR (mng_free_jsep) ;
+#endif
+#ifndef MNG_NO_DELTA_PNG
+FREE_CHUNK_HDR (mng_free_dhdr) ;
+FREE_CHUNK_HDR (mng_free_prom) ;
+FREE_CHUNK_HDR (mng_free_ipng) ;
+FREE_CHUNK_HDR (mng_free_pplt) ;
+#ifdef MNG_INCLUDE_JNG
+FREE_CHUNK_HDR (mng_free_ijng) ;
+#endif
+#endif
+FREE_CHUNK_HDR (mng_free_magn) ;
+#endif /* MNG_OPTIMIZE_CHUNKINITFREE */
+
+FREE_CHUNK_HDR (mng_free_idat) ;
+FREE_CHUNK_HDR (mng_free_iccp) ;
+FREE_CHUNK_HDR (mng_free_text) ;
+FREE_CHUNK_HDR (mng_free_ztxt) ;
+FREE_CHUNK_HDR (mng_free_itxt) ;
+FREE_CHUNK_HDR (mng_free_splt) ;
+FREE_CHUNK_HDR (mng_free_loop) ;
+#ifndef MNG_SKIPCHUNK_PAST
+FREE_CHUNK_HDR (mng_free_past) ;
+#endif
+FREE_CHUNK_HDR (mng_free_disc) ;
+FREE_CHUNK_HDR (mng_free_fram) ;
+FREE_CHUNK_HDR (mng_free_save) ;
+FREE_CHUNK_HDR (mng_free_seek) ;
+FREE_CHUNK_HDR (mng_free_expi) ;
+FREE_CHUNK_HDR (mng_free_need) ;
+#ifdef MNG_INCLUDE_JNG
+FREE_CHUNK_HDR (mng_free_jdaa) ;
+FREE_CHUNK_HDR (mng_free_jdat) ;
+#endif
+#ifndef MNG_NO_DELTA_PNG
+FREE_CHUNK_HDR (mng_free_drop) ;
+FREE_CHUNK_HDR (mng_free_dbyk) ;
+FREE_CHUNK_HDR (mng_free_ordr) ;
+#endif
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+FREE_CHUNK_HDR (mng_free_mpng) ;
+#endif
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+FREE_CHUNK_HDR (mng_free_adat) ;
+#endif
+FREE_CHUNK_HDR (mng_free_evnt) ;
+FREE_CHUNK_HDR (mng_free_unknown) ;
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_WRITE_PROCS
+
+#define ASSIGN_CHUNK_HDR(n) mng_retcode n (mng_datap   pData,    \
+                                           mng_chunkp  pChunkto, \
+                                           mng_chunkp  pChunkfrom)
+
+#ifdef MNG_OPTIMIZE_CHUNKASSIGN
+ASSIGN_CHUNK_HDR (mng_assign_general) ;
+#else /* MNG_OPTIMIZE_CHUNKASSIGN */
+ASSIGN_CHUNK_HDR (mng_assign_ihdr) ;
+ASSIGN_CHUNK_HDR (mng_assign_plte) ;
+ASSIGN_CHUNK_HDR (mng_assign_iend) ;
+ASSIGN_CHUNK_HDR (mng_assign_trns) ;
+ASSIGN_CHUNK_HDR (mng_assign_gama) ;
+ASSIGN_CHUNK_HDR (mng_assign_chrm) ;
+ASSIGN_CHUNK_HDR (mng_assign_srgb) ;
+ASSIGN_CHUNK_HDR (mng_assign_bkgd) ;
+ASSIGN_CHUNK_HDR (mng_assign_phys) ;
+ASSIGN_CHUNK_HDR (mng_assign_sbit) ;
+ASSIGN_CHUNK_HDR (mng_assign_hist) ;
+ASSIGN_CHUNK_HDR (mng_assign_time) ;
+ASSIGN_CHUNK_HDR (mng_assign_mhdr) ;
+ASSIGN_CHUNK_HDR (mng_assign_mend) ;
+ASSIGN_CHUNK_HDR (mng_assign_endl) ;
+ASSIGN_CHUNK_HDR (mng_assign_defi) ;
+ASSIGN_CHUNK_HDR (mng_assign_basi) ;
+ASSIGN_CHUNK_HDR (mng_assign_clon) ;
+ASSIGN_CHUNK_HDR (mng_assign_back) ;
+ASSIGN_CHUNK_HDR (mng_assign_move) ;
+ASSIGN_CHUNK_HDR (mng_assign_clip) ;
+ASSIGN_CHUNK_HDR (mng_assign_show) ;
+ASSIGN_CHUNK_HDR (mng_assign_term) ;
+ASSIGN_CHUNK_HDR (mng_assign_fpri) ;
+ASSIGN_CHUNK_HDR (mng_assign_phyg) ;
+#ifdef MNG_INCLUDE_JNG
+ASSIGN_CHUNK_HDR (mng_assign_jhdr) ;
+ASSIGN_CHUNK_HDR (mng_assign_jsep) ;
+#endif
+#ifndef MNG_NO_DELTA_PNG
+ASSIGN_CHUNK_HDR (mng_assign_dhdr) ;
+ASSIGN_CHUNK_HDR (mng_assign_prom) ;
+ASSIGN_CHUNK_HDR (mng_assign_ipng) ;
+ASSIGN_CHUNK_HDR (mng_assign_pplt) ;
+#ifdef MNG_INCLUDE_JNG
+ASSIGN_CHUNK_HDR (mng_assign_ijng) ;
+#endif
+#endif
+ASSIGN_CHUNK_HDR (mng_assign_magn) ;
+#endif /* MNG_OPTIMIZE_CHUNKASSIGN */
+
+ASSIGN_CHUNK_HDR (mng_assign_idat) ;
+ASSIGN_CHUNK_HDR (mng_assign_iccp) ;
+ASSIGN_CHUNK_HDR (mng_assign_text) ;
+ASSIGN_CHUNK_HDR (mng_assign_ztxt) ;
+ASSIGN_CHUNK_HDR (mng_assign_itxt) ;
+ASSIGN_CHUNK_HDR (mng_assign_splt) ;
+ASSIGN_CHUNK_HDR (mng_assign_loop) ;
+#ifndef MNG_SKIPCHUNK_PAST
+ASSIGN_CHUNK_HDR (mng_assign_past) ;
+#endif
+ASSIGN_CHUNK_HDR (mng_assign_disc) ;
+ASSIGN_CHUNK_HDR (mng_assign_fram) ;
+ASSIGN_CHUNK_HDR (mng_assign_save) ;
+ASSIGN_CHUNK_HDR (mng_assign_seek) ;
+ASSIGN_CHUNK_HDR (mng_assign_need) ;
+ASSIGN_CHUNK_HDR (mng_assign_expi) ;
+#ifdef MNG_INCLUDE_JNG
+ASSIGN_CHUNK_HDR (mng_assign_jdaa) ;
+ASSIGN_CHUNK_HDR (mng_assign_jdat) ;
+#endif
+#ifndef MNG_NO_DELTA_PNG
+ASSIGN_CHUNK_HDR (mng_assign_drop) ;
+ASSIGN_CHUNK_HDR (mng_assign_dbyk) ;
+ASSIGN_CHUNK_HDR (mng_assign_ordr) ;
+#endif
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+ASSIGN_CHUNK_HDR (mng_assign_mpng) ;
+#endif
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+ASSIGN_CHUNK_HDR (mng_assign_ahdr) ;
+ASSIGN_CHUNK_HDR (mng_assign_adat) ;
+#endif
+ASSIGN_CHUNK_HDR (mng_assign_evnt) ;
+ASSIGN_CHUNK_HDR (mng_assign_unknown) ;
+
+/* ************************************************************************** */
+
+#else /* MNG_INCLUDE_WRITE_PROCS */
+#define mng_assign_general 0
+#define mng_assign_ihdr 0
+#define mng_assign_plte 0
+#define mng_assign_idat 0
+#define mng_assign_iend 0
+#define mng_assign_trns 0
+#define mng_assign_gama 0
+#define mng_assign_chrm 0
+#define mng_assign_srgb 0
+#define mng_assign_iccp 0
+#define mng_assign_text 0
+#define mng_assign_ztxt 0
+#define mng_assign_itxt 0
+#define mng_assign_bkgd 0
+#define mng_assign_phys 0
+#define mng_assign_sbit 0
+#define mng_assign_splt 0
+#define mng_assign_hist 0
+#define mng_assign_time 0
+#define mng_assign_mhdr 0
+#define mng_assign_mend 0
+#define mng_assign_loop 0
+#define mng_assign_endl 0
+#define mng_assign_defi 0
+#define mng_assign_basi 0
+#define mng_assign_clon 0
+#ifndef MNG_SKIPCHUNK_PAST
+#define mng_assign_past 0
+#endif
+#define mng_assign_disc 0
+#define mng_assign_back 0
+#define mng_assign_fram 0
+#define mng_assign_move 0
+#define mng_assign_clip 0
+#define mng_assign_show 0
+#define mng_assign_term 0
+#define mng_assign_save 0
+#define mng_assign_seek 0
+#define mng_assign_expi 0
+#define mng_assign_fpri 0
+#define mng_assign_phyg 0
+#ifdef MNG_INCLUDE_JNG
+#define mng_assign_jhdr 0
+#define mng_assign_jdaa 0
+#define mng_assign_jdat 0
+#define mng_assign_jsep 0
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#define mng_assign_dhdr 0
+#define mng_assign_prom 0
+#define mng_assign_ipng 0
+#define mng_assign_pplt 0
+#ifdef MNG_INCLUDE_JNG
+#define mng_assign_ijng 0
+#endif
+#define mng_assign_drop 0
+#define mng_assign_dbyk 0
+#define mng_assign_ordr 0
+#endif
+#define mng_assign_magn 0
+#define mng_assign_need 0
+#define mng_assign_mpng 0
+#define mng_assign_ahdr 0
+#define mng_assign_adat 0
+#define mng_assign_evnt 0
+#define mng_assign_unknown 0
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+
+/* ************************************************************************** */
+
+#endif /* _libmng_chunk_prc_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_chunk_xs.c b/files/Source/LibMNG/libmng_chunk_xs.c
new file mode 100644
index 0000000..1311409
--- /dev/null
+++ b/files/Source/LibMNG/libmng_chunk_xs.c
@@ -0,0 +1,7016 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_chunk_xs.c         copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : chunk access functions (implementation)                    * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the chunk access functions               * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/06/2000 - G.Juyn                                * */
+/* *             - changed and filled iterate-chunk function                * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - fixed calling convention                                 * */
+/* *             - added getchunk functions                                 * */
+/* *             - added putchunk functions                                 * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added empty-chunk put-routines                           * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *             0.5.1 - 05/15/2000 - G.Juyn                                * */
+/* *             - added getimgdata & putimgdata functions                  * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/19/2000 - G.Juyn                                * */
+/* *             - B004 - fixed problem with MNG_SUPPORT_WRITE not defined  * */
+/* *               also for MNG_SUPPORT_WRITE without MNG_INCLUDE_JNG       * */
+/* *             - Cleaned up some code regarding mixed support             * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/19/2000 - G.Juyn                                * */
+/* *             - fixed creation-code                                      * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *             - added function to set simplicity field                   * */
+/* *             - fixed putchunk_unknown() function                        * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/07/2000 - G.Juyn                                * */
+/* *             - B111300 - fixup for improved portability                 * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 10/20/2000 - G.Juyn                                * */
+/* *             - fixed putchunk_plte() to set bEmpty parameter            * */
+/* *                                                                        * */
+/* *             0.9.5 - 01/25/2001 - G.Juyn                                * */
+/* *             - fixed some small compiler warnings (thanks Nikki)        * */
+/* *                                                                        * */
+/* *             1.0.5 - 09/07/2002 - G.Juyn                                * */
+/* *             - B578940 - unimplemented functions return errorcode       * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             - added HLAPI function to copy chunks                      * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - added check for TERM placement during create/write       * */
+/* *             1.0.5 - 11/28/2002 - G.Juyn                                * */
+/* *             - fixed definition of iMethodX/Y for MAGN chunk            * */
+/* *                                                                        * */
+/* *             1.0.6 - 05/25/2003 - G.R-P                                 * */
+/* *             - added MNG_SKIPCHUNK_cHNK footprint optimizations         * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added MNG_NO_DELTA_PNG reduction and more SKIPCHUNK      * */
+/* *               optimizations                                            * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *             1.0.6 - 08/17/2003 - G.R-P                                 * */
+/* *             - added conditionals around non-VLC chunk support          * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/01/2004 - G.Juyn                                * */
+/* *             - added missing get-/put-chunk-jdaa                        * */
+/* *             1.0.8 - 08/02/2004 - G.Juyn                                * */
+/* *             - added conditional to allow easier writing of large MNG's * */
+/* *                                                                        * */
+/* *             1.0.9 - 09/17/2004 - G.R-P                                 * */
+/* *             - added two more conditionals                              * */
+/* *             1.0.9 - 09/25/2004 - G.Juyn                                * */
+/* *             - replaced MNG_TWEAK_LARGE_FILES with permanent solution   * */
+/* *             1.0.9 - 17/14/2004 - G.Juyn                                * */
+/* *             - fixed PPLT getchunk/putchunk routines                    * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKINITFREE             * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_memory.h"
+#include "libmng_chunks.h"
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+#include "libmng_chunk_descr.h"
+#endif
+#include "libmng_chunk_prc.h"
+#include "libmng_chunk_io.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_ACCESS_CHUNKS
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_iterate_chunks (mng_handle       hHandle,
+                                         mng_uint32       iChunkseq,
+                                         mng_iteratechunk fProc)
+{
+  mng_uint32  iSeq;
+  mng_chunkid iChunkname;
+  mng_datap   pData;
+  mng_chunkp  pChunk;
+  mng_bool    bCont;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_ITERATE_CHUNKS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+  iSeq   = 0;
+  bCont  = MNG_TRUE;
+  pChunk = pData->pFirstchunk;         /* get the first chunk */
+                                       /* as long as there are some more */
+  while ((pChunk) && (bCont))          /* and the app didn't signal a stop */
+  {
+    if (iSeq >= iChunkseq)             /* reached the first target ? */
+    {                                  /* then call this and next ones back in... */
+      iChunkname = ((mng_chunk_headerp)pChunk)->iChunkname;
+      bCont      = fProc (hHandle, (mng_handle)pChunk, iChunkname, iSeq);
+    }
+
+    iSeq++;                            /* next one */
+    pChunk = ((mng_chunk_headerp)pChunk)->pNext;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_ITERATE_CHUNKS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_copy_chunk (mng_handle hHandle,
+                                     mng_handle hChunk,
+                                     mng_handle hHandleOut)
+{
+  mng_datap   pDataOut;
+  mng_chunkp  pChunk;
+  mng_chunkp  pChunkOut;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_COPY_CHUNK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handles */
+  MNG_VALIDHANDLE (hHandleOut)
+
+  pDataOut = (mng_datap)hHandleOut;    /* make outhandle addressable */
+  pChunk   = (mng_chunkp)hChunk;       /* address the chunk */
+
+  if (!pDataOut->bCreating)            /* aren't we creating a new file ? */
+    MNG_ERROR (pDataOut, MNG_FUNCTIONINVALID)
+                                       /* create a new chunk */
+  iRetcode = ((mng_createchunk)((mng_chunk_headerp)pChunk)->fCreate)
+                        (pDataOut, ((mng_chunk_headerp)pChunk), &pChunkOut);
+  if (!iRetcode)                       /* assign the chunk-specific data */
+    iRetcode = ((mng_assignchunk)((mng_chunk_headerp)pChunk)->fAssign)
+                          (pDataOut, pChunkOut, pChunk);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode; 
+
+  mng_add_chunk (pDataOut, pChunkOut); /* and put it in the output-stream */
+
+                                       /* could it be the end of the chain ? */
+  if (((mng_chunk_headerp)pChunkOut)->iChunkname == MNG_UINT_IEND)
+  {
+#ifdef MNG_INCLUDE_JNG
+    if ((pDataOut->iFirstchunkadded == MNG_UINT_IHDR) ||
+        (pDataOut->iFirstchunkadded == MNG_UINT_JHDR)    )
+#else
+    if (pDataOut->iFirstchunkadded == MNG_UINT_IHDR)
+#endif
+      pDataOut->bCreating = MNG_FALSE; /* right; this should be the last chunk !!! */
+  }
+
+  if (((mng_chunk_headerp)pChunkOut)->iChunkname == MNG_UINT_MEND)
+    pDataOut->bCreating = MNG_FALSE;   /* definitely this should be the last !!! */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_COPY_CHUNK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getchunk_ihdr (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iWidth,
+                                        mng_uint32 *iHeight,
+                                        mng_uint8  *iBitdepth,
+                                        mng_uint8  *iColortype,
+                                        mng_uint8  *iCompression,
+                                        mng_uint8  *iFilter,
+                                        mng_uint8  *iInterlace)
+{
+  mng_datap pData;
+  mng_ihdrp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_IHDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_ihdrp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_IHDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iWidth       = pChunk->iWidth;      /* fill the fields */
+  *iHeight      = pChunk->iHeight;
+  *iBitdepth    = pChunk->iBitdepth;
+  *iColortype   = pChunk->iColortype;
+  *iCompression = pChunk->iCompression;
+  *iFilter      = pChunk->iFilter;
+  *iInterlace   = pChunk->iInterlace;
+
+                                       /* fill the chunk */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_IHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getchunk_plte (mng_handle   hHandle,
+                                        mng_handle   hChunk,
+                                        mng_uint32   *iCount,
+                                        mng_palette8 *aPalette)
+{
+  mng_datap pData;
+  mng_pltep pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PLTE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_pltep)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_PLTE)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iCount = pChunk->iEntrycount;       /* fill the fields */
+
+  MNG_COPY (*aPalette, pChunk->aEntries, sizeof (mng_palette8));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PLTE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getchunk_idat (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iRawlen,
+                                        mng_ptr    *pRawdata)
+{
+  mng_datap pData;
+  mng_idatp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_IDAT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_idatp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_IDAT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iRawlen  = pChunk->iDatasize;       /* fill the fields */
+  *pRawdata = pChunk->pData;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_IDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getchunk_trns (mng_handle   hHandle,
+                                        mng_handle   hChunk,
+                                        mng_bool     *bEmpty,
+                                        mng_bool     *bGlobal,
+                                        mng_uint8    *iType,
+                                        mng_uint32   *iCount,
+                                        mng_uint8arr *aAlphas,
+                                        mng_uint16   *iGray,
+                                        mng_uint16   *iRed,
+                                        mng_uint16   *iGreen,
+                                        mng_uint16   *iBlue,
+                                        mng_uint32   *iRawlen,
+                                        mng_uint8arr *aRawdata)
+{
+  mng_datap pData;
+  mng_trnsp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TRNS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_trnsp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_tRNS)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty   = pChunk->bEmpty;          /* fill the fields */
+  *bGlobal  = pChunk->bGlobal;
+  *iType    = pChunk->iType;
+  *iCount   = pChunk->iCount;
+  *iGray    = pChunk->iGray;
+  *iRed     = pChunk->iRed;
+  *iGreen   = pChunk->iGreen;
+  *iBlue    = pChunk->iBlue;
+  *iRawlen  = pChunk->iRawlen;
+
+  MNG_COPY (*aAlphas,  pChunk->aEntries, sizeof (mng_uint8arr));
+  MNG_COPY (*aRawdata, pChunk->aRawdata, sizeof (mng_uint8arr));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TRNS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_gAMA
+mng_retcode MNG_DECL mng_getchunk_gama (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint32 *iGamma)
+{
+  mng_datap pData;
+  mng_gamap pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_GAMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_gamap)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_gAMA)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty = pChunk->bEmpty;            /* fill the fields */
+  *iGamma = pChunk->iGamma;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_GAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+mng_retcode MNG_DECL mng_getchunk_chrm (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint32 *iWhitepointx,
+                                        mng_uint32 *iWhitepointy,
+                                        mng_uint32 *iRedx,
+                                        mng_uint32 *iRedy,
+                                        mng_uint32 *iGreenx,
+                                        mng_uint32 *iGreeny,
+                                        mng_uint32 *iBluex,
+                                        mng_uint32 *iBluey)
+{
+  mng_datap pData;
+  mng_chrmp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CHRM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_chrmp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_cHRM)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty       = pChunk->bEmpty;      /* fill the fields */     
+  *iWhitepointx = pChunk->iWhitepointx;
+  *iWhitepointy = pChunk->iWhitepointy;
+  *iRedx        = pChunk->iRedx;
+  *iRedy        = pChunk->iRedy;
+  *iGreenx      = pChunk->iGreenx;
+  *iGreeny      = pChunk->iGreeny;
+  *iBluex       = pChunk->iBluex;
+  *iBluey       = pChunk->iBluey;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CHRM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sRGB
+mng_retcode MNG_DECL mng_getchunk_srgb (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint8  *iRenderingintent)
+{
+  mng_datap pData;
+  mng_srgbp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SRGB, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_srgbp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_sRGB)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty           = pChunk->bEmpty;  /* fill the fields */        
+  *iRenderingintent = pChunk->iRenderingintent;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+mng_retcode MNG_DECL mng_getchunk_iccp (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint32 *iNamesize,
+                                        mng_pchar  *zName,
+                                        mng_uint8  *iCompression,
+                                        mng_uint32 *iProfilesize,
+                                        mng_ptr    *pProfile)
+{
+  mng_datap pData;
+  mng_iccpp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ICCP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_iccpp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_iCCP)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty       = pChunk->bEmpty;      /* fill the fields */     
+  *iNamesize    = pChunk->iNamesize;
+  *zName        = pChunk->zName;
+  *iCompression = pChunk->iCompression;
+  *iProfilesize = pChunk->iProfilesize;
+  *pProfile     = pChunk->pProfile;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ICCP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tEXt
+mng_retcode MNG_DECL mng_getchunk_text (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iKeywordsize,
+                                        mng_pchar  *zKeyword,
+                                        mng_uint32 *iTextsize,
+                                        mng_pchar  *zText)
+{
+  mng_datap pData;
+  mng_textp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TEXT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_textp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_tEXt)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+                                       /* fill the fields */
+  *iKeywordsize = pChunk->iKeywordsize;
+  *zKeyword     = pChunk->zKeyword;
+  *iTextsize    = pChunk->iTextsize;
+  *zText        = pChunk->zText;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TEXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_zTXt
+mng_retcode MNG_DECL mng_getchunk_ztxt (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iKeywordsize,
+                                        mng_pchar  *zKeyword,
+                                        mng_uint8  *iCompression,
+                                        mng_uint32 *iTextsize,
+                                        mng_pchar  *zText)
+{
+  mng_datap pData;
+  mng_ztxtp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ZTXT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_ztxtp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_zTXt)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+                                       /* fill the fields */
+  *iKeywordsize = pChunk->iKeywordsize;
+  *zKeyword     = pChunk->zKeyword;
+  *iCompression = pChunk->iCompression;
+  *iTextsize    = pChunk->iTextsize;
+  *zText        = pChunk->zText;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ZTXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iTXt
+mng_retcode MNG_DECL mng_getchunk_itxt (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iKeywordsize,
+                                        mng_pchar  *zKeyword,
+                                        mng_uint8  *iCompressionflag,
+                                        mng_uint8  *iCompressionmethod,
+                                        mng_uint32 *iLanguagesize,
+                                        mng_pchar  *zLanguage,
+                                        mng_uint32 *iTranslationsize,
+                                        mng_pchar  *zTranslation,
+                                        mng_uint32 *iTextsize,
+                                        mng_pchar  *zText)
+{
+  mng_datap pData;
+  mng_itxtp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ITXT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_itxtp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_iTXt)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+                                       /* fill the fields */
+  *iKeywordsize       = pChunk->iKeywordsize;
+  *zKeyword           = pChunk->zKeyword;
+  *iCompressionflag   = pChunk->iCompressionflag;
+  *iCompressionmethod = pChunk->iCompressionmethod;
+  *iLanguagesize      = pChunk->iLanguagesize;
+  *zLanguage          = pChunk->zLanguage;
+  *iTranslationsize   = pChunk->iTranslationsize;
+  *zTranslation       = pChunk->zTranslation;
+  *iTextsize          = pChunk->iTextsize;
+  *zText              = pChunk->zText;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ITXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_bKGD
+mng_retcode MNG_DECL mng_getchunk_bkgd (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint8  *iType,
+                                        mng_uint8  *iIndex,
+                                        mng_uint16 *iGray,
+                                        mng_uint16 *iRed,
+                                        mng_uint16 *iGreen,
+                                        mng_uint16 *iBlue)
+{
+  mng_datap pData;
+  mng_bkgdp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BKGD, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_bkgdp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_bKGD)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty = pChunk->bEmpty;            /* fill the fields */
+  *iType  = pChunk->iType;
+  *iIndex = pChunk->iIndex;
+  *iGray  = pChunk->iGray;
+  *iRed   = pChunk->iRed;
+  *iGreen = pChunk->iGreen;
+  *iBlue  = pChunk->iBlue;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYs
+mng_retcode MNG_DECL mng_getchunk_phys (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint32 *iSizex,
+                                        mng_uint32 *iSizey,
+                                        mng_uint8  *iUnit)
+{
+  mng_datap pData;
+  mng_physp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PHYS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_physp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_pHYs)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty = pChunk->bEmpty;            /* fill the fields */
+  *iSizex = pChunk->iSizex;
+  *iSizey = pChunk->iSizey;
+  *iUnit  = pChunk->iUnit;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PHYS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sBIT
+mng_retcode MNG_DECL mng_getchunk_sbit (mng_handle    hHandle,
+                                        mng_handle    hChunk,
+                                        mng_bool      *bEmpty,
+                                        mng_uint8     *iType,
+                                        mng_uint8arr4 *aBits)
+{
+  mng_datap pData;
+  mng_sbitp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SBIT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_sbitp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_sBIT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty     = pChunk->bEmpty;
+  *iType      = pChunk->iType;
+  (*aBits)[0] = pChunk->aBits[0];
+  (*aBits)[1] = pChunk->aBits[1];
+  (*aBits)[2] = pChunk->aBits[2];
+  (*aBits)[3] = pChunk->aBits[3];
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SBIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+mng_retcode MNG_DECL mng_getchunk_splt (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint32 *iNamesize,
+                                        mng_pchar  *zName,
+                                        mng_uint8  *iSampledepth,
+                                        mng_uint32 *iEntrycount,
+                                        mng_ptr    *pEntries)
+{
+  mng_datap pData;
+  mng_spltp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SPLT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_spltp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_sPLT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty       = pChunk->bEmpty;      /* fill the fields */      
+  *iNamesize    = pChunk->iNamesize;
+  *zName        = pChunk->zName;
+  *iSampledepth = pChunk->iSampledepth;
+  *iEntrycount  = pChunk->iEntrycount;
+  *pEntries     = pChunk->pEntries;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_hIST
+mng_retcode MNG_DECL mng_getchunk_hist (mng_handle    hHandle,
+                                        mng_handle    hChunk,
+                                        mng_uint32    *iEntrycount,
+                                        mng_uint16arr *aEntries)
+{
+  mng_datap pData;
+  mng_histp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_HIST, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_histp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_hIST)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iEntrycount = pChunk->iEntrycount;  /* fill the fields */
+
+  MNG_COPY (*aEntries, pChunk->aEntries, sizeof (mng_uint16arr));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_HIST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tIME
+mng_retcode MNG_DECL mng_getchunk_time (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iYear,
+                                        mng_uint8  *iMonth,
+                                        mng_uint8  *iDay,
+                                        mng_uint8  *iHour,
+                                        mng_uint8  *iMinute,
+                                        mng_uint8  *iSecond)
+{
+  mng_datap pData;
+  mng_timep pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TIME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_timep)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_tIME)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iYear   = pChunk->iYear;            /* fill the fields */ 
+  *iMonth  = pChunk->iMonth;
+  *iDay    = pChunk->iDay;
+  *iHour   = pChunk->iHour;
+  *iMinute = pChunk->iMinute;
+  *iSecond = pChunk->iSecond;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TIME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getchunk_mhdr (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iWidth,
+                                        mng_uint32 *iHeight,
+                                        mng_uint32 *iTicks,
+                                        mng_uint32 *iLayercount,
+                                        mng_uint32 *iFramecount,
+                                        mng_uint32 *iPlaytime,
+                                        mng_uint32 *iSimplicity)
+{
+  mng_datap pData;
+  mng_mhdrp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MHDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_mhdrp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iWidth      = pChunk->iWidth;       /* fill the fields */   
+  *iHeight     = pChunk->iHeight;
+  *iTicks      = pChunk->iTicks;
+  *iLayercount = pChunk->iLayercount;
+  *iFramecount = pChunk->iFramecount;
+  *iPlaytime   = pChunk->iPlaytime;
+  *iSimplicity = pChunk->iSimplicity;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+mng_retcode MNG_DECL mng_getchunk_loop (mng_handle  hHandle,
+                                        mng_handle  hChunk,
+                                        mng_uint8   *iLevel,
+                                        mng_uint32  *iRepeat,
+                                        mng_uint8   *iTermination,
+                                        mng_uint32  *iItermin,
+                                        mng_uint32  *iItermax,
+                                        mng_uint32  *iCount,
+                                        mng_uint32p *pSignals)
+{
+  mng_datap pData;
+  mng_loopp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_LOOP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_loopp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_LOOP)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iLevel       = pChunk->iLevel;      /* fill teh fields */
+  *iRepeat      = pChunk->iRepeat;
+  *iTermination = pChunk->iTermination;
+  *iItermin     = pChunk->iItermin;
+  *iItermax     = pChunk->iItermax;
+  *iCount       = pChunk->iCount;
+  *pSignals     = pChunk->pSignals;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_LOOP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getchunk_endl (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint8  *iLevel)
+{
+  mng_datap pData;
+  mng_endlp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ENDL, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_endlp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_ENDL)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iLevel = pChunk->iLevel;            /* fill the field */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ENDL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DEFI
+mng_retcode MNG_DECL mng_getchunk_defi (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iObjectid,
+                                        mng_uint8  *iDonotshow,
+                                        mng_uint8  *iConcrete,
+                                        mng_bool   *bHasloca,
+                                        mng_int32  *iXlocation,
+                                        mng_int32  *iYlocation,
+                                        mng_bool   *bHasclip,
+                                        mng_int32  *iLeftcb,
+                                        mng_int32  *iRightcb,
+                                        mng_int32  *iTopcb,
+                                        mng_int32  *iBottomcb)
+{
+  mng_datap pData;
+  mng_defip pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DEFI, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_defip)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_DEFI)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iObjectid  = pChunk->iObjectid;     /* fill the fields */
+  *iDonotshow = pChunk->iDonotshow;
+  *iConcrete  = pChunk->iConcrete;
+  *bHasloca   = pChunk->bHasloca;
+  *iXlocation = pChunk->iXlocation;
+  *iYlocation = pChunk->iYlocation;
+  *bHasclip   = pChunk->bHasclip;
+  *iLeftcb    = pChunk->iLeftcb;
+  *iRightcb   = pChunk->iRightcb;
+  *iTopcb     = pChunk->iTopcb;
+  *iBottomcb  = pChunk->iBottomcb;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BASI
+mng_retcode MNG_DECL mng_getchunk_basi (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iWidth,
+                                        mng_uint32 *iHeight,
+                                        mng_uint8  *iBitdepth,
+                                        mng_uint8  *iColortype,
+                                        mng_uint8  *iCompression,
+                                        mng_uint8  *iFilter,
+                                        mng_uint8  *iInterlace,
+                                        mng_uint16 *iRed,
+                                        mng_uint16 *iGreen,
+                                        mng_uint16 *iBlue,
+                                        mng_uint16 *iAlpha,
+                                        mng_uint8  *iViewable)
+{
+  mng_datap pData;
+  mng_basip pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BASI, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_basip)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_BASI)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iWidth       = pChunk->iWidth;      /* fill the fields */
+  *iHeight      = pChunk->iHeight;
+  *iBitdepth    = pChunk->iBitdepth;
+  *iColortype   = pChunk->iColortype;
+  *iCompression = pChunk->iCompression;
+  *iFilter      = pChunk->iFilter;
+  *iInterlace   = pChunk->iInterlace;
+  *iRed         = pChunk->iRed;
+  *iGreen       = pChunk->iGreen;
+  *iBlue        = pChunk->iBlue;
+  *iAlpha       = pChunk->iAlpha;
+  *iViewable    = pChunk->iViewable;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BASI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLON
+mng_retcode MNG_DECL mng_getchunk_clon (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iSourceid,
+                                        mng_uint16 *iCloneid,
+                                        mng_uint8  *iClonetype,
+                                        mng_uint8  *iDonotshow,
+                                        mng_uint8  *iConcrete,
+                                        mng_bool   *bHasloca,
+                                        mng_uint8  *iLocationtype,
+                                        mng_int32  *iLocationx,
+                                        mng_int32  *iLocationy)
+{
+  mng_datap pData;
+  mng_clonp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CLON, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_clonp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_CLON)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iSourceid     = pChunk->iSourceid;  /* fill the fields */  
+  *iCloneid      = pChunk->iCloneid;
+  *iClonetype    = pChunk->iClonetype;
+  *iDonotshow    = pChunk->iDonotshow;
+  *iConcrete     = pChunk->iConcrete;
+  *bHasloca      = pChunk->bHasloca;
+  *iLocationtype = pChunk->iLocationtype;
+  *iLocationx    = pChunk->iLocationx;
+  *iLocationy    = pChunk->iLocationy;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CLON, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode MNG_DECL mng_getchunk_past (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iDestid,
+                                        mng_uint8  *iTargettype,
+                                        mng_int32  *iTargetx,
+                                        mng_int32  *iTargety,
+                                        mng_uint32 *iCount)
+{
+  mng_datap pData;
+  mng_pastp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PAST, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_pastp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_PAST)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iDestid     = pChunk->iDestid;       /* fill the fields */
+  *iTargettype = pChunk->iTargettype;
+  *iTargetx    = pChunk->iTargetx;
+  *iTargety    = pChunk->iTargety;
+  *iCount      = pChunk->iCount;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PAST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode MNG_DECL mng_getchunk_past_src (mng_handle hHandle,
+                                            mng_handle hChunk,
+                                            mng_uint32 iEntry,
+                                            mng_uint16 *iSourceid,
+                                            mng_uint8  *iComposition,
+                                            mng_uint8  *iOrientation,
+                                            mng_uint8  *iOffsettype,
+                                            mng_int32  *iOffsetx,
+                                            mng_int32  *iOffsety,
+                                            mng_uint8  *iBoundarytype,
+                                            mng_int32  *iBoundaryl,
+                                            mng_int32  *iBoundaryr,
+                                            mng_int32  *iBoundaryt,
+                                            mng_int32  *iBoundaryb)
+{
+  mng_datap        pData;
+  mng_pastp        pChunk;
+  mng_past_sourcep pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PAST_SRC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_pastp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_PAST)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  if (iEntry >= pChunk->iCount)        /* valid index ? */
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+                                       /* address the entry */
+  pEntry         = pChunk->pSources + iEntry;
+
+  *iSourceid     = pEntry->iSourceid;  /* fill the fields */
+  *iComposition  = pEntry->iComposition;
+  *iOrientation  = pEntry->iOrientation;
+  *iOffsettype   = pEntry->iOffsettype;
+  *iOffsetx      = pEntry->iOffsetx;
+  *iOffsety      = pEntry->iOffsety;
+  *iBoundarytype = pEntry->iBoundarytype;
+  *iBoundaryl    = pEntry->iBoundaryl;
+  *iBoundaryr    = pEntry->iBoundaryr;
+  *iBoundaryt    = pEntry->iBoundaryt;
+  *iBoundaryb    = pEntry->iBoundaryb;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PAST_SRC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+mng_retcode MNG_DECL mng_getchunk_disc (mng_handle  hHandle,
+                                        mng_handle  hChunk,
+                                        mng_uint32  *iCount,
+                                        mng_uint16p *pObjectids)
+{
+  mng_datap pData;
+  mng_discp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DISC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_discp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_DISC)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iCount     = pChunk->iCount;        /* fill the fields */
+  *pObjectids = pChunk->pObjectids;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DISC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BACK
+mng_retcode MNG_DECL mng_getchunk_back (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iRed,
+                                        mng_uint16 *iGreen,
+                                        mng_uint16 *iBlue,
+                                        mng_uint8  *iMandatory,
+                                        mng_uint16 *iImageid,
+                                        mng_uint8  *iTile)
+{
+  mng_datap pData;
+  mng_backp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BACK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_backp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_BACK)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iRed       = pChunk->iRed;          /* fill the fields */
+  *iGreen     = pChunk->iGreen;
+  *iBlue      = pChunk->iBlue;
+  *iMandatory = pChunk->iMandatory;
+  *iImageid   = pChunk->iImageid;
+  *iTile      = pChunk->iTile;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+mng_retcode MNG_DECL mng_getchunk_fram (mng_handle  hHandle,
+                                        mng_handle  hChunk,
+                                        mng_bool    *bEmpty,
+                                        mng_uint8   *iMode,
+                                        mng_uint32  *iNamesize,
+                                        mng_pchar   *zName,
+                                        mng_uint8   *iChangedelay,
+                                        mng_uint8   *iChangetimeout,
+                                        mng_uint8   *iChangeclipping,
+                                        mng_uint8   *iChangesyncid,
+                                        mng_uint32  *iDelay,
+                                        mng_uint32  *iTimeout,
+                                        mng_uint8   *iBoundarytype,
+                                        mng_int32   *iBoundaryl,
+                                        mng_int32   *iBoundaryr,
+                                        mng_int32   *iBoundaryt,
+                                        mng_int32   *iBoundaryb,
+                                        mng_uint32  *iCount,
+                                        mng_uint32p *pSyncids)
+{
+  mng_datap pData;
+  mng_framp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_FRAM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_framp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_FRAM)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty          = pChunk->bEmpty;   /* fill the fields */      
+  *iMode           = pChunk->iMode;
+  *iNamesize       = pChunk->iNamesize;
+  *zName           = pChunk->zName;
+  *iChangedelay    = pChunk->iChangedelay;
+  *iChangetimeout  = pChunk->iChangetimeout;
+  *iChangeclipping = pChunk->iChangeclipping;
+  *iChangesyncid   = pChunk->iChangesyncid;
+  *iDelay          = pChunk->iDelay;
+  *iTimeout        = pChunk->iTimeout;
+  *iBoundarytype   = pChunk->iBoundarytype;
+  *iBoundaryl      = pChunk->iBoundaryl;
+  *iBoundaryr      = pChunk->iBoundaryr;
+  *iBoundaryt      = pChunk->iBoundaryt;
+  *iBoundaryb      = pChunk->iBoundaryb;
+  *iCount          = pChunk->iCount;
+  *pSyncids        = pChunk->pSyncids;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_FRAM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MOVE
+mng_retcode MNG_DECL mng_getchunk_move (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iFirstid,
+                                        mng_uint16 *iLastid,
+                                        mng_uint8  *iMovetype,
+                                        mng_int32  *iMovex,
+                                        mng_int32  *iMovey)
+{
+  mng_datap pData;
+  mng_movep pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MOVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_movep)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_MOVE)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iFirstid  = pChunk->iFirstid;       /* fill the fields */
+  *iLastid   = pChunk->iLastid;
+  *iMovetype = pChunk->iMovetype;
+  *iMovex    = pChunk->iMovex;
+  *iMovey    = pChunk->iMovey;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MOVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLIP
+mng_retcode MNG_DECL mng_getchunk_clip (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iFirstid,
+                                        mng_uint16 *iLastid,
+                                        mng_uint8  *iCliptype,
+                                        mng_int32  *iClipl,
+                                        mng_int32  *iClipr,
+                                        mng_int32  *iClipt,
+                                        mng_int32  *iClipb)
+{
+  mng_datap pData;
+  mng_clipp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CLIP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_clipp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_CLIP)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iFirstid  = pChunk->iFirstid;       /* fill the fields */
+  *iLastid   = pChunk->iLastid;
+  *iCliptype = pChunk->iCliptype;
+  *iClipl    = pChunk->iClipl;
+  *iClipr    = pChunk->iClipr;
+  *iClipt    = pChunk->iClipt;
+  *iClipb    = pChunk->iClipb;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CLIP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SHOW
+mng_retcode MNG_DECL mng_getchunk_show (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint16 *iFirstid,
+                                        mng_uint16 *iLastid,
+                                        mng_uint8  *iMode)
+{
+  mng_datap pData;
+  mng_showp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SHOW, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_showp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_SHOW)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty   = pChunk->bEmpty;          /* fill the fields */
+  *iFirstid = pChunk->iFirstid;
+  *iLastid  = pChunk->iLastid;
+  *iMode    = pChunk->iMode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SHOW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_TERM
+mng_retcode MNG_DECL mng_getchunk_term (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint8  *iTermaction,
+                                        mng_uint8  *iIteraction,
+                                        mng_uint32 *iDelay,
+                                        mng_uint32 *iItermax)
+{
+  mng_datap pData;
+  mng_termp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TERM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_termp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_TERM)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iTermaction = pChunk->iTermaction;  /* fill the fields */
+  *iIteraction = pChunk->iIteraction;
+  *iDelay      = pChunk->iDelay;
+  *iItermax    = pChunk->iItermax;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode MNG_DECL mng_getchunk_save (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint8  *iOffsettype,
+                                        mng_uint32 *iCount)
+{
+  mng_datap pData;
+  mng_savep pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SAVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_savep)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_SAVE)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty      = pChunk->bEmpty;       /* fill the fields */
+  *iOffsettype = pChunk->iOffsettype;
+  *iCount      = pChunk->iCount;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getchunk_save_entry (mng_handle     hHandle,
+                                              mng_handle     hChunk,
+                                              mng_uint32     iEntry,
+                                              mng_uint8      *iEntrytype,
+                                              mng_uint32arr2 *iOffset,
+                                              mng_uint32arr2 *iStarttime,
+                                              mng_uint32     *iLayernr,
+                                              mng_uint32     *iFramenr,
+                                              mng_uint32     *iNamesize,
+                                              mng_pchar      *zName)
+{
+  mng_datap       pData;
+  mng_savep       pChunk;
+  mng_save_entryp pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SAVE_ENTRY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_savep)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_SAVE)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  if (iEntry >= pChunk->iCount)        /* valid index ? */
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+
+  pEntry  = pChunk->pEntries + iEntry; /* address the entry */
+                                       /* fill the fields */
+  *iEntrytype      = pEntry->iEntrytype;
+  (*iOffset)[0]    = pEntry->iOffset[0];
+  (*iOffset)[1]    = pEntry->iOffset[1];
+  (*iStarttime)[0] = pEntry->iStarttime[0];
+  (*iStarttime)[1] = pEntry->iStarttime[1];
+  *iLayernr        = pEntry->iLayernr;
+  *iFramenr        = pEntry->iFramenr;
+  *iNamesize       = pEntry->iNamesize;
+  *zName           = pEntry->zName;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SAVE_ENTRY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_retcode MNG_DECL mng_getchunk_seek (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iNamesize,
+                                        mng_pchar  *zName)
+{
+  mng_datap pData;
+  mng_seekp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SEEK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_seekp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_SEEK)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iNamesize = pChunk->iNamesize;      /* fill the fields */
+  *zName     = pChunk->zName;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SEEK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_eXPI
+mng_retcode MNG_DECL mng_getchunk_expi (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iSnapshotid,
+                                        mng_uint32 *iNamesize,
+                                        mng_pchar  *zName)
+{
+  mng_datap pData;
+  mng_expip pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EXPI, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_expip)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_eXPI)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iSnapshotid = pChunk->iSnapshotid;  /* fill the fields */
+  *iNamesize   = pChunk->iNamesize;
+  *zName       = pChunk->zName;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EXPI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_fPRI
+mng_retcode MNG_DECL mng_getchunk_fpri (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint8  *iDeltatype,
+                                        mng_uint8  *iPriority)
+{
+  mng_datap pData;
+  mng_fprip pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_FPRI, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_fprip)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_fPRI)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iDeltatype = pChunk->iDeltatype;    /* fill the fields */
+  *iPriority  = pChunk->iPriority;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_FPRI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+mng_retcode MNG_DECL mng_getchunk_need (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iKeywordssize,
+                                        mng_pchar  *zKeywords)
+{
+  mng_datap pData;
+  mng_needp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_NEED, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_needp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_nEED)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+                                       /* fill the fields */
+  *iKeywordssize = pChunk->iKeywordssize;
+  *zKeywords     = pChunk->zKeywords;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_NEED, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYg
+mng_retcode MNG_DECL mng_getchunk_phyg (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_bool   *bEmpty,
+                                        mng_uint32 *iSizex,
+                                        mng_uint32 *iSizey,
+                                        mng_uint8  *iUnit)
+{
+  mng_datap pData;
+  mng_phygp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PHYG, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_phygp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_pHYg)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *bEmpty = pChunk->bEmpty;            /* fill the fields */
+  *iSizex = pChunk->iSizex;
+  *iSizey = pChunk->iSizey;
+  *iUnit  = pChunk->iUnit;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PHYG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+mng_retcode MNG_DECL mng_getchunk_jhdr (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iWidth,
+                                        mng_uint32 *iHeight,
+                                        mng_uint8  *iColortype,
+                                        mng_uint8  *iImagesampledepth,
+                                        mng_uint8  *iImagecompression,
+                                        mng_uint8  *iImageinterlace,
+                                        mng_uint8  *iAlphasampledepth,
+                                        mng_uint8  *iAlphacompression,
+                                        mng_uint8  *iAlphafilter,
+                                        mng_uint8  *iAlphainterlace)
+{
+  mng_datap pData;
+  mng_jhdrp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JHDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_jhdrp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_JHDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iWidth            = pChunk->iWidth; /* fill the fields */          
+  *iHeight           = pChunk->iHeight;
+  *iColortype        = pChunk->iColortype;
+  *iImagesampledepth = pChunk->iImagesampledepth;
+  *iImagecompression = pChunk->iImagecompression;
+  *iImageinterlace   = pChunk->iImageinterlace;
+  *iAlphasampledepth = pChunk->iAlphasampledepth;
+  *iAlphacompression = pChunk->iAlphacompression;
+  *iAlphafilter      = pChunk->iAlphafilter;
+  *iAlphainterlace   = pChunk->iAlphainterlace;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+mng_retcode MNG_DECL mng_getchunk_jdat (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iRawlen,
+                                        mng_ptr    *pRawdata)
+{
+  mng_datap pData;
+  mng_jdatp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JDAT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_jdatp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_JDAT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iRawlen  = pChunk->iDatasize;       /* fill the fields */
+  *pRawdata = pChunk->pData;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+mng_retcode MNG_DECL mng_getchunk_jdaa (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iRawlen,
+                                        mng_ptr    *pRawdata)
+{
+  mng_datap pData;
+  mng_jdaap pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JDAA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_jdaap)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_JDAA)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iRawlen  = pChunk->iDatasize;       /* fill the fields */
+  *pRawdata = pChunk->pData;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JDAA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_getchunk_dhdr (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iObjectid,
+                                        mng_uint8  *iImagetype,
+                                        mng_uint8  *iDeltatype,
+                                        mng_uint32 *iBlockwidth,
+                                        mng_uint32 *iBlockheight,
+                                        mng_uint32 *iBlockx,
+                                        mng_uint32 *iBlocky)
+{
+  mng_datap pData;
+  mng_dhdrp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DHDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_dhdrp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_DHDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iObjectid    = pChunk->iObjectid;   /* fill the fields */
+  *iImagetype   = pChunk->iImagetype;
+  *iDeltatype   = pChunk->iDeltatype;
+  *iBlockwidth  = pChunk->iBlockwidth;
+  *iBlockheight = pChunk->iBlockheight;
+  *iBlockx      = pChunk->iBlockx;
+  *iBlocky      = pChunk->iBlocky;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_getchunk_prom (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint8  *iColortype,
+                                        mng_uint8  *iSampledepth,
+                                        mng_uint8  *iFilltype)
+{
+  mng_datap pData;
+  mng_promp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PROM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_promp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_PROM)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iColortype   = pChunk->iColortype;  /* fill the fields */
+  *iSampledepth = pChunk->iSampledepth;
+  *iFilltype    = pChunk->iFilltype;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PROM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_getchunk_pplt (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint8  *iDeltatype,
+                                        mng_uint32 *iCount)
+{
+  mng_datap pData;
+  mng_ppltp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PPLT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_ppltp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_PPLT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iDeltatype = pChunk->iDeltatype;    /* fill the fields */
+  *iCount     = pChunk->iCount;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_getchunk_pplt_entry (mng_handle hHandle,
+                                              mng_handle hChunk,
+                                              mng_uint32 iEntry,
+                                              mng_uint16 *iRed,
+                                              mng_uint16 *iGreen,
+                                              mng_uint16 *iBlue,
+                                              mng_uint16 *iAlpha,
+                                              mng_bool   *bUsed)
+{
+  mng_datap       pData;
+  mng_ppltp       pChunk;
+  mng_pplt_entryp pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PPLT_ENTRY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_ppltp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_PPLT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  if (iEntry >= pChunk->iCount)        /* valid index ? */
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+
+  pEntry  = &pChunk->aEntries[iEntry]; /* address the entry */
+
+  *iRed   = pEntry->iRed;              /* fill the fields */
+  *iGreen = pEntry->iGreen;
+  *iBlue  = pEntry->iBlue;
+  *iAlpha = pEntry->iAlpha;
+  *bUsed  = pEntry->bUsed;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PPLT_ENTRY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_getchunk_drop (mng_handle   hHandle,
+                                        mng_handle   hChunk,
+                                        mng_uint32   *iCount,
+                                        mng_chunkidp *pChunknames)
+{
+  mng_datap pData;
+  mng_dropp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DROP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_dropp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_DROP)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iCount      = pChunk->iCount;       /* fill the fields */
+  *pChunknames = pChunk->pChunknames;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DROP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+mng_retcode MNG_DECL mng_getchunk_dbyk (mng_handle  hHandle,
+                                        mng_handle  hChunk,
+                                        mng_chunkid *iChunkname,
+                                        mng_uint8   *iPolarity,
+                                        mng_uint32  *iKeywordssize,
+                                        mng_pchar   *zKeywords)
+{
+  mng_datap pData;
+  mng_dbykp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DBYK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_dbykp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_DBYK)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iChunkname    = pChunk->iChunkname; /* fill the fields */  
+  *iPolarity     = pChunk->iPolarity;
+  *iKeywordssize = pChunk->iKeywordssize;
+  *zKeywords     = pChunk->zKeywords;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DBYK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+mng_retcode MNG_DECL mng_getchunk_ordr (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iCount)
+{
+  mng_datap pData;
+  mng_ordrp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ORDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_ordrp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_ORDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iCount = pChunk->iCount;            /* fill the field */ 
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ORDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+mng_retcode MNG_DECL mng_getchunk_ordr_entry (mng_handle  hHandle,
+                                              mng_handle  hChunk,
+                                              mng_uint32  iEntry,
+                                              mng_chunkid *iChunkname,
+                                              mng_uint8   *iOrdertype)
+{
+  mng_datap       pData;
+  mng_ordrp       pChunk;
+  mng_ordr_entryp pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ORDR_ENTRY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_ordrp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_ORDR)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  if (iEntry >= pChunk->iCount)        /* valid index ? */
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+
+  pEntry = pChunk->pEntries + iEntry;  /* address the proper entry */
+
+  *iChunkname = pEntry->iChunkname;    /* fill the fields */
+  *iOrdertype = pEntry->iOrdertype;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ORDR_ENTRY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+mng_retcode MNG_DECL mng_getchunk_magn (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint16 *iFirstid,
+                                        mng_uint16 *iLastid,
+                                        mng_uint16 *iMethodX,
+                                        mng_uint16 *iMX,
+                                        mng_uint16 *iMY,
+                                        mng_uint16 *iML,
+                                        mng_uint16 *iMR,
+                                        mng_uint16 *iMT,
+                                        mng_uint16 *iMB,
+                                        mng_uint16 *iMethodY)
+{
+  mng_datap pData;
+  mng_magnp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MAGN, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_magnp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_MAGN)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iFirstid = pChunk->iFirstid;        /* fill the fields */
+  *iLastid  = pChunk->iLastid;
+  *iMethodX = (mng_uint16)pChunk->iMethodX;
+  *iMX      = pChunk->iMX;
+  *iMY      = pChunk->iMY;
+  *iML      = pChunk->iML;
+  *iMR      = pChunk->iMR;
+  *iMT      = pChunk->iMT;
+  *iMB      = pChunk->iMB;
+  *iMethodY = (mng_uint16)pChunk->iMethodY;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MAGN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_mpng (mng_handle hHandle,
+                                                mng_handle hChunk,
+                                                mng_uint32 *iFramewidth,
+                                                mng_uint32 *iFrameheight,
+                                                mng_uint16 *iNumplays,
+                                                mng_uint16 *iTickspersec,
+                                                mng_uint8  *iCompressionmethod,
+                                                mng_uint32 *iCount)
+{
+  mng_datap pData;
+  mng_mpngp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MPNG, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_mpngp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_mpNG)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+                                       /* fill the fields */
+  *iFramewidth        = pChunk->iFramewidth;
+  *iFrameheight       = pChunk->iFrameheight;
+  *iNumplays          = pChunk->iNumplays;
+  *iTickspersec       = pChunk->iTickspersec;
+  *iCompressionmethod = pChunk->iCompressionmethod;
+  *iCount             = pChunk->iFramessize / sizeof (mng_mpng_frame);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+MNG_EXT mng_retcode MNG_DECL mng_getchunk_mpng_frame (mng_handle hHandle,
+                                                      mng_handle hChunk,
+                                                      mng_uint32 iEntry,
+                                                      mng_uint32 *iX,
+                                                      mng_uint32 *iY,
+                                                      mng_uint32 *iWidth,
+                                                      mng_uint32 *iHeight,
+                                                      mng_int32  *iXoffset,
+                                                      mng_int32  *iYoffset,
+                                                      mng_uint16 *iTicks)
+{
+  mng_datap       pData;
+  mng_mpngp       pChunk;
+  mng_mpng_framep pFrame;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MPNG_FRAME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_mpngp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_mpNG)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+                                       /* valid index ? */
+  if (iEntry >= (pChunk->iFramessize / sizeof (mng_mpng_frame)))
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+
+  pFrame  = pChunk->pFrames + iEntry;  /* address the entry */
+                                       /* fill the fields */
+  *iX        = pFrame->iX;
+  *iY        = pFrame->iY;
+  *iWidth    = pFrame->iWidth;
+  *iHeight   = pFrame->iHeight;
+  *iXoffset  = pFrame->iXoffset;
+  *iYoffset  = pFrame->iYoffset;
+  *iTicks    = pFrame->iTicks;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MPNG_FRAME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_evNT
+mng_retcode MNG_DECL mng_getchunk_evnt (mng_handle hHandle,
+                                        mng_handle hChunk,
+                                        mng_uint32 *iCount)
+{
+  mng_datap pData;
+  mng_evntp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EVNT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_evntp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_evNT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  *iCount = pChunk->iCount;            /* fill the fields */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EVNT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getchunk_evnt_entry (mng_handle hHandle,
+                                              mng_handle hChunk,
+                                              mng_uint32 iEntry,
+                                              mng_uint8  *iEventtype,
+                                              mng_uint8  *iMasktype,
+                                              mng_int32  *iLeft,
+                                              mng_int32  *iRight,
+                                              mng_int32  *iTop,
+                                              mng_int32  *iBottom,
+                                              mng_uint16 *iObjectid,
+                                              mng_uint8  *iIndex,
+                                              mng_uint32 *iSegmentnamesize,
+                                              mng_pchar  *zSegmentname)
+{
+  mng_datap       pData;
+  mng_evntp       pChunk;
+  mng_evnt_entryp pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EVNT_ENTRY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_evntp)hChunk;          /* address the chunk */
+
+  if (pChunk->sHeader.iChunkname != MNG_UINT_evNT)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+
+  if (iEntry >= pChunk->iCount)        /* valid index ? */
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+
+  pEntry  = pChunk->pEntries + iEntry; /* address the entry */
+                                       /* fill the fields */
+  *iEventtype       = pEntry->iEventtype;
+  *iMasktype        = pEntry->iMasktype;
+  *iLeft            = pEntry->iLeft;    
+  *iRight           = pEntry->iRight;
+  *iTop             = pEntry->iTop;
+  *iBottom          = pEntry->iBottom;
+  *iObjectid        = pEntry->iObjectid;
+  *iIndex           = pEntry->iIndex;
+  *iSegmentnamesize = pEntry->iSegmentnamesize;
+  *zSegmentname     = pEntry->zSegmentname;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EVNT_ENTRY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getchunk_unknown (mng_handle  hHandle,
+                                           mng_handle  hChunk,
+                                           mng_chunkid *iChunkname,
+                                           mng_uint32  *iRawlen,
+                                           mng_ptr     *pRawdata)
+{
+  mng_datap          pData;
+  mng_unknown_chunkp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_UNKNOWN, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData  = (mng_datap)hHandle;         /* and make it addressable */
+  pChunk = (mng_unknown_chunkp)hChunk; /* address the chunk */
+
+  if (pChunk->sHeader.fCleanup != mng_free_unknown)
+    MNG_ERROR (pData, MNG_WRONGCHUNK)  /* ouch */
+                                       /* fill the fields */
+  *iChunkname = pChunk->sHeader.iChunkname;
+  *iRawlen    = pChunk->iDatasize;
+  *pRawdata   = pChunk->pData;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_UNKNOWN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_WRITE_PROCS
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_TERM
+MNG_LOCAL mng_bool check_term (mng_datap   pData,
+                               mng_chunkid iChunkname)
+{
+  mng_chunk_headerp pChunk = (mng_chunk_headerp)pData->pLastchunk;
+
+  if (!pChunk)                         /* nothing added yet ? */
+    return MNG_TRUE;
+                                       /* last added chunk is TERM ? */
+  if (pChunk->iChunkname != MNG_UINT_TERM)
+    return MNG_TRUE;
+                                       /* previous to last is MHDR ? */
+  if ((pChunk->pPrev) && (((mng_chunk_headerp)pChunk->pPrev)->iChunkname == MNG_UINT_MHDR))
+    return MNG_TRUE;
+
+  if (iChunkname == MNG_UINT_SEEK)     /* new chunk to be added is SEEK ? */
+    return MNG_TRUE;
+
+  return MNG_FALSE;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_ihdr (mng_handle hHandle,
+                                        mng_uint32 iWidth,
+                                        mng_uint32 iHeight,
+                                        mng_uint8  iBitdepth,
+                                        mng_uint8  iColortype,
+                                        mng_uint8  iCompression,
+                                        mng_uint8  iFilter,
+                                        mng_uint8  iInterlace)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_IHDR, mng_init_general, mng_free_general, mng_read_ihdr, mng_write_ihdr, mng_assign_general, 0, 0, sizeof(mng_ihdr)};
+#else
+          {MNG_UINT_IHDR, mng_init_ihdr, mng_free_ihdr, mng_read_ihdr, mng_write_ihdr, mng_assign_ihdr, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IHDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_IHDR))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_ihdr (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_IHDR, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+  ((mng_ihdrp)pChunk)->iWidth       = iWidth;
+  ((mng_ihdrp)pChunk)->iHeight      = iHeight;
+  ((mng_ihdrp)pChunk)->iBitdepth    = iBitdepth;
+  ((mng_ihdrp)pChunk)->iColortype   = iColortype;
+  ((mng_ihdrp)pChunk)->iCompression = iCompression;
+  ((mng_ihdrp)pChunk)->iFilter      = iFilter;
+  ((mng_ihdrp)pChunk)->iInterlace   = iInterlace;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_plte (mng_handle   hHandle,
+                                        mng_uint32   iCount,
+                                        mng_palette8 aPalette)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_PLTE, mng_init_general, mng_free_general, mng_read_plte, mng_write_plte, mng_assign_general, 0, 0, sizeof(mng_plte)};
+#else
+          {MNG_UINT_PLTE, mng_init_plte, mng_free_plte, mng_read_plte, mng_write_plte, mng_assign_plte, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PLTE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_PLTE))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_plte (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_PLTE, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_pltep)pChunk)->iEntrycount = iCount;
+  ((mng_pltep)pChunk)->bEmpty      = (mng_bool)(iCount == 0);
+
+  MNG_COPY (((mng_pltep)pChunk)->aEntries, aPalette, sizeof (mng_palette8));
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PLTE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_idat (mng_handle hHandle,
+                                        mng_uint32 iRawlen,
+                                        mng_ptr    pRawdata)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_IDAT, mng_init_general, mng_free_idat, mng_read_idat, mng_write_idat, mng_assign_idat, 0, 0, sizeof(mng_idat)};
+#else
+          {MNG_UINT_IDAT, mng_init_idat, mng_free_idat, mng_read_idat, mng_write_idat, mng_assign_idat, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IDAT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_IDAT))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_idat (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_IDAT, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_idatp)pChunk)->bEmpty    = (mng_bool)(iRawlen == 0);
+  ((mng_idatp)pChunk)->iDatasize = iRawlen;
+
+  if (iRawlen)
+  {
+    MNG_ALLOC (pData, ((mng_idatp)pChunk)->pData, iRawlen);
+    MNG_COPY (((mng_idatp)pChunk)->pData, pRawdata, iRawlen);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_iend (mng_handle hHandle)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_IEND, mng_init_general, mng_free_general, mng_read_iend, mng_write_iend, mng_assign_general, 0, 0, sizeof(mng_iend)};
+#else
+          {MNG_UINT_IEND, mng_init_iend, mng_free_iend, mng_read_iend, mng_write_iend, mng_assign_iend, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IEND, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_IEND))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_iend (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_IEND, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pData->iFirstchunkadded == MNG_UINT_IHDR) ||
+      (pData->iFirstchunkadded == MNG_UINT_JHDR)    )
+#else
+  if (pData->iFirstchunkadded == MNG_UINT_IHDR)
+#endif
+    pData->bCreating = MNG_FALSE;      /* should be last chunk !!! */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_trns (mng_handle   hHandle,
+                                        mng_bool     bEmpty,
+                                        mng_bool     bGlobal,
+                                        mng_uint8    iType,
+                                        mng_uint32   iCount,
+                                        mng_uint8arr aAlphas,
+                                        mng_uint16   iGray,
+                                        mng_uint16   iRed,
+                                        mng_uint16   iGreen,
+                                        mng_uint16   iBlue,
+                                        mng_uint32   iRawlen,
+                                        mng_uint8arr aRawdata)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_tRNS, mng_init_general, mng_free_general, mng_read_trns, mng_write_trns, mng_assign_general, 0, 0, sizeof(mng_trns)};
+#else
+          {MNG_UINT_tRNS, mng_init_trns, mng_free_trns, mng_read_trns, mng_write_trns, mng_assign_trns, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TRNS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_tRNS))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_trns (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_tRNS, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_trnsp)pChunk)->bEmpty   = bEmpty;
+  ((mng_trnsp)pChunk)->bGlobal  = bGlobal;
+  ((mng_trnsp)pChunk)->iType    = iType;
+  ((mng_trnsp)pChunk)->iCount   = iCount;
+  ((mng_trnsp)pChunk)->iGray    = iGray;
+  ((mng_trnsp)pChunk)->iRed     = iRed;
+  ((mng_trnsp)pChunk)->iGreen   = iGreen;
+  ((mng_trnsp)pChunk)->iBlue    = iBlue;
+  ((mng_trnsp)pChunk)->iRawlen  = iRawlen;
+
+  MNG_COPY (((mng_trnsp)pChunk)->aEntries, aAlphas,  sizeof (mng_uint8arr));
+  MNG_COPY (((mng_trnsp)pChunk)->aRawdata, aRawdata, sizeof (mng_uint8arr));
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TRNS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_gAMA
+mng_retcode MNG_DECL mng_putchunk_gama (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint32 iGamma)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_gAMA, mng_init_general, mng_free_general, mng_read_gama, mng_write_gama, mng_assign_general, 0, 0, sizeof(mng_gama)};
+#else
+          {MNG_UINT_gAMA, mng_init_gama, mng_free_gama, mng_read_gama, mng_write_gama, mng_assign_gama, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_GAMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_gAMA))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_gama (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_gAMA, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_gamap)pChunk)->bEmpty = bEmpty;
+  ((mng_gamap)pChunk)->iGamma = iGamma;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_GAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+mng_retcode MNG_DECL mng_putchunk_chrm (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint32 iWhitepointx,
+                                        mng_uint32 iWhitepointy,
+                                        mng_uint32 iRedx,
+                                        mng_uint32 iRedy,
+                                        mng_uint32 iGreenx,
+                                        mng_uint32 iGreeny,
+                                        mng_uint32 iBluex,
+                                        mng_uint32 iBluey)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_cHRM, mng_init_general, mng_free_general, mng_read_chrm, mng_write_chrm, mng_assign_general, 0, 0, sizeof(mng_chrm)};
+#else
+          {MNG_UINT_cHRM, mng_init_chrm, mng_free_chrm, mng_read_chrm, mng_write_chrm, mng_assign_chrm, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CHRM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_cHRM))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_chrm (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_cHRM, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_chrmp)pChunk)->bEmpty       = bEmpty;
+  ((mng_chrmp)pChunk)->iWhitepointx = iWhitepointx;
+  ((mng_chrmp)pChunk)->iWhitepointy = iWhitepointy;
+  ((mng_chrmp)pChunk)->iRedx        = iRedx;
+  ((mng_chrmp)pChunk)->iRedy        = iRedy;
+  ((mng_chrmp)pChunk)->iGreenx      = iGreenx;
+  ((mng_chrmp)pChunk)->iGreeny      = iGreeny;
+  ((mng_chrmp)pChunk)->iBluex       = iBluex;
+  ((mng_chrmp)pChunk)->iBluey       = iBluey;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CHRM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sRGB
+mng_retcode MNG_DECL mng_putchunk_srgb (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint8  iRenderingintent)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_sRGB, mng_init_general, mng_free_general, mng_read_srgb, mng_write_srgb, mng_assign_general, 0, 0, sizeof(mng_srgb)};
+#else
+          {MNG_UINT_sRGB, mng_init_srgb, mng_free_srgb, mng_read_srgb, mng_write_srgb, mng_assign_srgb, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SRGB, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_sRGB))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_srgb (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_sRGB, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_srgbp)pChunk)->bEmpty           = bEmpty;
+  ((mng_srgbp)pChunk)->iRenderingintent = iRenderingintent;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+mng_retcode MNG_DECL mng_putchunk_iccp (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint32 iNamesize,
+                                        mng_pchar  zName,
+                                        mng_uint8  iCompression,
+                                        mng_uint32 iProfilesize,
+                                        mng_ptr    pProfile)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_iCCP, mng_init_general, mng_free_iccp, mng_read_iccp, mng_write_iccp, mng_assign_iccp, 0, 0, sizeof(mng_iccp)};
+#else
+          {MNG_UINT_iCCP, mng_init_iccp, mng_free_iccp, mng_read_iccp, mng_write_iccp, mng_assign_iccp, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ICCP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_iCCP))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_iccp (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_iCCP, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_iccpp)pChunk)->bEmpty       = bEmpty;
+  ((mng_iccpp)pChunk)->iNamesize    = iNamesize;
+  ((mng_iccpp)pChunk)->iCompression = iCompression;
+  ((mng_iccpp)pChunk)->iProfilesize = iProfilesize;
+
+  if (iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_iccpp)pChunk)->zName, iNamesize + 1);
+    MNG_COPY (((mng_iccpp)pChunk)->zName, zName, iNamesize);
+  }
+
+  if (iProfilesize)
+  {
+    MNG_ALLOC (pData, ((mng_iccpp)pChunk)->pProfile, iProfilesize);
+    MNG_COPY (((mng_iccpp)pChunk)->pProfile, pProfile, iProfilesize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ICCP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tEXt
+mng_retcode MNG_DECL mng_putchunk_text (mng_handle hHandle,
+                                        mng_uint32 iKeywordsize,
+                                        mng_pchar  zKeyword,
+                                        mng_uint32 iTextsize,
+                                        mng_pchar  zText)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_tEXt, mng_init_general, mng_free_text, mng_read_text, mng_write_text, mng_assign_text, 0, 0, sizeof(mng_text)};
+#else
+          {MNG_UINT_tEXt, mng_init_text, mng_free_text, mng_read_text, mng_write_text, mng_assign_text, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TEXT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_tEXt))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_text (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_tEXt, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_textp)pChunk)->iKeywordsize = iKeywordsize;
+  ((mng_textp)pChunk)->iTextsize    = iTextsize;
+
+  if (iKeywordsize)
+  {
+    MNG_ALLOC (pData, ((mng_textp)pChunk)->zKeyword, iKeywordsize + 1);
+    MNG_COPY (((mng_textp)pChunk)->zKeyword, zKeyword, iKeywordsize);
+  }
+
+  if (iTextsize)
+  {
+    MNG_ALLOC (pData, ((mng_textp)pChunk)->zText, iTextsize + 1);
+    MNG_COPY (((mng_textp)pChunk)->zText, zText, iTextsize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TEXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_zTXt
+mng_retcode MNG_DECL mng_putchunk_ztxt (mng_handle hHandle,
+                                        mng_uint32 iKeywordsize,
+                                        mng_pchar  zKeyword,
+                                        mng_uint8  iCompression,
+                                        mng_uint32 iTextsize,
+                                        mng_pchar  zText)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_zTXt, mng_init_general, mng_free_ztxt, mng_read_ztxt, mng_write_ztxt, mng_assign_ztxt, 0, 0, sizeof(mng_ztxt)};
+#else
+          {MNG_UINT_zTXt, mng_init_ztxt, mng_free_ztxt, mng_read_ztxt, mng_write_ztxt, mng_assign_ztxt, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ZTXT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_zTXt))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_ztxt (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_zTXt, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_ztxtp)pChunk)->iKeywordsize = iKeywordsize;
+  ((mng_ztxtp)pChunk)->iCompression = iCompression;
+  ((mng_ztxtp)pChunk)->iTextsize    = iTextsize;
+
+  if (iKeywordsize)
+  {
+    MNG_ALLOC (pData, ((mng_ztxtp)pChunk)->zKeyword, iKeywordsize + 1);
+    MNG_COPY (((mng_ztxtp)pChunk)->zKeyword, zKeyword, iKeywordsize);
+  }
+
+  if (iTextsize)
+  {
+    MNG_ALLOC (pData, ((mng_ztxtp)pChunk)->zText, iTextsize + 1);
+    MNG_COPY  (((mng_ztxtp)pChunk)->zText, zText, iTextsize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ZTXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iTXt
+mng_retcode MNG_DECL mng_putchunk_itxt (mng_handle hHandle,
+                                        mng_uint32 iKeywordsize,
+                                        mng_pchar  zKeyword,
+                                        mng_uint8  iCompressionflag,
+                                        mng_uint8  iCompressionmethod,
+                                        mng_uint32 iLanguagesize,
+                                        mng_pchar  zLanguage,
+                                        mng_uint32 iTranslationsize,
+                                        mng_pchar  zTranslation,
+                                        mng_uint32 iTextsize,
+                                        mng_pchar  zText)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_iTXt, mng_init_general, mng_free_itxt, mng_read_itxt, mng_write_itxt, mng_assign_itxt, 0, 0, sizeof(mng_itxt)};
+#else
+          {MNG_UINT_iTXt, mng_init_itxt, mng_free_itxt, mng_read_itxt, mng_write_itxt, mng_assign_itxt, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ITXT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_iTXt))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_itxt (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_iTXt, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_itxtp)pChunk)->iKeywordsize       = iKeywordsize;
+  ((mng_itxtp)pChunk)->iCompressionflag   = iCompressionflag;
+  ((mng_itxtp)pChunk)->iCompressionmethod = iCompressionmethod;
+  ((mng_itxtp)pChunk)->iLanguagesize      = iLanguagesize;
+  ((mng_itxtp)pChunk)->iTranslationsize   = iTranslationsize;
+  ((mng_itxtp)pChunk)->iTextsize          = iTextsize;
+
+  if (iKeywordsize)
+  {
+    MNG_ALLOC (pData, ((mng_itxtp)pChunk)->zKeyword, iKeywordsize + 1);
+    MNG_COPY (((mng_itxtp)pChunk)->zKeyword, zKeyword, iKeywordsize);
+  }
+
+  if (iLanguagesize)
+  {
+    MNG_ALLOC (pData, ((mng_itxtp)pChunk)->zLanguage, iLanguagesize + 1);
+    MNG_COPY (((mng_itxtp)pChunk)->zLanguage, zLanguage, iLanguagesize);
+  }
+
+  if (iTranslationsize)
+  {
+    MNG_ALLOC (pData, ((mng_itxtp)pChunk)->zTranslation, iTranslationsize + 1);
+    MNG_COPY (((mng_itxtp)pChunk)->zTranslation, zTranslation, iTranslationsize);
+  }
+
+  if (iTextsize)
+  {
+    MNG_ALLOC (pData, ((mng_itxtp)pChunk)->zText, iTextsize + 1);
+    MNG_COPY (((mng_itxtp)pChunk)->zText, zText, iTextsize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ITXT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_bKGD
+mng_retcode MNG_DECL mng_putchunk_bkgd (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint8  iType,
+                                        mng_uint8  iIndex,
+                                        mng_uint16 iGray,
+                                        mng_uint16 iRed,
+                                        mng_uint16 iGreen,
+                                        mng_uint16 iBlue)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_bKGD, mng_init_general, mng_free_general, mng_read_bkgd, mng_write_bkgd, mng_assign_general, 0, 0, sizeof(mng_bkgd)};
+#else
+          {MNG_UINT_bKGD, mng_init_bkgd, mng_free_bkgd, mng_read_bkgd, mng_write_bkgd, mng_assign_bkgd, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BKGD, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_bKGD))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_bkgd (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_bKGD, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_bkgdp)pChunk)->bEmpty = bEmpty;
+  ((mng_bkgdp)pChunk)->iType  = iType;
+  ((mng_bkgdp)pChunk)->iIndex = iIndex;
+  ((mng_bkgdp)pChunk)->iGray  = iGray;
+  ((mng_bkgdp)pChunk)->iRed   = iRed;
+  ((mng_bkgdp)pChunk)->iGreen = iGreen;
+  ((mng_bkgdp)pChunk)->iBlue  = iBlue;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYs
+mng_retcode MNG_DECL mng_putchunk_phys (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint32 iSizex,
+                                        mng_uint32 iSizey,
+                                        mng_uint8  iUnit)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_pHYs, mng_init_general, mng_free_general, mng_read_phys, mng_write_phys, mng_assign_general, 0, 0, sizeof(mng_phys)};
+#else
+          {MNG_UINT_pHYs, mng_init_phys, mng_free_phys, mng_read_phys, mng_write_phys, mng_assign_phys, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PHYS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_pHYs))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_phys (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_pHYs, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_physp)pChunk)->bEmpty = bEmpty;
+  ((mng_physp)pChunk)->iSizex = iSizex;
+  ((mng_physp)pChunk)->iSizey = iSizey;
+  ((mng_physp)pChunk)->iUnit  = iUnit;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PHYS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sBIT
+mng_retcode MNG_DECL mng_putchunk_sbit (mng_handle    hHandle,
+                                        mng_bool      bEmpty,
+                                        mng_uint8     iType,
+                                        mng_uint8arr4 aBits)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_sBIT, mng_init_general, mng_free_general, mng_read_sbit, mng_write_sbit, mng_assign_general, 0, 0, sizeof(mng_sbit)};
+#else
+          {MNG_UINT_sBIT, mng_init_sbit, mng_free_sbit, mng_read_sbit, mng_write_sbit, mng_assign_sbit, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SBIT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_sBIT))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_sbit (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_sBIT, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_sbitp)pChunk)->bEmpty   = bEmpty;
+  ((mng_sbitp)pChunk)->iType    = iType;
+  ((mng_sbitp)pChunk)->aBits[0] = aBits[0];
+  ((mng_sbitp)pChunk)->aBits[1] = aBits[1];
+  ((mng_sbitp)pChunk)->aBits[2] = aBits[2];
+  ((mng_sbitp)pChunk)->aBits[3] = aBits[3];
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SBIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+mng_retcode MNG_DECL mng_putchunk_splt (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint32 iNamesize,
+                                        mng_pchar  zName,
+                                        mng_uint8  iSampledepth,
+                                        mng_uint32 iEntrycount,
+                                        mng_ptr    pEntries)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_sPLT, mng_init_general, mng_free_splt, mng_read_splt, mng_write_splt, mng_assign_splt, 0, 0, sizeof(mng_splt)};
+#else
+          {MNG_UINT_sPLT, mng_init_splt, mng_free_splt, mng_read_splt, mng_write_splt, mng_assign_splt, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SPLT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_sPLT))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_splt (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_sPLT, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_spltp)pChunk)->bEmpty       = bEmpty;
+  ((mng_spltp)pChunk)->iNamesize    = iNamesize;
+  ((mng_spltp)pChunk)->iSampledepth = iSampledepth;
+  ((mng_spltp)pChunk)->iEntrycount  = iEntrycount;
+
+  if (iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_spltp)pChunk)->zName, iNamesize + 1);
+    MNG_COPY (((mng_spltp)pChunk)->zName, zName, iNamesize);
+  }
+
+  if (iEntrycount)
+  {
+    mng_uint32 iSize = iEntrycount * ((iSampledepth >> 1) + 2);
+
+    MNG_ALLOC (pData, ((mng_spltp)pChunk)->pEntries, iSize);
+    MNG_COPY  (((mng_spltp)pChunk)->pEntries, pEntries, iSize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_hIST
+mng_retcode MNG_DECL mng_putchunk_hist (mng_handle    hHandle,
+                                        mng_uint32    iEntrycount,
+                                        mng_uint16arr aEntries)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_hIST, mng_init_general, mng_free_general, mng_read_hist, mng_write_hist, mng_assign_general, 0, 0, sizeof(mng_hist)};
+#else
+          {MNG_UINT_hIST, mng_init_hist, mng_free_hist, mng_read_hist, mng_write_hist, mng_assign_hist, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_HIST, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_hIST))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_hist (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_hIST, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_histp)pChunk)->iEntrycount = iEntrycount;
+
+  MNG_COPY (((mng_histp)pChunk)->aEntries, aEntries, sizeof (mng_uint16arr));
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_HIST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tIME
+mng_retcode MNG_DECL mng_putchunk_time (mng_handle hHandle,
+                                        mng_uint16 iYear,
+                                        mng_uint8  iMonth,
+                                        mng_uint8  iDay,
+                                        mng_uint8  iHour,
+                                        mng_uint8  iMinute,
+                                        mng_uint8  iSecond)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_tIME, mng_init_general, mng_free_general, mng_read_time, mng_write_time, mng_assign_general, 0, 0, sizeof(mng_time)};
+#else
+          {MNG_UINT_tIME, mng_init_time, mng_free_time, mng_read_time, mng_write_time, mng_assign_time, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TIME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_tIME))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_time (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_tIME, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_timep)pChunk)->iYear   = iYear;
+  ((mng_timep)pChunk)->iMonth  = iMonth;
+  ((mng_timep)pChunk)->iDay    = iDay;
+  ((mng_timep)pChunk)->iHour   = iHour;
+  ((mng_timep)pChunk)->iMinute = iMinute;
+  ((mng_timep)pChunk)->iSecond = iSecond;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TIME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_mhdr (mng_handle hHandle,
+                                        mng_uint32 iWidth,
+                                        mng_uint32 iHeight,
+                                        mng_uint32 iTicks,
+                                        mng_uint32 iLayercount,
+                                        mng_uint32 iFramecount,
+                                        mng_uint32 iPlaytime,
+                                        mng_uint32 iSimplicity)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_MHDR, mng_init_general, mng_free_general, mng_read_mhdr, mng_write_mhdr, mng_assign_general, 0, 0, sizeof(mng_mhdr)};
+#else
+          {MNG_UINT_MHDR, mng_init_mhdr, mng_free_mhdr, mng_read_mhdr, mng_write_mhdr, mng_assign_mhdr, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MHDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must be very first! */
+  if (pData->iFirstchunkadded != 0)
+    MNG_ERROR (pData, MNG_SEQUENCEERROR)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_MHDR))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_mhdr (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_MHDR, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_mhdrp)pChunk)->iWidth      = iWidth;
+  ((mng_mhdrp)pChunk)->iHeight     = iHeight;
+  ((mng_mhdrp)pChunk)->iTicks      = iTicks;
+  ((mng_mhdrp)pChunk)->iLayercount = iLayercount;
+  ((mng_mhdrp)pChunk)->iFramecount = iFramecount;
+  ((mng_mhdrp)pChunk)->iPlaytime   = iPlaytime;
+  ((mng_mhdrp)pChunk)->iSimplicity = iSimplicity;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_mend (mng_handle hHandle)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_MEND, mng_init_general, mng_free_general, mng_read_mend, mng_write_mend, mng_assign_general, 0, 0, sizeof(mng_mend)};
+#else
+          {MNG_UINT_MEND, mng_init_mend, mng_free_mend, mng_read_mend, mng_write_mend, mng_assign_mend, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MEND, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_MEND))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_mend (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_MEND, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+  pData->bCreating = MNG_FALSE;        /* should be last chunk !!! */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+mng_retcode MNG_DECL mng_putchunk_loop (mng_handle  hHandle,
+                                        mng_uint8   iLevel,
+                                        mng_uint32  iRepeat,
+                                        mng_uint8   iTermination,
+                                        mng_uint32  iItermin,
+                                        mng_uint32  iItermax,
+                                        mng_uint32  iCount,
+                                        mng_uint32p pSignals)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_LOOP, mng_init_general, mng_free_loop, mng_read_loop, mng_write_loop, mng_assign_loop, 0, 0, sizeof(mng_loop)};
+#else
+          {MNG_UINT_LOOP, mng_init_loop, mng_free_loop, mng_read_loop, mng_write_loop, mng_assign_loop, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_LOOP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_LOOP))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_loop (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_LOOP, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_loopp)pChunk)->iLevel       = iLevel;
+  ((mng_loopp)pChunk)->iRepeat      = iRepeat;
+  ((mng_loopp)pChunk)->iTermination = iTermination;
+  ((mng_loopp)pChunk)->iItermin     = iItermin;
+  ((mng_loopp)pChunk)->iItermax     = iItermax;
+  ((mng_loopp)pChunk)->iCount       = iCount;
+  ((mng_loopp)pChunk)->pSignals     = pSignals;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_LOOP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_endl (mng_handle hHandle,
+                                        mng_uint8  iLevel)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_ENDL, mng_init_general, mng_free_general, mng_read_endl, mng_write_endl, mng_assign_general, 0, 0, sizeof(mng_endl)};
+#else
+          {MNG_UINT_ENDL, mng_init_endl, mng_free_endl, mng_read_endl, mng_write_endl, mng_assign_endl, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ENDL, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_ENDL))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_endl (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_ENDL, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_endlp)pChunk)->iLevel = iLevel;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ENDL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DEFI
+mng_retcode MNG_DECL mng_putchunk_defi (mng_handle hHandle,
+                                        mng_uint16 iObjectid,
+                                        mng_uint8  iDonotshow,
+                                        mng_uint8  iConcrete,
+                                        mng_bool   bHasloca,
+                                        mng_int32  iXlocation,
+                                        mng_int32  iYlocation,
+                                        mng_bool   bHasclip,
+                                        mng_int32  iLeftcb,
+                                        mng_int32  iRightcb,
+                                        mng_int32  iTopcb,
+                                        mng_int32  iBottomcb)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_DEFI, mng_init_general, mng_free_general, mng_read_defi, mng_write_defi, mng_assign_general, 0, 0, sizeof(mng_defi)};
+#else
+          {MNG_UINT_DEFI, mng_init_defi, mng_free_defi, mng_read_defi, mng_write_defi, mng_assign_defi, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DEFI, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_DEFI))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_defi (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_DEFI, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_defip)pChunk)->iObjectid  = iObjectid;
+  ((mng_defip)pChunk)->iDonotshow = iDonotshow;
+  ((mng_defip)pChunk)->iConcrete  = iConcrete;
+  ((mng_defip)pChunk)->bHasloca   = bHasloca;
+  ((mng_defip)pChunk)->iXlocation = iXlocation;
+  ((mng_defip)pChunk)->iYlocation = iYlocation;
+  ((mng_defip)pChunk)->bHasclip   = bHasclip;
+  ((mng_defip)pChunk)->iLeftcb    = iLeftcb;
+  ((mng_defip)pChunk)->iRightcb   = iRightcb;
+  ((mng_defip)pChunk)->iTopcb     = iTopcb;
+  ((mng_defip)pChunk)->iBottomcb  = iBottomcb;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BASI
+mng_retcode MNG_DECL mng_putchunk_basi (mng_handle hHandle,
+                                        mng_uint32 iWidth,
+                                        mng_uint32 iHeight,
+                                        mng_uint8  iBitdepth,
+                                        mng_uint8  iColortype,
+                                        mng_uint8  iCompression,
+                                        mng_uint8  iFilter,
+                                        mng_uint8  iInterlace,
+                                        mng_uint16 iRed,
+                                        mng_uint16 iGreen,
+                                        mng_uint16 iBlue,
+                                        mng_uint16 iAlpha,
+                                        mng_uint8  iViewable)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_BASI, mng_init_general, mng_free_general, mng_read_basi, mng_write_basi, mng_assign_general, 0, 0, sizeof(mng_basi)};
+#else
+          {MNG_UINT_BASI, mng_init_basi, mng_free_basi, mng_read_basi, mng_write_basi, mng_assign_basi, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BASI, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_BASI))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_basi (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_BASI, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_basip)pChunk)->iWidth       = iWidth;
+  ((mng_basip)pChunk)->iHeight      = iHeight;
+  ((mng_basip)pChunk)->iBitdepth    = iBitdepth;
+  ((mng_basip)pChunk)->iColortype   = iColortype;
+  ((mng_basip)pChunk)->iCompression = iCompression;
+  ((mng_basip)pChunk)->iFilter      = iFilter;
+  ((mng_basip)pChunk)->iInterlace   = iInterlace;
+  ((mng_basip)pChunk)->iRed         = iRed;
+  ((mng_basip)pChunk)->iGreen       = iGreen;
+  ((mng_basip)pChunk)->iBlue        = iBlue;
+  ((mng_basip)pChunk)->iAlpha       = iAlpha;
+  ((mng_basip)pChunk)->iViewable    = iViewable;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BASI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLON
+mng_retcode MNG_DECL mng_putchunk_clon (mng_handle hHandle,
+                                        mng_uint16 iSourceid,
+                                        mng_uint16 iCloneid,
+                                        mng_uint8  iClonetype,
+                                        mng_uint8  iDonotshow,
+                                        mng_uint8  iConcrete,
+                                        mng_bool   bHasloca,
+                                        mng_uint8  iLocationtype,
+                                        mng_int32  iLocationx,
+                                        mng_int32  iLocationy)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_CLON, mng_init_general, mng_free_general, mng_read_clon, mng_write_clon, mng_assign_general, 0, 0, sizeof(mng_clon)};
+#else
+          {MNG_UINT_CLON, mng_init_clon, mng_free_clon, mng_read_clon, mng_write_clon, mng_assign_clon, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CLON, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_CLON))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_clon (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_CLON, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_clonp)pChunk)->iSourceid     = iSourceid;
+  ((mng_clonp)pChunk)->iCloneid      = iCloneid;
+  ((mng_clonp)pChunk)->iClonetype    = iClonetype;
+  ((mng_clonp)pChunk)->iDonotshow    = iDonotshow;
+  ((mng_clonp)pChunk)->iConcrete     = iConcrete;
+  ((mng_clonp)pChunk)->bHasloca      = bHasloca;
+  ((mng_clonp)pChunk)->iLocationtype = iLocationtype;
+  ((mng_clonp)pChunk)->iLocationx    = iLocationx;
+  ((mng_clonp)pChunk)->iLocationy    = iLocationy;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CLON, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode MNG_DECL mng_putchunk_past (mng_handle hHandle,
+                                        mng_uint16 iDestid,
+                                        mng_uint8  iTargettype,
+                                        mng_int32  iTargetx,
+                                        mng_int32  iTargety,
+                                        mng_uint32 iCount)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_PAST, mng_init_general, mng_free_past, mng_read_past, mng_write_past, mng_assign_past, 0, 0, sizeof(mng_past)};
+#else
+          {MNG_UINT_PAST, mng_init_past, mng_free_past, mng_read_past, mng_write_past, mng_assign_past, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PAST, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_PAST))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_past (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_PAST, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_pastp)pChunk)->iDestid     = iDestid;
+  ((mng_pastp)pChunk)->iTargettype = iTargettype;
+  ((mng_pastp)pChunk)->iTargetx    = iTargetx;
+  ((mng_pastp)pChunk)->iTargety    = iTargety;
+  ((mng_pastp)pChunk)->iCount      = iCount;
+
+  if (iCount)
+    MNG_ALLOC (pData, ((mng_pastp)pChunk)->pSources, iCount * sizeof (mng_past_source));
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PAST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode MNG_DECL mng_putchunk_past_src (mng_handle hHandle,
+                                            mng_uint32 iEntry,
+                                            mng_uint16 iSourceid,
+                                            mng_uint8  iComposition,
+                                            mng_uint8  iOrientation,
+                                            mng_uint8  iOffsettype,
+                                            mng_int32  iOffsetx,
+                                            mng_int32  iOffsety,
+                                            mng_uint8  iBoundarytype,
+                                            mng_int32  iBoundaryl,
+                                            mng_int32  iBoundaryr,
+                                            mng_int32  iBoundaryt,
+                                            mng_int32  iBoundaryb)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_past_sourcep pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PAST_SRC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+
+  pChunk = pData->pLastchunk;          /* last one must have been PAST ! */
+
+  if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_PAST)
+    MNG_ERROR (pData, MNG_NOCORRCHUNK)
+                                       /* index out of bounds ? */
+  if (iEntry >= ((mng_pastp)pChunk)->iCount)
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+                                       /* address proper entry */
+  pEntry = ((mng_pastp)pChunk)->pSources + iEntry;
+
+  pEntry->iSourceid     = iSourceid;   /* fill entry */
+  pEntry->iComposition  = iComposition;
+  pEntry->iOrientation  = iOrientation;
+  pEntry->iOffsettype   = iOffsettype;
+  pEntry->iOffsetx      = iOffsetx;
+  pEntry->iOffsety      = iOffsety;
+  pEntry->iBoundarytype = iBoundarytype;
+  pEntry->iBoundaryl    = iBoundaryl;
+  pEntry->iBoundaryr    = iBoundaryr;
+  pEntry->iBoundaryt    = iBoundaryt;
+  pEntry->iBoundaryb    = iBoundaryb;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PAST_SRC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+mng_retcode MNG_DECL mng_putchunk_disc (mng_handle  hHandle,
+                                        mng_uint32  iCount,
+                                        mng_uint16p pObjectids)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_DISC, mng_init_general, mng_free_disc, mng_read_disc, mng_write_disc, mng_assign_disc, 0, 0, sizeof(mng_disc)};
+#else
+          {MNG_UINT_DISC, mng_init_disc, mng_free_disc, mng_read_disc, mng_write_disc, mng_assign_disc, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DISC, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_DISC))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_disc (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_DISC, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_discp)pChunk)->iCount = iCount;
+
+  if (iCount)
+  {
+    mng_uint32 iSize = iCount * sizeof (mng_uint32);
+
+    MNG_ALLOC (pData, ((mng_discp)pChunk)->pObjectids, iSize);
+    MNG_COPY (((mng_discp)pChunk)->pObjectids, pObjectids, iSize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DISC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BACK
+mng_retcode MNG_DECL mng_putchunk_back (mng_handle hHandle,
+                                        mng_uint16 iRed,
+                                        mng_uint16 iGreen,
+                                        mng_uint16 iBlue,
+                                        mng_uint8  iMandatory,
+                                        mng_uint16 iImageid,
+                                        mng_uint8  iTile)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_BACK, mng_init_general, mng_free_general, mng_read_back, mng_write_back, mng_assign_general, 0, 0, sizeof(mng_back)};
+#else
+          {MNG_UINT_BACK, mng_init_back, mng_free_back, mng_read_back, mng_write_back, mng_assign_back, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BACK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_BACK))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_back (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_BACK, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_backp)pChunk)->iRed       = iRed;
+  ((mng_backp)pChunk)->iGreen     = iGreen;
+  ((mng_backp)pChunk)->iBlue      = iBlue;
+  ((mng_backp)pChunk)->iMandatory = iMandatory;
+  ((mng_backp)pChunk)->iImageid   = iImageid;
+  ((mng_backp)pChunk)->iTile      = iTile;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+mng_retcode MNG_DECL mng_putchunk_fram (mng_handle  hHandle,
+                                        mng_bool    bEmpty,
+                                        mng_uint8   iMode,
+                                        mng_uint32  iNamesize,
+                                        mng_pchar   zName,
+                                        mng_uint8   iChangedelay,
+                                        mng_uint8   iChangetimeout,
+                                        mng_uint8   iChangeclipping,
+                                        mng_uint8   iChangesyncid,
+                                        mng_uint32  iDelay,
+                                        mng_uint32  iTimeout,
+                                        mng_uint8   iBoundarytype,
+                                        mng_int32   iBoundaryl,
+                                        mng_int32   iBoundaryr,
+                                        mng_int32   iBoundaryt,
+                                        mng_int32   iBoundaryb,
+                                        mng_uint32  iCount,
+                                        mng_uint32p pSyncids)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_FRAM, mng_init_general, mng_free_fram, mng_read_fram, mng_write_fram, mng_assign_fram, 0, 0, sizeof(mng_fram)};
+#else
+          {MNG_UINT_FRAM, mng_init_fram, mng_free_fram, mng_read_fram, mng_write_fram, mng_assign_fram, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_FRAM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_FRAM))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_fram (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_FRAM, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_framp)pChunk)->bEmpty          = bEmpty;
+  ((mng_framp)pChunk)->iMode           = iMode;
+  ((mng_framp)pChunk)->iNamesize       = iNamesize;
+  ((mng_framp)pChunk)->iChangedelay    = iChangedelay;
+  ((mng_framp)pChunk)->iChangetimeout  = iChangetimeout;
+  ((mng_framp)pChunk)->iChangeclipping = iChangeclipping;
+  ((mng_framp)pChunk)->iChangesyncid   = iChangesyncid;
+  ((mng_framp)pChunk)->iDelay          = iDelay;
+  ((mng_framp)pChunk)->iTimeout        = iTimeout;
+  ((mng_framp)pChunk)->iBoundarytype   = iBoundarytype;
+  ((mng_framp)pChunk)->iBoundaryl      = iBoundaryl;
+  ((mng_framp)pChunk)->iBoundaryr      = iBoundaryr;
+  ((mng_framp)pChunk)->iBoundaryt      = iBoundaryt;
+  ((mng_framp)pChunk)->iBoundaryb      = iBoundaryb;
+  ((mng_framp)pChunk)->iCount          = iCount;
+
+  if (iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_framp)pChunk)->zName, iNamesize + 1);
+    MNG_COPY (((mng_framp)pChunk)->zName, zName, iNamesize);
+  }
+
+  if (iCount)
+  {
+    mng_uint32 iSize = iCount * sizeof (mng_uint32);
+
+    MNG_ALLOC (pData, ((mng_framp)pChunk)->pSyncids, iSize);
+    MNG_COPY (((mng_framp)pChunk)->pSyncids, pSyncids, iSize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_FRAM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MOVE
+mng_retcode MNG_DECL mng_putchunk_move (mng_handle hHandle,
+                                        mng_uint16 iFirstid,
+                                        mng_uint16 iLastid,
+                                        mng_uint8  iMovetype,
+                                        mng_int32  iMovex,
+                                        mng_int32  iMovey)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_MOVE, mng_init_general, mng_free_general, mng_read_move, mng_write_move, mng_assign_general, 0, 0, sizeof(mng_move)};
+#else
+          {MNG_UINT_MOVE, mng_init_move, mng_free_move, mng_read_move, mng_write_move, mng_assign_move, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MOVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_MOVE))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_move (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_MOVE, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_movep)pChunk)->iFirstid  = iFirstid;
+  ((mng_movep)pChunk)->iLastid   = iLastid;
+  ((mng_movep)pChunk)->iMovetype = iMovetype;
+  ((mng_movep)pChunk)->iMovex    = iMovex;
+  ((mng_movep)pChunk)->iMovey    = iMovey;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MOVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLIP
+mng_retcode MNG_DECL mng_putchunk_clip (mng_handle hHandle,
+                                        mng_uint16 iFirstid,
+                                        mng_uint16 iLastid,
+                                        mng_uint8  iCliptype,
+                                        mng_int32  iClipl,
+                                        mng_int32  iClipr,
+                                        mng_int32  iClipt,
+                                        mng_int32  iClipb)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_CLIP, mng_init_general, mng_free_general, mng_read_clip, mng_write_clip, mng_assign_general, 0, 0, sizeof(mng_clip)};
+#else
+          {MNG_UINT_CLIP, mng_init_clip, mng_free_clip, mng_read_clip, mng_write_clip, mng_assign_clip, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CLIP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_CLIP))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_clip (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_CLIP, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_clipp)pChunk)->iFirstid  = iFirstid;
+  ((mng_clipp)pChunk)->iLastid   = iLastid;
+  ((mng_clipp)pChunk)->iCliptype = iCliptype;
+  ((mng_clipp)pChunk)->iClipl    = iClipl;
+  ((mng_clipp)pChunk)->iClipr    = iClipr;
+  ((mng_clipp)pChunk)->iClipt    = iClipt;
+  ((mng_clipp)pChunk)->iClipb    = iClipb;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CLIP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SHOW
+mng_retcode MNG_DECL mng_putchunk_show (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint16 iFirstid,
+                                        mng_uint16 iLastid,
+                                        mng_uint8  iMode)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_SHOW, mng_init_general, mng_free_general, mng_read_show, mng_write_show, mng_assign_general, 0, 0, sizeof(mng_show)};
+#else
+          {MNG_UINT_SHOW, mng_init_show, mng_free_show, mng_read_show, mng_write_show, mng_assign_show, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SHOW, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_SHOW))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_show (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_SHOW, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_showp)pChunk)->bEmpty   = bEmpty;
+  ((mng_showp)pChunk)->iFirstid = iFirstid;
+  ((mng_showp)pChunk)->iLastid  = iLastid;
+  ((mng_showp)pChunk)->iMode    = iMode;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SHOW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_TERM
+mng_retcode MNG_DECL mng_putchunk_term (mng_handle hHandle,
+                                        mng_uint8  iTermaction,
+                                        mng_uint8  iIteraction,
+                                        mng_uint32 iDelay,
+                                        mng_uint32 iItermax)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_TERM, mng_init_general, mng_free_general, mng_read_term, mng_write_term, mng_assign_general, 0, 0, sizeof(mng_term)};
+#else
+          {MNG_UINT_TERM, mng_init_term, mng_free_term, mng_read_term, mng_write_term, mng_assign_term, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TERM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_term (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_TERM, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_termp)pChunk)->iTermaction = iTermaction;
+  ((mng_termp)pChunk)->iIteraction = iIteraction;
+  ((mng_termp)pChunk)->iDelay      = iDelay;
+  ((mng_termp)pChunk)->iItermax    = iItermax;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode MNG_DECL mng_putchunk_save (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint8  iOffsettype,
+                                        mng_uint32 iCount)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_SAVE, mng_init_general, mng_free_save, mng_read_save, mng_write_save, mng_assign_save, 0, 0, sizeof(mng_save)};
+#else
+          {MNG_UINT_SAVE, mng_init_save, mng_free_save, mng_read_save, mng_write_save, mng_assign_save, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SAVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_SAVE))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_save (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_SAVE, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_savep)pChunk)->bEmpty      = bEmpty;
+  ((mng_savep)pChunk)->iOffsettype = iOffsettype;
+  ((mng_savep)pChunk)->iCount      = iCount;
+
+  if (iCount)
+    MNG_ALLOC (pData, ((mng_savep)pChunk)->pEntries, iCount * sizeof (mng_save_entry));
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_save_entry (mng_handle     hHandle,
+                                              mng_uint32     iEntry,
+                                              mng_uint8      iEntrytype,
+                                              mng_uint32arr2 iOffset,
+                                              mng_uint32arr2 iStarttime,
+                                              mng_uint32     iLayernr,
+                                              mng_uint32     iFramenr,
+                                              mng_uint32     iNamesize,
+                                              mng_pchar      zName)
+{
+  mng_datap       pData;
+  mng_chunkp      pChunk;
+  mng_save_entryp pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SAVE_ENTRY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+
+  pChunk = pData->pLastchunk;          /* last one must have been SAVE ! */
+
+  if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_SAVE)
+    MNG_ERROR (pData, MNG_NOCORRCHUNK)
+                                       /* index out of bounds ? */
+  if (iEntry >= ((mng_savep)pChunk)->iCount)
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+                                       /* address proper entry */
+  pEntry = ((mng_savep)pChunk)->pEntries + iEntry;
+
+  pEntry->iEntrytype    = iEntrytype;  /* fill entry */
+  pEntry->iOffset[0]    = iOffset[0];
+  pEntry->iOffset[1]    = iOffset[1];
+  pEntry->iStarttime[0] = iStarttime[0];
+  pEntry->iStarttime[1] = iStarttime[1];
+  pEntry->iLayernr      = iLayernr;
+  pEntry->iFramenr      = iFramenr;
+  pEntry->iNamesize     = iNamesize;
+
+  if (iNamesize)
+  {
+    MNG_ALLOC (pData, pEntry->zName, iNamesize + 1);
+    MNG_COPY (pEntry->zName, zName, iNamesize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SAVE_ENTRY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_retcode MNG_DECL mng_putchunk_seek (mng_handle hHandle,
+                                        mng_uint32 iNamesize,
+                                        mng_pchar  zName)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_SEEK, mng_init_general, mng_free_seek, mng_read_seek, mng_write_seek, mng_assign_seek, 0, 0, sizeof(mng_seek)};
+#else
+          {MNG_UINT_SEEK, mng_init_seek, mng_free_seek, mng_read_seek, mng_write_seek, mng_assign_seek, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SEEK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_SEEK))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_seek (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_SEEK, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_seekp)pChunk)->iNamesize = iNamesize;
+
+  if (iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_seekp)pChunk)->zName, iNamesize + 1);
+    MNG_COPY (((mng_seekp)pChunk)->zName, zName, iNamesize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SEEK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_eXPI
+mng_retcode MNG_DECL mng_putchunk_expi (mng_handle hHandle,
+                                        mng_uint16 iSnapshotid,
+                                        mng_uint32 iNamesize,
+                                        mng_pchar  zName)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_eXPI, mng_init_general, mng_free_expi, mng_read_expi, mng_write_expi, mng_assign_general, 0, 0, sizeof(mng_expi)};
+#else
+          {MNG_UINT_eXPI, mng_init_expi, mng_free_expi, mng_read_expi, mng_write_expi, mng_assign_expi, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EXPI, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_eXPI))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_expi (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_eXPI, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_expip)pChunk)->iSnapshotid = iSnapshotid;
+  ((mng_expip)pChunk)->iNamesize   = iNamesize;
+
+  if (iNamesize)
+  {
+    MNG_ALLOC (pData, ((mng_expip)pChunk)->zName, iNamesize + 1);
+    MNG_COPY (((mng_expip)pChunk)->zName, zName, iNamesize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EXPI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_fPRI
+mng_retcode MNG_DECL mng_putchunk_fpri (mng_handle hHandle,
+                                        mng_uint8  iDeltatype,
+                                        mng_uint8  iPriority)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_fPRI, mng_init_general, mng_free_general, mng_read_fpri, mng_write_fpri, mng_assign_general, 0, 0, sizeof(mng_fpri)};
+#else
+          {MNG_UINT_fPRI, mng_init_fpri, mng_free_fpri, mng_read_fpri, mng_write_fpri, mng_assign_fpri, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_FPRI, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_fPRI))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_fpri (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_fPRI, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_fprip)pChunk)->iDeltatype = iDeltatype;
+  ((mng_fprip)pChunk)->iPriority  = iPriority;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_FPRI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+mng_retcode MNG_DECL mng_putchunk_need (mng_handle hHandle,
+                                        mng_uint32 iKeywordssize,
+                                        mng_pchar  zKeywords)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_nEED, mng_init_general, mng_free_need, mng_read_need, mng_write_need, mng_assign_need, 0, 0, sizeof(mng_need)};
+#else
+          {MNG_UINT_nEED, mng_init_need, mng_free_need, mng_read_need, mng_write_need, mng_assign_need, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_NEED, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_nEED))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_need (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_nEED, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_needp)pChunk)->iKeywordssize = iKeywordssize;
+
+  if (iKeywordssize)
+  {
+    MNG_ALLOC (pData, ((mng_needp)pChunk)->zKeywords, iKeywordssize + 1);
+    MNG_COPY (((mng_needp)pChunk)->zKeywords, zKeywords, iKeywordssize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_NEED, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYg
+mng_retcode MNG_DECL mng_putchunk_phyg (mng_handle hHandle,
+                                        mng_bool   bEmpty,
+                                        mng_uint32 iSizex,
+                                        mng_uint32 iSizey,
+                                        mng_uint8  iUnit)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_pHYg, mng_init_general, mng_free_general, mng_read_phyg, mng_write_phyg, mng_assign_general, 0, 0, sizeof(mng_phyg)};
+#else
+          {MNG_UINT_pHYg, mng_init_phyg, mng_free_phyg, mng_read_phyg, mng_write_phyg, mng_assign_phyg, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PHYG, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_pHYg))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_phyg (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_pHYg, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_phygp)pChunk)->bEmpty = bEmpty;
+  ((mng_phygp)pChunk)->iSizex = iSizex;
+  ((mng_phygp)pChunk)->iSizey = iSizey;
+  ((mng_phygp)pChunk)->iUnit  = iUnit;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PHYG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+mng_retcode MNG_DECL mng_putchunk_jhdr (mng_handle hHandle,
+                                        mng_uint32 iWidth,
+                                        mng_uint32 iHeight,
+                                        mng_uint8  iColortype,
+                                        mng_uint8  iImagesampledepth,
+                                        mng_uint8  iImagecompression,
+                                        mng_uint8  iImageinterlace,
+                                        mng_uint8  iAlphasampledepth,
+                                        mng_uint8  iAlphacompression,
+                                        mng_uint8  iAlphafilter,
+                                        mng_uint8  iAlphainterlace)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_JHDR, mng_init_general, mng_free_general, mng_read_jhdr, mng_write_jhdr, mng_assign_general, 0, 0, sizeof(mng_jhdr)};
+#else
+          {MNG_UINT_JHDR, mng_init_jhdr, mng_free_jhdr, mng_read_jhdr, mng_write_jhdr, mng_assign_jhdr, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JHDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_JHDR))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_jhdr (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_JHDR, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_jhdrp)pChunk)->iWidth            = iWidth;
+  ((mng_jhdrp)pChunk)->iHeight           = iHeight;
+  ((mng_jhdrp)pChunk)->iColortype        = iColortype;
+  ((mng_jhdrp)pChunk)->iImagesampledepth = iImagesampledepth;
+  ((mng_jhdrp)pChunk)->iImagecompression = iImagecompression;
+  ((mng_jhdrp)pChunk)->iImageinterlace   = iImageinterlace;
+  ((mng_jhdrp)pChunk)->iAlphasampledepth = iAlphasampledepth;
+  ((mng_jhdrp)pChunk)->iAlphacompression = iAlphacompression;
+  ((mng_jhdrp)pChunk)->iAlphafilter      = iAlphafilter;
+  ((mng_jhdrp)pChunk)->iAlphainterlace   = iAlphainterlace;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+mng_retcode MNG_DECL mng_putchunk_jdat (mng_handle hHandle,
+                                        mng_uint32 iRawlen,
+                                        mng_ptr    pRawdata)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_JDAT, mng_init_general, mng_free_jdat, mng_read_jdat, mng_write_jdat, mng_assign_jdat, 0, 0, sizeof(mng_jdat)};
+#else
+          {MNG_UINT_JDAT, mng_init_jdat, mng_free_jdat, mng_read_jdat, mng_write_jdat, mng_assign_jdat, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JDAT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR or JHDR first! */
+  if ((pData->iFirstchunkadded != MNG_UINT_MHDR) &&
+      (pData->iFirstchunkadded != MNG_UINT_JHDR)    )
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_JDAT))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_jdat (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_JDAT, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_jdatp)pChunk)->iDatasize = iRawlen;
+
+  if (iRawlen)
+  {
+    MNG_ALLOC (pData, ((mng_jdatp)pChunk)->pData, iRawlen);
+    MNG_COPY (((mng_jdatp)pChunk)->pData, pRawdata, iRawlen);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+#endif /*  MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+mng_retcode MNG_DECL mng_putchunk_jdaa (mng_handle hHandle,
+                                        mng_uint32 iRawlen,
+                                        mng_ptr    pRawdata)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_JDAA, mng_init_general, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0, sizeof(mng_jdaa)};
+#else
+          {MNG_UINT_JDAA, mng_init_jdaa, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JDAA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR or JHDR first! */
+  if ((pData->iFirstchunkadded != MNG_UINT_MHDR) &&
+      (pData->iFirstchunkadded != MNG_UINT_JHDR)    )
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_JDAA))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_jdaa (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_JDAA, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_jdaap)pChunk)->iDatasize = iRawlen;
+
+  if (iRawlen)
+  {
+    MNG_ALLOC (pData, ((mng_jdaap)pChunk)->pData, iRawlen);
+    MNG_COPY (((mng_jdaap)pChunk)->pData, pRawdata, iRawlen);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JDAA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+#endif /*  MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+mng_retcode MNG_DECL mng_putchunk_jsep (mng_handle hHandle)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_JSEP, mng_init_general, mng_free_general, mng_read_jsep, mng_write_jsep, mng_assign_general, 0, 0, sizeof(mng_jsep)};
+#else
+          {MNG_UINT_JSEP, mng_init_jsep, mng_free_jsep, mng_read_jsep, mng_write_jsep, mng_assign_jsep, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JSEP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR or JHDR first! */
+  if ((pData->iFirstchunkadded != MNG_UINT_MHDR) &&
+      (pData->iFirstchunkadded != MNG_UINT_JHDR)    )
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_JSEP))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_jsep (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_JSEP, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JSEP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_putchunk_dhdr (mng_handle hHandle,
+                                        mng_uint16 iObjectid,
+                                        mng_uint8  iImagetype,
+                                        mng_uint8  iDeltatype,
+                                        mng_uint32 iBlockwidth,
+                                        mng_uint32 iBlockheight,
+                                        mng_uint32 iBlockx,
+                                        mng_uint32 iBlocky)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_DHDR, mng_init_general, mng_free_general, mng_read_dhdr, mng_write_dhdr, mng_assign_general, 0, 0, sizeof(mng_dhdr)};
+#else
+          {MNG_UINT_DHDR, mng_init_dhdr, mng_free_dhdr, mng_read_dhdr, mng_write_dhdr, mng_assign_dhdr, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DHDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_DHDR))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_dhdr (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_DHDR, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_dhdrp)pChunk)->iObjectid    = iObjectid;
+  ((mng_dhdrp)pChunk)->iImagetype   = iImagetype;
+  ((mng_dhdrp)pChunk)->iDeltatype   = iDeltatype;
+  ((mng_dhdrp)pChunk)->iBlockwidth  = iBlockwidth;
+  ((mng_dhdrp)pChunk)->iBlockheight = iBlockheight;
+  ((mng_dhdrp)pChunk)->iBlockx      = iBlockx;
+  ((mng_dhdrp)pChunk)->iBlocky      = iBlocky;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_putchunk_prom (mng_handle hHandle,
+                                        mng_uint8  iColortype,
+                                        mng_uint8  iSampledepth,
+                                        mng_uint8  iFilltype)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_PROM, mng_init_general, mng_free_general, mng_read_prom, mng_write_prom, mng_assign_general, 0, 0, sizeof(mng_prom)};
+#else
+          {MNG_UINT_PROM, mng_init_prom, mng_free_prom, mng_read_prom, mng_write_prom, mng_assign_prom, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PROM, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_PROM))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_prom (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_PROM, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_promp)pChunk)->iColortype   = iColortype;
+  ((mng_promp)pChunk)->iSampledepth = iSampledepth;
+  ((mng_promp)pChunk)->iFilltype    = iFilltype;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PROM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_putchunk_ipng (mng_handle hHandle)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_IPNG, mng_init_general, mng_free_general, mng_read_ipng, mng_write_ipng, mng_assign_general, 0, 0, sizeof(mng_ipng)};
+#else
+          {MNG_UINT_IPNG, mng_init_ipng, mng_free_ipng, mng_read_ipng, mng_write_ipng, mng_assign_ipng, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IPNG, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_IPNG))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_ipng (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_IPNG, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_putchunk_pplt (mng_handle hHandle,
+                                        mng_uint8  iDeltatype,
+                                        mng_uint32 iCount)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_PPLT, mng_init_general, mng_free_general, mng_read_pplt, mng_write_pplt, mng_assign_general, 0, 0, sizeof(mng_pplt)};
+#else
+          {MNG_UINT_PPLT, mng_init_pplt, mng_free_pplt, mng_read_pplt, mng_write_pplt, mng_assign_pplt, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PPLT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_PPLT))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_pplt (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_PPLT, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_ppltp)pChunk)->iDeltatype = iDeltatype;
+  ((mng_ppltp)pChunk)->iCount     = iCount;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_putchunk_pplt_entry (mng_handle hHandle,
+                                              mng_uint32 iEntry,
+                                              mng_uint16 iRed,
+                                              mng_uint16 iGreen,
+                                              mng_uint16 iBlue,
+                                              mng_uint16 iAlpha)
+{
+  mng_datap       pData;
+  mng_chunkp      pChunk;
+  mng_pplt_entryp pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PPLT_ENTRY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+
+  pChunk = pData->pLastchunk;          /* last one must have been PPLT ! */
+
+  if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_PPLT)
+    MNG_ERROR (pData, MNG_NOCORRCHUNK)
+
+                                       /* index out of bounds ? */
+  if (iEntry >= ((mng_ppltp)pChunk)->iCount)
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+                                       /* address proper entry */
+  pEntry = (mng_pplt_entryp)(((mng_ppltp)pChunk)->aEntries) + iEntry;
+
+  pEntry->iRed   = (mng_uint8)iRed;    /* fill the entry */
+  pEntry->iGreen = (mng_uint8)iGreen;
+  pEntry->iBlue  = (mng_uint8)iBlue;
+  pEntry->iAlpha = (mng_uint8)iAlpha;
+  pEntry->bUsed  = MNG_TRUE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PPLT_ENTRY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+mng_retcode MNG_DECL mng_putchunk_ijng (mng_handle hHandle)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_IJNG, mng_init_general, mng_free_general, mng_read_ijng, mng_write_ijng, mng_assign_general, 0, 0, sizeof(mng_ijng)};
+#else
+          {MNG_UINT_IJNG, mng_init_ijng, mng_free_ijng, mng_read_ijng, mng_write_ijng, mng_assign_ijng, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IJNG, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_IJNG))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_ijng (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_IJNG, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IJNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode MNG_DECL mng_putchunk_drop (mng_handle   hHandle,
+                                        mng_uint32   iCount,
+                                        mng_chunkidp pChunknames)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_DROP, mng_init_general, mng_free_drop, mng_read_drop, mng_write_drop, mng_assign_drop, 0, 0, sizeof(mng_drop)};
+#else
+          {MNG_UINT_DROP, mng_init_drop, mng_free_drop, mng_read_drop, mng_write_drop, mng_assign_drop, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DROP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_DROP))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_drop (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_DROP, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_dropp)pChunk)->iCount = iCount;
+
+  if (iCount)
+  {
+    mng_uint32 iSize = iCount * sizeof (mng_chunkid);
+
+    MNG_ALLOC (pData, ((mng_dropp)pChunk)->pChunknames, iSize);
+    MNG_COPY (((mng_dropp)pChunk)->pChunknames, pChunknames, iSize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DROP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+mng_retcode MNG_DECL mng_putchunk_dbyk (mng_handle  hHandle,
+                                        mng_chunkid iChunkname,
+                                        mng_uint8   iPolarity,
+                                        mng_uint32  iKeywordssize,
+                                        mng_pchar   zKeywords)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_DBYK, mng_init_general, mng_free_dbyk, mng_read_dbyk, mng_write_dbyk, mng_assign_dbyk, 0, 0, sizeof(mng_dbyk)};
+#else
+          {MNG_UINT_DBYK, mng_init_dbyk, mng_free_dbyk, mng_read_dbyk, mng_write_dbyk, mng_assign_dbyk, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DBYK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_DBYK))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_dbyk (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_DBYK, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_dbykp)pChunk)->iChunkname    = iChunkname;
+  ((mng_dbykp)pChunk)->iPolarity     = iPolarity;
+  ((mng_dbykp)pChunk)->iKeywordssize = iKeywordssize;
+
+  if (iKeywordssize)
+  {
+    MNG_ALLOC (pData, ((mng_dbykp)pChunk)->zKeywords, iKeywordssize + 1);
+    MNG_COPY (((mng_dbykp)pChunk)->zKeywords, zKeywords, iKeywordssize);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DBYK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+mng_retcode MNG_DECL mng_putchunk_ordr (mng_handle hHandle,
+                                        mng_uint32 iCount)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_ORDR, mng_init_general, mng_free_ordr, mng_read_ordr, mng_write_ordr, mng_assign_ordr, 0, 0, sizeof(mng_ordr)};
+#else
+          {MNG_UINT_ORDR, mng_init_ordr, mng_free_ordr, mng_read_ordr, mng_write_ordr, mng_assign_ordr, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ORDR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_ORDR))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_ordr (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_ORDR, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_ordrp)pChunk)->iCount = iCount;
+
+  if (iCount)
+    MNG_ALLOC (pData, ((mng_ordrp)pChunk)->pEntries, iCount * sizeof (mng_ordr_entry));
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ORDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+mng_retcode MNG_DECL mng_putchunk_ordr_entry (mng_handle  hHandle,
+                                              mng_uint32  iEntry,
+                                              mng_chunkid iChunkname,
+                                              mng_uint8   iOrdertype)
+{
+  mng_datap       pData;
+  mng_chunkp      pChunk;
+  mng_ordr_entryp pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ORDR_ENTRY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+
+  pChunk = pData->pLastchunk;          /* last one must have been ORDR ! */
+
+  if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_ORDR)
+    MNG_ERROR (pData, MNG_NOCORRCHUNK)
+                                       /* index out of bounds ? */
+  if (iEntry >= ((mng_ordrp)pChunk)->iCount)
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+                                       /* address proper entry */
+  pEntry = ((mng_ordrp)pChunk)->pEntries + iEntry;
+
+  pEntry->iChunkname = iChunkname;     /* fill the entry */
+  pEntry->iOrdertype = iOrdertype;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ORDR_ENTRY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+mng_retcode MNG_DECL mng_putchunk_magn (mng_handle hHandle,
+                                        mng_uint16 iFirstid,
+                                        mng_uint16 iLastid,
+                                        mng_uint16 iMethodX,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iMY,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint16 iMT,
+                                        mng_uint16 iMB,
+                                        mng_uint16 iMethodY)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_MAGN, mng_init_general, mng_free_general, mng_read_magn, mng_write_magn, mng_assign_general, 0, 0, sizeof(mng_magn)};
+#else
+          {MNG_UINT_MAGN, mng_init_magn, mng_free_magn, mng_read_magn, mng_write_magn, mng_assign_magn, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MAGN, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_MAGN))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_magn (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_MAGN, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_magnp)pChunk)->iFirstid = iFirstid;
+  ((mng_magnp)pChunk)->iLastid  = iLastid;
+  ((mng_magnp)pChunk)->iMethodX = (mng_uint8)iMethodX;
+  ((mng_magnp)pChunk)->iMX      = iMX;
+  ((mng_magnp)pChunk)->iMY      = iMY;
+  ((mng_magnp)pChunk)->iML      = iML;
+  ((mng_magnp)pChunk)->iMR      = iMR;
+  ((mng_magnp)pChunk)->iMT      = iMT;
+  ((mng_magnp)pChunk)->iMB      = iMB;
+  ((mng_magnp)pChunk)->iMethodY = (mng_uint8)iMethodY;
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MAGN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_mpng (mng_handle hHandle,
+                                                mng_uint32 iFramewidth,
+                                                mng_uint32 iFrameheight,
+                                                mng_uint16 iNumplays,
+                                                mng_uint16 iTickspersec,
+                                                mng_uint8  iCompressionmethod,
+                                                mng_uint32 iCount)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_mpNG, mng_init_general, mng_free_mpng, mng_read_mpng, mng_write_mpng, mng_assign_mpng, 0, 0, sizeof(mng_mpng)};
+#else
+          {MNG_UINT_mpNG, mng_init_mpng, mng_free_mpng, mng_read_mpng, mng_write_mpng, mng_assign_mpng, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MPNG, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a IHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_IHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_mpng (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_mpNG, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_mpngp)pChunk)->iFramewidth        = iFramewidth;
+  ((mng_mpngp)pChunk)->iFrameheight       = iFrameheight;
+  ((mng_mpngp)pChunk)->iNumplays          = iNumplays;
+  ((mng_mpngp)pChunk)->iTickspersec       = iTickspersec;
+  ((mng_mpngp)pChunk)->iCompressionmethod = iCompressionmethod;
+  ((mng_mpngp)pChunk)->iFramessize        = iCount * sizeof (mng_mpng_frame);
+
+  if (iCount)
+    MNG_ALLOC (pData, ((mng_mpngp)pChunk)->pFrames, ((mng_mpngp)pChunk)->iFramessize);
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+MNG_EXT mng_retcode MNG_DECL mng_putchunk_mpng_frame (mng_handle hHandle,
+                                                      mng_uint32 iEntry,
+                                                      mng_uint32 iX,
+                                                      mng_uint32 iY,
+                                                      mng_uint32 iWidth,
+                                                      mng_uint32 iHeight,
+                                                      mng_int32  iXoffset,
+                                                      mng_int32  iYoffset,
+                                                      mng_uint16 iTicks)
+{
+  mng_datap       pData;
+  mng_chunkp      pChunk;
+  mng_mpng_framep pFrame;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MPNG_FRAME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a IHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_IHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+
+  pChunk = pData->pLastchunk;          /* last one must have been mpNG ! */
+
+  if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_mpNG)
+    MNG_ERROR (pData, MNG_NOCORRCHUNK)
+                                       /* index out of bounds ? */
+  if (iEntry >= (((mng_mpngp)pChunk)->iFramessize / sizeof (mng_mpng_frame)))
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+                                       /* address proper entry */
+  pFrame = ((mng_mpngp)pChunk)->pFrames + iEntry;
+                                       /* fill entry */
+  pFrame->iX        = iX;
+  pFrame->iY        = iY;
+  pFrame->iWidth    = iWidth;
+  pFrame->iHeight   = iHeight;
+  pFrame->iXoffset  = iXoffset;
+  pFrame->iYoffset  = iYoffset;
+  pFrame->iTicks    = iTicks;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MPNG_FRAME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_evNT
+mng_retcode MNG_DECL mng_putchunk_evnt (mng_handle hHandle,
+                                        mng_uint32 iCount)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_evNT, mng_init_general, mng_free_evnt, mng_read_evnt, mng_write_evnt, mng_assign_evnt, 0, 0, sizeof(mng_evnt)};
+#else
+          {MNG_UINT_evNT, mng_init_evnt, mng_free_evnt, mng_read_evnt, mng_write_evnt, mng_assign_evnt, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EVNT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, MNG_UINT_evNT))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_evnt (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_evNT, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_evntp)pChunk)->iCount = iCount;
+
+  if (iCount)
+    MNG_ALLOC (pData, ((mng_evntp)pChunk)->pEntries, iCount * sizeof (mng_evnt_entry));
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EVNT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_evnt_entry (mng_handle hHandle,
+                                              mng_uint32 iEntry,
+                                              mng_uint8  iEventtype,
+                                              mng_uint8  iMasktype,
+                                              mng_int32  iLeft,
+                                              mng_int32  iRight,
+                                              mng_int32  iTop,
+                                              mng_int32  iBottom,
+                                              mng_uint16 iObjectid,
+                                              mng_uint8  iIndex,
+                                              mng_uint32 iSegmentnamesize,
+                                              mng_pchar  zSegmentname)
+{
+  mng_datap       pData;
+  mng_chunkp      pChunk;
+  mng_evnt_entryp pEntry;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EVNT_ENTRY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a MHDR first! */
+  if (pData->iFirstchunkadded != MNG_UINT_MHDR)
+    MNG_ERROR (pData, MNG_NOHEADER)
+
+  pChunk = pData->pLastchunk;          /* last one must have been evNT ! */
+
+  if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_evNT)
+    MNG_ERROR (pData, MNG_NOCORRCHUNK)
+                                       /* index out of bounds ? */
+  if (iEntry >= ((mng_evntp)pChunk)->iCount)
+    MNG_ERROR (pData, MNG_INVALIDENTRYIX)
+                                       /* address proper entry */
+  pEntry = ((mng_evntp)pChunk)->pEntries + iEntry;
+                                       /* fill entry */
+  pEntry->iEventtype       = iEventtype;
+  pEntry->iMasktype        = iMasktype;
+  pEntry->iLeft            = iLeft;
+  pEntry->iRight           = iRight;
+  pEntry->iTop             = iTop;
+  pEntry->iBottom          = iBottom;
+  pEntry->iObjectid        = iObjectid;
+  pEntry->iIndex           = iIndex;
+  pEntry->iSegmentnamesize = iSegmentnamesize;
+
+  if (iSegmentnamesize)
+  {
+    MNG_ALLOC (pData, pEntry->zSegmentname, iSegmentnamesize + 1);
+    MNG_COPY (pEntry->zSegmentname, zSegmentname, iSegmentnamesize);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EVNT_ENTRY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putchunk_unknown (mng_handle  hHandle,
+                                           mng_chunkid iChunkname,
+                                           mng_uint32  iRawlen,
+                                           mng_ptr     pRawdata)
+{
+  mng_datap        pData;
+  mng_chunkp       pChunk;
+  mng_retcode      iRetcode;
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  mng_chunk_header sChunkheader =
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+          {MNG_UINT_HUH, mng_init_general, mng_free_unknown, mng_read_unknown, mng_write_unknown, mng_assign_unknown, 0, 0, sizeof(mng_unknown_chunk)};
+#else
+          {MNG_UINT_HUH, mng_init_unknown, mng_free_unknown, mng_read_unknown, mng_write_unknown, mng_assign_unknown, 0, 0};
+#endif
+#else
+  mng_chunk_header sChunkheader;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_UNKNOWN, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must have had a header first! */
+  if (pData->iFirstchunkadded == 0)
+    MNG_ERROR (pData, MNG_NOHEADER)
+                                       /* prevent misplaced TERM ! */
+  if (!check_term (pData, iChunkname))
+    MNG_ERROR (pData, MNG_TERMSEQERROR)
+                                       /* create the chunk */
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#else
+  iRetcode = mng_init_unknown (pData, &sChunkheader, &pChunk);
+#endif
+#else
+  mng_get_chunkheader(MNG_UINT_HUH, &sChunkheader);
+  iRetcode = mng_init_general (pData, &sChunkheader, &pChunk);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fill the chunk */
+  ((mng_unknown_chunkp)pChunk)->sHeader.iChunkname = iChunkname;
+  ((mng_unknown_chunkp)pChunk)->iDatasize          = iRawlen;
+
+  if (iRawlen)
+  {
+    MNG_ALLOC (pData, ((mng_unknown_chunkp)pChunk)->pData, iRawlen);
+    MNG_COPY (((mng_unknown_chunkp)pChunk)->pData, pRawdata, iRawlen);
+  }
+
+  mng_add_chunk (pData, pChunk);       /* add it to the list */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_UNKNOWN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getimgdata_seq (mng_handle        hHandle,
+                                         mng_uint32        iSeqnr,
+                                         mng_uint32        iCanvasstyle,
+                                         mng_getcanvasline fGetcanvasline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_SEQ, MNG_LC_START);
+#endif
+
+
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_SEQ, MNG_LC_END);
+#endif
+
+  return MNG_FNNOTIMPLEMENTED;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getimgdata_chunkseq (mng_handle        hHandle,
+                                              mng_uint32        iSeqnr,
+                                              mng_uint32        iCanvasstyle,
+                                              mng_getcanvasline fGetcanvasline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_CHUNKSEQ, MNG_LC_START);
+#endif
+
+
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_CHUNKSEQ, MNG_LC_END);
+#endif
+
+  return MNG_FNNOTIMPLEMENTED;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getimgdata_chunk (mng_handle        hHandle,
+                                           mng_handle        hChunk,
+                                           mng_uint32        iCanvasstyle,
+                                           mng_getcanvasline fGetcanvasline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_CHUNK, MNG_LC_START);
+#endif
+
+
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_CHUNK, MNG_LC_END);
+#endif
+
+  return MNG_FNNOTIMPLEMENTED;
+}
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_WRITE_PROCS
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_putimgdata_ihdr (mng_handle        hHandle,
+                                          mng_uint32        iWidth,
+                                          mng_uint32        iHeight,
+                                          mng_uint8         iColortype,
+                                          mng_uint8         iBitdepth,
+                                          mng_uint8         iCompression,
+                                          mng_uint8         iFilter,
+                                          mng_uint8         iInterlace,
+                                          mng_uint32        iCanvasstyle,
+                                          mng_getcanvasline fGetcanvasline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTIMGDATA_IHDR, MNG_LC_START);
+#endif
+
+
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTIMGDATA_IHDR, MNG_LC_END);
+#endif
+
+  return MNG_FNNOTIMPLEMENTED;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+mng_retcode MNG_DECL mng_putimgdata_jhdr (mng_handle        hHandle,
+                                          mng_uint32        iWidth,
+                                          mng_uint32        iHeight,
+                                          mng_uint8         iColortype,
+                                          mng_uint8         iBitdepth,
+                                          mng_uint8         iCompression,
+                                          mng_uint8         iInterlace,
+                                          mng_uint8         iAlphaBitdepth,
+                                          mng_uint8         iAlphaCompression,
+                                          mng_uint8         iAlphaFilter,
+                                          mng_uint8         iAlphaInterlace,
+                                          mng_uint32        iCanvasstyle,
+                                          mng_getcanvasline fGetcanvasline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTIMGDATA_JHDR, MNG_LC_START);
+#endif
+
+
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTIMGDATA_JHDR, MNG_LC_END);
+#endif
+
+  return MNG_FNNOTIMPLEMENTED;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_updatemngheader (mng_handle hHandle,
+                                          mng_uint32 iFramecount,
+                                          mng_uint32 iLayercount,
+                                          mng_uint32 iPlaytime)
+{
+  mng_datap  pData;
+  mng_chunkp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_UPDATEMNGHEADER, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must be a MNG animation! */
+  if ((pData->eImagetype != mng_it_mng) || (pData->iFirstchunkadded != MNG_UINT_MHDR))
+    MNG_ERROR (pData, MNG_NOMHDR)
+
+  pChunk = pData->pFirstchunk;         /* get the first chunk */
+                                       /* and update the variables */
+  ((mng_mhdrp)pChunk)->iFramecount = iFramecount;
+  ((mng_mhdrp)pChunk)->iLayercount = iLayercount;
+  ((mng_mhdrp)pChunk)->iPlaytime   = iPlaytime;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_UPDATEMNGHEADER, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_updatemngsimplicity (mng_handle hHandle,
+                                              mng_uint32 iSimplicity)
+{
+  mng_datap  pData;
+  mng_chunkp pChunk;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_UPDATEMNGSIMPLICITY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = (mng_datap)hHandle;          /* and make it addressable */
+
+  if (!pData->bCreating)               /* aren't we creating a new file ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID)
+                                       /* must be a MNG animation! */
+  if ((pData->eImagetype != mng_it_mng) || (pData->iFirstchunkadded != MNG_UINT_MHDR))
+    MNG_ERROR (pData, MNG_NOMHDR)
+
+  pChunk = pData->pFirstchunk;         /* get the first chunk */
+                                       /* and update the variable */
+  ((mng_mhdrp)pChunk)->iSimplicity = iSimplicity;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_UPDATEMNGSIMPLICITY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+
+/* ************************************************************************** */
+
+#endif /* MNG_ACCESS_CHUNKS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
+
+
diff --git a/files/Source/LibMNG/libmng_chunks.h b/files/Source/LibMNG/libmng_chunks.h
new file mode 100644
index 0000000..d10bf2d
--- /dev/null
+++ b/files/Source/LibMNG/libmng_chunks.h
@@ -0,0 +1,1026 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_chunks.h           copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Chunk structures (definition)                              * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of known chunk structures                       * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/04/2000 - G.Juyn                                * */
+/* *             - put in some extra comments                               * */
+/* *             0.5.1 - 05/06/2000 - G.Juyn                                * */
+/* *             - fixed layout for sBIT, PPLT                              * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed write callback definition                        * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - fixed layout for PPLT again (missed deltatype ?!?)       * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/31/2000 - G.Juyn                                * */
+/* *             - removed useless definition (contributed by Tim Rowley)   * */
+/* *             0.5.2 - 06/03/2000 - G.Juyn                                * */
+/* *             - fixed makeup for Linux gcc compile                       * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/10/2000 - G.Juyn                                * */
+/* *             - fixed DEFI behavior                                      * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added JDAA chunk                                         * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - added HLAPI function to copy chunks                      * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 11/28/2002 - G.Juyn                                * */
+/* *             - fixed definition of iMethodX/Y for MAGN chunk            * */
+/* *                                                                        * */
+/* *             1.0.6 - 05/25/2003 - G.R-P                                 * */
+/* *               added MNG_SKIPCHUNK_cHNK footprint optimizations         * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/24/2004 - G.R-P                                 * */
+/* *             - added conditional around MNG_NO_DELTA_PNG support        * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKINITFREE             * */
+/* *             1.0.9 - 12/06/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKREADER               * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_chunks_h_
+#define _libmng_chunks_h_
+
+/* ************************************************************************** */
+
+#ifdef MNG_SWAP_ENDIAN
+#define PNG_SIG 0x474e5089L
+#define JNG_SIG 0x474e4a8bL
+#define MNG_SIG 0x474e4d8aL
+#define POST_SIG 0x0a1a0a0dL
+#else
+#define PNG_SIG 0x89504e47L
+#define JNG_SIG 0x8b4a4e47L
+#define MNG_SIG 0x8a4d4e47L
+#define POST_SIG 0x0d0a1a0aL
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+
+typedef mng_retcode (*mng_f_specialfunc)  (mng_datap   pData,
+                                           mng_chunkp  pChunk,
+                                           mng_uint32* piRawlen,
+                                           mng_uint8p* ppRawdata);
+                                           
+typedef mng_retcode (*mng_c_specialfunc)  (mng_datap  pData,
+                                           mng_chunkp pChunk);
+
+#define MNG_FIELD_OPTIONAL    0x0001
+#define MNG_FIELD_TERMINATOR  0x0002
+#define MNG_FIELD_REPETITIVE  0x0004
+#define MNG_FIELD_DEFLATED    0x0008
+#define MNG_FIELD_IFIMGTYPES  0x01F0   /* image-type mask */
+#define MNG_FIELD_IFIMGTYPE0  0x0010
+#define MNG_FIELD_IFIMGTYPE2  0x0020
+#define MNG_FIELD_IFIMGTYPE3  0x0040
+#define MNG_FIELD_IFIMGTYPE4  0x0080
+#define MNG_FIELD_IFIMGTYPE6  0x0100
+#define MNG_FIELD_PUTIMGTYPE  0x0200
+#define MNG_FIELD_NOHIGHBIT   0x0400
+#define MNG_FIELD_GROUPMASK   0x7000
+#define MNG_FIELD_GROUP1      0x1000
+#define MNG_FIELD_GROUP2      0x2000
+#define MNG_FIELD_GROUP3      0x3000
+#define MNG_FIELD_GROUP4      0x4000
+#define MNG_FIELD_GROUP5      0x5000
+#define MNG_FIELD_GROUP6      0x6000
+#define MNG_FIELD_GROUP7      0x7000
+#define MNG_FIELD_INT         0x8000
+
+typedef struct {                       /* chunk-field descriptor */
+           mng_f_specialfunc pSpecialfunc;
+           mng_uint16        iFlags;
+           mng_uint16        iMinvalue;
+           mng_uint16        iMaxvalue;
+           mng_uint16        iLengthmin;
+           mng_uint16        iLengthmax;
+           mng_uint16        iOffsetchunk;
+           mng_uint16        iOffsetchunkind;
+           mng_uint16        iOffsetchunklen;
+        } mng_field_descriptor;
+typedef mng_field_descriptor * mng_field_descp;
+
+#define MNG_DESCR_GLOBAL      0x0001
+#define MNG_DESCR_EMPTY       0x0002
+#define MNG_DESCR_EMPTYEMBED  0x0006
+#define MNG_DESCR_EMPTYGLOBAL 0x000A
+
+#define MNG_DESCR_GenHDR      0x0001   /* IHDR/JHDR/BASI/DHDR */
+#define MNG_DESCR_JngHDR      0x0002   /* JHDR/DHDR */
+#define MNG_DESCR_MHDR        0x0004
+#define MNG_DESCR_IHDR        0x0008
+#define MNG_DESCR_JHDR        0x0010
+#define MNG_DESCR_DHDR        0x0020
+#define MNG_DESCR_LOOP        0x0040
+#define MNG_DESCR_PLTE        0x0080
+#define MNG_DESCR_SAVE        0x0100
+
+#define MNG_DESCR_NOIHDR      0x0001
+#define MNG_DESCR_NOJHDR      0x0002
+#define MNG_DESCR_NOBASI      0x0004
+#define MNG_DESCR_NODHDR      0x0008
+#define MNG_DESCR_NOIDAT      0x0010
+#define MNG_DESCR_NOJDAT      0x0020
+#define MNG_DESCR_NOJDAA      0x0040
+#define MNG_DESCR_NOPLTE      0x0080
+#define MNG_DESCR_NOJSEP      0x0100
+#define MNG_DESCR_NOMHDR      0x0200
+#define MNG_DESCR_NOTERM      0x0400
+#define MNG_DESCR_NOLOOP      0x0800
+#define MNG_DESCR_NOSAVE      0x1000
+
+typedef struct {                       /* chunk descriptor */
+           mng_imgtype       eImgtype;
+           mng_createobjtype eCreateobject;
+           mng_uint16        iObjsize;
+           mng_uint16        iOffsetempty;
+           mng_ptr           pObjcleanup;
+           mng_ptr           pObjprocess;
+           mng_c_specialfunc pSpecialfunc;
+           mng_field_descp   pFielddesc;
+           mng_uint16        iFielddesc;
+           mng_uint16        iAllowed;
+           mng_uint16        iMusthaves;
+           mng_uint16        iMustNOThaves;
+        } mng_chunk_descriptor;
+typedef mng_chunk_descriptor * mng_chunk_descp;
+
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+
+/* ************************************************************************** */
+
+typedef mng_retcode (*mng_createchunk)  (mng_datap   pData,
+                                         mng_chunkp  pHeader,
+                                         mng_chunkp* ppChunk);
+
+typedef mng_retcode (*mng_cleanupchunk) (mng_datap   pData,
+                                         mng_chunkp  pHeader);
+
+typedef mng_retcode (*mng_readchunk)    (mng_datap   pData,
+                                         mng_chunkp  pHeader,
+                                         mng_uint32  iRawlen,
+                                         mng_uint8p  pRawdata,
+                                         mng_chunkp* pChunk);
+
+typedef mng_retcode (*mng_writechunk)   (mng_datap   pData,
+                                         mng_chunkp  pChunk);
+
+typedef mng_retcode (*mng_assignchunk)  (mng_datap   pData,
+                                         mng_chunkp  pChunkto,
+                                         mng_chunkp  pChunkfrom);
+
+/* ************************************************************************** */
+
+typedef struct {                       /* generic header */
+           mng_chunkid       iChunkname;
+           mng_createchunk   fCreate;
+           mng_cleanupchunk  fCleanup;
+           mng_readchunk     fRead;
+           mng_writechunk    fWrite;
+           mng_assignchunk   fAssign;
+           mng_chunkp        pNext;    /* for double-linked list */
+           mng_chunkp        pPrev;
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+           mng_size_t        iChunksize;
+#endif
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+           mng_chunk_descp   pChunkdescr;
+#endif
+        } mng_chunk_header;
+typedef mng_chunk_header * mng_chunk_headerp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* IHDR */
+           mng_chunk_header  sHeader;
+           mng_uint32        iWidth;
+           mng_uint32        iHeight;
+           mng_uint8         iBitdepth;
+           mng_uint8         iColortype;
+           mng_uint8         iCompression;
+           mng_uint8         iFilter;
+           mng_uint8         iInterlace;
+        } mng_ihdr;
+typedef mng_ihdr * mng_ihdrp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* PLTE */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint32        iEntrycount;
+           mng_rgbpaltab     aEntries;
+        } mng_plte;
+typedef mng_plte * mng_pltep;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* IDAT */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint32        iDatasize;
+           mng_ptr           pData;
+        } mng_idat;
+typedef mng_idat * mng_idatp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* IEND */
+           mng_chunk_header  sHeader;
+        } mng_iend;
+typedef mng_iend * mng_iendp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* tRNS */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_bool          bGlobal;
+           mng_uint8         iType;    /* colortype (0,2,3) */
+           mng_uint32        iCount;
+           mng_uint8arr      aEntries;
+           mng_uint16        iGray;
+           mng_uint16        iRed;
+           mng_uint16        iGreen;
+           mng_uint16        iBlue;
+           mng_uint32        iRawlen;
+           mng_uint8arr      aRawdata;
+        } mng_trns;
+typedef mng_trns * mng_trnsp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* gAMA */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint32        iGamma;
+        } mng_gama;
+typedef mng_gama * mng_gamap;
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+typedef struct {                       /* cHRM */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint32        iWhitepointx;
+           mng_uint32        iWhitepointy;
+           mng_uint32        iRedx;
+           mng_uint32        iRedy;
+           mng_uint32        iGreenx;
+           mng_uint32        iGreeny;
+           mng_uint32        iBluex;
+           mng_uint32        iBluey;
+        } mng_chrm;
+typedef mng_chrm * mng_chrmp;
+#endif
+
+/* ************************************************************************** */
+
+typedef struct {                       /* sRGB */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint8         iRenderingintent;
+        } mng_srgb;
+typedef mng_srgb * mng_srgbp;
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+typedef struct {                       /* iCCP */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint32        iNamesize;
+           mng_pchar         zName;
+           mng_uint8         iCompression;
+           mng_uint32        iProfilesize;
+           mng_ptr           pProfile;
+        } mng_iccp;
+typedef mng_iccp * mng_iccpp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tEXt
+typedef struct {                       /* tEXt */
+           mng_chunk_header  sHeader;
+           mng_uint32        iKeywordsize;
+           mng_pchar         zKeyword;
+           mng_uint32        iTextsize;
+           mng_pchar         zText;
+        } mng_text;
+typedef mng_text * mng_textp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_zTXt
+typedef struct {                       /* zTXt */
+           mng_chunk_header  sHeader;
+           mng_uint32        iKeywordsize;
+           mng_pchar         zKeyword;
+           mng_uint8         iCompression;
+           mng_uint32        iTextsize;
+           mng_pchar         zText;
+        } mng_ztxt;
+typedef mng_ztxt * mng_ztxtp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iTXt
+typedef struct {                       /* iTXt */
+           mng_chunk_header  sHeader;
+           mng_uint32        iKeywordsize;
+           mng_pchar         zKeyword;
+           mng_uint8         iCompressionflag;
+           mng_uint8         iCompressionmethod;
+           mng_uint32        iLanguagesize;
+           mng_pchar         zLanguage;
+           mng_uint32        iTranslationsize;
+           mng_pchar         zTranslation;
+           mng_uint32        iTextsize;
+           mng_pchar         zText;
+        } mng_itxt;
+typedef mng_itxt * mng_itxtp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_bKGD
+typedef struct {                       /* bKGD */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint8         iType;    /* 3=indexed, 0=gray, 2=rgb */
+           mng_uint8         iIndex;
+           mng_uint16        iGray;
+           mng_uint16        iRed;
+           mng_uint16        iGreen;
+           mng_uint16        iBlue;
+        } mng_bkgd;
+typedef mng_bkgd * mng_bkgdp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYs
+typedef struct {                       /* pHYs */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint32        iSizex;
+           mng_uint32        iSizey;
+           mng_uint8         iUnit;
+        } mng_phys;
+typedef mng_phys * mng_physp;
+#endif
+
+/* ************************************************************************** */
+#ifndef MNG_SKIPCHUNK_sBIT
+
+typedef struct {                       /* sBIT */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint8         iType;    /* colortype (0,2,3,4,6,10,12,14,16) */
+           mng_uint8arr4     aBits;
+        } mng_sbit;
+typedef mng_sbit * mng_sbitp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sPLT
+typedef struct {                       /* sPLT */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint32        iNamesize;
+           mng_pchar         zName;
+           mng_uint8         iSampledepth;
+           mng_uint32        iEntrycount;
+           mng_ptr           pEntries;
+        } mng_splt;
+typedef mng_splt * mng_spltp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_hIST
+typedef struct {                       /* hIST */
+           mng_chunk_header  sHeader;
+           mng_uint32        iEntrycount;
+           mng_uint16arr     aEntries;
+        } mng_hist;
+typedef mng_hist * mng_histp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_tIME
+typedef struct {                       /* tIME */
+           mng_chunk_header  sHeader;
+           mng_uint16        iYear;
+           mng_uint8         iMonth;
+           mng_uint8         iDay;
+           mng_uint8         iHour;
+           mng_uint8         iMinute;
+           mng_uint8         iSecond;
+        } mng_time;
+typedef mng_time * mng_timep;
+#endif
+
+/* ************************************************************************** */
+
+typedef struct {                       /* MHDR */
+           mng_chunk_header  sHeader;
+           mng_uint32        iWidth;
+           mng_uint32        iHeight;
+           mng_uint32        iTicks;
+           mng_uint32        iLayercount;
+           mng_uint32        iFramecount;
+           mng_uint32        iPlaytime;
+           mng_uint32        iSimplicity;
+        } mng_mhdr;
+typedef mng_mhdr * mng_mhdrp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* MEND */
+           mng_chunk_header  sHeader;
+        } mng_mend;
+typedef mng_mend * mng_mendp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* LOOP */
+           mng_chunk_header  sHeader;
+           mng_uint8         iLevel;
+           mng_uint32        iRepeat;
+           mng_uint8         iTermination;
+           mng_uint32        iItermin;
+           mng_uint32        iItermax;
+           mng_uint32        iCount;
+           mng_uint32p       pSignals;
+        } mng_loop;
+typedef mng_loop * mng_loopp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* ENDL */
+           mng_chunk_header  sHeader;
+           mng_uint8         iLevel;
+        } mng_endl;
+typedef mng_endl * mng_endlp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* DEFI */
+           mng_chunk_header  sHeader;
+           mng_uint16        iObjectid;
+           mng_bool          bHasdonotshow;
+           mng_uint8         iDonotshow;
+           mng_bool          bHasconcrete;
+           mng_uint8         iConcrete;
+           mng_bool          bHasloca;
+           mng_int32         iXlocation;
+           mng_int32         iYlocation;
+           mng_bool          bHasclip;
+           mng_int32         iLeftcb;
+           mng_int32         iRightcb;
+           mng_int32         iTopcb;
+           mng_int32         iBottomcb;
+        } mng_defi;
+typedef mng_defi * mng_defip;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* BASI */
+           mng_chunk_header  sHeader;
+           mng_uint32        iWidth;
+           mng_uint32        iHeight;
+           mng_uint8         iBitdepth;
+           mng_uint8         iColortype;
+           mng_uint8         iCompression;
+           mng_uint8         iFilter;
+           mng_uint8         iInterlace;
+           mng_uint16        iRed;
+           mng_uint16        iGreen;
+           mng_uint16        iBlue;
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+           mng_bool          bHasalpha;
+#endif
+           mng_uint16        iAlpha;
+           mng_uint8         iViewable;
+        } mng_basi;
+typedef mng_basi * mng_basip;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* CLON */
+           mng_chunk_header  sHeader;
+           mng_uint16        iSourceid;
+           mng_uint16        iCloneid;
+           mng_uint8         iClonetype;
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+           mng_bool          bHasdonotshow;
+#endif
+           mng_uint8         iDonotshow;
+           mng_uint8         iConcrete;
+           mng_bool          bHasloca;
+           mng_uint8         iLocationtype;
+           mng_int32         iLocationx;
+           mng_int32         iLocationy;
+        } mng_clon;
+typedef mng_clon * mng_clonp;
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+typedef struct {                       /* PAST source */
+           mng_uint16        iSourceid;
+           mng_uint8         iComposition;
+           mng_uint8         iOrientation;
+           mng_uint8         iOffsettype;
+           mng_int32         iOffsetx;
+           mng_int32         iOffsety;
+           mng_uint8         iBoundarytype;
+           mng_int32         iBoundaryl;
+           mng_int32         iBoundaryr;
+           mng_int32         iBoundaryt;
+           mng_int32         iBoundaryb;
+        } mng_past_source;
+typedef mng_past_source * mng_past_sourcep;
+
+typedef struct {                       /* PAST */
+           mng_chunk_header  sHeader;
+           mng_uint16        iDestid;
+           mng_uint8         iTargettype;
+           mng_int32         iTargetx;
+           mng_int32         iTargety;
+           mng_uint32        iCount;
+           mng_past_sourcep  pSources;
+        } mng_past;
+typedef mng_past * mng_pastp;
+#endif
+
+/* ************************************************************************** */
+
+typedef struct {                       /* DISC */
+           mng_chunk_header  sHeader;
+           mng_uint32        iCount;
+           mng_uint16p       pObjectids;
+        } mng_disc;
+typedef mng_disc * mng_discp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* BACK */
+           mng_chunk_header  sHeader;
+           mng_uint16        iRed;
+           mng_uint16        iGreen;
+           mng_uint16        iBlue;
+           mng_uint8         iMandatory;
+           mng_uint16        iImageid;
+           mng_uint8         iTile;
+        } mng_back;
+typedef mng_back * mng_backp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* FRAM */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint8         iMode;
+           mng_uint32        iNamesize;
+           mng_pchar         zName;
+           mng_uint8         iChangedelay;
+           mng_uint8         iChangetimeout;
+           mng_uint8         iChangeclipping;
+           mng_uint8         iChangesyncid;
+           mng_uint32        iDelay;
+           mng_uint32        iTimeout;
+           mng_uint8         iBoundarytype;
+           mng_int32         iBoundaryl;
+           mng_int32         iBoundaryr;
+           mng_int32         iBoundaryt;
+           mng_int32         iBoundaryb;
+           mng_uint32        iCount;
+           mng_uint32p       pSyncids;
+        } mng_fram;
+typedef mng_fram * mng_framp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* MOVE */
+           mng_chunk_header  sHeader;
+           mng_uint16        iFirstid;
+           mng_uint16        iLastid;
+           mng_uint8         iMovetype;
+           mng_int32         iMovex;
+           mng_int32         iMovey;
+        } mng_move;
+typedef mng_move * mng_movep;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* CLIP */
+           mng_chunk_header  sHeader;
+           mng_uint16        iFirstid;
+           mng_uint16        iLastid;
+           mng_uint8         iCliptype;
+           mng_int32         iClipl;
+           mng_int32         iClipr;
+           mng_int32         iClipt;
+           mng_int32         iClipb;
+        } mng_clip;
+typedef mng_clip * mng_clipp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* SHOW */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint16        iFirstid;
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+           mng_bool          bHaslastid;
+#endif
+           mng_uint16        iLastid;
+           mng_uint8         iMode;
+        } mng_show;
+typedef mng_show * mng_showp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* TERM */
+           mng_chunk_header  sHeader;
+           mng_uint8         iTermaction;
+           mng_uint8         iIteraction;
+           mng_uint32        iDelay;
+           mng_uint32        iItermax;
+        } mng_term;
+typedef mng_term * mng_termp;
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+typedef struct {                       /* SAVE entry */
+           mng_uint8         iEntrytype;
+           mng_uint32arr2    iOffset;            /* 0=MSI, 1=LSI */
+           mng_uint32arr2    iStarttime;         /* 0=MSI, 1=LSI */
+           mng_uint32        iLayernr;
+           mng_uint32        iFramenr;
+           mng_uint32        iNamesize;
+           mng_pchar         zName;
+        } mng_save_entry;
+typedef mng_save_entry * mng_save_entryp;
+
+typedef struct {                       /* SAVE */
+           mng_chunk_header  sHeader;
+           mng_bool          bEmpty;
+           mng_uint8         iOffsettype;
+           mng_uint32        iCount;
+           mng_save_entryp   pEntries;
+        } mng_save;
+typedef mng_save * mng_savep;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+typedef struct {                       /* SEEK */
+           mng_chunk_header  sHeader;
+           mng_uint32        iNamesize;
+           mng_pchar         zName;
+        } mng_seek;
+typedef mng_seek * mng_seekp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_eXPI
+typedef struct {                       /* eXPI */
+           mng_chunk_header  sHeader;
+           mng_uint16        iSnapshotid;
+           mng_uint32        iNamesize;
+           mng_pchar         zName;
+        } mng_expi;
+typedef mng_expi * mng_expip;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_fPRI
+typedef struct {                       /* fPRI */
+           mng_chunk_header  sHeader;
+           mng_uint8         iDeltatype;
+           mng_uint8         iPriority;
+        } mng_fpri;
+typedef mng_fpri * mng_fprip;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_nEED
+typedef struct {                       /* nEED */
+           mng_chunk_header  sHeader;
+           mng_uint32        iKeywordssize;
+           mng_pchar         zKeywords;
+        } mng_need;
+typedef mng_need * mng_needp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_pHYg
+typedef mng_phys mng_phyg;             /* pHYg */
+typedef mng_phyg * mng_phygp;
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+typedef struct {                       /* JHDR */
+           mng_chunk_header  sHeader;
+           mng_uint32        iWidth;
+           mng_uint32        iHeight;
+           mng_uint8         iColortype;
+           mng_uint8         iImagesampledepth;
+           mng_uint8         iImagecompression;
+           mng_uint8         iImageinterlace;
+           mng_uint8         iAlphasampledepth;
+           mng_uint8         iAlphacompression;
+           mng_uint8         iAlphafilter;
+           mng_uint8         iAlphainterlace;
+        } mng_jhdr;
+typedef mng_jhdr * mng_jhdrp;
+
+/* ************************************************************************** */
+
+typedef mng_idat mng_jdaa;             /* JDAA */
+typedef mng_jdaa * mng_jdaap;
+
+/* ************************************************************************** */
+
+typedef mng_idat mng_jdat;             /* JDAT */
+typedef mng_jdat * mng_jdatp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* JSEP */
+           mng_chunk_header  sHeader;
+        } mng_jsep;
+typedef mng_jsep * mng_jsepp;
+
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+
+typedef struct {                       /* DHDR */
+           mng_chunk_header  sHeader;
+           mng_uint16        iObjectid;
+           mng_uint8         iImagetype;
+           mng_uint8         iDeltatype;
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+           mng_bool          bHasblocksize;
+#endif
+           mng_uint32        iBlockwidth;
+           mng_uint32        iBlockheight;
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+           mng_bool          bHasblockloc;
+#endif
+           mng_uint32        iBlockx;
+           mng_uint32        iBlocky;
+        } mng_dhdr;
+typedef mng_dhdr * mng_dhdrp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* PROM */
+           mng_chunk_header  sHeader;
+           mng_uint8         iColortype;
+           mng_uint8         iSampledepth;
+           mng_uint8         iFilltype;
+        } mng_prom;
+typedef mng_prom * mng_promp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* IPNG */
+           mng_chunk_header  sHeader;
+        } mng_ipng;
+typedef mng_ipng *mng_ipngp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* PPLT entry */
+           mng_uint8         iRed;
+           mng_uint8         iGreen;
+           mng_uint8         iBlue;
+           mng_uint8         iAlpha;
+           mng_bool          bUsed;
+        } mng_pplt_entry;
+typedef mng_pplt_entry * mng_pplt_entryp;
+
+typedef struct {                       /* PPLT */
+           mng_chunk_header  sHeader;
+           mng_uint8         iDeltatype;
+           mng_uint32        iCount;
+           mng_pplt_entry    aEntries [256];
+        } mng_pplt;
+typedef mng_pplt * mng_ppltp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* IJNG */
+           mng_chunk_header  sHeader;
+        } mng_ijng;
+typedef mng_ijng *mng_ijngp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* DROP */
+           mng_chunk_header  sHeader;
+           mng_uint32        iCount;
+           mng_chunkidp      pChunknames;
+        } mng_drop;
+typedef mng_drop * mng_dropp;
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DBYK
+typedef struct {                       /* DBYK */
+           mng_chunk_header  sHeader;
+           mng_chunkid       iChunkname;
+           mng_uint8         iPolarity;
+           mng_uint32        iKeywordssize;
+           mng_pchar         zKeywords;
+        } mng_dbyk;
+typedef mng_dbyk * mng_dbykp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_ORDR
+typedef struct {                       /* ORDR entry */
+           mng_chunkid       iChunkname;
+           mng_uint8         iOrdertype;
+        } mng_ordr_entry;
+typedef mng_ordr_entry * mng_ordr_entryp;
+
+typedef struct mng_ordr_struct {       /* ORDR */
+           mng_chunk_header  sHeader;
+           mng_uint32        iCount;
+           mng_ordr_entryp   pEntries;
+        } mng_ordr;
+typedef mng_ordr * mng_ordrp;
+#endif
+#endif /* MNG_NO_DELTA_PNG */
+
+/* ************************************************************************** */
+
+typedef struct {                       /* MAGN */
+           mng_chunk_header  sHeader;
+           mng_uint16        iFirstid;
+           mng_uint16        iLastid;
+           mng_uint8         iMethodX;
+           mng_uint16        iMX;
+           mng_uint16        iMY;
+           mng_uint16        iML;
+           mng_uint16        iMR;
+           mng_uint16        iMT;
+           mng_uint16        iMB;
+           mng_uint8         iMethodY;
+        } mng_magn;
+typedef mng_magn * mng_magnp;
+
+/* ************************************************************************** */
+
+typedef struct {                       /* evNT entry */
+           mng_uint8         iEventtype;
+           mng_uint8         iMasktype;
+           mng_int32         iLeft;
+           mng_int32         iRight;
+           mng_int32         iTop;
+           mng_int32         iBottom;
+           mng_uint16        iObjectid;
+           mng_uint8         iIndex;
+           mng_uint32        iSegmentnamesize;
+           mng_pchar         zSegmentname;
+        } mng_evnt_entry;
+typedef mng_evnt_entry * mng_evnt_entryp;
+
+typedef struct {                       /* evNT */
+           mng_chunk_header  sHeader;
+           mng_uint32        iCount;
+           mng_evnt_entryp   pEntries;
+        } mng_evnt;
+typedef mng_evnt * mng_evntp;
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+typedef struct {                       /* mpNG frame */
+           mng_uint32        iX;
+           mng_uint32        iY;
+           mng_uint32        iWidth;
+           mng_uint32        iHeight;
+           mng_int32         iXoffset;
+           mng_int32         iYoffset;
+           mng_uint16        iTicks;
+        } mng_mpng_frame;
+typedef mng_mpng_frame * mng_mpng_framep;
+
+typedef struct {                       /* mpNG */
+           mng_chunk_header  sHeader;
+           mng_uint32        iFramewidth;
+           mng_uint32        iFrameheight;
+           mng_uint16        iNumplays;
+           mng_uint16        iTickspersec;
+           mng_uint8         iCompressionmethod;
+           mng_uint32        iFramessize;
+           mng_mpng_framep   pFrames;
+        } mng_mpng;
+typedef mng_mpng * mng_mpngp;
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+typedef struct {                       /* ahDR */
+           mng_chunk_header  sHeader;
+           mng_uint32        iNumframes;
+           mng_uint32        iTickspersec;
+           mng_uint32        iNumplays;
+           mng_uint32        iTilewidth;
+           mng_uint32        iTileheight;
+           mng_uint8         iInterlace;
+           mng_uint8         iStillused;
+        } mng_ahdr;
+typedef mng_ahdr * mng_ahdrp;
+
+typedef struct {                       /* adAT tile */
+           mng_uint32        iTicks;
+           mng_int32         iXoffset;
+           mng_int32         iYoffset;
+           mng_uint8         iTilesource;
+        } mng_adat_tile;
+typedef mng_adat_tile * mng_adat_tilep;
+
+typedef struct {                       /* adAT */
+           mng_chunk_header  sHeader;
+           mng_uint32        iTilessize;
+           mng_adat_tilep    pTiles;
+        } mng_adat;
+typedef mng_adat * mng_adatp;
+#endif
+
+/* ************************************************************************** */
+
+typedef struct {                       /* unknown chunk */
+           mng_chunk_header  sHeader;
+           mng_uint32        iDatasize;
+           mng_ptr           pData;
+        } mng_unknown_chunk;
+typedef mng_unknown_chunk * mng_unknown_chunkp;
+
+/* ************************************************************************** */
+
+#endif /* _libmng_chunks_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_cms.c b/files/Source/LibMNG/libmng_cms.c
new file mode 100644
index 0000000..999575f
--- /dev/null
+++ b/files/Source/LibMNG/libmng_cms.c
@@ -0,0 +1,758 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_cms.c              copyright (c) 2000-2004 G.Juyn   * */
+/* * version   : 1.0.9                                                      * */
+/* *                                                                        * */
+/* * purpose   : color management routines (implementation)                 * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the color management routines            * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/01/2000 - G.Juyn                                * */
+/* *             - B001(105795) - fixed a typo and misconception about      * */
+/* *               freeing allocated gamma-table. (reported by Marti Maria) * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/09/2000 - G.Juyn                                * */
+/* *             - filled application-based color-management routines       * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added creatememprofile                                   * */
+/* *             - added callback error-reporting support                   * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *                                                                        * */
+/* *             0.5.2 - 06/10/2000 - G.Juyn                                * */
+/* *             - fixed some compilation-warnings (contrib Jason Morris)   * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - fixed problem with color-correction for stored images    * */
+/* *             0.5.3 - 06/23/2000 - G.Juyn                                * */
+/* *             - fixed problem with incorrect gamma-correction            * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/31/2000 - G.Juyn                                * */
+/* *             - fixed sRGB precedence for gamma_only corection           * */
+/* *                                                                        * */
+/* *             0.9.4 - 12/16/2000 - G.Juyn                                * */
+/* *             - fixed mixup of data- & function-pointers (thanks Dimitri)* */
+/* *                                                                        * */
+/* *             1.0.1 - 03/31/2001 - G.Juyn                                * */
+/* *             - ignore gamma=0 (see png-list for more info)              * */
+/* *             1.0.1 - 04/25/2001 - G.Juyn (reported by Gregg Kelly)      * */
+/* *             - fixed problem with cms profile being created multiple    * */
+/* *               times when both iCCP & cHRM/gAMA are present             * */
+/* *             1.0.1 - 04/25/2001 - G.Juyn                                * */
+/* *             - moved mng_clear_cms to libmng_cms                        * */
+/* *             1.0.1 - 05/02/2001 - G.Juyn                                * */
+/* *             - added "default" sRGB generation (Thanks Marti!)          * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/19/2002 - G.Juyn                                * */
+/* *             - optimized color-correction routines                      * */
+/* *             1.0.5 - 09/23/2002 - G.Juyn                                * */
+/* *             - added in-memory color-correction of abstract images      * */
+/* *             1.0.5 - 11/08/2002 - G.Juyn                                * */
+/* *             - fixed issues in init_app_cms()                           * */
+/* *                                                                        * */
+/* *             1.0.6 - 04/11/2003 - G.Juyn                                * */
+/* *             - B719420 - fixed several MNG_APP_CMS problems             * */
+/* *             1.0.6 - 07/11/2003 - G. R-P                                * */
+/* *             - added conditional MNG_SKIPCHUNK_cHRM/iCCP                * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_objects.h"
+#include "libmng_cms.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_DISPLAY_PROCS
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Little CMS helper routines                                             * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_LCMS
+
+#define MNG_CMS_FLAGS 0
+
+/* ************************************************************************** */
+
+void mnglcms_initlibrary ()
+{
+  cmsErrorAction (LCMS_ERROR_IGNORE);  /* LCMS should ignore errors! */
+}
+
+/* ************************************************************************** */
+
+mng_cmsprof mnglcms_createfileprofile (mng_pchar zFilename)
+{
+  return cmsOpenProfileFromFile (zFilename, "r");
+}
+
+/* ************************************************************************** */
+
+mng_cmsprof mnglcms_creatememprofile (mng_uint32 iProfilesize,
+                                      mng_ptr    pProfile)
+{
+  return cmsOpenProfileFromMem (pProfile, iProfilesize);
+}
+
+/* ************************************************************************** */
+
+mng_cmsprof mnglcms_createsrgbprofile (void)
+{
+  cmsCIExyY       D65;
+  cmsCIExyYTRIPLE Rec709Primaries = {
+                                      {0.6400, 0.3300, 1.0},
+                                      {0.3000, 0.6000, 1.0},
+                                      {0.1500, 0.0600, 1.0}
+                                    };
+  LPGAMMATABLE    Gamma24[3];
+  mng_cmsprof     hsRGB;
+
+  cmsWhitePointFromTemp(6504, &D65);
+  Gamma24[0] = Gamma24[1] = Gamma24[2] = cmsBuildGamma(256, 2.4);
+  hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma24);
+  cmsFreeGamma(Gamma24[0]);
+
+  return hsRGB;
+}
+
+/* ************************************************************************** */
+
+void mnglcms_freeprofile (mng_cmsprof hProf)
+{
+  cmsCloseProfile (hProf);
+  return;
+}
+
+/* ************************************************************************** */
+
+void mnglcms_freetransform (mng_cmstrans hTrans)
+{
+/* B001 start */
+  cmsDeleteTransform (hTrans);
+/* B001 end */
+  return;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_clear_cms (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLEAR_CMS, MNG_LC_START);
+#endif
+
+  if (pData->hTrans)                   /* transformation still active ? */
+    mnglcms_freetransform (pData->hTrans);
+
+  pData->hTrans = 0;
+
+  if (pData->hProf1)                   /* file profile still active ? */
+    mnglcms_freeprofile (pData->hProf1);
+
+  pData->hProf1 = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLEAR_CMS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_LCMS */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Color-management initialization & correction routines                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_LCMS
+
+mng_retcode mng_init_full_cms (mng_datap pData,
+                               mng_bool  bGlobal,
+                               mng_bool  bObject,
+                               mng_bool  bRetrobj)
+{
+  mng_cmsprof    hProf;
+  mng_cmstrans   hTrans;
+  mng_imagep     pImage = MNG_NULL;
+  mng_imagedatap pBuf   = MNG_NULL;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_FULL_CMS, MNG_LC_START);
+#endif
+
+  if (bObject)                         /* use object if present ? */
+  {                                    /* current object ? */
+    if ((mng_imagep)pData->pCurrentobj)
+      pImage = (mng_imagep)pData->pCurrentobj;
+    else                               /* if not; use object 0 */
+      pImage = (mng_imagep)pData->pObjzero;
+  }
+
+  if (bRetrobj)                        /* retrieving from an object ? */
+    pImage = (mng_imagep)pData->pRetrieveobj;
+
+  if (pImage)                          /* are we using an object ? */
+    pBuf = pImage->pImgbuf;            /* then address the buffer */
+
+  if ((!pBuf) || (!pBuf->bCorrected))  /* is the buffer already corrected ? */
+  {
+#ifndef MNG_SKIPCHUNK_iCCP
+    if (((pBuf) && (pBuf->bHasICCP)) || ((bGlobal) && (pData->bHasglobalICCP)))
+    {
+      if (!pData->hProf2)              /* output profile not defined ? */
+      {                                /* then assume sRGB !! */
+        pData->hProf2 = mnglcms_createsrgbprofile ();
+
+        if (!pData->hProf2)            /* handle error ? */
+          MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+      }
+
+      if ((pBuf) && (pBuf->bHasICCP))  /* generate a profile handle */
+        hProf = cmsOpenProfileFromMem (pBuf->pProfile, pBuf->iProfilesize);
+      else
+        hProf = cmsOpenProfileFromMem (pData->pGlobalProfile, pData->iGlobalProfilesize);
+
+      pData->hProf1 = hProf;           /* save for future use */
+
+      if (!hProf)                      /* handle error ? */
+        MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (pData->bIsRGBA16)            /* 16-bit intermediates ? */
+        hTrans = cmsCreateTransform (hProf,         TYPE_RGBA_16_SE,
+                                     pData->hProf2, TYPE_RGBA_16_SE,
+                                     INTENT_PERCEPTUAL, MNG_CMS_FLAGS);
+      else
+#endif
+        hTrans = cmsCreateTransform (hProf,         TYPE_RGBA_8,
+                                     pData->hProf2, TYPE_RGBA_8,
+                                     INTENT_PERCEPTUAL, MNG_CMS_FLAGS);
+
+      pData->hTrans = hTrans;          /* save for future use */
+
+      if (!hTrans)                     /* handle error ? */
+        MNG_ERRORL (pData, MNG_LCMS_NOTRANS);
+                                       /* load color-correction routine */
+      pData->fCorrectrow = (mng_fptr)mng_correct_full_cms;
+
+      return MNG_NOERROR;              /* and done */
+    }
+    else
+#endif
+    if (((pBuf) && (pBuf->bHasSRGB)) || ((bGlobal) && (pData->bHasglobalSRGB)))
+    {
+      mng_uint8 iIntent;
+
+      if (pData->bIssRGB)              /* sRGB system ? */
+        return MNG_NOERROR;            /* no conversion required */
+
+      if (!pData->hProf3)              /* sRGB profile not defined ? */
+      {                                /* then create it implicitly !! */
+        pData->hProf3 = mnglcms_createsrgbprofile ();
+
+        if (!pData->hProf3)            /* handle error ? */
+          MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+      }
+
+      hProf = pData->hProf3;           /* convert from sRGB profile */
+
+      if ((pBuf) && (pBuf->bHasSRGB))  /* determine rendering intent */
+        iIntent = pBuf->iRenderingintent;
+      else
+        iIntent = pData->iGlobalRendintent;
+
+      if (pData->bIsRGBA16)            /* 16-bit intermediates ? */
+        hTrans = cmsCreateTransform (hProf,         TYPE_RGBA_16_SE,
+                                     pData->hProf2, TYPE_RGBA_16_SE,
+                                     iIntent, MNG_CMS_FLAGS);
+      else
+        hTrans = cmsCreateTransform (hProf,         TYPE_RGBA_8,
+                                     pData->hProf2, TYPE_RGBA_8,
+                                     iIntent, MNG_CMS_FLAGS);
+
+      pData->hTrans = hTrans;          /* save for future use */
+
+      if (!hTrans)                     /* handle error ? */
+        MNG_ERRORL (pData, MNG_LCMS_NOTRANS);
+                                       /* load color-correction routine */
+      pData->fCorrectrow = (mng_fptr)mng_correct_full_cms;
+
+      return MNG_NOERROR;              /* and done */
+    }
+    else
+    if ( (((pBuf) && (pBuf->bHasCHRM)) || ((bGlobal) && (pData->bHasglobalCHRM))) &&
+         ( ((pBuf) && (pBuf->bHasGAMA) && (pBuf->iGamma > 0)) ||
+           ((bGlobal) && (pData->bHasglobalGAMA) && (pData->iGlobalGamma > 0))  )    )
+    {
+      mng_CIExyY       sWhitepoint;
+      mng_CIExyYTRIPLE sPrimaries;
+      mng_gammatabp    pGammatable[3];
+      mng_float        dGamma;
+
+      if (!pData->hProf2)              /* output profile not defined ? */
+      {                                /* then assume sRGB !! */
+        pData->hProf2 = mnglcms_createsrgbprofile ();
+
+        if (!pData->hProf2)            /* handle error ? */
+          MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+      }
+
+#ifndef MNG_SKIPCHUNK_cHRM
+      if ((pBuf) && (pBuf->bHasCHRM))  /* local cHRM ? */
+      {
+        sWhitepoint.x      = (mng_float)pBuf->iWhitepointx   / 100000;
+        sWhitepoint.y      = (mng_float)pBuf->iWhitepointy   / 100000;
+        sPrimaries.Red.x   = (mng_float)pBuf->iPrimaryredx   / 100000;
+        sPrimaries.Red.y   = (mng_float)pBuf->iPrimaryredy   / 100000;
+        sPrimaries.Green.x = (mng_float)pBuf->iPrimarygreenx / 100000;
+        sPrimaries.Green.y = (mng_float)pBuf->iPrimarygreeny / 100000;
+        sPrimaries.Blue.x  = (mng_float)pBuf->iPrimarybluex  / 100000;
+        sPrimaries.Blue.y  = (mng_float)pBuf->iPrimarybluey  / 100000;
+      }
+      else
+      {
+        sWhitepoint.x      = (mng_float)pData->iGlobalWhitepointx   / 100000;
+        sWhitepoint.y      = (mng_float)pData->iGlobalWhitepointy   / 100000;
+        sPrimaries.Red.x   = (mng_float)pData->iGlobalPrimaryredx   / 100000;
+        sPrimaries.Red.y   = (mng_float)pData->iGlobalPrimaryredy   / 100000;
+        sPrimaries.Green.x = (mng_float)pData->iGlobalPrimarygreenx / 100000;
+        sPrimaries.Green.y = (mng_float)pData->iGlobalPrimarygreeny / 100000;
+        sPrimaries.Blue.x  = (mng_float)pData->iGlobalPrimarybluex  / 100000;
+        sPrimaries.Blue.y  = (mng_float)pData->iGlobalPrimarybluey  / 100000;
+      }
+#endif
+
+      sWhitepoint.Y      =             /* Y component is always 1.0 */
+      sPrimaries.Red.Y   =
+      sPrimaries.Green.Y =
+      sPrimaries.Blue.Y  = 1.0;
+
+      if ((pBuf) && (pBuf->bHasGAMA))  /* get the gamma value */
+        dGamma = (mng_float)pBuf->iGamma / 100000;
+      else
+        dGamma = (mng_float)pData->iGlobalGamma / 100000;
+
+      dGamma = pData->dViewgamma / dGamma;
+
+      pGammatable [0] =                /* and build the lookup tables */
+      pGammatable [1] =
+      pGammatable [2] = cmsBuildGamma (256, dGamma);
+
+      if (!pGammatable [0])            /* enough memory ? */
+        MNG_ERRORL (pData, MNG_LCMS_NOMEM);
+                                       /* create the profile */
+      hProf = cmsCreateRGBProfile (&sWhitepoint, &sPrimaries, pGammatable);
+
+      cmsFreeGamma (pGammatable [0]);  /* free the temporary gamma tables ? */
+                                       /* yes! but just the one! */
+
+      pData->hProf1 = hProf;           /* save for future use */
+
+      if (!hProf)                      /* handle error ? */
+        MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+
+      if (pData->bIsRGBA16)            /* 16-bit intermediates ? */
+        hTrans = cmsCreateTransform (hProf,         TYPE_RGBA_16_SE,
+                                     pData->hProf2, TYPE_RGBA_16_SE,
+                                     INTENT_PERCEPTUAL, MNG_CMS_FLAGS);
+      else
+        hTrans = cmsCreateTransform (hProf,         TYPE_RGBA_8,
+                                     pData->hProf2, TYPE_RGBA_8,
+                                     INTENT_PERCEPTUAL, MNG_CMS_FLAGS);
+
+      pData->hTrans = hTrans;          /* save for future use */
+
+      if (!hTrans)                     /* handle error ? */
+        MNG_ERRORL (pData, MNG_LCMS_NOTRANS);
+                                       /* load color-correction routine */
+      pData->fCorrectrow = (mng_fptr)mng_correct_full_cms;
+
+      return MNG_NOERROR;              /* and done */
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_FULL_CMS, MNG_LC_END);
+#endif
+                                       /* if we get here, we'll only do gamma */
+  return mng_init_gamma_only (pData, bGlobal, bObject, bRetrobj);
+}
+#endif /* MNG_INCLUDE_LCMS */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_LCMS
+mng_retcode mng_correct_full_cms (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CORRECT_FULL_CMS, MNG_LC_START);
+#endif
+
+  cmsDoTransform (pData->hTrans, pData->pRGBArow, pData->pRGBArow, pData->iRowsamples);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CORRECT_FULL_CMS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_LCMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_GAMMA_ONLY) || defined(MNG_FULL_CMS) || defined(MNG_APP_CMS)
+mng_retcode mng_init_gamma_only (mng_datap pData,
+                                 mng_bool  bGlobal,
+                                 mng_bool  bObject,
+                                 mng_bool  bRetrobj)
+{
+  mng_float      dGamma;
+  mng_imagep     pImage = MNG_NULL;
+  mng_imagedatap pBuf   = MNG_NULL;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GAMMA_ONLY, MNG_LC_START);
+#endif
+
+  if (bObject)                         /* use object if present ? */
+  {                                    /* current object ? */
+    if ((mng_imagep)pData->pCurrentobj)
+      pImage = (mng_imagep)pData->pCurrentobj;
+    else                               /* if not; use object 0 */
+      pImage = (mng_imagep)pData->pObjzero;
+  }
+
+  if (bRetrobj)                        /* retrieving from an object ? */
+    pImage = (mng_imagep)pData->pRetrieveobj;
+
+  if (pImage)                          /* are we using an object ? */
+    pBuf = pImage->pImgbuf;            /* then address the buffer */
+
+  if ((!pBuf) || (!pBuf->bCorrected))  /* is the buffer already corrected ? */
+  {
+    if ((pBuf) && (pBuf->bHasSRGB))    /* get the gamma value */
+      dGamma = 0.45455;
+    else
+    if ((pBuf) && (pBuf->bHasGAMA))
+      dGamma = (mng_float)pBuf->iGamma / 100000;
+    else
+    if ((bGlobal) && (pData->bHasglobalSRGB))
+      dGamma = 0.45455;
+    else
+    if ((bGlobal) && (pData->bHasglobalGAMA))
+      dGamma = (mng_float)pData->iGlobalGamma / 100000;
+    else
+      dGamma = pData->dDfltimggamma;
+
+    if (dGamma > 0)                    /* ignore gamma=0 */
+    {
+      dGamma = pData->dViewgamma / (dGamma * pData->dDisplaygamma);
+
+      if (dGamma != pData->dLastgamma) /* lookup table needs to be computed ? */
+      {
+        mng_int32 iX;
+
+        pData->aGammatab [0] = 0;
+
+        for (iX = 1; iX <= 255; iX++)
+          pData->aGammatab [iX] = (mng_uint8)(pow (iX / 255.0, dGamma) * 255 + 0.5);
+
+        pData->dLastgamma = dGamma;    /* keep for next time */
+      }
+                                       /* load color-correction routine */
+      pData->fCorrectrow = (mng_fptr)mng_correct_gamma_only;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GAMMA_ONLY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_GAMMA_ONLY || MNG_FULL_CMS || MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#if defined(MNG_GAMMA_ONLY) || defined(MNG_FULL_CMS) || defined(MNG_APP_CMS)
+mng_retcode mng_correct_gamma_only (mng_datap pData)
+{
+  mng_uint8p pWork;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CORRECT_GAMMA_ONLY, MNG_LC_START);
+#endif
+
+  pWork = pData->pRGBArow;             /* address intermediate row */
+
+  if (pData->bIsRGBA16)                /* 16-bit intermediate row ? */
+  {
+
+  
+     /* TODO: 16-bit precision gamma processing */
+     /* we'll just do the high-order byte for now */
+
+     
+                                       /* convert all samples in the row */
+     for (iX = 0; iX < pData->iRowsamples; iX++)
+     {                                 /* using the precalculated gamma lookup table */
+       *pWork     = pData->aGammatab [*pWork];
+       *(pWork+2) = pData->aGammatab [*(pWork+2)];
+       *(pWork+4) = pData->aGammatab [*(pWork+4)];
+
+       pWork += 8;
+     }
+  }
+  else
+  {                                    /* convert all samples in the row */
+     for (iX = 0; iX < pData->iRowsamples; iX++)
+     {                                 /* using the precalculated gamma lookup table */
+       *pWork     = pData->aGammatab [*pWork];
+       *(pWork+1) = pData->aGammatab [*(pWork+1)];
+       *(pWork+2) = pData->aGammatab [*(pWork+2)];
+
+       pWork += 4;
+     }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CORRECT_GAMMA_ONLY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_GAMMA_ONLY || MNG_FULL_CMS || MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#ifdef MNG_APP_CMS
+mng_retcode mng_init_app_cms (mng_datap pData,
+                              mng_bool  bGlobal,
+                              mng_bool  bObject,
+                              mng_bool  bRetrobj)
+{
+  mng_imagep     pImage = MNG_NULL;
+  mng_imagedatap pBuf   = MNG_NULL;
+  mng_bool       bDone  = MNG_FALSE;
+  mng_retcode    iRetcode;
+  
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_APP_CMS, MNG_LC_START);
+#endif
+
+  if (bObject)                         /* use object if present ? */
+  {                                    /* current object ? */
+    if ((mng_imagep)pData->pCurrentobj)
+      pImage = (mng_imagep)pData->pCurrentobj;
+    else                               /* if not; use object 0 */
+      pImage = (mng_imagep)pData->pObjzero;
+  }
+
+  if (bRetrobj)                        /* retrieving from an object ? */
+    pImage = (mng_imagep)pData->pRetrieveobj;
+
+  if (pImage)                          /* are we using an object ? */
+    pBuf = pImage->pImgbuf;            /* then address the buffer */
+
+  if ((!pBuf) || (!pBuf->bCorrected))  /* is the buffer already corrected ? */
+  {
+#ifndef MNG_SKIPCHUNK_iCCP
+    if ( (pData->fProcessiccp) &&
+         (((pBuf) && (pBuf->bHasICCP)) || ((bGlobal) && (pData->bHasglobalICCP))) )
+    {
+      mng_uint32 iProfilesize;
+      mng_ptr    pProfile;
+
+      if ((pBuf) && (pBuf->bHasICCP))  /* get the right profile */
+      {
+        iProfilesize = pBuf->iProfilesize;
+        pProfile     = pBuf->pProfile;
+      }
+      else
+      {
+        iProfilesize = pData->iGlobalProfilesize;
+        pProfile     = pData->pGlobalProfile;
+      }
+                                       /* inform the app */
+      if (!pData->fProcessiccp ((mng_handle)pData, iProfilesize, pProfile))
+        MNG_ERROR (pData, MNG_APPCMSERROR);
+                                       /* load color-correction routine */
+      pData->fCorrectrow = (mng_fptr)mng_correct_app_cms;
+      bDone              = MNG_TRUE;
+    }
+#endif
+
+    if ( (pData->fProcesssrgb) &&
+         (((pBuf) && (pBuf->bHasSRGB)) || ((bGlobal) && (pData->bHasglobalSRGB))) )
+    {
+      mng_uint8 iIntent;
+
+      if ((pBuf) && (pBuf->bHasSRGB))  /* determine rendering intent */
+        iIntent = pBuf->iRenderingintent;
+      else
+        iIntent = pData->iGlobalRendintent;
+                                       /* inform the app */
+      if (!pData->fProcesssrgb ((mng_handle)pData, iIntent))
+        MNG_ERROR (pData, MNG_APPCMSERROR);
+                                       /* load color-correction routine */
+      pData->fCorrectrow = (mng_fptr)mng_correct_app_cms;
+      bDone              = MNG_TRUE;
+    }
+
+#ifndef MNG_SKIPCHUNK_cHRM
+    if ( (pData->fProcesschroma) &&
+         (((pBuf) && (pBuf->bHasCHRM)) || ((bGlobal) && (pData->bHasglobalCHRM))) )
+    {
+      mng_uint32 iWhitepointx,   iWhitepointy;
+      mng_uint32 iPrimaryredx,   iPrimaryredy;
+      mng_uint32 iPrimarygreenx, iPrimarygreeny;
+      mng_uint32 iPrimarybluex,  iPrimarybluey;
+
+      if ((pBuf) && (pBuf->bHasCHRM))  /* local cHRM ? */
+      {
+        iWhitepointx   = pBuf->iWhitepointx;
+        iWhitepointy   = pBuf->iWhitepointy;
+        iPrimaryredx   = pBuf->iPrimaryredx;
+        iPrimaryredy   = pBuf->iPrimaryredy;
+        iPrimarygreenx = pBuf->iPrimarygreenx;
+        iPrimarygreeny = pBuf->iPrimarygreeny;
+        iPrimarybluex  = pBuf->iPrimarybluex;
+        iPrimarybluey  = pBuf->iPrimarybluey;
+      }
+      else
+      {
+        iWhitepointx   = pData->iGlobalWhitepointx;
+        iWhitepointy   = pData->iGlobalWhitepointy;
+        iPrimaryredx   = pData->iGlobalPrimaryredx;
+        iPrimaryredy   = pData->iGlobalPrimaryredy;
+        iPrimarygreenx = pData->iGlobalPrimarygreenx;
+        iPrimarygreeny = pData->iGlobalPrimarygreeny;
+        iPrimarybluex  = pData->iGlobalPrimarybluex;
+        iPrimarybluey  = pData->iGlobalPrimarybluey;
+      }
+                                       /* inform the app */
+      if (!pData->fProcesschroma ((mng_handle)pData, iWhitepointx,   iWhitepointy,
+                                                     iPrimaryredx,   iPrimaryredy,
+                                                     iPrimarygreenx, iPrimarygreeny,
+                                                     iPrimarybluex,  iPrimarybluey))
+        MNG_ERROR (pData, MNG_APPCMSERROR);
+                                       /* load color-correction routine */
+      pData->fCorrectrow = (mng_fptr)mng_correct_app_cms;
+      bDone              = MNG_TRUE;
+    }
+#endif
+
+    if ( (pData->fProcessgamma) &&
+         (((pBuf) && (pBuf->bHasGAMA)) || ((bGlobal) && (pData->bHasglobalGAMA))) )
+    {
+      mng_uint32 iGamma;
+
+      if ((pBuf) && (pBuf->bHasGAMA))  /* get the gamma value */
+        iGamma = pBuf->iGamma;
+      else
+        iGamma = pData->iGlobalGamma;
+                                       /* inform the app */
+      if (!pData->fProcessgamma ((mng_handle)pData, iGamma))
+      {                                /* app wants us to use internal routines ! */
+        iRetcode = mng_init_gamma_only (pData, bGlobal, bObject, bRetrobj);
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+      }
+      else
+      {                                /* load color-correction routine */
+        pData->fCorrectrow = (mng_fptr)mng_correct_app_cms;
+      }
+
+      bDone = MNG_TRUE;
+    }
+
+    if (!bDone)                        /* no color-info at all ? */
+    {
+                                       /* then use default image gamma ! */
+      if (!pData->fProcessgamma ((mng_handle)pData,
+                                 (mng_uint32)((pData->dDfltimggamma * 100000) + 0.5)))
+      {                                /* app wants us to use internal routines ! */
+        iRetcode = mng_init_gamma_only (pData, bGlobal, bObject, bRetrobj);
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+      }
+      else
+      {                                /* load color-correction routine */
+        pData->fCorrectrow = (mng_fptr)mng_correct_app_cms;
+      }  
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_APP_CMS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#ifdef MNG_APP_CMS
+mng_retcode mng_correct_app_cms (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CORRECT_APP_CMS, MNG_LC_START);
+#endif
+
+  if (pData->fProcessarow)             /* let the app do something with our row */
+    if (!pData->fProcessarow ((mng_handle)pData, pData->iRowsamples,
+                              pData->bIsRGBA16, pData->pRGBArow))
+      MNG_ERROR (pData, MNG_APPCMSERROR);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CORRECT_APP_CMS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_DISPLAY_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
+
+
diff --git a/files/Source/LibMNG/libmng_cms.h b/files/Source/LibMNG/libmng_cms.h
new file mode 100644
index 0000000..4459f80
--- /dev/null
+++ b/files/Source/LibMNG/libmng_cms.h
@@ -0,0 +1,92 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_cms.h              copyright (c) 2000-2003 G.Juyn   * */
+/* * version   : 1.0.6                                                      * */
+/* *                                                                        * */
+/* * purpose   : color management routines (definition)                     * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of color management routines                    * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added creatememprofile                                   * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             1.0.1 - 04/25/2001 - G.Juyn                                * */
+/* *             - moved mng_clear_cms to libmng_cms                        * */
+/* *             1.0.1 - 05/02/2001 - G.Juyn                                * */
+/* *             - added "default" sRGB generation (Thanks Marti!)          * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/19/2002 - G.Juyn                                * */
+/* *             - optimized color-correction routines                      * */
+/* *                                                                        * */
+/* *             1.0.6 - 04/11/2003 - G.Juyn                                * */
+/* *             - B719420 - fixed several MNG_APP_CMS problems             * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_cms_h_
+#define _libmng_cms_h_
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_LCMS
+void        mnglcms_initlibrary       (void);
+mng_cmsprof mnglcms_createfileprofile (mng_pchar    zFilename);
+mng_cmsprof mnglcms_creatememprofile  (mng_uint32   iProfilesize,
+                                       mng_ptr      pProfile );
+mng_cmsprof mnglcms_createsrgbprofile (void);
+void        mnglcms_freeprofile       (mng_cmsprof  hProf    );
+void        mnglcms_freetransform     (mng_cmstrans hTrans   );
+
+mng_retcode mng_clear_cms             (mng_datap    pData    );
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_FULL_CMS
+mng_retcode mng_init_full_cms          (mng_datap pData,
+                                        mng_bool  bGlobal,
+                                        mng_bool  bObject,
+                                        mng_bool  bRetrobj);
+mng_retcode mng_correct_full_cms       (mng_datap pData);
+#endif
+
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+mng_retcode mng_init_gamma_only        (mng_datap pData,
+                                        mng_bool  bGlobal,
+                                        mng_bool  bObject,
+                                        mng_bool  bRetrobj);
+mng_retcode mng_correct_gamma_only     (mng_datap pData);
+#endif
+
+#ifdef MNG_APP_CMS
+mng_retcode mng_init_app_cms           (mng_datap pData,
+                                        mng_bool  bGlobal,
+                                        mng_bool  bObject,
+                                        mng_bool  bRetrobj);
+mng_retcode mng_correct_app_cms        (mng_datap pData);
+#endif
+
+/* ************************************************************************** */
+
+#endif /* _libmng_cms_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_conf.h b/files/Source/LibMNG/libmng_conf.h
new file mode 100644
index 0000000..8e0925e
--- /dev/null
+++ b/files/Source/LibMNG/libmng_conf.h
@@ -0,0 +1,306 @@
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_conf.h             copyright (c) G.Juyn 2000-2004   * */
+/* * version   : 1.0.9                                                      * */
+/* *                                                                        * */
+/* * purpose   : main configuration file                                    * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : The configuration file. Change this to include/exclude     * */
+/* *             the options you want or do not want in libmng.             * */
+/* *                                                                        * */
+/* * changes   : 0.5.2 - 06/02/2000 - G.Juyn                                * */
+/* *             - separated configuration-options into this file           * */
+/* *             - changed to most likely configuration (?)                 * */
+/* *             0.5.2 - 06/03/2000 - G.Juyn                                * */
+/* *             - changed options to create a standard so-library          * */
+/* *               with everything enabled                                  * */
+/* *             0.5.2 - 06/04/2000 - G.Juyn                                * */
+/* *             - changed options to create a standard win32-dll           * */
+/* *               with everything enabled                                  * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/12/2000 - G.Juyn                                * */
+/* *             - added workaround for faulty PhotoShop iCCP chunk         * */
+/* *             0.9.3 - 09/16/2000 - G.Juyn                                * */
+/* *             - removed trace-options from default SO/DLL builds         * */
+/* *                                                                        * */
+/* *             1.0.4 - 06/22/2002 - G.Juyn                                * */
+/* *             - B526138 - returned IJGSRC6B calling convention to        * */
+/* *               default for MSVC                                         * */
+/* *                                                                        * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             - added 'supports' call to check function availability     * */
+/* *                                                                        * */
+/* *             1.0.6 - 06/22/2002 - G.R-P                                 * */
+/* *             - added MNG_NO_INCLUDE_JNG conditional                     * */
+/* *             - added MNG_SKIPCHUNK_evNT conditional                     * */
+/* *             1.0.6 - 07/14/2002 - G.R-P                                 * */
+/* *             - added MNG_NO_SUPPORT_FUNCQUERY conditional               * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/07/2004 - G.R-P                                 * */
+/* *             - added MNG_VERSION_QUERY_SUPPORT_ conditional             * */
+/* *                                                                        * */
+/* *             1.0.9 - 05/12/2004 - G.Juyn                                * */
+/* *             - clearified MNG_BIGENDIAN_SUPPORTED conditional           * */
+/* *             - added MNG_LITTLEENDIAN_SUPPORTED conditional             * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_conf_h_
+#define _libmng_conf_h_
+
+#ifdef MNG_MOZILLA_CFG
+#include "special\mozcfg\mozlibmngconf.h"
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  User-selectable compile-time options                                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* ************************************************************************** */
+/* added by FreeImage */
+/* ************************************************************************** */
+
+#define MNG_OPTIMIZE_CHUNKINITFREE
+#define MNG_OPTIMIZE_OBJCLEANUP
+#define MNG_OPTIMIZE_CHUNKASSIGN
+#define MNG_OPTIMIZE_CHUNKREADER
+
+/* ************************************************************************** */
+
+/* enable exactly one(1) of the MNG-(sub)set selectors */
+/* use this to select which (sub)set of the MNG specification you wish
+   to support */
+/* generally you'll want full support as the library provides it automatically
+   for you! if you're really strung on memory-requirements you can opt
+   to enable less support (but it's just NOT a good idea!) */
+/* NOTE that this isn't actually implemented yet */
+
+#if !defined(MNG_SUPPORT_FULL) && !defined(MNG_SUPPORT_LC) && !defined(MNG_SUPPORT_VLC)
+#define MNG_SUPPORT_FULL
+/* #define MNG_SUPPORT_LC */
+/* #define MNG_SUPPORT_VLC */
+#endif
+
+/* ************************************************************************** */
+
+/* enable JPEG support if required */
+/* use this to enable the JNG support routines */
+/* this requires an external jpeg package;
+   currently only IJG's jpgsrc6b is supported! */
+/* NOTE that the IJG code can be either 8- or 12-bit (eg. not both);
+   so choose the one you've defined in jconfig.h; if you don't know what
+   the heck I'm talking about, just leave it at 8-bit support (thank you!) */
+
+#ifndef MNG_NO_INCLUDE_JNG
+#ifdef MNG_SUPPORT_FULL                /* full support includes JNG */
+#define MNG_SUPPORT_IJG6B
+#endif
+
+#ifndef MNG_SUPPORT_IJG6B
+#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL)
+#define MNG_SUPPORT_IJG6B
+#endif
+#endif
+
+#if defined(MNG_SUPPORT_IJG6B) && !defined(MNG_SUPPORT_JPEG8) && !defined(MNG_SUPPORT_JPEG12)
+#define MNG_SUPPORT_JPEG8
+/* #define MNG_SUPPORT_JPEG12 */
+#endif
+
+/* The following is required to export the IJG routines from the DLL in
+   the Windows-standard calling convention;
+   currently this only works for Borland C++ !!! */
+
+#if defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL)
+#if defined(MNG_SUPPORT_IJG6B) && defined(__BORLANDC__)
+#define MNG_DEFINE_JPEG_STDCALL
+#endif
+#endif
+#endif
+
+/* ************************************************************************** */
+
+/* enable required high-level functions */
+/* use this to select the high-level functions you require */
+/* if you only need to display a MNG, disable write support! */
+/* if you only need to examine a MNG, disable write & display support! */
+/* if you only need to copy a MNG, disable display support! */
+/* if you only need to create a MNG, disable read & display support! */
+/* NOTE that turning all options off will be very unuseful! */
+
+#if !defined(MNG_SUPPORT_READ) && !defined(MNG_SUPPORT_WRITE) && !defined(MNG_SUPPORT_DISPLAY)
+#define MNG_SUPPORT_READ
+#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL)
+#define MNG_SUPPORT_WRITE
+#endif
+#define MNG_SUPPORT_DISPLAY
+#endif
+
+/* ************************************************************************** */
+
+/* enable chunk access functions */
+/* use this to select whether you need access to the individual chunks */
+/* useful if you want to examine a read MNG (you'll also need MNG_STORE_CHUNKS !)*/
+/* required if you need to create & write a new MNG! */
+
+#ifndef MNG_ACCESS_CHUNKS
+#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL)
+#define MNG_ACCESS_CHUNKS
+#endif
+#endif
+
+/* ************************************************************************** */
+
+/* enable exactly one(1) of the color-management functionality selectors */
+/* use this to select the level of automatic color support */
+/* MNG_FULL_CMS requires the lcms (little cms) external package ! */
+/* if you want your own app (or the OS) to handle color-management
+   select MNG_APP_CMS */
+
+#define MNG_GAMMA_ONLY
+/* #define MNG_FULL_CMS */
+/* #define MNG_APP_CMS */
+
+/* ************************************************************************** */
+
+/* enable automatic dithering */
+/* use this if you need dithering support to convert high-resolution
+   images to a low-resolution output-device */
+/* NOTE that this is not supported yet */
+
+/* #define MNG_AUTO_DITHER */
+
+/* ************************************************************************** */
+
+/* enable whether chunks should be stored for reference later */
+/* use this if you need to examine the chunks of a MNG you have read,
+   or (re-)write a MNG you have read */
+/* turn this off if you want to reduce memory-consumption */
+
+#ifndef MNG_STORE_CHUNKS
+#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL)
+#define MNG_STORE_CHUNKS
+#endif
+#endif
+
+/* ************************************************************************** */
+
+/* enable internal memory management (if your compiler supports it) */
+/* use this if your compiler supports the 'standard' memory functions
+   (calloc & free), and you want the library to use these functions and not
+   bother your app with memory-callbacks */
+
+/* #define MNG_INTERNAL_MEMMNGMT */
+
+/* ************************************************************************** */
+
+/* enable internal tracing-functionality (manual debugging purposes) */
+/* use this if you have trouble location bugs or problems */
+/* NOTE that you'll need to specify the trace callback function! */
+
+/* #define MNG_SUPPORT_TRACE */
+
+/* ************************************************************************** */
+
+/* enable extended error- and trace-telltaling */
+/* use this if you need explanatory messages with errors and/or tracing */
+
+#if !defined(MNG_ERROR_TELLTALE) && !defined(MNG_TRACE_TELLTALE)
+#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL)
+#define MNG_ERROR_TELLTALE
+#define MNG_TRACE_TELLTALE
+#endif
+#endif
+
+/* ************************************************************************** */
+
+/* enable BIG/LITTLE endian optimizations */
+/* enable BIG if you're on an architecture that supports big-endian reads
+   and writes that aren't word-aligned */
+/* according to reliable sources this only works for PowerPC (bigendian mode)
+   and 680x0 */
+/* enable LITTLE if you're on an architecture that supports little-endian */
+/* when in doubt leave both off !!! */
+
+/* #define MNG_BIGENDIAN_SUPPORTED */
+/* #define MNG_LITTLEENDIAN_SUPPORTED */
+
+/* ************************************************************************** */
+/* enable 'version' functions */
+#if !defined(MNG_VERSION_QUERY_SUPPORT) && \
+    !defined(MNG_NO_VERSION_QUERY_SUPPORT)
+#define MNG_VERSION_QUERY_SUPPORT
+#endif
+
+/* enable 'supports' function */
+/* use this if you need to query the availability of functions at runtime;
+   useful for apps that dynamically load the library and that need specific
+   functions */
+
+#if !defined(MNG_NO_SUPPORT_FUNCQUERY) && !defined(MNG_SUPPORT_FUNCQUERY)
+#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || \
+    defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL)
+#define MNG_SUPPORT_FUNCQUERY
+#endif
+#endif
+
+/* ************************************************************************** */
+
+/* enable dynamic MNG features */
+/* use this if you would like to have dynamic support for specifically
+   designed MNGs; eg. this is useful for 'rollover' effects such as common
+   on the world wide web */
+
+#ifndef MNG_SUPPORT_DYNAMICMNG
+#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL)
+#define MNG_SUPPORT_DYNAMICMNG
+#endif
+#endif
+#ifndef MNG_SUPPORT_DYNAMICMNG
+#ifndef MNG_SKIPCHUNK_evNT
+#define MNG_SKIPCHUNK_evNT
+#endif
+#endif
+
+#ifdef MNG_INCLUDE_JNG
+#ifndef MNG_NO_ACCESS_JPEG
+#ifndef MNG_ACCESS_JPEG
+#define MNG_ACCESS_JPEG
+#endif
+#endif
+#endif
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifndef MNG_NO_ACCESS_ZLIB
+#ifndef MNG_ACCESS_ZLIB
+#define MNG_ACCESS_ZLIB
+#endif
+#endif
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  End of user-selectable compile-time options                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#endif /* _libmng_conf_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_data.h b/files/Source/LibMNG/libmng_data.h
new file mode 100644
index 0000000..430dca9
--- /dev/null
+++ b/files/Source/LibMNG/libmng_data.h
@@ -0,0 +1,1029 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_data.h             copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : main data structure definition                             * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the library main data structure              * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/04/2000 - G.Juyn                                * */
+/* *             - added CRC table to main structure (for thread-safety)    * */
+/* *             0.5.1 - 05/06/2000 - G.Juyn                                * */
+/* *             - added iPLTEentries for checking hIST-length              * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed palette definition to exported palette-type      * */
+/* *             - removed frozen indicator                                 * */
+/* *             - added create/write indicators                            * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/13/2000 - G.Juyn                                * */
+/* *             - added eMNGma hack (will be removed in 1.0.0 !!!)         * */
+/* *             - added TERM animation object pointer (easier reference)   * */
+/* *             - added saved-data structure for SAVE/SEEK processing      * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/18/2000 - G.Juyn                                * */
+/* *             - added fields for JNG support (IJG-based)                 * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - changed global tRNS definition                           * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added delta-image fields                                 * */
+/* *             0.5.2 - 06/01/2000 - G.Juyn                                * */
+/* *             - added internal delta-image processing callbacks          * */
+/* *             0.5.2 - 06/02/2000 - G.Juyn                                * */
+/* *             - changed SWAP_ENDIAN to BIGENDIAN_SUPPORTED               * */
+/* *               (contributed by Tim Rowley)                              * */
+/* *             - added getalphaline callback for RGB8_A8 canvasstyle      * */
+/* *             0.5.2 - 06/06/2000 - G.Juyn                                * */
+/* *             - added parameter for delayed buffer-processing            * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/16/2000 - G.Juyn                                * */
+/* *             - added update-region parms for refresh calback            * */
+/* *             - added Needrefresh parameter                              * */
+/* *             0.5.3 - 06/17/2000 - G.Juyn                                * */
+/* *             - added Deltaimmediate parm for faster delta-processing    * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added Speed parameter to facilitate testing              * */
+/* *             - added Imagelevel parameter for processtext callback      * */
+/* *             0.5.3 - 06/26/2000 - G.Juyn                                * */
+/* *             - changed userdata variable to mng_ptr                     * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/07/2000 - G.Juyn                                * */
+/* *             - added variables for go_xxxx processing                   * */
+/* *             0.9.1 - 07/08/2000 - G.Juyn                                * */
+/* *             - added variables for improved timing support              * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added callbacks for SAVE/SEEK processing                 * */
+/* *             - added variable for NEEDSECTIONWAIT breaks                * */
+/* *             - added variable for freeze & reset processing             * */
+/* *             0.9.1 - 07/17/2000 - G.Juyn                                * */
+/* *             - fixed suspension-buffering for 32K+ chunks               * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/29/2000 - G.Juyn                                * */
+/* *             - removed Nextbackxxx fields (no longer used)              * */
+/* *             0.9.2 - 07/31/2000 - G.Juyn                                * */
+/* *             - fixed wrapping of suspension parameters                  * */
+/* *             0.9.2 - 08/04/2000 - G.Juyn                                * */
+/* *             - B111096 - fixed large-buffer read-suspension             * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *             0.9.3 - 09/10/2000 - G.Juyn                                * */
+/* *             - fixed DEFI behavior                                      * */
+/* *             0.9.3 - 10/10/2000 - G.Juyn                                * */
+/* *             - added support for alpha-depth prediction                 * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - added support for nEED                                   * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added optional support for bKGD for PNG images           * */
+/* *             - added support for JDAA                                   * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added callback to process non-critical unknown chunks    * */
+/* *             - fixed support for bKGD                                   * */
+/* *             0.9.3 - 10/19/2000 - G.Juyn                                * */
+/* *             - implemented delayed delta-processing                     * */
+/* *             0.9.4 - 12/16/2000 - G.Juyn                                * */
+/* *             - fixed mixup of data- & function-pointers (thanks Dimitri)* */
+/* *                                                                        * */
+/* *             1.0.1 - 02/08/2001 - G.Juyn                                * */
+/* *             - added MEND processing callback                           * */
+/* *             1.0.1 - 02/13/2001 - G.Juyn                                * */
+/* *             - fixed first FRAM_MODE=4 timing problem                   * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added optimization option for MNG-video playback         * */
+/* *             - added processterm callback                               * */
+/* *             1.0.2 - 06/25/2001 - G.Juyn                                * */
+/* *             - added option to turn off progressive refresh             * */
+/* *                                                                        * */
+/* *             1.0.5 - 07/08/2002 - G.Juyn                                * */
+/* *             - B578572 - removed eMNGma hack (thanks Dimitri!)          * */
+/* *             1.0.5 - 07/16/2002 - G.Juyn                                * */
+/* *             - B581625 - large chunks fail with suspension reads        * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed PROM support                                   * */
+/* *             1.0.5 - 09/15/2002 - G.Juyn                                * */
+/* *             - fixed LOOP iteration=0 special case                      * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - finished support for BACK image & tiling                 * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - added another fix for misplaced TERM chunk               * */
+/* *             - completed support for condition=2 in TERM chunk          * */
+/* *             1.0.5 - 10/20/2002 - G.Juyn                                * */
+/* *             - fixed processing for multiple objects in MAGN            * */
+/* *             - fixed display of visible target of PAST operation        * */
+/* *             1.0.5 - 11/07/2002 - G.Juyn                                * */
+/* *             - added support to get totals after mng_read()             * */
+/* *             1.0.5 - 24/02/2003 - G.Juyn                                * */
+/* *             - B683152 - libjpeg suspension not always honored correctly* */
+/* *                                                                        * */
+/* *             1.0.6 - 04/11/2003 - G.Juyn                                * */
+/* *             - B719420 - fixed several MNG_APP_CMS problems             * */
+/* *             1.0.6 - 07/05/2003 - G. R-P                                * */
+/* *             - optionally use zlib's crc32() function                   * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added SKIPCHUNK conditionals around PAST chunk support   * */
+/* *             1.0.6 - 08/17/2003 - G.R-P                                 * */
+/* *             - added iPNGdepth member to pData structure                * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/10/2004 - G.R-P                                 * */
+/* *             - added conditionals around openstream/closestream         * */
+/* *             1.0.7 - 03/24/2004 - G.R-P                                 * */
+/* *             - added more SKIPCHUNK conditionals                        * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/02/2004 - G.Juyn                                * */
+/* *             - added CRC existence & checking flags                     * */
+/* *             1.0.8 - 04/10/2004 - G.Juyn                                * */
+/* *             - added data-push mechanisms for specialized decoders      * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/11/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_DISPLAYCALLS              * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_data_h_
+#define _libmng_data_h_
+
+/* ************************************************************************** */
+
+#define MNG_MAGIC 0x52530a0aL
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Internal structures                                                    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+typedef mng_palette8 mng_rgbpaltab;
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * The saved_data structure                                               * */
+/* *                                                                        * */
+/* * This contains the saved data after a SAVE chunk has been processed.    * */
+/* * The data is saved from the main data structure during SAVE processing, * */
+/* * and restored to the main data structure during SEEK processing.        * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+typedef struct mng_savedata_struct {
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+           mng_bool          bHasglobalPLTE;     /* global PLTE chunk processed */
+           mng_bool          bHasglobalTRNS;     /* global tRNS chunk processed */
+           mng_bool          bHasglobalGAMA;     /* global gAMA chunk processed */
+           mng_bool          bHasglobalCHRM;     /* global cHRM chunk processed */
+           mng_bool          bHasglobalSRGB;     /* global sRGB chunk processed */
+           mng_bool          bHasglobalICCP;     /* global iCCP chunk processed */
+           mng_bool          bHasglobalBKGD;     /* global bKGD chunk processed */
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+#ifdef MNG_SUPPORT_DISPLAY
+           mng_uint16        iBACKred;           /* BACK fields */
+           mng_uint16        iBACKgreen;
+           mng_uint16        iBACKblue;
+           mng_uint8         iBACKmandatory;
+           mng_uint16        iBACKimageid;
+           mng_uint8         iBACKtile;
+
+           mng_uint8         iFRAMmode;          /* FRAM fields (global) */
+           mng_uint32        iFRAMdelay;
+           mng_uint32        iFRAMtimeout;
+           mng_bool          bFRAMclipping;
+           mng_int32         iFRAMclipl;
+           mng_int32         iFRAMclipr;
+           mng_int32         iFRAMclipt;
+           mng_int32         iFRAMclipb;
+
+           mng_uint32        iGlobalPLTEcount;   /* global PLTE fields */
+           mng_rgbpaltab     aGlobalPLTEentries;
+
+           mng_uint32        iGlobalTRNSrawlen;  /* global tRNS fields */
+           mng_uint8arr      aGlobalTRNSrawdata;
+
+           mng_uint32        iGlobalGamma;       /* global gAMA fields */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+           mng_uint32        iGlobalWhitepointx; /* global cHRM fields */
+           mng_uint32        iGlobalWhitepointy;
+           mng_uint32        iGlobalPrimaryredx;
+           mng_uint32        iGlobalPrimaryredy;
+           mng_uint32        iGlobalPrimarygreenx;
+           mng_uint32        iGlobalPrimarygreeny;
+           mng_uint32        iGlobalPrimarybluex;
+           mng_uint32        iGlobalPrimarybluey;
+#endif
+
+           mng_uint8         iGlobalRendintent;  /* global sRGB fields */
+
+           mng_uint32        iGlobalProfilesize; /* global iCCP fields */
+           mng_ptr           pGlobalProfile;
+
+           mng_uint16        iGlobalBKGDred;     /* global bKGD fields */
+           mng_uint16        iGlobalBKGDgreen;
+           mng_uint16        iGlobalBKGDblue;
+#endif /* MNG_SUPPORT_DISPLAY */
+
+        } mng_savedata;
+
+typedef mng_savedata * mng_savedatap;
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Internal buffer structure for data push mechanisms                     * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+typedef struct {
+           mng_ptr           pNext;              /* for linked list */
+           mng_ptr           pData;              /* used for chunks & data */
+           mng_uint32        iLength;
+           mng_bool          bOwned;
+           mng_uint8p        pDatanext;          /* only used for data */
+           mng_uint32        iRemaining;
+        } mng_pushdata;
+typedef mng_pushdata * mng_pushdatap;
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * The main libmng data structure                                         * */
+/* *                                                                        * */
+/* * The handle used in all functions points to this structure which        * */
+/* * contains all volatile data necessary to process the network graphic.   * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+typedef struct mng_data_struct {
+
+           mng_uint32        iMagic;             /* magic number to validate
+                                                    a given handle */     
+           mng_ptr           pUserdata;          /* application workdata */
+
+           mng_imgtype       eSigtype;           /* image information */
+           mng_imgtype       eImagetype;         /* initially zeroed */
+           mng_uint32        iWidth;             /* filled after header is processed */
+           mng_uint32        iHeight;
+           mng_uint32        iTicks;             /* these only after MHDR */
+           mng_uint32        iLayercount;
+           mng_uint32        iFramecount;
+           mng_uint32        iPlaytime;
+           mng_uint32        iSimplicity;
+           mng_uint8         iAlphadepth;        /* indicates expected alpha-depth */
+
+           mng_uint32        iImagelevel;        /* level of image inside a stream */
+
+           mng_uint32        iCanvasstyle;       /* layout of the drawing-canvas */
+           mng_uint32        iBkgdstyle;         /* layout of the background-canvas */
+
+           mng_int8          iMagnify;           /* magnification factor (not used yet) */
+           mng_uint32        iOffsetx;           /* x-offset for extremely large image */
+           mng_uint32        iOffsety;           /* y-offset for extremely large image */
+           mng_uint32        iCanvaswidth;       /* real canvas size */
+           mng_uint32        iCanvasheight;      /* must be set by processheader callback */
+
+           mng_uint16        iBGred;             /* default background color */
+           mng_uint16        iBGgreen;           /* initially "black" */
+           mng_uint16        iBGblue;
+           mng_bool          bUseBKGD;           /* preferred use of bKGD for PNG */
+
+           mng_bool          bIssRGB;            /* indicates sRGB system */
+
+#ifdef MNG_FULL_CMS                              /* little CMS variables */
+           mng_cmsprof       hProf1;             /* image input profile */
+           mng_cmsprof       hProf2;             /* default output profile */
+           mng_cmsprof       hProf3;             /* default sRGB profile */
+           mng_cmstrans      hTrans;             /* current transformation handle */
+#endif
+
+           mng_float         dViewgamma;         /* gamma calculation variables */
+           mng_float         dDisplaygamma;      /* initially set for sRGB conditions */
+           mng_float         dDfltimggamma;
+
+           mng_bool          bStorechunks;       /* switch for storing chunkdata */
+           mng_bool          bSectionbreaks;     /* indicate NEEDSECTIONWAIT breaks */
+           mng_bool          bCacheplayback;     /* switch to cache playback info */
+           mng_bool          bDoProgressive;     /* progressive refresh for large images */
+           mng_uint32        iCrcmode;           /* CRC existence & checking flags */
+
+           mng_speedtype     iSpeed;             /* speed-modifier for animations */
+
+           mng_uint32        iMaxwidth;          /* maximum canvas size */
+           mng_uint32        iMaxheight;         /* initially set to 1024 x 1024 */
+
+           mng_int32         iErrorcode;         /* error reporting fields */
+           mng_int8          iSeverity;
+           mng_int32         iErrorx1;
+           mng_int32         iErrorx2;
+           mng_pchar         zErrortext;
+
+           mng_memalloc      fMemalloc;          /* callback pointers */
+           mng_memfree       fMemfree;           /* initially nulled */
+           mng_releasedata   fReleasedata;
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+           mng_openstream    fOpenstream;
+           mng_closestream   fClosestream;
+#endif
+           mng_readdata      fReaddata;
+           mng_writedata     fWritedata;
+           mng_errorproc     fErrorproc;
+           mng_traceproc     fTraceproc;
+           mng_processheader fProcessheader;
+           mng_processtext   fProcesstext;
+           mng_processsave   fProcesssave;
+           mng_processseek   fProcessseek;
+           mng_processneed   fProcessneed;
+           mng_processmend   fProcessmend;
+           mng_processunknown fProcessunknown;
+           mng_processterm   fProcessterm;
+           mng_getcanvasline fGetcanvasline;
+           mng_getbkgdline   fGetbkgdline;
+           mng_getalphaline  fGetalphaline;
+           mng_refresh       fRefresh;
+           mng_gettickcount  fGettickcount;
+           mng_settimer      fSettimer;
+           mng_processgamma  fProcessgamma;
+           mng_processchroma fProcesschroma;
+           mng_processsrgb   fProcesssrgb;
+           mng_processiccp   fProcessiccp;
+           mng_processarow   fProcessarow;
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+#ifndef MNG_NO_OLD_VERSIONS
+           mng_bool          bPreDraft48;        /* flags ancient style draft */
+#endif
+
+           mng_chunkid       iChunkname;         /* read/write-state variables */
+           mng_uint32        iChunkseq;
+           mng_chunkp        pFirstchunk;        /* double-linked list of */
+           mng_chunkp        pLastchunk;         /* stored chunk-structures */
+
+           mng_bool          bHasheader;         /* first header chunk processed */
+           mng_bool          bHasMHDR;           /* inside a MHDR-MEND sequence */
+           mng_bool          bHasIHDR;           /* inside a IHDR-IEND sequence */
+           mng_bool          bHasBASI;           /* inside a BASI-IEND sequence */
+           mng_bool          bHasDHDR;           /* inside a DHDR-IEND sequence */
+#ifdef MNG_INCLUDE_JNG
+           mng_bool          bHasJHDR;           /* inside a JHDR-IEND sequence */
+           mng_bool          bHasJSEP;           /* passed the JSEP separator */
+           mng_bool          bHasJDAA;           /* at least 1 JDAA processed */
+           mng_bool          bHasJDAT;           /* at least 1 JDAT processed */
+#endif
+           mng_bool          bHasPLTE;           /* PLTE chunk processed */
+           mng_bool          bHasTRNS;           /* tRNS chunk processed */
+           mng_bool          bHasGAMA;           /* gAMA chunk processed */
+           mng_bool          bHasCHRM;           /* cHRM chunk processed */
+           mng_bool          bHasSRGB;           /* sRGB chunk processed */
+           mng_bool          bHasICCP;           /* iCCP chunk processed */
+           mng_bool          bHasBKGD;           /* bKGD chunk processed */
+           mng_bool          bHasIDAT;           /* at least 1 IDAT processed */
+           
+           mng_bool          bHasSAVE;           /* SAVE chunk processed */
+           mng_bool          bHasBACK;           /* BACK chunk processed */
+           mng_bool          bHasFRAM;           /* FRAM chunk processed */
+           mng_bool          bHasTERM;           /* TERM chunk processed */
+           mng_bool          bHasLOOP;           /* at least 1 LOOP open */
+
+           mng_bool          bHasglobalPLTE;     /* global PLTE chunk processed */
+           mng_bool          bHasglobalTRNS;     /* global tRNS chunk processed */
+           mng_bool          bHasglobalGAMA;     /* global gAMA chunk processed */
+           mng_bool          bHasglobalCHRM;     /* global cHRM chunk processed */
+           mng_bool          bHasglobalSRGB;     /* global sRGB chunk processed */
+           mng_bool          bHasglobalICCP;     /* global iCCP chunk processed */
+           mng_bool          bHasglobalBKGD;     /* global bKGD chunk processed */
+
+           mng_uint32        iDatawidth;         /* IHDR/BASI/DHDR fields */
+           mng_uint32        iDataheight;        /* valid if inside IHDR-IEND, */
+           mng_uint8         iBitdepth;          /* BASI-IEND or DHDR-IEND */
+           mng_uint8         iColortype;
+           mng_uint8         iCompression;
+           mng_uint8         iFilter;
+           mng_uint8         iInterlace;
+
+           mng_uint32        iPLTEcount;         /* PLTE fields */
+
+#ifdef MNG_INCLUDE_JNG
+           mng_uint8         iJHDRcolortype;     /* JHDR fields */
+           mng_uint8         iJHDRimgbitdepth;   /* valid if inside JHDR-IEND */
+           mng_uint8         iJHDRimgcompression;
+           mng_uint8         iJHDRimginterlace;
+           mng_uint8         iJHDRalphabitdepth;
+           mng_uint8         iJHDRalphacompression;
+           mng_uint8         iJHDRalphafilter;
+           mng_uint8         iJHDRalphainterlace;
+#endif
+
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+#ifdef MNG_SUPPORT_READ
+           mng_bool          bReading;           /* read processing variables */
+           mng_bool          bHavesig;
+           mng_bool          bEOF;
+           mng_uint32        iReadbufsize;
+           mng_uint8p        pReadbuf;
+
+           mng_uint32        iLargebufsize;      /* temp for very large chunks */
+           mng_uint8p        pLargebuf;
+
+           mng_uint32        iSuspendtime;       /* tickcount at last suspension */
+           mng_bool          bSuspended;         /* input-reading has been suspended;
+                                                    we're expecting a call to
+                                                    mng_read_resume! */
+           mng_uint8         iSuspendpoint;      /* indicates at which point the flow
+                                                    was broken to suspend input-reading */
+                                                    
+           mng_bool          bSuspensionmode;    /* I/O-suspension variables */
+           mng_uint32        iSuspendbufsize;
+           mng_uint8p        pSuspendbuf;
+           mng_uint8p        pSuspendbufnext;
+           mng_uint32        iSuspendbufleft;
+           mng_uint32        iChunklen;          /* chunk length */
+           mng_uint8p        pReadbufnext;       /* 32K+ suspension-processing */
+           mng_uint8p        pLargebufnext;
+
+           mng_pushdatap     pFirstpushchunk;    /* variables for push mechanisms */
+           mng_pushdatap     pLastpushchunk;
+           mng_pushdatap     pFirstpushdata;
+           mng_pushdatap     pLastpushdata;
+#endif /* MNG_SUPPORT_READ */
+
+#ifdef MNG_SUPPORT_WRITE
+           mng_bool          bCreating;          /* create/write processing variables */
+           mng_bool          bWriting;
+           mng_chunkid       iFirstchunkadded;
+           mng_uint32        iWritebufsize;
+           mng_uint8p        pWritebuf;
+#endif
+
+#ifdef MNG_SUPPORT_DISPLAY
+           mng_bool          bDisplaying;        /* display-state variables */
+           mng_bool          bFramedone;
+           mng_uint32        iFrameseq;
+           mng_uint32        iLayerseq;
+           mng_uint32        iFrametime;         /* millisecs */
+
+           mng_uint32        iTotalframes;       /* end-totals after mng_read() */
+           mng_uint32        iTotallayers;
+           mng_uint32        iTotalplaytime;     /* millisecs */
+
+           mng_bool          bSkipping;          /* LOOP iteration=0 */
+           
+#ifdef MNG_SUPPORT_DYNAMICMNG
+           mng_bool          bDynamic;           /* MNG is dynamic (eg. has events) */
+           mng_bool          bRunningevent;      /* currently processing an event */
+           mng_bool          bStopafterseek;     /* stop after next SEEK */
+           mng_int32         iEventx;            /* X/Y of current event */
+           mng_int32         iEventy;
+           mng_objectp       pLastmousemove;     /* last event triggered */
+#endif
+
+           mng_uint32        iRequestframe;      /* go_xxxx variables */
+           mng_uint32        iRequestlayer;
+           mng_uint32        iRequesttime;
+           mng_bool          bSearching;
+
+           mng_bool          bRestorebkgd;       /* flags restore required before IDAT/JDAT */
+
+           mng_uint32        iRuntime;           /* millisecs since start */
+           mng_uint32        iSynctime;          /* tickcount at last framesync */
+           mng_uint32        iStarttime;         /* tickcount at start */
+           mng_uint32        iEndtime;           /* tickcount at end */
+           mng_bool          bRunning;           /* animation is active */
+           mng_bool          bTimerset;          /* the timer has been set;
+                                                    we're expecting a call to
+                                                    mng_display_resume! */
+           mng_uint8         iBreakpoint;        /* indicates at which point the
+                                                    flow was broken to run the timer */
+           mng_bool          bSectionwait;       /* indicates a section break */
+           mng_bool          bFreezing;          /* indicates app requested a freeze */   
+           mng_bool          bResetting;         /* indicates app requested a reset */   
+           mng_bool          bNeedrefresh;       /* indicates screen-refresh is needed */
+           mng_bool          bMisplacedTERM;     /* indicates TERM is out of place */
+           mng_bool          bOnlyfirstframe;    /* show first frame after TERM and stop */
+           mng_uint32        iFramesafterTERM;   /* determines frame-count after TERM */          
+           mng_objectp       pCurrentobj;        /* current "object" */
+           mng_objectp       pCurraniobj;        /* current animation object
+                                                    "to be"/"being" processed */
+           mng_objectp       pTermaniobj;        /* TERM animation object */
+           mng_uint32        iIterations;        /* TERM/MEND iteration count */
+           mng_objectp       pObjzero;           /* "on-the-fly" image (object = 0) */
+           mng_objectp       pLastclone;         /* last clone */
+           mng_objectp       pStoreobj;          /* current store object for row routines */
+           mng_objectp       pStorebuf;          /* current store object-buffer for row routines */
+           mng_objectp       pRetrieveobj;       /* current retrieve object for row routines */
+           mng_savedatap     pSavedata;          /* pointer to saved data (after SAVE) */
+
+           mng_uint32        iUpdateleft;        /* update region for refresh */
+           mng_uint32        iUpdateright;
+           mng_uint32        iUpdatetop;
+           mng_uint32        iUpdatebottom;
+
+           mng_int8          iPass;              /* current interlacing pass;
+                                                    negative value means no interlace */
+           mng_int32         iRow;               /* current row counter */
+           mng_int32         iRowinc;            /* row increment for this pass */
+           mng_int32         iCol;               /* current starting column */
+           mng_int32         iColinc;            /* column increment for this pass */
+           mng_int32         iRowsamples;        /* nr. of samples in current workrow */
+           mng_int32         iSamplemul;         /* needed to calculate rowsize */
+           mng_int32         iSampleofs;            /* from rowsamples */
+           mng_int32         iSamplediv;
+           mng_int32         iRowsize;           /* size of actual data in work row */
+           mng_int32         iRowmax;            /* maximum size of data in work row */
+           mng_int32         iFilterofs;         /* offset to filter-byte in work row */
+           mng_int32         iPixelofs;          /* offset to pixel-bytes in work row */
+           mng_uint32        iLevel0;            /* leveling variables */
+           mng_uint32        iLevel1;
+           mng_uint32        iLevel2;
+           mng_uint32        iLevel3;
+           mng_uint8p        pWorkrow;           /* working row of pixel-data */
+           mng_uint8p        pPrevrow;           /* previous row of pixel-data */
+           mng_uint8p        pRGBArow;           /* intermediate row of RGBA8 or RGBA16 data */
+           mng_bool          bIsRGBA16;          /* indicates intermediate row is RGBA16 */
+           mng_bool          bIsOpaque;          /* indicates intermediate row is fully opaque */
+           mng_int32         iFilterbpp;         /* bpp index for filtering routines */
+
+           mng_int32         iSourcel;           /* variables for showing objects */
+           mng_int32         iSourcer;
+           mng_int32         iSourcet;
+           mng_int32         iSourceb;
+           mng_int32         iDestl;
+           mng_int32         iDestr;
+           mng_int32         iDestt;
+           mng_int32         iDestb;
+
+           mng_objectp       pFirstimgobj;       /* double-linked list of */
+           mng_objectp       pLastimgobj;        /* image-object structures */
+           mng_objectp       pFirstaniobj;       /* double-linked list of */
+           mng_objectp       pLastaniobj;        /* animation-object structures */
+#ifdef MNG_SUPPORT_DYNAMICMNG
+           mng_objectp       pFirstevent;        /* double-linked list of */
+           mng_objectp       pLastevent;         /* event-object structures */
+#endif
+
+#if defined(MNG_GAMMA_ONLY) || defined(MNG_FULL_CMS) || defined(MNG_APP_CMS)
+           mng_uint8         aGammatab[256];     /* precomputed gamma lookup table */
+           mng_float         dLastgamma;         /* last gamma used to compute table */
+#endif
+
+           mng_fptr          fDisplayrow;        /* internal callback to display an
+                                                    uncompressed/unfiltered/
+                                                    color-corrected row */
+           mng_fptr          fRestbkgdrow;       /* internal callback for restore-
+                                                    background processing of a row */
+           mng_fptr          fCorrectrow;        /* internal callback to color-correct an
+                                                    uncompressed/unfiltered row */
+           mng_fptr          fRetrieverow;       /* internal callback to retrieve an
+                                                    uncompressed/unfiltered row of data */
+           mng_fptr          fStorerow;          /* internal callback to store an
+                                                    uncompressed/unfiltered row of data */
+           mng_fptr          fProcessrow;        /* internal callback to process an
+                                                    uncompressed row of data */
+           mng_fptr          fDifferrow;         /* internal callback to perform
+                                                    added filter leveling and
+                                                    differing on an unfiltered row */
+           mng_fptr          fScalerow;          /* internal callback to scale a
+                                                    delta-row to the bitdepth of its target */
+           mng_fptr          fDeltarow;          /* internal callback to execute a
+                                                    delta-row onto a target */
+#ifndef MNG_SKIPCHUNK_PAST
+           mng_fptr          fFliprow;           /* internal callback to flip a row of pixels
+                                                    left<->right for a PAST operation */
+           mng_fptr          fTilerow;           /* internal callback to tile a row of pixels
+                                                    during a PAST operation */
+#endif
+           mng_fptr          fInitrowproc;       /* internal callback to initialize
+                                                    the row processing */
+
+           mng_uint16        iDEFIobjectid;      /* DEFI fields */
+           mng_bool          bDEFIhasdonotshow;
+           mng_uint8         iDEFIdonotshow;
+           mng_bool          bDEFIhasconcrete;
+           mng_uint8         iDEFIconcrete;
+           mng_bool          bDEFIhasloca;
+           mng_int32         iDEFIlocax;
+           mng_int32         iDEFIlocay;
+           mng_bool          bDEFIhasclip;
+           mng_int32         iDEFIclipl;
+           mng_int32         iDEFIclipr;
+           mng_int32         iDEFIclipt;
+           mng_int32         iDEFIclipb;
+
+           mng_uint16        iBACKred;           /* BACK fields */
+           mng_uint16        iBACKgreen;
+           mng_uint16        iBACKblue;
+           mng_uint8         iBACKmandatory;
+           mng_uint16        iBACKimageid;
+           mng_uint8         iBACKtile;
+
+           mng_int32         iBackimgoffsx;      /* temp variables for restore_bkgd */
+           mng_int32         iBackimgoffsy;
+           mng_uint32        iBackimgwidth;
+           mng_uint32        iBackimgheight;
+
+#ifndef MNG_SKIPCHUNK_FRAM
+           mng_uint8         iFRAMmode;          /* FRAM fields (global) */
+           mng_uint32        iFRAMdelay;
+           mng_uint32        iFRAMtimeout;
+           mng_bool          bFRAMclipping;
+           mng_int32         iFRAMclipl;
+           mng_int32         iFRAMclipr;
+           mng_int32         iFRAMclipt;
+           mng_int32         iFRAMclipb;
+
+           mng_uint8         iFramemode;         /* current subframe variables */
+           mng_uint32        iFramedelay;
+           mng_uint32        iFrametimeout;
+           mng_bool          bFrameclipping;
+           mng_int32         iFrameclipl;
+           mng_int32         iFrameclipr;
+           mng_int32         iFrameclipt;
+           mng_int32         iFrameclipb;
+
+           mng_uint32        iNextdelay;         /* delay *after* next image */
+#endif
+
+#ifndef MNG_SKIPCHUNK_SHOW
+           mng_uint8         iSHOWmode;          /* SHOW fields */
+           mng_uint16        iSHOWfromid;
+           mng_uint16        iSHOWtoid;
+           mng_uint16        iSHOWnextid;
+           mng_int16         iSHOWskip;
+#endif
+
+           mng_uint32        iGlobalPLTEcount;   /* global PLTE fields */
+           mng_rgbpaltab     aGlobalPLTEentries;
+
+           mng_uint32        iGlobalTRNSrawlen;  /* global tRNS fields */
+           mng_uint8arr      aGlobalTRNSrawdata;
+
+           mng_uint32        iGlobalGamma;       /* global gAMA fields */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+           mng_uint32        iGlobalWhitepointx; /* global cHRM fields */
+           mng_uint32        iGlobalWhitepointy;
+           mng_uint32        iGlobalPrimaryredx;
+           mng_uint32        iGlobalPrimaryredy;
+           mng_uint32        iGlobalPrimarygreenx;
+           mng_uint32        iGlobalPrimarygreeny;
+           mng_uint32        iGlobalPrimarybluex;
+           mng_uint32        iGlobalPrimarybluey;
+#endif
+
+           mng_uint8         iGlobalRendintent;  /* global sRGB fields */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+           mng_uint32        iGlobalProfilesize; /* global iCCP fields */
+           mng_ptr           pGlobalProfile;
+#endif
+
+           mng_uint16        iGlobalBKGDred;     /* global bKGD fields */
+           mng_uint16        iGlobalBKGDgreen;
+           mng_uint16        iGlobalBKGDblue;
+
+           mng_ptr           pDeltaImage;        /* delta-image fields */
+           mng_uint8         iDeltaImagetype;
+#endif /* MNG_SUPPORT_DISPLAY */
+           mng_uint8         iDeltatype;         /* need this one in read processing !! */
+#ifdef MNG_SUPPORT_DISPLAY
+           mng_uint32        iDeltaBlockwidth;
+           mng_uint32        iDeltaBlockheight;
+           mng_uint32        iDeltaBlockx;
+           mng_uint32        iDeltaBlocky;
+           mng_bool          bDeltaimmediate;
+
+           mng_fptr          fDeltagetrow;       /* internal delta-proc callbacks */
+           mng_fptr          fDeltaaddrow;
+           mng_fptr          fDeltareplacerow;
+           mng_fptr          fDeltaputrow;
+
+#ifndef MNG_SKIPCHUNK_PROM
+           mng_fptr          fPromoterow;        /* internal PROM fields */
+           mng_fptr          fPromBitdepth;
+           mng_ptr           pPromBuf;
+           mng_uint8         iPromColortype;
+           mng_uint8         iPromBitdepth;
+           mng_uint8         iPromFilltype;
+           mng_uint32        iPromWidth;
+           mng_ptr           pPromSrc;
+           mng_ptr           pPromDst;
+#endif
+
+#ifndef MNG_SKIPCHUNK_MAGN
+           mng_uint16        iMAGNfromid;
+           mng_uint16        iMAGNcurrentid;
+           mng_uint16        iMAGNtoid;
+#endif
+
+#ifndef MNG_SKIPCHUNK_PAST
+           mng_uint16        iPASTid;
+           mng_int32         iPastx;             /* target x/y of last PAST */
+           mng_int32         iPasty;
+#endif
+
+           mng_objectp       pLastseek;          /* last processed ani_seek object */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+           mng_objectp       pMPNG;              /* mpNG object if available */
+#endif
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+           mng_objectp       pANG;               /* ANG object if available */
+#endif
+
+#endif /* MNG_SUPPORT_DISPLAY */
+
+#ifdef MNG_INCLUDE_ZLIB
+           z_stream          sZlib;              /* zlib (de)compression variables */
+
+           mng_int32         iZlevel;            /* zlib compression parameters */
+           mng_int32         iZmethod;
+           mng_int32         iZwindowbits;
+           mng_int32         iZmemlevel;
+           mng_int32         iZstrategy;
+
+           mng_uint32        iMaxIDAT;           /* maximum size of IDAT data */
+
+           mng_bool          bInflating;         /* indicates "inflate" in progress */
+           mng_bool          bDeflating;         /* indicates "deflate" in progress */
+#endif /* MNG_INCLUDE_ZLIB */
+
+#ifdef MNG_INCLUDE_JNG
+           mngjpeg_dctmethod eJPEGdctmethod;     /* IJG compression variables */
+           mng_int32         iJPEGquality;
+           mng_int32         iJPEGsmoothing;
+           mng_bool          bJPEGcompressprogr;
+           mng_bool          bJPEGcompressopt;
+
+           mng_uint32        iMaxJDAT;           /* maximum size of JDAT/JDAA data */
+
+           mngjpeg_compp     pJPEGcinfo;         /* compression structure */
+           mngjpeg_errorp    pJPEGcerr;          /* error-manager compress */
+
+           mngjpeg_decompp   pJPEGdinfo;         /* decompression structure (JDAT) */
+           mngjpeg_errorp    pJPEGderr;          /* error-manager decompress (JDAT) */
+           mngjpeg_sourcep   pJPEGdsrc;          /* source-manager decompress (JDAT) */
+
+           mngjpeg_decompp   pJPEGdinfo2;        /* decompression structure (JDAA) */
+           mngjpeg_errorp    pJPEGderr2;         /* error-manager decompress (JDAA) */
+           mngjpeg_sourcep   pJPEGdsrc2;         /* source-manager decompress (JDAA) */
+
+           mng_uint8p        pJPEGbuf;           /* buffer for JPEG (de)compression (JDAT) */
+           mng_uint32        iJPEGbufmax;        /* allocated space for buffer (JDAT) */
+           mng_uint8p        pJPEGcurrent;       /* current pointer into buffer (JDAT) */
+           mng_uint32        iJPEGbufremain;     /* remaining bytes in buffer (JDAT) */
+           mng_uint32        iJPEGtoskip;        /* bytes to skip on next input-block (JDAT) */
+
+           mng_uint8p        pJPEGbuf2;          /* buffer for JPEG (de)compression (JDAA) */
+           mng_uint32        iJPEGbufmax2;       /* allocated space for buffer (JDAA) */
+           mng_uint8p        pJPEGcurrent2;      /* current pointer into buffer (JDAA) */
+           mng_uint32        iJPEGbufremain2;    /* remaining bytes in buffer (JDAA) */
+           mng_uint32        iJPEGtoskip2;       /* bytes to skip on next input-block (JDAA) */
+
+           mng_uint8p        pJPEGrow;           /* buffer for a JPEG row of samples (JDAT) */
+           mng_uint32        iJPEGrowlen;
+
+           mng_uint8p        pJPEGrow2;          /* buffer for a JPEG row of samples (JDAA) */
+           mng_uint32        iJPEGrowlen2;
+
+           mng_bool          bJPEGcompress;      /* indicates "compress" initialized */
+
+           mng_bool          bJPEGdecompress;    /* indicates "decompress" initialized (JDAT) */
+           mng_bool          bJPEGhasheader;     /* indicates "readheader" succeeded (JDAT) */
+           mng_bool          bJPEGdecostarted;   /* indicates "decompress" started (JDAT) */
+           mng_bool          bJPEGscanstarted;   /* indicates "first scan" started (JDAT) */
+           mng_bool          bJPEGscanending;    /* indicates "finish_output" suspended (JDAT) */
+           mng_bool          bJPEGprogressive;   /* indicates a progressive image (JDAT) */
+
+           mng_bool          bJPEGdecompress2;   /* indicates "decompress" initialized (JDAA) */
+           mng_bool          bJPEGhasheader2;    /* indicates "readheader" succeeded (JDAA) */
+           mng_bool          bJPEGdecostarted2;  /* indicates "decompress" started (JDAA) */
+           mng_bool          bJPEGscanstarted2;  /* indicates "first scan" started (JDAA) */
+           mng_bool          bJPEGprogressive2;  /* indicates a progressive image (JDAA) */
+
+           mng_fptr          fStorerow2;         /* internal callback to store an
+                                                    uncompressed/unfiltered row of JPEG-data (JDAT) */
+
+           mng_fptr          fStorerow3;         /* internal callback to store an
+                                                    uncompressed/unfiltered row of JPEG-data (JDAA) */
+
+           mng_uint32        iJPEGrow;           /* row-number for current JPEG row */
+           mng_uint32        iJPEGalpharow;      /* nr. of rows filled with alpha */
+           mng_uint32        iJPEGrgbrow;        /* nr. of rows filled with 'color'-info */
+           mng_uint32        iJPEGdisprow;       /* nr. of rows already displayed "on-the-fly" */
+
+#if defined(MNG_USE_SETJMP) && defined (MNG_INCLUDE_IJG6B)
+           jmp_buf           sErrorbuf;          /* setjmp/longjmp buffer (error-recovery) */
+#endif
+
+#endif /* MNG_INCLUDE_JNG */
+
+#ifndef MNG_USE_ZLIB_CRC
+           mng_uint32        aCRCtable [256];    /* CRC prefab table */
+           mng_bool          bCRCcomputed;       /* "has been built" indicator */
+#endif
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+           png_imgtype       ePng_imgtype;
+#endif
+
+#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT)
+           mng_uint8         iPNGdepth;          /* Real input depth */
+           mng_uint8         iPNGmult;
+#endif
+
+#ifdef MNG_OPTIMIZE_DISPLAYCALLS
+           mng_uint32        iRawlen;            /* temp vars for display processing */
+           mng_uint8p        pRawdata;
+#ifndef MNG_SKIPCHUNK_BASI
+           mng_uint16        iBASIred;
+           mng_uint16        iBASIgreen;
+           mng_uint16        iBASIblue;
+           mng_bool          bBASIhasalpha;
+           mng_uint16        iBASIalpha;
+           mng_uint8         iBASIviewable;
+#endif
+#ifndef MNG_SKIPCHUNK_CLON
+           mng_uint16        iCLONsourceid;
+           mng_uint16        iCLONcloneid;
+           mng_uint8         iCLONclonetype;
+           mng_bool          bCLONhasdonotshow;
+           mng_uint8         iCLONdonotshow;
+           mng_uint8         iCLONconcrete;
+           mng_bool          bCLONhasloca;
+           mng_uint8         iCLONlocationtype;
+           mng_int32         iCLONlocationx;
+           mng_int32         iCLONlocationy;
+#endif
+#ifndef MNG_SKIPCHUNK_DISC
+           mng_uint32        iDISCcount;
+           mng_uint16p       pDISCids;
+#endif
+#ifndef MNG_SKIPCHUNK_FRAM
+           mng_uint8         iTempFramemode;
+           mng_uint8         iTempChangedelay;
+           mng_uint32        iTempDelay;
+           mng_uint8         iTempChangetimeout;
+           mng_uint32        iTempTimeout;
+           mng_uint8         iTempChangeclipping;
+           mng_uint8         iTempCliptype;
+           mng_int32         iTempClipl;
+           mng_int32         iTempClipr;
+           mng_int32         iTempClipt;
+           mng_int32         iTempClipb;
+#endif
+#ifndef MNG_SKIPCHUNK_MOVE
+           mng_uint16        iMOVEfromid;
+           mng_uint16        iMOVEtoid;
+           mng_uint8         iMOVEmovetype;
+           mng_int32         iMOVEmovex;
+           mng_int32         iMOVEmovey;
+#endif
+#ifndef MNG_SKIPCHUNK_CLIP
+           mng_uint16        iCLIPfromid;
+           mng_uint16        iCLIPtoid;
+           mng_uint8         iCLIPcliptype;
+           mng_int32         iCLIPclipl;
+           mng_int32         iCLIPclipr;
+           mng_int32         iCLIPclipt;
+           mng_int32         iCLIPclipb;
+#endif
+#ifndef MNG_NO_DELTA_PNG
+           mng_uint16        iDHDRobjectid;
+           mng_uint8         iDHDRimagetype;
+           mng_uint8         iDHDRdeltatype;
+           mng_uint32        iDHDRblockwidth;
+           mng_uint32        iDHDRblockheight;
+           mng_uint32        iDHDRblockx;
+           mng_uint32        iDHDRblocky;
+           mng_uint8         iPROMbitdepth;
+           mng_uint8         iPROMcolortype;
+           mng_uint8         iPROMfilltype;
+           mng_uint8         iPPLTtype;
+           mng_uint32        iPPLTcount;
+           mng_palette8ep    paPPLTindexentries;
+           mng_uint8p        paPPLTalphaentries;
+           mng_uint8p        paPPLTusedentries;
+#endif
+#ifndef MNG_SKIPCHUNK_MAGN
+           mng_uint16        iMAGNfirstid;
+           mng_uint16        iMAGNlastid;
+           mng_uint8         iMAGNmethodX;
+           mng_uint16        iMAGNmX;
+           mng_uint16        iMAGNmY;
+           mng_uint16        iMAGNmL;
+           mng_uint16        iMAGNmR;
+           mng_uint16        iMAGNmT;
+           mng_uint16        iMAGNmB;
+           mng_uint8         iMAGNmethodY;
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+           mng_uint16        iPASTtargetid;
+           mng_uint8         iPASTtargettype;
+           mng_int32         iPASTtargetx;
+           mng_int32         iPASTtargety;
+           mng_uint32        iPASTcount;
+           mng_ptr           pPASTsources;
+#endif
+#endif /* MNG_OPTIMIZE_DISPLAYCALLS */
+
+        } mng_data;
+
+typedef mng_data * mng_datap;
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Internal Callback-Function prototypes                                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+typedef mng_retcode(*mng_displayrow)  (mng_datap  pData);
+typedef mng_retcode(*mng_restbkgdrow) (mng_datap  pData);
+typedef mng_retcode(*mng_correctrow)  (mng_datap  pData);
+typedef mng_retcode(*mng_retrieverow) (mng_datap  pData);
+typedef mng_retcode(*mng_storerow)    (mng_datap  pData);
+typedef mng_retcode(*mng_processrow)  (mng_datap  pData);
+typedef mng_retcode(*mng_initrowproc) (mng_datap  pData);
+typedef mng_retcode(*mng_differrow)   (mng_datap  pData);
+typedef mng_retcode(*mng_scalerow)    (mng_datap  pData);
+typedef mng_retcode(*mng_deltarow)    (mng_datap  pData);
+typedef mng_retcode(*mng_promoterow)  (mng_datap  pData);
+typedef mng_retcode(*mng_fliprow)     (mng_datap  pData);
+typedef mng_retcode(*mng_tilerow)     (mng_datap  pData);
+
+typedef mng_uint8  (*mng_bitdepth_8)  (mng_uint8  iB);
+typedef mng_uint16 (*mng_bitdepth_16) (mng_uint8  iB);
+
+typedef mng_retcode(*mng_magnify_x)   (mng_datap  pData,
+                                       mng_uint16 iMX,
+                                       mng_uint16 iML,
+                                       mng_uint16 iMR,
+                                       mng_uint32 iWidth,
+                                       mng_uint8p iSrcline,
+                                       mng_uint8p iDstline);
+typedef mng_retcode(*mng_magnify_y)   (mng_datap  pData,
+                                       mng_int32  iM,
+                                       mng_int32  iS,
+                                       mng_uint32 iWidth,
+                                       mng_uint8p iSrcline1,
+                                       mng_uint8p iSrcline2,
+                                       mng_uint8p iDstline);
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Routines for swapping byte-order from and to graphic files             * */
+/* * (This code is adapted from the libpng package)                         * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_BIGENDIAN_SUPPORTED
+mng_uint32 mng_get_uint32 (mng_uint8p pBuf);
+mng_int32  mng_get_int32  (mng_uint8p pBuf);
+mng_uint16 mng_get_uint16 (mng_uint8p pBuf);
+void       mng_put_uint32 (mng_uint8p pBuf,
+                           mng_uint32 i);
+void       mng_put_int32  (mng_uint8p pBuf,
+                           mng_int32  i);
+void       mng_put_uint16 (mng_uint8p pBuf,
+                           mng_uint16 i);
+#else /* MNG_BIGENDIAN_SUPPORTED */
+#define mng_get_uint32(P)   *(mng_uint32p)(P)
+#define mng_get_int32(P)    *(mng_int32p)(P)
+#define mng_get_uint16(P)   *(mng_uint16p)(P)
+#define mng_put_uint32(P,I) *(mng_uint32p)(P) = (I)
+#define mng_put_int32(P,I)  *(mng_int32p)(P) = (I)
+#define mng_put_uint16(P,I) *(mng_uint16p)(P) = (I)
+#endif /* MNG_BIGENDIAN_SUPPORTED */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Some handy(?) macro definitions                                        * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MAX_COORD(a, b)  (((a) > (b)) ? (a) : (b))
+#define MIN_COORD(a, b)  (((a) < (b)) ? (a) : (b))
+
+/* ************************************************************************** */
+
+#endif /* _libmng_data_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_display.c b/files/Source/LibMNG/libmng_display.c
new file mode 100644
index 0000000..adfb91d
--- /dev/null
+++ b/files/Source/LibMNG/libmng_display.c
@@ -0,0 +1,7135 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_display.c          copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Display management (implementation)                        * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the display management routines          * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added callback error-reporting support                   * */
+/* *             - fixed frame_delay misalignment                           * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - added sanity check for frozen status                     * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *             0.5.1 - 05/13/2000 - G.Juyn                                * */
+/* *             - changed display_mend to reset state to initial or SAVE   * */
+/* *             - added eMNGma hack (will be removed in 1.0.0 !!!)         * */
+/* *             - added TERM animation object pointer (easier reference)   * */
+/* *             - added process_save & process_seek routines               * */
+/* *             0.5.1 - 05/14/2000 - G.Juyn                                * */
+/* *             - added save_state and restore_state for SAVE/SEEK/TERM    * */
+/* *               processing                                               * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/20/2000 - G.Juyn                                * */
+/* *             - added JNG support (JHDR/JDAT)                            * */
+/* *             0.5.2 - 05/23/2000 - G.Juyn                                * */
+/* *             - fixed problem with DEFI clipping                         * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added delta-image support (DHDR,PROM,IPNG,IJNG)          * */
+/* *             0.5.2 - 05/31/2000 - G.Juyn                                * */
+/* *             - fixed pointer confusion (contributed by Tim Rowley)      * */
+/* *             0.5.2 - 06/03/2000 - G.Juyn                                * */
+/* *             - fixed makeup for Linux gcc compile                       * */
+/* *             0.5.2 - 06/05/2000 - G.Juyn                                * */
+/* *             - added support for RGB8_A8 canvasstyle                    * */
+/* *             0.5.2 - 06/09/2000 - G.Juyn                                * */
+/* *             - fixed timer-handling to run with Mozilla (Tim Rowley)    * */
+/* *             0.5.2 - 06/10/2000 - G.Juyn                                * */
+/* *             - fixed some compilation-warnings (contrib Jason Morris)   * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/12/2000 - G.Juyn                                * */
+/* *             - fixed display of stored JNG images                       * */
+/* *             0.5.3 - 06/13/2000 - G.Juyn                                * */
+/* *             - fixed problem with BASI-IEND as object 0                 * */
+/* *             0.5.3 - 06/16/2000 - G.Juyn                                * */
+/* *             - changed progressive-display processing                   * */
+/* *             0.5.3 - 06/17/2000 - G.Juyn                                * */
+/* *             - changed delta-image processing                           * */
+/* *             0.5.3 - 06/20/2000 - G.Juyn                                * */
+/* *             - fixed some minor stuff                                   * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added speed-modifier to timing routine                   * */
+/* *             0.5.3 - 06/22/2000 - G.Juyn                                * */
+/* *             - added support for PPLT chunk processing                  * */
+/* *             0.5.3 - 06/29/2000 - G.Juyn                                * */
+/* *             - swapped refresh parameters                               * */
+/* *                                                                        * */
+/* *             0.9.0 - 06/30/2000 - G.Juyn                                * */
+/* *             - changed refresh parameters to 'x,y,width,height'         * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/07/2000 - G.Juyn                                * */
+/* *             - implemented support for freeze/reset/resume & go_xxxx    * */
+/* *             0.9.1 - 07/08/2000 - G.Juyn                                * */
+/* *             - added support for improved timing                        * */
+/* *             0.9.1 - 07/14/2000 - G.Juyn                                * */
+/* *             - changed EOF processing behavior                          * */
+/* *             - fixed TERM delay processing                              * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - fixed freeze & reset processing                          * */
+/* *             0.9.1 - 07/16/2000 - G.Juyn                                * */
+/* *             - fixed storage of images during mng_read()                * */
+/* *             - fixed support for mng_display() after mng_read()         * */
+/* *             0.9.1 - 07/24/2000 - G.Juyn                                * */
+/* *             - fixed reading of still-images                            * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/07/2000 - G.Juyn                                * */
+/* *             - B111300 - fixup for improved portability                 * */
+/* *             0.9.3 - 08/21/2000 - G.Juyn                                * */
+/* *             - fixed TERM processing delay of 0 msecs                   * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/10/2000 - G.Juyn                                * */
+/* *             - fixed problem with no refresh after TERM                 * */
+/* *             - fixed DEFI behavior                                      * */
+/* *             0.9.3 - 09/16/2000 - G.Juyn                                * */
+/* *             - fixed timing & refresh behavior for single PNG/JNG       * */
+/* *             0.9.3 - 09/19/2000 - G.Juyn                                * */
+/* *             - refixed timing & refresh behavior for single PNG/JNG     * */
+/* *             0.9.3 - 10/02/2000 - G.Juyn                                * */
+/* *             - fixed timing again (this is getting boring...)           * */
+/* *             - refixed problem with no refresh after TERM               * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added JDAA chunk                                         * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - fixed support for bKGD                                   * */
+/* *             0.9.3 - 10/18/2000 - G.Juyn                                * */
+/* *             - fixed delta-processing behavior                          * */
+/* *             0.9.3 - 10/19/2000 - G.Juyn                                * */
+/* *             - added storage for pixel-/alpha-sampledepth for delta's   * */
+/* *             0.9.3 - 10/27/2000 - G.Juyn                                * */
+/* *             - fixed separate read() & display() processing             * */
+/* *                                                                        * */
+/* *             0.9.4 - 10/31/2000 - G.Juyn                                * */
+/* *             - fixed possible loop in display_resume() (Thanks Vova!)   * */
+/* *             0.9.4 - 11/20/2000 - G.Juyn                                * */
+/* *             - fixed unwanted repetition in mng_readdisplay()           * */
+/* *             0.9.4 - 11/24/2000 - G.Juyn                                * */
+/* *             - moved restore of object 0 to libmng_display              * */
+/* *             - added restore of object 0 to TERM processing !!!         * */
+/* *             - fixed TERM delay processing                              * */
+/* *             - fixed TERM end processing (count = 0)                    * */
+/* *             0.9.4 - 12/16/2000 - G.Juyn                                * */
+/* *             - fixed mixup of data- & function-pointers (thanks Dimitri)* */
+/* *             0.9.4 -  1/18/2001 - G.Juyn                                * */
+/* *             - removed test filter-methods 1 & 65                       * */
+/* *             - set default level-set for filtertype=64 to all zeroes    * */
+/* *                                                                        * */
+/* *             0.9.5 -  1/20/2001 - G.Juyn                                * */
+/* *             - fixed compiler-warnings Mozilla (thanks Tim)             * */
+/* *             0.9.5 -  1/23/2001 - G.Juyn                                * */
+/* *             - fixed timing-problem with switching framing_modes        * */
+/* *                                                                        * */
+/* *             1.0.1 - 02/08/2001 - G.Juyn                                * */
+/* *             - added MEND processing callback                           * */
+/* *             1.0.1 - 02/13/2001 - G.Juyn                                * */
+/* *             - fixed first FRAM_MODE=4 timing problem                   * */
+/* *             1.0.1 - 04/21/2001 - G.Juyn                                * */
+/* *             - fixed memory-leak for JNGs with alpha (Thanks Gregg!)    * */
+/* *             - added BGRA8 canvas with premultiplied alpha              * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/25/2001 - G.Juyn                                * */
+/* *             - fixed memory-leak with delta-images (Thanks Michael!)    * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed PROM support                                   * */
+/* *             - completed delta-image support                            * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/13/2002 - G.Juyn                                * */
+/* *             - fixed read/write of MAGN chunk                           * */
+/* *             1.0.5 - 09/15/2002 - G.Juyn                                * */
+/* *             - fixed LOOP iteration=0 special case                      * */
+/* *             1.0.5 - 09/19/2002 - G.Juyn                                * */
+/* *             - fixed color-correction for restore-background handling   * */
+/* *             - optimized restore-background for bKGD cases              * */
+/* *             - cleaned up some old stuff                                * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - finished support for BACK image & tiling                 * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 09/22/2002 - G.Juyn                                * */
+/* *             - added bgrx8 canvas (filler byte)                         * */
+/* *             1.0.5 - 10/05/2002 - G.Juyn                                * */
+/* *             - fixed dropping mix of frozen/unfrozen objects            * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - added proposed change in handling of TERM- & if-delay    * */
+/* *             - added another fix for misplaced TERM chunk               * */
+/* *             - completed support for condition=2 in TERM chunk          * */
+/* *             1.0.5 - 10/18/2002 - G.Juyn                                * */
+/* *             - fixed clipping-problem with BACK tiling (Thanks Sakura!) * */
+/* *             1.0.5 - 10/20/2002 - G.Juyn                                * */
+/* *             - fixed processing for multiple objects in MAGN            * */
+/* *             - fixed display of visible target of PAST operation        * */
+/* *             1.0.5 - 10/30/2002 - G.Juyn                                * */
+/* *             - modified TERM/MEND processing for max(1, TERM_delay,     * */
+/* *               interframe_delay)                                        * */
+/* *             1.0.5 - 11/04/2002 - G.Juyn                                * */
+/* *             - fixed layer- & frame-counting during read()              * */
+/* *             - fixed goframe/golayer/gotime processing                  * */
+/* *             1.0.5 - 01/19/2003 - G.Juyn                                * */
+/* *             - B654627 - fixed SEGV when no gettickcount callback       * */
+/* *             - B664383 - fixed typo                                     * */
+/* *             - finalized changes in TERM/final_delay to elected proposal* */
+/* *                                                                        * */
+/* *             1.0.6 - 05/11/2003 - G. Juyn                               * */
+/* *             - added conditionals around canvas update routines         * */
+/* *             1.0.6 - 05/25/2003 - G.R-P                                 * */
+/* *             - added MNG_SKIPCHUNK_cHNK footprint optimizations         * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added conditionals around some JNG-supporting code       * */
+/* *             - added conditionals around 16-bit supporting code         * */
+/* *             - reversed some loops to use decrementing counter          * */
+/* *             - combined init functions into one function                * */
+/* *             1.0.6 - 07/10/2003 - G.R-P                                 * */
+/* *             - replaced nested switches with simple init setup function * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *             1.0.6 - 08/17/2003 - G.R-P                                 * */
+/* *             - added conditionals around non-VLC chunk support          * */
+/* *                                                                        * */
+/* *             1.0.7 - 11/27/2003 - R.A                                   * */
+/* *             - added CANVAS_RGB565 and CANVAS_BGR565                    * */
+/* *             1.0.7 - 12/06/2003 - R.A                                   * */
+/* *             - added CANVAS_RGBA565 and CANVAS_BGRA565                  * */
+/* *             1.0.7 - 01/25/2004 - J.S                                   * */
+/* *             - added premultiplied alpha canvas' for RGBA, ARGB, ABGR   * */
+/* *                                                                        * */
+/* *             1.0.8 - 03/31/2004 - G.Juyn                                * */
+/* *             - fixed problem with PAST usage where source > dest        * */
+/* *             1.0.8 - 05/04/2004 - G.R-P.                                * */
+/* *             - fixed misplaced 16-bit conditionals                      * */
+/* *                                                                        * */
+/* *             1.0.9 - 09/18/2004 - G.R-P.                                * */
+/* *             - revised some SKIPCHUNK conditionals                      * */
+/* *             1.0.9 - 10/10/2004 - G.R-P.                                * */
+/* *             - added MNG_NO_1_2_4BIT_SUPPORT                            * */
+/* *             1.0.9 - 10/14/2004 - G.Juyn                                * */
+/* *             - added bgr565_a8 canvas-style (thanks to J. Elvander)     * */
+/* *             1.0.9 - 12/11/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_DISPLAYCALLS              * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* *             1.0.10 - 07/06/2005 - G.R-P.                               * */
+/* *             - added more SKIPCHUNK conditionals                        * */
+/* *             1.0.10 - 12/28/2005 - G.R-P.                               * */
+/* *             - added missing SKIPCHUNK_MAGN conditional                 * */
+/* *             1.0.10 - 03/07/2006 - (thanks to W. Manthey)               * */
+/* *             - added CANVAS_RGB555 and CANVAS_BGR555                    * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - fixed several compiler warnings                          * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_chunks.h"
+#include "libmng_objects.h"
+#include "libmng_object_prc.h"
+#include "libmng_memory.h"
+#include "libmng_zlib.h"
+#include "libmng_jpeg.h"
+#include "libmng_cms.h"
+#include "libmng_pixels.h"
+#include "libmng_display.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_DISPLAY_PROCS
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode set_delay (mng_datap  pData,
+                                 mng_uint32 iInterval)
+{
+  if (!iInterval)                      /* at least 1 msec please! */
+    iInterval = 1;
+
+  if (pData->bRunning)                 /* only when really displaying */
+    if (!pData->fSettimer ((mng_handle)pData, iInterval))
+      MNG_ERROR (pData, MNG_APPTIMERERROR);
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+  if ((!pData->bDynamic) || (pData->bRunning))
+#else
+  if (pData->bRunning)
+#endif
+    pData->bTimerset = MNG_TRUE;       /* and indicate so */
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_uint32 calculate_delay (mng_datap  pData,
+                                      mng_uint32 iDelay)
+{
+  mng_uint32 iTicks   = pData->iTicks;
+  mng_uint32 iWaitfor = 1;             /* default non-MNG delay */
+
+  if (!iTicks)                         /* tick_count not specified ? */
+    if (pData->eImagetype == mng_it_mng)
+      iTicks = 1000;
+
+  if (iTicks)
+  {
+    switch (pData->iSpeed)             /* honor speed modifier */
+    {
+      case mng_st_fast :
+        {
+          iWaitfor = (mng_uint32)(( 500 * iDelay) / iTicks);
+          break;
+        }
+      case mng_st_slow :
+        {
+          iWaitfor = (mng_uint32)((3000 * iDelay) / iTicks);
+          break;
+        }
+      case mng_st_slowest :
+        {
+          iWaitfor = (mng_uint32)((8000 * iDelay) / iTicks);
+          break;
+        }
+      default :
+        {
+          iWaitfor = (mng_uint32)((1000 * iDelay) / iTicks);
+        }
+    }
+  }
+
+  return iWaitfor;
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Progressive display refresh - does the call to the refresh callback    * */
+/* * and sets the timer to allow the app to perform the actual refresh to   * */
+/* * the screen (eg. process its main message-loop)                         * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_display_progressive_refresh (mng_datap  pData,
+                                             mng_uint32 iInterval)
+{
+  {                                    /* let the app refresh first ? */
+    if ((pData->bRunning) && (!pData->bSkipping) &&
+        (pData->iUpdatetop < pData->iUpdatebottom) && (pData->iUpdateleft < pData->iUpdateright))
+    {
+      if (!pData->fRefresh (((mng_handle)pData),
+                            pData->iUpdateleft, pData->iUpdatetop,
+                            pData->iUpdateright  - pData->iUpdateleft,
+                            pData->iUpdatebottom - pData->iUpdatetop))
+        MNG_ERROR (pData, MNG_APPMISCERROR);
+
+      pData->iUpdateleft   = 0;        /* reset update-region */
+      pData->iUpdateright  = 0;
+      pData->iUpdatetop    = 0;
+      pData->iUpdatebottom = 0;        /* reset refreshneeded indicator */
+      pData->bNeedrefresh  = MNG_FALSE;
+                                       /* interval requested ? */
+      if ((!pData->bFreezing) && (iInterval))
+      {                                /* setup the timer */
+        mng_retcode iRetcode = set_delay (pData, iInterval);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+      }
+    }
+  }
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Generic display routines                                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode interframe_delay (mng_datap pData)
+{
+  mng_uint32  iWaitfor = 0;
+  mng_uint32  iInterval;
+  mng_uint32  iRuninterval;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INTERFRAME_DELAY, MNG_LC_START);
+#endif
+
+  {
+#ifndef MNG_SKIPCHUNK_FRAM
+    if (pData->iFramedelay > 0)        /* real delay ? */
+    {                                  /* let the app refresh first ? */
+      if ((pData->bRunning) && (!pData->bSkipping) &&
+          (pData->iUpdatetop < pData->iUpdatebottom) && (pData->iUpdateleft < pData->iUpdateright))
+        if (!pData->fRefresh (((mng_handle)pData),
+                              pData->iUpdateleft,  pData->iUpdatetop,
+                              pData->iUpdateright - pData->iUpdateleft,
+                              pData->iUpdatebottom - pData->iUpdatetop))
+          MNG_ERROR (pData, MNG_APPMISCERROR);
+
+      pData->iUpdateleft   = 0;        /* reset update-region */
+      pData->iUpdateright  = 0;
+      pData->iUpdatetop    = 0;
+      pData->iUpdatebottom = 0;        /* reset refreshneeded indicator */
+      pData->bNeedrefresh  = MNG_FALSE;
+
+#ifndef MNG_SKIPCHUNK_TERM
+      if (pData->bOnlyfirstframe)      /* only processing first frame after TERM ? */
+      {
+        pData->iFramesafterTERM++;
+                                       /* did we do a frame yet ? */
+        if (pData->iFramesafterTERM > 1)
+        {                              /* then that's it; just stop right here ! */
+          pData->pCurraniobj = MNG_NULL;
+          pData->bRunning    = MNG_FALSE;
+
+          return MNG_NOERROR;
+        }
+      }
+#endif
+
+      if (pData->fGettickcount)
+      {                                /* get current tickcount */
+        pData->iRuntime = pData->fGettickcount ((mng_handle)pData);
+                                       /* calculate interval since last sync-point */
+        if (pData->iRuntime < pData->iSynctime)
+          iRuninterval    = pData->iRuntime + ~pData->iSynctime + 1;
+        else
+          iRuninterval    = pData->iRuntime - pData->iSynctime;
+                                       /* calculate actual run-time */
+        if (pData->iRuntime < pData->iStarttime)
+          pData->iRuntime = pData->iRuntime + ~pData->iStarttime + 1;
+        else
+          pData->iRuntime = pData->iRuntime - pData->iStarttime;
+      }
+      else
+      {
+        iRuninterval = 0;
+      }
+
+      iWaitfor = calculate_delay (pData, pData->iFramedelay);
+
+      if (iWaitfor > iRuninterval)     /* delay necessary ? */
+        iInterval = iWaitfor - iRuninterval;
+      else
+        iInterval = 1;                 /* force app to process messageloop */
+                                       /* set the timer ? */
+      if (((pData->bRunning) || (pData->bSearching) || (pData->bReading)) &&
+          (!pData->bSkipping))
+      {
+        iRetcode = set_delay (pData, iInterval);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+      }
+    }
+
+    if (!pData->bSkipping)             /* increase frametime in advance */
+      pData->iFrametime = pData->iFrametime + iWaitfor;
+                                       /* setup for next delay */
+    pData->iFramedelay = pData->iNextdelay;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INTERFRAME_DELAY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL void set_display_routine (mng_datap pData)
+{                                        /* actively running ? */
+  if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping))
+  {
+    switch (pData->iCanvasstyle)         /* determine display routine */
+    {
+#ifndef MNG_SKIPCANVAS_RGB8
+      case MNG_CANVAS_RGB8    : { pData->fDisplayrow = (mng_fptr)mng_display_rgb8;     break; }
+#endif
+#ifndef MNG_SKIPCANVAS_RGBA8
+      case MNG_CANVAS_RGBA8   : { pData->fDisplayrow = (mng_fptr)mng_display_rgba8;    break; }
+#endif
+#ifndef MNG_SKIPCANVAS_RGBA8_PM
+      case MNG_CANVAS_RGBA8_PM: { pData->fDisplayrow = (mng_fptr)mng_display_rgba8_pm; break; }
+#endif
+#ifndef MNG_SKIPCANVAS_ARGB8
+      case MNG_CANVAS_ARGB8   : { pData->fDisplayrow = (mng_fptr)mng_display_argb8;    break; }
+#endif
+#ifndef MNG_SKIPCANVAS_ARGB8_PM
+      case MNG_CANVAS_ARGB8_PM: { pData->fDisplayrow = (mng_fptr)mng_display_argb8_pm; break; }
+#endif
+#ifndef MNG_SKIPCANVAS_RGB8_A8
+      case MNG_CANVAS_RGB8_A8 : { pData->fDisplayrow = (mng_fptr)mng_display_rgb8_a8;  break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGR8
+      case MNG_CANVAS_BGR8    : { pData->fDisplayrow = (mng_fptr)mng_display_bgr8;     break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGRX8
+      case MNG_CANVAS_BGRX8   : { pData->fDisplayrow = (mng_fptr)mng_display_bgrx8;    break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGRA8
+      case MNG_CANVAS_BGRA8   : { pData->fDisplayrow = (mng_fptr)mng_display_bgra8;    break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGRA8_PM
+      case MNG_CANVAS_BGRA8_PM: { pData->fDisplayrow = (mng_fptr)mng_display_bgra8_pm; break; }
+#endif
+#ifndef MNG_SKIPCANVAS_ABGR8
+      case MNG_CANVAS_ABGR8   : { pData->fDisplayrow = (mng_fptr)mng_display_abgr8;    break; }
+#endif
+#ifndef MNG_SKIPCANVAS_ABGR8_PM
+      case MNG_CANVAS_ABGR8_PM: { pData->fDisplayrow = (mng_fptr)mng_display_abgr8_pm; break; }
+#endif
+#ifndef MNG_SKIPCANVAS_RGB565
+      case MNG_CANVAS_RGB565  : { pData->fDisplayrow = (mng_fptr)mng_display_rgb565;   break; }
+#endif
+#ifndef MNG_SKIPCANVAS_RGBA565
+      case MNG_CANVAS_RGBA565 : { pData->fDisplayrow = (mng_fptr)mng_display_rgba565;  break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGR565
+      case MNG_CANVAS_BGR565  : { pData->fDisplayrow = (mng_fptr)mng_display_bgr565;   break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGRA565
+      case MNG_CANVAS_BGRA565 : { pData->fDisplayrow = (mng_fptr)mng_display_bgra565;  break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGR565_A8
+      case MNG_CANVAS_BGR565_A8 : { pData->fDisplayrow = (mng_fptr)mng_display_bgr565_a8;  break; }
+#endif
+#ifndef MNG_SKIPCANVAS_RGB555
+      case MNG_CANVAS_RGB555  : { pData->fDisplayrow = (mng_fptr)mng_display_rgb555;  break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGR555
+      case MNG_CANVAS_BGR555  : { pData->fDisplayrow = (mng_fptr)mng_display_bgr555;  break; }
+#endif
+
+#ifndef MNG_NO_16BIT_SUPPORT
+/*      case MNG_CANVAS_RGB16   : { pData->fDisplayrow = (mng_fptr)mng_display_rgb16;    break; } */
+/*      case MNG_CANVAS_RGBA16  : { pData->fDisplayrow = (mng_fptr)mng_display_rgba16;   break; } */
+/*      case MNG_CANVAS_ARGB16  : { pData->fDisplayrow = (mng_fptr)mng_display_argb16;   break; } */
+/*      case MNG_CANVAS_BGR16   : { pData->fDisplayrow = (mng_fptr)mng_display_bgr16;    break; } */
+/*      case MNG_CANVAS_BGRA16  : { pData->fDisplayrow = (mng_fptr)mng_display_bgra16;   break; } */
+/*      case MNG_CANVAS_ABGR16  : { pData->fDisplayrow = (mng_fptr)mng_display_abgr16;   break; } */
+#endif
+/*      case MNG_CANVAS_INDEX8  : { pData->fDisplayrow = (mng_fptr)mng_display_index8;   break; } */
+/*      case MNG_CANVAS_INDEXA8 : { pData->fDisplayrow = (mng_fptr)mng_display_indexa8;  break; } */
+/*      case MNG_CANVAS_AINDEX8 : { pData->fDisplayrow = (mng_fptr)mng_display_aindex8;  break; } */
+/*      case MNG_CANVAS_GRAY8   : { pData->fDisplayrow = (mng_fptr)mng_display_gray8;    break; } */
+/*      case MNG_CANVAS_AGRAY8  : { pData->fDisplayrow = (mng_fptr)mng_display_agray8;   break; } */
+/*      case MNG_CANVAS_GRAYA8  : { pData->fDisplayrow = (mng_fptr)mng_display_graya8;   break; } */
+#ifndef MNG_NO_16BIT_SUPPORT
+/*      case MNG_CANVAS_GRAY16  : { pData->fDisplayrow = (mng_fptr)mng_display_gray16;   break; } */
+/*      case MNG_CANVAS_GRAYA16 : { pData->fDisplayrow = (mng_fptr)mng_display_graya16;  break; } */
+/*      case MNG_CANVAS_AGRAY16 : { pData->fDisplayrow = (mng_fptr)mng_display_agray16;  break; } */
+#endif
+/*      case MNG_CANVAS_DX15    : { pData->fDisplayrow = (mng_fptr)mng_display_dx15;     break; } */
+/*      case MNG_CANVAS_DX16    : { pData->fDisplayrow = (mng_fptr)mng_display_dx16;     break; } */
+    }
+  }
+
+  return;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode load_bkgdlayer (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_LOAD_BKGDLAYER, MNG_LC_START);
+#endif
+                                       /* actively running ? */
+  if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping))
+  {
+    mng_int32   iY;
+    mng_retcode iRetcode;
+    mng_bool    bColorcorr   = MNG_FALSE;
+                                       /* save values */
+    mng_int32   iDestl       = pData->iDestl;
+    mng_int32   iDestr       = pData->iDestr;
+    mng_int32   iDestt       = pData->iDestt;
+    mng_int32   iDestb       = pData->iDestb;
+    mng_int32   iSourcel     = pData->iSourcel;
+    mng_int32   iSourcer     = pData->iSourcer;
+    mng_int32   iSourcet     = pData->iSourcet;
+    mng_int32   iSourceb     = pData->iSourceb;
+    mng_int8    iPass        = pData->iPass;
+    mng_int32   iRow         = pData->iRow;
+    mng_int32   iRowinc      = pData->iRowinc;
+    mng_int32   iCol         = pData->iCol;
+    mng_int32   iColinc      = pData->iColinc;
+    mng_int32   iRowsamples  = pData->iRowsamples;
+    mng_int32   iRowsize     = pData->iRowsize;
+    mng_uint8p  pPrevrow     = pData->pPrevrow;
+    mng_uint8p  pRGBArow     = pData->pRGBArow;
+    mng_bool    bIsRGBA16    = pData->bIsRGBA16;
+    mng_bool    bIsOpaque    = pData->bIsOpaque;
+    mng_fptr    fCorrectrow  = pData->fCorrectrow;
+    mng_fptr    fDisplayrow  = pData->fDisplayrow;
+    mng_fptr    fRetrieverow = pData->fRetrieverow;
+    mng_objectp pCurrentobj  = pData->pCurrentobj;
+    mng_objectp pRetrieveobj = pData->pRetrieveobj;
+
+    pData->iDestl   = 0;               /* determine clipping region */
+    pData->iDestt   = 0;
+    pData->iDestr   = pData->iWidth;
+    pData->iDestb   = pData->iHeight;
+
+#ifndef MNG_SKIPCHUNK_FRAM
+    if (pData->bFrameclipping)         /* frame clipping specified ? */
+    {
+      pData->iDestl = MAX_COORD (pData->iDestl,  pData->iFrameclipl);
+      pData->iDestt = MAX_COORD (pData->iDestt,  pData->iFrameclipt);
+      pData->iDestr = MIN_COORD (pData->iDestr,  pData->iFrameclipr);
+      pData->iDestb = MIN_COORD (pData->iDestb,  pData->iFrameclipb);
+    }
+#endif
+                                       /* anything to clear ? */
+    if ((pData->iDestr >= pData->iDestl) && (pData->iDestb >= pData->iDestt))
+    {
+      pData->iPass       = -1;         /* these are the object's dimensions now */
+      pData->iRow        = 0;
+      pData->iRowinc     = 1;
+      pData->iCol        = 0;
+      pData->iColinc     = 1;
+      pData->iRowsamples = pData->iWidth;
+      pData->iRowsize    = pData->iRowsamples << 2;
+      pData->bIsRGBA16   = MNG_FALSE;  /* let's keep it simple ! */
+      pData->bIsOpaque   = MNG_TRUE;
+
+      pData->iSourcel    = 0;          /* source relative to destination */
+      pData->iSourcer    = pData->iDestr - pData->iDestl;
+      pData->iSourcet    = 0;
+      pData->iSourceb    = pData->iDestb - pData->iDestt;
+
+      set_display_routine (pData);     /* determine display routine */
+                                       /* default restore using preset BG color */
+      pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgcolor;
+
+#ifndef MNG_SKIPCHUNK_bKGD
+      if (((pData->eImagetype == mng_it_png) || (pData->eImagetype == mng_it_jng)) &&
+          (pData->bUseBKGD))
+      {                                /* prefer bKGD in PNG/JNG */
+        if (!pData->pCurrentobj)
+          pData->pCurrentobj = pData->pObjzero;
+
+        if (((mng_imagep)pData->pCurrentobj)->pImgbuf->bHasBKGD)
+        {
+          pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bkgd;
+          bColorcorr          = MNG_TRUE;
+        }
+      }
+#endif
+
+      if (pData->fGetbkgdline)         /* background-canvas-access callback set ? */
+      {
+        switch (pData->iBkgdstyle)
+        {
+#ifndef MNG_SKIPCANVAS_RGB8
+          case MNG_CANVAS_RGB8    : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_rgb8;    break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGR8
+          case MNG_CANVAS_BGR8    : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgr8;    break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGRX8
+          case MNG_CANVAS_BGRX8   : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgrx8;   break; }
+#endif
+#ifndef MNG_SKIPCANVAS_BGR565
+          case MNG_CANVAS_BGR565  : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgr565;  break; }
+#endif
+#ifndef MNG_SKIPCANVAS_RGB565
+          case MNG_CANVAS_RGB565  : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_rgb565;  break; }
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+  /*        case MNG_CANVAS_RGB16   : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_rgb16;   break; } */
+  /*        case MNG_CANVAS_BGR16   : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgr16;   break; } */
+#endif
+  /*        case MNG_CANVAS_INDEX8  : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_index8;  break; } */
+  /*        case MNG_CANVAS_GRAY8   : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_gray8;   break; } */
+#ifndef MNG_NO_16BIT_SUPPORT
+  /*        case MNG_CANVAS_GRAY16  : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_gray16;  break; } */
+#endif
+  /*        case MNG_CANVAS_DX15    : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_dx15;    break; } */
+  /*        case MNG_CANVAS_DX16    : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_dx16;    break; } */
+        }
+      }
+
+#ifndef MNG_SKIPCHUNK_BACK
+      if (pData->bHasBACK)
+      {                                /* background image ? */
+        if ((pData->iBACKmandatory & 0x02) && (pData->iBACKimageid))
+        {
+          pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_backcolor;
+          bColorcorr          = MNG_TRUE;
+        }
+        else                           /* background color ? */
+        if (pData->iBACKmandatory & 0x01)
+        {
+          pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_backcolor;
+          bColorcorr          = MNG_TRUE;
+        }
+      }
+#endif
+
+      pData->fCorrectrow = MNG_NULL;   /* default no color-correction */
+
+      if (bColorcorr)                  /* do we have to do color-correction ? */
+      {
+#ifdef MNG_NO_CMS
+        iRetcode = MNG_NOERROR;
+#else
+#if defined(MNG_FULL_CMS)              /* determine color-management routine */
+        iRetcode = mng_init_full_cms   (pData, MNG_TRUE, MNG_FALSE, MNG_FALSE);
+#elif defined(MNG_GAMMA_ONLY)
+        iRetcode = mng_init_gamma_only (pData, MNG_TRUE, MNG_FALSE, MNG_FALSE);
+#elif defined(MNG_APP_CMS)
+        iRetcode = mng_init_app_cms    (pData, MNG_TRUE, MNG_FALSE, MNG_FALSE);
+#endif
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+#endif /* MNG_NO_CMS */
+      }
+                                       /* get a temporary row-buffer */
+      MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize);
+
+      iY       = pData->iDestt;        /* this is where we start */
+      iRetcode = MNG_NOERROR;          /* so far, so good */
+
+      while ((!iRetcode) && (iY < pData->iDestb))
+      {                                /* restore a background row */
+        iRetcode = ((mng_restbkgdrow)pData->fRestbkgdrow) (pData);
+                                       /* color correction ? */
+        if ((!iRetcode) && (pData->fCorrectrow))
+          iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData);
+
+        if (!iRetcode)                 /* so... display it */
+          iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData);
+
+        if (!iRetcode)
+          iRetcode = mng_next_row (pData);
+
+        iY++;                          /* and next line */
+      }
+                                       /* drop the temporary row-buffer */
+      MNG_FREE (pData, pData->pRGBArow, pData->iRowsize);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+
+#if defined(MNG_FULL_CMS)              /* cleanup cms stuff */
+      if (bColorcorr)                  /* did we do color-correction ? */
+      {
+        iRetcode = mng_clear_cms (pData);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+      }
+#endif
+#ifndef MNG_SKIPCHUNK_BACK
+                                       /* background image ? */
+      if ((pData->bHasBACK) && (pData->iBACKmandatory & 0x02) && (pData->iBACKimageid))
+      {
+        mng_imagep pImage;
+                                       /* let's find that object then */
+        pData->pRetrieveobj = mng_find_imageobject (pData, pData->iBACKimageid);
+        pImage              = (mng_imagep)pData->pRetrieveobj;
+                                       /* exists, viewable and visible ? */
+        if ((pImage) && (pImage->bViewable) && (pImage->bVisible))
+        {                              /* will it fall within the target region ? */
+          if ((pImage->iPosx < pData->iDestr) && (pImage->iPosy < pData->iDestb)             &&
+              ((pData->iBACKtile) ||
+               ((pImage->iPosx + (mng_int32)pImage->pImgbuf->iWidth  >= pData->iDestl) &&
+                (pImage->iPosy + (mng_int32)pImage->pImgbuf->iHeight >= pData->iDestt)    )) &&
+              ((!pImage->bClipped) ||
+               ((pImage->iClipl <= pImage->iClipr) && (pImage->iClipt <= pImage->iClipb)     &&
+                (pImage->iClipl < pData->iDestr)   && (pImage->iClipr >= pData->iDestl)      &&
+                (pImage->iClipt < pData->iDestb)   && (pImage->iClipb >= pData->iDestt)         )))
+          {                            /* right; we've got ourselves something to do */
+            if (pImage->bClipped)      /* clip output region with image's clipping region ? */
+            {
+              if (pImage->iClipl > pData->iDestl)
+                pData->iDestl = pImage->iClipl;
+              if (pImage->iClipr < pData->iDestr)
+                pData->iDestr = pImage->iClipr;
+              if (pImage->iClipt > pData->iDestt)
+                pData->iDestt = pImage->iClipt;
+              if (pImage->iClipb < pData->iDestb)
+                pData->iDestb = pImage->iClipb;
+            }
+                                       /* image offset does some extra clipping too ! */
+            if (pImage->iPosx > pData->iDestl)
+              pData->iDestl = pImage->iPosx;
+            if (pImage->iPosy > pData->iDestt)
+              pData->iDestt = pImage->iPosy;
+
+            if (!pData->iBACKtile)     /* without tiling further clipping is needed */
+            {
+              if (pImage->iPosx + (mng_int32)pImage->pImgbuf->iWidth  < pData->iDestr)
+                pData->iDestr = pImage->iPosx + (mng_int32)pImage->pImgbuf->iWidth;
+              if (pImage->iPosy + (mng_int32)pImage->pImgbuf->iHeight < pData->iDestb)
+                pData->iDestb = pImage->iPosy + (mng_int32)pImage->pImgbuf->iHeight;
+            }
+            
+            pData->iSourcel    = 0;    /* source relative to destination */
+            pData->iSourcer    = pData->iDestr - pData->iDestl;
+            pData->iSourcet    = 0;
+            pData->iSourceb    = pData->iDestb - pData->iDestt;
+                                       /* 16-bit background ? */
+
+#ifdef MNG_NO_16BIT_SUPPORT
+            pData->bIsRGBA16   = MNG_FALSE;
+#else
+            pData->bIsRGBA16      = (mng_bool)(pImage->pImgbuf->iBitdepth > 8);
+#endif
+                                       /* let restore routine know the offsets !!! */
+            pData->iBackimgoffsx  = pImage->iPosx;
+            pData->iBackimgoffsy  = pImage->iPosy;
+            pData->iBackimgwidth  = pImage->pImgbuf->iWidth;
+            pData->iBackimgheight = pImage->pImgbuf->iHeight;
+            pData->iRow           = 0; /* start at the top again !! */
+                                       /* determine background object retrieval routine */
+            switch (pImage->pImgbuf->iColortype)
+            {
+              case  0 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                          if (pImage->pImgbuf->iBitdepth > 8)
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_g16;
+                          else
+#endif
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_g8;
+
+                          pData->bIsOpaque      = (mng_bool)(!pImage->pImgbuf->bHasTRNS);
+                          break;
+                        }
+
+              case  2 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                          if (pImage->pImgbuf->iBitdepth > 8)
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16;
+                          else
+#endif
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8;
+
+                          pData->bIsOpaque      = (mng_bool)(!pImage->pImgbuf->bHasTRNS);
+                          break;
+                        }
+
+              case  3 : { pData->fRetrieverow   = (mng_fptr)mng_retrieve_idx8;
+                          pData->bIsOpaque      = (mng_bool)(!pImage->pImgbuf->bHasTRNS);
+                          break;
+                        }
+
+              case  4 : { 
+#ifndef MNG_NO_16BIT_SUPPORT
+			if (pImage->pImgbuf->iBitdepth > 8)
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16;
+                          else
+#endif
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8;
+
+                          pData->bIsOpaque      = MNG_FALSE;
+                          break;
+                        }
+
+              case  6 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                          if (pImage->pImgbuf->iBitdepth > 8)
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16;
+                          else
+#endif
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8;
+
+                          pData->bIsOpaque      = MNG_FALSE;
+                          break;
+                        }
+
+              case  8 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                          if (pImage->pImgbuf->iBitdepth > 8)
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_g16;
+                          else
+#endif
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_g8;
+
+                          pData->bIsOpaque      = MNG_TRUE;
+                          break;
+                        }
+
+              case 10 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                          if (pImage->pImgbuf->iBitdepth > 8)
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16;
+                          else
+#endif
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8;
+
+                          pData->bIsOpaque      = MNG_TRUE;
+                          break;
+                        }
+
+              case 12 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                          if (pImage->pImgbuf->iBitdepth > 8)
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16;
+                          else
+#endif
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8;
+
+                          pData->bIsOpaque      = MNG_FALSE;
+                          break;
+                        }
+
+              case 14 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                          if (pImage->pImgbuf->iBitdepth > 8)
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16;
+                          else
+#endif
+                            pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8;
+
+                          pData->bIsOpaque      = MNG_FALSE;
+                          break;
+                        }
+            }
+
+#ifdef MNG_NO_CMS
+            iRetcode = MNG_NOERROR;
+#else
+#if defined(MNG_FULL_CMS)              /* determine color-management routine */
+            iRetcode = mng_init_full_cms   (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#elif defined(MNG_GAMMA_ONLY)
+            iRetcode = mng_init_gamma_only (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#elif defined(MNG_APP_CMS)
+            iRetcode = mng_init_app_cms    (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#endif
+            if (iRetcode)              /* on error bail out */
+              return iRetcode;
+#endif /* MNG_NO_CMS */
+                                       /* get temporary row-buffers */
+            MNG_ALLOC (pData, pData->pPrevrow, pData->iRowsize);
+            MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize);
+
+            iY       = pData->iDestt;  /* this is where we start */
+            iRetcode = MNG_NOERROR;    /* so far, so good */
+
+            while ((!iRetcode) && (iY < pData->iDestb))
+            {                          /* restore a background row */
+              iRetcode = mng_restore_bkgd_backimage (pData);
+                                       /* color correction ? */
+              if ((!iRetcode) && (pData->fCorrectrow))
+                iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData);
+
+              if (!iRetcode)           /* so... display it */
+                iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData);
+
+              if (!iRetcode)
+                iRetcode = mng_next_row (pData);
+
+              iY++;                    /* and next line */
+            }
+                                       /* drop temporary row-buffers */
+            MNG_FREE (pData, pData->pRGBArow, pData->iRowsize);
+            MNG_FREE (pData, pData->pPrevrow, pData->iRowsize);
+
+            if (iRetcode)              /* on error bail out */
+              return iRetcode;
+
+#if defined(MNG_FULL_CMS)              /* cleanup cms stuff */
+            iRetcode = mng_clear_cms (pData);
+
+            if (iRetcode)              /* on error bail out */
+              return iRetcode;
+#endif
+          }
+        }
+      }
+#endif
+    }
+
+    pData->iDestl       = iDestl;      /* restore values */
+    pData->iDestr       = iDestr;
+    pData->iDestt       = iDestt;
+    pData->iDestb       = iDestb;
+    pData->iSourcel     = iSourcel;
+    pData->iSourcer     = iSourcer;
+    pData->iSourcet     = iSourcet;
+    pData->iSourceb     = iSourceb;
+    pData->iPass        = iPass;
+    pData->iRow         = iRow;
+    pData->iRowinc      = iRowinc;
+    pData->iCol         = iCol;
+    pData->iColinc      = iColinc;
+    pData->iRowsamples  = iRowsamples;
+    pData->iRowsize     = iRowsize;
+    pData->pPrevrow     = pPrevrow;
+    pData->pRGBArow     = pRGBArow;
+    pData->bIsRGBA16    = bIsRGBA16;
+    pData->bIsOpaque    = bIsOpaque;
+    pData->fCorrectrow  = fCorrectrow;
+    pData->fDisplayrow  = fDisplayrow; 
+    pData->fRetrieverow = fRetrieverow;
+    pData->pCurrentobj  = pCurrentobj;
+    pData->pRetrieveobj = pRetrieveobj;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_LOAD_BKGDLAYER, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode clear_canvas (mng_datap pData)
+{
+  mng_int32   iY;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLEAR_CANVAS, MNG_LC_START);
+#endif
+
+  pData->iDestl      = 0;              /* clipping region is full canvas! */
+  pData->iDestt      = 0;
+  pData->iDestr      = pData->iWidth;
+  pData->iDestb      = pData->iHeight;
+
+  pData->iSourcel    = 0;              /* source is same as destination */
+  pData->iSourcer    = pData->iWidth;
+  pData->iSourcet    = 0;
+  pData->iSourceb    = pData->iHeight;
+
+  pData->iPass       = -1;             /* these are the object's dimensions now */
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iWidth;
+  pData->iRowsize    = pData->iRowsamples << 2;
+  pData->bIsRGBA16   = MNG_FALSE;      /* let's keep it simple ! */
+  pData->bIsOpaque   = MNG_TRUE;
+
+  set_display_routine (pData);         /* determine display routine */
+                                       /* get a temporary row-buffer */
+                                       /* it's transparent black by default!! */
+  MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize);
+
+  iY       = pData->iDestt;            /* this is where we start */
+  iRetcode = MNG_NOERROR;              /* so far, so good */
+
+  while ((!iRetcode) && (iY < pData->iDestb))
+  {                                    /* clear a row then */
+    iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData);
+
+    if (!iRetcode)
+      iRetcode = mng_next_row (pData); /* adjust variables for next row */
+
+    iY++;                              /* and next line */
+  }
+                                       /* drop the temporary row-buffer */
+  MNG_FREE (pData, pData->pRGBArow, pData->iRowsize);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLEAR_CANVAS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode next_frame (mng_datap  pData,
+                                  mng_uint8  iFramemode,
+                                  mng_uint8  iChangedelay,
+                                  mng_uint32 iDelay,
+                                  mng_uint8  iChangetimeout,
+                                  mng_uint32 iTimeout,
+                                  mng_uint8  iChangeclipping,
+                                  mng_uint8  iCliptype,
+                                  mng_int32  iClipl,
+                                  mng_int32  iClipr,
+                                  mng_int32  iClipt,
+                                  mng_int32  iClipb)
+{
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_FRAME, MNG_LC_START);
+#endif
+
+  if (!pData->iBreakpoint)             /* no previous break here ? */
+  {
+#ifndef MNG_SKIPCHUNK_FRAM
+    mng_uint8 iOldmode = pData->iFramemode;
+                                       /* interframe delay required ? */
+    if ((iOldmode == 2) || (iOldmode == 4))
+    {
+      if ((pData->iFrameseq) && (iFramemode != 1) && (iFramemode != 3))
+        iRetcode = interframe_delay (pData);
+      else
+        pData->iFramedelay = pData->iNextdelay;
+    }
+    else
+    {                                  /* delay before inserting background layer? */
+      if ((pData->bFramedone) && (iFramemode == 4))
+        iRetcode = interframe_delay (pData);
+    }
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* now we'll assume we're in the next frame! */
+    if (iFramemode)                    /* save the new framing mode ? */
+    {
+      pData->iFRAMmode  = iFramemode;
+      pData->iFramemode = iFramemode;
+    }
+    else                               /* reload default */
+      pData->iFramemode = pData->iFRAMmode;
+
+    if (iChangedelay)                  /* delay changed ? */
+    {
+      pData->iNextdelay = iDelay;      /* for *after* next subframe */
+
+      if ((iOldmode == 2) || (iOldmode == 4))
+        pData->iFramedelay = pData->iFRAMdelay;
+
+      if (iChangedelay == 2)           /* also overall ? */
+        pData->iFRAMdelay = iDelay;
+    }
+    else
+    {                                  /* reload default */
+      pData->iNextdelay = pData->iFRAMdelay;
+    }
+
+    if (iChangetimeout)                /* timeout changed ? */
+    {                                  /* for next subframe */
+      pData->iFrametimeout = iTimeout;
+
+      if ((iChangetimeout == 2) ||     /* also overall ? */
+          (iChangetimeout == 4) ||
+          (iChangetimeout == 6) ||
+          (iChangetimeout == 8))
+        pData->iFRAMtimeout = iTimeout;
+    }
+    else                               /* reload default */
+      pData->iFrametimeout = pData->iFRAMtimeout;
+
+    if (iChangeclipping)               /* clipping changed ? */
+    {
+      pData->bFrameclipping = MNG_TRUE;
+
+      if (!iCliptype)                  /* absolute ? */
+      {
+        pData->iFrameclipl = iClipl;
+        pData->iFrameclipr = iClipr;
+        pData->iFrameclipt = iClipt;
+        pData->iFrameclipb = iClipb;
+      }
+      else                             /* relative */
+      {
+        pData->iFrameclipl = pData->iFrameclipl + iClipl;
+        pData->iFrameclipr = pData->iFrameclipr + iClipr;
+        pData->iFrameclipt = pData->iFrameclipt + iClipt;
+        pData->iFrameclipb = pData->iFrameclipb + iClipb;
+      }
+
+      if (iChangeclipping == 2)        /* also overall ? */
+      {
+        pData->bFRAMclipping = MNG_TRUE;
+
+        if (!iCliptype)                /* absolute ? */
+        {
+          pData->iFRAMclipl = iClipl;
+          pData->iFRAMclipr = iClipr;
+          pData->iFRAMclipt = iClipt;
+          pData->iFRAMclipb = iClipb;
+        }
+        else                           /* relative */
+        {
+          pData->iFRAMclipl = pData->iFRAMclipl + iClipl;
+          pData->iFRAMclipr = pData->iFRAMclipr + iClipr;
+          pData->iFRAMclipt = pData->iFRAMclipt + iClipt;
+          pData->iFRAMclipb = pData->iFRAMclipb + iClipb;
+        }
+      }
+    }
+    else
+    {                                  /* reload defaults */
+      pData->bFrameclipping = pData->bFRAMclipping;
+      pData->iFrameclipl    = pData->iFRAMclipl;
+      pData->iFrameclipr    = pData->iFRAMclipr;
+      pData->iFrameclipt    = pData->iFRAMclipt;
+      pData->iFrameclipb    = pData->iFRAMclipb;
+    }
+#endif
+  }
+
+  if (!pData->bTimerset)               /* timer still off ? */
+  {
+    if (
+#ifndef MNG_SKIPCHUNK_FRAM
+       (pData->iFramemode == 4) ||    /* insert background layer after a new frame */
+#endif
+        (!pData->iLayerseq))           /* and certainly before the very first layer */
+      iRetcode = load_bkgdlayer (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    pData->iFrameseq++;                /* count the frame ! */
+    pData->bFramedone = MNG_TRUE;      /* and indicate we've done one */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_FRAME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode next_layer (mng_datap pData)
+{
+  mng_imagep  pImage;
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_LAYER, MNG_LC_START);
+#endif
+
+#ifndef MNG_SKIPCHUNK_FRAM
+  if (!pData->iBreakpoint)             /* no previous break here ? */
+  {                                    /* interframe delay required ? */
+    if ((pData->eImagetype == mng_it_mng) && (pData->iLayerseq) &&
+        ((pData->iFramemode == 1) || (pData->iFramemode == 3)))
+      iRetcode = interframe_delay (pData);
+    else
+      pData->iFramedelay = pData->iNextdelay;
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif
+
+  if (!pData->bTimerset)               /* timer still off ? */
+  {
+    if (!pData->iLayerseq)             /* restore background for the very first layer ? */
+    {                                  /* wait till IDAT/JDAT for PNGs & JNGs !!! */
+      if ((pData->eImagetype == mng_it_png) || (pData->eImagetype == mng_it_jng))
+        pData->bRestorebkgd = MNG_TRUE;
+      else
+      {                                /* for MNG we do it right away */
+        iRetcode = load_bkgdlayer (pData);
+        pData->iLayerseq++;            /* and it counts as a layer then ! */
+      }
+    }
+#ifndef MNG_SKIPCHUNK_FRAM
+    else
+    if (pData->iFramemode == 3)        /* restore background for each layer ? */
+      iRetcode = load_bkgdlayer (pData);
+#endif
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* processing a delta-image ? */
+      pImage = (mng_imagep)pData->pDeltaImage;
+    else
+#endif
+      pImage = (mng_imagep)pData->pCurrentobj;
+
+    if (!pImage)                       /* not an active object ? */
+      pImage = (mng_imagep)pData->pObjzero;
+                                       /* determine display rectangle */
+    pData->iDestl   = MAX_COORD ((mng_int32)0,   pImage->iPosx);
+    pData->iDestt   = MAX_COORD ((mng_int32)0,   pImage->iPosy);
+                                       /* is it a valid buffer ? */
+    if ((pImage->pImgbuf->iWidth) && (pImage->pImgbuf->iHeight))
+    {
+      pData->iDestr = MIN_COORD ((mng_int32)pData->iWidth,
+                                 pImage->iPosx + (mng_int32)pImage->pImgbuf->iWidth );
+      pData->iDestb = MIN_COORD ((mng_int32)pData->iHeight,
+                                 pImage->iPosy + (mng_int32)pImage->pImgbuf->iHeight);
+    }
+    else                               /* it's a single image ! */
+    {
+      pData->iDestr = MIN_COORD ((mng_int32)pData->iWidth,
+                                 (mng_int32)pData->iDatawidth );
+      pData->iDestb = MIN_COORD ((mng_int32)pData->iHeight,
+                                 (mng_int32)pData->iDataheight);
+    }
+
+#ifndef MNG_SKIPCHUNK_FRAM
+    if (pData->bFrameclipping)         /* frame clipping specified ? */
+    {
+      pData->iDestl = MAX_COORD (pData->iDestl,  pData->iFrameclipl);
+      pData->iDestt = MAX_COORD (pData->iDestt,  pData->iFrameclipt);
+      pData->iDestr = MIN_COORD (pData->iDestr,  pData->iFrameclipr);
+      pData->iDestb = MIN_COORD (pData->iDestb,  pData->iFrameclipb);
+    }
+#endif
+
+    if (pImage->bClipped)              /* is the image clipped itself ? */
+    {
+      pData->iDestl = MAX_COORD (pData->iDestl,  pImage->iClipl);
+      pData->iDestt = MAX_COORD (pData->iDestt,  pImage->iClipt);
+      pData->iDestr = MIN_COORD (pData->iDestr,  pImage->iClipr);
+      pData->iDestb = MIN_COORD (pData->iDestb,  pImage->iClipb);
+    }
+                                       /* determine source starting point */
+    pData->iSourcel = MAX_COORD ((mng_int32)0,   pData->iDestl - pImage->iPosx);
+    pData->iSourcet = MAX_COORD ((mng_int32)0,   pData->iDestt - pImage->iPosy);
+
+    if ((pImage->pImgbuf->iWidth) && (pImage->pImgbuf->iHeight))
+    {                                  /* and maximum size  */
+      pData->iSourcer = MIN_COORD ((mng_int32)pImage->pImgbuf->iWidth,
+                                   pData->iSourcel + pData->iDestr - pData->iDestl);
+      pData->iSourceb = MIN_COORD ((mng_int32)pImage->pImgbuf->iHeight,
+                                   pData->iSourcet + pData->iDestb - pData->iDestt);
+    }
+    else                               /* it's a single image ! */
+    {
+      pData->iSourcer = pData->iSourcel + pData->iDestr - pData->iDestl;
+      pData->iSourceb = pData->iSourcet + pData->iDestb - pData->iDestt;
+    }
+
+    pData->iLayerseq++;                /* count the layer ! */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_LAYER, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_display_image (mng_datap  pData,
+                               mng_imagep pImage,
+                               mng_bool   bLayeradvanced)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_IMAGE, MNG_LC_START);
+#endif
+                                       /* actively running ? */
+#ifndef MNG_SKIPCHUNK_MAGN
+  if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping))
+  {
+    if ( (!pData->iBreakpoint) &&      /* needs magnification ? */
+         ( (pImage->iMAGN_MethodX) || (pImage->iMAGN_MethodY) ) )
+    {
+      iRetcode = mng_magnify_imageobject (pData, pImage);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+  }
+#endif
+
+  pData->pRetrieveobj = pImage;        /* so retrieve-row and color-correction can find it */
+
+  if (!bLayeradvanced)                 /* need to advance the layer ? */
+  {
+    mng_imagep pSave    = pData->pCurrentobj;
+    pData->pCurrentobj  = pImage;
+    next_layer (pData);                /* advance to next layer */
+    pData->pCurrentobj  = pSave;
+  }
+                                       /* need to restore the background ? */
+  if ((!pData->bTimerset) && (pData->bRestorebkgd))
+  {
+    mng_imagep pSave    = pData->pCurrentobj;
+    pData->pCurrentobj  = pImage;
+    pData->bRestorebkgd = MNG_FALSE;
+    iRetcode            = load_bkgdlayer (pData);
+    pData->pCurrentobj  = pSave;
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    pData->iLayerseq++;                /* and it counts as a layer then ! */
+  }
+                                       /* actively running ? */
+  if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping))
+  {
+    if (!pData->bTimerset)             /* all systems still go ? */
+    {
+      pData->iBreakpoint = 0;          /* let's make absolutely sure... */
+                                       /* anything to display ? */
+      if ((pData->iDestr >= pData->iDestl) && (pData->iDestb >= pData->iDestt))
+      {
+        mng_int32 iY;
+
+        set_display_routine (pData);   /* determine display routine */
+                                       /* and image-buffer retrieval routine */
+        switch (pImage->pImgbuf->iColortype)
+        {
+          case  0 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                      if (pImage->pImgbuf->iBitdepth > 8)
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_g16;
+                      else
+#endif
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_g8;
+
+                      pData->bIsOpaque      = (mng_bool)(!pImage->pImgbuf->bHasTRNS);
+                      break;
+                    }
+
+          case  2 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                      if (pImage->pImgbuf->iBitdepth > 8)
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16;
+                      else
+#endif
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8;
+
+                      pData->bIsOpaque      = (mng_bool)(!pImage->pImgbuf->bHasTRNS);
+                      break;
+                    }
+
+
+          case  3 : { pData->fRetrieverow   = (mng_fptr)mng_retrieve_idx8;
+                      pData->bIsOpaque      = (mng_bool)(!pImage->pImgbuf->bHasTRNS);
+                      break;
+                    }
+
+
+          case  4 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                      if (pImage->pImgbuf->iBitdepth > 8)
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16;
+                      else
+#endif
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8;
+
+                      pData->bIsOpaque      = MNG_FALSE;
+                      break;
+                    }
+
+
+          case  6 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                      if (pImage->pImgbuf->iBitdepth > 8)
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16;
+                      else
+#endif
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8;
+
+                      pData->bIsOpaque      = MNG_FALSE;
+                      break;
+                    }
+
+          case  8 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                      if (pImage->pImgbuf->iBitdepth > 8)
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_g16;
+                      else
+#endif
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_g8;
+
+                      pData->bIsOpaque      = MNG_TRUE;
+                      break;
+                    }
+
+          case 10 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                      if (pImage->pImgbuf->iBitdepth > 8)
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16;
+                      else
+#endif
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8;
+
+                      pData->bIsOpaque      = MNG_TRUE;
+                      break;
+                    }
+
+
+          case 12 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                      if (pImage->pImgbuf->iBitdepth > 8)
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16;
+                      else
+#endif
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8;
+
+                      pData->bIsOpaque      = MNG_FALSE;
+                      break;
+                    }
+
+
+          case 14 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                      if (pImage->pImgbuf->iBitdepth > 8)
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16;
+                      else
+#endif
+                        pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8;
+
+                      pData->bIsOpaque      = MNG_FALSE;
+                      break;
+                    }
+
+        }
+
+        pData->iPass       = -1;       /* these are the object's dimensions now */
+        pData->iRow        = pData->iSourcet;
+        pData->iRowinc     = 1;
+        pData->iCol        = 0;
+        pData->iColinc     = 1;
+        pData->iRowsamples = pImage->pImgbuf->iWidth;
+        pData->iRowsize    = pData->iRowsamples << 2;
+        pData->bIsRGBA16   = MNG_FALSE;
+                                       /* adjust for 16-bit object ? */
+#ifndef MNG_NO_16BIT_SUPPORT
+        if (pImage->pImgbuf->iBitdepth > 8)
+        {
+          pData->bIsRGBA16 = MNG_TRUE;
+          pData->iRowsize  = pData->iRowsamples << 3;
+        }
+#endif
+
+        pData->fCorrectrow = MNG_NULL; /* default no color-correction */
+
+#ifdef MNG_NO_CMS
+        iRetcode = MNG_NOERROR;
+#else
+#if defined(MNG_FULL_CMS)              /* determine color-management routine */
+        iRetcode = mng_init_full_cms   (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#elif defined(MNG_GAMMA_ONLY)
+        iRetcode = mng_init_gamma_only (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#elif defined(MNG_APP_CMS)
+        iRetcode = mng_init_app_cms    (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#endif
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+#endif /* MNG_NO_CMS */
+                                       /* get a temporary row-buffer */
+        MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize);
+
+        iY = pData->iSourcet;          /* this is where we start */
+
+        while ((!iRetcode) && (iY < pData->iSourceb))
+        {                              /* get a row */
+          iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData);
+                                       /* color correction ? */
+          if ((!iRetcode) && (pData->fCorrectrow))
+            iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData);
+
+          if (!iRetcode)               /* so... display it */
+            iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData);
+
+          if (!iRetcode)               /* adjust variables for next row */
+            iRetcode = mng_next_row (pData);
+
+          iY++;                        /* and next line */
+        }
+                                       /* drop the temporary row-buffer */
+        MNG_FREE (pData, pData->pRGBArow, pData->iRowsize);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+
+#if defined(MNG_FULL_CMS)              /* cleanup cms stuff */
+        iRetcode = mng_clear_cms (pData);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+#endif
+      }
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_IMAGE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* whehehe, this is good ! */
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_execute_delta_image (mng_datap  pData,
+                                     mng_imagep pTarget,
+                                     mng_imagep pDelta)
+{
+  mng_imagedatap pBuftarget = pTarget->pImgbuf;
+  mng_imagedatap pBufdelta  = pDelta->pImgbuf;
+  mng_uint32     iY;
+  mng_retcode    iRetcode;
+  mng_ptr        pSaveRGBA;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_EXECUTE_DELTA_IMAGE, MNG_LC_START);
+#endif
+                                       /* actively running ? */
+  if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping))
+  {
+    if (pBufdelta->bHasPLTE)           /* palette in delta ? */
+    {
+      mng_uint32 iX;
+                                       /* new palette larger than old one ? */
+      if ((!pBuftarget->bHasPLTE) || (pBuftarget->iPLTEcount < pBufdelta->iPLTEcount))
+        pBuftarget->iPLTEcount = pBufdelta->iPLTEcount;
+                                       /* it's definitely got a PLTE now */
+      pBuftarget->bHasPLTE = MNG_TRUE;
+
+      for (iX = 0; iX < pBufdelta->iPLTEcount; iX++)
+      {
+        pBuftarget->aPLTEentries[iX].iRed   = pBufdelta->aPLTEentries[iX].iRed;
+        pBuftarget->aPLTEentries[iX].iGreen = pBufdelta->aPLTEentries[iX].iGreen;
+        pBuftarget->aPLTEentries[iX].iBlue  = pBufdelta->aPLTEentries[iX].iBlue;
+      }
+    }
+
+    if (pBufdelta->bHasTRNS)           /* cheap transparency in delta ? */
+    {
+      switch (pData->iColortype)       /* drop it into the target */
+      {
+        case 0: {                      /* gray */
+                  pBuftarget->iTRNSgray  = pBufdelta->iTRNSgray;
+                  pBuftarget->iTRNSred   = 0;
+                  pBuftarget->iTRNSgreen = 0;
+                  pBuftarget->iTRNSblue  = 0;
+                  pBuftarget->iTRNScount = 0;
+                  break;
+                }
+        case 2: {                      /* rgb */
+                  pBuftarget->iTRNSgray  = 0;
+                  pBuftarget->iTRNSred   = pBufdelta->iTRNSred;
+                  pBuftarget->iTRNSgreen = pBufdelta->iTRNSgreen;
+                  pBuftarget->iTRNSblue  = pBufdelta->iTRNSblue;
+                  pBuftarget->iTRNScount = 0;
+                  break;
+                }
+        case 3: {                      /* indexed */
+                  pBuftarget->iTRNSgray  = 0;
+                  pBuftarget->iTRNSred   = 0;
+                  pBuftarget->iTRNSgreen = 0;
+                  pBuftarget->iTRNSblue  = 0;
+                                       /* existing range smaller than new one ? */
+                  if ((!pBuftarget->bHasTRNS) || (pBuftarget->iTRNScount < pBufdelta->iTRNScount))
+                    pBuftarget->iTRNScount = pBufdelta->iTRNScount;
+
+                  MNG_COPY (pBuftarget->aTRNSentries, pBufdelta->aTRNSentries, pBufdelta->iTRNScount);
+                  break;
+                }
+      }
+
+      pBuftarget->bHasTRNS = MNG_TRUE; /* tell it it's got a tRNS now */
+    }
+
+#ifndef MNG_SKIPCHUNK_bKGD
+    if (pBufdelta->bHasBKGD)           /* bkgd in source ? */
+    {                                  /* drop it onto the target */
+      pBuftarget->bHasBKGD   = MNG_TRUE;
+      pBuftarget->iBKGDindex = pBufdelta->iBKGDindex;
+      pBuftarget->iBKGDgray  = pBufdelta->iBKGDgray;
+      pBuftarget->iBKGDred   = pBufdelta->iBKGDred;
+      pBuftarget->iBKGDgreen = pBufdelta->iBKGDgreen;
+      pBuftarget->iBKGDblue  = pBufdelta->iBKGDblue;
+    }
+#endif
+
+    if (pBufdelta->bHasGAMA)           /* gamma in source ? */
+    {
+      pBuftarget->bHasGAMA = MNG_TRUE; /* drop it onto the target */
+      pBuftarget->iGamma   = pBufdelta->iGamma;
+    }
+
+#ifndef MNG_SKIPCHUNK_cHRM
+    if (pBufdelta->bHasCHRM)           /* chroma in delta ? */
+    {                                  /* drop it onto the target */
+      pBuftarget->bHasCHRM       = MNG_TRUE;
+      pBuftarget->iWhitepointx   = pBufdelta->iWhitepointx;
+      pBuftarget->iWhitepointy   = pBufdelta->iWhitepointy;
+      pBuftarget->iPrimaryredx   = pBufdelta->iPrimaryredx;
+      pBuftarget->iPrimaryredy   = pBufdelta->iPrimaryredy;
+      pBuftarget->iPrimarygreenx = pBufdelta->iPrimarygreenx;
+      pBuftarget->iPrimarygreeny = pBufdelta->iPrimarygreeny;
+      pBuftarget->iPrimarybluex  = pBufdelta->iPrimarybluex;
+      pBuftarget->iPrimarybluey  = pBufdelta->iPrimarybluey;
+    }
+#endif
+
+#ifndef MNG_SKIPCHUNK_sRGB
+    if (pBufdelta->bHasSRGB)           /* sRGB in delta ? */
+    {                                  /* drop it onto the target */
+      pBuftarget->bHasSRGB         = MNG_TRUE;
+      pBuftarget->iRenderingintent = pBufdelta->iRenderingintent;
+    }
+#endif
+
+#ifndef MNG_SKIPCHUNK_iCCP
+    if (pBufdelta->bHasICCP)           /* ICC profile in delta ? */
+    {
+      pBuftarget->bHasICCP = MNG_TRUE; /* drop it onto the target */
+
+      if (pBuftarget->pProfile)        /* profile existed ? */
+        MNG_FREEX (pData, pBuftarget->pProfile, pBuftarget->iProfilesize);
+                                       /* allocate a buffer & copy it */
+      MNG_ALLOC (pData, pBuftarget->pProfile, pBufdelta->iProfilesize);
+      MNG_COPY  (pBuftarget->pProfile, pBufdelta->pProfile, pBufdelta->iProfilesize);
+                                       /* store its length as well */
+      pBuftarget->iProfilesize = pBufdelta->iProfilesize;
+    }
+#endif
+                                       /* need to execute delta pixels ? */
+    if ((!pData->bDeltaimmediate) && (pData->iDeltatype != MNG_DELTATYPE_NOCHANGE))
+    {
+      pData->fScalerow = MNG_NULL;     /* not needed by default */
+
+      switch (pBufdelta->iBitdepth)    /* determine scaling routine */
+      {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+        case  1 : {
+                    switch (pBuftarget->iBitdepth)
+                    {
+                      case  2 : { pData->fScalerow = (mng_fptr)mng_scale_g1_g2;  break; }
+                      case  4 : { pData->fScalerow = (mng_fptr)mng_scale_g1_g4;  break; }
+
+                      case  8 : { pData->fScalerow = (mng_fptr)mng_scale_g1_g8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                      case 16 : { pData->fScalerow = (mng_fptr)mng_scale_g1_g16; break; }
+#endif
+                    }
+                    break;
+                  }
+
+        case  2 : {
+                    switch (pBuftarget->iBitdepth)
+                    {
+                      case  1 : { pData->fScalerow = (mng_fptr)mng_scale_g2_g1;  break; }
+                      case  4 : { pData->fScalerow = (mng_fptr)mng_scale_g2_g4;  break; }
+                      case  8 : { pData->fScalerow = (mng_fptr)mng_scale_g2_g8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                      case 16 : { pData->fScalerow = (mng_fptr)mng_scale_g2_g16; break; }
+#endif
+                    }
+                    break;
+                  }
+
+        case  4 : {
+                    switch (pBuftarget->iBitdepth)
+                    {
+                      case  1 : { pData->fScalerow = (mng_fptr)mng_scale_g4_g1;  break; }
+                      case  2 : { pData->fScalerow = (mng_fptr)mng_scale_g4_g2;  break; }
+                      case  8 : { pData->fScalerow = (mng_fptr)mng_scale_g4_g8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                      case 16 : { pData->fScalerow = (mng_fptr)mng_scale_g4_g16; break; }
+#endif
+                    }
+                    break;
+                  }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+
+        case  8 : {
+                    switch (pBufdelta->iColortype)
+                    {
+                      case  0 : ;
+                      case  3 : ;
+                      case  8 : {
+                                  switch (pBuftarget->iBitdepth)
+                                  {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                                    case  1 : { pData->fScalerow = (mng_fptr)mng_scale_g8_g1;  break; }
+                                    case  2 : { pData->fScalerow = (mng_fptr)mng_scale_g8_g2;  break; }
+                                    case  4 : { pData->fScalerow = (mng_fptr)mng_scale_g8_g4;  break; }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+#ifndef MNG_NO_16BIT_SUPPORT
+                                    case 16 : { pData->fScalerow = (mng_fptr)mng_scale_g8_g16; break; }
+#endif
+                                  }
+                                  break;
+                                }
+                      case  2 : ;
+                      case 10 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                                  if (pBuftarget->iBitdepth == 16)
+                                    pData->fScalerow = (mng_fptr)mng_scale_rgb8_rgb16;
+#endif
+                                  break;
+                                }
+                      case  4 : ;
+                      case 12 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                                  if (pBuftarget->iBitdepth == 16)
+                                    pData->fScalerow = (mng_fptr)mng_scale_ga8_ga16;
+#endif
+                                  break;
+                                }
+                      case  6 : ;
+                      case 14 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                                  if (pBuftarget->iBitdepth == 16)
+                                    pData->fScalerow = (mng_fptr)mng_scale_rgba8_rgba16;
+#endif
+                                  break;
+                                }
+                    }
+                    break;
+                  }
+
+#ifndef MNG_NO_16BIT_SUPPORT
+        case 16 : {
+                    switch (pBufdelta->iColortype)
+                    {
+                      case  0 : ;
+                      case  3 : ;
+                      case  8 : {
+                                  switch (pBuftarget->iBitdepth)
+                                  {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                                    case 1 : { pData->fScalerow = (mng_fptr)mng_scale_g16_g1; break; }
+                                    case 2 : { pData->fScalerow = (mng_fptr)mng_scale_g16_g2; break; }
+                                    case 4 : { pData->fScalerow = (mng_fptr)mng_scale_g16_g4; break; }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                                    case 8 : { pData->fScalerow = (mng_fptr)mng_scale_g16_g8; break; }
+                                  }
+                                  break;
+                                }
+                      case  2 : ;
+                      case 10 : {
+                                  if (pBuftarget->iBitdepth == 8)
+                                    pData->fScalerow = (mng_fptr)mng_scale_rgb16_rgb8;
+                                  break;
+                                }
+                      case  4 : ;
+                      case 12 : {
+                                  if (pBuftarget->iBitdepth == 8)
+                                    pData->fScalerow = (mng_fptr)mng_scale_ga16_ga8;
+                                  break;
+                                }
+                      case  6 : ;
+                      case 14 : {
+                                  if (pBuftarget->iBitdepth == 8)
+                                    pData->fScalerow = (mng_fptr)mng_scale_rgba16_rgba8;
+                                  break;
+                                }
+                    }
+                    break;
+                  }
+#endif
+
+      }
+
+      pData->fDeltarow = MNG_NULL;     /* let's assume there's nothing to do */
+
+      switch (pBuftarget->iColortype)  /* determine delta processing routine */
+      {
+        case  0 : ;
+        case  8 : {                     /* gray */
+                    if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD    ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+                    {
+                      if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3) ||
+                          (pBufdelta->iColortype == 8))
+                      {
+                        switch (pBuftarget->iBitdepth)
+                        {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                          case  1 : { pData->fDeltarow = (mng_fptr)mng_delta_g1_g1;   break; }
+                          case  2 : { pData->fDeltarow = (mng_fptr)mng_delta_g2_g2;   break; }
+                          case  4 : { pData->fDeltarow = (mng_fptr)mng_delta_g4_g4;   break; }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                          case  8 : { pData->fDeltarow = (mng_fptr)mng_delta_g8_g8;   break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                          case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_g16_g16; break; }
+#endif
+                        }
+                      }
+                    }
+
+                    break;
+                  }
+
+        case  2 : ;
+        case 10 : {                     /* rgb */
+                    if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD    ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+                    {
+                      if ((pBufdelta->iColortype == 2) || (pBufdelta->iColortype == 10))
+                      {
+                        switch (pBuftarget->iBitdepth)
+                        {
+                          case  8 : { pData->fDeltarow = (mng_fptr)mng_delta_rgb8_rgb8;   break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                          case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_rgb16_rgb16; break; }
+#endif
+                        }
+                      }
+                    }
+
+                    break;
+                  }
+
+        case  3 : {                     /* indexed; abuse gray routines */
+                    if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD    ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+                    {
+                      if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3))
+                      {
+                        switch (pBuftarget->iBitdepth)
+                        {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                          case  1 : { pData->fDeltarow = (mng_fptr)mng_delta_g1_g1; break; }
+                          case  2 : { pData->fDeltarow = (mng_fptr)mng_delta_g2_g2; break; }
+                          case  4 : { pData->fDeltarow = (mng_fptr)mng_delta_g4_g4; break; }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                          case  8 : { pData->fDeltarow = (mng_fptr)mng_delta_g8_g8; break; }
+                        }
+                      }
+                    }
+
+                    break;
+                  }
+
+        case  4 : ;
+        case 12 : {                     /* gray + alpha */
+                    if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD    ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+                    {
+                      if ((pBufdelta->iColortype == 4) || (pBufdelta->iColortype == 12))
+                      {
+                        switch (pBuftarget->iBitdepth)
+                        {
+                          case  8 : { pData->fDeltarow = (mng_fptr)mng_delta_ga8_ga8;   break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                          case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_ga16_ga16; break; }
+#endif
+                        }
+                      }
+                    }
+                    else
+                    if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD    ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)    )
+                    {
+                      if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3) ||
+                          (pBufdelta->iColortype == 8))
+                      {
+                        switch (pBuftarget->iBitdepth)
+                        {
+                          case  8 : { pData->fDeltarow = (mng_fptr)mng_delta_ga8_g8;   break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                          case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_ga16_g16; break; }
+#endif
+                        }
+                      }
+                    }
+                    else
+                    if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD    ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)    )
+                    {
+                      if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3))
+                      {
+                        switch (pBuftarget->iBitdepth)
+                        {
+                          case  8 : { pData->fDeltarow = (mng_fptr)mng_delta_ga8_a8;   break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                          case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_ga16_a16; break; }
+#endif
+                        }
+                      }
+                    }
+
+                    break;
+                  }
+
+        case  6 : ;
+        case 14 : {                     /* rgb + alpha */
+                    if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD    ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+                    {
+                      if ((pBufdelta->iColortype == 6) || (pBufdelta->iColortype == 14))
+                      {
+                        switch (pBuftarget->iBitdepth)
+                        {
+                          case  8 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba8_rgba8;   break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                          case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba16_rgba16; break; }
+#endif
+                        }
+                      }
+                    }
+                    else
+                    if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD    ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)    )
+                    {
+                      if ((pBufdelta->iColortype == 2) || (pBufdelta->iColortype == 10))
+                      {
+                        switch (pBuftarget->iBitdepth)
+                        {
+                          case  8 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba8_rgb8;   break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                          case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba16_rgb16; break; }
+#endif
+                        }
+                      }
+                    }
+                    else
+                    if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD    ) ||
+                        (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)    )
+                    {
+                      if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3))
+                      {
+                        switch (pBuftarget->iBitdepth)
+                        {
+                          case  8 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba8_a8;   break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                          case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba16_a16; break; }
+#endif
+                        }
+                      }
+                    }
+
+                    break;
+                  }
+
+      }
+
+      if (pData->fDeltarow)            /* do we need to take action ? */
+      {
+        pData->iPass        = -1;      /* setup row dimensions and stuff */
+        pData->iRow         = pData->iDeltaBlocky;
+        pData->iRowinc      = 1;
+        pData->iCol         = pData->iDeltaBlockx;
+        pData->iColinc      = 1;
+        pData->iRowsamples  = pBufdelta->iWidth;
+        pData->iRowsize     = pBuftarget->iRowsize;
+                                       /* indicate where to retrieve & where to store */
+        pData->pRetrieveobj = (mng_objectp)pDelta;
+        pData->pStoreobj    = (mng_objectp)pTarget;
+
+        pSaveRGBA = pData->pRGBArow;   /* save current temp-buffer! */
+                                       /* get a temporary row-buffer */
+        MNG_ALLOC (pData, pData->pRGBArow, (pBufdelta->iRowsize << 1));
+
+        iY       = 0;                  /* this is where we start */
+        iRetcode = MNG_NOERROR;        /* still oke for now */
+
+        while ((!iRetcode) && (iY < pBufdelta->iHeight))
+        {                              /* get a row */
+          mng_uint8p pWork = pBufdelta->pImgdata + (iY * pBufdelta->iRowsize);
+
+          MNG_COPY (pData->pRGBArow, pWork, pBufdelta->iRowsize);
+
+          if (pData->fScalerow)        /* scale it (if necessary) */
+            iRetcode = ((mng_scalerow)pData->fScalerow) (pData);
+
+          if (!iRetcode)               /* and... execute it */
+            iRetcode = ((mng_deltarow)pData->fDeltarow) (pData);
+
+          if (!iRetcode)               /* adjust variables for next row */
+            iRetcode = mng_next_row (pData);
+
+          iY++;                        /* and next line */
+        }
+                                       /* drop the temporary row-buffer */
+        MNG_FREE (pData, pData->pRGBArow, (pBufdelta->iRowsize << 1));
+        pData->pRGBArow = pSaveRGBA;   /* restore saved temp-buffer! */
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+
+      }
+      else
+        MNG_ERROR (pData, MNG_INVALIDDELTA);
+
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_EXECUTE_DELTA_IMAGE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_DELTA_PNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+MNG_LOCAL mng_retcode save_state (mng_datap pData)
+{
+  mng_savedatap pSave;
+  mng_imagep    pImage;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SAVE_STATE, MNG_LC_START);
+#endif
+
+  if (pData->pSavedata)                /* sanity check */
+    MNG_ERROR (pData, MNG_INTERNALERROR);
+                                       /* get a buffer for saving */
+  MNG_ALLOC (pData, pData->pSavedata, sizeof (mng_savedata));
+
+  pSave = pData->pSavedata;            /* address it more directly */
+                                       /* and copy global data from the main struct */
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+  pSave->bHasglobalPLTE       = pData->bHasglobalPLTE;
+  pSave->bHasglobalTRNS       = pData->bHasglobalTRNS;
+  pSave->bHasglobalGAMA       = pData->bHasglobalGAMA;
+  pSave->bHasglobalCHRM       = pData->bHasglobalCHRM;
+  pSave->bHasglobalSRGB       = pData->bHasglobalSRGB;
+  pSave->bHasglobalICCP       = pData->bHasglobalICCP;
+  pSave->bHasglobalBKGD       = pData->bHasglobalBKGD;
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+#ifndef MNG_SKIPCHUNK_BACK
+  pSave->iBACKred             = pData->iBACKred;
+  pSave->iBACKgreen           = pData->iBACKgreen;
+  pSave->iBACKblue            = pData->iBACKblue;
+  pSave->iBACKmandatory       = pData->iBACKmandatory;
+  pSave->iBACKimageid         = pData->iBACKimageid;
+  pSave->iBACKtile            = pData->iBACKtile;
+#endif
+
+#ifndef MNG_SKIPCHUNK_FRAM
+  pSave->iFRAMmode            = pData->iFRAMmode;
+  pSave->iFRAMdelay           = pData->iFRAMdelay;
+  pSave->iFRAMtimeout         = pData->iFRAMtimeout;
+  pSave->bFRAMclipping        = pData->bFRAMclipping;
+  pSave->iFRAMclipl           = pData->iFRAMclipl;
+  pSave->iFRAMclipr           = pData->iFRAMclipr;
+  pSave->iFRAMclipt           = pData->iFRAMclipt;
+  pSave->iFRAMclipb           = pData->iFRAMclipb;
+#endif
+
+  pSave->iGlobalPLTEcount     = pData->iGlobalPLTEcount;
+
+  MNG_COPY (pSave->aGlobalPLTEentries, pData->aGlobalPLTEentries, sizeof (mng_rgbpaltab));
+
+  pSave->iGlobalTRNSrawlen    = pData->iGlobalTRNSrawlen;
+  MNG_COPY (pSave->aGlobalTRNSrawdata, pData->aGlobalTRNSrawdata, 256);
+
+  pSave->iGlobalGamma         = pData->iGlobalGamma;
+
+#ifndef MNG_SKIPCHUNK_cHRM
+  pSave->iGlobalWhitepointx   = pData->iGlobalWhitepointx;
+  pSave->iGlobalWhitepointy   = pData->iGlobalWhitepointy;
+  pSave->iGlobalPrimaryredx   = pData->iGlobalPrimaryredx;
+  pSave->iGlobalPrimaryredy   = pData->iGlobalPrimaryredy;
+  pSave->iGlobalPrimarygreenx = pData->iGlobalPrimarygreenx;
+  pSave->iGlobalPrimarygreeny = pData->iGlobalPrimarygreeny;
+  pSave->iGlobalPrimarybluex  = pData->iGlobalPrimarybluex;
+  pSave->iGlobalPrimarybluey  = pData->iGlobalPrimarybluey;
+#endif
+
+#ifndef MNG_SKIPCHUNK_sRGB
+  pSave->iGlobalRendintent    = pData->iGlobalRendintent;
+#endif
+
+#ifndef MNG_SKIPCHUNK_iCCP
+  pSave->iGlobalProfilesize   = pData->iGlobalProfilesize;
+
+  if (pSave->iGlobalProfilesize)       /* has a profile ? */
+  {                                    /* then copy that ! */
+    MNG_ALLOC (pData, pSave->pGlobalProfile, pSave->iGlobalProfilesize);
+    MNG_COPY (pSave->pGlobalProfile, pData->pGlobalProfile, pSave->iGlobalProfilesize);
+  }
+#endif
+
+#ifndef MNG_SKIPCHUNK_bKGD
+  pSave->iGlobalBKGDred       = pData->iGlobalBKGDred;
+  pSave->iGlobalBKGDgreen     = pData->iGlobalBKGDgreen;
+  pSave->iGlobalBKGDblue      = pData->iGlobalBKGDblue;
+#endif
+
+                                       /* freeze current image objects */
+  pImage = (mng_imagep)pData->pFirstimgobj;
+
+  while (pImage)
+  {                                    /* freeze the object AND its buffer */
+    pImage->bFrozen          = MNG_TRUE;
+    pImage->pImgbuf->bFrozen = MNG_TRUE;
+                                       /* neeeext */
+    pImage = (mng_imagep)pImage->sHeader.pNext;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SAVE_STATE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_reset_objzero (mng_datap pData)
+{
+  mng_imagep  pImage   = (mng_imagep)pData->pObjzero;
+  mng_retcode iRetcode = mng_reset_object_details (pData, pImage, 0, 0, 0,
+                                                   0, 0, 0, 0, MNG_TRUE);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  pImage->bVisible             = MNG_TRUE;
+  pImage->bViewable            = MNG_TRUE;
+  pImage->iPosx                = 0;
+  pImage->iPosy                = 0;
+  pImage->bClipped             = MNG_FALSE;
+  pImage->iClipl               = 0;
+  pImage->iClipr               = 0;
+  pImage->iClipt               = 0;
+  pImage->iClipb               = 0;
+#ifndef MNG_SKIPCHUNK_MAGN
+  pImage->iMAGN_MethodX        = 0;
+  pImage->iMAGN_MethodY        = 0;
+  pImage->iMAGN_MX             = 0;
+  pImage->iMAGN_MY             = 0;
+  pImage->iMAGN_ML             = 0;
+  pImage->iMAGN_MR             = 0;
+  pImage->iMAGN_MT             = 0;
+  pImage->iMAGN_MB             = 0;
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode restore_state (mng_datap pData)
+{
+#ifndef MNG_SKIPCHUNK_SAVE
+  mng_savedatap pSave;
+#endif
+  mng_imagep    pImage;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_STATE, MNG_LC_START);
+#endif
+                                       /* restore object 0 status !!! */
+  iRetcode = mng_reset_objzero (pData);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* fresh cycle; fake no frames done yet */
+  pData->bFramedone             = MNG_FALSE;
+
+#ifndef MNG_SKIPCHUNK_SAVE
+  if (pData->pSavedata)                /* do we have a saved state ? */
+  {
+    pSave = pData->pSavedata;          /* address it more directly */
+                                       /* and copy it back to the main struct */
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+    pData->bHasglobalPLTE       = pSave->bHasglobalPLTE;
+    pData->bHasglobalTRNS       = pSave->bHasglobalTRNS;
+    pData->bHasglobalGAMA       = pSave->bHasglobalGAMA;
+    pData->bHasglobalCHRM       = pSave->bHasglobalCHRM;
+    pData->bHasglobalSRGB       = pSave->bHasglobalSRGB;
+    pData->bHasglobalICCP       = pSave->bHasglobalICCP;
+    pData->bHasglobalBKGD       = pSave->bHasglobalBKGD;
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+#ifndef MNG_SKIPCHUNK_BACK
+    pData->iBACKred             = pSave->iBACKred;
+    pData->iBACKgreen           = pSave->iBACKgreen;
+    pData->iBACKblue            = pSave->iBACKblue;
+    pData->iBACKmandatory       = pSave->iBACKmandatory;
+    pData->iBACKimageid         = pSave->iBACKimageid;
+    pData->iBACKtile            = pSave->iBACKtile;
+#endif
+
+#ifndef MNG_SKIPCHUNK_FRAM
+    pData->iFRAMmode            = pSave->iFRAMmode;
+/*    pData->iFRAMdelay           = pSave->iFRAMdelay; */
+    pData->iFRAMtimeout         = pSave->iFRAMtimeout;
+    pData->bFRAMclipping        = pSave->bFRAMclipping;
+    pData->iFRAMclipl           = pSave->iFRAMclipl;
+    pData->iFRAMclipr           = pSave->iFRAMclipr;
+    pData->iFRAMclipt           = pSave->iFRAMclipt;
+    pData->iFRAMclipb           = pSave->iFRAMclipb;
+                                       /* NOOOOOOOOOOOO */
+/*    pData->iFramemode           = pSave->iFRAMmode;
+    pData->iFramedelay          = pSave->iFRAMdelay;
+    pData->iFrametimeout        = pSave->iFRAMtimeout;
+    pData->bFrameclipping       = pSave->bFRAMclipping;
+    pData->iFrameclipl          = pSave->iFRAMclipl;
+    pData->iFrameclipr          = pSave->iFRAMclipr;
+    pData->iFrameclipt          = pSave->iFRAMclipt;
+    pData->iFrameclipb          = pSave->iFRAMclipb; */
+
+/*    pData->iNextdelay           = pSave->iFRAMdelay; */
+    pData->iNextdelay           = pData->iFramedelay;
+#endif
+
+    pData->iGlobalPLTEcount     = pSave->iGlobalPLTEcount;
+    MNG_COPY (pData->aGlobalPLTEentries, pSave->aGlobalPLTEentries, sizeof (mng_rgbpaltab));
+
+    pData->iGlobalTRNSrawlen    = pSave->iGlobalTRNSrawlen;
+    MNG_COPY (pData->aGlobalTRNSrawdata, pSave->aGlobalTRNSrawdata, 256);
+
+    pData->iGlobalGamma         = pSave->iGlobalGamma;
+
+#ifndef MNG_SKIPCHUNK_cHRM
+    pData->iGlobalWhitepointx   = pSave->iGlobalWhitepointx;
+    pData->iGlobalWhitepointy   = pSave->iGlobalWhitepointy;
+    pData->iGlobalPrimaryredx   = pSave->iGlobalPrimaryredx;
+    pData->iGlobalPrimaryredy   = pSave->iGlobalPrimaryredy;
+    pData->iGlobalPrimarygreenx = pSave->iGlobalPrimarygreenx;
+    pData->iGlobalPrimarygreeny = pSave->iGlobalPrimarygreeny;
+    pData->iGlobalPrimarybluex  = pSave->iGlobalPrimarybluex;
+    pData->iGlobalPrimarybluey  = pSave->iGlobalPrimarybluey;
+#endif
+
+    pData->iGlobalRendintent    = pSave->iGlobalRendintent;
+
+#ifndef MNG_SKIPCHUNK_iCCP
+    pData->iGlobalProfilesize   = pSave->iGlobalProfilesize;
+
+    if (pData->iGlobalProfilesize)     /* has a profile ? */
+    {                                  /* then copy that ! */
+      MNG_ALLOC (pData, pData->pGlobalProfile, pData->iGlobalProfilesize);
+      MNG_COPY (pData->pGlobalProfile, pSave->pGlobalProfile, pData->iGlobalProfilesize);
+    }
+#endif
+
+#ifndef MNG_SKIPCHUNK_bKGD
+    pData->iGlobalBKGDred       = pSave->iGlobalBKGDred;
+    pData->iGlobalBKGDgreen     = pSave->iGlobalBKGDgreen;
+    pData->iGlobalBKGDblue      = pSave->iGlobalBKGDblue;
+#endif
+  }
+  else                                 /* no saved-data; so reset the lot */
+#endif /* SKIPCHUNK_SAVE */
+  {
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+    pData->bHasglobalPLTE       = MNG_FALSE;
+    pData->bHasglobalTRNS       = MNG_FALSE;
+    pData->bHasglobalGAMA       = MNG_FALSE;
+    pData->bHasglobalCHRM       = MNG_FALSE;
+    pData->bHasglobalSRGB       = MNG_FALSE;
+    pData->bHasglobalICCP       = MNG_FALSE;
+    pData->bHasglobalBKGD       = MNG_FALSE;
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+#ifndef MNG_SKIPCHUNK_TERM
+    if (!pData->bMisplacedTERM)        /* backward compatible ugliness !!! */
+    {
+      pData->iBACKred           = 0;
+      pData->iBACKgreen         = 0;
+      pData->iBACKblue          = 0;
+      pData->iBACKmandatory     = 0;
+      pData->iBACKimageid       = 0;
+      pData->iBACKtile          = 0;
+    }
+#endif
+
+#ifndef MNG_SKIPCHUNK_FRAM
+    pData->iFRAMmode            = 1;
+/*    pData->iFRAMdelay           = 1; */
+    pData->iFRAMtimeout         = 0x7fffffffl;
+    pData->bFRAMclipping        = MNG_FALSE;
+    pData->iFRAMclipl           = 0;
+    pData->iFRAMclipr           = 0;
+    pData->iFRAMclipt           = 0;
+    pData->iFRAMclipb           = 0;
+                                       /* NOOOOOOOOOOOO */
+/*    pData->iFramemode           = 1;
+    pData->iFramedelay          = 1;
+    pData->iFrametimeout        = 0x7fffffffl;
+    pData->bFrameclipping       = MNG_FALSE;
+    pData->iFrameclipl          = 0;
+    pData->iFrameclipr          = 0;
+    pData->iFrameclipt          = 0;
+    pData->iFrameclipb          = 0; */
+
+/*    pData->iNextdelay           = 1; */
+    pData->iNextdelay           = pData->iFramedelay;
+#endif
+
+    pData->iGlobalPLTEcount     = 0;
+
+    pData->iGlobalTRNSrawlen    = 0;
+
+    pData->iGlobalGamma         = 0;
+
+#ifndef MNG_SKIPCHUNK_cHRM
+    pData->iGlobalWhitepointx   = 0;
+    pData->iGlobalWhitepointy   = 0;
+    pData->iGlobalPrimaryredx   = 0;
+    pData->iGlobalPrimaryredy   = 0;
+    pData->iGlobalPrimarygreenx = 0;
+    pData->iGlobalPrimarygreeny = 0;
+    pData->iGlobalPrimarybluex  = 0;
+    pData->iGlobalPrimarybluey  = 0;
+#endif
+
+    pData->iGlobalRendintent    = 0;
+
+#ifndef MNG_SKIPCHUNK_iCCP
+    if (pData->iGlobalProfilesize)     /* free a previous profile ? */
+      MNG_FREE (pData, pData->pGlobalProfile, pData->iGlobalProfilesize);
+
+    pData->iGlobalProfilesize   = 0;
+#endif
+
+#ifndef MNG_SKIPCHUNK_bKGD
+    pData->iGlobalBKGDred       = 0;
+    pData->iGlobalBKGDgreen     = 0;
+    pData->iGlobalBKGDblue      = 0;
+#endif
+  }
+
+#ifndef MNG_SKIPCHUNK_TERM
+  if (!pData->bMisplacedTERM)          /* backward compatible ugliness !!! */
+  {
+    pImage = (mng_imagep)pData->pFirstimgobj;
+                                       /* drop un-frozen image objects */
+    while (pImage)
+    {
+      mng_imagep pNext = (mng_imagep)pImage->sHeader.pNext;
+
+      if (!pImage->bFrozen)            /* is it un-frozen ? */
+      {
+        mng_imagep pPrev = (mng_imagep)pImage->sHeader.pPrev;
+
+        if (pPrev)                     /* unlink it */
+          pPrev->sHeader.pNext = pNext;
+        else
+          pData->pFirstimgobj  = pNext;
+
+        if (pNext)
+          pNext->sHeader.pPrev = pPrev;
+        else
+          pData->pLastimgobj   = pPrev;
+
+        if (pImage->pImgbuf->bFrozen)  /* buffer frozen ? */
+        {
+          if (pImage->pImgbuf->iRefcount < 2)
+            MNG_ERROR (pData, MNG_INTERNALERROR);
+                                       /* decrease ref counter */
+          pImage->pImgbuf->iRefcount--;
+                                       /* just cleanup the object then */
+          MNG_FREEX (pData, pImage, sizeof (mng_image));
+        }
+        else
+        {                              /* free the image buffer */
+          iRetcode = mng_free_imagedataobject (pData, pImage->pImgbuf);
+                                       /* and cleanup the object */
+          MNG_FREEX (pData, pImage, sizeof (mng_image));
+
+          if (iRetcode)                /* on error bail out */
+            return iRetcode;
+        }
+      }
+
+      pImage = pNext;                  /* neeeext */
+    }
+  }
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_STATE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * General display processing routine                                     * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_process_display (mng_datap pData)
+{
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY, MNG_LC_START);
+#endif
+
+  if (!pData->iBreakpoint)             /* not broken previously ? */
+  {
+    if ((pData->iRequestframe) || (pData->iRequestlayer) || (pData->iRequesttime))
+    {
+      pData->bSearching = MNG_TRUE;    /* indicate we're searching */
+
+      iRetcode = clear_canvas (pData); /* make the canvas virgin black ?!? */
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+                                       /* let's start from the top, shall we */
+      pData->pCurraniobj = pData->pFirstaniobj;
+    }
+  }
+
+  do                                   /* process the objects */
+  {
+    if (pData->bSearching)             /* clear timer-flag when searching !!! */
+      pData->bTimerset = MNG_FALSE;
+                                       /* do we need to finish something first ? */
+    if ((pData->iBreakpoint) && (pData->iBreakpoint < 99))
+    {
+      switch (pData->iBreakpoint)      /* return to broken display routine */
+      {
+#ifndef MNG_SKIPCHUNK_FRAM
+        case  1 : { iRetcode = mng_process_display_fram2 (pData); break; }
+#endif
+#ifndef MNG_SKIPCHUNK_SHOW
+        case  3 : ;                    /* same as 4 !!! */
+        case  4 : { iRetcode = mng_process_display_show  (pData); break; }
+#endif
+#ifndef MNG_SKIPCHUNK_CLON
+        case  5 : { iRetcode = mng_process_display_clon2 (pData); break; }
+#endif
+#ifndef MNG_SKIPCHUNK_MAGN
+        case  9 : { iRetcode = mng_process_display_magn2 (pData); break; }
+        case 10 : { iRetcode = mng_process_display_mend2 (pData); break; }
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+        case 11 : { iRetcode = mng_process_display_past2 (pData); break; }
+#endif
+        default : MNG_ERROR (pData, MNG_INTERNALERROR);
+      }
+    }
+    else
+    {
+      if (pData->pCurraniobj)
+        iRetcode = ((mng_object_headerp)pData->pCurraniobj)->fProcess (pData, pData->pCurraniobj);
+    }
+
+    if (!pData->bTimerset)             /* reset breakpoint flag ? */
+      pData->iBreakpoint = 0;
+                                       /* can we advance to next object ? */
+    if ((!iRetcode) && (pData->pCurraniobj) &&
+        (!pData->bTimerset) && (!pData->bSectionwait))
+    {
+      pData->pCurraniobj = ((mng_object_headerp)pData->pCurraniobj)->pNext;
+                                       /* MEND processing to be done ? */
+      if ((pData->eImagetype == mng_it_mng) && (!pData->pCurraniobj))
+        iRetcode = mng_process_display_mend (pData);
+
+      if (!pData->pCurraniobj)         /* refresh after last image ? */
+        pData->bNeedrefresh = MNG_TRUE;
+    }
+
+    if (pData->bSearching)             /* are we looking for something ? */
+    {
+      if ((pData->iRequestframe) && (pData->iRequestframe <= pData->iFrameseq))
+      {
+        pData->iRequestframe = 0;      /* found the frame ! */
+        pData->bSearching    = MNG_FALSE;
+      }
+      else
+      if ((pData->iRequestlayer) && (pData->iRequestlayer <= pData->iLayerseq))
+      {
+        pData->iRequestlayer = 0;      /* found the layer ! */
+        pData->bSearching    = MNG_FALSE;
+      }
+      else
+      if ((pData->iRequesttime) && (pData->iRequesttime <= pData->iFrametime))
+      {
+        pData->iRequesttime  = 0;      /* found the playtime ! */
+        pData->bSearching    = MNG_FALSE;
+      }
+    }
+  }                                    /* until error or a break or no more objects */
+  while ((!iRetcode) && (pData->pCurraniobj) &&
+         (((pData->bRunning) && (!pData->bTimerset)) || (pData->bSearching)) &&
+         (!pData->bSectionwait) && (!pData->bFreezing));
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* refresh needed ? */
+  if ((!pData->bTimerset) && (pData->bNeedrefresh))
+  {
+    iRetcode = mng_display_progressive_refresh (pData, 1);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+                                       /* timer break ? */
+  if ((pData->bTimerset) && (!pData->iBreakpoint))
+    pData->iBreakpoint = 99;
+  else
+  if (!pData->bTimerset)
+    pData->iBreakpoint = 0;            /* reset if no timer break */
+
+  if ((!pData->bTimerset) && (!pData->pCurraniobj))
+    pData->bRunning = MNG_FALSE;       /* all done now ! */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Chunk display processing routines                                      * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+png_imgtype mng_png_imgtype(mng_uint8 colortype, mng_uint8 bitdepth)
+{
+  png_imgtype ret;
+  switch (bitdepth)
+  {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case 1:
+    {
+      png_imgtype imgtype[]={png_g1,png_none,png_none,png_idx1};
+      ret=imgtype[colortype];
+      break;
+    }
+    case 2:
+    {
+      png_imgtype imgtype[]={png_g2,png_none,png_none,png_idx2};
+      ret=imgtype[colortype];
+      break;
+    }
+    case 4:
+    {
+      png_imgtype imgtype[]={png_g4,png_none,png_none,png_idx4};
+      ret=imgtype[colortype];
+      break;
+    }
+#endif
+    case 8:
+    {
+      png_imgtype imgtype[]={png_g8,png_none,png_rgb8,png_idx8,png_ga8,
+          png_none,png_rgba8};
+      ret=imgtype[colortype];
+      break;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    case 16:
+    {
+      png_imgtype imgtype[]={png_g16,png_none,png_rgb16,png_none,png_ga16,
+          png_none,png_rgba16};
+      ret=imgtype[colortype];
+      break;
+    }
+#endif
+    default:
+      ret=png_none;
+      break;
+  }
+  return (ret);
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_display_ihdr (mng_datap pData)
+{                                      /* address the current "object" if any */
+  mng_imagep pImage = (mng_imagep)pData->pCurrentobj;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IHDR, MNG_LC_START);
+#endif
+
+  if (!pData->bHasDHDR)
+  {
+    pData->fInitrowproc = MNG_NULL;    /* do nothing by default */
+    pData->fDisplayrow  = MNG_NULL;
+    pData->fCorrectrow  = MNG_NULL;
+    pData->fStorerow    = MNG_NULL;
+    pData->fProcessrow  = MNG_NULL;
+    pData->fDifferrow   = MNG_NULL;
+    pData->pStoreobj    = MNG_NULL;
+  }
+
+  if (!pData->iBreakpoint)             /* not previously broken ? */
+  {
+    mng_retcode iRetcode = MNG_NOERROR;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* is a delta-image ? */
+    {
+      if (pData->iDeltatype == MNG_DELTATYPE_REPLACE)
+        iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pDeltaImage,
+                                             pData->iDatawidth, pData->iDataheight,
+                                             pData->iBitdepth, pData->iColortype,
+                                             pData->iCompression, pData->iFilter,
+                                             pData->iInterlace, MNG_TRUE);
+      else
+      if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD    ) ||
+          (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+      {
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iPixelsampledepth = pData->iBitdepth;
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iBitdepth;
+      }
+      else
+      if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD    ) ||
+          (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)    )
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iBitdepth;
+      else
+      if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD    ) ||
+          (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)    )
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iPixelsampledepth = pData->iBitdepth;
+
+      if (!iRetcode)
+      {                                /* process immediately if bitdepth & colortype are equal */
+        pData->bDeltaimmediate =
+          (mng_bool)((pData->iBitdepth  == ((mng_imagep)pData->pDeltaImage)->pImgbuf->iBitdepth ) &&
+                     (pData->iColortype == ((mng_imagep)pData->pDeltaImage)->pImgbuf->iColortype)    );
+                                       /* be sure to reset object 0 */
+        iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pObjzero,
+                                             pData->iDatawidth, pData->iDataheight,
+                                             pData->iBitdepth, pData->iColortype,
+                                             pData->iCompression, pData->iFilter,
+                                             pData->iInterlace, MNG_TRUE);
+      }
+    }
+    else
+#endif
+    {
+      if (pImage)                      /* update object buffer ? */
+        iRetcode = mng_reset_object_details (pData, pImage,
+                                             pData->iDatawidth, pData->iDataheight,
+                                             pData->iBitdepth, pData->iColortype,
+                                             pData->iCompression, pData->iFilter,
+                                             pData->iInterlace, MNG_TRUE);
+      else
+        iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pObjzero,
+                                             pData->iDatawidth, pData->iDataheight,
+                                             pData->iBitdepth, pData->iColortype,
+                                             pData->iCompression, pData->iFilter,
+                                             pData->iInterlace, MNG_TRUE);
+    }
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+#ifndef MNG_NO_DELTA_PNG
+  if (!pData->bHasDHDR)
+#endif
+  {
+    if (pImage)                        /* real object ? */
+      pData->pStoreobj = pImage;       /* tell the row routines */
+    else                               /* otherwise use object 0 */
+      pData->pStoreobj = pData->pObjzero;
+
+#if !defined(MNG_INCLUDE_MPNG_PROPOSAL) && !defined(MNG_INCLUDE_ANG_PROPOSAL)
+    if (                               /* display "on-the-fly" ? */
+#ifndef MNG_SKIPCHUNK_MAGN
+         (((mng_imagep)pData->pStoreobj)->iMAGN_MethodX == 0) &&
+         (((mng_imagep)pData->pStoreobj)->iMAGN_MethodY == 0) &&
+#endif
+         ( (pData->eImagetype == mng_it_png         ) ||
+           (((mng_imagep)pData->pStoreobj)->bVisible)    )       )
+    {
+      next_layer (pData);              /* that's a new layer then ! */
+
+      if (pData->bTimerset)            /* timer break ? */
+        pData->iBreakpoint = 2;
+      else
+      {
+        pData->iBreakpoint = 0;
+                                       /* anything to display ? */
+        if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt))
+          set_display_routine (pData); /* then determine display routine */
+      }
+    }
+#endif
+  }
+
+  if (!pData->bTimerset)               /* no timer break ? */
+  {
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+    pData->fInitrowproc = (mng_fptr)mng_init_rowproc;
+    pData->ePng_imgtype=mng_png_imgtype(pData->iColortype,pData->iBitdepth);
+#else
+    switch (pData->iColortype)         /* determine row initialization routine */
+    {
+      case 0 : {                       /* gray */
+                 switch (pData->iBitdepth)
+                 {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                   case  1 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g1_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g1_i;
+
+                               break;
+                             }
+                   case  2 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g2_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g2_i;
+
+                               break;
+                             }
+                   case  4 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g4_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g4_i;
+                               break;
+                             }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g8_i;
+
+                               break;
+                             }
+#ifndef MNG_NO_16BIT_SUPPORT
+                   case 16 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g16_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_g16_i;
+
+                               break;
+                             }
+#endif
+                 }
+
+                 break;
+               }
+      case 2 : {                       /* rgb */
+                 switch (pData->iBitdepth)
+                 {
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgb8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgb8_i;
+                               break;
+                             }
+#ifndef MNG_NO_16BIT_SUPPORT
+                   case 16 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgb16_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgb16_i;
+
+                               break;
+                             }
+#endif
+                 }
+
+                 break;
+               }
+      case 3 : {                       /* indexed */
+                 switch (pData->iBitdepth)
+                 {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                   case  1 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx1_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx1_i;
+
+                               break;
+                             }
+                   case  2 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx2_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx2_i;
+
+                               break;
+                             }
+                   case  4 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx4_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx4_i;
+
+                               break;
+                             }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_idx8_i;
+
+                               break;
+                             }
+                 }
+
+                 break;
+               }
+      case 4 : {                       /* gray+alpha */
+                 switch (pData->iBitdepth)
+                 {
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_ga8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_ga8_i;
+
+                               break;
+                             }
+#ifndef MNG_NO_16BIT_SUPPORT
+                   case 16 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_ga16_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_ga16_i;
+                               break;
+                             }
+#endif
+                 }
+
+                 break;
+               }
+      case 6 : {                       /* rgb+alpha */
+                 switch (pData->iBitdepth)
+                 {
+                   case  8 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgba8_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgba8_i;
+
+                               break;
+                             }
+#ifndef MNG_NO_16BIT_SUPPORT
+                   case 16 : {
+                               if (!pData->iInterlace)
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgba16_ni;
+                               else
+                                 pData->fInitrowproc = (mng_fptr)mng_init_rgba16_i;
+
+                               break;
+                             }
+#endif
+                 }
+
+                 break;
+               }
+    }
+#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */
+
+    pData->iFilterofs = 0;             /* determine filter characteristics */
+    pData->iLevel0    = 0;             /* default levels */
+    pData->iLevel1    = 0;    
+    pData->iLevel2    = 0;
+    pData->iLevel3    = 0;
+
+#ifdef FILTER192                       /* leveling & differing ? */
+    if (pData->iFilter == MNG_FILTER_DIFFERING)
+    {
+      switch (pData->iColortype)
+      {
+        case 0 : {
+                   if (pData->iBitdepth <= 8)
+                     pData->iFilterofs = 1;
+                   else
+                     pData->iFilterofs = 2;
+
+                   break;
+                 }
+        case 2 : {
+                   if (pData->iBitdepth <= 8)
+                     pData->iFilterofs = 3;
+                   else
+                     pData->iFilterofs = 6;
+
+                   break;
+                 }
+        case 3 : {
+                   pData->iFilterofs = 1;
+                   break;
+                 }
+        case 4 : {
+                   if (pData->iBitdepth <= 8)
+                     pData->iFilterofs = 2;
+                   else
+                     pData->iFilterofs = 4;
+
+                   break;
+                 }
+        case 6 : {
+                   if (pData->iBitdepth <= 8)
+                     pData->iFilterofs = 4;
+                   else
+                     pData->iFilterofs = 8;
+
+                   break;
+                 }
+      }
+    }
+#endif
+
+#ifdef FILTER193                       /* no adaptive filtering ? */
+    if (pData->iFilter == MNG_FILTER_NOFILTER)
+      pData->iPixelofs = pData->iFilterofs;
+    else
+#endif    
+      pData->iPixelofs = pData->iFilterofs + 1;
+
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+mng_retcode mng_process_display_mpng (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MPNG, MNG_LC_START);
+#endif
+
+  pData->iAlphadepth = 8;              /* assume transparency !! */
+
+  if (pData->fProcessheader)           /* inform the app (creating the output canvas) ? */
+  {
+    pData->iWidth  = ((mng_mpng_objp)pData->pMPNG)->iFramewidth;
+    pData->iHeight = ((mng_mpng_objp)pData->pMPNG)->iFrameheight;
+
+    if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight))
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+  next_layer (pData);                  /* first mPNG layer then ! */
+  pData->bTimerset   = MNG_FALSE;
+  pData->iBreakpoint = 0;
+
+  if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt))
+    set_display_routine (pData);       /* then determine display routine */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+mng_retcode mng_process_display_ang (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_ANG, MNG_LC_START);
+#endif
+
+  if (pData->fProcessheader)           /* inform the app (creating the output canvas) ? */
+  {
+    if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight))
+      MNG_ERROR (pData, MNG_APPMISCERROR);
+  }
+
+  next_layer (pData);                  /* first mPNG layer then ! */
+  pData->bTimerset   = MNG_FALSE;
+  pData->iBreakpoint = 0;
+
+  if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt))
+    set_display_routine (pData);       /* then determine display routine */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_ANG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_idat (mng_datap  pData,
+                                      mng_uint32 iRawlen,
+                                      mng_uint8p pRawdata)
+#else
+mng_retcode mng_process_display_idat (mng_datap  pData)
+#endif
+{
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IDAT, MNG_LC_START);
+#endif
+
+#if defined(MNG_INCLUDE_MPNG_PROPOSAL) || defined(MNG_INCLUDE_ANG_PROPOSAL) 
+  if ((pData->eImagetype == mng_it_png) && (pData->iLayerseq <= 0))
+  {
+    if (pData->fProcessheader)         /* inform the app (creating the output canvas) ? */
+      if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight))
+        MNG_ERROR (pData, MNG_APPMISCERROR);
+
+    next_layer (pData);                /* first regular PNG layer then ! */
+    pData->bTimerset   = MNG_FALSE;
+    pData->iBreakpoint = 0;
+
+    if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt))
+      set_display_routine (pData);     /* then determine display routine */
+  }
+#endif
+
+  if (pData->bRestorebkgd)             /* need to restore the background ? */
+  {
+    pData->bRestorebkgd = MNG_FALSE;
+    iRetcode            = load_bkgdlayer (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    pData->iLayerseq++;                /* and it counts as a layer then ! */
+  }
+
+  if (pData->fInitrowproc)             /* need to initialize row processing? */
+  {
+    iRetcode = ((mng_initrowproc)pData->fInitrowproc) (pData);
+    pData->fInitrowproc = MNG_NULL;    /* only call this once !!! */
+  }
+
+  if ((!iRetcode) && (!pData->bInflating))
+                                       /* initialize inflate */
+    iRetcode = mngzlib_inflateinit (pData);
+
+  if (!iRetcode)                       /* all ok? then inflate, my man */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    iRetcode = mngzlib_inflaterows (pData, iRawlen, pRawdata);
+#else
+    iRetcode = mngzlib_inflaterows (pData, pData->iRawlen, pData->pRawdata);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+    
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_display_iend (mng_datap pData)
+{
+  mng_retcode iRetcode, iRetcode2;
+  mng_bool bDodisplay = MNG_FALSE;
+  mng_bool bMagnify   = MNG_FALSE;
+  mng_bool bCleanup   = (mng_bool)(pData->iBreakpoint != 0);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IEND, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_JNG                 /* progressive+alpha JNG can be displayed now */
+  if ( (pData->bHasJHDR                                         ) &&
+       ( (pData->bJPEGprogressive) || (pData->bJPEGprogressive2)) &&
+       ( (pData->eImagetype == mng_it_jng         ) ||
+         (((mng_imagep)pData->pStoreobj)->bVisible)             ) &&
+       ( (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) ||
+         (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    )    )
+    bDodisplay = MNG_TRUE;
+#endif
+
+#ifndef MNG_SKIPCHUNK_MAGN
+  if ( (pData->pStoreobj) &&           /* on-the-fly magnification ? */
+       ( (((mng_imagep)pData->pStoreobj)->iMAGN_MethodX) ||
+         (((mng_imagep)pData->pStoreobj)->iMAGN_MethodY)    ) )
+    bMagnify = MNG_TRUE;
+#endif
+
+  if ((pData->bHasBASI) ||             /* was it a BASI stream */
+      (bDodisplay)      ||             /* or should we display the JNG */
+#ifndef MNG_SKIPCHUNK_MAGN
+      (bMagnify)        ||             /* or should we magnify it */
+#endif
+                                       /* or did we get broken here last time ? */
+      ((pData->iBreakpoint) && (pData->iBreakpoint != 8)))
+  {
+    mng_imagep pImage = (mng_imagep)pData->pCurrentobj;
+
+    if (!pImage)                       /* or was it object 0 ? */
+      pImage = (mng_imagep)pData->pObjzero;
+                                       /* display it now then ? */
+    if ((pImage->bVisible) && (pImage->bViewable))
+    {                                  /* ok, so do it */
+      iRetcode = mng_display_image (pData, pImage, bDodisplay);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+
+      if (pData->bTimerset)            /* timer break ? */
+        pData->iBreakpoint = 6;
+    }
+  }
+#ifndef MNG_NO_DELTA_PNG
+  else
+  if ((pData->bHasDHDR) ||             /* was it a DHDR stream */
+      (pData->iBreakpoint == 8))       /* or did we get broken here last time ? */
+  {
+    mng_imagep pImage = (mng_imagep)pData->pDeltaImage;
+
+    if (!pData->iBreakpoint)
+    {                                  /* perform the delta operations needed */
+      iRetcode = mng_execute_delta_image (pData, pImage, (mng_imagep)pData->pObjzero);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+                                       /* display it now then ? */
+    if ((pImage->bVisible) && (pImage->bViewable))
+    {                                  /* ok, so do it */
+      iRetcode = mng_display_image (pData, pImage, MNG_FALSE);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+
+      if (pData->bTimerset)            /* timer break ? */
+        pData->iBreakpoint = 8;
+    }
+  }
+#endif
+
+  if (!pData->bTimerset)               /* can we continue ? */
+  {
+    pData->iBreakpoint = 0;            /* clear this flag now ! */
+
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    if (pData->eImagetype == mng_it_mpng)
+    {
+      pData->pCurraniobj = pData->pFirstaniobj;
+    } else
+#endif
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+    if (pData->eImagetype == mng_it_ang)
+    {
+      pData->pCurraniobj = pData->pFirstaniobj;
+    } else
+#endif
+    {                                  /* cleanup object 0 */
+      mng_reset_object_details (pData, (mng_imagep)pData->pObjzero,
+                                0, 0, 0, 0, 0, 0, 0, MNG_TRUE);
+    }
+
+    if (pData->bInflating)             /* if we've been inflating */
+    {                                  /* cleanup row-processing, */
+      iRetcode  = mng_cleanup_rowproc (pData);
+                                       /* also cleanup inflate! */
+      iRetcode2 = mngzlib_inflatefree (pData);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+      if (iRetcode2)
+        return iRetcode2;
+    }
+
+#ifdef MNG_INCLUDE_JNG
+    if (pData->bJPEGdecompress)        /* if we've been decompressing JDAT */
+    {                                  /* cleanup row-processing, */
+      iRetcode  = mng_cleanup_rowproc (pData);
+                                       /* also cleanup decompress! */
+      iRetcode2 = mngjpeg_decompressfree (pData);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+      if (iRetcode2)
+        return iRetcode2;
+    }
+
+    if (pData->bJPEGdecompress2)       /* if we've been decompressing JDAA */
+    {                                  /* cleanup row-processing, */
+      iRetcode  = mng_cleanup_rowproc (pData);
+                                       /* also cleanup decompress! */
+      iRetcode2 = mngjpeg_decompressfree2 (pData);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+      if (iRetcode2)
+        return iRetcode2;
+    }
+#endif
+
+    if (bCleanup)                      /* if we got broken last time we need to cleanup */
+    {
+      pData->bHasIHDR = MNG_FALSE;     /* IEND signals the end for most ... */
+      pData->bHasBASI = MNG_FALSE;
+      pData->bHasDHDR = MNG_FALSE;
+#ifdef MNG_INCLUDE_JNG
+      pData->bHasJHDR = MNG_FALSE;
+      pData->bHasJSEP = MNG_FALSE;
+      pData->bHasJDAA = MNG_FALSE;
+      pData->bHasJDAT = MNG_FALSE;
+#endif
+      pData->bHasPLTE = MNG_FALSE;
+      pData->bHasTRNS = MNG_FALSE;
+      pData->bHasGAMA = MNG_FALSE;
+      pData->bHasCHRM = MNG_FALSE;
+      pData->bHasSRGB = MNG_FALSE;
+      pData->bHasICCP = MNG_FALSE;
+      pData->bHasBKGD = MNG_FALSE;
+      pData->bHasIDAT = MNG_FALSE;
+    }
+                                       /* if the image was displayed on the fly, */
+                                       /* we'll have to make the app refresh */
+    if ((pData->eImagetype != mng_it_mng) && (pData->fDisplayrow))
+      pData->bNeedrefresh = MNG_TRUE;
+     
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+/* change in the MNG spec with regards to TERM delay & interframe_delay
+   as proposed by Adam M. Costello (option 4) and finalized by official vote
+   during december 2002 / check the 'mng-list' archives for more details */
+
+mng_retcode mng_process_display_mend (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MEND, MNG_LC_START);
+#endif
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+  if (pData->bStopafterseek)           /* need to stop after this ? */
+  {
+    pData->bFreezing      = MNG_TRUE;  /* stop processing on this one */
+    pData->bRunningevent  = MNG_FALSE;
+    pData->bStopafterseek = MNG_FALSE;
+    pData->bNeedrefresh   = MNG_TRUE;  /* make sure the last bit is displayed ! */
+  }
+#endif
+
+#ifndef MNG_SKIPCHUNK_TERM
+                                       /* TERM processed ? */
+  if ((pData->bDisplaying) && (pData->bRunning) &&
+      (pData->bHasTERM) && (pData->pTermaniobj))
+  {
+    mng_retcode   iRetcode;
+    mng_ani_termp pTERM;
+                                       /* get the right animation object ! */
+    pTERM = (mng_ani_termp)pData->pTermaniobj;
+
+    pData->iIterations++;              /* increase iteration count */
+
+    switch (pTERM->iTermaction)        /* determine what to do! */
+    {
+      case 0 : {                       /* show last frame indefinitly */
+                 break;                /* piece of cake, that is... */
+               }
+
+      case 1 : {                       /* cease displaying anything */
+                                       /* max(1, TERM delay, interframe_delay) */
+#ifndef MNG_SKIPCHUNK_FRAM
+                 if (pTERM->iDelay > pData->iFramedelay)
+                   pData->iFramedelay = pTERM->iDelay;
+                 if (!pData->iFramedelay)
+                   pData->iFramedelay = 1;
+#endif
+
+                 iRetcode = interframe_delay (pData);
+                                       /* no interframe_delay? then fake it */
+                 if ((!iRetcode) && (!pData->bTimerset))
+                   iRetcode = set_delay (pData, 1);
+
+                 if (iRetcode)
+                   return iRetcode;
+
+                 pData->iBreakpoint = 10;
+                 break;
+               }
+
+      case 2 : {                       /* show first image after TERM */
+                 iRetcode = restore_state (pData);
+
+                 if (iRetcode)         /* on error bail out */
+                   return iRetcode;
+                                       /* notify the app ? */
+                 if (pData->fProcessmend)
+                   if (!pData->fProcessmend ((mng_handle)pData, pData->iIterations, 0))
+                     MNG_ERROR (pData, MNG_APPMISCERROR);
+
+                                       /* show first frame after TERM chunk */
+                 pData->pCurraniobj      = pTERM;
+                 pData->bOnlyfirstframe  = MNG_TRUE;
+                 pData->iFramesafterTERM = 0;
+
+                                       /* max(1, TERM delay, interframe_delay) */
+#ifndef MNG_SKIPCHUNK_FRAM
+                 if (pTERM->iDelay > pData->iFramedelay)
+                   pData->iFramedelay = pTERM->iDelay;
+                 if (!pData->iFramedelay)
+                   pData->iFramedelay = 1;
+#endif
+
+                 break;
+               }
+
+      case 3 : {                       /* repeat */
+                 if ((pTERM->iItermax) && (pTERM->iItermax < 0x7FFFFFFF))
+                   pTERM->iItermax--;
+
+                 if (pTERM->iItermax)  /* go back to TERM ? */
+                 {                     /* restore to initial or SAVE state */
+                   iRetcode = restore_state (pData);
+
+                   if (iRetcode)       /* on error bail out */
+                     return iRetcode;
+                                       /* notify the app ? */
+                   if (pData->fProcessmend)
+                     if (!pData->fProcessmend ((mng_handle)pData,
+                                               pData->iIterations, pTERM->iItermax))
+                       MNG_ERROR (pData, MNG_APPMISCERROR);
+
+                                       /* restart from TERM chunk */
+                   pData->pCurraniobj = pTERM;
+
+                   if (pTERM->iDelay)  /* set the delay (?) */
+                   {
+                                       /* max(1, TERM delay, interframe_delay) */
+#ifndef MNG_SKIPCHUNK_FRAM
+                     if (pTERM->iDelay > pData->iFramedelay)
+                       pData->iFramedelay = pTERM->iDelay;
+                     if (!pData->iFramedelay)
+                       pData->iFramedelay = 1;
+#endif
+
+                     pData->bNeedrefresh = MNG_TRUE;
+                   }
+                 }
+                 else
+                 {
+                   switch (pTERM->iIteraction)
+                   {
+                     case 0 : {        /* show last frame indefinitly */
+                                break; /* piece of cake, that is... */
+                              }
+
+                     case 1 : {        /* cease displaying anything */
+                                       /* max(1, TERM delay, interframe_delay) */
+#ifndef MNG_SKIPCHUNK_FRAM
+                                if (pTERM->iDelay > pData->iFramedelay)
+                                  pData->iFramedelay = pTERM->iDelay;
+                                if (!pData->iFramedelay)
+                                  pData->iFramedelay = 1;
+#endif
+
+                                iRetcode = interframe_delay (pData);
+                                       /* no interframe_delay? then fake it */
+                                if ((!iRetcode) && (!pData->bTimerset))
+                                  iRetcode = set_delay (pData, 1);
+
+                                if (iRetcode)
+                                  return iRetcode;
+
+                                pData->iBreakpoint = 10;
+                                break;
+                              }
+
+                     case 2 : {        /* show first image after TERM */
+                                iRetcode = restore_state (pData);
+                                       /* on error bail out */
+                                if (iRetcode)
+                                  return iRetcode;
+                                       /* notify the app ? */
+                                if (pData->fProcessmend)
+                                  if (!pData->fProcessmend ((mng_handle)pData,
+                                                            pData->iIterations, 0))
+                                    MNG_ERROR (pData, MNG_APPMISCERROR);
+
+                                       /* show first frame after TERM chunk */
+                                pData->pCurraniobj      = pTERM;
+                                pData->bOnlyfirstframe  = MNG_TRUE;
+                                pData->iFramesafterTERM = 0;
+                                       /* max(1, TERM delay, interframe_delay) */
+#ifndef MNG_SKIPCHUNK_FRAM
+                                if (pTERM->iDelay > pData->iFramedelay)
+                                  pData->iFramedelay = pTERM->iDelay;
+                                if (!pData->iFramedelay)
+                                  pData->iFramedelay = 1;
+#endif
+
+                                break;
+                              }
+                   }
+                 }
+
+                 break;
+               }
+    }
+  }
+#endif /* MNG_SKIPCHUNK_TERM */
+                                       /* just reading ? */
+  if ((!pData->bDisplaying) && (pData->bReading))
+    if (pData->fProcessmend)           /* inform the app ? */
+      if (!pData->fProcessmend ((mng_handle)pData, 0, 0))
+        MNG_ERROR (pData, MNG_APPMISCERROR);
+
+  if (!pData->pCurraniobj)             /* always let the app refresh at the end ! */
+    pData->bNeedrefresh = MNG_TRUE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_display_mend2 (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MEND, MNG_LC_START);
+#endif
+
+#ifndef MNG_SKIPCHUNK_FRAM
+  pData->bFrameclipping = MNG_FALSE;   /* nothing to do but restore the app background */
+#endif
+  load_bkgdlayer (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MEND, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DEFI
+mng_retcode mng_process_display_defi (mng_datap pData)
+{
+  mng_imagep pImage;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DEFI, MNG_LC_START);
+#endif
+
+  if (!pData->iDEFIobjectid)           /* object id=0 ? */
+  {
+    pImage             = (mng_imagep)pData->pObjzero;
+
+    if (pData->bDEFIhasdonotshow)
+      pImage->bVisible = (mng_bool)(pData->iDEFIdonotshow == 0);
+
+    if (pData->bDEFIhasloca)
+    {
+      pImage->iPosx    = pData->iDEFIlocax;
+      pImage->iPosy    = pData->iDEFIlocay;
+    }
+
+    if (pData->bDEFIhasclip)
+    {
+      pImage->bClipped = pData->bDEFIhasclip;
+      pImage->iClipl   = pData->iDEFIclipl;
+      pImage->iClipr   = pData->iDEFIclipr;
+      pImage->iClipt   = pData->iDEFIclipt;
+      pImage->iClipb   = pData->iDEFIclipb;
+    }
+
+    pData->pCurrentobj = 0;            /* not a real object ! */
+  }
+  else
+  {                                    /* already exists ? */
+    pImage = (mng_imagep)mng_find_imageobject (pData, pData->iDEFIobjectid);
+
+    if (!pImage)                       /* if not; create new */
+    {
+      mng_retcode iRetcode = mng_create_imageobject (pData, pData->iDEFIobjectid,
+                                                     (mng_bool)(pData->iDEFIconcrete == 1),
+                                                     (mng_bool)(pData->iDEFIdonotshow == 0),
+                                                     MNG_FALSE, 0, 0, 0, 0, 0, 0, 0,
+                                                     pData->iDEFIlocax, pData->iDEFIlocay,
+                                                     pData->bDEFIhasclip,
+                                                     pData->iDEFIclipl, pData->iDEFIclipr,
+                                                     pData->iDEFIclipt, pData->iDEFIclipb,
+                                                     &pImage);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+    else
+    {                                  /* exists; then set new info */
+      if (pData->bDEFIhasdonotshow)
+        pImage->bVisible = (mng_bool)(pData->iDEFIdonotshow == 0);
+
+      pImage->bViewable  = MNG_FALSE;
+
+      if (pData->bDEFIhasloca)
+      {
+        pImage->iPosx    = pData->iDEFIlocax;
+        pImage->iPosy    = pData->iDEFIlocay;
+      }
+
+      if (pData->bDEFIhasclip)
+      {
+        pImage->bClipped = pData->bDEFIhasclip;
+        pImage->iClipl   = pData->iDEFIclipl;
+        pImage->iClipr   = pData->iDEFIclipr;
+        pImage->iClipt   = pData->iDEFIclipt;
+        pImage->iClipb   = pData->iDEFIclipb;
+      }
+
+      if (pData->bDEFIhasconcrete)
+        pImage->pImgbuf->bConcrete = (mng_bool)(pData->iDEFIconcrete == 1);
+    }
+
+    pData->pCurrentobj = pImage;       /* others may want to know this */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BASI
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_basi (mng_datap  pData,
+                                      mng_uint16 iRed,
+                                      mng_uint16 iGreen,
+                                      mng_uint16 iBlue,
+                                      mng_bool   bHasalpha,
+                                      mng_uint16 iAlpha,
+                                      mng_uint8  iViewable)
+#else
+mng_retcode mng_process_display_basi (mng_datap  pData)
+#endif
+{                                      /* address the current "object" if any */
+  mng_imagep     pImage = (mng_imagep)pData->pCurrentobj;
+  mng_uint8p     pWork;
+  mng_uint32     iX;
+  mng_imagedatap pBuf;
+  mng_retcode    iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_BASI, MNG_LC_START);
+#endif
+
+  if (!pImage)                         /* or is it an "on-the-fly" image ? */
+    pImage = (mng_imagep)pData->pObjzero;
+                                       /* address the object-buffer */
+  pBuf               = pImage->pImgbuf;
+
+  pData->fDisplayrow = MNG_NULL;       /* do nothing by default */
+  pData->fCorrectrow = MNG_NULL;
+  pData->fStorerow   = MNG_NULL;
+  pData->fProcessrow = MNG_NULL;
+                                       /* set parms now that they're known */
+  iRetcode = mng_reset_object_details (pData, pImage, pData->iDatawidth,
+                                       pData->iDataheight, pData->iBitdepth,
+                                       pData->iColortype, pData->iCompression,
+                                       pData->iFilter, pData->iInterlace, MNG_FALSE);
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+                                       /* save the viewable flag */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  pImage->bViewable = (mng_bool)(iViewable == 1);
+#else
+  pImage->bViewable = (mng_bool)(pData->iBASIviewable == 1);
+#endif
+  pBuf->bViewable   = pImage->bViewable;
+  pData->pStoreobj  = pImage;          /* let row-routines know which object */
+
+  pWork = pBuf->pImgdata;              /* fill the object-buffer with the specified
+                                          "color" sample */
+  switch (pData->iColortype)           /* depending on color_type & bit_depth */
+  {
+    case 0 : {                         /* gray */
+#ifndef MNG_NO_16BIT_SUPPORT
+               if (pData->iBitdepth == 16)
+               {
+#ifdef MNG_DECREMENT_LOOPS
+                 for (iX = pData->iDatawidth * pData->iDataheight;
+                    iX > 0;iX--)
+#else
+                 for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++)
+#endif
+                 {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   mng_put_uint16 (pWork, iRed);
+#else
+                   mng_put_uint16 (pWork, pData->iBASIred);
+#endif
+                   pWork += 2;
+                 }
+               }
+               else
+#endif
+               {
+#ifdef MNG_DECREMENT_LOOPS
+                 for (iX = pData->iDatawidth * pData->iDataheight;
+                    iX > 0;iX--)
+#else
+                 for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++)
+#endif
+                 {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   *pWork = (mng_uint8)iRed;
+#else
+                   *pWork = (mng_uint8)pData->iBASIred;
+#endif
+                   pWork++;
+                 }
+               }
+                                       /* force tRNS ? */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+               if ((bHasalpha) && (!iAlpha))
+#else
+               if ((pData->bBASIhasalpha) && (!pData->iBASIalpha))
+#endif
+               {
+                 pBuf->bHasTRNS  = MNG_TRUE;
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                 pBuf->iTRNSgray = iRed;
+#else
+                 pBuf->iTRNSgray = pData->iBASIred;
+#endif
+               }
+
+               break;
+             }
+
+    case 2 : {                         /* rgb */
+#ifndef MNG_NO_16BIT_SUPPORT
+               if (pData->iBitdepth == 16)
+               {
+#ifdef MNG_DECREMENT_LOOPS
+                 for (iX = pData->iDatawidth * pData->iDataheight;
+                    iX > 0;iX--)
+#else
+                 for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++)
+#endif
+                 {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   mng_put_uint16 (pWork,   iRed  );
+                   mng_put_uint16 (pWork+2, iGreen);
+                   mng_put_uint16 (pWork+4, iBlue );
+#else
+                   mng_put_uint16 (pWork,   pData->iBASIred  );
+                   mng_put_uint16 (pWork+2, pData->iBASIgreen);
+                   mng_put_uint16 (pWork+4, pData->iBASIblue );
+#endif
+                   pWork += 6;
+                 }
+               }
+               else
+#endif
+               {
+#ifdef MNG_DECREMENT_LOOPS
+                 for (iX = pData->iDatawidth * pData->iDataheight;
+                    iX > 0;iX--)
+#else
+                 for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++)
+#endif
+                 {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   *pWork     = (mng_uint8)iRed;
+                   *(pWork+1) = (mng_uint8)iGreen;
+                   *(pWork+2) = (mng_uint8)iBlue;
+#else
+                   *pWork     = (mng_uint8)pData->iBASIred;
+                   *(pWork+1) = (mng_uint8)pData->iBASIgreen;
+                   *(pWork+2) = (mng_uint8)pData->iBASIblue;
+#endif
+                   pWork += 3;
+                 }
+               }
+                                       /* force tRNS ? */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+               if ((bHasalpha) && (!iAlpha))
+#else
+               if ((pData->bBASIhasalpha) && (!pData->iBASIalpha))
+#endif
+               {
+                 pBuf->bHasTRNS   = MNG_TRUE;
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                 pBuf->iTRNSred   = iRed;
+                 pBuf->iTRNSgreen = iGreen;
+                 pBuf->iTRNSblue  = iBlue;
+#else
+                 pBuf->iTRNSred   = pData->iBASIred;
+                 pBuf->iTRNSgreen = pData->iBASIgreen;
+                 pBuf->iTRNSblue  = pData->iBASIblue;
+#endif
+               }
+
+               break;
+             }
+
+    case 3 : {                         /* indexed */
+               pBuf->bHasPLTE = MNG_TRUE;
+
+               switch (pData->iBitdepth)
+               {
+                 case 1  : { pBuf->iPLTEcount =   2; break; }
+                 case 2  : { pBuf->iPLTEcount =   4; break; }
+                 case 4  : { pBuf->iPLTEcount =  16; break; }
+                 case 8  : { pBuf->iPLTEcount = 256; break; }
+                 default : { pBuf->iPLTEcount =   1; break; }
+               }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+               pBuf->aPLTEentries [0].iRed   = (mng_uint8)iRed;
+               pBuf->aPLTEentries [0].iGreen = (mng_uint8)iGreen;
+               pBuf->aPLTEentries [0].iBlue  = (mng_uint8)iBlue;
+#else
+               pBuf->aPLTEentries [0].iRed   = (mng_uint8)pData->iBASIred;
+               pBuf->aPLTEentries [0].iGreen = (mng_uint8)pData->iBASIgreen;
+               pBuf->aPLTEentries [0].iBlue  = (mng_uint8)pData->iBASIblue;
+#endif
+
+               for (iX = 1; iX < pBuf->iPLTEcount; iX++)
+               {
+                 pBuf->aPLTEentries [iX].iRed   = 0;
+                 pBuf->aPLTEentries [iX].iGreen = 0;
+                 pBuf->aPLTEentries [iX].iBlue  = 0;
+               }
+                                       /* force tRNS ? */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+               if ((bHasalpha) && (iAlpha < 255))
+#else
+               if ((pData->bBASIhasalpha) && (pData->iBASIalpha < 255))
+#endif
+               {
+                 pBuf->bHasTRNS         = MNG_TRUE;
+                 pBuf->iTRNScount       = 1;
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                 pBuf->aTRNSentries [0] = (mng_uint8)iAlpha;
+#else
+                 pBuf->aTRNSentries [0] = (mng_uint8)pData->iBASIalpha;
+#endif
+               }
+
+               break;
+             }
+
+    case 4 : {                         /* gray+alpha */
+#ifndef MNG_NO_16BIT_SUPPORT
+               if (pData->iBitdepth == 16)
+               {
+#ifdef MNG_DECREMENT_LOOPS
+                 for (iX = pData->iDatawidth * pData->iDataheight;
+                    iX > 0;iX--)
+#else
+                 for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++)
+#endif
+                 {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   mng_put_uint16 (pWork,   iRed);
+                   mng_put_uint16 (pWork+2, iAlpha);
+#else
+                   mng_put_uint16 (pWork,   pData->iBASIred);
+                   mng_put_uint16 (pWork+2, pData->iBASIalpha);
+#endif
+                   pWork += 4;
+                 }
+               }
+               else
+#endif
+               {
+#ifdef MNG_DECREMENT_LOOPS
+                 for (iX = pData->iDatawidth * pData->iDataheight;
+                    iX > 0;iX--)
+#else
+                 for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++)
+#endif
+                 {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   *pWork     = (mng_uint8)iRed;
+                   *(pWork+1) = (mng_uint8)iAlpha;
+#else
+                   *pWork     = (mng_uint8)pData->iBASIred;
+                   *(pWork+1) = (mng_uint8)pData->iBASIalpha;
+#endif
+                   pWork += 2;
+                 }
+               }
+
+               break;
+             }
+
+    case 6 : {                         /* rgb+alpha */
+#ifndef MNG_NO_16BIT_SUPPORT
+               if (pData->iBitdepth == 16)
+               {
+#ifdef MNG_DECREMENT_LOOPS
+                 for (iX = pData->iDatawidth * pData->iDataheight;
+                    iX > 0;iX--)
+#else
+                 for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++)
+#endif
+                 {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   mng_put_uint16 (pWork,   iRed);
+                   mng_put_uint16 (pWork+2, iGreen);
+                   mng_put_uint16 (pWork+4, iBlue);
+                   mng_put_uint16 (pWork+6, iAlpha);
+#else
+                   mng_put_uint16 (pWork,   pData->iBASIred);
+                   mng_put_uint16 (pWork+2, pData->iBASIgreen);
+                   mng_put_uint16 (pWork+4, pData->iBASIblue);
+                   mng_put_uint16 (pWork+6, pData->iBASIalpha);
+#endif
+                   pWork += 8;
+                 }
+               }
+               else
+#endif
+               {
+#ifdef MNG_DECREMENT_LOOPS
+                 for (iX = pData->iDatawidth * pData->iDataheight;
+                    iX > 0;iX--)
+#else
+                 for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++)
+#endif
+                 {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   *pWork     = (mng_uint8)iRed;
+                   *(pWork+1) = (mng_uint8)iGreen;
+                   *(pWork+2) = (mng_uint8)iBlue;
+                   *(pWork+3) = (mng_uint8)iAlpha;
+#else
+                   *pWork     = (mng_uint8)pData->iBASIred;
+                   *(pWork+1) = (mng_uint8)pData->iBASIgreen;
+                   *(pWork+2) = (mng_uint8)pData->iBASIblue;
+                   *(pWork+3) = (mng_uint8)pData->iBASIalpha;
+#endif
+                   pWork += 4;
+                 }
+               }
+
+               break;
+             }
+
+  }
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+  pData->fInitrowproc = (mng_fptr)mng_init_rowproc;
+  pData->ePng_imgtype=mng_png_imgtype(pData->iColortype,pData->iBitdepth);
+#else
+  switch (pData->iColortype)           /* determine row initialization routine */
+  {                                    /* just to accomodate IDAT if it arrives */
+    case 0 : {                         /* gray */
+               switch (pData->iBitdepth)
+               {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                 case  1 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_g1_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_g1_i;
+
+                             break;
+                           }
+                 case  2 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_g2_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_g2_i;
+
+                             break;
+                           }
+                 case  4 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_g4_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_g4_i;
+
+                             break;
+                           }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                 case  8 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_g8_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_g8_i;
+
+                             break;
+                           }
+#ifndef MNG_NO_16BIT_SUPPORT
+                 case 16 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_g16_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_g16_i;
+
+                             break;
+                           }
+#endif
+               }
+
+               break;
+             }
+    case 2 : {                         /* rgb */
+               switch (pData->iBitdepth)
+               {
+                 case  8 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_rgb8_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_rgb8_i;
+
+                             break;
+                           }
+#ifndef MNG_NO_16BIT_SUPPORT
+                 case 16 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_rgb16_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_rgb16_i;
+
+                             break;
+                           }
+#endif
+               }
+
+               break;
+             }
+    case 3 : {                         /* indexed */
+               switch (pData->iBitdepth)
+               {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                 case  1 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_idx1_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_idx1_i;
+
+                             break;
+                           }
+                 case  2 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_idx2_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_idx2_i;
+
+                             break;
+                           }
+                 case  4 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_idx4_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_idx4_i;
+
+                             break;
+                           }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                 case  8 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_idx8_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_idx8_i;
+
+                             break;
+                           }
+               }
+
+               break;
+             }
+    case 4 : {                         /* gray+alpha */
+               switch (pData->iBitdepth)
+               {
+                 case  8 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_ga8_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_ga8_i;
+
+                             break;
+                           }
+#ifndef MNG_NO_16BIT_SUPPORT
+                 case 16 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_ga16_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_ga16_i;
+
+                             break;
+                           }
+#endif
+               }
+
+               break;
+             }
+    case 6 : {                         /* rgb+alpha */
+               switch (pData->iBitdepth)
+               {
+                 case  8 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_rgba8_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_rgba8_i;
+
+                             break;
+                           }
+#ifndef MNG_NO_16BIT_SUPPORT
+                 case 16 : {
+                             if (!pData->iInterlace)
+                               pData->fInitrowproc = (mng_fptr)mng_init_rgba16_ni;
+                             else
+                               pData->fInitrowproc = (mng_fptr)mng_init_rgba16_i;
+
+                             break;
+                           }
+#endif
+               }
+
+               break;
+             }
+  }
+#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */
+
+  pData->iFilterofs = 0;               /* determine filter characteristics */
+  pData->iLevel0    = 0;               /* default levels */
+  pData->iLevel1    = 0;
+  pData->iLevel2    = 0;
+  pData->iLevel3    = 0;
+
+#ifdef FILTER192
+  if (pData->iFilter == 0xC0)          /* leveling & differing ? */
+  {
+    switch (pData->iColortype)
+    {
+      case 0 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (pData->iBitdepth <= 8)
+#endif
+                   pData->iFilterofs = 1;
+#ifndef MNG_NO_16BIT_SUPPORT
+                 else
+                   pData->iFilterofs = 2;
+#endif
+
+                 break;
+               }
+      case 2 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (pData->iBitdepth <= 8)
+#endif
+                   pData->iFilterofs = 3;
+#ifndef MNG_NO_16BIT_SUPPORT
+                 else
+                   pData->iFilterofs = 6;
+#endif
+
+                 break;
+               }
+      case 3 : {
+                 pData->iFilterofs = 1;
+                 break;
+               }
+      case 4 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (pData->iBitdepth <= 8)
+#endif
+                   pData->iFilterofs = 2;
+#ifndef MNG_NO_16BIT_SUPPORT
+                 else
+                   pData->iFilterofs = 4;
+#endif
+
+                 break;
+               }
+      case 6 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (pData->iBitdepth <= 8)
+#endif
+                   pData->iFilterofs = 4;
+#ifndef MNG_NO_16BIT_SUPPORT
+                 else
+                   pData->iFilterofs = 8;
+#endif
+
+                 break;
+               }
+    }
+  }
+#endif
+
+#ifdef FILTER193
+  if (pData->iFilter == 0xC1)          /* no adaptive filtering ? */
+    pData->iPixelofs = pData->iFilterofs;
+  else
+#endif
+    pData->iPixelofs = pData->iFilterofs + 1;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_BASI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLON
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_clon (mng_datap  pData,
+                                      mng_uint16 iSourceid,
+                                      mng_uint16 iCloneid,
+                                      mng_uint8  iClonetype,
+                                      mng_bool   bHasdonotshow,
+                                      mng_uint8  iDonotshow,
+                                      mng_uint8  iConcrete,
+                                      mng_bool   bHasloca,
+                                      mng_uint8  iLocationtype,
+                                      mng_int32  iLocationx,
+                                      mng_int32  iLocationy)
+#else
+mng_retcode mng_process_display_clon (mng_datap  pData)
+#endif
+{
+  mng_imagep  pSource, pClone;
+  mng_bool    bVisible, bAbstract;
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLON, MNG_LC_START);
+#endif
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                                       /* locate the source object first */
+  pSource = mng_find_imageobject (pData, iSourceid);
+                                       /* check if the clone exists */
+  pClone  = mng_find_imageobject (pData, iCloneid);
+#else
+                                       /* locate the source object first */
+  pSource = mng_find_imageobject (pData, pData->iCLONsourceid);
+                                       /* check if the clone exists */
+  pClone  = mng_find_imageobject (pData, pData->iCLONcloneid);
+#endif
+
+  if (!pSource)                        /* source must exist ! */
+    MNG_ERROR (pData, MNG_OBJECTUNKNOWN);
+
+  if (pClone)                          /* clone must not exist ! */
+    MNG_ERROR (pData, MNG_OBJECTEXISTS);
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  if (bHasdonotshow)                   /* DoNotShow flag filled ? */
+    bVisible = (mng_bool)(iDonotshow == 0);
+  else
+    bVisible = pSource->bVisible;
+#else
+  if (pData->bCLONhasdonotshow)        /* DoNotShow flag filled ? */
+    bVisible = (mng_bool)(pData->iCLONdonotshow == 0);
+  else
+    bVisible = pSource->bVisible;
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  bAbstract  = (mng_bool)(iConcrete == 1);
+#else
+  bAbstract  = (mng_bool)(pData->iCLONconcrete == 1);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  switch (iClonetype)                  /* determine action to take */
+  {
+    case 0 : {                         /* full clone */
+               iRetcode = mng_clone_imageobject (pData, iCloneid, MNG_FALSE,
+                                                 bVisible, bAbstract, bHasloca,
+                                                 iLocationtype, iLocationx, iLocationy,
+                                                 pSource, &pClone);
+               break;
+             }
+
+    case 1 : {                         /* partial clone */
+               iRetcode = mng_clone_imageobject (pData, iCloneid, MNG_TRUE,
+                                                 bVisible, bAbstract, bHasloca,
+                                                 iLocationtype, iLocationx, iLocationy,
+                                                 pSource, &pClone);
+               break;
+             }
+
+    case 2 : {                         /* renumber object */
+               iRetcode = mng_renum_imageobject (pData, pSource, iCloneid,
+                                                 bVisible, bAbstract, bHasloca,
+                                                 iLocationtype, iLocationx, iLocationy);
+               pClone   = pSource;
+               break;
+             }
+
+  }
+#else
+  switch (pData->iCLONclonetype)       /* determine action to take */
+  {
+    case 0 : {                         /* full clone */
+               iRetcode = mng_clone_imageobject (pData, pData->iCLONcloneid, MNG_FALSE,
+                                                 bVisible, bAbstract,
+                                                 pData->bCLONhasloca, pData->iCLONlocationtype,
+                                                 pData->iCLONlocationx, pData->iCLONlocationy,
+                                                 pSource, &pClone);
+               break;
+             }
+
+    case 1 : {                         /* partial clone */
+               iRetcode = mng_clone_imageobject (pData, pData->iCLONcloneid, MNG_TRUE,
+                                                 bVisible, bAbstract,
+                                                 pData->bCLONhasloca, pData->iCLONlocationtype,
+                                                 pData->iCLONlocationx, pData->iCLONlocationy,
+                                                 pSource, &pClone);
+               break;
+             }
+
+    case 2 : {                         /* renumber object */
+               iRetcode = mng_renum_imageobject (pData, pSource, pData->iCLONcloneid,
+                                                 bVisible, bAbstract,
+                                                 pData->bCLONhasloca, pData->iCLONlocationtype,
+                                                 pData->iCLONlocationx, pData->iCLONlocationy);
+               pClone   = pSource;
+               break;
+             }
+
+  }
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+                                       /* display on the fly ? */
+  if ((pClone->bViewable) && (pClone->bVisible))
+  {
+    pData->pLastclone = pClone;        /* remember in case of timer break ! */
+                                       /* display it */
+    mng_display_image (pData, pClone, MNG_FALSE);
+
+    if (pData->bTimerset)              /* timer break ? */
+      pData->iBreakpoint = 5;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLON, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_display_clon2 (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLON, MNG_LC_START);
+#endif
+                                       /* only called after timer break ! */
+  mng_display_image (pData, (mng_imagep)pData->pLastclone, MNG_FALSE);
+  pData->iBreakpoint = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLON, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_disc (mng_datap   pData,
+                                      mng_uint32  iCount,
+                                      mng_uint16p pIds)
+#else
+mng_retcode mng_process_display_disc (mng_datap   pData)
+#endif
+{
+  mng_uint32 iX;
+  mng_imagep pImage;
+  mng_uint32 iRetcode;
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DISC, MNG_LC_START);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  if (iCount)                          /* specific list ? */
+#else
+  if (pData->iDISCcount)               /* specific list ? */
+#endif
+  {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    mng_uint16p pWork = pIds;
+#else
+    mng_uint16p pWork = pData->pDISCids;
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifdef MNG_DECREMENT_LOOPS             /* iterate the list */
+    for (iX = iCount; iX > 0; iX--)
+#else
+    for (iX = 0; iX < iCount; iX++)
+#endif
+#else
+#ifdef MNG_DECREMENT_LOOPS             /* iterate the list */
+    for (iX = pData->iDISCcount; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iDISCcount; iX++)
+#endif
+#endif
+    {
+      pImage = mng_find_imageobject (pData, *pWork++);
+
+      if (pImage)                      /* found the object ? */
+      {                                /* then drop it */
+        iRetcode = mng_free_imageobject (pData, pImage);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+      }
+    }
+  }
+  else                                 /* empty: drop all un-frozen objects */
+  {
+    mng_imagep pNext = (mng_imagep)pData->pFirstimgobj;
+
+    while (pNext)                      /* any left ? */
+    {
+      pImage = pNext;
+      pNext  = pImage->sHeader.pNext;
+
+      if (!pImage->bFrozen)            /* not frozen ? */
+      {                                /* then drop it */
+        iRetcode = mng_free_imageobject (pData, pImage);
+                       
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+      }
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DISC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_fram (mng_datap  pData,
+                                      mng_uint8  iFramemode,
+                                      mng_uint8  iChangedelay,
+                                      mng_uint32 iDelay,
+                                      mng_uint8  iChangetimeout,
+                                      mng_uint32 iTimeout,
+                                      mng_uint8  iChangeclipping,
+                                      mng_uint8  iCliptype,
+                                      mng_int32  iClipl,
+                                      mng_int32  iClipr,
+                                      mng_int32  iClipt,
+                                      mng_int32  iClipb)
+#else
+mng_retcode mng_process_display_fram (mng_datap  pData)
+#endif
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_FRAM, MNG_LC_START);
+#endif
+                                       /* advance a frame then */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = next_frame (pData, iFramemode, iChangedelay, iDelay,
+                         iChangetimeout, iTimeout, iChangeclipping,
+                         iCliptype, iClipl, iClipr, iClipt, iClipb);
+#else
+  iRetcode = next_frame (pData, pData->iTempFramemode, pData->iTempChangedelay,
+                         pData->iTempDelay, pData->iTempChangetimeout,
+                         pData->iTempTimeout, pData->iTempChangeclipping,
+                         pData->iTempCliptype, pData->iTempClipl, pData->iTempClipr,
+                         pData->iTempClipt, pData->iTempClipb);
+#endif
+
+  if (pData->bTimerset)                /* timer break ? */
+    pData->iBreakpoint = 1;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_FRAM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_display_fram2 (mng_datap pData)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_FRAM, MNG_LC_START);
+#endif
+                                       /* again; after the break */
+  iRetcode = next_frame (pData, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+  pData->iBreakpoint = 0;              /* not again! */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_FRAM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MOVE
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_move (mng_datap  pData,
+                                      mng_uint16 iFromid,
+                                      mng_uint16 iToid,
+                                      mng_uint8  iMovetype,
+                                      mng_int32  iMovex,
+                                      mng_int32  iMovey)
+#else
+mng_retcode mng_process_display_move (mng_datap  pData)
+#endif
+{
+  mng_uint16 iX;
+  mng_imagep pImage;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MOVE, MNG_LC_START);
+#endif
+                                       /* iterate the list */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  for (iX = iFromid; iX <= iToid; iX++)
+#else
+  for (iX = pData->iMOVEfromid; iX <= pData->iMOVEtoid; iX++)
+#endif
+  {
+    if (!iX)                           /* object id=0 ? */
+      pImage = (mng_imagep)pData->pObjzero;
+    else
+      pImage = mng_find_imageobject (pData, iX);
+
+    if (pImage)                        /* object exists ? */
+    {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      switch (iMovetype)
+#else
+      switch (pData->iMOVEmovetype)
+#endif
+      {
+        case 0 : {                     /* absolute */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   pImage->iPosx = iMovex;
+                   pImage->iPosy = iMovey;
+#else
+                   pImage->iPosx = pData->iMOVEmovex;
+                   pImage->iPosy = pData->iMOVEmovey;
+#endif
+                   break;
+                 }
+        case 1 : {                     /* relative */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   pImage->iPosx = pImage->iPosx + iMovex;
+                   pImage->iPosy = pImage->iPosy + iMovey;
+#else
+                   pImage->iPosx = pImage->iPosx + pData->iMOVEmovex;
+                   pImage->iPosy = pImage->iPosy + pData->iMOVEmovey;
+#endif
+                   break;
+                 }
+      }
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MOVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLIP
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_clip (mng_datap  pData,
+                                      mng_uint16 iFromid,
+                                      mng_uint16 iToid,
+                                      mng_uint8  iCliptype,
+                                      mng_int32  iClipl,
+                                      mng_int32  iClipr,
+                                      mng_int32  iClipt,
+                                      mng_int32  iClipb)
+#else
+mng_retcode mng_process_display_clip (mng_datap  pData)
+#endif
+{
+  mng_uint16 iX;
+  mng_imagep pImage;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLIP, MNG_LC_START);
+#endif
+                                       /* iterate the list */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  for (iX = iFromid; iX <= iToid; iX++)
+#else
+  for (iX = pData->iCLIPfromid; iX <= pData->iCLIPtoid; iX++)
+#endif
+  {
+    if (!iX)                           /* object id=0 ? */
+      pImage = (mng_imagep)pData->pObjzero;
+    else
+      pImage = mng_find_imageobject (pData, iX);
+
+    if (pImage)                        /* object exists ? */
+    {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      switch (iCliptype)
+#else
+      switch (pData->iCLIPcliptype)
+#endif
+      {
+        case 0 : {                     /* absolute */
+                   pImage->bClipped = MNG_TRUE;
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   pImage->iClipl   = iClipl;
+                   pImage->iClipr   = iClipr;
+                   pImage->iClipt   = iClipt;
+                   pImage->iClipb   = iClipb;
+#else
+                   pImage->iClipl   = pData->iCLIPclipl;
+                   pImage->iClipr   = pData->iCLIPclipr;
+                   pImage->iClipt   = pData->iCLIPclipt;
+                   pImage->iClipb   = pData->iCLIPclipb;
+#endif
+                   break;
+                 }
+        case 1 : {                    /* relative */
+                   pImage->bClipped = MNG_TRUE;
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   pImage->iClipl   = pImage->iClipl + iClipl;
+                   pImage->iClipr   = pImage->iClipr + iClipr;
+                   pImage->iClipt   = pImage->iClipt + iClipt;
+                   pImage->iClipb   = pImage->iClipb + iClipb;
+#else
+                   pImage->iClipl   = pImage->iClipl + pData->iCLIPclipl;
+                   pImage->iClipr   = pImage->iClipr + pData->iCLIPclipr;
+                   pImage->iClipt   = pImage->iClipt + pData->iCLIPclipt;
+                   pImage->iClipb   = pImage->iClipb + pData->iCLIPclipb;
+#endif
+                   break;
+                 }
+      }
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLIP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SHOW
+mng_retcode mng_process_display_show (mng_datap pData)
+{
+  mng_int16  iX, iS, iFrom, iTo;
+  mng_imagep pImage;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SHOW, MNG_LC_START);
+#endif
+
+  /* TODO: optimization for the cases where "abs (iTo - iFrom)" is rather high;
+     especially where ((iFrom==1) && (iTo==65535)); eg. an empty SHOW !!! */
+
+  if (pData->iBreakpoint == 3)         /* previously broken during cycle-mode ? */
+  {
+    pImage = mng_find_imageobject (pData, pData->iSHOWnextid);
+                 
+    if (pImage)                        /* still there ? */
+      mng_display_image (pData, pImage, MNG_FALSE);
+
+    pData->iBreakpoint = 0;            /* let's not go through this again! */
+  }
+  else
+  {
+    if (pData->iBreakpoint)            /* previously broken at other point ? */
+    {                                  /* restore last parms */
+      iFrom = (mng_int16)pData->iSHOWfromid;
+      iTo   = (mng_int16)pData->iSHOWtoid;
+      iX    = (mng_int16)pData->iSHOWnextid;
+      iS    = (mng_int16)pData->iSHOWskip;
+    }
+    else
+    {                                  /* regular sequence ? */
+      if (pData->iSHOWtoid >= pData->iSHOWfromid)
+        iS  = 1;
+      else                             /* reverse sequence ! */
+        iS  = -1;
+
+      iFrom = (mng_int16)pData->iSHOWfromid;
+      iTo   = (mng_int16)pData->iSHOWtoid;
+      iX    = iFrom;
+
+      pData->iSHOWfromid = (mng_uint16)iFrom;
+      pData->iSHOWtoid   = (mng_uint16)iTo;
+      pData->iSHOWskip   = iS;
+    }
+                                       /* cycle mode ? */
+    if ((pData->iSHOWmode == 6) || (pData->iSHOWmode == 7))
+    {
+      mng_uint16 iTrigger = 0;
+      mng_uint16 iFound   = 0;
+      mng_uint16 iPass    = 0;
+      mng_imagep pFound   = 0;
+
+      do
+      {
+        iPass++;                       /* lets prevent endless loops when there
+                                          are no potential candidates in the list! */
+
+        if (iS > 0)                    /* forward ? */
+        {
+          for (iX = iFrom; iX <= iTo; iX += iS)
+          {
+            pImage = mng_find_imageobject (pData, (mng_uint16)iX);
+                         
+            if (pImage)                /* object exists ? */
+            {
+              if (iFound)              /* already found a candidate ? */
+                pImage->bVisible = MNG_FALSE;
+              else
+              if (iTrigger)            /* found the trigger ? */
+              {
+                pImage->bVisible = MNG_TRUE;
+                iFound           = iX;
+                pFound           = pImage;
+              }
+              else
+              if (pImage->bVisible)    /* ok, this is the trigger */
+              {
+                pImage->bVisible = MNG_FALSE;
+                iTrigger         = iX;
+              }
+            }
+          }
+        }
+        else
+        {
+          for (iX = iFrom; iX >= iTo; iX += iS)
+          {
+            pImage = mng_find_imageobject (pData, (mng_uint16)iX);
+                         
+            if (pImage)                /* object exists ? */
+            {
+              if (iFound)              /* already found a candidate ? */
+                pImage->bVisible = MNG_FALSE;
+              else
+              if (iTrigger)            /* found the trigger ? */
+              {
+                pImage->bVisible = MNG_TRUE;
+                iFound           = iX;
+                pFound           = pImage;
+              }
+              else
+              if (pImage->bVisible)    /* ok, this is the trigger */
+              {
+                pImage->bVisible = MNG_FALSE;
+                iTrigger         = iX;
+              }
+            }
+          }
+        }
+
+        if (!iTrigger)                 /* did not find a trigger ? */
+          iTrigger = 1;                /* then fake it so the first image
+                                          gets nominated */
+      }                                /* cycle back to beginning ? */
+      while ((iPass < 2) && (iTrigger) && (!iFound));
+
+      pData->iBreakpoint = 0;          /* just a sanity precaution */
+                                       /* display it ? */
+      if ((pData->iSHOWmode == 6) && (pFound))
+      {
+        mng_display_image (pData, pFound, MNG_FALSE);
+
+        if (pData->bTimerset)          /* timer set ? */
+        {
+          pData->iBreakpoint = 3;
+          pData->iSHOWnextid = iFound; /* save it for after the break */
+        }
+      }
+    }
+    else
+    {
+      do
+      {
+        pImage = mng_find_imageobject (pData, iX);
+                     
+        if (pImage)                    /* object exists ? */
+        {
+          if (pData->iBreakpoint)      /* did we get broken last time ? */
+          {                            /* could only happen in the display routine */
+            mng_display_image (pData, pImage, MNG_FALSE);
+            pData->iBreakpoint = 0;    /* only once inside this loop please ! */
+          }
+          else
+          {
+            switch (pData->iSHOWmode)  /* do what ? */
+            {
+              case 0 : {
+                         pImage->bVisible = MNG_TRUE;
+                         mng_display_image (pData, pImage, MNG_FALSE);
+                         break;
+                       }
+              case 1 : {
+                         pImage->bVisible = MNG_FALSE;
+                         break;
+                       }
+              case 2 : {
+                         if (pImage->bVisible)
+                           mng_display_image (pData, pImage, MNG_FALSE);
+                         break;
+                       }
+              case 3 : {
+                         pImage->bVisible = MNG_TRUE;
+                         break;
+                       }
+              case 4 : {
+                         pImage->bVisible = (mng_bool)(!pImage->bVisible);
+                         if (pImage->bVisible)
+                           mng_display_image (pData, pImage, MNG_FALSE);
+                         break;
+                       }
+              case 5 : {
+                         pImage->bVisible = (mng_bool)(!pImage->bVisible);
+                       }
+            }
+          }
+        }
+
+        if (!pData->bTimerset)         /* next ? */
+          iX += iS;
+
+      }                                /* continue ? */
+      while ((!pData->bTimerset) && (((iS > 0) && (iX <= iTo)) ||
+                                     ((iS < 0) && (iX >= iTo))    ));
+
+      if (pData->bTimerset)            /* timer set ? */
+      {
+        pData->iBreakpoint = 4;
+        pData->iSHOWnextid = iX;       /* save for next time */
+      }
+      else
+        pData->iBreakpoint = 0;
+        
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SHOW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode mng_process_display_save (mng_datap pData)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SAVE, MNG_LC_START);
+#endif
+
+  iRetcode = save_state (pData);       /* save the current state */
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_retcode mng_process_display_seek (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SEEK, MNG_LC_START);
+#endif
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+  if (pData->bStopafterseek)           /* need to stop after this SEEK ? */
+  {
+    pData->bFreezing      = MNG_TRUE;  /* stop processing on this one */
+    pData->bRunningevent  = MNG_FALSE;
+    pData->bStopafterseek = MNG_FALSE;
+    pData->bNeedrefresh   = MNG_TRUE;  /* make sure the last bit is displayed ! */
+  }
+  else
+#endif
+  {                                    /* restore the initial or SAVE state */
+    mng_retcode iRetcode = restore_state (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+                                       /* stop after next SEEK ? */
+    if ((pData->bDynamic) || (pData->bRunningevent))
+      pData->bStopafterseek = MNG_TRUE;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SEEK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+mng_retcode mng_process_display_jhdr (mng_datap pData)
+{                                      /* address the current "object" if any */
+  mng_imagep  pImage   = (mng_imagep)pData->pCurrentobj;
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JHDR, MNG_LC_START);
+#endif
+
+  if (!pData->bHasDHDR)
+  {
+    pData->fInitrowproc  = MNG_NULL;   /* do nothing by default */
+    pData->fDisplayrow   = MNG_NULL;
+    pData->fCorrectrow   = MNG_NULL;
+    pData->fStorerow     = MNG_NULL;
+    pData->fProcessrow   = MNG_NULL;
+    pData->fDifferrow    = MNG_NULL;
+    pData->fStorerow2    = MNG_NULL;
+    pData->fStorerow3    = MNG_NULL;
+
+    pData->pStoreobj     = MNG_NULL;   /* initialize important work-parms */
+
+    pData->iJPEGrow      = 0;
+    pData->iJPEGalpharow = 0;
+    pData->iJPEGrgbrow   = 0;
+    pData->iRowmax       = 0;          /* so init_rowproc does the right thing ! */
+  }
+
+  if (!pData->iBreakpoint)             /* not previously broken ? */
+  {
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* delta-image ? */
+    {
+      if (pData->iDeltatype == MNG_DELTATYPE_REPLACE)
+      {
+        iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pDeltaImage,
+                                             pData->iDatawidth, pData->iDataheight,
+                                             pData->iJHDRimgbitdepth, pData->iJHDRcolortype,
+                                             pData->iJHDRalphacompression, pData->iJHDRalphafilter,
+                                             pData->iJHDRalphainterlace, MNG_TRUE);
+
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphabitdepth    = pData->iJHDRalphabitdepth;
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iJHDRcompression  = pData->iJHDRimgcompression;
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iJHDRinterlace    = pData->iJHDRimginterlace;
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth;
+      }
+      else
+      if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD    ) ||
+          (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+      {
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iPixelsampledepth = pData->iJHDRimgbitdepth;
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth;
+      }
+      else
+      if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD    ) ||
+          (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)    )
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth;
+      else
+      if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD    ) ||
+          (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)    )
+        ((mng_imagep)pData->pDeltaImage)->pImgbuf->iPixelsampledepth = pData->iJHDRimgbitdepth;
+        
+    }
+    else
+#endif /* MNG_NO_DELTA_PNG */
+    {
+      if (pImage)                      /* update object buffer ? */
+      {
+        iRetcode = mng_reset_object_details (pData, pImage,
+                                             pData->iDatawidth, pData->iDataheight,
+                                             pData->iJHDRimgbitdepth, pData->iJHDRcolortype,
+                                             pData->iJHDRalphacompression, pData->iJHDRalphafilter,
+                                             pData->iJHDRalphainterlace, MNG_TRUE);
+
+        pImage->pImgbuf->iAlphabitdepth    = pData->iJHDRalphabitdepth;
+        pImage->pImgbuf->iJHDRcompression  = pData->iJHDRimgcompression;
+        pImage->pImgbuf->iJHDRinterlace    = pData->iJHDRimginterlace;
+        pImage->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth;
+      }
+      else                             /* update object 0 */
+      {
+        iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pObjzero,
+                                             pData->iDatawidth, pData->iDataheight,
+                                             pData->iJHDRimgbitdepth, pData->iJHDRcolortype,
+                                             pData->iJHDRalphacompression, pData->iJHDRalphafilter,
+                                             pData->iJHDRalphainterlace, MNG_TRUE);
+
+        ((mng_imagep)pData->pObjzero)->pImgbuf->iAlphabitdepth    = pData->iJHDRalphabitdepth;
+        ((mng_imagep)pData->pObjzero)->pImgbuf->iJHDRcompression  = pData->iJHDRimgcompression;
+        ((mng_imagep)pData->pObjzero)->pImgbuf->iJHDRinterlace    = pData->iJHDRimginterlace;
+        ((mng_imagep)pData->pObjzero)->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth;
+      }
+    }
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+  if (!pData->bHasDHDR)
+  {                                    /* we're always storing a JPEG */
+    if (pImage)                        /* real object ? */
+      pData->pStoreobj = pImage;       /* tell the row routines */
+    else                               /* otherwise use object 0 */
+      pData->pStoreobj = pData->pObjzero;
+                                       /* display "on-the-fly" ? */
+    if (
+#ifndef MNG_SKIPCHUNK_MAGN
+         ( ((mng_imagep)pData->pStoreobj)->iMAGN_MethodX == 0) &&
+         ( ((mng_imagep)pData->pStoreobj)->iMAGN_MethodY == 0) &&
+#endif
+         ( (pData->eImagetype == mng_it_jng         ) ||
+           (((mng_imagep)pData->pStoreobj)->bVisible)    )       )
+    {
+      next_layer (pData);              /* that's a new layer then ! */
+
+      pData->iBreakpoint = 0;
+
+      if (pData->bTimerset)            /* timer break ? */
+        pData->iBreakpoint = 7;
+      else
+      if (pData->bRunning)             /* still running ? */
+      {                                /* anything to display ? */
+        if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt))
+        {
+          set_display_routine (pData); /* then determine display routine */
+                                       /* display from the object we store in */
+          pData->pRetrieveobj = pData->pStoreobj;
+        }
+      }
+    }
+  }
+
+  if (!pData->bTimerset)               /* no timer break ? */
+  {                                    /* default row initialization ! */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+    pData->ePng_imgtype=png_none;
+#endif
+    pData->fInitrowproc = (mng_fptr)mng_init_rowproc;
+
+    if ((!pData->bHasDHDR) || (pData->iDeltatype == MNG_DELTATYPE_REPLACE))
+    {                                  /* 8-bit JPEG ? */
+      if (pData->iJHDRimgbitdepth == 8)
+      {                                /* intermediate row is 8-bit deep */
+        pData->bIsRGBA16   = MNG_FALSE;
+        pData->iRowsamples = pData->iDatawidth;
+
+        switch (pData->iJHDRcolortype) /* determine pixel processing routines */
+        {
+          case MNG_COLORTYPE_JPEGGRAY :
+               {
+                 pData->fStorerow2   = (mng_fptr)mng_store_jpeg_g8;
+                 pData->fRetrieverow = (mng_fptr)mng_retrieve_g8;
+                 pData->bIsOpaque    = MNG_TRUE;
+                 break;
+               }
+          case MNG_COLORTYPE_JPEGCOLOR :
+               {
+                 pData->fStorerow2   = (mng_fptr)mng_store_jpeg_rgb8;
+                 pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8;
+                 pData->bIsOpaque    = MNG_TRUE;
+                 break;
+               }
+          case MNG_COLORTYPE_JPEGGRAYA :
+               {
+                 pData->fStorerow2   = (mng_fptr)mng_store_jpeg_ga8;
+                 pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8;
+                 pData->bIsOpaque    = MNG_FALSE;
+                 break;
+               }
+          case MNG_COLORTYPE_JPEGCOLORA :
+               {
+                 pData->fStorerow2   = (mng_fptr)mng_store_jpeg_rgba8;
+                 pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8;
+                 pData->bIsOpaque    = MNG_FALSE;
+                 break;
+               }
+        }
+      }
+#ifndef MNG_NO_16BIT_SUPPORT
+      else
+      {
+        pData->bIsRGBA16 = MNG_TRUE;   /* intermediate row is 16-bit deep */
+
+        /* TODO: 12-bit JPEG */
+        /* TODO: 8- + 12-bit JPEG (eg. type=20) */
+
+      }
+#endif
+                                       /* possible IDAT alpha-channel ? */
+      if (pData->iJHDRalphacompression == MNG_COMPRESSION_DEFLATE)
+      {
+                                       /* determine alpha processing routine */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+        pData->fInitrowproc = (mng_fptr)mng_init_rowproc;
+#endif
+        switch (pData->iJHDRalphabitdepth)
+        {
+#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+          case  1 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a1_ni;  break; }
+          case  2 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a2_ni;  break; }
+          case  4 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a4_ni;  break; }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+          case  8 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a8_ni;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+          case 16 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a16_ni; break; }
+#endif
+#else
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+          case  1 : { pData->ePng_imgtype = png_jpeg_a1;  break; }
+          case  2 : { pData->ePng_imgtype = png_jpeg_a2;  break; }
+          case  4 : { pData->ePng_imgtype = png_jpeg_a4;  break; }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+          case  8 : { pData->ePng_imgtype = png_jpeg_a8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+          case 16 : { pData->ePng_imgtype = png_jpeg_a16; break; }
+#endif
+#endif
+        }
+      }
+      else                             /* possible JDAA alpha-channel ? */
+      if (pData->iJHDRalphacompression == MNG_COMPRESSION_BASELINEJPEG)
+      {                                /* 8-bit JPEG ? */
+        if (pData->iJHDRimgbitdepth == 8)
+        {
+          if (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA)
+            pData->fStorerow3 = (mng_fptr)mng_store_jpeg_g8_alpha;
+          else
+          if (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)
+            pData->fStorerow3 = (mng_fptr)mng_store_jpeg_rgb8_alpha;
+        }
+        else
+        {
+          /* TODO: 12-bit JPEG with 8-bit JDAA */
+        }
+      }
+                                       /* initialize JPEG library */
+      iRetcode = mngjpeg_initialize (pData);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+    else
+    {                                  /* must be alpha add/replace !! */
+      if ((pData->iDeltatype != MNG_DELTATYPE_BLOCKALPHAADD    ) &&
+          (pData->iDeltatype != MNG_DELTATYPE_BLOCKALPHAREPLACE)    )
+        MNG_ERROR (pData, MNG_INVDELTATYPE);
+                                       /* determine alpha processing routine */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+        pData->fInitrowproc = (mng_fptr)mng_init_rowproc;
+#endif
+      switch (pData->iJHDRalphabitdepth)
+      {
+#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+        case  1 : { pData->fInitrowproc = (mng_fptr)mng_init_g1_ni;  break; }
+        case  2 : { pData->fInitrowproc = (mng_fptr)mng_init_g2_ni;  break; }
+        case  4 : { pData->fInitrowproc = (mng_fptr)mng_init_g4_ni;  break; }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+        case  8 : { pData->fInitrowproc = (mng_fptr)mng_init_g8_ni;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+        case 16 : { pData->fInitrowproc = (mng_fptr)mng_init_g16_ni; break; }
+#endif
+#else
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+        case  1 : { pData->ePng_imgtype = png_jpeg_a1;  break; }
+        case  2 : { pData->ePng_imgtype = png_jpeg_a2;  break; }
+        case  4 : { pData->ePng_imgtype = png_jpeg_a4;  break; }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+        case  8 : { pData->ePng_imgtype = png_jpeg_a8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+        case 16 : { pData->ePng_imgtype = png_jpeg_a16; break; }
+#endif
+#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */
+      }
+    }
+
+    pData->iFilterofs = 0;             /* determine filter characteristics */
+    pData->iLevel0    = 0;             /* default levels */
+    pData->iLevel1    = 0;    
+    pData->iLevel2    = 0;
+    pData->iLevel3    = 0;
+
+#ifdef FILTER192                       /* leveling & differing ? */
+    if (pData->iJHDRalphafilter == 0xC0)
+    {
+       if (pData->iJHDRalphabitdepth <= 8)
+         pData->iFilterofs = 1;
+       else
+         pData->iFilterofs = 2;
+
+    }
+#endif
+#ifdef FILTER193                       /* no adaptive filtering ? */
+    if (pData->iJHDRalphafilter == 0xC1)
+      pData->iPixelofs = pData->iFilterofs;
+    else
+#endif
+      pData->iPixelofs = pData->iFilterofs + 1;
+
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_jdaa (mng_datap  pData,
+                                      mng_uint32 iRawlen,
+                                      mng_uint8p pRawdata)
+#else
+mng_retcode mng_process_display_jdaa (mng_datap  pData)
+#endif
+{
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JDAA, MNG_LC_START);
+#endif
+
+  if (!pData->bJPEGdecompress2)        /* if we're not decompressing already */
+  {
+    if (pData->fInitrowproc)           /* initialize row-processing? */
+    {
+      iRetcode = ((mng_initrowproc)pData->fInitrowproc) (pData);
+      pData->fInitrowproc = MNG_NULL;  /* only call this once !!! */
+    }
+
+    if (!iRetcode)                     /* initialize decompress */
+      iRetcode = mngjpeg_decompressinit2 (pData);
+  }
+
+  if (!iRetcode)                       /* all ok? then decompress, my man */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    iRetcode = mngjpeg_decompressdata2 (pData, iRawlen, pRawdata);
+#else
+    iRetcode = mngjpeg_decompressdata2 (pData, pData->iRawlen, pData->pRawdata);
+#endif
+
+  if (iRetcode)
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JDAA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_jdat (mng_datap  pData,
+                                      mng_uint32 iRawlen,
+                                      mng_uint8p pRawdata)
+#else
+mng_retcode mng_process_display_jdat (mng_datap  pData)
+#endif
+{
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JDAT, MNG_LC_START);
+#endif
+
+  if (pData->bRestorebkgd)             /* need to restore the background ? */
+  {
+    pData->bRestorebkgd = MNG_FALSE;
+    iRetcode            = load_bkgdlayer (pData);
+
+    pData->iLayerseq++;                /* and it counts as a layer then ! */
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+  if (!pData->bJPEGdecompress)         /* if we're not decompressing already */
+  {
+    if (pData->fInitrowproc)           /* initialize row-processing? */
+    {
+      iRetcode = ((mng_initrowproc)pData->fInitrowproc) (pData);
+      pData->fInitrowproc = MNG_NULL;  /* only call this once !!! */
+    }
+
+    if (!iRetcode)                     /* initialize decompress */
+      iRetcode = mngjpeg_decompressinit (pData);
+  }
+
+  if (!iRetcode)                       /* all ok? then decompress, my man */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    iRetcode = mngjpeg_decompressdata (pData, iRawlen, pRawdata);
+#else
+    iRetcode = mngjpeg_decompressdata (pData, pData->iRawlen, pData->pRawdata);
+#endif
+
+  if (iRetcode)
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_dhdr (mng_datap  pData,
+                                      mng_uint16 iObjectid,
+                                      mng_uint8  iImagetype,
+                                      mng_uint8  iDeltatype,
+                                      mng_uint32 iBlockwidth,
+                                      mng_uint32 iBlockheight,
+                                      mng_uint32 iBlockx,
+                                      mng_uint32 iBlocky)
+#else
+mng_retcode mng_process_display_dhdr (mng_datap  pData)
+#endif
+{
+  mng_imagep  pImage;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DHDR, MNG_LC_START);
+#endif
+
+  pData->fInitrowproc     = MNG_NULL;  /* do nothing by default */
+  pData->fDisplayrow      = MNG_NULL;
+  pData->fCorrectrow      = MNG_NULL;
+  pData->fStorerow        = MNG_NULL;
+  pData->fProcessrow      = MNG_NULL;
+  pData->pStoreobj        = MNG_NULL;
+
+  pData->fDeltagetrow     = MNG_NULL;
+  pData->fDeltaaddrow     = MNG_NULL;
+  pData->fDeltareplacerow = MNG_NULL;
+  pData->fDeltaputrow     = MNG_NULL;
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  pImage = mng_find_imageobject (pData, iObjectid);
+#else
+  pImage = mng_find_imageobject (pData, pData->iDHDRobjectid);
+#endif
+
+  if (pImage)                          /* object exists ? */
+  {
+    if (pImage->pImgbuf->bConcrete)    /* is it concrete ? */
+    {                                  /* previous magnification to be done ? */
+#ifndef MNG_SKIPCHUNK_MAGN
+      if ((pImage->iMAGN_MethodX) || (pImage->iMAGN_MethodY))
+      {
+        iRetcode = mng_magnify_imageobject (pData, pImage);
+                       
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+      }
+#endif
+                                       /* save delta fields */
+      pData->pDeltaImage           = (mng_ptr)pImage;
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      pData->iDeltaImagetype       = iImagetype;
+      pData->iDeltatype            = iDeltatype;
+      pData->iDeltaBlockwidth      = iBlockwidth;
+      pData->iDeltaBlockheight     = iBlockheight;
+      pData->iDeltaBlockx          = iBlockx;
+      pData->iDeltaBlocky          = iBlocky;
+#else
+      pData->iDeltaImagetype       = pData->iDHDRimagetype;
+      pData->iDeltatype            = pData->iDHDRdeltatype;
+      pData->iDeltaBlockwidth      = pData->iDHDRblockwidth;
+      pData->iDeltaBlockheight     = pData->iDHDRblockheight;
+      pData->iDeltaBlockx          = pData->iDHDRblockx;
+      pData->iDeltaBlocky          = pData->iDHDRblocky;
+#endif
+                                       /* restore target-object fields */
+      pData->iDatawidth            = pImage->pImgbuf->iWidth;
+      pData->iDataheight           = pImage->pImgbuf->iHeight;
+      pData->iBitdepth             = pImage->pImgbuf->iBitdepth;
+      pData->iColortype            = pImage->pImgbuf->iColortype;
+      pData->iCompression          = pImage->pImgbuf->iCompression;
+      pData->iFilter               = pImage->pImgbuf->iFilter;
+      pData->iInterlace            = pImage->pImgbuf->iInterlace;
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      if ((iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD    ) ||
+          (iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+        pData->iBitdepth           = pImage->pImgbuf->iPixelsampledepth;
+      else
+      if ((iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD    ) ||
+          (iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)    )
+        pData->iBitdepth           = pImage->pImgbuf->iAlphasampledepth;
+      else
+      if ((iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD    ) ||
+          (iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)    )
+        pData->iBitdepth           = pImage->pImgbuf->iPixelsampledepth;
+#else
+      if ((pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKPIXELADD    ) ||
+          (pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+        pData->iBitdepth           = pImage->pImgbuf->iPixelsampledepth;
+      else
+      if ((pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKALPHAADD    ) ||
+          (pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)    )
+        pData->iBitdepth           = pImage->pImgbuf->iAlphasampledepth;
+      else
+      if ((pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKCOLORADD    ) ||
+          (pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)    )
+        pData->iBitdepth           = pImage->pImgbuf->iPixelsampledepth;
+#endif
+
+#ifdef MNG_INCLUDE_JNG
+      pData->iJHDRimgbitdepth      = pImage->pImgbuf->iBitdepth;
+      pData->iJHDRcolortype        = pImage->pImgbuf->iColortype;
+      pData->iJHDRimgcompression   = pImage->pImgbuf->iJHDRcompression;
+      pData->iJHDRimginterlace     = pImage->pImgbuf->iJHDRinterlace;
+      pData->iJHDRalphacompression = pImage->pImgbuf->iCompression;
+      pData->iJHDRalphafilter      = pImage->pImgbuf->iFilter;
+      pData->iJHDRalphainterlace   = pImage->pImgbuf->iInterlace;
+      pData->iJHDRalphabitdepth    = pImage->pImgbuf->iAlphabitdepth;
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                                       /* block size specified ? */
+      if (iDeltatype != MNG_DELTATYPE_NOCHANGE)
+      {                                /* block entirely within target ? */
+        if (iDeltatype != MNG_DELTATYPE_REPLACE)
+        {
+          if (((iBlockx + iBlockwidth ) > pData->iDatawidth ) ||
+              ((iBlocky + iBlockheight) > pData->iDataheight)    )
+            MNG_ERROR (pData, MNG_INVALIDBLOCK);
+        }
+
+        pData->iDatawidth          = iBlockwidth;
+        pData->iDataheight         = iBlockheight;
+      }
+#else
+                                       /* block size specified ? */
+      if (pData->iDHDRdeltatype != MNG_DELTATYPE_NOCHANGE)
+      {                                /* block entirely within target ? */
+        if (pData->iDHDRdeltatype != MNG_DELTATYPE_REPLACE)
+        {
+          if (((pData->iDHDRblockx + pData->iDHDRblockwidth ) > pData->iDatawidth ) ||
+              ((pData->iDHDRblocky + pData->iDHDRblockheight) > pData->iDataheight)    )
+            MNG_ERROR (pData, MNG_INVALIDBLOCK);
+        }
+
+        pData->iDatawidth          = pData->iDHDRblockwidth;
+        pData->iDataheight         = pData->iDHDRblockheight;
+      }
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      switch (iDeltatype)              /* determine nr of delta-channels */
+#else
+      switch (pData->iDHDRdeltatype)   /* determine nr of delta-channels */
+#endif
+      {
+         case MNG_DELTATYPE_BLOCKALPHAADD : ;
+         case MNG_DELTATYPE_BLOCKALPHAREPLACE :
+              {
+#ifdef MNG_INCLUDE_JNG
+                if ((pData->iColortype     == MNG_COLORTYPE_GRAYA    ) ||
+                    (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA)    )
+                {
+                  pData->iColortype     = MNG_COLORTYPE_GRAY;
+                  pData->iJHDRcolortype = MNG_COLORTYPE_JPEGGRAY;
+                }
+                else
+                if ((pData->iColortype     == MNG_COLORTYPE_RGBA      ) ||
+                    (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    )
+                {
+                  pData->iColortype     = MNG_COLORTYPE_GRAY;
+                  pData->iJHDRcolortype = MNG_COLORTYPE_JPEGGRAY;
+                }
+#else
+                if (pData->iColortype      == MNG_COLORTYPE_GRAYA)
+                  pData->iColortype     = MNG_COLORTYPE_GRAY;
+                else
+                if (pData->iColortype      == MNG_COLORTYPE_RGBA)
+                  pData->iColortype     = MNG_COLORTYPE_GRAY;
+#endif
+                else                   /* target has no alpha; that sucks! */
+                  MNG_ERROR (pData, MNG_TARGETNOALPHA);
+
+                break;
+              }
+
+         case MNG_DELTATYPE_BLOCKCOLORADD : ;
+         case MNG_DELTATYPE_BLOCKCOLORREPLACE :
+              {
+#ifdef MNG_INCLUDE_JNG
+                if ((pData->iColortype     == MNG_COLORTYPE_GRAYA    ) ||
+                    (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA)    )
+                {
+                  pData->iColortype     = MNG_COLORTYPE_GRAY;
+                  pData->iJHDRcolortype = MNG_COLORTYPE_JPEGGRAY;
+                }
+                else
+                if ((pData->iColortype     == MNG_COLORTYPE_RGBA      ) ||
+                    (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    )
+                {
+                  pData->iColortype     = MNG_COLORTYPE_RGB;
+                  pData->iJHDRcolortype = MNG_COLORTYPE_JPEGCOLOR;
+                }
+#else
+                if (pData->iColortype == MNG_COLORTYPE_GRAYA)
+                  pData->iColortype = MNG_COLORTYPE_GRAY;
+                else
+                if (pData->iColortype == MNG_COLORTYPE_RGBA)
+                  pData->iColortype = MNG_COLORTYPE_RGB;
+#endif                  
+                else                   /* target has no alpha; that sucks! */
+                  MNG_ERROR (pData, MNG_TARGETNOALPHA);
+
+                break;
+              }
+
+      }
+                                       /* full image replace ? */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      if (iDeltatype == MNG_DELTATYPE_REPLACE)
+#else
+      if (pData->iDHDRdeltatype == MNG_DELTATYPE_REPLACE)
+#endif
+      {
+        iRetcode = mng_reset_object_details (pData, pImage,
+                                             pData->iDatawidth, pData->iDataheight,
+                                             pData->iBitdepth, pData->iColortype,
+                                             pData->iCompression, pData->iFilter,
+                                             pData->iInterlace, MNG_FALSE);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+
+        pData->pStoreobj = pImage;     /* and store straight into this object */
+      }
+      else
+      {
+        mng_imagedatap pBufzero, pBuf;
+                                       /* we store in object 0 and process it later */
+        pData->pStoreobj = pData->pObjzero;
+                                       /* make sure to initialize object 0 then */
+        iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pObjzero,
+                                             pData->iDatawidth, pData->iDataheight,
+                                             pData->iBitdepth, pData->iColortype,
+                                             pData->iCompression, pData->iFilter,
+                                             pData->iInterlace, MNG_TRUE);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+
+        pBuf     = pImage->pImgbuf;    /* copy possible palette & cheap transparency */
+        pBufzero = ((mng_imagep)pData->pObjzero)->pImgbuf;
+
+        pBufzero->bHasPLTE = pBuf->bHasPLTE;
+        pBufzero->bHasTRNS = pBuf->bHasTRNS;
+
+        if (pBufzero->bHasPLTE)        /* copy palette ? */
+        {
+          mng_uint32 iX;
+
+          pBufzero->iPLTEcount = pBuf->iPLTEcount;
+
+          for (iX = 0; iX < pBuf->iPLTEcount; iX++)
+          {
+            pBufzero->aPLTEentries [iX].iRed   = pBuf->aPLTEentries [iX].iRed;
+            pBufzero->aPLTEentries [iX].iGreen = pBuf->aPLTEentries [iX].iGreen;
+            pBufzero->aPLTEentries [iX].iBlue  = pBuf->aPLTEentries [iX].iBlue;
+          }
+        }
+
+        if (pBufzero->bHasTRNS)        /* copy cheap transparency ? */
+        {
+          pBufzero->iTRNSgray  = pBuf->iTRNSgray;
+          pBufzero->iTRNSred   = pBuf->iTRNSred;
+          pBufzero->iTRNSgreen = pBuf->iTRNSgreen;
+          pBufzero->iTRNSblue  = pBuf->iTRNSblue;
+          pBufzero->iTRNScount = pBuf->iTRNScount;
+
+          MNG_COPY (pBufzero->aTRNSentries, pBuf->aTRNSentries,
+                    sizeof (pBufzero->aTRNSentries));
+        }
+                                       /* process immediately if bitdepth & colortype are equal */
+        pData->bDeltaimmediate =
+          (mng_bool)((pData->bDisplaying) && (!pData->bSkipping) &&
+                     ((pData->bRunning) || (pData->bSearching)) &&
+                     (pData->iBitdepth  == ((mng_imagep)pData->pDeltaImage)->pImgbuf->iBitdepth ) &&
+                     (pData->iColortype == ((mng_imagep)pData->pDeltaImage)->pImgbuf->iColortype)    );
+      }
+ 
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+  pData->fInitrowproc = (mng_fptr)mng_init_rowproc;
+  pData->ePng_imgtype = mng_png_imgtype (pData->iColortype, pData->iBitdepth);
+#else
+      switch (pData->iColortype)       /* determine row initialization routine */
+      {
+        case 0 : {                     /* gray */
+                   switch (pData->iBitdepth)
+                   {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                     case  1 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g1_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g1_i;
+
+                                 break;
+                               }
+                     case  2 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g2_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g2_i;
+
+                                 break;
+                               }
+                     case  4 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g4_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g4_i;
+
+                                 break;
+                               }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                     case  8 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g8_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g8_i;
+
+                                 break;
+                               }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g16_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_g16_i;
+
+                                 break;
+                               }
+#endif
+                   }
+
+                   break;
+                 }
+        case 2 : {                     /* rgb */
+                   switch (pData->iBitdepth)
+                   {
+                     case  8 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_rgb8_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_rgb8_i;
+
+                                 break;
+                               }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_rgb16_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_rgb16_i;
+
+                                 break;
+                               }
+#endif
+                   }
+
+                   break;
+                 }
+        case 3 : {                     /* indexed */
+                   switch (pData->iBitdepth)
+                   {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                     case  1 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_idx1_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_idx1_i;
+
+                                 break;
+                               }
+                     case  2 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_idx2_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_idx2_i;
+
+                                 break;
+                               }
+                     case  4 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_idx4_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_idx4_i;
+
+                                 break;
+                               }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+                     case  8 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_idx8_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_idx8_i;
+
+                                 break;
+                               }
+                   }
+
+                   break;
+                 }
+        case 4 : {                     /* gray+alpha */
+                   switch (pData->iBitdepth)
+                   {
+                     case  8 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_ga8_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_ga8_i;
+
+                                 break;
+                               }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_ga16_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_ga16_i;
+
+                                 break;
+                               }
+#endif
+                   }
+
+                   break;
+                 }
+        case 6 : {                     /* rgb+alpha */
+                   switch (pData->iBitdepth)
+                   {
+                     case  8 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_rgba8_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_rgba8_i;
+
+                                 break;
+                               }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : {
+                                 if (!pData->iInterlace)
+                                   pData->fInitrowproc = (mng_fptr)mng_init_rgba16_ni;
+                                 else
+                                   pData->fInitrowproc = (mng_fptr)mng_init_rgba16_i;
+
+                                 break;
+                               }
+#endif
+                   }
+
+                   break;
+                 }
+      }
+#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */
+    }
+    else
+      MNG_ERROR (pData, MNG_OBJNOTCONCRETE);
+
+  }
+  else
+    MNG_ERROR (pData, MNG_OBJECTUNKNOWN);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_prom (mng_datap  pData,
+                                      mng_uint8  iBitdepth,
+                                      mng_uint8  iColortype,
+                                      mng_uint8  iFilltype)
+#else
+mng_retcode mng_process_display_prom (mng_datap  pData)
+#endif
+{
+  mng_imagep     pImage;
+  mng_imagedatap pBuf;
+  mng_retcode    iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PROM, MNG_LC_START);
+#endif
+
+  if (!pData->pDeltaImage)             /* gotta have this now! */
+    MNG_ERROR (pData, MNG_INVALIDDELTA);
+
+  pImage = (mng_imagep)pData->pDeltaImage;
+  pBuf   = pImage->pImgbuf;
+                                       /* can't demote bitdepth! */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  if (iBitdepth < pBuf->iBitdepth)
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if ( ((pBuf->iColortype == MNG_COLORTYPE_GRAY      ) &&
+        (iColortype       != MNG_COLORTYPE_GRAY      ) &&
+        (iColortype       != MNG_COLORTYPE_GRAYA     ) &&
+        (iColortype       != MNG_COLORTYPE_RGB       ) &&
+        (iColortype       != MNG_COLORTYPE_RGBA      )    ) ||
+       ((pBuf->iColortype == MNG_COLORTYPE_GRAYA     ) &&
+        (iColortype       != MNG_COLORTYPE_GRAYA     ) &&
+        (iColortype       != MNG_COLORTYPE_RGBA      )    ) ||
+       ((pBuf->iColortype == MNG_COLORTYPE_RGB       ) &&
+        (iColortype       != MNG_COLORTYPE_RGB       ) &&
+        (iColortype       != MNG_COLORTYPE_RGBA      )    ) ||
+       ((pBuf->iColortype == MNG_COLORTYPE_RGBA      ) &&
+        (iColortype       != MNG_COLORTYPE_RGBA      )    ) ||
+#ifdef MNG_INCLUDE_JNG
+       ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY  ) &&
+        (iColortype       != MNG_COLORTYPE_JPEGGRAY  ) &&
+        (iColortype       != MNG_COLORTYPE_JPEGCOLOR ) &&
+        (iColortype       != MNG_COLORTYPE_JPEGGRAYA ) &&
+        (iColortype       != MNG_COLORTYPE_JPEGCOLORA)    ) ||
+       ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLOR ) &&
+        (iColortype       != MNG_COLORTYPE_JPEGCOLOR ) &&
+        (iColortype       != MNG_COLORTYPE_JPEGCOLORA)    ) ||
+       ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAYA ) &&
+        (iColortype       != MNG_COLORTYPE_JPEGGRAYA ) &&
+        (iColortype       != MNG_COLORTYPE_JPEGCOLORA)    ) ||
+       ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLORA) &&
+        (iColortype       != MNG_COLORTYPE_JPEGCOLORA)    ) ||
+#endif
+       ((pBuf->iColortype == MNG_COLORTYPE_INDEXED   ) &&
+        (iColortype       != MNG_COLORTYPE_INDEXED   ) &&
+        (iColortype       != MNG_COLORTYPE_RGB       ) &&
+        (iColortype       != MNG_COLORTYPE_RGBA      )    )    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  iRetcode = mng_promote_imageobject (pData, pImage, iBitdepth, iColortype, iFilltype);
+#else
+  if (pData->iPROMbitdepth < pBuf->iBitdepth)
+    MNG_ERROR (pData, MNG_INVALIDBITDEPTH);
+
+  if ( ((pBuf->iColortype      == MNG_COLORTYPE_GRAY      ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_GRAY      ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_GRAYA     ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_RGB       ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_RGBA      )    ) ||
+       ((pBuf->iColortype      == MNG_COLORTYPE_GRAYA     ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_GRAYA     ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_RGBA      )    ) ||
+       ((pBuf->iColortype      == MNG_COLORTYPE_RGB       ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_RGB       ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_RGBA      )    ) ||
+       ((pBuf->iColortype      == MNG_COLORTYPE_RGBA      ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_RGBA      )    ) ||
+#ifdef MNG_INCLUDE_JNG
+       ((pBuf->iColortype      == MNG_COLORTYPE_JPEGGRAY  ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_JPEGGRAY  ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLOR ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_JPEGGRAYA ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLORA)    ) ||
+       ((pBuf->iColortype      == MNG_COLORTYPE_JPEGCOLOR ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLOR ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLORA)    ) ||
+       ((pBuf->iColortype      == MNG_COLORTYPE_JPEGGRAYA ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_JPEGGRAYA ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLORA)    ) ||
+       ((pBuf->iColortype      == MNG_COLORTYPE_JPEGCOLORA) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLORA)    ) ||
+#endif
+       ((pBuf->iColortype      == MNG_COLORTYPE_INDEXED   ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_INDEXED   ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_RGB       ) &&
+        (pData->iPROMcolortype != MNG_COLORTYPE_RGBA      )    )    )
+    MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+  iRetcode = mng_promote_imageobject (pData, pImage, pData->iPROMbitdepth,
+                                      pData->iPROMcolortype, pData->iPROMfilltype);
+#endif
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PROM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_process_display_ipng (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IPNG, MNG_LC_START);
+#endif
+                                       /* indicate it for what it is now */
+  pData->iDeltaImagetype = MNG_IMAGETYPE_PNG;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+mng_retcode mng_process_display_ijng (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IJNG, MNG_LC_START);
+#endif
+                                       /* indicate it for what it is now */
+  pData->iDeltaImagetype = MNG_IMAGETYPE_JNG;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IJNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_pplt (mng_datap      pData,
+                                      mng_uint8      iType,
+                                      mng_uint32     iCount,
+                                      mng_palette8ep paIndexentries,
+                                      mng_uint8p     paAlphaentries,
+                                      mng_uint8p     paUsedentries)
+#else
+mng_retcode mng_process_display_pplt (mng_datap      pData)
+#endif
+{
+  mng_uint32     iX;
+  mng_imagep     pImage = (mng_imagep)pData->pObjzero;
+  mng_imagedatap pBuf   = pImage->pImgbuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PPLT, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iX = iCount;
+#else
+  iX = pData->iPPLTcount;
+#endif
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  switch (iType)
+#else
+  switch (pData->iPPLTtype)
+#endif
+  {
+    case MNG_DELTATYPE_REPLACERGB :
+      {
+#ifdef MNG_DECREMENT_LOOPS
+        for (; iX > 0;iX--)
+#else
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+        for (iX = 0; iX < iCount; iX++)
+#else
+        for (iX = 0; iX < pData->iPPLTcount; iX++)
+#endif
+#endif
+        {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+          if (paUsedentries [iX])
+          {
+            pBuf->aPLTEentries [iX].iRed   = paIndexentries [iX].iRed;
+            pBuf->aPLTEentries [iX].iGreen = paIndexentries [iX].iGreen;
+            pBuf->aPLTEentries [iX].iBlue  = paIndexentries [iX].iBlue;
+          }
+#else
+          if (pData->paPPLTusedentries [iX])
+          {
+            pBuf->aPLTEentries [iX].iRed   = pData->paPPLTindexentries [iX].iRed;
+            pBuf->aPLTEentries [iX].iGreen = pData->paPPLTindexentries [iX].iGreen;
+            pBuf->aPLTEentries [iX].iBlue  = pData->paPPLTindexentries [iX].iBlue;
+          }
+#endif
+        }
+
+        break;
+      }
+    case MNG_DELTATYPE_DELTARGB :
+      {
+#ifdef MNG_DECREMENT_LOOPS
+        for (; iX > 0;iX--)
+#else
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+        for (iX = 0; iX < iCount; iX++)
+#else
+        for (iX = 0; iX < pData->iPPLTcount; iX++)
+#endif
+#endif
+        {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+          if (paUsedentries [iX])
+          {
+            pBuf->aPLTEentries [iX].iRed   =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iRed   +
+                                           paIndexentries [iX].iRed  );
+            pBuf->aPLTEentries [iX].iGreen =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iGreen +
+                                           paIndexentries [iX].iGreen);
+            pBuf->aPLTEentries [iX].iBlue  =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iBlue  +
+                                           paIndexentries [iX].iBlue );
+          }
+#else
+          if (pData->paPPLTusedentries [iX])
+          {
+            pBuf->aPLTEentries [iX].iRed   =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iRed   +
+                                           pData->paPPLTindexentries [iX].iRed  );
+            pBuf->aPLTEentries [iX].iGreen =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iGreen +
+                                           pData->paPPLTindexentries [iX].iGreen);
+            pBuf->aPLTEentries [iX].iBlue  =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iBlue  +
+                                           pData->paPPLTindexentries [iX].iBlue );
+          }
+#endif
+        }
+
+        break;
+      }
+    case MNG_DELTATYPE_REPLACEALPHA :
+      {
+#ifdef MNG_DECREMENT_LOOPS
+        for (; iX > 0;iX--)
+#else
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+        for (iX = 0; iX < iCount; iX++)
+#else
+        for (iX = 0; iX < pData->iPPLTcount; iX++)
+#endif
+#endif
+        {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+          if (paUsedentries [iX])
+            pBuf->aTRNSentries [iX] = paAlphaentries [iX];
+        }
+#else
+          if (pData->paPPLTusedentries [iX])
+            pBuf->aTRNSentries [iX] = pData->paPPLTalphaentries [iX];
+        }
+#endif
+
+        break;
+      }
+    case MNG_DELTATYPE_DELTAALPHA :
+      {
+#ifdef MNG_DECREMENT_LOOPS
+        for (; iX > 0;iX--)
+#else
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+        for (iX = 0; iX < iCount; iX++)
+#else
+        for (iX = 0; iX < pData->iPPLTcount; iX++)
+#endif
+#endif
+        {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+          if (paUsedentries [iX])
+            pBuf->aTRNSentries [iX] =
+                               (mng_uint8)(pBuf->aTRNSentries [iX] +
+                                           paAlphaentries [iX]);
+#else
+          if (pData->paPPLTusedentries [iX])
+            pBuf->aTRNSentries [iX] =
+                               (mng_uint8)(pBuf->aTRNSentries [iX] +
+                                           pData->paPPLTalphaentries [iX]);
+#endif
+        }
+
+        break;
+      }
+    case MNG_DELTATYPE_REPLACERGBA :
+      {
+#ifdef MNG_DECREMENT_LOOPS
+        for (; iX > 0;iX--)
+#else
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+        for (iX = 0; iX < iCount; iX++)
+#else
+        for (iX = 0; iX < pData->iPPLTcount; iX++)
+#endif
+#endif
+        {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+          if (paUsedentries [iX])
+          {
+            pBuf->aPLTEentries [iX].iRed   = paIndexentries [iX].iRed;
+            pBuf->aPLTEentries [iX].iGreen = paIndexentries [iX].iGreen;
+            pBuf->aPLTEentries [iX].iBlue  = paIndexentries [iX].iBlue;
+            pBuf->aTRNSentries [iX]        = paAlphaentries [iX];
+          }
+#else
+          if (pData->paPPLTusedentries [iX])
+          {
+            pBuf->aPLTEentries [iX].iRed   = pData->paPPLTindexentries [iX].iRed;
+            pBuf->aPLTEentries [iX].iGreen = pData->paPPLTindexentries [iX].iGreen;
+            pBuf->aPLTEentries [iX].iBlue  = pData->paPPLTindexentries [iX].iBlue;
+            pBuf->aTRNSentries [iX]        = pData->paPPLTalphaentries [iX];
+          }
+#endif
+        }
+
+        break;
+      }
+    case MNG_DELTATYPE_DELTARGBA :
+      {
+#ifdef MNG_DECREMENT_LOOPS
+        for (; iX > 0;iX--)
+#else
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+        for (iX = 0; iX < iCount; iX++)
+#else
+        for (iX = 0; iX < pData->iPPLTcount; iX++)
+#endif
+#endif
+        {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+          if (paUsedentries [iX])
+          {
+            pBuf->aPLTEentries [iX].iRed   =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iRed   +
+                                           paIndexentries [iX].iRed  );
+            pBuf->aPLTEentries [iX].iGreen =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iGreen +
+                                           paIndexentries [iX].iGreen);
+            pBuf->aPLTEentries [iX].iBlue  =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iBlue  +
+                                           paIndexentries [iX].iBlue );
+            pBuf->aTRNSentries [iX] =
+                               (mng_uint8)(pBuf->aTRNSentries [iX] +
+                                           paAlphaentries [iX]);
+          }
+#else
+          if (pData->paPPLTusedentries [iX])
+          {
+            pBuf->aPLTEentries [iX].iRed   =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iRed   +
+                                           pData->paPPLTindexentries [iX].iRed  );
+            pBuf->aPLTEentries [iX].iGreen =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iGreen +
+                                           pData->paPPLTindexentries [iX].iGreen);
+            pBuf->aPLTEentries [iX].iBlue  =
+                               (mng_uint8)(pBuf->aPLTEentries [iX].iBlue  +
+                                           pData->paPPLTindexentries [iX].iBlue );
+            pBuf->aTRNSentries [iX] =
+                               (mng_uint8)(pBuf->aTRNSentries [iX] +
+                                           pData->paPPLTalphaentries [iX]);
+          }
+#endif
+        }
+
+        break;
+      }
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  if ((iType != MNG_DELTATYPE_REPLACERGB) && (iType != MNG_DELTATYPE_DELTARGB))
+#else
+  if ((pData->iPPLTtype != MNG_DELTATYPE_REPLACERGB) &&
+      (pData->iPPLTtype != MNG_DELTATYPE_DELTARGB  )    )
+#endif
+  {
+    if (pBuf->bHasTRNS)
+    {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      if (iCount > pBuf->iTRNScount)
+        pBuf->iTRNScount = iCount;
+#else
+      if (pData->iPPLTcount > pBuf->iTRNScount)
+        pBuf->iTRNScount = pData->iPPLTcount;
+#endif
+    }
+    else
+    {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      pBuf->iTRNScount = iCount;
+      pBuf->bHasTRNS   = MNG_TRUE;
+#else
+      pBuf->iTRNScount = pData->iPPLTcount;
+      pBuf->bHasTRNS   = MNG_TRUE;
+#endif
+    }
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  if ((iType != MNG_DELTATYPE_REPLACEALPHA) && (iType != MNG_DELTATYPE_DELTAALPHA))
+#else
+  if ((pData->iPPLTtype != MNG_DELTATYPE_REPLACEALPHA) &&
+      (pData->iPPLTtype != MNG_DELTATYPE_DELTAALPHA  )    )
+#endif
+  {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    if (iCount > pBuf->iPLTEcount)
+      pBuf->iPLTEcount = iCount;
+#else
+    if (pData->iPPLTcount > pBuf->iPLTEcount)
+      pBuf->iPLTEcount = pData->iPPLTcount;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_magn (mng_datap  pData,
+                                      mng_uint16 iFirstid,
+                                      mng_uint16 iLastid,
+                                      mng_uint8  iMethodX,
+                                      mng_uint16 iMX,
+                                      mng_uint16 iMY,
+                                      mng_uint16 iML,
+                                      mng_uint16 iMR,
+                                      mng_uint16 iMT,
+                                      mng_uint16 iMB,
+                                      mng_uint8  iMethodY)
+#else
+mng_retcode mng_process_display_magn (mng_datap  pData)
+#endif
+{
+  mng_uint16 iX;
+  mng_imagep pImage;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MAGN, MNG_LC_START);
+#endif
+                                       /* iterate the object-ids */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  for (iX = iFirstid; iX <= iLastid; iX++)
+#else
+  for (iX = pData->iMAGNfirstid; iX <= pData->iMAGNlastid; iX++)
+#endif
+  {
+    if (iX == 0)                       /* process object 0 ? */
+    {
+      pImage = (mng_imagep)pData->pObjzero;
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      pImage->iMAGN_MethodX = iMethodX;
+      pImage->iMAGN_MethodY = iMethodY;
+      pImage->iMAGN_MX      = iMX;
+      pImage->iMAGN_MY      = iMY;
+      pImage->iMAGN_ML      = iML;
+      pImage->iMAGN_MR      = iMR;
+      pImage->iMAGN_MT      = iMT;
+      pImage->iMAGN_MB      = iMB;
+#else
+      pImage->iMAGN_MethodX = pData->iMAGNmethodX;
+      pImage->iMAGN_MethodY = pData->iMAGNmethodY;
+      pImage->iMAGN_MX      = pData->iMAGNmX;
+      pImage->iMAGN_MY      = pData->iMAGNmY;
+      pImage->iMAGN_ML      = pData->iMAGNmL;
+      pImage->iMAGN_MR      = pData->iMAGNmR;
+      pImage->iMAGN_MT      = pData->iMAGNmT;
+      pImage->iMAGN_MB      = pData->iMAGNmB;
+#endif
+    }
+    else
+    {
+      pImage = mng_find_imageobject (pData, iX);
+                                       /* object exists & is not frozen ? */
+      if ((pImage) && (!pImage->bFrozen))
+      {                                /* previous magnification to be done ? */
+        if ((pImage->iMAGN_MethodX) || (pImage->iMAGN_MethodY))
+        {
+          mng_retcode iRetcode = mng_magnify_imageobject (pData, pImage);
+          if (iRetcode)                /* on error bail out */
+            return iRetcode;
+        }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+        pImage->iMAGN_MethodX = iMethodX;
+        pImage->iMAGN_MethodY = iMethodY;
+        pImage->iMAGN_MX      = iMX;
+        pImage->iMAGN_MY      = iMY;
+        pImage->iMAGN_ML      = iML;
+        pImage->iMAGN_MR      = iMR;
+        pImage->iMAGN_MT      = iMT;
+        pImage->iMAGN_MB      = iMB;
+#else
+        pImage->iMAGN_MethodX = pData->iMAGNmethodX;
+        pImage->iMAGN_MethodY = pData->iMAGNmethodY;
+        pImage->iMAGN_MX      = pData->iMAGNmX;
+        pImage->iMAGN_MY      = pData->iMAGNmY;
+        pImage->iMAGN_ML      = pData->iMAGNmL;
+        pImage->iMAGN_MR      = pData->iMAGNmR;
+        pImage->iMAGN_MT      = pData->iMAGNmT;
+        pImage->iMAGN_MB      = pData->iMAGNmB;
+#endif
+      }
+    }
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  pData->iMAGNfromid = iFirstid;
+  pData->iMAGNtoid   = iLastid;
+  iX                 = iFirstid;
+#else
+  pData->iMAGNfromid = pData->iMAGNfirstid;
+  pData->iMAGNtoid   = pData->iMAGNlastid;
+  iX                 = pData->iMAGNfirstid;
+#endif
+                                       /* iterate again for showing */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  while ((iX <= iLastid) && (!pData->bTimerset))
+#else
+  while ((iX <= pData->iMAGNlastid) && (!pData->bTimerset))
+#endif
+  {
+    pData->iMAGNcurrentid = iX;
+
+    if (iX)                            /* only real objects ! */
+    {
+      pImage = mng_find_imageobject (pData, iX);
+                                       /* object exists & is not frozen  &
+                                          is visible & is viewable ? */
+      if ((pImage) && (!pImage->bFrozen) &&
+          (pImage->bVisible) && (pImage->bViewable))
+      {
+        mng_retcode iRetcode = mng_display_image (pData, pImage, MNG_FALSE);
+        if (iRetcode)
+          return iRetcode;
+      }
+    }
+
+    iX++;
+  }
+
+  if (pData->bTimerset)                /* broken ? */
+    pData->iBreakpoint = 9;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MAGN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_display_magn2 (mng_datap pData)
+{
+  mng_uint16 iX;
+  mng_imagep pImage;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MAGN, MNG_LC_START);
+#endif
+
+  iX = pData->iMAGNcurrentid;
+                                       /* iterate again for showing */
+  while ((iX <= pData->iMAGNtoid) && (!pData->bTimerset))
+  {
+    pData->iMAGNcurrentid = iX;
+
+    if (iX)                            /* only real objects ! */
+    {
+      pImage = mng_find_imageobject (pData, iX);
+                                       /* object exists & is not frozen  &
+                                          is visible & is viewable ? */
+      if ((pImage) && (!pImage->bFrozen) &&
+          (pImage->bVisible) && (pImage->bViewable))
+      {
+        mng_retcode iRetcode = mng_display_image (pData, pImage, MNG_FALSE);
+        if (iRetcode)
+          return iRetcode;
+      }
+    }
+
+    iX++;
+  }
+
+  if (pData->bTimerset)                /* broken ? */
+    pData->iBreakpoint = 9;
+  else
+    pData->iBreakpoint = 0;            /* not again ! */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MAGN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+mng_retcode mng_process_display_past (mng_datap  pData,
+                                      mng_uint16 iTargetid,
+                                      mng_uint8  iTargettype,
+                                      mng_int32  iTargetx,
+                                      mng_int32  iTargety,
+                                      mng_uint32 iCount,
+                                      mng_ptr    pSources)
+#else
+mng_retcode mng_process_display_past (mng_datap  pData)
+#endif
+{
+  mng_retcode      iRetcode = MNG_NOERROR;
+  mng_imagep       pTargetimg;
+  mng_imagep       pSourceimg;
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  mng_past_sourcep pSource = (mng_past_sourcep)pSources;
+#else
+  mng_past_sourcep pSource = (mng_past_sourcep)pData->pPASTsources;
+#endif
+  mng_uint32       iX      = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PAST, MNG_LC_START);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  if (iTargetid)                       /* a real destination object ? */
+#else
+  if (pData->iPASTtargetid)            /* a real destination object ? */
+#endif
+  {                                    /* let's find it then */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    pTargetimg = (mng_imagep)mng_find_imageobject (pData, iTargetid);
+#else
+    pTargetimg = (mng_imagep)mng_find_imageobject (pData, pData->iPASTtargetid);
+#endif
+
+    if (!pTargetimg)                   /* if it doesn't exists; do a barf */
+      MNG_ERROR (pData, MNG_OBJECTUNKNOWN);
+                                       /* it's gotta be abstract !!! */
+    if (pTargetimg->pImgbuf->bConcrete)
+      MNG_ERROR (pData, MNG_OBJNOTABSTRACT);
+                                       /* we want 32-/64-bit RGBA to play with ! */
+    if ((pTargetimg->pImgbuf->iBitdepth <= MNG_BITDEPTH_8)          ||
+        (pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_GRAY)    ||
+        (pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_RGB)     ||
+        (pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_INDEXED) ||
+        (pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_GRAYA)      )
+      iRetcode = mng_promote_imageobject (pData, pTargetimg, MNG_BITDEPTH_8,
+                                          MNG_COLORTYPE_RGBA,
+                                          MNG_FILLMETHOD_LEFTBITREPLICATE);
+    else
+    if ((pTargetimg->pImgbuf->iBitdepth > MNG_BITDEPTH_8)              &&
+        ((pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_GRAY)  ||
+         (pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_RGB)   ||
+         (pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_GRAYA)    )   )
+      iRetcode = mng_promote_imageobject (pData, pTargetimg, MNG_BITDEPTH_16,
+                                          MNG_COLORTYPE_RGBA,
+                                          MNG_FILLMETHOD_LEFTBITREPLICATE);
+#ifdef MNG_INCLUDE_JNG
+    else
+    if ((pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_JPEGGRAY)  ||
+        (pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_JPEGCOLOR) ||
+        (pTargetimg->pImgbuf->iColortype ==  MNG_COLORTYPE_JPEGGRAYA)    )
+      iRetcode = mng_promote_imageobject (pData, pTargetimg,
+                                          pTargetimg->pImgbuf->iBitdepth,
+                                          MNG_COLORTYPE_JPEGCOLORA,
+                                          MNG_FILLMETHOD_LEFTBITREPLICATE);
+#endif
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+                                       /* make it really abstract ? */
+    if (!pTargetimg->pImgbuf->bCorrected)
+    {
+      iRetcode = mng_colorcorrect_object (pData, pTargetimg);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+  }
+  else
+  {                                    /* pasting into object 0 !!! */
+    pTargetimg = (mng_imagep)pData->pObjzero;
+                                       /* is it usable ??? */
+    if ((pTargetimg->bClipped) &&
+        (pTargetimg->iClipr > pTargetimg->iPosx) &&
+        (pTargetimg->iClipb > pTargetimg->iPosy))
+    {
+                                       /* make it 32-bit RGBA please !!! */
+      iRetcode = mng_reset_object_details (pData, pTargetimg,
+                                           pTargetimg->iClipr - pTargetimg->iPosx,
+                                           pTargetimg->iClipb - pTargetimg->iPosy,
+                                           MNG_BITDEPTH_8, MNG_COLORTYPE_RGBA,
+                                           0, 0, 0, MNG_FALSE);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+    else
+      pTargetimg = MNG_NULL;           /* clipped beyond visibility ! */
+  }
+
+  if (pTargetimg)                      /* usable destination ? */
+  {
+    mng_int32      iSourceY;
+    mng_int32      iSourceYinc;
+    mng_int32      iSourcerowsize;
+    mng_int32      iSourcesamples;
+    mng_bool       bSourceRGBA16;
+    mng_int32      iTargetY;
+    mng_int32      iTargetrowsize;
+    mng_int32      iTargetsamples;
+    mng_bool       bTargetRGBA16 = MNG_FALSE;
+    mng_int32      iTemprowsize;
+    mng_imagedatap pBuf;
+#ifndef MNG_SKIPCHUNK_MAGN
+                                       /* needs magnification ? */
+    if ((pTargetimg->iMAGN_MethodX) || (pTargetimg->iMAGN_MethodY))
+      iRetcode = mng_magnify_imageobject (pData, pTargetimg);
+#endif
+
+    if (!iRetcode)                     /* still ok ? */
+    {
+      bTargetRGBA16 = (mng_bool)(pTargetimg->pImgbuf->iBitdepth > 8);
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+      switch (iTargettype)             /* determine target x/y */
+#else
+      switch (pData->iPASTtargettype)  /* determine target x/y */
+#endif
+      {
+        case 0 : {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   pData->iPastx = iTargetx;
+                   pData->iPasty = iTargety;
+#else
+                   pData->iPastx = pData->iPASTtargetx;
+                   pData->iPasty = pData->iPASTtargety;
+#endif
+                   break;
+                 }
+
+        case 1 : {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   pData->iPastx = pTargetimg->iPastx + iTargetx;
+                   pData->iPasty = pTargetimg->iPasty + iTargety;
+#else
+                   pData->iPastx = pTargetimg->iPastx + pData->iPASTtargetx;
+                   pData->iPasty = pTargetimg->iPasty + pData->iPASTtargety;
+#endif
+                   break;
+                 }
+
+        case 2 : {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+                   pData->iPastx += iTargetx;
+                   pData->iPasty += iTargety;
+#else
+                   pData->iPastx += pData->iPASTtargetx;
+                   pData->iPasty += pData->iPASTtargety;
+#endif
+                   break;
+                 }
+      }
+                                       /* save for next time ... */
+      pTargetimg->iPastx      = pData->iPastx;
+      pTargetimg->iPasty      = pData->iPasty;
+                                       /* address destination for row-routines */
+      pData->pStoreobj        = (mng_objectp)pTargetimg;
+      pData->pStorebuf        = (mng_objectp)pTargetimg->pImgbuf;
+    }
+                                       /* process the sources one by one */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    while ((!iRetcode) && (iX < iCount))
+#else
+    while ((!iRetcode) && (iX < pData->iPASTcount))
+#endif
+    {                                  /* find the little bastards first */
+      pSourceimg              = (mng_imagep)mng_find_imageobject (pData, pSource->iSourceid);
+                                       /* exists and viewable? */
+      if ((pSourceimg) && (pSourceimg->bViewable))
+      {                                /* needs magnification ? */
+#ifndef MNG_SKIPCHUNK_MAGN
+        if ((pSourceimg->iMAGN_MethodX) || (pSourceimg->iMAGN_MethodY))
+          iRetcode = mng_magnify_imageobject (pData, pSourceimg);
+#endif
+
+        if (!iRetcode)                 /* still ok ? */
+        {
+          pBuf                = (mng_imagedatap)pSourceimg->pImgbuf;
+                                       /* address source for row-routines */
+          pData->pRetrieveobj = (mng_objectp)pSourceimg;
+
+          pData->iPass        = -1;    /* init row-processing variables */
+          pData->iRowinc      = 1;
+          pData->iColinc      = 1;
+          pData->iPixelofs    = 0;
+          iSourcesamples      = (mng_int32)pBuf->iWidth;
+          iSourcerowsize      = pBuf->iRowsize;
+          bSourceRGBA16       = (mng_bool)(pBuf->iBitdepth > 8);
+                                       /* make sure the delta-routines do the right thing */
+          pData->iDeltatype   = MNG_DELTATYPE_BLOCKPIXELREPLACE;
+
+          switch (pBuf->iColortype)
+          {
+            case  0 : { 
+#ifndef MNG_NO_16BIT_SUPPORT
+                         if (bSourceRGBA16)
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_g16;
+                        else
+#endif
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_g8;
+
+                        pData->bIsOpaque      = (mng_bool)(!pBuf->bHasTRNS);
+                        break;
+                      }
+
+            case  2 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                        if (bSourceRGBA16)
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16;
+                        else
+#endif
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8;
+
+                        pData->bIsOpaque      = (mng_bool)(!pBuf->bHasTRNS);
+                        break;
+                      }
+
+
+            case  3 : { pData->fRetrieverow   = (mng_fptr)mng_retrieve_idx8;
+                        pData->bIsOpaque      = (mng_bool)(!pBuf->bHasTRNS);
+                        break;
+                      }
+
+
+            case  4 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                        if (bSourceRGBA16)
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16;
+                        else
+#endif
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8;
+
+                        pData->bIsOpaque      = MNG_FALSE;
+                        break;
+                      }
+
+
+            case  6 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                         if (bSourceRGBA16)
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16;
+                        else
+#endif
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8;
+
+                        pData->bIsOpaque      = MNG_FALSE;
+                        break;
+                      }
+
+            case  8 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                         if (bSourceRGBA16)
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_g16;
+                        else
+#endif
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_g8;
+
+                        pData->bIsOpaque      = MNG_TRUE;
+                        break;
+                      }
+
+            case 10 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                         if (bSourceRGBA16)
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16;
+                        else
+#endif
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8;
+
+                        pData->bIsOpaque      = MNG_TRUE;
+                        break;
+                      }
+
+
+            case 12 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                         if (bSourceRGBA16)
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16;
+                        else
+#endif
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8;
+
+                        pData->bIsOpaque      = MNG_FALSE;
+                        break;
+                      }
+
+
+            case 14 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                         if (bSourceRGBA16)
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16;
+                        else
+#endif
+                          pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8;
+
+                        pData->bIsOpaque      = MNG_FALSE;
+                        break;
+                      }
+          }
+                                       /* determine scaling */
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_NO_DELTA_PNG
+          if ((!bSourceRGBA16) && (bTargetRGBA16))
+            pData->fScalerow = (mng_fptr)mng_scale_rgba8_rgba16;
+          else
+          if ((bSourceRGBA16) && (!bTargetRGBA16))
+            pData->fScalerow = (mng_fptr)mng_scale_rgba16_rgba8;
+          else
+#endif
+#endif
+            pData->fScalerow = MNG_NULL;
+
+                                       /* default no color-correction */
+          pData->fCorrectrow = MNG_NULL;
+
+#if defined(MNG_FULL_CMS)              /* determine color-management routine */
+          iRetcode = mng_init_full_cms   (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#elif defined(MNG_GAMMA_ONLY)
+          iRetcode = mng_init_gamma_only (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#elif defined(MNG_APP_CMS)
+          iRetcode = mng_init_app_cms    (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#endif
+        }
+
+        if (!iRetcode)                 /* still ok ? */
+        {  
+          pData->fFliprow = MNG_NULL;  /* no flipping or tiling by default */
+          pData->fTilerow = MNG_NULL;
+                                       /* but perhaps we do have to ... */
+          switch (pSource->iOrientation)
+          {
+            case 2 : ;
+            case 4 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                       if (bTargetRGBA16)
+                         pData->fFliprow = (mng_fptr)mng_flip_rgba16;
+                       else
+#endif
+                         pData->fFliprow = (mng_fptr)mng_flip_rgba8;
+                       break;
+                     }
+
+            case 8 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                       if (bTargetRGBA16)
+                         pData->fTilerow = (mng_fptr)mng_tile_rgba16;
+                       else
+#endif
+                         pData->fTilerow = (mng_fptr)mng_tile_rgba8;
+                       break;
+                     }
+          }
+                                       /* determine composition routine */
+                                       /* note that we're abusing the delta-routine setup !!! */
+          switch (pSource->iComposition)
+          {
+            case 0 : {                 /* composite over */
+#ifndef MNG_NO_16BIT_SUPPORT
+                       if (bTargetRGBA16)
+                         pData->fDeltarow = (mng_fptr)mng_composeover_rgba16;
+                       else
+#endif
+                         pData->fDeltarow = (mng_fptr)mng_composeover_rgba8;
+                       break;
+                     }
+
+            case 1 : {                 /* replace */
+#ifndef MNG_NO_16BIT_SUPPORT
+                       if (bTargetRGBA16)
+                         pData->fDeltarow = (mng_fptr)mng_delta_rgba16_rgba16;
+                       else
+#endif
+                         pData->fDeltarow = (mng_fptr)mng_delta_rgba8_rgba8;
+                       break;
+                     }
+
+            case 2 : {                 /* composite under */
+#ifndef MNG_NO_16BIT_SUPPORT
+                       if (bTargetRGBA16)
+                         pData->fDeltarow = (mng_fptr)mng_composeunder_rgba16;
+                       else
+#endif
+                         pData->fDeltarow = (mng_fptr)mng_composeunder_rgba8;
+                       break;
+                     }
+          }
+                                       /* determine offsets & clipping */
+          if (pSource->iOffsettype == 1)
+          {
+            pData->iDestl          = pData->iPastx + pSource->iOffsetx;
+            pData->iDestt          = pData->iPasty + pSource->iOffsety;
+          }
+          else
+          {
+            pData->iDestl          = pSource->iOffsetx;
+            pData->iDestt          = pSource->iOffsety;
+          }
+
+          pData->iDestr            = (mng_int32)pTargetimg->pImgbuf->iWidth;
+          pData->iDestb            = (mng_int32)pTargetimg->pImgbuf->iHeight;
+                                       /* take the source dimension into account ? */
+          if (pSource->iOrientation != 8)
+          {
+            pData->iDestr          = MIN_COORD (pData->iDestr, pData->iDestl + (mng_int32)pBuf->iWidth);
+            pData->iDestb          = MIN_COORD (pData->iDestb, pData->iDestt + (mng_int32)pBuf->iHeight);
+          }
+                                       /* source clipping */
+          if (pSource->iBoundarytype == 1)
+          {
+            if (pData->iDestl < pData->iPastx + pSource->iBoundaryl)
+              pData->iSourcel      = pData->iPastx + pSource->iBoundaryl - pData->iDestl;
+            else
+              pData->iSourcel      = 0;
+
+            if (pData->iDestt < pData->iPasty + pSource->iBoundaryt)
+              pData->iSourcet      = pData->iPasty + pSource->iBoundaryt - pData->iDestt;
+            else
+              pData->iSourcet      = 0;
+
+            pData->iDestl          = MAX_COORD (pData->iDestl, pData->iPastx + pSource->iBoundaryl);
+            pData->iDestt          = MAX_COORD (pData->iDestt, pData->iPasty + pSource->iBoundaryt);
+            pData->iDestr          = MIN_COORD (pData->iDestr, pData->iPastx + pSource->iBoundaryr);
+            pData->iDestb          = MIN_COORD (pData->iDestb, pData->iPasty + pSource->iBoundaryb);
+          }
+          else
+          {
+            if (pData->iDestl < pSource->iBoundaryl)
+              pData->iSourcel      = pSource->iBoundaryl - pData->iDestl;
+            else
+              pData->iSourcel      = 0;
+
+            if (pData->iDestt < pSource->iBoundaryt)
+              pData->iSourcet      = pSource->iBoundaryt - pData->iDestt;
+            else
+              pData->iSourcet      = 0;
+
+            pData->iDestl          = MAX_COORD (pData->iDestl, pSource->iBoundaryl);
+            pData->iDestt          = MAX_COORD (pData->iDestt, pSource->iBoundaryt);
+            pData->iDestr          = MIN_COORD (pData->iDestr, pSource->iBoundaryr);
+            pData->iDestb          = MIN_COORD (pData->iDestb, pSource->iBoundaryb);
+          }
+
+          if (pData->iSourcel)         /* indent source ? */
+          {
+#ifndef MNG_NO_16BIT_SUPPORT
+             if (bTargetRGBA16)        /* abuse tiling routine to shift source-pixels */
+               pData->fTilerow = (mng_fptr)mng_tile_rgba16;
+             else
+#endif
+               pData->fTilerow = (mng_fptr)mng_tile_rgba8;
+          }
+                                       /* anything to display ? */
+          if ((pData->iDestl <= pData->iDestr) && (pData->iDestt <= pData->iDestb))
+          {                            /* init variables for the loop */
+            if ((pSource->iOrientation == 2) || (pSource->iOrientation == 6))
+            {
+              iSourceY             = (mng_int32)pBuf->iHeight - 1 - pData->iSourcet;
+              iSourceYinc          = -1;
+            }
+            else
+            {
+              iSourceY             = pData->iSourcet;
+              iSourceYinc          = 1;
+            }
+
+            iTargetY               = pData->iDestt;
+            pData->iCol            = pData->iDestl;
+
+            iTargetsamples         = pData->iDestr - pData->iDestl;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+            if (bTargetRGBA16)
+              iTargetrowsize       = (iTargetsamples << 3);
+            else
+#endif
+              iTargetrowsize       = (iTargetsamples << 2);
+
+                                       /* get temporary work-buffers */
+            if (iSourcerowsize > iTargetrowsize)
+              iTemprowsize         = iSourcerowsize << 1;
+            else
+              iTemprowsize         = iTargetrowsize << 1;
+            MNG_ALLOC (pData, pData->pRGBArow, iTemprowsize);
+            MNG_ALLOC (pData, pData->pWorkrow, iTemprowsize);
+
+            while ((!iRetcode) && (iTargetY < pData->iDestb))
+            {                          /* get a row */
+              pData->iRow          = iSourceY;
+              pData->iRowsamples   = iSourcesamples;
+              pData->iRowsize      = iSourcerowsize;
+              pData->bIsRGBA16     = bSourceRGBA16;
+              iRetcode             = ((mng_retrieverow)pData->fRetrieverow) (pData);
+                                       /* scale it (if necessary) */
+              if ((!iRetcode) && (pData->fScalerow))
+                iRetcode           = ((mng_scalerow)pData->fScalerow) (pData);
+
+              pData->bIsRGBA16     = bTargetRGBA16;
+                                       /* color correction (if necessary) */
+              if ((!iRetcode) && (pData->fCorrectrow))
+                iRetcode           = ((mng_correctrow)pData->fCorrectrow) (pData);
+                                       /* flipping (if necessary) */
+              if ((!iRetcode) && (pData->fFliprow))
+                iRetcode           = ((mng_fliprow)pData->fFliprow) (pData);
+                                       /* tiling (if necessary) */
+              if ((!iRetcode) && (pData->fTilerow))
+                iRetcode           = ((mng_tilerow)pData->fTilerow) (pData);
+
+              if (!iRetcode)           /* and paste..... */
+              {
+                pData->iRow        = iTargetY;
+                pData->iRowsamples = iTargetsamples;
+                pData->iRowsize    = iTargetrowsize;
+                iRetcode           = ((mng_deltarow)pData->fDeltarow) (pData);
+              }
+
+              iSourceY += iSourceYinc; /* and next line */
+
+              if (iSourceY < 0)
+                iSourceY = (mng_int32)pBuf->iHeight - 1;
+              else
+              if (iSourceY >= (mng_int32)pBuf->iHeight)
+                iSourceY = 0;
+
+              iTargetY++;
+            }
+                                       /* drop the temporary row-buffer */
+            MNG_FREEX (pData, pData->pWorkrow, iTemprowsize);
+            MNG_FREEX (pData, pData->pRGBArow, iTemprowsize);
+          }
+
+#if defined(MNG_FULL_CMS)              /* cleanup cms stuff */
+          if (!iRetcode)
+            iRetcode = mng_clear_cms (pData);
+#endif
+        }
+
+        pSource++;                     /* neeeeext */
+        iX++;
+      }
+    }
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    if (!iTargetid)                    /* did we paste into object 0 ? */
+#else
+    if (!pData->iPASTtargetid)         /* did we paste into object 0 ? */
+#endif
+    {                                  /* display it then ! */
+      iRetcode = mng_display_image (pData, pTargetimg, MNG_FALSE);
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+    else
+    {                                  /* target is visible & viewable ? */
+      if ((pTargetimg->bVisible) && (pTargetimg->bViewable))
+      {
+        iRetcode = mng_display_image (pData, pTargetimg, MNG_FALSE);
+        if (iRetcode)
+          return iRetcode;
+      }
+    }  
+  }
+
+  if (pData->bTimerset)                /* broken ? */
+  {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    pData->iPASTid     = iTargetid;
+#else
+    pData->iPASTid     = pData->iPASTtargetid;
+#endif
+    pData->iBreakpoint = 11;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PAST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SKIPCHUNK_PAST */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_process_display_past2 (mng_datap pData)
+{
+  mng_retcode iRetcode;
+  mng_imagep  pTargetimg;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PAST, MNG_LC_START);
+#endif
+
+  if (pData->iPASTid)                  /* a real destination object ? */
+    pTargetimg = (mng_imagep)mng_find_imageobject (pData, pData->iPASTid);
+  else                                 /* otherwise object 0 */
+    pTargetimg = (mng_imagep)pData->pObjzero;
+
+  iRetcode = mng_display_image (pData, pTargetimg, MNG_FALSE);
+  if (iRetcode)
+    return iRetcode;
+
+  pData->iBreakpoint = 0;              /* only once */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PAST, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SKIPCHUNK_PAST */
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_DISPLAY_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
+
diff --git a/files/Source/LibMNG/libmng_display.h b/files/Source/LibMNG/libmng_display.h
new file mode 100644
index 0000000..f394dd2
--- /dev/null
+++ b/files/Source/LibMNG/libmng_display.h
@@ -0,0 +1,343 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_display.h          copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Display management (definition)                            * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the display managament routines              * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/20/2000 - G.Juyn                                * */
+/* *             - added JNG support stuff                                  * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/16/2000 - G.Juyn                                * */
+/* *             - changed progressive-display processing                   * */
+/* *             0.5.3 - 06/22/2000 - G.Juyn                                * */
+/* *             - added support for delta-image processing                 * */
+/* *             - added support for PPLT chunk processing                  * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *             0.9.3 - 08/07/2000 - G.Juyn                                * */
+/* *             - B111300 - fixup for improved portability                 * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added JDAA chunk                                         * */
+/* *                                                                        * */
+/* *             0.9.4 - 11/24/2000 - G.Juyn                                * */
+/* *             - moved restore of object 0 to libmng_display              * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/13/2002 - G.Juyn                                * */
+/* *             - fixed read/write of MAGN chunk                           * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - added proposed change in handling of TERM- & if-delay    * */
+/* *             1.0.5 - 10/20/2002 - G.Juyn                                * */
+/* *             - fixed display of visible target of PAST operation        * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/24/2004 - G.R-P.                                * */
+/* *             - added some SKIPCHUNK conditionals                        * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/11/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_DISPLAYCALLS              * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_display_h_
+#define _libmng_display_h_
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_DISPLAY_PROCS
+
+/* ************************************************************************** */
+
+mng_retcode mng_display_progressive_refresh (mng_datap  pData,
+                                             mng_uint32 iInterval);
+
+/* ************************************************************************** */
+
+mng_retcode mng_reset_objzero         (mng_datap      pData);
+
+mng_retcode mng_display_image         (mng_datap      pData,
+                                       mng_imagep     pImage,
+                                       mng_bool       bLayeradvanced);
+
+mng_retcode mng_execute_delta_image   (mng_datap      pData,
+                                       mng_imagep     pTarget,
+                                       mng_imagep     pDelta);
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_display       (mng_datap      pData);
+
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+png_imgtype mng_png_imgtype           (mng_uint8      colortype,
+                                       mng_uint8      bitdepth);
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+
+mng_retcode mng_process_display_ihdr  (mng_datap      pData);
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+mng_retcode mng_process_display_mpng  (mng_datap      pData);
+#endif
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+mng_retcode mng_process_display_ang   (mng_datap      pData);
+#endif
+
+mng_retcode mng_process_display_idat  (mng_datap      pData,
+                                       mng_uint32     iRawlen,
+                                       mng_uint8p     pRawdata);
+
+mng_retcode mng_process_display_iend  (mng_datap      pData);
+mng_retcode mng_process_display_mend  (mng_datap      pData);
+mng_retcode mng_process_display_mend2 (mng_datap      pData);
+mng_retcode mng_process_display_defi  (mng_datap      pData);
+
+#ifndef MNG_SKIPCHUNK_BASI
+mng_retcode mng_process_display_basi  (mng_datap      pData,
+                                       mng_uint16     iRed,
+                                       mng_uint16     iGreen,
+                                       mng_uint16     iBlue,
+                                       mng_bool       bHasalpha,
+                                       mng_uint16     iAlpha,
+                                       mng_uint8      iViewable);
+#endif
+
+#ifndef MNG_SKIPCHUNK_CLON
+mng_retcode mng_process_display_clon  (mng_datap      pData,
+                                       mng_uint16     iSourceid,
+                                       mng_uint16     iCloneid,
+                                       mng_uint8      iClonetype,
+                                       mng_bool       bHasdonotshow,
+                                       mng_uint8      iDonotshow,
+                                       mng_uint8      iConcrete,
+                                       mng_bool       bHasloca,
+                                       mng_uint8      iLocationtype,
+                                       mng_int32      iLocationx,
+                                       mng_int32      iLocationy);
+mng_retcode mng_process_display_clon2 (mng_datap      pData);
+#endif
+
+#ifndef MNG_SKIPCHUNK_DISC
+mng_retcode mng_process_display_disc  (mng_datap      pData,
+                                       mng_uint32     iCount,
+                                       mng_uint16p    pIds);
+#endif
+
+#ifndef MNG_SKIPCHUNK_FRAM
+mng_retcode mng_process_display_fram  (mng_datap      pData,
+                                       mng_uint8      iFramemode,
+                                       mng_uint8      iChangedelay,
+                                       mng_uint32     iDelay,
+                                       mng_uint8      iChangetimeout,
+                                       mng_uint32     iTimeout,
+                                       mng_uint8      iChangeclipping,
+                                       mng_uint8      iCliptype,
+                                       mng_int32      iClipl,
+                                       mng_int32      iClipr,
+                                       mng_int32      iClipt,
+                                       mng_int32      iClipb);
+mng_retcode mng_process_display_fram2 (mng_datap      pData);
+#endif
+
+#ifndef MNG_SKIPCHUNK_MOVE
+mng_retcode mng_process_display_move  (mng_datap      pData,
+                                       mng_uint16     iFromid,
+                                       mng_uint16     iToid,
+                                       mng_uint8      iMovetype,
+                                       mng_int32      iMovex,
+                                       mng_int32      iMovey);
+#endif
+
+#ifndef MNG_SKIPCHUNK_CLIP
+mng_retcode mng_process_display_clip  (mng_datap      pData,
+                                       mng_uint16     iFromid,
+                                       mng_uint16     iToid,
+                                       mng_uint8      iCliptype,
+                                       mng_int32      iClipl,
+                                       mng_int32      iClipr,
+                                       mng_int32      iClipt,
+                                       mng_int32      iClipb);
+#endif
+
+#ifndef MNG_SKIPCHUNK_SHOW
+mng_retcode mng_process_display_show  (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode mng_process_display_save  (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_retcode mng_process_display_seek  (mng_datap      pData);
+#endif
+#ifdef MNG_INCLUDE_JNG
+mng_retcode mng_process_display_jhdr  (mng_datap      pData);
+
+mng_retcode mng_process_display_jdaa  (mng_datap      pData,
+                                       mng_uint32     iRawlen,
+                                       mng_uint8p     pRawdata);
+
+mng_retcode mng_process_display_jdat  (mng_datap      pData,
+                                       mng_uint32     iRawlen,
+                                       mng_uint8p     pRawdata);
+
+#endif
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_process_display_dhdr  (mng_datap      pData,
+                                       mng_uint16     iObjectid,
+                                       mng_uint8      iImagetype,
+                                       mng_uint8      iDeltatype,
+                                       mng_uint32     iBlockwidth,
+                                       mng_uint32     iBlockheight,
+                                       mng_uint32     iBlockx,
+                                       mng_uint32     iBlocky);
+
+mng_retcode mng_process_display_prom  (mng_datap      pData,
+                                       mng_uint8      iBitdepth,
+                                       mng_uint8      iColortype,
+                                       mng_uint8      iFilltype);
+
+mng_retcode mng_process_display_ipng  (mng_datap      pData);
+#ifdef MNG_INCLUDE_JNG
+mng_retcode mng_process_display_ijng  (mng_datap      pData);
+#endif
+
+mng_retcode mng_process_display_pplt  (mng_datap      pData,
+                                       mng_uint8      iType,
+                                       mng_uint32     iCount,
+                                       mng_palette8ep paIndexentries,
+                                       mng_uint8p     paAlphaentries,
+                                       mng_uint8p     paUsedentries);
+#endif
+
+#ifndef MNG_SKIPCHUNK_MAGN
+mng_retcode mng_process_display_magn  (mng_datap      pData,
+                                       mng_uint16     iFirstid,
+                                       mng_uint16     iLastid,
+                                       mng_uint8      iMethodX,
+                                       mng_uint16     iMX,
+                                       mng_uint16     iMY,
+                                       mng_uint16     iML,
+                                       mng_uint16     iMR,
+                                       mng_uint16     iMT,
+                                       mng_uint16     iMB,
+                                       mng_uint8      iMethodY);
+mng_retcode mng_process_display_magn2 (mng_datap      pData);
+#endif
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_process_display_past  (mng_datap      pData,
+                                       mng_uint16     iTargetid,
+                                       mng_uint8      iTargettype,
+                                       mng_int32      iTargetx,
+                                       mng_int32      iTargety,
+                                       mng_uint32     iCount,
+                                       mng_ptr        pSources);
+mng_retcode mng_process_display_past2 (mng_datap      pData);
+#endif
+
+#else /* MNG_OPTIMIZE_DISPLAYCALLS */
+
+mng_retcode mng_process_display_ihdr  (mng_datap      pData);
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+mng_retcode mng_process_display_mpng  (mng_datap      pData);
+#endif
+mng_retcode mng_process_display_idat  (mng_datap      pData);
+mng_retcode mng_process_display_iend  (mng_datap      pData);
+mng_retcode mng_process_display_mend  (mng_datap      pData);
+mng_retcode mng_process_display_mend2 (mng_datap      pData);
+mng_retcode mng_process_display_defi  (mng_datap      pData);
+#ifndef MNG_SKIPCHUNK_BASI
+mng_retcode mng_process_display_basi  (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_CLON
+mng_retcode mng_process_display_clon  (mng_datap      pData);
+mng_retcode mng_process_display_clon2 (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_DISC
+mng_retcode mng_process_display_disc  (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_FRAM
+mng_retcode mng_process_display_fram  (mng_datap      pData);
+mng_retcode mng_process_display_fram2 (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_MOVE
+mng_retcode mng_process_display_move  (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_CLIP
+mng_retcode mng_process_display_clip  (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_SHOW
+mng_retcode mng_process_display_show  (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode mng_process_display_save  (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_retcode mng_process_display_seek  (mng_datap      pData);
+#endif
+#ifdef MNG_INCLUDE_JNG
+mng_retcode mng_process_display_jhdr  (mng_datap      pData);
+mng_retcode mng_process_display_jdaa  (mng_datap      pData);
+mng_retcode mng_process_display_jdat  (mng_datap      pData);
+#endif
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_process_display_dhdr  (mng_datap      pData);
+mng_retcode mng_process_display_prom  (mng_datap      pData);
+mng_retcode mng_process_display_ipng  (mng_datap      pData);
+#ifdef MNG_INCLUDE_JNG
+mng_retcode mng_process_display_ijng  (mng_datap      pData);
+#endif
+mng_retcode mng_process_display_pplt  (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_MAGN
+mng_retcode mng_process_display_magn  (mng_datap      pData);
+mng_retcode mng_process_display_magn2 (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_process_display_past  (mng_datap      pData);
+mng_retcode mng_process_display_past2 (mng_datap      pData);
+#endif
+
+#endif /* MNG_OPTIMIZE_DISPLAYCALLS */
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_DISPLAY_PROCS */
+
+/* ************************************************************************** */
+
+#endif /* _libmng_display_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_dither.c b/files/Source/LibMNG/libmng_dither.c
new file mode 100644
index 0000000..e23850c
--- /dev/null
+++ b/files/Source/LibMNG/libmng_dither.c
@@ -0,0 +1,58 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_dither.c           copyright (c) 2000-2004 G.Juyn   * */
+/* * version   : 1.0.9                                                      * */
+/* *                                                                        * */
+/* * purpose   : Dithering routines (implementation)                        * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the dithering routines                   * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_dither.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_dither_a_row (mng_datap  pData,
+                              mng_uint8p pRow)
+{
+
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
+
+
diff --git a/files/Source/LibMNG/libmng_dither.h b/files/Source/LibMNG/libmng_dither.h
new file mode 100644
index 0000000..d9217c0
--- /dev/null
+++ b/files/Source/LibMNG/libmng_dither.h
@@ -0,0 +1,45 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_dither.h           copyright (c) 2000-2002 G.Juyn   * */
+/* * version   : 1.0.5                                                      * */
+/* *                                                                        * */
+/* * purpose   : Dithering routines (definition)                            * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the dithering routines                       * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_dither_h_
+#define _libmng_dither_h_
+
+/* ************************************************************************** */
+
+mng_retcode mng_dither_a_row (mng_datap  pData,
+                              mng_uint8p pRow);
+
+/* ************************************************************************** */
+
+#endif /* _libmng_dither_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_error.c b/files/Source/LibMNG/libmng_error.c
new file mode 100644
index 0000000..3a4da20
--- /dev/null
+++ b/files/Source/LibMNG/libmng_error.c
@@ -0,0 +1,326 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_error.c            copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Error routines (implementation)                            * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the general error handling routines      * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/23/2000 - G.Juyn                                * */
+/* *             - added error telltaling                                   * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added errorstrings for delta-image processing            * */
+/* *             0.5.2 - 05/31/2000 - G.Juyn                                * */
+/* *             - fixed up punctuation (contributed by Tim Rowley)         * */
+/* *             0.5.2 - 06/06/2000 - G.Juyn                                * */
+/* *             - added errorstring for delayed buffer-processing          * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/06/2000 - G.Juyn                                * */
+/* *             - added MNG_NEEDTIMERWAIT errorstring                      * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added NEEDSECTIONWAIT errorstring                        * */
+/* *             - added macro + routine to set returncode without          * */
+/* *               calling error callback                                   * */
+/* *             0.9.1 - 07/19/2000 - G.Juyn                                * */
+/* *             - added errorstring for updatemngheader if not a MNG       * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/09/2000 - G.Juyn                                * */
+/* *             - added check for simplicity-bits in MHDR                  * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - fixed processing of unknown critical chunks              * */
+/* *             - added support for nEED                                   * */
+/* *             0.9.3 - 10/20/2000 - G.Juyn                                * */
+/* *             - added errorcode for delayed delta-processing             * */
+/* *                                                                        * */
+/* *             0.9.4 - 01/18/2001 - G.Juyn                                * */
+/* *             - added errorcode for MAGN methods                         * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added optimization option for MNG-video playback         * */
+/* *                                                                        * */
+/* *             1.0.5 - 07/04/2002 - G.Juyn                                * */
+/* *             - added errorcode for extreme chunk-sizes                  * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed delta-image support                            * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 09/15/2002 - G.Juyn                                * */
+/* *             - fixed LOOP iteration=0 special case                      * */
+/* *             1.0.5 - 09/19/2002 - G.Juyn                                * */
+/* *             - warnings are ignored by default now!                     * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - added check for TERM placement during create/write       * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G. R-P                                * */
+/* *             - added MNG_SKIPCHUNK_CHNK, MNG_NO_DELTA_PNG reductions.   * */
+/* *             - skipped more code when MNG_INCLUDE_JNG is not enabled.   * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditional around evNT chunk support              * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/24/2004 - G.R-P                                 * */
+/* *             - fixed typo on SKIPCHUNK_evNT (->PAST)                    * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ERROR_STRINGS
+MNG_LOCAL mng_error_entry const error_table [] =
+  {
+    {MNG_NOERROR,          "No error"},
+    {MNG_OUTOFMEMORY,      "Out of memory"},
+    {MNG_INVALIDHANDLE,    "The handle is invalid"},
+    {MNG_NOCALLBACK,       "A required callback is not defined"},
+    {MNG_UNEXPECTEDEOF,    "Encountered unexpected end-of-file"},
+    {MNG_ZLIBERROR,        "zlib encountered an error"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_JPEGERROR,        "ijgsrc6b encountered an error"},
+#endif
+    {MNG_LCMSERROR,        "lcms encountered an error"},
+    {MNG_NOOUTPUTPROFILE,  "No output-profile defined for CMS"},
+    {MNG_NOSRGBPROFILE,    "No sRGB-profile defined for CMS"},
+    {MNG_BUFOVERFLOW,      "Internal buffer-overflow"},
+    {MNG_FUNCTIONINVALID,  "Function is invalid at this point"},
+    {MNG_OUTPUTERROR,      "Writing was unsuccessful; disk full?"},
+    {MNG_JPEGBUFTOOSMALL,  "Internal buffer for JPEG processing too small"},
+    {MNG_NEEDMOREDATA,     "Reading suspended; waiting for I/O to catch up"},
+    {MNG_NEEDTIMERWAIT,    "Timer suspension; normal animation delay"},
+    {MNG_NEEDSECTIONWAIT,  "SEEK suspension; application decides"},
+    {MNG_LOOPWITHCACHEOFF, "LOOP encountered when playback cache is turned off"},
+
+    {MNG_APPIOERROR,       "Application signalled I/O error"},
+    {MNG_APPTIMERERROR,    "Application signalled timing error"},
+    {MNG_APPCMSERROR,      "Application signalled CMS error"},
+    {MNG_APPMISCERROR,     "Application signalled an error"},
+    {MNG_APPTRACEABORT,    "Application signalled error during trace-callback"},
+
+    {MNG_INTERNALERROR,    "Internal error in libmng"},
+
+    {MNG_INVALIDSIG,       "The signature is invalid"},
+    {MNG_INVALIDCRC,       "The CRC for this chunk is invalid"},
+    {MNG_INVALIDLENGTH,    "Chunk-length is invalid"},
+    {MNG_SEQUENCEERROR,    "Chunk out of sequence"},
+    {MNG_CHUNKNOTALLOWED,  "Chunk not allowed at this point"},
+    {MNG_MULTIPLEERROR,    "Chunk cannot occur multiple times"},
+    {MNG_PLTEMISSING,      "Missing PLTE chunk"},
+    {MNG_IDATMISSING,      "Missing IDAT chunk(s)"},
+    {MNG_CANNOTBEEMPTY,    "Chunk cannot be empty"},
+    {MNG_GLOBALLENGTHERR,  "Global data length invalid"},
+    {MNG_INVALIDBITDEPTH,  "The bit_depth is invalid"},
+    {MNG_INVALIDCOLORTYPE, "The color_type is invalid"},
+    {MNG_INVALIDCOMPRESS,  "The compression_method is invalid"},
+    {MNG_INVALIDFILTER,    "The filter_method or filter_type is invalid"},
+    {MNG_INVALIDINTERLACE, "The interlace_method is invalid"},
+    {MNG_NOTENOUGHIDAT,    "There is not enough data in the IDAT chunk(s)"},
+    {MNG_PLTEINDEXERROR,   "Palette-index out of bounds"},
+    {MNG_NULLNOTFOUND,     "NULL separator not found"},
+    {MNG_KEYWORDNULL,      "Keyword cannot be zero-length"},
+    {MNG_OBJECTUNKNOWN,    "Object does not exist"},
+    {MNG_OBJECTEXISTS,     "Object already exists"},
+    {MNG_TOOMUCHIDAT,      "Too much data in IDAT chunk(s)"},
+    {MNG_INVSAMPLEDEPTH,   "The sample_depth is invalid"},
+    {MNG_INVOFFSETSIZE,    "The offset_type is invalid"},
+    {MNG_INVENTRYTYPE,     "The entry_type is invalid"},
+    {MNG_ENDWITHNULL,      "Chunk must not end with NULL byte"},
+    {MNG_INVIMAGETYPE,     "The image_type is invalid"},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_INVDELTATYPE,     "The delta_type is invalid"},
+#endif
+    {MNG_INVALIDINDEX,     "Index-value out of bounds"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_TOOMUCHJDAT,      "Too much data in JDAT chunk(s)"},
+    {MNG_JPEGPARMSERR,     "JHDR parameters & JFIF-data do not match"},
+#endif
+    {MNG_INVFILLMETHOD,    "The fill_method is invalid"},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_OBJNOTCONCRETE,   "Target object for DHDR must be concrete"},
+#endif
+    {MNG_TARGETNOALPHA,    "Target object must have alpha-channel"},
+    {MNG_MNGTOOCOMPLEX,    "MHDR simplicity indicates unsupported feature(s)"},
+    {MNG_UNKNOWNCRITICAL,  "Unknown critical chunk encountered"},
+#ifndef MNG_SKIPCHUNK_nEED
+    {MNG_UNSUPPORTEDNEED,  "Requested nEED resources are not supported"},
+#endif
+    {MNG_INVALIDDELTA,     "The delta operation is invalid (mismatched color_types?)"},
+    {MNG_INVALIDMETHOD,    "Method is invalid"},
+    {MNG_IMPROBABLELENGTH, "Chunklength is incredibly large"},
+    {MNG_INVALIDBLOCK,     "Delta block width and or height invalid"},
+    {MNG_INVALIDEVENT,     "Event type is invalid"},
+    {MNG_INVALIDMASK,      "Mask type is invalid"},
+    {MNG_NOMATCHINGLOOP,   "ENDL without matching LOOP"},
+#ifndef MNG_SKIPCHUNK_evNT
+    {MNG_SEEKNOTFOUND,     "evNT points to unknown SEEK"},
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_OBJNOTABSTRACT,   "Destination object for PAST must be abstract"},
+#endif
+    {MNG_TERMSEQERROR,     "TERM misplaced during creation of MNG stream"},
+    {MNG_INVALIDFIELDVAL,  "invalid fieldvalue (generic)"},
+    {MNG_INVALIDWIDTH,     "invalid frame/image width"},
+    {MNG_INVALIDHEIGHT,    "invalid frame/image height"},
+
+    {MNG_INVALIDCNVSTYLE,  "Canvas_style is invalid"},
+    {MNG_WRONGCHUNK,       "Attempt to access the wrong chunk"},
+    {MNG_INVALIDENTRYIX,   "Attempt to access an non-existing entry"},
+    {MNG_NOHEADER,         "No valid header-chunk"},
+    {MNG_NOCORRCHUNK,      "Parent chunk not found"},
+    {MNG_NOMHDR,           "No MNG header (MHDR) found"},
+
+    {MNG_IMAGETOOLARGE,    "Image is larger than defined maximum"},
+    {MNG_NOTANANIMATION,   "Image is not an animation"},
+    {MNG_FRAMENRTOOHIGH,   "Framenr out of bounds"},
+    {MNG_LAYERNRTOOHIGH,   "Layernr out of bounds"},
+    {MNG_PLAYTIMETOOHIGH,  "Playtime out of bounds"},
+    {MNG_FNNOTIMPLEMENTED, "Function not yet implemented"},
+    {MNG_IMAGEFROZEN,      "Image is frozen"},
+
+    {MNG_LCMS_NOHANDLE,    "Handle could not be initialized"},
+    {MNG_LCMS_NOMEM,       "No memory for gamma-table(s)"},
+    {MNG_LCMS_NOTRANS,     "Transformation could not be initialized"}
+  };
+#endif /* MNG_INCLUDE_ERROR_STRINGS */
+
+/* ************************************************************************** */
+
+mng_bool mng_store_error (mng_datap   pData,
+                          mng_retcode iError,
+                          mng_retcode iExtra1,
+                          mng_retcode iExtra2)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (pData, MNG_FN_STORE_ERROR, MNG_LC_START);
+#endif
+
+  if (pData != 0)
+  {
+    pData->iErrorcode = iError;        /* save also for getlasterror */
+    pData->iErrorx1   = iExtra1;
+    pData->iErrorx2   = iExtra2;
+
+#ifdef MNG_INCLUDE_ERROR_STRINGS
+    {                                  /* binary search variables */
+      mng_int32        iTop, iLower, iUpper, iMiddle;
+      mng_error_entryp pEntry;         /* pointer to found entry */
+                                       /* determine max index of table */
+      iTop = (sizeof (error_table) / sizeof (error_table [0])) - 1;
+
+      iLower  = 0;                     /* initialize binary search */
+      iMiddle = iTop >> 1;             /* start in the middle */
+      iUpper  = iTop;
+      pEntry  = 0;                     /* no goods yet! */
+
+      do                               /* the binary search itself */
+        {
+          if (error_table [iMiddle].iError < iError)
+            iLower = iMiddle + 1;
+          else if (error_table [iMiddle].iError > iError)
+            iUpper = iMiddle - 1;
+          else
+          {
+            pEntry = &error_table [iMiddle];
+            break;
+          }
+
+          iMiddle = (iLower + iUpper) >> 1;
+        }
+      while (iLower <= iUpper);
+
+      if (pEntry)                      /* found it ? */
+        pData->zErrortext = pEntry->zErrortext;
+      else
+        pData->zErrortext = "Unknown error";
+      }
+#else /* MNG_INCLUDE_ERROR_STRINGS */
+    pData->zErrortext = 0;
+#endif /* MNG_INCLUDE_ERROR_STRINGS */
+
+    if (iError == 0)                   /* no error is not severe ! */
+    {
+      pData->iSeverity = 0;
+    }
+    else
+    {
+      switch (iError&0x3C00)           /* determine the severity */
+      {
+        case 0x0800 : { pData->iSeverity = 5; break; }
+        case 0x1000 : { pData->iSeverity = 2; break; }
+        case 0x2000 : { pData->iSeverity = 1; break; }      
+        default     : { pData->iSeverity = 9; }
+      }
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (pData, MNG_FN_STORE_ERROR, MNG_LC_END);
+#endif
+
+  return MNG_TRUE;
+}
+
+/* ************************************************************************** */
+
+mng_bool mng_process_error (mng_datap   pData,
+                            mng_retcode iError,
+                            mng_retcode iExtra1,
+                            mng_retcode iExtra2)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (pData, MNG_FN_PROCESS_ERROR, MNG_LC_START);
+#endif
+
+  mng_store_error (pData, iError, iExtra1, iExtra2);
+
+  if ((pData != MNG_NULL) && (pData->iMagic == MNG_MAGIC))
+  {
+    if (pData->fErrorproc)             /* callback defined ? */
+      return pData->fErrorproc (((mng_handle)pData), iError, pData->iSeverity,
+                                pData->iChunkname, pData->iChunkseq,
+                                pData->iErrorx1, pData->iErrorx2, pData->zErrortext);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (pData, MNG_FN_PROCESS_ERROR, MNG_LC_END);
+#endif
+
+  return MNG_TRUE;                     /* warnings are ignored by default ! */
+}
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_error.h b/files/Source/LibMNG/libmng_error.h
new file mode 100644
index 0000000..b49ff73
--- /dev/null
+++ b/files/Source/LibMNG/libmng_error.h
@@ -0,0 +1,119 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_error.h            copyright (c) 2000-2002 G.Juyn   * */
+/* * version   : 1.0.5                                                      * */
+/* *                                                                        * */
+/* * purpose   : Error functions (definition)                               * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the generic error-codes and functions        * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/06/2000 - G.Juyn                                * */
+/* *             - added some errorcodes                                    * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - added some errorcodes                                    * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added application errorcodes (used with callbacks)       * */
+/* *             - moved chunk-access errorcodes to severity 5              * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/20/2000 - G.Juyn                                * */
+/* *             - added JNG errorcodes                                     * */
+/* *             0.5.2 - 05/23/2000 - G.Juyn                                * */
+/* *             - added error tell-tale definition                         * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added errorcodes for delta-image processing              * */
+/* *             0.5.2 - 06/06/2000 - G.Juyn                                * */
+/* *             - added errorcode for delayed buffer-processing            * */
+/* *             - moved errorcodes to "libmng.h"                           * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added macro + routine to set returncode without          * */
+/* *               calling error callback                                   * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 08/20/2002 - G.Juyn                                * */
+/* *             - added option for soft-handling of errors                 * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_error_h_
+#define _libmng_error_h_
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Default error routines                                                 * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_bool mng_store_error   (mng_datap   pData,
+                            mng_retcode iError,
+                            mng_retcode iExtra1,
+                            mng_retcode iExtra2);
+
+mng_bool mng_process_error (mng_datap   pData,
+                            mng_retcode iError,
+                            mng_retcode iExtra1,
+                            mng_retcode iExtra2);
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Error handling macros                                                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_SOFTERRORS
+#define MNG_ERROR(D,C)      { if (!mng_process_error (D, C, 0, 0)) return C; }
+#define MNG_ERRORZ(D,Z)     { if (!mng_process_error (D, MNG_ZLIBERROR, Z, 0)) return MNG_ZLIBERROR; }
+#define MNG_ERRORJ(D,J)     { if (!mng_process_error (D, MNG_JPEGERROR, J, 0)) return MNG_JPEGERROR; }
+#define MNG_ERRORL(D,L)     { if (!mng_process_error (D, MNG_LCMSERROR, L, 0)) return MNG_LCMSERROR; }
+#else
+#define MNG_ERROR(D,C)      { mng_process_error (D, C, 0, 0); return C; }
+#define MNG_ERRORZ(D,Z)     { mng_process_error (D, MNG_ZLIBERROR, Z, 0); return MNG_ZLIBERROR; }
+#define MNG_ERRORJ(D,J)     { mng_process_error (D, MNG_JPEGERROR, J, 0); return MNG_JPEGERROR; }
+#define MNG_ERRORL(D,L)     { mng_process_error (D, MNG_LCMSERROR, L, 0); return MNG_LCMSERROR; }
+#endif
+
+#define MNG_RETURN(D,C)     { mng_store_error (D, C, 0, 0); return C; }
+
+#define MNG_WARNING(D,C)    { if (!mng_process_error (D, C, 0, 0)) return C; }
+
+#define MNG_VALIDHANDLE(H)  { if ((H == 0) || (((mng_datap)H)->iMagic != MNG_MAGIC)) \
+                                return MNG_INVALIDHANDLE; }
+#define MNG_VALIDHANDLEX(H) { if ((H == 0) || (((mng_datap)H)->iMagic != MNG_MAGIC)) \
+                                return 0; }
+#define MNG_VALIDCB(D,C)    { if (!((mng_datap)D)->C) \
+                                MNG_ERROR (((mng_datap)D), MNG_NOCALLBACK) }
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Error string-table entry                                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+typedef struct {
+                 mng_retcode iError;
+                 mng_pchar   zErrortext;
+               } mng_error_entry;
+typedef mng_error_entry const * mng_error_entryp;
+
+/* ************************************************************************** */
+
+#endif /* _libmng_error_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_filter.c b/files/Source/LibMNG/libmng_filter.c
new file mode 100644
index 0000000..ed69a75
--- /dev/null
+++ b/files/Source/LibMNG/libmng_filter.c
@@ -0,0 +1,978 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_filter.c           copyright (c) 2000-2004 G.Juyn   * */
+/* * version   : 1.0.9                                                      * */
+/* *                                                                        * */
+/* * purpose   : Filtering routines (implementation)                        * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the filtering routines                   * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/07/2002 - G.Juyn                                * */
+/* *             - added test-option for PNG filter method 193 (=no filter) * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - reversed some loops to use decrementing counter          * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_filter.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_FILTERS
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode filter_sub (mng_datap pData)
+{
+  mng_uint32 iBpp;
+  mng_uint8p pRawx;
+  mng_uint8p pRawx_prev;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_SUB, MNG_LC_START);
+#endif
+
+  iBpp       = pData->iFilterbpp;
+  pRawx      = pData->pWorkrow + pData->iPixelofs + iBpp;
+  pRawx_prev = pData->pWorkrow + pData->iPixelofs;
+
+  for (iX = iBpp; iX < pData->iRowsize; iX++)
+  {
+    *pRawx = (mng_uint8)(*pRawx + *pRawx_prev);
+    pRawx++;
+    pRawx_prev++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_SUB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode filter_up (mng_datap pData)
+{
+  mng_uint8p pRawx;
+  mng_uint8p pPriorx;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_UP, MNG_LC_START);
+#endif
+
+  pRawx   = pData->pWorkrow + pData->iPixelofs;
+  pPriorx = pData->pPrevrow + pData->iPixelofs;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsize - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsize; iX++)
+#endif
+  {
+    *pRawx = (mng_uint8)(*pRawx + *pPriorx);
+    pRawx++;
+    pPriorx++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_UP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode filter_average (mng_datap pData)
+{
+  mng_int32  iBpp;
+  mng_uint8p pRawx;
+  mng_uint8p pRawx_prev;
+  mng_uint8p pPriorx;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_AVERAGE, MNG_LC_START);
+#endif
+
+  iBpp       = pData->iFilterbpp;
+  pRawx      = pData->pWorkrow + pData->iPixelofs;
+  pPriorx    = pData->pPrevrow + pData->iPixelofs;
+  pRawx_prev = pData->pWorkrow + pData->iPixelofs;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = iBpp - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < iBpp; iX++)
+#endif
+  {
+    *pRawx = (mng_uint8)(*pRawx + ((*pPriorx) >> 1));
+    pRawx++;
+    pPriorx++;
+  }
+
+  for (iX = iBpp; iX < pData->iRowsize; iX++)
+  {
+    *pRawx = (mng_uint8)(*pRawx + ((*pRawx_prev + *pPriorx) >> 1));
+    pRawx++;
+    pPriorx++;
+    pRawx_prev++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_AVERAGE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode filter_paeth (mng_datap pData)
+{
+  mng_int32  iBpp;
+  mng_uint8p pRawx;
+  mng_uint8p pRawx_prev;
+  mng_uint8p pPriorx;
+  mng_uint8p pPriorx_prev;
+  mng_int32  iX;
+  mng_uint32 iA, iB, iC;
+  mng_uint32 iP;
+  mng_uint32 iPa, iPb, iPc;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_PAETH, MNG_LC_START);
+#endif
+
+  iBpp         = pData->iFilterbpp;
+  pRawx        = pData->pWorkrow + pData->iPixelofs;
+  pPriorx      = pData->pPrevrow + pData->iPixelofs;
+  pRawx_prev   = pData->pWorkrow + pData->iPixelofs;
+  pPriorx_prev = pData->pPrevrow + pData->iPixelofs;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = iBpp - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < iBpp; iX++)
+#endif
+  {
+    *pRawx = (mng_uint8)(*pRawx + *pPriorx);
+
+    pRawx++;
+    pPriorx++;
+  }
+
+  for (iX = iBpp; iX < pData->iRowsize; iX++)
+  {
+    iA  = (mng_uint32)*pRawx_prev;
+    iB  = (mng_uint32)*pPriorx;
+    iC  = (mng_uint32)*pPriorx_prev;
+    iP  = iA + iB - iC;
+    iPa = abs (iP - iA);
+    iPb = abs (iP - iB);
+    iPc = abs (iP - iC);
+
+    if ((iPa <= iPb) && (iPa <= iPc))
+      *pRawx = (mng_uint8)(*pRawx + iA);
+    else
+      if (iPb <= iPc)
+        *pRawx = (mng_uint8)(*pRawx + iB);
+      else
+        *pRawx = (mng_uint8)(*pRawx + iC);
+
+    pRawx++;
+    pPriorx++;
+    pRawx_prev++;
+    pPriorx_prev++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_PAETH, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_filter_a_row (mng_datap pData)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_A_ROW, MNG_LC_START);
+#endif
+
+  switch (*(pData->pWorkrow + pData->iFilterofs))
+  {
+    case 1  : {
+                iRetcode = filter_sub     (pData);
+                break;
+              }
+    case 2  : {
+                iRetcode = filter_up      (pData);
+                break;
+              }
+    case 3  : {
+                iRetcode = filter_average (pData);
+                break;
+              }
+    case 4  : {
+                iRetcode = filter_paeth   (pData);
+                break;
+              }
+
+    default : iRetcode = MNG_INVALIDFILTER;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FILTER_A_ROW, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifdef FILTER192
+mng_retcode mng_init_rowdiffering (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ROWDIFFERING, MNG_LC_START);
+#endif
+
+  if (pData->iFilter == 0xC0)          /* has leveling parameters ? */
+  {
+    switch (pData->iColortype)         /* salvage leveling parameters */
+    {
+      case 0 : {                       /* gray */
+                 if (pData->iBitdepth <= 8)
+                   pData->iLevel0 = (mng_uint16)*pData->pWorkrow;
+                 else
+                   pData->iLevel0 = mng_get_uint16 (pData->pWorkrow);
+
+                 break;
+               }
+      case 2 : {                       /* rgb */
+                 if (pData->iBitdepth <= 8)
+                 {
+                   pData->iLevel0 = (mng_uint16)*pData->pWorkrow;
+                   pData->iLevel1 = (mng_uint16)*(pData->pWorkrow+1);
+                   pData->iLevel2 = (mng_uint16)*(pData->pWorkrow+2);
+                 }
+                 else
+                 {
+                   pData->iLevel0 = mng_get_uint16 (pData->pWorkrow);
+                   pData->iLevel1 = mng_get_uint16 (pData->pWorkrow+2);
+                   pData->iLevel2 = mng_get_uint16 (pData->pWorkrow+4);
+                 }
+
+                 break;
+               }
+      case 3 : {                       /* indexed */
+                 pData->iLevel0 = (mng_uint16)*pData->pWorkrow;
+                 break;
+               }
+      case 4 : {                       /* gray+alpha */
+                 if (pData->iBitdepth <= 8)
+                 {
+                   pData->iLevel0 = (mng_uint16)*pData->pWorkrow;
+                   pData->iLevel1 = (mng_uint16)*(pData->pWorkrow+1);
+                 }
+                 else
+                 {
+                   pData->iLevel0 = mng_get_uint16 (pData->pWorkrow);
+                   pData->iLevel1 = mng_get_uint16 (pData->pWorkrow+2);
+                 }
+
+                 break;
+               }
+      case 6 : {                       /* rgb+alpha */
+                 if (pData->iBitdepth <= 8)
+                 {
+                   pData->iLevel0 = (mng_uint16)*pData->pWorkrow;
+                   pData->iLevel1 = (mng_uint16)*(pData->pWorkrow+1);
+                   pData->iLevel2 = (mng_uint16)*(pData->pWorkrow+2);
+                   pData->iLevel3 = (mng_uint16)*(pData->pWorkrow+3);
+                 }
+                 else
+                 {
+                   pData->iLevel0 = mng_get_uint16 (pData->pWorkrow);
+                   pData->iLevel1 = mng_get_uint16 (pData->pWorkrow+2);
+                   pData->iLevel2 = mng_get_uint16 (pData->pWorkrow+4);
+                   pData->iLevel3 = mng_get_uint16 (pData->pWorkrow+6);
+                 }
+
+                 break;
+               }
+    }
+  }
+                                       /* shift the entire row back in place */
+  pRawi = pData->pWorkrow + pData->iFilterofs;
+  pRawo = pData->pWorkrow;
+
+  for (iX = 0; iX < pData->iRowsize + pData->iPixelofs - pData->iFilterofs; iX++)
+    *pRawo++ = *pRawi++;
+
+  pData->iFilterofs = 0;               /* indicate so ! */
+
+#ifdef FILTER193
+  if (pData->iFilter == 0xC1)          /* no adaptive filtering ? */
+    pData->iPixelofs = pData->iFilterofs;
+  else
+#endif
+    pData->iPixelofs = pData->iFilterofs + 1;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ROWDIFFERING, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_g1 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G1, MNG_LC_START);
+#endif
+
+  if (pData->iLevel0 & 0x01)           /* is it uneven level ? */
+  {
+    pRawi = pData->pWorkrow + pData->iPixelofs;
+    pRawo = pData->pPrevrow + pData->iPixelofs;
+                                       /* just invert every bit */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsize - 1; iX >= 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsize; iX++)
+#endif
+      *pRawo++ = (mng_uint8)(~(*pRawi++));
+
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_g2 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+  mng_int32  iC, iS;
+  mng_uint8  iB, iN, iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G2, MNG_LC_START);
+#endif
+
+  pRawi = pData->pWorkrow + pData->iPixelofs;
+  pRawo = pData->pPrevrow + pData->iPixelofs;
+  iC    = 0;
+  iB    = 0;
+  iN    = 0;
+  iS    = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iC)
+    {
+      iC = 4;
+      iB = *pRawi++;
+      iN = 0;
+      iS = 8;
+    }
+
+    iS -= 2;
+    iQ = (mng_uint8)(((iB >> iS) + pData->iLevel0) & 0x03);
+    iN = (mng_uint8)((iN << 2) + iQ);
+    iC--;
+
+    if (!iC)
+      *pRawo++ = iN;
+
+  }
+
+  if (iC)
+    *pRawo = (mng_uint8)(iN << iS);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_g4 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+  mng_int32  iC, iS;
+  mng_uint8  iB, iN, iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G4, MNG_LC_START);
+#endif
+
+  pRawi = pData->pWorkrow + pData->iPixelofs;
+  pRawo = pData->pPrevrow + pData->iPixelofs;
+  iC    = 0;
+  iB    = 0;
+  iN    = 0;
+  iS    = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iC)
+    {
+      iC = 2;
+      iB = *pRawi++;
+      iN = 0;
+      iS = 8;
+    }
+
+    iS -= 4;
+    iQ = (mng_uint8)(((iB >> iS) + pData->iLevel0) & 0x0F);
+    iN = (mng_uint8)((iN << 4) + iQ);
+    iC--;
+
+    if (!iC)
+      *pRawo++ = iN;
+
+  }
+
+  if (iC)
+    *pRawo = (mng_uint8)(iN << iS);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_g8 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G8, MNG_LC_START);
+#endif
+
+  pRawi = pData->pWorkrow + pData->iPixelofs;
+  pRawo = pData->pPrevrow + pData->iPixelofs;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pRawo++ = (mng_uint8)(((mng_uint16)*pRawi + pData->iLevel0) & 0xFF);
+
+    pRawi++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_g16 (mng_datap pData)
+{
+  mng_uint16p pRawi, pRawo;
+  mng_int32   iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G16, MNG_LC_START);
+#endif
+
+  pRawi = (mng_uint16p)(pData->pWorkrow + pData->iPixelofs);
+  pRawo = (mng_uint16p)(pData->pPrevrow + pData->iPixelofs);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pRawo++ = (mng_uint16)(((mng_uint32)*pRawi + (mng_uint32)pData->iLevel0) & 0xFFFF);
+
+    pRawi++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_rgb8 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_RGB8, MNG_LC_START);
+#endif
+
+  pRawi = pData->pWorkrow + pData->iPixelofs;
+  pRawo = pData->pPrevrow + pData->iPixelofs;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pRawo+1) = (mng_uint8)(((mng_uint16)*(pRawi+1) + pData->iLevel1) & 0xFF);
+    *pRawo     = (mng_uint8)(((mng_uint16)*pRawi     + pData->iLevel0 +
+                              (mng_uint16)*(pRawo+1)) & 0xFF);
+    *(pRawo+2) = (mng_uint8)(((mng_uint16)*(pRawi+2) + pData->iLevel2 +
+                              (mng_uint16)*(pRawo+1)) & 0xFF);
+
+    pRawi += 3;
+    pRawo += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_rgb16 (mng_datap pData)
+{
+  mng_uint16p pRawi, pRawo;
+  mng_int32   iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_RGB16, MNG_LC_START);
+#endif
+
+  pRawi = (mng_uint16p)(pData->pWorkrow + pData->iPixelofs);
+  pRawo = (mng_uint16p)(pData->pPrevrow + pData->iPixelofs);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pRawo+1) = (mng_uint16)(((mng_uint32)*(pRawi+1) + (mng_uint32)pData->iLevel1) & 0xFFFF);
+    *pRawo     = (mng_uint16)(((mng_uint32)*pRawi     + (mng_uint32)pData->iLevel0 +
+                               (mng_uint32)*(pRawo+1)) & 0xFFFF);
+    *(pRawo+2) = (mng_uint16)(((mng_uint32)*(pRawi+2) + (mng_uint32)pData->iLevel2 +
+                               (mng_uint32)*(pRawo+1)) & 0xFFFF);
+
+    pRawi += 3;
+    pRawo += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_idx1 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_IDX1, MNG_LC_START);
+#endif
+
+  if (pData->iLevel0 & 0x01)           /* is it uneven level ? */
+  {
+    pRawi = pData->pWorkrow + pData->iPixelofs;
+    pRawo = pData->pPrevrow + pData->iPixelofs;
+                                       /* just invert every bit */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsize - 1; iX >= 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsize; iX++)
+#endif
+      *pRawo++ = (mng_uint8)(~(*pRawi++));
+
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_IDX1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_idx2 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+  mng_int32  iC, iS;
+  mng_uint8  iB, iN, iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_IDX2, MNG_LC_START);
+#endif
+
+  pRawi = pData->pWorkrow + pData->iPixelofs;
+  pRawo = pData->pPrevrow + pData->iPixelofs;
+  iC    = 0;
+  iB    = 0;
+  iN    = 0;
+  iS    = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iC)
+    {
+      iC = 4;
+      iB = *pRawi++;
+      iN = 0;
+      iS = 8;
+    }
+
+    iS -= 2;
+    iQ = (mng_uint8)(((iB >> iS) + pData->iLevel0) & 0x03);
+    iN = (mng_uint8)((iN << 2) + iQ);
+    iC--;
+
+    if (!iC)
+      *pRawo++ = iN;
+
+  }
+
+  if (iC)
+    *pRawo = (mng_uint8)(iN << iS);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_IDX2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_idx4 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+  mng_int32  iC, iS;
+  mng_uint8  iB, iN, iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_IDX4, MNG_LC_START);
+#endif
+
+  pRawi = pData->pWorkrow + pData->iPixelofs;
+  pRawo = pData->pPrevrow + pData->iPixelofs;
+  iC    = 0;
+  iB    = 0;
+  iN    = 0;
+  iS    = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iC)
+    {
+      iC = 2;
+      iB = *pRawi++;
+      iN = 0;
+      iS = 8;
+    }
+
+    iS -= 4;
+    iQ = (mng_uint8)(((iB >> iS) + pData->iLevel0) & 0x0F);
+    iN = (mng_uint8)((iN << 4) + iQ);
+    iC--;
+
+    if (!iC)
+      *pRawo++ = iN;
+
+  }
+
+  if (iC)
+    *pRawo = (mng_uint8)(iN << iS);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_IDX4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_idx8 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_IDX8, MNG_LC_START);
+#endif
+
+  pRawi = pData->pWorkrow + pData->iPixelofs;
+  pRawo = pData->pPrevrow + pData->iPixelofs;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pRawo++ = (mng_uint8)(((mng_uint16)*pRawi + pData->iLevel0) & 0xFF);
+
+    pRawi++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_IDX8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_ga8 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_GA8, MNG_LC_START);
+#endif
+
+  pRawi = pData->pWorkrow + pData->iPixelofs;
+  pRawo = pData->pPrevrow + pData->iPixelofs;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pRawo     = (mng_uint8)(((mng_uint16)*pRawi     + pData->iLevel0) & 0xFF);
+    *(pRawo+1) = (mng_uint8)(((mng_uint16)*(pRawi+1) + pData->iLevel1) & 0xFF);
+
+    pRawi += 2;
+    pRawo += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_GA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_ga16 (mng_datap pData)
+{
+  mng_uint16p pRawi, pRawo;
+  mng_int32   iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_GA16, MNG_LC_START);
+#endif
+
+  pRawi = (mng_uint16p)(pData->pWorkrow + pData->iPixelofs);
+  pRawo = (mng_uint16p)(pData->pPrevrow + pData->iPixelofs);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pRawo     = (mng_uint16)(((mng_uint32)*pRawi     + (mng_uint32)pData->iLevel0) & 0xFFFF);
+    *(pRawo+1) = (mng_uint16)(((mng_uint32)*(pRawi+1) + (mng_uint32)pData->iLevel1) & 0xFFFF);
+
+    pRawi += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_GA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_rgba8 (mng_datap pData)
+{
+  mng_uint8p pRawi, pRawo;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_RGBA8, MNG_LC_START);
+#endif
+
+  pRawi = pData->pWorkrow + pData->iPixelofs;
+  pRawo = pData->pPrevrow + pData->iPixelofs;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pRawo+1) = (mng_uint8)(((mng_uint16)*(pRawi+1) + pData->iLevel1) & 0xFF);
+    *pRawo     = (mng_uint8)(((mng_uint16)*pRawi     + pData->iLevel0 +
+                              (mng_uint16)*(pRawo+1)) & 0xFF);
+    *(pRawo+2) = (mng_uint8)(((mng_uint16)*(pRawi+2) + pData->iLevel2 +
+                              (mng_uint16)*(pRawo+1)) & 0xFF);
+    *(pRawo+3) = (mng_uint8)(((mng_uint16)*(pRawi+3) + pData->iLevel3) & 0xFF);
+
+    pRawi += 4;
+    pRawo += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_differ_rgba16 (mng_datap pData)
+{
+  mng_uint16p pRawi, pRawo;
+  mng_int32   iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_RGBA16, MNG_LC_START);
+#endif
+
+  pRawi = (mng_uint16p)(pData->pWorkrow + pData->iPixelofs);
+  pRawo = (mng_uint16p)(pData->pPrevrow + pData->iPixelofs);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples - 1; iX >= 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pRawo+1) = (mng_uint16)(((mng_uint32)*(pRawi+1) + (mng_uint32)pData->iLevel1) & 0xFFFF);
+    *pRawo     = (mng_uint16)(((mng_uint32)*pRawi     + (mng_uint32)pData->iLevel0 +
+                               (mng_uint32)*(pRawo+1)) & 0xFFFF);
+    *(pRawo+2) = (mng_uint16)(((mng_uint32)*(pRawi+2) + (mng_uint32)pData->iLevel2 +
+                               (mng_uint32)*(pRawo+1)) & 0xFFFF);
+    *(pRawo+3) = (mng_uint16)(((mng_uint32)*(pRawi+3) + (mng_uint32)pData->iLevel3) & 0xFFFF);
+
+    pRawi += 4;
+    pRawo += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DIFFER_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* FILTER192 */
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_FILTERS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_filter.h b/files/Source/LibMNG/libmng_filter.h
new file mode 100644
index 0000000..9ac9c7f
--- /dev/null
+++ b/files/Source/LibMNG/libmng_filter.h
@@ -0,0 +1,69 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_filter.h           copyright (c) 2000-2002 G.Juyn   * */
+/* * version   : 1.0.5                                                      * */
+/* *                                                                        * */
+/* * purpose   : Filtering routines (definition)                            * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the filtering routines                       * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_filter_h_
+#define _libmng_filter_h_
+
+/* ************************************************************************** */
+
+mng_retcode mng_filter_a_row         (mng_datap pData);
+
+/* ************************************************************************** */
+
+#ifdef FILTER192
+mng_retcode mng_init_rowdiffering    (mng_datap pData);
+
+mng_retcode mng_differ_g1            (mng_datap pData);
+mng_retcode mng_differ_g2            (mng_datap pData);
+mng_retcode mng_differ_g4            (mng_datap pData);
+mng_retcode mng_differ_g8            (mng_datap pData);
+mng_retcode mng_differ_g16           (mng_datap pData);
+mng_retcode mng_differ_rgb8          (mng_datap pData);
+mng_retcode mng_differ_rgb16         (mng_datap pData);
+mng_retcode mng_differ_idx1          (mng_datap pData);
+mng_retcode mng_differ_idx2          (mng_datap pData);
+mng_retcode mng_differ_idx4          (mng_datap pData);
+mng_retcode mng_differ_idx8          (mng_datap pData);
+mng_retcode mng_differ_ga8           (mng_datap pData);
+mng_retcode mng_differ_ga16          (mng_datap pData);
+mng_retcode mng_differ_rgba8         (mng_datap pData);
+mng_retcode mng_differ_rgba16        (mng_datap pData);
+#endif
+
+/* ************************************************************************** */
+
+#endif /* _libmng_filter_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_hlapi.c b/files/Source/LibMNG/libmng_hlapi.c
new file mode 100644
index 0000000..6199d59
--- /dev/null
+++ b/files/Source/LibMNG/libmng_hlapi.c
@@ -0,0 +1,2995 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_hlapi.c            copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : high-level application API (implementation)                * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the high-level function interface        * */
+/* *             for applications.                                          * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/06/2000 - G.Juyn                                * */
+/* *             - added init of iPLTEcount                                 * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed calling-convention definition                    * */
+/* *             - changed status-handling of display-routines              * */
+/* *             - added versioning-control routines                        * */
+/* *             - filled the write routine                                 * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added callback error-reporting support                   * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *             0.5.1 - 05/13/2000 - G.Juyn                                * */
+/* *             - added eMNGma hack (will be removed in 1.0.0 !!!)         * */
+/* *             - added TERM animation object pointer (easier reference)   * */
+/* *             0.5.1 - 05/14/2000 - G.Juyn                                * */
+/* *             - added cleanup of saved-data (SAVE/SEEK processing)       * */
+/* *             0.5.1 - 05/16/2000 - G.Juyn                                * */
+/* *             - moved the actual write_graphic functionality from here   * */
+/* *               to its appropriate function in the mng_write module      * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/19/2000 - G.Juyn                                * */
+/* *             - cleaned up some code regarding mixed support             * */
+/* *             - added JNG support                                        * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - moved init of default zlib parms here from "mng_zlib.c"  * */
+/* *             - added init of default IJG parms                          * */
+/* *             0.5.2 - 05/29/2000 - G.Juyn                                * */
+/* *             - fixed inconsistancy with freeing global iCCP profile     * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added delta-image field initialization                   * */
+/* *             0.5.2 - 06/06/2000 - G.Juyn                                * */
+/* *             - added initialization of the buffer-suspend parameter     * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/16/2000 - G.Juyn                                * */
+/* *             - added initialization of update-region for refresh        * */
+/* *             - added initialization of Needrefresh parameter            * */
+/* *             0.5.3 - 06/17/2000 - G.Juyn                                * */
+/* *             - added initialization of Deltaimmediate                   * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added initialization of Speed                            * */
+/* *             - added initialization of Imagelevel                       * */
+/* *             0.5.3 - 06/26/2000 - G.Juyn                                * */
+/* *             - changed userdata variable to mng_ptr                     * */
+/* *             0.5.3 - 06/29/2000 - G.Juyn                                * */
+/* *             - fixed initialization routine for new mng_handle type     * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/06/2000 - G.Juyn                                * */
+/* *             - changed mng_display_resume to allow to be called after   * */
+/* *               a suspension return with MNG_NEEDMOREDATA                * */
+/* *             - added returncode MNG_NEEDTIMERWAIT for timer breaks      * */
+/* *             0.9.1 - 07/07/2000 - G.Juyn                                * */
+/* *             - implemented support for freeze/reset/resume & go_xxxx    * */
+/* *             0.9.1 - 07/08/2000 - G.Juyn                                * */
+/* *             - added support for improved timing                        * */
+/* *             - added support for improved I/O-suspension                * */
+/* *             0.9.1 - 07/14/2000 - G.Juyn                                * */
+/* *             - changed EOF processing behavior                          * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added callbacks for SAVE/SEEK processing                 * */
+/* *             - added variable for NEEDSECTIONWAIT breaks                * */
+/* *             - added variable for freeze & reset processing             * */
+/* *             0.9.1 - 07/17/2000 - G.Juyn                                * */
+/* *             - added error cleanup processing                           * */
+/* *             - fixed support for mng_display_reset()                    * */
+/* *             - fixed suspension-buffering for 32K+ chunks               * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/29/2000 - G.Juyn                                * */
+/* *             - fixed small bugs in display processing                   * */
+/* *             0.9.2 - 07/31/2000 - G.Juyn                                * */
+/* *             - fixed wrapping of suspension parameters                  * */
+/* *             0.9.2 - 08/04/2000 - G.Juyn                                * */
+/* *             - B111096 - fixed large-buffer read-suspension             * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *             0.9.3 - 09/10/2000 - G.Juyn                                * */
+/* *             - fixed DEFI behavior                                      * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - added support for nEED                                   * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added optional support for bKGD for PNG images           * */
+/* *             - raised initial maximum canvas size                       * */
+/* *             - added support for JDAA                                   * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added callback to process non-critical unknown chunks    * */
+/* *             - fixed support for delta-images during read() / display() * */
+/* *             0.9.3 - 10/18/2000 - G.Juyn                                * */
+/* *             - added closestream() processing for mng_cleanup()         * */
+/* *             0.9.3 - 10/27/2000 - G.Juyn                                * */
+/* *             - fixed separate read() & display() processing             * */
+/* *                                                                        * */
+/* *             0.9.4 - 11/20/2000 - G.Juyn                                * */
+/* *             - fixed unwanted repetition in mng_readdisplay()           * */
+/* *             0.9.4 - 11/24/2000 - G.Juyn                                * */
+/* *             - moved restore of object 0 to libmng_display              * */
+/* *                                                                        * */
+/* *             1.0.1 - 02/08/2001 - G.Juyn                                * */
+/* *             - added MEND processing callback                           * */
+/* *             1.0.1 - 02/13/2001 - G.Juyn                                * */
+/* *             - fixed first FRAM_MODE=4 timing problem                   * */
+/* *             1.0.1 - 04/21/2001 - G.Juyn                                * */
+/* *             - fixed bug with display_reset/display_resume (Thanks G!)  * */
+/* *             1.0.1 - 04/22/2001 - G.Juyn                                * */
+/* *             - fixed memory-leak (Thanks Gregg!)                        * */
+/* *             1.0.1 - 04/23/2001 - G.Juyn                                * */
+/* *             - fixed reset_rundata to drop all objects                  * */
+/* *             1.0.1 - 04/25/2001 - G.Juyn                                * */
+/* *             - moved mng_clear_cms to libmng_cms                        * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added optimization option for MNG-video playback         * */
+/* *             - added processterm callback                               * */
+/* *             1.0.2 - 06/25/2001 - G.Juyn                                * */
+/* *             - added option to turn off progressive refresh             * */
+/* *                                                                        * */
+/* *             1.0.5 - 07/08/2002 - G.Juyn                                * */
+/* *             - B578572 - removed eMNGma hack (thanks Dimitri!)          * */
+/* *             1.0.5 - 07/16/2002 - G.Juyn                                * */
+/* *             - B581625 - large chunks fail with suspension reads        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/15/2002 - G.Juyn                                * */
+/* *             - fixed LOOP iteration=0 special case                      * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - added another fix for misplaced TERM chunk               * */
+/* *             - completed support for condition=2 in TERM chunk          * */
+/* *             - added beta version function & constant                   * */
+/* *             1.0.5 - 10/11/2002 - G.Juyn                                * */
+/* *             - added mng_status_dynamic to supports function            * */
+/* *             1.0.5 - 11/04/2002 - G.Juyn                                * */
+/* *             - changed FRAMECOUNT/LAYERCOUNT/PLAYTIME error to warning  * */
+/* *             1.0.5 - 11/07/2002 - G.Juyn                                * */
+/* *             - added support to get totals after mng_read()             * */
+/* *             1.0.5 - 11/29/2002 - G.Juyn                                * */
+/* *             - fixed goxxxxx() support for zero values                  * */
+/* *                                                                        * */
+/* *             1.0.6 - 05/25/2003 - G.R-P                                 * */
+/* *             - added MNG_SKIPCHUNK_cHNK footprint optimizations         * */
+/* *             1.0.6 - 07/11/2003 - G.R-P                                 * */
+/* *             - added conditionals zlib and jpeg property accessors      * */
+/* *             1.0.6 - 07/14/2003 - G.R-P                                 * */
+/* *             - added conditionals around "mng_display_go*" and other    * */
+/* *               unused functions                                         * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/07/2004 - G. Randers-Pehrson                    * */
+/* *             - put gamma, cms-related declarations inside #ifdef        * */
+/* *             1.0.7 - 03/10/2004 - G.R-P                                 * */
+/* *             - added conditionals around openstream/closestream         * */
+/* *             1.0.7 - 03/24/2004 - G.R-P                                 * */
+/* *             - fixed zTXT -> zTXt typo                                  * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/02/2004 - G.Juyn                                * */
+/* *             - added CRC existence & checking flags                     * */
+/* *             1.0.8 - 04/10/2004 - G.Juyn                                * */
+/* *             - added data-push mechanisms for specialized decoders      * */
+/* *             1.0.8 - 07/06/2004 - G.R-P                                 * */
+/* *             - defend against using undefined openstream function       * */
+/* *             1.0.8 - 08/02/2004 - G.Juyn                                * */
+/* *             - added conditional to allow easier writing of large MNG's * */
+/* *                                                                        * */
+/* *             1.0.9 - 08/17/2004 - G.R-P                                 * */
+/* *             - added more SKIPCHUNK conditionals                        * */
+/* *             1.0.9 - 09/25/2004 - G.Juyn                                * */
+/* *             - replaced MNG_TWEAK_LARGE_FILES with permanent solution   * */
+/* *             1.0.9 - 10/03/2004 - G.Juyn                                * */
+/* *             - added function to retrieve current FRAM delay            * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* *             1.0.10 - 07/06/2005 - G.R-P                                * */
+/* *             - added more SKIPCHUNK conditionals                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *             1.0.10 - 07/06/2007 - G.R-P bugfix by Lucas Quintana       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_objects.h"
+#include "libmng_object_prc.h"
+#include "libmng_chunks.h"
+#include "libmng_memory.h"
+#include "libmng_read.h"
+#include "libmng_write.h"
+#include "libmng_display.h"
+#include "libmng_zlib.h"
+#include "libmng_jpeg.h"
+#include "libmng_cms.h"
+#include "libmng_pixels.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * local routines                                                         * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_LOCAL mng_retcode mng_drop_objects (mng_datap pData,
+                                        mng_bool  bDropaniobj)
+{
+  mng_objectp       pObject;
+  mng_objectp       pNext;
+  mng_cleanupobject fCleanup;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DROP_OBJECTS, MNG_LC_START);
+#endif
+
+  pObject = pData->pFirstimgobj;       /* get first stored image-object (if any) */
+
+  while (pObject)                      /* more objects to discard ? */
+  {
+    pNext = ((mng_object_headerp)pObject)->pNext;
+                                       /* call appropriate cleanup */
+    fCleanup = ((mng_object_headerp)pObject)->fCleanup;
+    fCleanup (pData, pObject);
+
+    pObject = pNext;                   /* neeeext */
+  }
+
+  pData->pFirstimgobj = MNG_NULL;      /* clean this up!!! */
+  pData->pLastimgobj  = MNG_NULL;
+
+  if (bDropaniobj)                     /* drop animation objects ? */
+  {
+    pObject = pData->pFirstaniobj;     /* get first stored animation-object (if any) */
+
+    while (pObject)                    /* more objects to discard ? */
+    {
+      pNext = ((mng_object_headerp)pObject)->pNext;
+                                       /* call appropriate cleanup */
+      fCleanup = ((mng_object_headerp)pObject)->fCleanup;
+      fCleanup (pData, pObject);
+
+      pObject = pNext;                 /* neeeext */
+    }
+
+    pData->pFirstaniobj = MNG_NULL;    /* clean this up!!! */
+    pData->pLastaniobj  = MNG_NULL;
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+    pObject = pData->pFirstevent;      /* get first event-object (if any) */
+
+    while (pObject)                    /* more objects to discard ? */
+    {
+      pNext = ((mng_object_headerp)pObject)->pNext;
+                                       /* call appropriate cleanup */
+      fCleanup = ((mng_object_headerp)pObject)->fCleanup;
+      fCleanup (pData, pObject);
+
+      pObject = pNext;                 /* neeeext */
+    }
+
+    pData->pFirstevent = MNG_NULL;     /* clean this up!!! */
+    pData->pLastevent  = MNG_NULL;
+#endif
+  }
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+  if (pData->pMPNG)                    /* drop MPNG data (if any) */
+  {
+    fCleanup = ((mng_object_headerp)pData->pMPNG)->fCleanup;
+    fCleanup (pData, pData->pMPNG);
+    pData->pMPNG = MNG_NULL;
+  }
+#endif
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+  if (pData->pANG)                     /* drop ANG data (if any) */
+  {
+    fCleanup = ((mng_object_headerp)pData->pANG)->fCleanup;
+    fCleanup (pData, pData->pANG);
+    pData->pANG = MNG_NULL;
+  }
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DROP_OBJECTS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_SKIPCHUNK_SAVE
+MNG_LOCAL mng_retcode mng_drop_savedata (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DROP_SAVEDATA, MNG_LC_START);
+#endif
+
+  if (pData->pSavedata)                /* sanity check */
+  {                                    /* address it more directly */
+    mng_savedatap pSave = pData->pSavedata;
+
+    if (pSave->iGlobalProfilesize)     /* cleanup the profile ? */
+      MNG_FREEX (pData, pSave->pGlobalProfile, pSave->iGlobalProfilesize);
+                                       /* cleanup the save structure */
+    MNG_FREE (pData, pData->pSavedata, sizeof (mng_savedata));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DROP_SAVEDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+MNG_LOCAL mng_retcode mng_reset_rundata (mng_datap pData)
+{
+  mng_drop_invalid_objects (pData);    /* drop invalidly stored objects */
+#ifndef MNG_SKIPCHUNK_SAVE
+  mng_drop_savedata        (pData);    /* drop stored savedata */
+#endif
+  mng_reset_objzero        (pData);    /* reset object 0 */
+                                       /* drop stored objects (if any) */
+  mng_drop_objects         (pData, MNG_FALSE);
+
+  pData->bFramedone            = MNG_FALSE;
+  pData->iFrameseq             = 0;    /* reset counters & stuff */
+  pData->iLayerseq             = 0;
+  pData->iFrametime            = 0;
+
+  pData->bSkipping             = MNG_FALSE;
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+  pData->bRunningevent         = MNG_FALSE;
+  pData->bStopafterseek        = MNG_FALSE;
+  pData->iEventx               = 0;
+  pData->iEventy               = 0;
+  pData->pLastmousemove        = MNG_NULL;
+#endif
+
+  pData->iRequestframe         = 0;
+  pData->iRequestlayer         = 0;
+  pData->iRequesttime          = 0;
+  pData->bSearching            = MNG_FALSE;
+
+  pData->iRuntime              = 0;
+  pData->iSynctime             = 0;
+  pData->iStarttime            = 0;
+  pData->iEndtime              = 0;
+  pData->bRunning              = MNG_FALSE;
+  pData->bTimerset             = MNG_FALSE;
+  pData->iBreakpoint           = 0;
+  pData->bSectionwait          = MNG_FALSE;
+  pData->bFreezing             = MNG_FALSE;
+  pData->bResetting            = MNG_FALSE;
+  pData->bNeedrefresh          = MNG_FALSE;
+  pData->bOnlyfirstframe       = MNG_FALSE;
+  pData->iFramesafterTERM      = 0;
+
+  pData->iIterations           = 0;
+                                       /* start of animation objects! */
+  pData->pCurraniobj           = MNG_NULL;
+
+  pData->iUpdateleft           = 0;    /* reset region */
+  pData->iUpdateright          = 0;
+  pData->iUpdatetop            = 0;
+  pData->iUpdatebottom         = 0;
+  pData->iPLTEcount            = 0;    /* reset PLTE data */
+
+#ifndef MNG_SKIPCHUNK_DEFI
+  pData->iDEFIobjectid         = 0;    /* reset DEFI data */
+  pData->bDEFIhasdonotshow     = MNG_FALSE;
+  pData->iDEFIdonotshow        = 0;
+  pData->bDEFIhasconcrete      = MNG_FALSE;
+  pData->iDEFIconcrete         = 0;
+  pData->bDEFIhasloca          = MNG_FALSE;
+  pData->iDEFIlocax            = 0;
+  pData->iDEFIlocay            = 0;
+  pData->bDEFIhasclip          = MNG_FALSE;
+  pData->iDEFIclipl            = 0;
+  pData->iDEFIclipr            = 0;
+  pData->iDEFIclipt            = 0;
+  pData->iDEFIclipb            = 0;
+#endif
+
+#ifndef MNG_SKIPCHUNK_BACK
+  pData->iBACKred              = 0;    /* reset BACK data */
+  pData->iBACKgreen            = 0;
+  pData->iBACKblue             = 0;
+  pData->iBACKmandatory        = 0;
+  pData->iBACKimageid          = 0;
+  pData->iBACKtile             = 0;
+#endif
+
+#ifndef MNG_SKIPCHUNK_FRAM
+  pData->iFRAMmode             = 1;     /* default global FRAM variables */
+  pData->iFRAMdelay            = 1;
+  pData->iFRAMtimeout          = 0x7fffffffl;
+  pData->bFRAMclipping         = MNG_FALSE;
+  pData->iFRAMclipl            = 0;
+  pData->iFRAMclipr            = 0;
+  pData->iFRAMclipt            = 0;
+  pData->iFRAMclipb            = 0;
+
+  pData->iFramemode            = 1;     /* again for the current frame */
+  pData->iFramedelay           = 1;
+  pData->iFrametimeout         = 0x7fffffffl;
+  pData->bFrameclipping        = MNG_FALSE;
+  pData->iFrameclipl           = 0;
+  pData->iFrameclipr           = 0;
+  pData->iFrameclipt           = 0;
+  pData->iFrameclipb           = 0;
+
+  pData->iNextdelay            = 1;
+#endif
+
+#ifndef MNG_SKIPCHUNK_SHOW
+  pData->iSHOWmode             = 0;    /* reset SHOW data */
+  pData->iSHOWfromid           = 0;
+  pData->iSHOWtoid             = 0;
+  pData->iSHOWnextid           = 0;
+  pData->iSHOWskip             = 0;
+#endif
+
+  pData->iGlobalPLTEcount      = 0;    /* reset global PLTE data */
+
+  pData->iGlobalTRNSrawlen     = 0;    /* reset global tRNS data */
+
+  pData->iGlobalGamma          = 0;    /* reset global gAMA data */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+  pData->iGlobalWhitepointx    = 0;    /* reset global cHRM data */
+  pData->iGlobalWhitepointy    = 0;
+  pData->iGlobalPrimaryredx    = 0;
+  pData->iGlobalPrimaryredy    = 0;
+  pData->iGlobalPrimarygreenx  = 0;
+  pData->iGlobalPrimarygreeny  = 0;
+  pData->iGlobalPrimarybluex   = 0;
+  pData->iGlobalPrimarybluey   = 0;
+#endif
+
+#ifndef MNG_SKIPCHUNK_sRGB
+  pData->iGlobalRendintent     = 0;    /* reset global sRGB data */
+#endif
+
+#ifndef MNG_SKIPCHUNK_iCCP
+  if (pData->iGlobalProfilesize)       /* drop global profile (if any) */
+    MNG_FREE (pData, pData->pGlobalProfile, pData->iGlobalProfilesize);
+
+  pData->iGlobalProfilesize    = 0;    
+#endif
+
+#ifndef MNG_SKIPCHUNK_bKGD
+  pData->iGlobalBKGDred        = 0;    /* reset global bKGD data */
+  pData->iGlobalBKGDgreen      = 0;
+  pData->iGlobalBKGDblue       = 0;
+#endif
+#ifndef MNG_NO_DELTA_PNG
+                                       /* reset delta-image */
+  pData->pDeltaImage           = MNG_NULL;
+  pData->iDeltaImagetype       = 0;
+  pData->iDeltatype            = 0;
+  pData->iDeltaBlockwidth      = 0;
+  pData->iDeltaBlockheight     = 0;
+  pData->iDeltaBlockx          = 0;
+  pData->iDeltaBlocky          = 0;
+  pData->bDeltaimmediate       = MNG_FALSE;
+
+  pData->fDeltagetrow          = MNG_NULL;
+  pData->fDeltaaddrow          = MNG_NULL;
+  pData->fDeltareplacerow      = MNG_NULL;
+  pData->fDeltaputrow          = MNG_NULL;
+
+  pData->fPromoterow           = MNG_NULL;
+  pData->fPromBitdepth         = MNG_NULL;
+  pData->pPromBuf              = MNG_NULL;
+  pData->iPromColortype        = 0;
+  pData->iPromBitdepth         = 0;
+  pData->iPromFilltype         = 0;
+  pData->iPromWidth            = 0;
+  pData->pPromSrc              = MNG_NULL;
+  pData->pPromDst              = MNG_NULL;
+#endif
+
+#ifndef MNG_SKIPCHUNK_MAGN
+  pData->iMAGNfromid           = 0;
+  pData->iMAGNtoid             = 0;
+#endif
+
+#ifndef MNG_SKIPCHUNK_PAST
+  pData->iPastx                = 0;
+  pData->iPasty                = 0;
+#endif
+
+  pData->pLastseek             = MNG_NULL;
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+MNG_LOCAL void cleanup_errors (mng_datap pData)
+{
+  pData->iErrorcode = MNG_NOERROR;
+  pData->iSeverity  = 0;
+  pData->iErrorx1   = 0;
+  pData->iErrorx2   = 0;
+  pData->zErrortext = MNG_NULL;
+
+  return;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+MNG_LOCAL mng_retcode make_pushbuffer (mng_datap       pData,
+                                       mng_ptr         pPushdata,
+                                       mng_size_t      iLength,
+                                       mng_bool        bTakeownership,
+                                       mng_pushdatap * pPush)
+{
+  mng_pushdatap pTemp;
+
+  MNG_ALLOC (pData, pTemp, sizeof(mng_pushdata));
+
+  pTemp->pNext      = MNG_NULL;
+
+  if (bTakeownership)                  /* are we going to own the buffer? */
+  {                                    /* then just copy the pointer */
+    pTemp->pData    = (mng_uint8p)pPushdata;
+  }
+  else
+  {                                    /* otherwise create new buffer */
+    MNG_ALLOCX (pData, pTemp->pData, iLength);
+    if (!pTemp->pData)                 /* succeeded? */
+    {
+      MNG_FREEX (pData, pTemp, sizeof(mng_pushdata));
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+                                       /* and copy the bytes across */
+    MNG_COPY (pTemp->pData, pPushdata, iLength);
+  }
+
+  pTemp->iLength    = iLength;
+  pTemp->bOwned     = bTakeownership;
+  pTemp->pDatanext  = pTemp->pData;
+  pTemp->iRemaining = iLength;
+
+  *pPush            = pTemp;           /* return it */
+
+  return MNG_NOERROR;                  /* and all's well */
+}
+#endif
+
+#ifdef MNG_VERSION_QUERY_SUPPORT
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Versioning control                                                    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_pchar MNG_DECL mng_version_text    (void)
+{
+  return MNG_VERSION_TEXT;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_version_so      (void)
+{
+  return MNG_VERSION_SO;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_version_dll     (void)
+{
+  return MNG_VERSION_DLL;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_version_major   (void)
+{
+  return MNG_VERSION_MAJOR;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_version_minor   (void)
+{
+  return MNG_VERSION_MINOR;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_version_release (void)
+{
+  return MNG_VERSION_RELEASE;
+}
+
+/* ************************************************************************** */
+
+mng_bool MNG_DECL mng_version_beta (void)
+{
+  return MNG_VERSION_BETA;
+}
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * 'supports' function                                                    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_FUNCQUERY
+typedef struct {
+                 mng_pchar  zFunction;
+                 mng_uint8  iMajor;    /* Major == 0 means not implemented ! */ 
+                 mng_uint8  iMinor;
+                 mng_uint8  iRelease;
+               } mng_func_entry;
+typedef mng_func_entry const * mng_func_entryp;
+
+MNG_LOCAL mng_func_entry const func_table [] =
+  {                                    /* keep it alphabetically sorted !!!!! */
+    {"mng_cleanup",                1, 0, 0},
+    {"mng_copy_chunk",             1, 0, 5},
+    {"mng_create",                 1, 0, 0},
+    {"mng_display",                1, 0, 0},
+    {"mng_display_freeze",         1, 0, 0},
+#ifndef MNG_NO_DISPLAY_GO_SUPPORTED
+    {"mng_display_goframe",        1, 0, 0},
+    {"mng_display_golayer",        1, 0, 0},
+    {"mng_display_gotime",         1, 0, 0},
+#endif
+    {"mng_display_reset",          1, 0, 0},
+    {"mng_display_resume",         1, 0, 0},
+    {"mng_get_alphabitdepth",      1, 0, 0},
+    {"mng_get_alphacompression",   1, 0, 0},
+    {"mng_get_alphadepth",         1, 0, 0},
+    {"mng_get_alphafilter",        1, 0, 0},
+    {"mng_get_alphainterlace",     1, 0, 0},
+    {"mng_get_bgcolor",            1, 0, 0},
+    {"mng_get_bitdepth",           1, 0, 0},
+    {"mng_get_bkgdstyle",          1, 0, 0},
+    {"mng_get_cacheplayback",      1, 0, 2},
+    {"mng_get_canvasstyle",        1, 0, 0},
+    {"mng_get_colortype",          1, 0, 0},
+    {"mng_get_compression",        1, 0, 0},
+#ifndef MNG_NO_CURRENT_INFO
+    {"mng_get_currentframe",       1, 0, 0},
+    {"mng_get_currentlayer",       1, 0, 0},
+    {"mng_get_currentplaytime",    1, 0, 0},
+#endif
+    {"mng_get_currframdelay",      1, 0, 9},
+#ifndef MNG_NO_DFLT_INFO
+    {"mng_get_dfltimggamma",       1, 0, 0},
+    {"mng_get_dfltimggammaint",    1, 0, 0},
+#endif
+    {"mng_get_displaygamma",       1, 0, 0},
+    {"mng_get_displaygammaint",    1, 0, 0},
+    {"mng_get_doprogressive",      1, 0, 2},
+    {"mng_get_filter",             1, 0, 0},
+    {"mng_get_framecount",         1, 0, 0},
+    {"mng_get_imageheight",        1, 0, 0},
+    {"mng_get_imagelevel",         1, 0, 0},
+    {"mng_get_imagetype",          1, 0, 0},
+    {"mng_get_imagewidth",         1, 0, 0},
+    {"mng_get_interlace",          1, 0, 0},
+#ifdef MNG_ACCESS_JPEG
+    {"mng_get_jpeg_dctmethod",     1, 0, 0},
+    {"mng_get_jpeg_maxjdat",       1, 0, 0},
+    {"mng_get_jpeg_optimized",     1, 0, 0},
+    {"mng_get_jpeg_progressive",   1, 0, 0},
+    {"mng_get_jpeg_quality",       1, 0, 0},
+    {"mng_get_jpeg_smoothing",     1, 0, 0},
+#endif
+    {"mng_get_lastbackchunk",      1, 0, 3},
+    {"mng_get_lastseekname",       1, 0, 5},
+    {"mng_get_layercount",         1, 0, 0},
+#ifndef MNG_SKIP_MAXCANVAS
+    {"mng_get_maxcanvasheight",    1, 0, 0},
+    {"mng_get_maxcanvaswidth",     1, 0, 0},
+#endif
+    {"mng_get_playtime",           1, 0, 0},
+    {"mng_get_refreshpass",        1, 0, 0},
+    {"mng_get_runtime",            1, 0, 0},
+    {"mng_get_sectionbreaks",      1, 0, 0},
+    {"mng_get_sigtype",            1, 0, 0},
+    {"mng_get_simplicity",         1, 0, 0},
+    {"mng_get_speed",              1, 0, 0},
+    {"mng_get_srgb",               1, 0, 0},
+    {"mng_get_starttime",          1, 0, 0},
+    {"mng_get_storechunks",        1, 0, 0},
+    {"mng_get_suspensionmode",     1, 0, 0},
+    {"mng_get_ticks",              1, 0, 0},
+#ifndef MNG_NO_CURRENT_INFO
+    {"mng_get_totalframes",        1, 0, 5},
+    {"mng_get_totallayers",        1, 0, 5},
+    {"mng_get_totalplaytime",      1, 0, 5},
+#endif
+    {"mng_get_usebkgd",            1, 0, 0},
+    {"mng_get_userdata",           1, 0, 0},
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+    {"mng_get_viewgamma",          1, 0, 0},
+    {"mng_get_viewgammaint",       1, 0, 0},
+#endif
+#ifdef MNG_ACCESS_ZLIB
+    {"mng_get_zlib_level",         1, 0, 0},
+    {"mng_get_zlib_maxidat",       1, 0, 0},
+    {"mng_get_zlib_memlevel",      1, 0, 0},
+    {"mng_get_zlib_method",        1, 0, 0},
+    {"mng_get_zlib_strategy",      1, 0, 0},
+    {"mng_get_zlib_windowbits",    1, 0, 0},
+#endif
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+    {"mng_getcb_closestream",      1, 0, 0},
+#endif
+    {"mng_getcb_errorproc",        1, 0, 0},
+    {"mng_getcb_getalphaline",     1, 0, 0},
+    {"mng_getcb_getbkgdline",      1, 0, 0},
+    {"mng_getcb_getcanvasline",    1, 0, 0},
+    {"mng_getcb_gettickcount",     1, 0, 0},
+    {"mng_getcb_memalloc",         1, 0, 0},
+    {"mng_getcb_memfree",          1, 0, 0},
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+    {"mng_getcb_openstream",       1, 0, 0},
+#endif
+    {"mng_getcb_processarow",      1, 0, 0},
+    {"mng_getcb_processchroma",    1, 0, 0},
+    {"mng_getcb_processgamma",     1, 0, 0},
+    {"mng_getcb_processheader",    1, 0, 0},
+    {"mng_getcb_processiccp",      1, 0, 0},
+    {"mng_getcb_processmend",      1, 0, 1},
+    {"mng_getcb_processneed",      1, 0, 0},
+    {"mng_getcb_processsave",      1, 0, 0},
+    {"mng_getcb_processseek",      1, 0, 0},
+    {"mng_getcb_processsrgb",      1, 0, 0},
+    {"mng_getcb_processterm",      1, 0, 2},
+    {"mng_getcb_processtext",      1, 0, 0},
+    {"mng_getcb_processunknown",   1, 0, 0},
+    {"mng_getcb_readdata",         1, 0, 0},
+    {"mng_getcb_refresh",          1, 0, 0},
+    {"mng_getcb_releasedata",      1, 0, 8},
+    {"mng_getcb_settimer",         1, 0, 0},
+    {"mng_getcb_traceproc",        1, 0, 0},
+    {"mng_getcb_writedata",        1, 0, 0},
+    {"mng_getchunk_back",          1, 0, 0},
+    {"mng_getchunk_basi",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_bKGD
+    {"mng_getchunk_bkgd",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_cHRM
+    {"mng_getchunk_chrm",          1, 0, 0},
+#endif
+    {"mng_getchunk_clip",          1, 0, 0},
+    {"mng_getchunk_clon",          1, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_dBYK
+    {"mng_getchunk_dbyk",          1, 0, 0},
+#endif
+#endif
+    {"mng_getchunk_defi",          1, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+    {"mng_getchunk_dhdr",          1, 0, 0},
+#endif
+    {"mng_getchunk_disc",          1, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+    {"mng_getchunk_drop",          1, 0, 0},
+#endif
+    {"mng_getchunk_endl",          1, 0, 0},
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {"mng_getchunk_mpng",          1, 0, 10},
+    {"mng_getchunk_mpng_frame",    1, 0, 10},
+#endif
+#ifndef MNG_SKIPCHUNK_evNT
+    {"mng_getchunk_evnt",          1, 0, 5},
+    {"mng_getchunk_evnt_entry",    1, 0, 5},
+#endif
+#ifndef MNG_SKIPCHUNK_eXPI
+    {"mng_getchunk_expi",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_fPRI
+    {"mng_getchunk_fpri",          1, 0, 0},
+#endif
+    {"mng_getchunk_fram",          1, 0, 0},
+    {"mng_getchunk_gama",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_hIST
+    {"mng_getchunk_hist",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_iCCP
+    {"mng_getchunk_iccp",          1, 0, 0},
+#endif
+    {"mng_getchunk_idat",          1, 0, 0},
+    {"mng_getchunk_iend",          1, 0, 0},
+    {"mng_getchunk_ihdr",          1, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+    {"mng_getchunk_ijng",          1, 0, 0},
+#endif
+    {"mng_getchunk_ipng",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_iTXt
+    {"mng_getchunk_itxt",          1, 0, 0},
+#endif
+#ifdef MNG_INCLUDE_JNG
+    {"mng_getchunk_jdaa",          1, 0, 0},
+    {"mng_getchunk_jdat",          1, 0, 0},
+    {"mng_getchunk_jhdr",          1, 0, 0},
+    {"mng_getchunk_jsep",          1, 0, 0},
+#endif
+    {"mng_getchunk_loop",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_MAGN
+    {"mng_getchunk_magn",          1, 0, 0},
+#endif
+    {"mng_getchunk_mend",          1, 0, 0},
+    {"mng_getchunk_mhdr",          1, 0, 0},
+    {"mng_getchunk_move",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_nEED
+    {"mng_getchunk_need",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_ORDR
+#ifndef MNG_NO_DELTA_PNG
+    {"mng_getchunk_ordr",          1, 0, 0},
+    {"mng_getchunk_ordr_entry",    1, 0, 0},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+    {"mng_getchunk_past",          1, 0, 0},
+    {"mng_getchunk_past_src",      1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYg
+    {"mng_getchunk_phyg",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYs
+    {"mng_getchunk_phys",          1, 0, 0},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {"mng_getchunk_plte",          1, 0, 0},
+    {"mng_getchunk_pplt",          1, 0, 0},
+    {"mng_getchunk_pplt_entry",    1, 0, 0},
+    {"mng_getchunk_prom",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+    {"mng_getchunk_save",          1, 0, 0},
+    {"mng_getchunk_save_entry",    1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_sBIT
+    {"mng_getchunk_sbit",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+    {"mng_getchunk_seek",          1, 0, 0},
+#endif
+    {"mng_getchunk_show",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_sPLT
+    {"mng_getchunk_splt",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_sRGB
+    {"mng_getchunk_srgb",          1, 0, 0},
+#endif
+    {"mng_getchunk_term",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_tEXt
+    {"mng_getchunk_text",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_tIME
+    {"mng_getchunk_time",          1, 0, 0},
+#endif
+    {"mng_getchunk_trns",          1, 0, 0},
+    {"mng_getchunk_unkown",        1, 0, 0},
+#ifndef MNG_SKIPCHUNK_zTXt
+    {"mng_getchunk_ztxt",          1, 0, 0},
+#endif
+    {"mng_getimgdata_chunk",       0, 0, 0},
+    {"mng_getimgdata_chunkseq",    0, 0, 0},
+    {"mng_getimgdata_seq",         0, 0, 0},
+    {"mng_getlasterror",           1, 0, 0},
+    {"mng_initialize",             1, 0, 0},
+    {"mng_iterate_chunks",         1, 0, 0},
+    {"mng_putchunk_back",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_BASI
+    {"mng_putchunk_basi",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+    {"mng_putchunk_bkgd",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_cHRM
+    {"mng_putchunk_chrm",          1, 0, 0},
+#endif
+    {"mng_putchunk_clip",          1, 0, 0},
+    {"mng_putchunk_clon",          1, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+    {"mng_putchunk_dbyk",          1, 0, 0},
+#endif
+#endif
+    {"mng_putchunk_defi",          1, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+    {"mng_putchunk_dhdr",          1, 0, 0},
+#endif
+    {"mng_putchunk_disc",          1, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+    {"mng_putchunk_drop",          1, 0, 0},
+#endif
+    {"mng_putchunk_endl",          1, 0, 0},
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {"mng_putchunk_mpng",          1, 0, 10},
+    {"mng_putchunk_mpng_frame",    1, 0, 10},
+#endif
+#ifndef MNG_SKIPCHUNK_evNT
+    {"mng_putchunk_evnt",          1, 0, 5},
+    {"mng_putchunk_evnt_entry",    1, 0, 5},
+#endif
+#ifndef MNG_SKIPCHUNK_eXPI
+    {"mng_putchunk_expi",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_fPRI
+    {"mng_putchunk_fpri",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_FRAM
+    {"mng_putchunk_fram",          1, 0, 0},
+#endif
+    {"mng_putchunk_gama",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_hIST
+    {"mng_putchunk_hist",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_iCCP
+    {"mng_putchunk_iccp",          1, 0, 0},
+#endif
+    {"mng_putchunk_idat",          1, 0, 0},
+    {"mng_putchunk_iend",          1, 0, 0},
+    {"mng_putchunk_ihdr",          1, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+    {"mng_putchunk_ijng",          1, 0, 0},
+#endif
+    {"mng_putchunk_ipng",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_iTXt
+    {"mng_putchunk_itxt",          1, 0, 0},
+#endif
+#ifdef MNG_INCLUDE_JNG
+    {"mng_putchunk_jdaa",          1, 0, 0},
+    {"mng_putchunk_jdat",          1, 0, 0},
+    {"mng_putchunk_jhdr",          1, 0, 0},
+    {"mng_putchunk_jsep",          1, 0, 0},
+#endif
+    {"mng_putchunk_loop",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_MAGN
+    {"mng_putchunk_magn",          1, 0, 0},
+#endif
+    {"mng_putchunk_mend",          1, 0, 0},
+    {"mng_putchunk_mhdr",          1, 0, 0},
+    {"mng_putchunk_move",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_nEED
+    {"mng_putchunk_need",          1, 0, 0},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+    {"mng_putchunk_ordr",          1, 0, 0},
+    {"mng_putchunk_ordr_entry",    1, 0, 0},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+    {"mng_putchunk_past",          1, 0, 0},
+    {"mng_putchunk_past_src",      1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYg
+    {"mng_putchunk_phyg",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYs
+    {"mng_putchunk_phys",          1, 0, 0},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {"mng_putchunk_plte",          1, 0, 0},
+    {"mng_putchunk_pplt",          1, 0, 0},
+    {"mng_putchunk_pplt_entry",    1, 0, 0},
+    {"mng_putchunk_prom",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+    {"mng_putchunk_save",          1, 0, 0},
+    {"mng_putchunk_save_entry",    1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_sBIT
+    {"mng_putchunk_sbit",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+    {"mng_putchunk_seek",          1, 0, 0},
+#endif
+    {"mng_putchunk_show",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_sPLT
+    {"mng_putchunk_splt",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_sRGB
+    {"mng_putchunk_srgb",          1, 0, 0},
+#endif
+    {"mng_putchunk_term",          1, 0, 0},
+#ifndef MNG_SKIPCHUNK_tEXt
+    {"mng_putchunk_text",          1, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_tIME
+    {"mng_putchunk_time",          1, 0, 0},
+#endif
+    {"mng_putchunk_trns",          1, 0, 0},
+    {"mng_putchunk_unkown",        1, 0, 0},
+#ifndef MNG_SKIPCHUNK_zTXt
+    {"mng_putchunk_ztxt",          1, 0, 0},
+#endif
+    {"mng_putimgdata_ihdr",        0, 0, 0},
+    {"mng_putimgdata_jhdr",        0, 0, 0},
+    {"mng_reset",                  1, 0, 0},
+    {"mng_read",                   1, 0, 0},
+    {"mng_read_pushchunk",         1, 0, 8},
+    {"mng_read_pushdata",          1, 0, 8},
+    {"mng_read_pushsig",           1, 0, 8},
+    {"mng_read_resume",            1, 0, 0},
+    {"mng_readdisplay",            1, 0, 0},
+    {"mng_set_bgcolor",            1, 0, 0},
+    {"mng_set_bkgdstyle",          1, 0, 0},
+    {"mng_set_cacheplayback",      1, 0, 2},
+    {"mng_set_canvasstyle",        1, 0, 0},
+    {"mng_set_dfltimggamma",       1, 0, 0},
+#ifndef MNG_NO_DFLT_INFO
+    {"mng_set_dfltimggammaint",    1, 0, 0},
+#endif
+    {"mng_set_displaygamma",       1, 0, 0},
+    {"mng_set_displaygammaint",    1, 0, 0},
+    {"mng_set_doprogressive",      1, 0, 2},
+#ifdef MNG_ACCESS_JPEG
+    {"mng_set_jpeg_dctmethod",     1, 0, 0},
+    {"mng_set_jpeg_maxjdat",       1, 0, 0},
+    {"mng_set_jpeg_optimized",     1, 0, 0},
+    {"mng_set_jpeg_progressive",   1, 0, 0},
+    {"mng_set_jpeg_quality",       1, 0, 0},
+    {"mng_set_jpeg_smoothing",     1, 0, 0},
+#endif
+#ifndef MNG_SKIP_MAXCANVAS
+    {"mng_set_maxcanvasheight",    1, 0, 0},
+    {"mng_set_maxcanvassize",      1, 0, 0},
+    {"mng_set_maxcanvaswidth",     1, 0, 0},
+#endif
+    {"mng_set_outputprofile",      1, 0, 0},
+    {"mng_set_outputprofile2",     1, 0, 0},
+    {"mng_set_outputsrgb",         1, 0, 1},
+    {"mng_set_sectionbreaks",      1, 0, 0},
+    {"mng_set_speed",              1, 0, 0},
+    {"mng_set_srgb",               1, 0, 0},
+    {"mng_set_srgbimplicit",       1, 0, 1},
+    {"mng_set_srgbprofile",        1, 0, 0},
+    {"mng_set_srgbprofile2",       1, 0, 0},
+    {"mng_set_storechunks",        1, 0, 0},
+    {"mng_set_suspensionmode",     1, 0, 0},
+    {"mng_set_usebkgd",            1, 0, 0},
+    {"mng_set_userdata",           1, 0, 0},
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+    {"mng_set_viewgamma",          1, 0, 0},
+    {"mng_set_viewgammaint",       1, 0, 0},
+#endif
+#ifdef MNG_ACCESS_ZLIB
+    {"mng_set_zlib_level",         1, 0, 0},
+    {"mng_set_zlib_maxidat",       1, 0, 0},
+    {"mng_set_zlib_memlevel",      1, 0, 0},
+    {"mng_set_zlib_method",        1, 0, 0},
+    {"mng_set_zlib_strategy",      1, 0, 0},
+    {"mng_set_zlib_windowbits",    1, 0, 0},
+#endif
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+    {"mng_setcb_closestream",      1, 0, 0},
+#endif
+    {"mng_setcb_errorproc",        1, 0, 0},
+    {"mng_setcb_getalphaline",     1, 0, 0},
+    {"mng_setcb_getbkgdline",      1, 0, 0},
+    {"mng_setcb_getcanvasline",    1, 0, 0},
+    {"mng_setcb_gettickcount",     1, 0, 0},
+    {"mng_setcb_memalloc",         1, 0, 0},
+    {"mng_setcb_memfree",          1, 0, 0},
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+    {"mng_setcb_openstream",       1, 0, 0},
+#endif
+    {"mng_setcb_processarow",      1, 0, 0},
+    {"mng_setcb_processchroma",    1, 0, 0},
+    {"mng_setcb_processgamma",     1, 0, 0},
+    {"mng_setcb_processheader",    1, 0, 0},
+    {"mng_setcb_processiccp",      1, 0, 0},
+    {"mng_setcb_processmend",      1, 0, 1},
+    {"mng_setcb_processneed",      1, 0, 0},
+    {"mng_setcb_processsave",      1, 0, 0},
+    {"mng_setcb_processseek",      1, 0, 0},
+    {"mng_setcb_processsrgb",      1, 0, 0},
+    {"mng_setcb_processterm",      1, 0, 2},
+    {"mng_setcb_processtext",      1, 0, 0},
+    {"mng_setcb_processunknown",   1, 0, 0},
+    {"mng_setcb_readdata",         1, 0, 0},
+    {"mng_setcb_refresh",          1, 0, 0},
+    {"mng_setcb_releasedata",      1, 0, 8},
+    {"mng_setcb_settimer",         1, 0, 0},
+    {"mng_setcb_traceproc",        1, 0, 0},
+    {"mng_setcb_writedata",        1, 0, 0},
+    {"mng_status_creating",        1, 0, 0},
+    {"mng_status_displaying",      1, 0, 0},
+    {"mng_status_dynamic",         1, 0, 5},
+    {"mng_status_error",           1, 0, 0},
+    {"mng_status_reading",         1, 0, 0},
+    {"mng_status_running",         1, 0, 0},
+    {"mng_status_runningevent",    1, 0, 5},
+    {"mng_status_suspendbreak",    1, 0, 0},
+    {"mng_status_timerbreak",      1, 0, 0},
+    {"mng_status_writing",         1, 0, 0},
+    {"mng_supports_func",          1, 0, 5},
+    {"mng_trapevent",              1, 0, 5},
+    {"mng_updatemngheader",        1, 0, 0},
+    {"mng_updatemngsimplicity",    1, 0, 0},
+    {"mng_version_beta",           1, 0, 5},
+    {"mng_version_dll",            1, 0, 0},
+    {"mng_version_major",          1, 0, 0},
+    {"mng_version_minor",          1, 0, 0},
+    {"mng_version_release",        1, 0, 0},
+    {"mng_version_so",             1, 0, 0},
+    {"mng_version_text",           1, 0, 0},
+    {"mng_write",                  1, 0, 0},
+  };
+
+mng_bool MNG_DECL mng_supports_func (mng_pchar  zFunction,
+                                     mng_uint8* iMajor,
+                                     mng_uint8* iMinor,
+                                     mng_uint8* iRelease)
+{
+  mng_int32       iTop, iLower, iUpper, iMiddle;
+  mng_func_entryp pEntry;          /* pointer to found entry */
+                                   /* determine max index of table */
+  iTop = (sizeof (func_table) / sizeof (func_table [0])) - 1;
+
+  iLower  = 0;                     /* initialize binary search */
+  iMiddle = iTop >> 1;             /* start in the middle */
+  iUpper  = iTop;
+  pEntry  = 0;                     /* no goods yet! */
+
+  do                               /* the binary search itself */
+    {
+      mng_int32 iRslt = strcmp(func_table [iMiddle].zFunction, zFunction);
+      if (iRslt < 0)
+        iLower = iMiddle + 1;
+      else if (iRslt > 0)
+        iUpper = iMiddle - 1;
+      else
+      {
+        pEntry = &func_table [iMiddle];
+        break;
+      };
+
+      iMiddle = (iLower + iUpper) >> 1;
+    }
+  while (iLower <= iUpper);
+
+  if (pEntry)                      /* found it ? */
+  {
+    *iMajor   = pEntry->iMajor;
+    *iMinor   = pEntry->iMinor;
+    *iRelease = pEntry->iRelease;
+    return MNG_TRUE;
+  }
+  else
+  {
+    *iMajor   = 0;
+    *iMinor   = 0;
+    *iRelease = 0;
+    return MNG_FALSE;
+  }
+}
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * HLAPI routines                                                         * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_handle MNG_DECL mng_initialize (mng_ptr       pUserdata,
+                                    mng_memalloc  fMemalloc,
+                                    mng_memfree   fMemfree,
+                                    mng_traceproc fTraceproc)
+{
+  mng_datap   pData;
+#ifdef MNG_SUPPORT_DISPLAY
+  mng_retcode iRetcode;
+  mng_imagep  pImage;
+#endif
+
+#ifdef MNG_INTERNAL_MEMMNGMT           /* allocate the main datastruc */
+  pData = (mng_datap)calloc (1, sizeof (mng_data));
+#else
+  pData = (mng_datap)fMemalloc (sizeof (mng_data));
+#endif
+
+  if (!pData)
+    return MNG_NULL;                   /* error: out of memory?? */
+                                       /* validate the structure */
+  pData->iMagic                = MNG_MAGIC;
+                                       /* save userdata field */
+  pData->pUserdata             = pUserdata;
+                                       /* remember trace callback */
+  pData->fTraceproc            = fTraceproc;
+
+#ifdef MNG_SUPPORT_TRACE
+  if (mng_trace (pData, MNG_FN_INITIALIZE, MNG_LC_INITIALIZE))
+  {
+    MNG_FREEX (pData, pData, sizeof (mng_data));
+    return MNG_NULL;
+  }
+#endif
+                                       /* default canvas styles are 8-bit RGB */
+  pData->iCanvasstyle          = MNG_CANVAS_RGB8;
+  pData->iBkgdstyle            = MNG_CANVAS_RGB8;
+
+  pData->iBGred                = 0;  /* black */
+  pData->iBGgreen              = 0;
+  pData->iBGblue               = 0;
+
+  pData->bUseBKGD              = MNG_TRUE;
+
+#ifdef MNG_FULL_CMS
+  pData->bIssRGB               = MNG_TRUE;
+  pData->hProf1                = 0;    /* no profiles yet */
+  pData->hProf2                = 0;
+  pData->hProf3                = 0;
+  pData->hTrans                = 0;
+#endif
+
+  pData->dViewgamma            = 1.0;
+  pData->dDisplaygamma         = 2.2;
+  pData->dDfltimggamma         = 0.45455;
+                                       /* initially remember chunks */
+  pData->bStorechunks          = MNG_TRUE;
+                                       /* no breaks at section-borders */
+  pData->bSectionbreaks        = MNG_FALSE;
+                                       /* initially cache playback info */
+  pData->bCacheplayback        = MNG_TRUE;
+                                       /* progressive refresh for large images */
+  pData->bDoProgressive        = MNG_TRUE;
+                                       /* crc exists; should check; error for
+                                          critical chunks; warning for ancillery;
+                                          generate crc for output */
+  pData->iCrcmode              = MNG_CRC_DEFAULT;
+                                       /* normal animation-speed ! */
+  pData->iSpeed                = mng_st_normal;
+                                       /* initial image limits */
+  pData->iMaxwidth             = 10000;
+  pData->iMaxheight            = 10000;
+
+#ifdef MNG_INTERNAL_MEMMNGMT           /* internal management */
+  pData->fMemalloc             = MNG_NULL;
+  pData->fMemfree              = MNG_NULL;
+#else                                  /* keep callbacks */
+  pData->fMemalloc             = fMemalloc;
+  pData->fMemfree              = fMemfree;
+#endif
+                                       /* no value (yet) */
+  pData->fReleasedata          = MNG_NULL;    
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+  pData->fOpenstream           = MNG_NULL;
+  pData->fClosestream          = MNG_NULL;
+#endif
+  pData->fReaddata             = MNG_NULL;
+  pData->fWritedata            = MNG_NULL;
+  pData->fErrorproc            = MNG_NULL;
+  pData->fProcessheader        = MNG_NULL;
+  pData->fProcesstext          = MNG_NULL;
+  pData->fProcesssave          = MNG_NULL;
+  pData->fProcessseek          = MNG_NULL;
+  pData->fProcessneed          = MNG_NULL;
+  pData->fProcessmend          = MNG_NULL;
+  pData->fProcessunknown       = MNG_NULL;
+  pData->fProcessterm          = MNG_NULL;
+  pData->fGetcanvasline        = MNG_NULL;
+  pData->fGetbkgdline          = MNG_NULL;
+  pData->fGetalphaline         = MNG_NULL;
+  pData->fRefresh              = MNG_NULL;
+  pData->fGettickcount         = MNG_NULL;
+  pData->fSettimer             = MNG_NULL;
+  pData->fProcessgamma         = MNG_NULL;
+  pData->fProcesschroma        = MNG_NULL;
+  pData->fProcesssrgb          = MNG_NULL;
+  pData->fProcessiccp          = MNG_NULL;
+  pData->fProcessarow          = MNG_NULL;
+
+#if defined(MNG_SUPPORT_DISPLAY) && (defined(MNG_GAMMA_ONLY) || defined(MNG_FULL_CMS))
+  pData->dLastgamma            = 0;    /* lookup table needs first-time calc */
+#endif
+
+#ifdef MNG_SUPPORT_DISPLAY             /* create object 0 */
+  iRetcode = mng_create_imageobject (pData, 0, MNG_TRUE, MNG_TRUE, MNG_TRUE,
+                                     0, 0, 0, 0, 0, 0, 0, 0, 0, MNG_FALSE,
+                                     0, 0, 0, 0, &pImage);
+
+  if (iRetcode)                        /* on error drop out */
+  {
+    MNG_FREEX (pData, pData, sizeof (mng_data));
+    return MNG_NULL;
+  }
+
+  pData->pObjzero = pImage;
+#endif
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_INCLUDE_LCMS)
+  mnglcms_initlibrary ();              /* init lcms particulars */
+#endif
+
+#ifdef MNG_SUPPORT_READ
+  pData->bSuspensionmode       = MNG_FALSE;
+  pData->iSuspendbufsize       = 0;
+  pData->pSuspendbuf           = MNG_NULL;
+  pData->pSuspendbufnext       = MNG_NULL;
+  pData->iSuspendbufleft       = 0;
+  pData->iChunklen             = 0;
+  pData->pReadbufnext          = MNG_NULL;
+  pData->pLargebufnext         = MNG_NULL;
+
+  pData->pFirstpushchunk       = MNG_NULL;
+  pData->pLastpushchunk        = MNG_NULL;
+  pData->pFirstpushdata        = MNG_NULL;
+  pData->pLastpushdata         = MNG_NULL;
+#endif
+
+#ifdef MNG_INCLUDE_ZLIB
+  mngzlib_initialize (pData);          /* initialize zlib structures and such */
+                                       /* default zlib compression parameters */
+  pData->iZlevel               = MNG_ZLIB_LEVEL;
+  pData->iZmethod              = MNG_ZLIB_METHOD;
+  pData->iZwindowbits          = MNG_ZLIB_WINDOWBITS;
+  pData->iZmemlevel            = MNG_ZLIB_MEMLEVEL;
+  pData->iZstrategy            = MNG_ZLIB_STRATEGY;
+                                       /* default maximum IDAT data size */
+  pData->iMaxIDAT              = MNG_MAX_IDAT_SIZE;
+#endif
+
+#ifdef MNG_INCLUDE_JNG                 /* default IJG compression parameters */
+  pData->eJPEGdctmethod        = MNG_JPEG_DCT;
+  pData->iJPEGquality          = MNG_JPEG_QUALITY;
+  pData->iJPEGsmoothing        = MNG_JPEG_SMOOTHING;
+  pData->bJPEGcompressprogr    = MNG_JPEG_PROGRESSIVE;
+  pData->bJPEGcompressopt      = MNG_JPEG_OPTIMIZED;
+                                       /* default maximum JDAT data size */
+  pData->iMaxJDAT              = MNG_MAX_JDAT_SIZE;
+#endif
+
+  mng_reset ((mng_handle)pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  if (mng_trace (pData, MNG_FN_INITIALIZE, MNG_LC_END))
+  {
+    MNG_FREEX (pData, pData, sizeof (mng_data));
+    return MNG_NULL;
+  }
+#endif
+
+  return (mng_handle)pData;            /* if we get here, we're in business */
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_reset (mng_handle hHandle)
+{
+  mng_datap pData;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_RESET, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)(hHandle));      /* address main structure */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_SKIPCHUNK_SAVE
+  mng_drop_savedata (pData);           /* cleanup saved-data from SAVE/SEEK */
+#endif
+#endif
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_FULL_CMS)
+  mng_clear_cms (pData);               /* cleanup left-over cms stuff if any */
+#endif
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_INCLUDE_JNG)
+  mngjpeg_cleanup (pData);             /* cleanup jpeg stuff */
+#endif
+
+#ifdef MNG_INCLUDE_ZLIB
+  if (pData->bInflating)               /* if we've been inflating */
+  {
+#ifdef MNG_INCLUDE_DISPLAY_PROCS
+    mng_cleanup_rowproc (pData);       /* cleanup row-processing, */
+#endif
+    mngzlib_inflatefree (pData);       /* cleanup inflate! */
+  }
+#endif /* MNG_INCLUDE_ZLIB */
+
+#ifdef MNG_SUPPORT_READ
+  if ((pData->bReading) && (!pData->bEOF))
+    mng_process_eof (pData);           /* cleanup app streaming */
+                                       /* cleanup default read buffers */
+  MNG_FREE (pData, pData->pReadbuf,    pData->iReadbufsize);
+  MNG_FREE (pData, pData->pLargebuf,   pData->iLargebufsize);
+  MNG_FREE (pData, pData->pSuspendbuf, pData->iSuspendbufsize);
+
+  while (pData->pFirstpushdata)        /* release any pushed data & chunks */
+    mng_release_pushdata (pData);
+  while (pData->pFirstpushchunk)
+    mng_release_pushchunk (pData);
+#endif
+
+#ifdef MNG_SUPPORT_WRITE               /* cleanup default write buffer */
+  MNG_FREE (pData, pData->pWritebuf, pData->iWritebufsize);
+#endif
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+  mng_drop_chunks  (pData);            /* drop stored chunks (if any) */
+#endif
+
+#ifdef MNG_SUPPORT_DISPLAY
+  mng_drop_objects (pData, MNG_TRUE);  /* drop stored objects (if any) */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+  if (pData->iGlobalProfilesize)       /* drop global profile (if any) */
+    MNG_FREEX (pData, pData->pGlobalProfile, pData->iGlobalProfilesize);
+#endif
+#endif
+
+  pData->eSigtype              = mng_it_unknown;
+  pData->eImagetype            = mng_it_unknown;
+  pData->iWidth                = 0;    /* these are unknown yet */
+  pData->iHeight               = 0;
+  pData->iTicks                = 0;
+  pData->iLayercount           = 0;
+  pData->iFramecount           = 0;
+  pData->iPlaytime             = 0;
+  pData->iSimplicity           = 0;
+  pData->iAlphadepth           = 16;   /* assume the worst! */
+
+  pData->iImagelevel           = 0;    /* no image encountered */
+
+  pData->iMagnify              = 0;    /* 1-to-1 display */
+  pData->iOffsetx              = 0;    /* no offsets */
+  pData->iOffsety              = 0;
+  pData->iCanvaswidth          = 0;    /* let the app decide during processheader */
+  pData->iCanvasheight         = 0;
+                                       /* so far, so good */
+  pData->iErrorcode            = MNG_NOERROR;
+  pData->iSeverity             = 0;
+  pData->iErrorx1              = 0;
+  pData->iErrorx2              = 0;
+  pData->zErrortext            = MNG_NULL;
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+                                       /* let's assume the best scenario */
+#ifndef MNG_NO_OLD_VERSIONS
+  pData->bPreDraft48           = MNG_FALSE;
+#endif
+                                       /* the unknown chunk */
+  pData->iChunkname            = MNG_UINT_HUH;
+  pData->iChunkseq             = 0;
+  pData->pFirstchunk           = MNG_NULL;
+  pData->pLastchunk            = MNG_NULL;
+                                       /* nothing processed yet */
+  pData->bHasheader            = MNG_FALSE;
+  pData->bHasMHDR              = MNG_FALSE;
+  pData->bHasIHDR              = MNG_FALSE;
+  pData->bHasBASI              = MNG_FALSE;
+  pData->bHasDHDR              = MNG_FALSE;
+#ifdef MNG_INCLUDE_JNG
+  pData->bHasJHDR              = MNG_FALSE;
+  pData->bHasJSEP              = MNG_FALSE;
+  pData->bHasJDAA              = MNG_FALSE;
+  pData->bHasJDAT              = MNG_FALSE;
+#endif
+  pData->bHasPLTE              = MNG_FALSE;
+  pData->bHasTRNS              = MNG_FALSE;
+  pData->bHasGAMA              = MNG_FALSE;
+  pData->bHasCHRM              = MNG_FALSE;
+  pData->bHasSRGB              = MNG_FALSE;
+  pData->bHasICCP              = MNG_FALSE;
+  pData->bHasBKGD              = MNG_FALSE;
+  pData->bHasIDAT              = MNG_FALSE;
+
+  pData->bHasSAVE              = MNG_FALSE;
+  pData->bHasBACK              = MNG_FALSE;
+  pData->bHasFRAM              = MNG_FALSE;
+  pData->bHasTERM              = MNG_FALSE;
+  pData->bHasLOOP              = MNG_FALSE;
+                                       /* there's no global stuff yet either */
+  pData->bHasglobalPLTE        = MNG_FALSE;
+  pData->bHasglobalTRNS        = MNG_FALSE;
+  pData->bHasglobalGAMA        = MNG_FALSE;
+  pData->bHasglobalCHRM        = MNG_FALSE;
+  pData->bHasglobalSRGB        = MNG_FALSE;
+  pData->bHasglobalICCP        = MNG_FALSE;
+
+  pData->iDatawidth            = 0;    /* no IHDR/BASI/DHDR done yet */
+  pData->iDataheight           = 0;
+  pData->iBitdepth             = 0;
+  pData->iColortype            = 0;
+  pData->iCompression          = 0;
+  pData->iFilter               = 0;
+  pData->iInterlace            = 0;
+
+#ifdef MNG_INCLUDE_JNG
+  pData->iJHDRcolortype        = 0;    /* no JHDR data */
+  pData->iJHDRimgbitdepth      = 0;
+  pData->iJHDRimgcompression   = 0;
+  pData->iJHDRimginterlace     = 0;
+  pData->iJHDRalphabitdepth    = 0;
+  pData->iJHDRalphacompression = 0;
+  pData->iJHDRalphafilter      = 0;
+  pData->iJHDRalphainterlace   = 0;
+#endif
+
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+#ifdef MNG_SUPPORT_READ                /* no reading done */
+  pData->bReading              = MNG_FALSE;
+  pData->bHavesig              = MNG_FALSE;
+  pData->bEOF                  = MNG_FALSE;
+  pData->iReadbufsize          = 0;
+  pData->pReadbuf              = MNG_NULL;
+
+  pData->iLargebufsize         = 0;
+  pData->pLargebuf             = MNG_NULL;
+
+  pData->iSuspendtime          = 0;
+  pData->bSuspended            = MNG_FALSE;
+  pData->iSuspendpoint         = 0;
+
+  pData->pSuspendbufnext       = pData->pSuspendbuf;
+  pData->iSuspendbufleft       = 0;
+#endif /* MNG_SUPPORT_READ */
+
+#ifdef MNG_SUPPORT_WRITE               /* no creating/writing done */
+  pData->bCreating             = MNG_FALSE;
+  pData->bWriting              = MNG_FALSE;
+  pData->iFirstchunkadded      = 0;
+  pData->iWritebufsize         = 0;
+  pData->pWritebuf             = MNG_NULL;
+#endif /* MNG_SUPPORT_WRITE */
+
+#ifdef MNG_SUPPORT_DISPLAY             /* done nuttin' yet */
+  pData->bDisplaying           = MNG_FALSE;
+  pData->iFrameseq             = 0;
+  pData->iLayerseq             = 0;
+  pData->iFrametime            = 0;
+
+  pData->iTotallayers          = 0;
+  pData->iTotalframes          = 0;
+  pData->iTotalplaytime        = 0;
+
+  pData->bSkipping             = MNG_FALSE;
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+  pData->bDynamic              = MNG_FALSE;
+  pData->bRunningevent         = MNG_FALSE;
+  pData->bStopafterseek        = MNG_FALSE;
+  pData->iEventx               = 0;
+  pData->iEventy               = 0;
+  pData->pLastmousemove        = MNG_NULL;
+#endif
+
+  pData->iRequestframe         = 0;
+  pData->iRequestlayer         = 0;
+  pData->iRequesttime          = 0;
+  pData->bSearching            = MNG_FALSE;
+
+  pData->bRestorebkgd          = MNG_FALSE;
+
+  pData->iRuntime              = 0;
+  pData->iSynctime             = 0;
+  pData->iStarttime            = 0;
+  pData->iEndtime              = 0;
+  pData->bRunning              = MNG_FALSE;
+  pData->bTimerset             = MNG_FALSE;
+  pData->iBreakpoint           = 0;
+  pData->bSectionwait          = MNG_FALSE;
+  pData->bFreezing             = MNG_FALSE;
+  pData->bResetting            = MNG_FALSE;
+  pData->bNeedrefresh          = MNG_FALSE;
+  pData->bMisplacedTERM        = MNG_FALSE;
+  pData->bOnlyfirstframe       = MNG_FALSE;
+  pData->iFramesafterTERM      = 0;
+                                       /* these don't exist yet */
+  pData->pCurrentobj           = MNG_NULL;
+  pData->pCurraniobj           = MNG_NULL;
+  pData->pTermaniobj           = MNG_NULL;
+  pData->pLastclone            = MNG_NULL;
+  pData->pStoreobj             = MNG_NULL;
+  pData->pStorebuf             = MNG_NULL;
+  pData->pRetrieveobj          = MNG_NULL;
+                                       /* no saved data ! */
+  pData->pSavedata             = MNG_NULL;
+
+  pData->iUpdateleft           = 0;    /* no region updated yet */
+  pData->iUpdateright          = 0;
+  pData->iUpdatetop            = 0;
+  pData->iUpdatebottom         = 0;
+
+  pData->iPass                 = -1;   /* interlacing stuff and temp buffers */
+  pData->iRow                  = 0;
+  pData->iRowinc               = 1;
+  pData->iCol                  = 0;
+  pData->iColinc               = 1;
+  pData->iRowsamples           = 0;
+  pData->iSamplemul            = 0;
+  pData->iSampleofs            = 0;
+  pData->iSamplediv            = 0;
+  pData->iRowsize              = 0;
+  pData->iRowmax               = 0;
+  pData->iFilterofs            = 0;
+  pData->iPixelofs             = 1;
+  pData->iLevel0               = 0;
+  pData->iLevel1               = 0;
+  pData->iLevel2               = 0;
+  pData->iLevel3               = 0;
+  pData->pWorkrow              = MNG_NULL;
+  pData->pPrevrow              = MNG_NULL;
+  pData->pRGBArow              = MNG_NULL;
+  pData->bIsRGBA16             = MNG_TRUE;
+  pData->bIsOpaque             = MNG_TRUE;
+  pData->iFilterbpp            = 1;
+
+  pData->iSourcel              = 0;    /* always initialized just before */
+  pData->iSourcer              = 0;    /* compositing the next layer */
+  pData->iSourcet              = 0;
+  pData->iSourceb              = 0;
+  pData->iDestl                = 0;
+  pData->iDestr                = 0;
+  pData->iDestt                = 0;
+  pData->iDestb                = 0;
+                                       /* lists are empty */
+  pData->pFirstimgobj          = MNG_NULL;
+  pData->pLastimgobj           = MNG_NULL;
+  pData->pFirstaniobj          = MNG_NULL;
+  pData->pLastaniobj           = MNG_NULL;
+#ifdef MNG_SUPPORT_DYNAMICMNG
+  pData->pFirstevent           = MNG_NULL;
+  pData->pLastevent            = MNG_NULL;
+#endif
+                                       /* no processing callbacks */
+  pData->fDisplayrow           = MNG_NULL;
+  pData->fRestbkgdrow          = MNG_NULL;
+  pData->fCorrectrow           = MNG_NULL;
+  pData->fRetrieverow          = MNG_NULL;
+  pData->fStorerow             = MNG_NULL;
+  pData->fProcessrow           = MNG_NULL;
+  pData->fDifferrow            = MNG_NULL;
+  pData->fScalerow             = MNG_NULL;
+  pData->fDeltarow             = MNG_NULL;
+#ifndef MNG_SKIPCHUNK_PAST
+  pData->fFliprow              = MNG_NULL;
+  pData->fTilerow              = MNG_NULL;
+#endif
+  pData->fInitrowproc          = MNG_NULL;
+
+  pData->iPLTEcount            = 0;    /* no PLTE data */
+
+#ifndef MNG_SKIPCHUNK_DEFI
+  pData->iDEFIobjectid         = 0;    /* no DEFI data */
+  pData->bDEFIhasdonotshow     = MNG_FALSE;
+  pData->iDEFIdonotshow        = 0;
+  pData->bDEFIhasconcrete      = MNG_FALSE;
+  pData->iDEFIconcrete         = 0;
+  pData->bDEFIhasloca          = MNG_FALSE;
+  pData->iDEFIlocax            = 0;
+  pData->iDEFIlocay            = 0;
+  pData->bDEFIhasclip          = MNG_FALSE;
+  pData->iDEFIclipl            = 0;
+  pData->iDEFIclipr            = 0;
+  pData->iDEFIclipt            = 0;
+  pData->iDEFIclipb            = 0;
+#endif
+
+#ifndef MNG_SKIPCHUNK_BACK
+  pData->iBACKred              = 0;    /* no BACK data */
+  pData->iBACKgreen            = 0;
+  pData->iBACKblue             = 0;
+  pData->iBACKmandatory        = 0;
+  pData->iBACKimageid          = 0;
+  pData->iBACKtile             = 0;
+#endif
+
+#ifndef MNG_SKIPCHUNK_FRAM
+  pData->iFRAMmode             = 1;     /* default global FRAM variables */
+  pData->iFRAMdelay            = 1;
+  pData->iFRAMtimeout          = 0x7fffffffl;
+  pData->bFRAMclipping         = MNG_FALSE;
+  pData->iFRAMclipl            = 0;
+  pData->iFRAMclipr            = 0;
+  pData->iFRAMclipt            = 0;
+  pData->iFRAMclipb            = 0;
+
+  pData->iFramemode            = 1;     /* again for the current frame */
+  pData->iFramedelay           = 1;
+  pData->iFrametimeout         = 0x7fffffffl;
+  pData->bFrameclipping        = MNG_FALSE;
+  pData->iFrameclipl           = 0;
+  pData->iFrameclipr           = 0;
+  pData->iFrameclipt           = 0;
+  pData->iFrameclipb           = 0;
+
+  pData->iNextdelay            = 1;
+#endif
+
+#ifndef MNG_SKIPCHUNK_SHOW
+  pData->iSHOWmode             = 0;    /* no SHOW data */
+  pData->iSHOWfromid           = 0;
+  pData->iSHOWtoid             = 0;
+  pData->iSHOWnextid           = 0;
+  pData->iSHOWskip             = 0;
+#endif
+
+  pData->iGlobalPLTEcount      = 0;    /* no global PLTE data */
+
+  pData->iGlobalTRNSrawlen     = 0;    /* no global tRNS data */
+
+  pData->iGlobalGamma          = 0;    /* no global gAMA data */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+  pData->iGlobalWhitepointx    = 0;    /* no global cHRM data */
+  pData->iGlobalWhitepointy    = 0;
+  pData->iGlobalPrimaryredx    = 0;
+  pData->iGlobalPrimaryredy    = 0;
+  pData->iGlobalPrimarygreenx  = 0;
+  pData->iGlobalPrimarygreeny  = 0;
+  pData->iGlobalPrimarybluex   = 0;
+  pData->iGlobalPrimarybluey   = 0;
+#endif
+
+  pData->iGlobalRendintent     = 0;    /* no global sRGB data */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+  pData->iGlobalProfilesize    = 0;    /* no global iCCP data */
+  pData->pGlobalProfile        = MNG_NULL;
+#endif
+
+#ifndef MNG_SKIPCHUNK_bKGD
+  pData->iGlobalBKGDred        = 0;    /* no global bKGD data */
+  pData->iGlobalBKGDgreen      = 0;
+  pData->iGlobalBKGDblue       = 0;
+#endif
+                                       /* no delta-image */
+#ifndef MNG_NO_DELTA_PNG
+  pData->pDeltaImage           = MNG_NULL;
+  pData->iDeltaImagetype       = 0;
+  pData->iDeltatype            = 0;
+  pData->iDeltaBlockwidth      = 0;
+  pData->iDeltaBlockheight     = 0;
+  pData->iDeltaBlockx          = 0;
+  pData->iDeltaBlocky          = 0;
+  pData->bDeltaimmediate       = MNG_FALSE;
+
+  pData->fDeltagetrow          = MNG_NULL;
+  pData->fDeltaaddrow          = MNG_NULL;
+  pData->fDeltareplacerow      = MNG_NULL;
+  pData->fDeltaputrow          = MNG_NULL;
+
+  pData->fPromoterow           = MNG_NULL;
+  pData->fPromBitdepth         = MNG_NULL;
+  pData->pPromBuf              = MNG_NULL;
+  pData->iPromColortype        = 0;
+  pData->iPromBitdepth         = 0;
+  pData->iPromFilltype         = 0;
+  pData->iPromWidth            = 0;
+  pData->pPromSrc              = MNG_NULL;
+  pData->pPromDst              = MNG_NULL;
+#endif
+
+#ifndef MNG_SKIPCHUNK_MAGN
+  pData->iMAGNfromid           = 0;
+  pData->iMAGNtoid             = 0;
+#endif
+
+#ifndef MNG_SKIPCHUNK_PAST
+  pData->iPastx                = 0;
+  pData->iPasty                = 0;
+#endif
+
+  pData->pLastseek             = MNG_NULL;
+#endif
+
+#ifdef MNG_INCLUDE_ZLIB
+  pData->bInflating            = 0;    /* no inflating or deflating */
+  pData->bDeflating            = 0;    /* going on at the moment */
+#endif
+
+#ifdef MNG_SUPPORT_DISPLAY             /* reset object 0 */
+  mng_reset_objzero (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_RESET, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_cleanup (mng_handle* hHandle)
+{
+  mng_datap pData;                     /* local vars */
+#ifndef MNG_INTERNAL_MEMMNGMT
+  mng_memfree fFree;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)*hHandle), MNG_FN_CLEANUP, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (*hHandle)           /* check validity handle */
+  pData = ((mng_datap)(*hHandle));     /* and address main structure */
+
+  mng_reset (*hHandle);                /* do an implicit reset to cleanup most stuff */
+
+#ifdef MNG_SUPPORT_DISPLAY             /* drop object 0 */
+  mng_free_imageobject (pData, (mng_imagep)pData->pObjzero);
+#endif
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_FULL_CMS)
+  if (pData->hProf2)                   /* output profile defined ? */
+    mnglcms_freeprofile (pData->hProf2);
+
+  if (pData->hProf3)                   /* sRGB profile defined ? */
+    mnglcms_freeprofile (pData->hProf3);
+#endif 
+
+#ifdef MNG_INCLUDE_ZLIB
+  mngzlib_cleanup (pData);             /* cleanup zlib stuff */
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)*hHandle), MNG_FN_CLEANUP, MNG_LC_CLEANUP)
+#endif
+
+  pData->iMagic = 0;                   /* invalidate the actual memory */
+
+#ifdef MNG_INTERNAL_MEMMNGMT
+  free ((void *)*hHandle);             /* cleanup the data-structure */
+#else
+  fFree = ((mng_datap)*hHandle)->fMemfree;
+  fFree ((mng_ptr)*hHandle, sizeof (mng_data));
+#endif
+
+  *hHandle = 0;                        /* wipe pointer to inhibit future use */
+
+  return MNG_NOERROR;                  /* and we're done */
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_read (mng_handle hHandle)
+{
+  mng_datap   pData;                   /* local vars */
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle and callbacks */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+#ifndef MNG_INTERNAL_MEMMNGMT
+  MNG_VALIDCB (hHandle, fMemalloc)
+  MNG_VALIDCB (hHandle, fMemfree)
+#endif
+
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+  MNG_VALIDCB (hHandle, fOpenstream)
+  MNG_VALIDCB (hHandle, fClosestream)
+#endif
+  MNG_VALIDCB (hHandle, fReaddata)
+
+#ifdef MNG_SUPPORT_DISPLAY             /* valid at this point ? */
+  if ((pData->bReading) || (pData->bDisplaying))
+#else
+  if (pData->bReading)
+#endif
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+#ifdef MNG_SUPPORT_WRITE
+  if ((pData->bWriting) || (pData->bCreating))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+#endif
+
+  if (!pData->bCacheplayback)          /* must store playback info to work!! */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  pData->bReading = MNG_TRUE;          /* read only! */
+
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+  if (pData->fOpenstream && !pData->fOpenstream (hHandle))
+    /* open it and start reading */
+    iRetcode = MNG_APPIOERROR;
+  else
+#endif
+    iRetcode = mng_read_graphic (pData);
+
+  if (pData->bEOF)                     /* already at EOF ? */
+  {
+    pData->bReading = MNG_FALSE;       /* then we're no longer reading */
+    
+#ifdef MNG_SUPPORT_DISPLAY
+    mng_reset_rundata (pData);         /* reset rundata */
+#endif
+  }
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  if (pData->bSuspended)               /* read suspension ? */
+  {
+     iRetcode            = MNG_NEEDMOREDATA;
+     pData->iSuspendtime = pData->fGettickcount ((mng_handle)pData);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_read_pushdata (mng_handle hHandle,
+                                        mng_ptr    pData,
+                                        mng_size_t iLength,
+                                        mng_bool   bTakeownership)
+{
+  mng_datap     pMyData;               /* local vars */
+  mng_pushdatap pPush;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHDATA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pMyData = ((mng_datap)hHandle);      /* and make it addressable */
+                                       /* create a containing buffer */
+  iRetcode = make_pushbuffer (pMyData, pData, iLength, bTakeownership, &pPush);
+  if (iRetcode)
+    return iRetcode;
+
+  if (pMyData->pLastpushdata)          /* and update the buffer chain */
+    pMyData->pLastpushdata->pNext = pPush;
+  else
+    pMyData->pFirstpushdata = pPush;
+
+  pMyData->pLastpushdata = pPush;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_read_pushsig (mng_handle  hHandle,
+                                       mng_imgtype eSigtype)
+{
+  mng_datap pData;                     /* local vars */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHSIG, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+  if (pData->bHavesig)                 /* can we expect this call ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  pData->eSigtype = eSigtype;
+  pData->bHavesig = MNG_TRUE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHSIG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_read_pushchunk (mng_handle hHandle,
+                                         mng_ptr    pChunk,
+                                         mng_size_t iLength,
+                                         mng_bool   bTakeownership)
+{
+  mng_datap     pMyData;               /* local vars */
+  mng_pushdatap pPush;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHCHUNK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pMyData = ((mng_datap)hHandle);      /* and make it addressable */
+                                       /* create a containing buffer */
+  iRetcode = make_pushbuffer (pMyData, pChunk, iLength, bTakeownership, &pPush);
+  if (iRetcode)
+    return iRetcode;
+
+  if (pMyData->pLastpushchunk)         /* and update the buffer chain */
+    pMyData->pLastpushchunk->pNext = pPush;
+  else
+    pMyData->pFirstpushchunk = pPush;
+
+  pMyData->pLastpushchunk = pPush;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHCHUNK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_read_resume (mng_handle hHandle)
+{
+  mng_datap   pData;                   /* local vars */
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_RESUME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+                                       /* can we expect this call ? */
+  if ((!pData->bReading) || (!pData->bSuspended))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  pData->bSuspended = MNG_FALSE;       /* reset the flag */
+
+#ifdef MNG_SUPPORT_DISPLAY             /* re-synchronize ? */
+  if ((pData->bDisplaying) && (pData->bRunning))
+    pData->iSynctime  = pData->iSynctime - pData->iSuspendtime +
+                        pData->fGettickcount (hHandle);
+#endif
+
+  iRetcode = mng_read_graphic (pData); /* continue reading now */
+
+  if (pData->bEOF)                     /* at EOF ? */
+  {
+    pData->bReading = MNG_FALSE;       /* then we're no longer reading */
+    
+#ifdef MNG_SUPPORT_DISPLAY
+    mng_reset_rundata (pData);         /* reset rundata */
+#endif
+  }
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  if (pData->bSuspended)               /* read suspension ? */
+  {
+     iRetcode            = MNG_NEEDMOREDATA;
+     pData->iSuspendtime = pData->fGettickcount ((mng_handle)pData);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_RESUME, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_write (mng_handle hHandle)
+{
+  mng_datap   pData;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_WRITE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle and callbacks */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+#ifndef MNG_INTERNAL_MEMMNGMT
+  MNG_VALIDCB (hHandle, fMemalloc)
+  MNG_VALIDCB (hHandle, fMemfree)
+#endif
+
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+  MNG_VALIDCB (hHandle, fOpenstream)
+  MNG_VALIDCB (hHandle, fClosestream)
+#endif
+  MNG_VALIDCB (hHandle, fWritedata)
+
+#ifdef MNG_SUPPORT_READ
+  if (pData->bReading)                 /* valid at this point ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+#endif
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  iRetcode = mng_write_graphic (pData);/* do the write */
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_WRITE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_create (mng_handle hHandle)
+{
+  mng_datap   pData;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_CREATE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle and callbacks */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+#ifndef MNG_INTERNAL_MEMMNGMT
+  MNG_VALIDCB (hHandle, fMemalloc)
+  MNG_VALIDCB (hHandle, fMemfree)
+#endif
+
+#ifdef MNG_SUPPORT_READ
+  if (pData->bReading)                 /* valid at this point ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+#endif
+
+  if ((pData->bWriting) || (pData->bCreating))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  iRetcode = mng_reset (hHandle);      /* clear any previous stuff */
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  pData->bCreating = MNG_TRUE;         /* indicate we're creating a new file */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_CREATE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_READ)
+mng_retcode MNG_DECL mng_readdisplay (mng_handle hHandle)
+{
+  mng_datap   pData;                   /* local vars */
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READDISPLAY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle and callbacks */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+#ifndef MNG_INTERNAL_MEMMNGMT
+  MNG_VALIDCB (hHandle, fMemalloc)
+  MNG_VALIDCB (hHandle, fMemfree)
+#endif
+
+  MNG_VALIDCB (hHandle, fReaddata)
+  MNG_VALIDCB (hHandle, fGetcanvasline)
+  MNG_VALIDCB (hHandle, fRefresh)
+  MNG_VALIDCB (hHandle, fGettickcount)
+  MNG_VALIDCB (hHandle, fSettimer)
+                                       /* valid at this point ? */
+  if ((pData->bReading) || (pData->bDisplaying))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+#ifdef MNG_SUPPORT_WRITE
+  if ((pData->bWriting) || (pData->bCreating))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+#endif
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  pData->bReading      = MNG_TRUE;     /* read & display! */
+  pData->bDisplaying   = MNG_TRUE;
+  pData->bRunning      = MNG_TRUE;
+  pData->iFrameseq     = 0;
+  pData->iLayerseq     = 0;
+  pData->iFrametime    = 0;
+  pData->iRequestframe = 0;
+  pData->iRequestlayer = 0;
+  pData->iRequesttime  = 0;
+  pData->bSearching    = MNG_FALSE;
+  pData->iRuntime      = 0;
+  pData->iSynctime     = pData->fGettickcount (hHandle);
+  pData->iSuspendtime  = 0;
+  pData->iStarttime    = pData->iSynctime;
+  pData->iEndtime      = 0;
+
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+  if (pData->fOpenstream && !pData->fOpenstream (hHandle))
+    /* open it and start reading */
+    iRetcode = MNG_APPIOERROR;
+  else
+#endif
+    iRetcode = mng_read_graphic (pData);
+
+  if (pData->bEOF)                     /* already at EOF ? */
+  {
+    pData->bReading = MNG_FALSE;       /* then we're no longer reading */
+    mng_drop_invalid_objects (pData);  /* drop invalidly stored objects */
+  }
+  
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  if (pData->bSuspended)               /* read suspension ? */
+  {
+     iRetcode            = MNG_NEEDMOREDATA;
+     pData->iSuspendtime = pData->fGettickcount ((mng_handle)pData);
+  }
+  else
+  if (pData->bTimerset)                /* indicate timer break ? */
+    iRetcode = MNG_NEEDTIMERWAIT;
+  else
+  if (pData->bSectionwait)             /* indicate section break ? */
+    iRetcode = MNG_NEEDSECTIONWAIT;
+  else
+  {                                    /* no breaks = end of run */
+    pData->bRunning = MNG_FALSE;
+
+    if (pData->bFreezing)              /* dynamic MNG reached SEEK ? */
+      pData->bFreezing = MNG_FALSE;    /* reset it ! */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_READDISPLAY, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif /* MNG_SUPPORT_DISPLAY && MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_display (mng_handle hHandle)
+{
+  mng_datap   pData;                   /* local vars */
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle and callbacks */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+#ifndef MNG_INTERNAL_MEMMNGMT
+  MNG_VALIDCB (hHandle, fMemalloc)
+  MNG_VALIDCB (hHandle, fMemfree)
+#endif
+
+  MNG_VALIDCB (hHandle, fGetcanvasline)
+  MNG_VALIDCB (hHandle, fRefresh)
+  MNG_VALIDCB (hHandle, fGettickcount)
+  MNG_VALIDCB (hHandle, fSettimer)
+
+  if (pData->bDisplaying)              /* valid at this point ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+    
+#ifdef MNG_SUPPORT_READ
+  if (pData->bReading)
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+#endif
+
+#ifdef MNG_SUPPORT_WRITE
+  if ((pData->bWriting) || (pData->bCreating))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+#endif
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  pData->bDisplaying   = MNG_TRUE;     /* display! */
+  pData->bRunning      = MNG_TRUE;
+  pData->iFrameseq     = 0;
+  pData->iLayerseq     = 0;
+  pData->iFrametime    = 0;
+  pData->iRequestframe = 0;
+  pData->iRequestlayer = 0;
+  pData->iRequesttime  = 0;
+  pData->bSearching    = MNG_FALSE;
+  pData->iRuntime      = 0;
+  pData->iSynctime     = pData->fGettickcount (hHandle);
+#ifdef MNG_SUPPORT_READ
+  pData->iSuspendtime  = 0;
+#endif  
+  pData->iStarttime    = pData->iSynctime;
+  pData->iEndtime      = 0;
+  pData->pCurraniobj   = pData->pFirstaniobj;
+                                       /* go do it */
+  iRetcode = mng_process_display (pData);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  if (pData->bTimerset)                /* indicate timer break ? */
+    iRetcode = MNG_NEEDTIMERWAIT;
+  else
+  {                                    /* no breaks = end of run */
+    pData->bRunning = MNG_FALSE;
+
+    if (pData->bFreezing)              /* dynamic MNG reached SEEK ? */
+      pData->bFreezing = MNG_FALSE;    /* reset it ! */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_display_resume (mng_handle hHandle)
+{
+  mng_datap   pData;                   /* local vars */
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_RESUME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+  if (!pData->bDisplaying)             /* can we expect this call ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+                                       /* was it running ? */
+  if ((pData->bRunning) || (pData->bReading))
+  {                                    /* are we expecting this call ? */
+    if ((pData->bTimerset) || (pData->bSuspended) || (pData->bSectionwait)) 
+    {
+      pData->bTimerset    = MNG_FALSE; /* reset the flags */
+      pData->bSectionwait = MNG_FALSE;
+
+#ifdef MNG_SUPPORT_READ
+      if (pData->bReading)             /* set during read&display ? */
+      {
+        if (pData->bSuspended)         /* calculate proper synchronization */
+          pData->iSynctime = pData->iSynctime - pData->iSuspendtime +
+                             pData->fGettickcount (hHandle);
+        else
+          pData->iSynctime = pData->fGettickcount (hHandle);
+
+        pData->bSuspended = MNG_FALSE; /* now reset this flag */  
+                                       /* and continue reading */
+        iRetcode = mng_read_graphic (pData);
+
+        if (pData->bEOF)               /* already at EOF ? */
+        {
+          pData->bReading = MNG_FALSE; /* then we're no longer reading */
+                                       /* drop invalidly stored objects */
+          mng_drop_invalid_objects (pData);
+        }
+      }
+      else
+#endif /* MNG_SUPPORT_READ */
+      {                                /* synchronize timing */
+        pData->iSynctime = pData->fGettickcount (hHandle);
+                                       /* resume display processing */
+        iRetcode = mng_process_display (pData);
+      }
+    }
+    else
+    {
+      MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+    }
+  }
+  else
+  {                                    /* synchronize timing */
+    pData->iSynctime = pData->fGettickcount (hHandle);
+    pData->bRunning  = MNG_TRUE;       /* it's restarted again ! */
+                                       /* resume display processing */
+    iRetcode = mng_process_display (pData);
+  }
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+  if (pData->bSuspended)               /* read suspension ? */
+  {
+     iRetcode            = MNG_NEEDMOREDATA;
+     pData->iSuspendtime = pData->fGettickcount ((mng_handle)pData);
+  }
+  else
+  if (pData->bTimerset)                /* indicate timer break ? */
+    iRetcode = MNG_NEEDTIMERWAIT;
+  else
+  if (pData->bSectionwait)             /* indicate section break ? */
+    iRetcode = MNG_NEEDSECTIONWAIT;
+  else
+  {                                    /* no breaks = end of run */
+    pData->bRunning = MNG_FALSE;
+
+    if (pData->bFreezing)              /* trying to freeze ? */
+      pData->bFreezing = MNG_FALSE;    /* then we're there */
+
+    if (pData->bResetting)             /* trying to reset as well ? */
+    {                                  /* full stop!!! */
+      pData->bDisplaying = MNG_FALSE;
+
+      iRetcode = mng_reset_rundata (pData);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+  }
+  
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_RESUME, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_display_freeze (mng_handle hHandle)
+{
+  mng_datap pData;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_FREEZE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+                                       /* can we expect this call ? */
+  if ((!pData->bDisplaying) || (pData->bReading))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  if (pData->bRunning)                 /* is it running ? */
+  {
+    mng_retcode iRetcode;
+
+    pData->bFreezing = MNG_TRUE;       /* indicate we need to freeze */
+                                       /* continue "normal" processing */
+    iRetcode = mng_display_resume (hHandle);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_FREEZE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_display_reset (mng_handle hHandle)
+{
+  mng_datap   pData;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_RESET, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+                                       /* can we expect this call ? */
+  if ((!pData->bDisplaying) || (pData->bReading))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  if (!pData->bCacheplayback)          /* must store playback info to work!! */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  if (pData->bRunning)                 /* is it running ? */
+  {
+    pData->bFreezing  = MNG_TRUE;      /* indicate we need to freeze */
+    pData->bResetting = MNG_TRUE;      /* indicate we're about to reset too */
+                                       /* continue normal processing ? */
+    iRetcode = mng_display_resume (hHandle);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+  else
+  {                                    /* full stop!!! */
+    pData->bDisplaying = MNG_FALSE;
+
+    iRetcode = mng_reset_rundata (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_RESET, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_NO_DISPLAY_GO_SUPPORTED
+mng_retcode MNG_DECL mng_display_goframe (mng_handle hHandle,
+                                          mng_uint32 iFramenr)
+{
+  mng_datap   pData;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOFRAME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+  if (pData->eImagetype != mng_it_mng) /* is it an animation ? */
+    MNG_ERROR (pData, MNG_NOTANANIMATION);
+                                       /* can we expect this call ? */
+  if ((!pData->bDisplaying) || (pData->bRunning))
+    MNG_ERROR ((mng_datap)hHandle, MNG_FUNCTIONINVALID);
+
+  if (!pData->bCacheplayback)          /* must store playback info to work!! */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  if (iFramenr > pData->iTotalframes)  /* is the parameter within bounds ? */
+    MNG_ERROR (pData, MNG_FRAMENRTOOHIGH);
+                                       /* within MHDR bounds ? */
+  if ((pData->iFramecount) && (iFramenr > pData->iFramecount))
+    MNG_WARNING (pData, MNG_FRAMENRTOOHIGH);
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  if (pData->iFrameseq > iFramenr)     /* search from current or go back to start ? */
+  {
+    iRetcode = mng_reset_rundata (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+  if (iFramenr)
+  {
+    pData->iRequestframe = iFramenr;   /* go find the requested frame then */
+    iRetcode = mng_process_display (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    pData->bTimerset = MNG_FALSE;      /* reset just to be safe */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOFRAME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_NO_DISPLAY_GO_SUPPORTED
+mng_retcode MNG_DECL mng_display_golayer (mng_handle hHandle,
+                                          mng_uint32 iLayernr)
+{
+  mng_datap   pData;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOLAYER, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+  if (pData->eImagetype != mng_it_mng) /* is it an animation ? */
+    MNG_ERROR (pData, MNG_NOTANANIMATION);
+                                       /* can we expect this call ? */
+  if ((!pData->bDisplaying) || (pData->bRunning))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  if (!pData->bCacheplayback)          /* must store playback info to work!! */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  if (iLayernr > pData->iTotallayers)  /* is the parameter within bounds ? */
+    MNG_ERROR (pData, MNG_LAYERNRTOOHIGH);
+                                       /* within MHDR bounds ? */
+  if ((pData->iLayercount) && (iLayernr > pData->iLayercount))
+    MNG_WARNING (pData, MNG_LAYERNRTOOHIGH);
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  if (pData->iLayerseq > iLayernr)     /* search from current or go back to start ? */
+  {
+    iRetcode = mng_reset_rundata (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+  if (iLayernr)
+  {
+    pData->iRequestlayer = iLayernr;   /* go find the requested layer then */
+    iRetcode = mng_process_display (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    pData->bTimerset = MNG_FALSE;      /* reset just to be safe */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOLAYER, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_NO_DISPLAY_GO_SUPPORTED
+mng_retcode MNG_DECL mng_display_gotime (mng_handle hHandle,
+                                         mng_uint32 iPlaytime)
+{
+  mng_datap   pData;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOTIME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+  if (pData->eImagetype != mng_it_mng) /* is it an animation ? */
+    MNG_ERROR (pData, MNG_NOTANANIMATION);
+                                       /* can we expect this call ? */
+  if ((!pData->bDisplaying) || (pData->bRunning))
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  if (!pData->bCacheplayback)          /* must store playback info to work!! */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+                                       /* is the parameter within bounds ? */
+  if (iPlaytime > pData->iTotalplaytime)
+    MNG_ERROR (pData, MNG_PLAYTIMETOOHIGH);
+                                       /* within MHDR bounds ? */
+  if ((pData->iPlaytime) && (iPlaytime > pData->iPlaytime))
+    MNG_WARNING (pData, MNG_PLAYTIMETOOHIGH);
+
+  cleanup_errors (pData);              /* cleanup previous errors */
+
+  if (pData->iFrametime > iPlaytime)   /* search from current or go back to start ? */
+  {
+    iRetcode = mng_reset_rundata (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+  if (iPlaytime)
+  {
+    pData->iRequesttime = iPlaytime;   /* go find the requested playtime then */
+    iRetcode = mng_process_display (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    pData->bTimerset = MNG_FALSE;      /* reset just to be safe */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOTIME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG)
+mng_retcode MNG_DECL mng_trapevent (mng_handle hHandle,
+                                    mng_uint8  iEventtype,
+                                    mng_int32  iX,
+                                    mng_int32  iY)
+{
+  mng_datap   pData;
+  mng_eventp  pEvent;
+  mng_bool    bFound = MNG_FALSE;
+  mng_retcode iRetcode;
+  mng_imagep  pImage;
+  mng_uint8p  pPixel;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_TRAPEVENT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+  if (pData->eImagetype != mng_it_mng) /* is it an animation ? */
+    MNG_ERROR (pData, MNG_NOTANANIMATION);
+
+  if (!pData->bDisplaying)             /* can we expect this call ? */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  if (!pData->bCacheplayback)          /* must store playback info to work!! */
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+                                       /* let's find a matching event object */
+  pEvent = (mng_eventp)pData->pFirstevent;
+
+  while ((pEvent) && (!bFound))
+  {                                    /* matching eventtype ? */
+    if (pEvent->iEventtype == iEventtype)
+    {
+      switch (pEvent->iMasktype)       /* check X/Y on basis of masktype */
+      {
+        case MNG_MASK_NONE :           /* no mask is easy */
+          {
+            bFound = MNG_TRUE;
+            break;
+          }
+
+        case MNG_MASK_BOX :            /* inside the given box ? */
+          {                            /* right- and bottom-border don't count ! */
+            if ((iX >= pEvent->iLeft) && (iX < pEvent->iRight) &&
+                (iY >= pEvent->iTop) && (iY < pEvent->iBottom))
+              bFound = MNG_TRUE;
+            break;
+          }
+          
+        case MNG_MASK_OBJECT :         /* non-zero pixel in the image object ? */
+          {
+            pImage = mng_find_imageobject (pData, pEvent->iObjectid);
+                                       /* valid image ? */
+            if ((pImage) && (pImage->pImgbuf->iBitdepth <= 8) &&
+                ((pImage->pImgbuf->iColortype == 0) || (pImage->pImgbuf->iColortype == 3)) &&
+                ((mng_int32)pImage->pImgbuf->iWidth  > iX) &&
+                ((mng_int32)pImage->pImgbuf->iHeight > iY))
+            {
+              pPixel = pImage->pImgbuf->pImgdata + ((pImage->pImgbuf->iWidth * iY) + iX);
+
+              if (*pPixel)             /* non-zero ? */
+                bFound = MNG_TRUE;
+            }
+
+            break;
+          }
+
+        case MNG_MASK_OBJECTIX :       /* pixel in the image object matches index ? */
+          {
+            pImage = mng_find_imageobject (pData, pEvent->iObjectid);
+                                       /* valid image ? */
+            if ((pImage) && (pImage->pImgbuf->iBitdepth <= 8) &&
+                ((pImage->pImgbuf->iColortype == 0) || (pImage->pImgbuf->iColortype == 3)) &&
+                ((mng_int32)pImage->pImgbuf->iWidth  > iX) && (iX >= 0) &&
+                ((mng_int32)pImage->pImgbuf->iHeight > iY) && (iY >= 0))
+            {
+              pPixel = pImage->pImgbuf->pImgdata + ((pImage->pImgbuf->iWidth * iY) + iX);
+                                       /* matching index ? */
+              if (*pPixel == pEvent->iIndex)
+                bFound = MNG_TRUE;
+            }
+
+            break;
+          }
+
+        case MNG_MASK_BOXOBJECT :      /* non-zero pixel in the image object ? */
+          {
+            mng_int32 iTempx = iX - pEvent->iLeft;
+            mng_int32 iTempy = iY - pEvent->iTop;
+
+            pImage = mng_find_imageobject (pData, pEvent->iObjectid);
+                                       /* valid image ? */
+            if ((pImage) && (pImage->pImgbuf->iBitdepth <= 8) &&
+                ((pImage->pImgbuf->iColortype == 0) || (pImage->pImgbuf->iColortype == 3)) &&
+                (iTempx < (mng_int32)pImage->pImgbuf->iWidth) &&
+                (iTempx >= 0) && (iX < pEvent->iRight) &&
+                (iTempy < (mng_int32)pImage->pImgbuf->iHeight) &&
+                (iTempy >= 0) && (iY < pEvent->iBottom))
+            {
+              pPixel = pImage->pImgbuf->pImgdata + ((pImage->pImgbuf->iWidth * iTempy) + iTempx);
+
+              if (*pPixel)             /* non-zero ? */
+                bFound = MNG_TRUE;
+            }
+
+            break;
+          }
+
+        case MNG_MASK_BOXOBJECTIX :    /* pixel in the image object matches index ? */
+          {
+            mng_int32 iTempx = iX - pEvent->iLeft;
+            mng_int32 iTempy = iY - pEvent->iTop;
+
+            pImage = mng_find_imageobject (pData, pEvent->iObjectid);
+                                       /* valid image ? */
+            if ((pImage) && (pImage->pImgbuf->iBitdepth <= 8) &&
+                ((pImage->pImgbuf->iColortype == 0) || (pImage->pImgbuf->iColortype == 3)) &&
+                (iTempx < (mng_int32)pImage->pImgbuf->iWidth) &&
+                (iTempx >= 0) && (iX < pEvent->iRight) &&
+                (iTempy < (mng_int32)pImage->pImgbuf->iHeight) &&
+                (iTempy >= 0) && (iY < pEvent->iBottom))
+            {
+              pPixel = pImage->pImgbuf->pImgdata + ((pImage->pImgbuf->iWidth * iTempy) + iTempx);
+                                       /* matching index ? */
+              if (*pPixel == pEvent->iIndex)
+                bFound = MNG_TRUE;
+            }
+
+            break;
+          }
+
+      }
+    }
+
+    if (!bFound)                       /* try the next one */
+      pEvent = (mng_eventp)pEvent->sHeader.pNext;
+  }
+                                       /* found one that's not the last mousemove ? */
+  if ((pEvent) && ((mng_objectp)pEvent != pData->pLastmousemove))
+  {                                    /* can we start an event process now ? */
+    if ((!pData->bReading) && (!pData->bRunning))
+    {
+      pData->iEventx = iX;             /* save coordinates */
+      pData->iEventy = iY;
+                                       /* do it then ! */
+      iRetcode = pEvent->sHeader.fProcess (pData, pEvent);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+                                       /* remember last mousemove event */
+      if (pEvent->iEventtype == MNG_EVENT_MOUSEMOVE)
+        pData->pLastmousemove = (mng_objectp)pEvent;
+      else
+        pData->pLastmousemove = MNG_NULL;
+    }
+    else
+    {
+
+      /* TODO: store unprocessed events or not ??? */
+
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_TRAPEVENT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_getlasterror (mng_handle   hHandle,
+                                       mng_int8*    iSeverity,
+                                       mng_chunkid* iChunkname,
+                                       mng_uint32*  iChunkseq,
+                                       mng_int32*   iExtra1,
+                                       mng_int32*   iExtra2,
+                                       mng_pchar*   zErrortext)
+{
+  mng_datap pData;                     /* local vars */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETLASTERROR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)            /* check validity handle */
+  pData = ((mng_datap)hHandle);        /* and make it addressable */
+
+  *iSeverity  = pData->iSeverity;      /* return the appropriate fields */
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+  *iChunkname = pData->iChunkname;
+  *iChunkseq  = pData->iChunkseq;
+#else
+  *iChunkname = MNG_UINT_HUH;
+  *iChunkseq  = 0;
+#endif
+
+  *iExtra1    = pData->iErrorx1;
+  *iExtra2    = pData->iErrorx2;
+  *zErrortext = pData->zErrortext;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETLASTERROR, MNG_LC_END);
+#endif
+
+  return pData->iErrorcode;            /* and the errorcode */
+}
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
+
diff --git a/files/Source/LibMNG/libmng_jpeg.c b/files/Source/LibMNG/libmng_jpeg.c
new file mode 100644
index 0000000..5042e1d
--- /dev/null
+++ b/files/Source/LibMNG/libmng_jpeg.c
@@ -0,0 +1,1088 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_jpeg.c             copyright (c) 2000-2004 G.Juyn   * */
+/* * version   : 1.0.9                                                      * */
+/* *                                                                        * */
+/* * purpose   : JPEG library interface (implementation)                    * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the JPEG library interface               * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/22/2000 - G.Juyn                                * */
+/* *             - implemented all the JNG routines                         * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/17/2000 - G.Juyn                                * */
+/* *             - added tracing of JPEG calls                              * */
+/* *             0.5.3 - 06/24/2000 - G.Juyn                                * */
+/* *             - fixed inclusion of IJG read/write code                   * */
+/* *             0.5.3 - 06/29/2000 - G.Juyn                                * */
+/* *             - fixed some 64-bit warnings                               * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added support for JDAA                                   * */
+/* *                                                                        * */
+/* *             1.0.1 - 04/19/2001 - G.Juyn                                * */
+/* *             - added export of JPEG functions for DLL                   * */
+/* *             1.0.1 - 04/22/2001 - G.Juyn                                * */
+/* *             - fixed memory-leaks (Thanks Gregg!)                       * */
+/* *                                                                        * */
+/* *             1.0.4 - 06/22/2002 - G.Juyn                                * */
+/* *             - B526138 - returned IJGSRC6B calling convention to        * */
+/* *               default for MSVC                                         * */
+/* *                                                                        * */
+/* *             1.0.5 - 24/02/2003 - G.Juyn                                * */
+/* *             - B683152 - libjpeg suspension not always honored correctly* */
+/* *                                                                        * */
+/* *             1.0.6 - 03/04/2003 - G.Juyn                                * */
+/* *             - fixed some compiler-warnings                             * */
+/* *                                                                        * */
+/* *             1.0.8 - 08/01/2004 - G.Juyn                                * */
+/* *             - added support for 3+byte pixelsize for JPEG's            * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_memory.h"
+#include "libmng_pixels.h"
+#include "libmng_jpeg.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#if defined(MNG_INCLUDE_JNG) && defined(MNG_INCLUDE_DISPLAY_PROCS)
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Local IJG callback routines (source-manager, error-manager and such)   * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_IJG6B
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+#ifdef MNG_DEFINE_JPEG_STDCALL
+void MNG_DECL mng_init_source (j_decompress_ptr cinfo)
+#else
+void mng_init_source (j_decompress_ptr cinfo)
+#endif
+{
+  return;                              /* nothing needed */
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+#ifdef MNG_DEFINE_JPEG_STDCALL
+boolean MNG_DECL mng_fill_input_buffer (j_decompress_ptr cinfo)
+#else
+boolean mng_fill_input_buffer (j_decompress_ptr cinfo)
+#endif
+{
+  return FALSE;                        /* force IJG routine to return to caller */
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+#ifdef MNG_DEFINE_JPEG_STDCALL
+void MNG_DECL mng_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+#else
+void mng_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+#endif
+{
+  if (num_bytes > 0)                   /* ignore fony calls */
+  {                                    /* address my generic structure */
+    mng_datap pData = (mng_datap)cinfo->client_data;
+                                       /* address source manager */
+    mngjpeg_sourcep pSrc = pData->pJPEGdinfo->src;
+                                       /* problem scenario ? */
+    if (pSrc->bytes_in_buffer < (size_t)num_bytes)
+    {                                  /* tell the boss we need to skip some data! */
+      pData->iJPEGtoskip = (mng_uint32)((size_t)num_bytes - pSrc->bytes_in_buffer);
+
+      pSrc->bytes_in_buffer = 0;       /* let the JPEG lib suspend */
+      pSrc->next_input_byte = MNG_NULL;
+    }
+    else
+    {                                  /* simply advance in the buffer */
+      pSrc->bytes_in_buffer -= num_bytes;
+      pSrc->next_input_byte += num_bytes;
+    }
+  }
+
+  return;
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+#ifdef MNG_DEFINE_JPEG_STDCALL
+void MNG_DECL mng_skip_input_data2 (j_decompress_ptr cinfo, long num_bytes)
+#else
+void mng_skip_input_data2 (j_decompress_ptr cinfo, long num_bytes)
+#endif
+{
+  if (num_bytes > 0)                   /* ignore fony calls */
+  {                                    /* address my generic structure */
+    mng_datap pData = (mng_datap)cinfo->client_data;
+                                       /* address source manager */
+    mngjpeg_sourcep pSrc = pData->pJPEGdinfo2->src;
+                                       /* problem scenario ? */
+    if (pSrc->bytes_in_buffer < (size_t)num_bytes)
+    {                                  /* tell the boss we need to skip some data! */
+      pData->iJPEGtoskip2 = (mng_uint32)((size_t)num_bytes - pSrc->bytes_in_buffer);
+
+      pSrc->bytes_in_buffer = 0;       /* let the JPEG lib suspend */
+      pSrc->next_input_byte = MNG_NULL;
+    }
+    else
+    {                                  /* simply advance in the buffer */
+      pSrc->bytes_in_buffer -= num_bytes;
+      pSrc->next_input_byte += num_bytes;
+    }
+  }
+
+  return;
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+#ifdef MNG_DEFINE_JPEG_STDCALL
+void MNG_DECL mng_term_source (j_decompress_ptr cinfo)
+#else
+void mng_term_source (j_decompress_ptr cinfo)
+#endif
+{
+  return;                              /* nothing needed */
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_USE_SETJMP
+#ifdef MNG_DEFINE_JPEG_STDCALL
+void MNG_DECL mng_error_exit (j_common_ptr cinfo)
+#else
+void mng_error_exit (j_common_ptr cinfo)
+#endif
+{                                      /* address my generic structure */
+  mng_datap pData = (mng_datap)cinfo->client_data;
+
+#ifdef MNG_ERROR_TELLTALE              /* fill the message text ??? */
+  (*cinfo->err->output_message) (cinfo);
+#endif
+                                       /* return to the point of no return... */
+  longjmp (pData->sErrorbuf, cinfo->err->msg_code);
+}
+#endif /* MNG_USE_SETJMP */
+
+/* ************************************************************************** */
+
+#ifdef MNG_USE_SETJMP
+#ifdef MNG_DEFINE_JPEG_STDCALL
+void MNG_DECL mng_output_message (j_common_ptr cinfo)
+#else
+void mng_output_message (j_common_ptr cinfo)
+#endif
+{
+  return;                              /* just do nothing ! */
+}
+#endif /* MNG_USE_SETJMP */
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_IJG6B */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Global JPEG routines                                                   * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mngjpeg_initialize (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_INITIALIZE, MNG_LC_START);
+#endif
+                                       /* allocate space for JPEG structures if necessary */
+#ifdef MNG_INCLUDE_JNG_READ
+  if (pData->pJPEGderr   == MNG_NULL)
+    MNG_ALLOC (pData, pData->pJPEGderr,   sizeof (mngjpeg_error ));
+  if (pData->pJPEGdsrc   == MNG_NULL)
+    MNG_ALLOC (pData, pData->pJPEGdsrc,   sizeof (mngjpeg_source));
+  if (pData->pJPEGdinfo  == MNG_NULL)
+    MNG_ALLOC (pData, pData->pJPEGdinfo,  sizeof (mngjpeg_decomp));
+                                       /* enable reverse addressing */
+  pData->pJPEGdinfo->client_data  = pData;
+
+  if (pData->pJPEGderr2  == MNG_NULL)
+    MNG_ALLOC (pData, pData->pJPEGderr2,  sizeof (mngjpeg_error ));
+  if (pData->pJPEGdsrc2  == MNG_NULL)
+    MNG_ALLOC (pData, pData->pJPEGdsrc2,  sizeof (mngjpeg_source));
+  if (pData->pJPEGdinfo2 == MNG_NULL)
+    MNG_ALLOC (pData, pData->pJPEGdinfo2, sizeof (mngjpeg_decomp));
+                                       /* enable reverse addressing */
+  pData->pJPEGdinfo2->client_data = pData;
+#endif
+
+#ifdef MNG_INCLUDE_JNG_WRITE
+  if (pData->pJPEGcerr  == MNG_NULL)
+    MNG_ALLOC (pData, pData->pJPEGcerr,  sizeof (mngjpeg_error ));
+  if (pData->pJPEGcinfo == MNG_NULL)
+    MNG_ALLOC (pData, pData->pJPEGcinfo, sizeof (mngjpeg_comp  ));
+                                       /* enable reverse addressing */
+  pData->pJPEGcinfo->client_data = pData;
+#endif
+
+  if (pData->pJPEGbuf   == MNG_NULL)   /* initialize temporary buffers */
+  {
+    pData->iJPEGbufmax     = MNG_JPEG_MAXBUF;
+    MNG_ALLOC (pData, pData->pJPEGbuf, pData->iJPEGbufmax);
+  }
+
+  if (pData->pJPEGbuf2  == MNG_NULL) 
+  {
+    pData->iJPEGbufmax2    = MNG_JPEG_MAXBUF;
+    MNG_ALLOC (pData, pData->pJPEGbuf2, pData->iJPEGbufmax2);
+  }
+
+  pData->pJPEGcurrent      = pData->pJPEGbuf;
+  pData->iJPEGbufremain    = 0;
+  pData->pJPEGrow          = MNG_NULL;
+  pData->iJPEGrowlen       = 0;
+  pData->iJPEGtoskip       = 0;
+
+  pData->pJPEGcurrent2     = pData->pJPEGbuf2;
+  pData->iJPEGbufremain2   = 0;
+  pData->pJPEGrow2         = MNG_NULL;
+  pData->iJPEGrowlen2      = 0;
+  pData->iJPEGtoskip2      = 0;
+                                      /* not doing anything yet ! */
+  pData->bJPEGcompress     = MNG_FALSE;
+  
+  pData->bJPEGdecompress   = MNG_FALSE;
+  pData->bJPEGhasheader    = MNG_FALSE;
+  pData->bJPEGdecostarted  = MNG_FALSE;
+  pData->bJPEGscanstarted  = MNG_FALSE;
+  pData->bJPEGscanending   = MNG_FALSE;
+
+  pData->bJPEGdecompress2  = MNG_FALSE;
+  pData->bJPEGhasheader2   = MNG_FALSE;
+  pData->bJPEGdecostarted2 = MNG_FALSE;
+  pData->bJPEGscanstarted2 = MNG_FALSE;
+
+  pData->iJPEGrow          = 0;        /* zero input/output lines */
+  pData->iJPEGalpharow     = 0;
+  pData->iJPEGrgbrow       = 0;
+  pData->iJPEGdisprow      = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_INITIALIZE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mngjpeg_cleanup (mng_datap pData)
+{
+#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP)
+  mng_retcode iRetcode;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_CLEANUP, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_IJG6B
+#ifdef MNG_USE_SETJMP
+  iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */
+  if (iRetcode != 0)                   /* got here from longjmp ? */
+    MNG_ERRORJ (pData, iRetcode);      /* then IJG-lib issued an error */
+#endif
+
+#ifdef MNG_INCLUDE_JNG_READ            /* still decompressing something ? */
+  if (pData->bJPEGdecompress)
+    jpeg_destroy_decompress (pData->pJPEGdinfo);
+  if (pData->bJPEGdecompress2)
+    jpeg_destroy_decompress (pData->pJPEGdinfo2);
+#endif
+
+#ifdef MNG_INCLUDE_JNG_WRITE
+  if (pData->bJPEGcompress)            /* still compressing something ? */
+    jpeg_destroy_compress (pData->pJPEGcinfo);
+#endif
+
+#endif /* MNG_INCLUDE_IJG6B */
+                                       /* cleanup temporary buffers */
+  MNG_FREE (pData, pData->pJPEGbuf2, pData->iJPEGbufmax2);
+  MNG_FREE (pData, pData->pJPEGbuf,  pData->iJPEGbufmax);
+                                       /* cleanup space for JPEG structures */
+#ifdef MNG_INCLUDE_JNG_WRITE
+  MNG_FREE (pData, pData->pJPEGcinfo,  sizeof (mngjpeg_comp  ));
+  MNG_FREE (pData, pData->pJPEGcerr,   sizeof (mngjpeg_error ));
+#endif
+
+#ifdef MNG_INCLUDE_JNG_READ
+  MNG_FREE (pData, pData->pJPEGdinfo,  sizeof (mngjpeg_decomp));
+  MNG_FREE (pData, pData->pJPEGdsrc,   sizeof (mngjpeg_source));
+  MNG_FREE (pData, pData->pJPEGderr,   sizeof (mngjpeg_error ));
+  MNG_FREE (pData, pData->pJPEGdinfo2, sizeof (mngjpeg_decomp));
+  MNG_FREE (pData, pData->pJPEGdsrc2,  sizeof (mngjpeg_source));
+  MNG_FREE (pData, pData->pJPEGderr2,  sizeof (mngjpeg_error ));
+#endif
+
+  MNG_FREE (pData, pData->pJPEGrow2, pData->iJPEGrowlen2);
+  MNG_FREE (pData, pData->pJPEGrow,  pData->iJPEGrowlen);
+                                       /* whatever we were doing ... */
+                                       /* we don't anymore ... */
+  pData->bJPEGcompress     = MNG_FALSE;
+
+  pData->bJPEGdecompress   = MNG_FALSE;
+  pData->bJPEGhasheader    = MNG_FALSE;
+  pData->bJPEGdecostarted  = MNG_FALSE;
+  pData->bJPEGscanstarted  = MNG_FALSE;
+  pData->bJPEGscanending   = MNG_FALSE;
+
+  pData->bJPEGdecompress2  = MNG_FALSE;
+  pData->bJPEGhasheader2   = MNG_FALSE;
+  pData->bJPEGdecostarted2 = MNG_FALSE;
+  pData->bJPEGscanstarted2 = MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_CLEANUP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * JPEG decompression routines (JDAT)                                     * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+mng_retcode mngjpeg_decompressinit (mng_datap pData)
+{
+#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP)
+  mng_retcode iRetcode;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_IJG6B
+  /* allocate and initialize a JPEG decompression object */
+  pData->pJPEGdinfo->err = jpeg_std_error (pData->pJPEGderr);
+
+#ifdef MNG_USE_SETJMP                  /* setup local JPEG error-routines */
+  pData->pJPEGderr->error_exit     = mng_error_exit;
+  pData->pJPEGderr->output_message = mng_output_message;
+
+  iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */
+  if (iRetcode != 0)                   /* got here from longjmp ? */
+    MNG_ERRORJ (pData, iRetcode);      /* then IJG-lib issued an error */
+#endif /* MNG_USE_SETJMP */
+
+  /* allocate and initialize a JPEG decompression object (continued) */
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_JPEG_CREATE_DECOMPRESS)
+#endif
+  jpeg_create_decompress (pData->pJPEGdinfo);
+
+  pData->bJPEGdecompress = MNG_TRUE;   /* indicate it's initialized */
+
+  /* specify the source of the compressed data (eg, a file) */
+                                       /* no, not a file; we have buffered input */
+  pData->pJPEGdinfo->src = pData->pJPEGdsrc;
+                                       /* use the default handler */
+  pData->pJPEGdinfo->src->resync_to_restart = jpeg_resync_to_restart;
+                                       /* setup local source routine & parms */
+  pData->pJPEGdinfo->src->init_source       = mng_init_source;
+  pData->pJPEGdinfo->src->fill_input_buffer = mng_fill_input_buffer;
+  pData->pJPEGdinfo->src->skip_input_data   = mng_skip_input_data;
+  pData->pJPEGdinfo->src->term_source       = mng_term_source;
+  pData->pJPEGdinfo->src->next_input_byte   = pData->pJPEGcurrent;
+  pData->pJPEGdinfo->src->bytes_in_buffer   = pData->iJPEGbufremain;
+
+#endif /* MNG_INCLUDE_IJG6B */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+mng_retcode mngjpeg_decompressdata (mng_datap  pData,
+                                    mng_uint32 iRawsize,
+                                    mng_uint8p pRawdata)
+{
+  mng_retcode iRetcode;
+  mng_uint32  iRemain;
+  mng_uint8p  pWork;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_START);
+#endif
+
+#if defined (MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP)
+  iRetcode = setjmp (pData->sErrorbuf);/* initialize local JPEG error-recovery */
+  if (iRetcode != 0)                   /* got here from longjmp ? */
+    MNG_ERRORJ (pData, iRetcode);      /* then IJG-lib issued an error */
+#endif
+
+  pWork   = pRawdata;
+  iRemain = iRawsize;
+
+  if (pData->iJPEGtoskip)              /* JPEG-lib told us to skip some more data ? */
+  {
+    if (iRemain > pData->iJPEGtoskip)  /* enough data in this buffer ? */
+    {
+      iRemain -= pData->iJPEGtoskip;   /* skip enough to access the next byte */
+      pWork   += pData->iJPEGtoskip;
+
+      pData->iJPEGtoskip = 0;          /* no more to skip then */
+    }
+    else
+    {
+      pData->iJPEGtoskip -= iRemain;   /* skip all data in the buffer */
+      iRemain = 0;                     /* and indicate this accordingly */
+    }
+                                       /* the skip set current-pointer to NULL ! */
+    pData->pJPEGcurrent = pData->pJPEGbuf;
+  }
+
+  while (iRemain)                      /* repeat until no more input-bytes */
+  {                                    /* need to shift anything ? */
+    if ((pData->pJPEGcurrent > pData->pJPEGbuf) &&
+        (pData->pJPEGcurrent - pData->pJPEGbuf + pData->iJPEGbufremain + iRemain > pData->iJPEGbufmax))
+    {
+      if (pData->iJPEGbufremain > 0)   /* then do so */
+        MNG_COPY (pData->pJPEGbuf, pData->pJPEGcurrent, pData->iJPEGbufremain);
+
+      pData->pJPEGcurrent = pData->pJPEGbuf;
+    }
+                                       /* does the remaining input fit into the buffer ? */
+    if (pData->iJPEGbufremain + iRemain <= pData->iJPEGbufmax)
+    {                                  /* move the lot */
+      MNG_COPY ((pData->pJPEGcurrent + pData->iJPEGbufremain), pWork, iRemain);
+
+      pData->iJPEGbufremain += iRemain;/* adjust remaining_bytes counter */
+      iRemain = 0;                     /* and indicate there's no input left */
+    }
+    else
+    {                                  /* calculate what does fit */
+      mng_uint32 iFits = pData->iJPEGbufmax - pData->iJPEGbufremain;
+
+      if (iFits <= 0)                  /* no space is just bugger 'm all */
+        MNG_ERROR (pData, MNG_JPEGBUFTOOSMALL);
+                                       /* move that */
+      MNG_COPY ((pData->pJPEGcurrent + pData->iJPEGbufremain), pWork, iFits);
+
+      pData->iJPEGbufremain += iFits;  /* adjust remain_bytes counter */
+      iRemain -= iFits;                /* and the input-parms */
+      pWork   += iFits;
+    }
+
+#ifdef MNG_INCLUDE_IJG6B
+    pData->pJPEGdinfo->src->next_input_byte = pData->pJPEGcurrent;
+    pData->pJPEGdinfo->src->bytes_in_buffer = pData->iJPEGbufremain;
+
+    if (!pData->bJPEGhasheader)        /* haven't got the header yet ? */
+    {
+      /* call jpeg_read_header() to obtain image info */
+#ifdef MNG_SUPPORT_TRACE
+      MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_READ_HEADER)
+#endif
+      if (jpeg_read_header (pData->pJPEGdinfo, TRUE) != JPEG_SUSPENDED)
+      {                                /* indicate the header's oke */
+        pData->bJPEGhasheader = MNG_TRUE;
+                                       /* let's do some sanity checks ! */
+        if ((pData->pJPEGdinfo->image_width  != pData->iDatawidth ) ||
+            (pData->pJPEGdinfo->image_height != pData->iDataheight)    )
+          MNG_ERROR (pData, MNG_JPEGPARMSERR);
+
+        if ( ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAY ) ||
+              (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA)    ) &&
+             (pData->pJPEGdinfo->jpeg_color_space != JCS_GRAYSCALE  )    )
+          MNG_ERROR (pData, MNG_JPEGPARMSERR);
+
+        if ( ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLOR ) ||
+              (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    ) &&
+             (pData->pJPEGdinfo->jpeg_color_space != JCS_YCbCr       )    )
+          MNG_ERROR (pData, MNG_JPEGPARMSERR);
+                                       /* indicate whether or not it's progressive */
+        pData->bJPEGprogressive = (mng_bool)jpeg_has_multiple_scans (pData->pJPEGdinfo);
+                                       /* progressive+alpha can't display "on-the-fly"!! */
+        if ((pData->bJPEGprogressive) &&
+            ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) ||
+             (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    ))
+          pData->fDisplayrow = MNG_NULL;
+                                       /* allocate a row of JPEG-samples */
+        if (pData->pJPEGdinfo->jpeg_color_space == JCS_YCbCr)
+          pData->iJPEGrowlen = pData->pJPEGdinfo->image_width * RGB_PIXELSIZE;
+        else
+          pData->iJPEGrowlen = pData->pJPEGdinfo->image_width;
+
+        MNG_ALLOC (pData, pData->pJPEGrow, pData->iJPEGrowlen);
+
+        pData->iJPEGrgbrow = 0;        /* quite empty up to now */
+      }
+
+      pData->pJPEGcurrent   = (mng_uint8p)pData->pJPEGdinfo->src->next_input_byte;
+      pData->iJPEGbufremain = (mng_uint32)pData->pJPEGdinfo->src->bytes_in_buffer;
+    }
+                                       /* decompress not started ? */
+    if ((pData->bJPEGhasheader) && (!pData->bJPEGdecostarted))
+    {
+      /* set parameters for decompression */
+
+      if (pData->bJPEGprogressive)     /* progressive display ? */
+        pData->pJPEGdinfo->buffered_image = TRUE;
+
+      /* jpeg_start_decompress(...); */
+#ifdef MNG_SUPPORT_TRACE
+      MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_START_DECOMPRESS)
+#endif
+      if (jpeg_start_decompress (pData->pJPEGdinfo) == TRUE)
+                                       /* indicate it started */
+        pData->bJPEGdecostarted = MNG_TRUE;
+
+      pData->pJPEGcurrent   = (mng_uint8p)pData->pJPEGdinfo->src->next_input_byte;
+      pData->iJPEGbufremain = (mng_uint32)pData->pJPEGdinfo->src->bytes_in_buffer;
+    }
+                                       /* process some scanlines ? */
+    if ((pData->bJPEGhasheader) && (pData->bJPEGdecostarted) &&
+	    ((!jpeg_input_complete (pData->pJPEGdinfo)) ||
+         (pData->pJPEGdinfo->output_scanline < pData->pJPEGdinfo->output_height) ||
+         ((pData->bJPEGprogressive) && (pData->bJPEGscanending))))
+    {
+      mng_int32 iLines = 0;
+
+      /* for (each output pass) */
+      do
+      {                                /* address the row output buffer */
+        JSAMPROW pRow = (JSAMPROW)pData->pJPEGrow;
+
+                                       /* init new pass ? */
+        if ((pData->bJPEGprogressive) && (!pData->bJPEGscanstarted))
+        {
+          pData->bJPEGscanstarted = MNG_TRUE;
+
+          /* adjust output decompression parameters if required */
+          /* nop */
+
+          /* start a new output pass */
+#ifdef MNG_SUPPORT_TRACE
+          MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_START_OUTPUT)
+#endif
+          jpeg_start_output (pData->pJPEGdinfo, pData->pJPEGdinfo->input_scan_number);
+
+          pData->iJPEGrow = 0;         /* start at row 0 in the image again */
+        }
+
+        /* while (scan lines remain to be read) */
+        if ((!pData->bJPEGprogressive) || (!pData->bJPEGscanending))
+        {
+          do
+          {
+          /*   jpeg_read_scanlines(...); */
+#ifdef MNG_SUPPORT_TRACE
+            MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_READ_SCANLINES)
+#endif
+            iLines = jpeg_read_scanlines (pData->pJPEGdinfo, (JSAMPARRAY)&pRow, 1);
+
+            pData->pJPEGcurrent   = (mng_uint8p)pData->pJPEGdinfo->src->next_input_byte;
+            pData->iJPEGbufremain = (mng_uint32)pData->pJPEGdinfo->src->bytes_in_buffer;
+
+            if (iLines > 0)            /* got something ? */
+            {
+              if (pData->fStorerow2)   /* store in object ? */
+              {
+                iRetcode = ((mng_storerow)pData->fStorerow2) (pData);
+
+                if (iRetcode)          /* on error bail out */
+                return iRetcode;
+
+              }
+            }
+          }
+          while ((pData->pJPEGdinfo->output_scanline < pData->pJPEGdinfo->output_height) &&
+                 (iLines > 0));        /* until end-of-image or not enough input-data */
+        }
+
+        /* terminate output pass */
+        if ((pData->bJPEGprogressive) &&
+            (pData->pJPEGdinfo->output_scanline >= pData->pJPEGdinfo->output_height))
+        {
+#ifdef MNG_SUPPORT_TRACE
+          MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_FINISH_OUTPUT)
+#endif
+          if (jpeg_finish_output (pData->pJPEGdinfo) != JPEG_SUSPENDED)
+          {                            /* this scan has ended */
+            pData->bJPEGscanstarted = MNG_FALSE;
+            pData->bJPEGscanending  = MNG_FALSE;
+          }
+          else
+          {
+            pData->bJPEGscanending  = MNG_TRUE;
+          }
+        }
+      }
+      while ((!jpeg_input_complete (pData->pJPEGdinfo)) &&
+             (iLines > 0) && (!pData->bJPEGscanending));
+    }
+                                       /* end of image ? */
+    if ((pData->bJPEGhasheader) && (pData->bJPEGdecostarted) &&
+        (!pData->bJPEGscanending) && (jpeg_input_complete (pData->pJPEGdinfo)) &&
+        (pData->pJPEGdinfo->input_scan_number == pData->pJPEGdinfo->output_scan_number))
+    {
+      /* jpeg_finish_decompress(...); */
+#ifdef MNG_SUPPORT_TRACE
+      MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_FINISH_DECOMPRESS)
+#endif
+      if (jpeg_finish_decompress (pData->pJPEGdinfo) == TRUE)
+      {                                /* indicate it's done */
+        pData->bJPEGhasheader   = MNG_FALSE;
+        pData->bJPEGdecostarted = MNG_FALSE;
+        pData->pJPEGcurrent     = (mng_uint8p)pData->pJPEGdinfo->src->next_input_byte;
+        pData->iJPEGbufremain   = (mng_uint32)pData->pJPEGdinfo->src->bytes_in_buffer;
+                                       /* remaining fluff is an error ! */
+        if ((pData->iJPEGbufremain > 0) || (iRemain > 0))
+          MNG_ERROR (pData, MNG_TOOMUCHJDAT);
+      }
+    }
+#endif /* MNG_INCLUDE_IJG6B */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+mng_retcode mngjpeg_decompressfree (mng_datap pData)
+{
+#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP)
+  mng_retcode iRetcode;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_IJG6B
+#ifdef MNG_USE_SETJMP
+  iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */
+  if (iRetcode != 0)                   /* got here from longjmp ? */
+    MNG_ERRORJ (pData, iRetcode);      /* then IJG-lib issued an error */
+#endif
+                                       /* free the row of JPEG-samples*/
+  MNG_FREE (pData, pData->pJPEGrow, pData->iJPEGrowlen);
+
+  /* release the JPEG decompression object */
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_JPEG_DESTROY_DECOMPRESS)
+#endif
+  jpeg_destroy_decompress (pData->pJPEGdinfo);
+
+  pData->bJPEGdecompress = MNG_FALSE;  /* indicate it's done */
+
+#endif /* MNG_INCLUDE_IJG6B */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * JPEG decompression routines (JDAA)                                     * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+mng_retcode mngjpeg_decompressinit2 (mng_datap pData)
+{
+#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP)
+  mng_retcode iRetcode;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_IJG6B
+  /* allocate and initialize a JPEG decompression object */
+  pData->pJPEGdinfo2->err = jpeg_std_error (pData->pJPEGderr2);
+
+#ifdef MNG_USE_SETJMP                  /* setup local JPEG error-routines */
+  pData->pJPEGderr2->error_exit     = mng_error_exit;
+  pData->pJPEGderr2->output_message = mng_output_message;
+
+  iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */
+  if (iRetcode != 0)                   /* got here from longjmp ? */
+    MNG_ERRORJ (pData, iRetcode);      /* then IJG-lib issued an error */
+#endif /* MNG_USE_SETJMP */
+
+  /* allocate and initialize a JPEG decompression object (continued) */
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_JPEG_CREATE_DECOMPRESS)
+#endif
+  jpeg_create_decompress (pData->pJPEGdinfo2);
+
+  pData->bJPEGdecompress2 = MNG_TRUE;  /* indicate it's initialized */
+
+  /* specify the source of the compressed data (eg, a file) */
+                                       /* no, not a file; we have buffered input */
+  pData->pJPEGdinfo2->src = pData->pJPEGdsrc2;
+                                       /* use the default handler */
+  pData->pJPEGdinfo2->src->resync_to_restart = jpeg_resync_to_restart;
+                                       /* setup local source routine & parms */
+  pData->pJPEGdinfo2->src->init_source       = mng_init_source;
+  pData->pJPEGdinfo2->src->fill_input_buffer = mng_fill_input_buffer;
+  pData->pJPEGdinfo2->src->skip_input_data   = mng_skip_input_data2;
+  pData->pJPEGdinfo2->src->term_source       = mng_term_source;
+  pData->pJPEGdinfo2->src->next_input_byte   = pData->pJPEGcurrent2;
+  pData->pJPEGdinfo2->src->bytes_in_buffer   = pData->iJPEGbufremain2;
+
+#endif /* MNG_INCLUDE_IJG6B */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+mng_retcode mngjpeg_decompressdata2 (mng_datap  pData,
+                                     mng_uint32 iRawsize,
+                                     mng_uint8p pRawdata)
+{
+  mng_retcode iRetcode;
+  mng_uint32  iRemain;
+  mng_uint8p  pWork;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_START);
+#endif
+
+#if defined (MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP)
+  iRetcode = setjmp (pData->sErrorbuf);/* initialize local JPEG error-recovery */
+  if (iRetcode != 0)                   /* got here from longjmp ? */
+    MNG_ERRORJ (pData, iRetcode);      /* then IJG-lib issued an error */
+#endif
+
+  pWork   = pRawdata;
+  iRemain = iRawsize;
+
+  if (pData->iJPEGtoskip2)             /* JPEG-lib told us to skip some more data ? */
+  {
+    if (iRemain > pData->iJPEGtoskip2) /* enough data in this buffer ? */
+    {
+      iRemain -= pData->iJPEGtoskip2;  /* skip enough to access the next byte */
+      pWork   += pData->iJPEGtoskip2;
+
+      pData->iJPEGtoskip2 = 0;         /* no more to skip then */
+    }
+    else
+    {
+      pData->iJPEGtoskip2 -= iRemain;  /* skip all data in the buffer */
+      iRemain = 0;                     /* and indicate this accordingly */
+    }
+                                       /* the skip set current-pointer to NULL ! */
+    pData->pJPEGcurrent2 = pData->pJPEGbuf2;
+  }
+
+  while (iRemain)                      /* repeat until no more input-bytes */
+  {                                    /* need to shift anything ? */
+    if ((pData->pJPEGcurrent2 > pData->pJPEGbuf2) &&
+        (pData->pJPEGcurrent2 - pData->pJPEGbuf2 + pData->iJPEGbufremain2 + iRemain > pData->iJPEGbufmax2))
+    {
+      if (pData->iJPEGbufremain2 > 0)  /* then do so */
+        MNG_COPY (pData->pJPEGbuf2, pData->pJPEGcurrent2, pData->iJPEGbufremain2);
+
+      pData->pJPEGcurrent2 = pData->pJPEGbuf2;
+    }
+                                       /* does the remaining input fit into the buffer ? */
+    if (pData->iJPEGbufremain2 + iRemain <= pData->iJPEGbufmax2)
+    {                                  /* move the lot */
+      MNG_COPY ((pData->pJPEGcurrent2 + pData->iJPEGbufremain2), pWork, iRemain);
+                                       /* adjust remaining_bytes counter */
+      pData->iJPEGbufremain2 += iRemain;
+      iRemain = 0;                     /* and indicate there's no input left */
+    }
+    else
+    {                                  /* calculate what does fit */
+      mng_uint32 iFits = pData->iJPEGbufmax2 - pData->iJPEGbufremain2;
+
+      if (iFits <= 0)                  /* no space is just bugger 'm all */
+        MNG_ERROR (pData, MNG_JPEGBUFTOOSMALL);
+                                       /* move that */
+      MNG_COPY ((pData->pJPEGcurrent2 + pData->iJPEGbufremain2), pWork, iFits);
+
+      pData->iJPEGbufremain2 += iFits; /* adjust remain_bytes counter */
+      iRemain -= iFits;                /* and the input-parms */
+      pWork   += iFits;
+    }
+
+#ifdef MNG_INCLUDE_IJG6B
+    pData->pJPEGdinfo2->src->next_input_byte = pData->pJPEGcurrent2;
+    pData->pJPEGdinfo2->src->bytes_in_buffer = pData->iJPEGbufremain2;
+
+    if (!pData->bJPEGhasheader2)       /* haven't got the header yet ? */
+    {
+      /* call jpeg_read_header() to obtain image info */
+#ifdef MNG_SUPPORT_TRACE
+      MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_READ_HEADER)
+#endif
+      if (jpeg_read_header (pData->pJPEGdinfo2, TRUE) != JPEG_SUSPENDED)
+      {                                /* indicate the header's oke */
+        pData->bJPEGhasheader2 = MNG_TRUE;
+                                       /* let's do some sanity checks ! */
+        if ((pData->pJPEGdinfo2->image_width  != pData->iDatawidth ) ||
+            (pData->pJPEGdinfo2->image_height != pData->iDataheight)    )
+          MNG_ERROR (pData, MNG_JPEGPARMSERR);
+
+        if (pData->pJPEGdinfo2->jpeg_color_space != JCS_GRAYSCALE)
+          MNG_ERROR (pData, MNG_JPEGPARMSERR);
+                                       /* indicate whether or not it's progressive */
+        pData->bJPEGprogressive2 = (mng_bool)jpeg_has_multiple_scans (pData->pJPEGdinfo2);
+
+        if (pData->bJPEGprogressive2)  /* progressive alphachannel not allowed !!! */
+          MNG_ERROR (pData, MNG_JPEGPARMSERR);
+                                       /* allocate a row of JPEG-samples */
+        if (pData->pJPEGdinfo2->jpeg_color_space == JCS_YCbCr)
+          pData->iJPEGrowlen2 = pData->pJPEGdinfo2->image_width * RGB_PIXELSIZE;
+        else
+          pData->iJPEGrowlen2 = pData->pJPEGdinfo2->image_width;
+
+        MNG_ALLOC (pData, pData->pJPEGrow2, pData->iJPEGrowlen2);
+
+        pData->iJPEGalpharow = 0;      /* quite empty up to now */
+      }
+
+      pData->pJPEGcurrent2   = (mng_uint8p)pData->pJPEGdinfo2->src->next_input_byte;
+      pData->iJPEGbufremain2 = (mng_uint32)pData->pJPEGdinfo2->src->bytes_in_buffer;
+    }
+                                       /* decompress not started ? */
+    if ((pData->bJPEGhasheader2) && (!pData->bJPEGdecostarted2))
+    {
+      /* set parameters for decompression */
+
+      if (pData->bJPEGprogressive2)    /* progressive display ? */
+        pData->pJPEGdinfo2->buffered_image = TRUE;
+
+      /* jpeg_start_decompress(...); */
+#ifdef MNG_SUPPORT_TRACE
+      MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_START_DECOMPRESS)
+#endif
+      if (jpeg_start_decompress (pData->pJPEGdinfo2) == TRUE)
+                                       /* indicate it started */
+        pData->bJPEGdecostarted2 = MNG_TRUE;
+
+      pData->pJPEGcurrent2   = (mng_uint8p)pData->pJPEGdinfo2->src->next_input_byte;
+      pData->iJPEGbufremain2 = (mng_uint32)pData->pJPEGdinfo2->src->bytes_in_buffer;
+    }
+                                       /* process some scanlines ? */
+    if ((pData->bJPEGhasheader2) && (pData->bJPEGdecostarted2) &&
+	    ((!jpeg_input_complete (pData->pJPEGdinfo2)) ||
+         (pData->pJPEGdinfo2->output_scanline < pData->pJPEGdinfo2->output_height)))
+    {
+      mng_int32 iLines;
+
+      /* for (each output pass) */
+      do
+      {                                /* address the row output buffer */
+        JSAMPROW pRow = (JSAMPROW)pData->pJPEGrow2;
+
+                                       /* init new pass ? */
+        if ((pData->bJPEGprogressive2) &&
+            ((!pData->bJPEGscanstarted2) ||
+             (pData->pJPEGdinfo2->output_scanline >= pData->pJPEGdinfo2->output_height)))
+        {
+          pData->bJPEGscanstarted2 = MNG_TRUE;
+
+          /* adjust output decompression parameters if required */
+          /* nop */
+
+          /* start a new output pass */
+#ifdef MNG_SUPPORT_TRACE
+          MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_START_OUTPUT)
+#endif
+          jpeg_start_output (pData->pJPEGdinfo2, pData->pJPEGdinfo2->input_scan_number);
+
+          pData->iJPEGrow = 0;         /* start at row 0 in the image again */
+        }
+
+        /* while (scan lines remain to be read) */
+        do
+        {
+          /*   jpeg_read_scanlines(...); */
+#ifdef MNG_SUPPORT_TRACE
+          MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_READ_SCANLINES)
+#endif
+          iLines = jpeg_read_scanlines (pData->pJPEGdinfo2, (JSAMPARRAY)&pRow, 1);
+
+          pData->pJPEGcurrent2   = (mng_uint8p)pData->pJPEGdinfo2->src->next_input_byte;
+          pData->iJPEGbufremain2 = (mng_uint32)pData->pJPEGdinfo2->src->bytes_in_buffer;
+
+          if (iLines > 0)              /* got something ? */
+          {
+            if (pData->fStorerow3)     /* store in object ? */
+            {
+              iRetcode = ((mng_storerow)pData->fStorerow3) (pData);
+
+              if (iRetcode)            /* on error bail out */
+                return iRetcode;
+
+            }
+          }
+        }
+        while ((pData->pJPEGdinfo2->output_scanline < pData->pJPEGdinfo2->output_height) &&
+               (iLines > 0));          /* until end-of-image or not enough input-data */
+
+        /* terminate output pass */
+        if ((pData->bJPEGprogressive2) &&
+            (pData->pJPEGdinfo2->output_scanline >= pData->pJPEGdinfo2->output_height))
+        {
+#ifdef MNG_SUPPORT_TRACE
+          MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_FINISH_OUTPUT)
+#endif
+          if (jpeg_finish_output (pData->pJPEGdinfo2) == JPEG_SUSPENDED)
+            jpeg_finish_output (pData->pJPEGdinfo2);
+                                       /* this scan has ended */
+          pData->bJPEGscanstarted2 = MNG_FALSE;
+        }
+      }
+      while ((!jpeg_input_complete (pData->pJPEGdinfo2)) && (iLines > 0));
+    }
+                                       /* end of image ? */
+    if ((pData->bJPEGhasheader2) && (pData->bJPEGdecostarted2) &&
+        (jpeg_input_complete (pData->pJPEGdinfo2)) &&
+        (pData->pJPEGdinfo2->input_scan_number == pData->pJPEGdinfo2->output_scan_number))
+    {
+      /* jpeg_finish_decompress(...); */
+#ifdef MNG_SUPPORT_TRACE
+      MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_FINISH_DECOMPRESS)
+#endif
+      if (jpeg_finish_decompress (pData->pJPEGdinfo2) == TRUE)
+      {                                /* indicate it's done */
+        pData->bJPEGhasheader2   = MNG_FALSE;
+        pData->bJPEGdecostarted2 = MNG_FALSE;
+        pData->pJPEGcurrent2     = (mng_uint8p)pData->pJPEGdinfo2->src->next_input_byte;
+        pData->iJPEGbufremain2   = (mng_uint32)pData->pJPEGdinfo2->src->bytes_in_buffer;
+                                       /* remaining fluff is an error ! */
+        if ((pData->iJPEGbufremain2 > 0) || (iRemain > 0))
+          MNG_ERROR (pData, MNG_TOOMUCHJDAT);
+      }
+    }
+#endif /* MNG_INCLUDE_IJG6B */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG_READ
+mng_retcode mngjpeg_decompressfree2 (mng_datap pData)
+{
+#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP)
+  mng_retcode iRetcode;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_IJG6B
+#ifdef MNG_USE_SETJMP
+  iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */
+  if (iRetcode != 0)                   /* got here from longjmp ? */
+    MNG_ERRORJ (pData, iRetcode);      /* then IJG-lib issued an error */
+#endif
+                                       /* free the row of JPEG-samples*/
+  MNG_FREE (pData, pData->pJPEGrow2, pData->iJPEGrowlen2);
+
+  /* release the JPEG decompression object */
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_JPEG_DESTROY_DECOMPRESS)
+#endif
+  jpeg_destroy_decompress (pData->pJPEGdinfo2);
+
+  pData->bJPEGdecompress2 = MNG_FALSE; /* indicate it's done */
+
+#endif /* MNG_INCLUDE_IJG6B */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_INCLUDE_JNG_READ */
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_JNG && MNG_INCLUDE_DISPLAY_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_jpeg.h b/files/Source/LibMNG/libmng_jpeg.h
new file mode 100644
index 0000000..a072af9
--- /dev/null
+++ b/files/Source/LibMNG/libmng_jpeg.h
@@ -0,0 +1,57 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_jpeg.h             copyright (c) 2000-2002 G.Juyn   * */
+/* * version   : 1.0.0                                                      * */
+/* *                                                                        * */
+/* * purpose   : JPEG library interface (definition)                        * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the JPEG library interface                   * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added support for JDAA                                   * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_jpeg_h_
+#define _libmng_jpeg_h_
+
+/* ************************************************************************** */
+
+mng_retcode mngjpeg_initialize      (mng_datap  pData);
+mng_retcode mngjpeg_cleanup         (mng_datap  pData);
+
+mng_retcode mngjpeg_decompressinit  (mng_datap  pData);
+mng_retcode mngjpeg_decompressdata  (mng_datap  pData,
+                                     mng_uint32 iRawsize,
+                                     mng_uint8p pRawdata);
+mng_retcode mngjpeg_decompressfree  (mng_datap  pData);
+
+mng_retcode mngjpeg_decompressinit2 (mng_datap  pData);
+mng_retcode mngjpeg_decompressdata2 (mng_datap  pData,
+                                     mng_uint32 iRawsize,
+                                     mng_uint8p pRawdata);
+mng_retcode mngjpeg_decompressfree2 (mng_datap  pData);
+
+/* ************************************************************************** */
+
+#endif /* _libmng_jpeg_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_memory.h b/files/Source/LibMNG/libmng_memory.h
new file mode 100644
index 0000000..b92d0c1
--- /dev/null
+++ b/files/Source/LibMNG/libmng_memory.h
@@ -0,0 +1,64 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_memory.h           copyright (c) 2000-2003 G.Juyn   * */
+/* * version   : 1.0.0                                                      * */
+/* *                                                                        * */
+/* * purpose   : Memory management (definition)                             * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of memory management functions                  * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/12/2000 - G.Juyn                                * */
+/* *             - swapped MNG_COPY parameter-names                         * */
+/* *             0.5.3 - 06/27/2000 - G.Juyn                                * */
+/* *             - changed size parameter to mng_size_t                     * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_memory_h_
+#define _libmng_memory_h_
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Generic memory manager macros                                          * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INTERNAL_MEMMNGMT
+#define MNG_ALLOC(H,P,L)  { P = calloc (1, (mng_size_t)(L)); \
+                            if (P == 0) { MNG_ERROR (H, MNG_OUTOFMEMORY) } }
+#define MNG_ALLOCX(H,P,L) { P = calloc (1, (mng_size_t)(L)); }
+#define MNG_FREE(H,P,L)   { if (P) { free (P); P = 0; } }
+#define MNG_FREEX(H,P,L)  { if (P) free (P); }
+#else
+#define MNG_ALLOC(H,P,L)  { P = H->fMemalloc ((mng_size_t)(L)); \
+                            if (P == 0) { MNG_ERROR (H, MNG_OUTOFMEMORY) } }
+#define MNG_ALLOCX(H,P,L) { P = H->fMemalloc ((mng_size_t)(L)); }
+#define MNG_FREE(H,P,L)   { if (P) { H->fMemfree (P, (mng_size_t)(L)); P = 0; } }
+#define MNG_FREEX(H,P,L)  { if (P) { H->fMemfree (P, (mng_size_t)(L)); } }
+#endif /* mng_internal_memmngmt */
+
+#define MNG_COPY(D,S,L)   { memcpy (D, S, (mng_size_t)(L)); }
+
+/* ************************************************************************** */
+
+#endif /* _libmng_memory_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_object_prc.c b/files/Source/LibMNG/libmng_object_prc.c
new file mode 100644
index 0000000..f6691ff
--- /dev/null
+++ b/files/Source/LibMNG/libmng_object_prc.c
@@ -0,0 +1,6998 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_object_prc.c       copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Object processing routines (implementation)                * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the internal object processing routines  * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/20/2000 - G.Juyn                                * */
+/* *             - fixed to support JNG objects                             * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - added support for global color-chunks in animation       * */
+/* *             - added support for global PLTE,tRNS,bKGD in animation     * */
+/* *             - added SAVE & SEEK animation objects                      * */
+/* *             0.5.2 - 05/29/2000 - G.Juyn                                * */
+/* *             - added initialization of framenr/layernr/playtime         * */
+/* *             - changed ani_object create routines not to return the     * */
+/* *               created object (wasn't necessary)                        * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added object promotion routine (PROM handling)           * */
+/* *             - added ani-object routines for delta-image processing     * */
+/* *             - added compression/filter/interlace fields to             * */
+/* *               object-buffer for delta-image processing                 * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/17/2000 - G.Juyn                                * */
+/* *             - changed support for delta-image processing               * */
+/* *             0.5.3 - 06/20/2000 - G.Juyn                                * */
+/* *             - fixed some small things (as precaution)                  * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added processing of PLTE/tRNS & color-info for           * */
+/* *               delta-images in the ani_objects chain                    * */
+/* *             0.5.3 - 06/22/2000 - G.Juyn                                * */
+/* *             - added support for PPLT chunk                             * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/07/2000 - G.Juyn                                * */
+/* *             - added support for freeze/restart/resume & go_xxxx        * */
+/* *             0.9.1 - 07/16/2000 - G.Juyn                                * */
+/* *             - fixed support for mng_display() after mng_read()         * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/29/2000 - G.Juyn                                * */
+/* *             - fixed small bugs in display processing                   * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/07/2000 - G.Juyn                                * */
+/* *             - B111300 - fixup for improved portability                 * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/10/2000 - G.Juyn                                * */
+/* *             - fixed DEFI behavior                                      * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added valid-flag to stored objects for read() / display()* */
+/* *             - added routine to discard "invalid" objects               * */
+/* *             0.9.3 - 10/18/2000 - G.Juyn                                * */
+/* *             - fixed delta-processing behavior                          * */
+/* *             0.9.3 - 10/19/2000 - G.Juyn                                * */
+/* *             - added storage for pixel-/alpha-sampledepth for delta's   * */
+/* *                                                                        * */
+/* *             0.9.4 -  1/18/2001 - G.Juyn                                * */
+/* *             - removed "old" MAGN methods 3 & 4                         * */
+/* *             - added "new" MAGN methods 3, 4 & 5                        * */
+/* *                                                                        * */
+/* *             0.9.5 -  1/22/2001 - G.Juyn                                * */
+/* *             - B129681 - fixed compiler warnings SGI/Irix               * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added optimization option for MNG-video playback         * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed PROM support                                   * */
+/* *             1.0.5 - 08/16/2002 - G.Juyn                                * */
+/* *             - completed MAGN support (16-bit functions)                * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/13/2002 - G.Juyn                                * */
+/* *             - fixed read/write of MAGN chunk                           * */
+/* *             1.0.5 - 09/15/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 09/23/2002 - G.Juyn                                * */
+/* *             - fixed reset_object_detail to clear old buffer            * */
+/* *             - added in-memory color-correction of abstract images      * */
+/* *             1.0.5 - 10/05/2002 - G.Juyn                                * */
+/* *             - fixed problem with cloned objects marked as invalid      * */
+/* *             - fixed problem cloning frozen object_buffers              * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - fixed DISC support                                       * */
+/* *             1.0.5 - 11/04/2002 - G.Juyn                                * */
+/* *             - fixed goframe/golayer/gotime processing                  * */
+/* *             1.0.5 - 11/07/2002 - G.Juyn                                * */
+/* *             - fixed magnification bug with object 0                    * */
+/* *             1.0.5 - 01/19/2003 - G.Juyn                                * */
+/* *             - B664911 - fixed buffer overflow during init              * */
+/* *                                                                        * */
+/* *             1.0.6 - 04/19/2003 - G.Juyn                                * */
+/* *             - fixed problem with infinite loops during readdisplay()   * */
+/* *             1.0.6 - 05/25/2003 - G.R-P                                 * */
+/* *             - added MNG_SKIPCHUNK_cHNK footprint optimizations         * */
+/* *             1.0.6 - 06/09/2003 - G. R-P                                * */
+/* *             - added conditionals around 8-bit magn routines            * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added conditionals around some JNG-supporting code       * */
+/* *             - removed conditionals around 8-bit magn routines          * */
+/* *             - added conditionals around delta-png and 16-bit code      * */
+/* *             1.0.6 - 07/14/2003 - G.R-P                                 * */
+/* *             - added MNG_NO_LOOP_SIGNALS_SUPPORTED conditional          * */
+/* *             1.0.6 - 07/29/2003 - G.Juyn                                * */
+/* *             - fixed invalid test in promote_imageobject                * */
+/* *             1.0.6 - 07/29/2003 - G.R-P.                                * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *             1.0.6 - 08/17/2003 - G.R-P.                                * */
+/* *             - added conditionals around MAGN chunk support             * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/21/2004 - G.Juyn                                * */
+/* *             - fixed some 64-bit platform compiler warnings             * */
+/* *                                                                        * */
+/* *             1.0.9 - 10/10/2004 - G.R-P.                                * */
+/* *             - added MNG_NO_1_2_4BIT_SUPPORT support                    * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_OBJCLEANUP                * */
+/* *             1.0.9 - 12/11/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_DISPLAYCALLS              * */
+/* *             1.0.9 - 12/31/2004 - G.R-P.                                * */
+/* *             - fixed warnings about possible uninitialized pointers     * */
+/* *             1.0.9 - 01/02/2005 - G.Juyn                                * */
+/* *             - fixing some compiler-warnings                            * */
+/* *                                                                        * */
+/* *             1.0.10 - 02/07/2005 - G.Juyn                               * */
+/* *             - fixed some compiler-warnings                             * */
+/* *             1.0.10 - 07/30/2005 - G.Juyn                               * */
+/* *             - fixed problem with CLON object during readdisplay()      * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_memory.h"
+#include "libmng_chunks.h"
+#include "libmng_objects.h"
+#include "libmng_display.h"
+#include "libmng_pixels.h"
+#include "libmng_object_prc.h"
+#include "libmng_cms.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_DISPLAY_PROCS
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Generic object routines                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_drop_invalid_objects (mng_datap pData)
+{
+  mng_objectp       pObject;
+  mng_objectp       pNext;
+  mng_cleanupobject fCleanup;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DROP_INVALID_OBJECTS, MNG_LC_START);
+#endif
+
+  pObject = pData->pFirstimgobj;       /* get first stored image-object (if any) */
+
+  while (pObject)                      /* more objects to check ? */
+  {
+    pNext = ((mng_object_headerp)pObject)->pNext;
+                                       /* invalid ? */
+    if (!((mng_imagep)pObject)->bValid)
+    {                                  /* call appropriate cleanup */
+      fCleanup = ((mng_object_headerp)pObject)->fCleanup;
+      fCleanup (pData, pObject);
+    }
+
+    pObject = pNext;                   /* neeeext */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DROP_INVALID_OBJECTS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+MNG_LOCAL mng_retcode create_obj_general (mng_datap          pData,
+                                          mng_size_t         iObjsize,
+                                          mng_cleanupobject  fCleanup,
+                                          mng_processobject  fProcess,
+                                          mng_ptr            *ppObject)
+{
+  mng_object_headerp pWork;
+
+  MNG_ALLOC (pData, pWork, iObjsize);
+
+  pWork->fCleanup = fCleanup;
+  pWork->fProcess = fProcess;
+  pWork->iObjsize = iObjsize;
+  *ppObject       = (mng_ptr)pWork;
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode mng_free_obj_general (mng_datap   pData,
+                                            mng_objectp pObject)
+{
+  MNG_FREEX (pData, pObject, ((mng_object_headerp)pObject)->iObjsize);
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Image-data-object routines                                             * */
+/* *                                                                        * */
+/* * these handle the "object buffer" as defined by the MNG specification   * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_create_imagedataobject (mng_datap      pData,
+                                        mng_bool       bConcrete,
+                                        mng_bool       bViewable,
+                                        mng_uint32     iWidth,
+                                        mng_uint32     iHeight,
+                                        mng_uint8      iBitdepth,
+                                        mng_uint8      iColortype,
+                                        mng_uint8      iCompression,
+                                        mng_uint8      iFilter,
+                                        mng_uint8      iInterlace,
+                                        mng_imagedatap *ppObject)
+{
+  mng_imagedatap pImagedata;
+  mng_uint32 iSamplesize = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_IMGDATAOBJECT, MNG_LC_START);
+#endif
+                                       /* get a buffer */
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+  {
+    mng_ptr pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_imagedata),
+                                               (mng_cleanupobject)mng_free_imagedataobject,
+                                               MNG_NULL, &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pImagedata = (mng_imagedatap)pTemp;
+  }
+#else
+  MNG_ALLOC (pData, pImagedata, sizeof (mng_imagedata));
+                                       /* fill the appropriate fields */
+  pImagedata->sHeader.fCleanup   = (mng_cleanupobject)mng_free_imagedataobject;
+  pImagedata->sHeader.fProcess   = MNG_NULL;
+#endif
+  pImagedata->iRefcount          = 1;
+  pImagedata->bFrozen            = MNG_FALSE;
+  pImagedata->bConcrete          = bConcrete;
+  pImagedata->bViewable          = bViewable;
+  pImagedata->iWidth             = iWidth;
+  pImagedata->iHeight            = iHeight;
+  pImagedata->iBitdepth          = iBitdepth;
+  pImagedata->iColortype         = iColortype;
+  pImagedata->iCompression       = iCompression;
+  pImagedata->iFilter            = iFilter;
+  pImagedata->iInterlace         = iInterlace;
+  pImagedata->bCorrected         = MNG_FALSE;
+  pImagedata->iAlphabitdepth     = 0;
+  pImagedata->iJHDRcompression   = 0;
+  pImagedata->iJHDRinterlace     = 0;
+  pImagedata->iPixelsampledepth  = iBitdepth;
+  pImagedata->iAlphasampledepth  = iBitdepth;
+                                       /* determine samplesize from color_type/bit_depth */
+  switch (iColortype)                  /* for < 8-bit samples we just reserve 8 bits */
+  {
+    case  0  : ;                       /* gray */
+    case  8  : {                       /* JPEG gray */
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (iBitdepth > 8)
+                   iSamplesize = 2;
+                 else
+#endif
+                   iSamplesize = 1;
+
+                 break;
+               }
+    case  2  : ;                       /* rgb */
+    case 10  : {                       /* JPEG rgb */
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (iBitdepth > 8)
+                   iSamplesize = 6;
+                 else
+#endif
+                   iSamplesize = 3;
+
+                 break;
+               }
+    case  3  : {                       /* indexed */
+                 iSamplesize = 1;
+                 break;
+               }
+    case  4  : ;                       /* gray+alpha */
+    case 12  : {                       /* JPEG gray+alpha */
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (iBitdepth > 8)
+                   iSamplesize = 4;
+                 else
+#endif
+                   iSamplesize = 2;
+
+                 break;
+               }
+    case  6  : ;                       /* rgb+alpha */
+    case 14  : {                       /* JPEG rgb+alpha */
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (iBitdepth > 8)
+                   iSamplesize = 8;
+                 else
+#endif
+                   iSamplesize = 4;
+
+                 break;
+               }
+  }
+                                       /* make sure we remember all this */
+  pImagedata->iSamplesize  = iSamplesize;
+  pImagedata->iRowsize     = iSamplesize * iWidth;
+  pImagedata->iImgdatasize = pImagedata->iRowsize * iHeight;
+
+  if (pImagedata->iImgdatasize)        /* need a buffer ? */
+  {                                    /* so allocate it */
+    MNG_ALLOCX (pData, pImagedata->pImgdata, pImagedata->iImgdatasize);
+
+    if (!pImagedata->pImgdata)         /* enough memory ? */
+    {
+      MNG_FREEX (pData, pImagedata, sizeof (mng_imagedata));
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+  }
+                                       /* check global stuff */
+  pImagedata->bHasGAMA           = pData->bHasglobalGAMA;
+#ifndef MNG_SKIPCHUNK_cHRM
+  pImagedata->bHasCHRM           = pData->bHasglobalCHRM;
+#endif
+  pImagedata->bHasSRGB           = pData->bHasglobalSRGB;
+#ifndef MNG_SKIPCHUNK_iCCP
+  pImagedata->bHasICCP           = pData->bHasglobalICCP;
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+  pImagedata->bHasBKGD           = pData->bHasglobalBKGD;
+#endif
+
+  if (pData->bHasglobalGAMA)           /* global gAMA present ? */
+    pImagedata->iGamma           = pData->iGlobalGamma;
+
+#ifndef MNG_SKIPCHUNK_cHRM
+  if (pData->bHasglobalCHRM)           /* global cHRM present ? */
+  {
+    pImagedata->iWhitepointx     = pData->iGlobalWhitepointx;
+    pImagedata->iWhitepointy     = pData->iGlobalWhitepointy;
+    pImagedata->iPrimaryredx     = pData->iGlobalPrimaryredx;
+    pImagedata->iPrimaryredy     = pData->iGlobalPrimaryredy;
+    pImagedata->iPrimarygreenx   = pData->iGlobalPrimarygreenx;
+    pImagedata->iPrimarygreeny   = pData->iGlobalPrimarygreeny;
+    pImagedata->iPrimarybluex    = pData->iGlobalPrimarybluex;
+    pImagedata->iPrimarybluey    = pData->iGlobalPrimarybluey;
+  }
+#endif
+
+  if (pData->bHasglobalSRGB)           /* glbal sRGB present ? */
+    pImagedata->iRenderingintent = pData->iGlobalRendintent;
+
+#ifndef MNG_SKIPCHUNK_iCCP
+  if (pData->bHasglobalICCP)           /* glbal iCCP present ? */
+  {
+    pImagedata->iProfilesize     = pData->iGlobalProfilesize;
+
+    if (pImagedata->iProfilesize)
+    {
+      MNG_ALLOCX (pData, pImagedata->pProfile, pImagedata->iProfilesize);
+
+      if (!pImagedata->pProfile)       /* enough memory ? */
+      {
+        MNG_FREEX (pData, pImagedata->pImgdata, pImagedata->iImgdatasize);
+        MNG_FREEX (pData, pImagedata, sizeof (mng_imagedata));
+        MNG_ERROR (pData, MNG_OUTOFMEMORY);
+      }
+
+      MNG_COPY  (pImagedata->pProfile, pData->pGlobalProfile, pImagedata->iProfilesize);
+    }
+  }
+#endif
+
+#ifndef MNG_SKIPCHUNK_bKGD
+  if (pData->bHasglobalBKGD)           /* global bKGD present ? */
+  {
+    pImagedata->iBKGDred         = pData->iGlobalBKGDred;
+    pImagedata->iBKGDgreen       = pData->iGlobalBKGDgreen;
+    pImagedata->iBKGDblue        = pData->iGlobalBKGDblue;
+  }
+#endif
+
+  *ppObject = pImagedata;              /* return it */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_IMGDATAOBJECT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_imagedataobject   (mng_datap      pData,
+                                        mng_imagedatap pImagedata)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IMGDATAOBJECT, MNG_LC_START);
+#endif
+
+  if (pImagedata->iRefcount)           /* decrease reference count */
+    pImagedata->iRefcount--;
+
+  if (!pImagedata->iRefcount)          /* reached zero ? */
+  {
+#ifndef MNG_SKIPCHUNK_iCCP
+    if (pImagedata->iProfilesize)      /* stored an iCCP profile ? */
+      MNG_FREEX (pData, pImagedata->pProfile, pImagedata->iProfilesize);
+#endif
+    if (pImagedata->iImgdatasize)      /* sample-buffer present ? */
+      MNG_FREEX (pData, pImagedata->pImgdata, pImagedata->iImgdatasize);
+                                       /* drop the buffer */
+    MNG_FREEX (pData, pImagedata, sizeof (mng_imagedata));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IMGDATAOBJECT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_clone_imagedataobject  (mng_datap      pData,
+                                        mng_bool       bConcrete,
+                                        mng_imagedatap pSource,
+                                        mng_imagedatap *ppClone)
+{
+  mng_imagedatap pNewdata;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLONE_IMGDATAOBJECT, MNG_LC_START);
+#endif
+                                       /* get a buffer */
+  MNG_ALLOC (pData, pNewdata, sizeof (mng_imagedata));
+                                       /* blatently copy the original buffer */
+  MNG_COPY (pNewdata, pSource, sizeof (mng_imagedata));
+
+  pNewdata->iRefcount = 1;             /* only the reference count */
+  pNewdata->bConcrete = bConcrete;     /* and concrete-flag are different */
+  pNewdata->bFrozen   = MNG_FALSE;
+
+  if (pNewdata->iImgdatasize)          /* sample buffer present ? */
+  {
+    MNG_ALLOCX (pData, pNewdata->pImgdata, pNewdata->iImgdatasize);
+
+    if (!pNewdata->pImgdata)           /* not enough memory ? */
+    {
+      MNG_FREEX (pData, pNewdata, sizeof (mng_imagedata));
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+                                       /* make a copy */
+    MNG_COPY (pNewdata->pImgdata, pSource->pImgdata, pNewdata->iImgdatasize);
+  }
+
+#ifndef MNG_SKIPCHUNK_iCCP
+  if (pNewdata->iProfilesize)          /* iCCP profile present ? */
+  {
+    MNG_ALLOCX (pData, pNewdata->pProfile, pNewdata->iProfilesize);
+
+    if (!pNewdata->pProfile)           /* enough memory ? */
+    {
+      MNG_FREEX (pData, pNewdata, sizeof (mng_imagedata));
+      MNG_ERROR (pData, MNG_OUTOFMEMORY);
+    }
+                                       /* make a copy */
+    MNG_COPY (pNewdata->pProfile, pSource->pProfile, pNewdata->iProfilesize);
+  }
+#endif
+
+  *ppClone = pNewdata;                 /* return the clone */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLONE_IMGDATAOBJECT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Image-object routines                                                  * */
+/* *                                                                        * */
+/* * these handle the "object" as defined by the MNG specification          * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_create_imageobject (mng_datap  pData,
+                                    mng_uint16 iId,
+                                    mng_bool   bConcrete,
+                                    mng_bool   bVisible,
+                                    mng_bool   bViewable,
+                                    mng_uint32 iWidth,
+                                    mng_uint32 iHeight,
+                                    mng_uint8  iBitdepth,
+                                    mng_uint8  iColortype,
+                                    mng_uint8  iCompression,
+                                    mng_uint8  iFilter,
+                                    mng_uint8  iInterlace,
+                                    mng_int32  iPosx,
+                                    mng_int32  iPosy,
+                                    mng_bool   bClipped,
+                                    mng_int32  iClipl,
+                                    mng_int32  iClipr,
+                                    mng_int32  iClipt,
+                                    mng_int32  iClipb,
+                                    mng_imagep *ppObject)
+{
+  mng_imagep     pImage;
+  mng_imagep     pPrev, pNext;
+  mng_retcode    iRetcode;
+  mng_imagedatap pImgbuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_IMGOBJECT, MNG_LC_START);
+#endif
+                                       /* get a buffer */
+  MNG_ALLOC (pData, pImage, sizeof (mng_image));
+                                       /* now get a new "object buffer" */
+  iRetcode = mng_create_imagedataobject (pData, bConcrete, bViewable,
+                                         iWidth, iHeight, iBitdepth, iColortype,
+                                         iCompression, iFilter, iInterlace,
+                                         &pImgbuf);
+
+  if (iRetcode)                        /* on error bail out */
+  {
+    MNG_FREEX (pData, pImage, sizeof (mng_image));
+    return iRetcode;
+  }
+                                       /* fill the appropriate fields */
+  pImage->sHeader.fCleanup = (mng_cleanupobject)mng_free_imageobject;
+  pImage->sHeader.fProcess = MNG_NULL;
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+  pImage->sHeader.iObjsize = sizeof (mng_image);
+#endif
+  pImage->iId              = iId;
+  pImage->bFrozen          = MNG_FALSE;
+  pImage->bVisible         = bVisible;
+  pImage->bViewable        = bViewable;
+  pImage->bValid           = (mng_bool)((pData->bDisplaying) &&
+                                        ((pData->bRunning) || (pData->bSearching)) &&
+                                        (!pData->bFreezing));
+  pImage->iPosx            = iPosx;
+  pImage->iPosy            = iPosy;
+  pImage->bClipped         = bClipped;
+  pImage->iClipl           = iClipl;
+  pImage->iClipr           = iClipr;
+  pImage->iClipt           = iClipt;
+  pImage->iClipb           = iClipb;
+#ifndef MNG_SKIPCHUNK_MAGN
+  pImage->iMAGN_MethodX    = 0;
+  pImage->iMAGN_MethodY    = 0;
+  pImage->iMAGN_MX         = 0;
+  pImage->iMAGN_MY         = 0;
+  pImage->iMAGN_ML         = 0;
+  pImage->iMAGN_MR         = 0;
+  pImage->iMAGN_MT         = 0;
+  pImage->iMAGN_MB         = 0;
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+  pImage->iPastx           = 0;
+  pImage->iPasty           = 0;
+#endif
+  pImage->pImgbuf          = pImgbuf;
+
+  if (iId)                             /* only if not object 0 ! */
+  {                                    /* find previous lower object-id */
+    pPrev = (mng_imagep)pData->pLastimgobj;
+
+    while ((pPrev) && (pPrev->iId > iId))
+      pPrev = (mng_imagep)pPrev->sHeader.pPrev;
+
+    if (pPrev)                         /* found it ? */
+    {
+      pImage->sHeader.pPrev = pPrev;   /* than link it in place */
+      pImage->sHeader.pNext = pPrev->sHeader.pNext;
+      pPrev->sHeader.pNext  = pImage;
+    }
+    else                               /* if not found, it becomes the first ! */
+    {
+      pImage->sHeader.pNext = pData->pFirstimgobj;
+      pData->pFirstimgobj   = pImage;
+    }
+
+    pNext                   = (mng_imagep)pImage->sHeader.pNext;
+
+    if (pNext)
+      pNext->sHeader.pPrev  = pImage;
+    else
+      pData->pLastimgobj    = pImage;
+    
+  }  
+
+  *ppObject = pImage;                  /* and return the new buffer */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_IMGOBJECT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* okido */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_imageobject (mng_datap  pData,
+                                  mng_imagep pImage)
+{
+  mng_retcode    iRetcode;
+  mng_imagep     pPrev   = pImage->sHeader.pPrev;
+  mng_imagep     pNext   = pImage->sHeader.pNext;
+  mng_imagedatap pImgbuf = pImage->pImgbuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IMGOBJECT, MNG_LC_START);
+#endif
+
+  if (pImage->iId)                     /* not for object 0 */
+  {
+    if (pPrev)                         /* unlink from the list first ! */
+      pPrev->sHeader.pNext = pImage->sHeader.pNext;
+    else
+      pData->pFirstimgobj  = pImage->sHeader.pNext;
+
+    if (pNext)
+      pNext->sHeader.pPrev = pImage->sHeader.pPrev;
+    else
+      pData->pLastimgobj   = pImage->sHeader.pPrev;
+
+  }
+                                       /* unlink the image-data buffer */
+  iRetcode = mng_free_imagedataobject (pData, pImgbuf);
+                                       /* drop its own buffer */
+  MNG_FREEX (pData, pImage, sizeof (mng_image));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_IMGOBJECT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+mng_imagep mng_find_imageobject (mng_datap  pData,
+                                 mng_uint16 iId)
+{
+  mng_imagep pImage = (mng_imagep)pData->pFirstimgobj;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (pData, MNG_FN_FIND_IMGOBJECT, MNG_LC_START);
+#endif
+                                       /* look up the right id */
+  while ((pImage) && (pImage->iId != iId))
+    pImage = (mng_imagep)pImage->sHeader.pNext;
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+  if ((!pImage) && (pData->eImagetype == mng_it_mpng))
+    pImage = pData->pObjzero;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (pData, MNG_FN_FIND_IMGOBJECT, MNG_LC_END);
+#endif
+
+  return pImage;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_clone_imageobject (mng_datap  pData,
+                                   mng_uint16 iId,
+                                   mng_bool   bPartial,
+                                   mng_bool   bVisible,
+                                   mng_bool   bAbstract,
+                                   mng_bool   bHasloca,
+                                   mng_uint8  iLocationtype,
+                                   mng_int32  iLocationx,
+                                   mng_int32  iLocationy,
+                                   mng_imagep pSource,
+                                   mng_imagep *ppClone)
+{
+  mng_imagep     pNew;
+  mng_imagep     pPrev, pNext;
+  mng_retcode    iRetcode;
+  mng_imagedatap pImgbuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLONE_IMGOBJECT, MNG_LC_START);
+#endif
+
+#ifndef MNG_SKIPCHUNK_MAGN
+  if ((pSource->iId) &&                /* needs magnification ? */
+      ((pSource->iMAGN_MethodX) || (pSource->iMAGN_MethodY)))
+  {
+    iRetcode = mng_magnify_imageobject (pData, pSource);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif
+                                       /* get a buffer */
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+  {
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_image),
+                                               (mng_cleanupobject)mng_free_imageobject,
+                                               MNG_NULL, &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pNew = (mng_imagep)pTemp;
+  }
+#else
+  MNG_ALLOC (pData, pNew, sizeof (mng_image));
+                                       /* fill or copy the appropriate fields */
+  pNew->sHeader.fCleanup = (mng_cleanupobject)mng_free_imageobject;
+  pNew->sHeader.fProcess = MNG_NULL;
+#endif
+  pNew->iId              = iId;
+  pNew->bFrozen          = MNG_FALSE;
+  pNew->bVisible         = bVisible;
+  pNew->bViewable        = pSource->bViewable;
+  pNew->bValid           = MNG_TRUE;
+
+  if (bHasloca)                        /* location info available ? */
+  {
+    if (iLocationtype == 0)            /* absolute position ? */
+    {
+      pNew->iPosx        = iLocationx;
+      pNew->iPosy        = iLocationy;
+    }
+    else                               /* relative */
+    {
+      pNew->iPosx        = pSource->iPosx + iLocationx;
+      pNew->iPosy        = pSource->iPosy + iLocationy;
+    }
+  }
+  else                                 /* copy from source */
+  {
+    pNew->iPosx          = pSource->iPosx;
+    pNew->iPosy          = pSource->iPosy;
+  }
+                                       /* copy clipping info */
+  pNew->bClipped         = pSource->bClipped;
+  pNew->iClipl           = pSource->iClipl;
+  pNew->iClipr           = pSource->iClipr;
+  pNew->iClipt           = pSource->iClipt;
+  pNew->iClipb           = pSource->iClipb;
+#ifndef MNG_SKIPCHUNK_MAGN
+                                       /* copy magnification info */
+/*  pNew->iMAGN_MethodX    = pSource->iMAGN_MethodX;     LET'S NOT !!!!!!
+  pNew->iMAGN_MethodY    = pSource->iMAGN_MethodY;
+  pNew->iMAGN_MX         = pSource->iMAGN_MX;
+  pNew->iMAGN_MY         = pSource->iMAGN_MY;
+  pNew->iMAGN_ML         = pSource->iMAGN_ML;
+  pNew->iMAGN_MR         = pSource->iMAGN_MR;
+  pNew->iMAGN_MT         = pSource->iMAGN_MT;
+  pNew->iMAGN_MB         = pSource->iMAGN_MB; */
+#endif
+
+#ifndef MNG_SKIPCHUNK_PAST
+  pNew->iPastx           = 0;          /* initialize PAST info */
+  pNew->iPasty           = 0;
+#endif
+
+  if (iId)                             /* not for object 0 */
+  {                                    /* find previous lower object-id */
+    pPrev = (mng_imagep)pData->pLastimgobj;
+    while ((pPrev) && (pPrev->iId > iId))
+      pPrev = (mng_imagep)pPrev->sHeader.pPrev;
+
+    if (pPrev)                         /* found it ? */
+    {
+      pNew->sHeader.pPrev  = pPrev;    /* than link it in place */
+      pNew->sHeader.pNext  = pPrev->sHeader.pNext;
+      pPrev->sHeader.pNext = pNew;
+    }
+    else                               /* if not found, it becomes the first ! */
+    {
+      pNew->sHeader.pNext  = pData->pFirstimgobj;
+      pData->pFirstimgobj  = pNew;
+    }
+
+    pNext                  = (mng_imagep)pNew->sHeader.pNext;
+
+    if (pNext)
+      pNext->sHeader.pPrev = pNew;
+    else
+      pData->pLastimgobj   = pNew;
+
+  }
+
+  if (bPartial)                        /* partial clone ? */
+  {
+    pNew->pImgbuf = pSource->pImgbuf;  /* use the same object buffer */
+    pNew->pImgbuf->iRefcount++;        /* and increase the reference count */
+  }
+  else                                 /* create a full clone ! */
+  {
+    mng_bool bConcrete = MNG_FALSE;    /* it's abstract by default (?) */
+
+    if (!bAbstract)                    /* determine concreteness from source ? */
+      bConcrete = pSource->pImgbuf->bConcrete;
+                                       /* create a full clone ! */
+    iRetcode = mng_clone_imagedataobject (pData, bConcrete, pSource->pImgbuf, &pImgbuf);
+
+    if (iRetcode)                      /* on error bail out */
+    {
+      MNG_FREEX (pData, pNew, sizeof (mng_image));
+      return iRetcode;
+    }
+
+    pNew->pImgbuf = pImgbuf;           /* and remember it */
+  }
+
+  *ppClone = pNew;                     /* return it */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLONE_IMGOBJECT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_renum_imageobject (mng_datap  pData,
+                                   mng_imagep pSource,
+                                   mng_uint16 iId,
+                                   mng_bool   bVisible,
+                                   mng_bool   bAbstract,
+                                   mng_bool   bHasloca,
+                                   mng_uint8  iLocationtype,
+                                   mng_int32  iLocationx,
+                                   mng_int32  iLocationy)
+{
+  mng_imagep pPrev, pNext;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RENUM_IMGOBJECT, MNG_LC_START);
+#endif
+
+  pSource->bVisible  = bVisible;       /* store the new visibility */
+
+  if (bHasloca)                        /* location info available ? */
+  {
+    if (iLocationtype == 0)            /* absolute position ? */
+    {
+      pSource->iPosx = iLocationx;
+      pSource->iPosy = iLocationy;
+    }
+    else                               /* relative */
+    {
+      pSource->iPosx = pSource->iPosx + iLocationx;
+      pSource->iPosy = pSource->iPosy + iLocationy;
+    }
+  }
+
+  if (iId)                             /* not for object 0 */
+  {                                    /* find previous lower object-id */
+    pPrev = (mng_imagep)pData->pLastimgobj;
+    while ((pPrev) && (pPrev->iId > iId))
+      pPrev = (mng_imagep)pPrev->sHeader.pPrev;
+                                       /* different from current ? */
+    if (pPrev != (mng_imagep)pSource->sHeader.pPrev)
+    {
+      if (pSource->sHeader.pPrev)      /* unlink from current position !! */
+        ((mng_imagep)pSource->sHeader.pPrev)->sHeader.pNext = pSource->sHeader.pNext;
+      else
+        pData->pFirstimgobj                                 = pSource->sHeader.pNext;
+
+      if (pSource->sHeader.pNext)
+        ((mng_imagep)pSource->sHeader.pNext)->sHeader.pPrev = pSource->sHeader.pPrev;
+      else
+        pData->pLastimgobj                                  = pSource->sHeader.pPrev;
+
+      if (pPrev)                       /* found the previous ? */
+      {                                /* than link it in place */
+        pSource->sHeader.pPrev = pPrev;
+        pSource->sHeader.pNext = pPrev->sHeader.pNext;
+        pPrev->sHeader.pNext   = pSource;
+      }
+      else                             /* if not found, it becomes the first ! */
+      {
+        pSource->sHeader.pNext = pData->pFirstimgobj;
+        pData->pFirstimgobj    = pSource;
+      }
+
+      pNext                    = (mng_imagep)pSource->sHeader.pNext;
+
+      if (pNext)
+        pNext->sHeader.pPrev   = pSource;
+      else
+        pData->pLastimgobj     = pSource;
+
+    }
+  }
+
+  pSource->iId = iId;                  /* now set the new id! */
+
+  if (bAbstract)                       /* force it to abstract ? */
+    pSource->pImgbuf->bConcrete = MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RENUM_IMGOBJECT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_reset_object_details (mng_datap  pData,
+                                      mng_imagep pImage,
+                                      mng_uint32 iWidth,
+                                      mng_uint32 iHeight,
+                                      mng_uint8  iBitdepth,
+                                      mng_uint8  iColortype,
+                                      mng_uint8  iCompression,
+                                      mng_uint8  iFilter,
+                                      mng_uint8  iInterlace,
+                                      mng_bool   bResetall)
+{
+  mng_imagedatap pBuf  = pImage->pImgbuf;
+  mng_uint32     iSamplesize = 0;
+  mng_uint32     iRowsize;
+  mng_uint32     iImgdatasize;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESET_OBJECTDETAILS, MNG_LC_START);
+#endif
+
+  pBuf->iWidth         = iWidth;       /* set buffer characteristics */
+  pBuf->iHeight        = iHeight;
+  pBuf->iBitdepth      = iBitdepth;
+  pBuf->iColortype     = iColortype;
+  pBuf->iCompression   = iCompression;
+  pBuf->iFilter        = iFilter;
+  pBuf->iInterlace     = iInterlace;
+  pBuf->bCorrected     = MNG_FALSE;
+  pBuf->iAlphabitdepth = 0;
+                                       /* determine samplesize from color_type/bit_depth */
+  switch (iColortype)                  /* for < 8-bit samples we just reserve 8 bits */
+  {
+    case  0  : ;                       /* gray */
+    case  8  : {                       /* JPEG gray */
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (iBitdepth > 8)
+                   iSamplesize = 2;
+                 else
+#endif
+                   iSamplesize = 1;
+
+                 break;
+               }
+    case  2  : ;                       /* rgb */
+    case 10  : {                       /* JPEG rgb */
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (iBitdepth > 8)
+                   iSamplesize = 6;
+                 else
+#endif
+                   iSamplesize = 3;
+
+                 break;
+               }
+    case  3  : {                       /* indexed */
+                 iSamplesize = 1;
+                 break;
+               }
+    case  4  : ;                       /* gray+alpha */
+    case 12  : {                       /* JPEG gray+alpha */
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (iBitdepth > 8)
+                   iSamplesize = 4;
+                 else
+#endif
+                   iSamplesize = 2;
+
+                 break;
+               }
+    case  6  : ;                       /* rgb+alpha */
+    case 14  : {                       /* JPEG rgb+alpha */
+#ifndef MNG_NO_16BIT_SUPPORT
+                 if (iBitdepth > 8)
+                   iSamplesize = 8;
+                 else
+#endif
+                   iSamplesize = 4;
+
+                 break;
+               }
+  }
+
+  iRowsize     = iSamplesize * iWidth;
+  iImgdatasize = iRowsize    * iHeight;
+                                       /* buffer size changed ? */
+  if (iImgdatasize != pBuf->iImgdatasize)
+  {                                    /* drop the old one */
+    MNG_FREE (pData, pBuf->pImgdata, pBuf->iImgdatasize);
+
+    if (iImgdatasize)                  /* allocate new sample-buffer ? */
+      MNG_ALLOC (pData, pBuf->pImgdata, iImgdatasize);
+  }
+  else
+  {
+    if (iImgdatasize)                  /* clear old buffer */
+    {
+      mng_uint8p pTemp = pBuf->pImgdata;
+      mng_uint32 iX;
+      
+      for (iX = 0; iX < (iImgdatasize & (mng_uint32)(~3L)); iX += 4)
+      {
+        *((mng_uint32p)pTemp) = 0x00000000l;
+        pTemp += 4;
+      }
+
+      while (pTemp < (pBuf->pImgdata + iImgdatasize))
+      {
+        *pTemp = 0;
+        pTemp++;
+      }
+    }
+  }
+
+  pBuf->iSamplesize  = iSamplesize;    /* remember new sizes */
+  pBuf->iRowsize     = iRowsize;
+  pBuf->iImgdatasize = iImgdatasize;
+
+  if (!pBuf->iPixelsampledepth)        /* set delta sampledepths if empty */
+    pBuf->iPixelsampledepth = iBitdepth;
+  if (!pBuf->iAlphasampledepth)
+    pBuf->iAlphasampledepth = iBitdepth;
+                                       /* dimension set and clipping not ? */
+  if ((iWidth) && (iHeight) && (!pImage->bClipped))
+  {
+    pImage->iClipl   = 0;              /* set clipping to dimension by default */
+    pImage->iClipr   = iWidth;
+    pImage->iClipt   = 0;
+    pImage->iClipb   = iHeight;
+  }
+
+#ifndef MNG_SKIPCHUNK_MAGN
+  if (pImage->iId)                     /* reset magnification info ? */
+  {
+    pImage->iMAGN_MethodX = 0;
+    pImage->iMAGN_MethodY = 0;
+    pImage->iMAGN_MX      = 0;
+    pImage->iMAGN_MY      = 0;
+    pImage->iMAGN_ML      = 0;
+    pImage->iMAGN_MR      = 0;
+    pImage->iMAGN_MT      = 0;
+    pImage->iMAGN_MB      = 0;
+  }
+#endif
+
+  if (bResetall)                       /* reset the other characteristics ? */
+  {
+#ifndef MNG_SKIPCHUNK_PAST
+    pImage->iPastx = 0;
+    pImage->iPasty = 0;
+#endif
+
+    pBuf->bHasPLTE = MNG_FALSE;
+    pBuf->bHasTRNS = MNG_FALSE;
+    pBuf->bHasGAMA = pData->bHasglobalGAMA;
+#ifndef MNG_SKIPCHUNK_cHRM
+    pBuf->bHasCHRM = pData->bHasglobalCHRM;
+#endif
+    pBuf->bHasSRGB = pData->bHasglobalSRGB;
+#ifndef MNG_SKIPCHUNK_iCCP
+    pBuf->bHasICCP = pData->bHasglobalICCP;
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+    pBuf->bHasBKGD = pData->bHasglobalBKGD;
+#endif
+
+#ifndef MNG_SKIPCHUNK_iCCP
+    if (pBuf->iProfilesize)            /* drop possibly old ICC profile */
+    {
+      MNG_FREE (pData, pBuf->pProfile, pBuf->iProfilesize);
+      pBuf->iProfilesize     = 0;
+    }  
+#endif
+
+    if (pData->bHasglobalGAMA)         /* global gAMA present ? */
+      pBuf->iGamma           = pData->iGlobalGamma;
+
+#ifndef MNG_SKIPCHUNK_cHRM
+    if (pData->bHasglobalCHRM)         /* global cHRM present ? */
+    {
+      pBuf->iWhitepointx     = pData->iGlobalWhitepointx;
+      pBuf->iWhitepointy     = pData->iGlobalWhitepointy;
+      pBuf->iPrimaryredx     = pData->iGlobalPrimaryredx;
+      pBuf->iPrimaryredy     = pData->iGlobalPrimaryredy;
+      pBuf->iPrimarygreenx   = pData->iGlobalPrimarygreenx;
+      pBuf->iPrimarygreeny   = pData->iGlobalPrimarygreeny;
+      pBuf->iPrimarybluex    = pData->iGlobalPrimarybluex;
+      pBuf->iPrimarybluey    = pData->iGlobalPrimarybluey;
+    }
+#endif
+
+    if (pData->bHasglobalSRGB)           /* global sRGB present ? */
+      pBuf->iRenderingintent = pData->iGlobalRendintent;
+
+#ifndef MNG_SKIPCHUNK_iCCP
+    if (pData->bHasglobalICCP)           /* global iCCP present ? */
+    {
+      if (pData->iGlobalProfilesize)
+      {
+        MNG_ALLOC (pData, pBuf->pProfile, pData->iGlobalProfilesize);
+        MNG_COPY  (pBuf->pProfile, pData->pGlobalProfile, pData->iGlobalProfilesize);
+      }
+
+      pBuf->iProfilesize     = pData->iGlobalProfilesize;
+    }
+#endif
+
+#ifndef MNG_SKIPCHUNK_bKGD
+    if (pData->bHasglobalBKGD)           /* global bKGD present ? */
+    {
+      pBuf->iBKGDred         = pData->iGlobalBKGDred;
+      pBuf->iBKGDgreen       = pData->iGlobalBKGDgreen;
+      pBuf->iBKGDblue        = pData->iGlobalBKGDblue;
+    }
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESET_OBJECTDETAILS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#if !defined(MNG_NO_DELTA_PNG) || !defined(MNG_SKIPCHUNK_PAST) || !defined(MNG_SKIPCHUNK_MAGN)
+mng_retcode mng_promote_imageobject (mng_datap  pData,
+                                     mng_imagep pImage,
+                                     mng_uint8  iBitdepth,
+                                     mng_uint8  iColortype,
+                                     mng_uint8  iFilltype)
+{
+  mng_retcode    iRetcode       = MNG_NOERROR;
+  mng_imagedatap pBuf           = pImage->pImgbuf;
+  mng_uint32     iW             = pBuf->iWidth;
+  mng_uint32     iH             = pBuf->iHeight;
+  mng_uint8p     pNewbuf;
+  mng_uint32     iNewbufsize;
+  mng_uint32     iNewrowsize;
+  mng_uint32     iNewsamplesize = pBuf->iSamplesize;
+  mng_uint32     iY;
+  mng_uint8      iTempdepth;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IMGOBJECT, MNG_LC_START);
+#endif
+
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+  if (iBitdepth < 8)
+    iBitdepth=8;
+  if (pBuf->iBitdepth < 8)
+    pBuf->iBitdepth=8;
+#endif
+#ifdef MNG_NO_16BIT_SUPPORT
+  if (iBitdepth > 8)
+    iBitdepth=8;
+  if (pBuf->iBitdepth > 8)
+    pBuf->iBitdepth=8;
+#endif
+
+  pData->fPromoterow    = MNG_NULL;    /* init promotion fields */
+  pData->fPromBitdepth  = MNG_NULL;
+  pData->iPromColortype = iColortype;
+  pData->iPromBitdepth  = iBitdepth;
+  pData->iPromFilltype  = iFilltype;
+
+  if (iBitdepth != pBuf->iBitdepth)    /* determine bitdepth promotion */
+  {
+    if (pBuf->iColortype == MNG_COLORTYPE_INDEXED)
+      iTempdepth = 8;
+    else
+      iTempdepth = pBuf->iBitdepth;
+
+#ifndef MNG_NO_DELTA_PNG
+    if (iFilltype == MNG_FILLMETHOD_ZEROFILL)
+    {
+      switch (iTempdepth)
+      {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+        case 1 : {
+                   switch (iBitdepth)
+                   {
+                     case  2 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_1_2;  break; }
+                     case  4 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_1_4;  break; }
+                     case  8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_1_8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_1_16; break; }
+#endif
+                   }
+                   break;
+                 }
+        case 2 : {
+                   switch (iBitdepth)
+                   {
+                     case  4 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_2_4;  break; }
+                     case  8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_2_8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_2_16; break; }
+#endif
+                   }
+                   break;
+                 }
+        case 4 : {
+                   switch (iBitdepth)
+                   {
+                     case  8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_4_8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_4_16; break; }
+#endif
+                   }
+                   break;
+                 }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+        case 8 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                   if (iBitdepth == 16)
+                     pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_8_16;
+#endif
+                   break;
+                 }
+      }
+    }
+    else
+#endif
+    {
+      switch (iTempdepth)
+      {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+        case 1 : {
+                   switch (iBitdepth)
+                   {
+                     case  2 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_1_2;  break; }
+                     case  4 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_1_4;  break; }
+                     case  8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_1_8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_1_16; break; }
+#endif
+                   }
+                   break;
+                 }
+        case 2 : {
+                   switch (iBitdepth)
+                   {
+                     case  4 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_2_4;  break; }
+                     case  8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_2_8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_2_16; break; }
+#endif
+                   }
+                   break;
+                 }
+        case 4 : {
+                   switch (iBitdepth)
+                   {
+                     case  8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_4_8;  break; }
+#ifndef MNG_NO_16BIT_SUPPORT
+                     case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_4_16; break; }
+#endif
+                   }
+                   break;
+                 }
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+        case 8 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+                   if (iBitdepth == 16)
+                     pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_8_16;
+#endif
+                   break;
+                 }
+      }
+    }
+  }
+                                       /* g -> g */
+  if ((pBuf->iColortype == MNG_COLORTYPE_GRAY) &&
+      (iColortype == MNG_COLORTYPE_GRAY))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_g16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_g8;
+    }
+
+    iNewsamplesize = 1;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 2;
+#endif
+  }
+  else                                 /* g -> ga */
+  if ((pBuf->iColortype == MNG_COLORTYPE_GRAY) &&
+      (iColortype == MNG_COLORTYPE_GRAYA))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_ga16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_ga8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_g16_ga16;
+#endif
+
+    iNewsamplesize = 2;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 4;
+#endif
+  }
+  else                                 /* g -> rgb */
+  if ((pBuf->iColortype == MNG_COLORTYPE_GRAY) &&
+      (iColortype == MNG_COLORTYPE_RGB))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_rgb16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_rgb8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_g16_rgb16;
+#endif
+
+    iNewsamplesize = 3;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 6;
+#endif
+  }
+  else                                 /* g -> rgba */
+  if ((pBuf->iColortype == MNG_COLORTYPE_GRAY) &&
+      (iColortype == MNG_COLORTYPE_RGBA))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_rgba16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_rgba8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_g16_rgba16;
+#endif
+
+    iNewsamplesize = 4;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 8;
+#endif
+  }
+  else                                 /* ga -> ga */
+  if ((pBuf->iColortype == MNG_COLORTYPE_GRAYA) &&
+      (iColortype == MNG_COLORTYPE_GRAYA))
+  {
+    iNewsamplesize = 2;
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_ga8_ga16;
+    if (iBitdepth == 16)
+      iNewsamplesize = 4;
+#endif
+  }
+  else                                 /* ga -> rgba */
+  if ((pBuf->iColortype == MNG_COLORTYPE_GRAYA) &&
+      (iColortype == MNG_COLORTYPE_RGBA))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_ga8_rgba16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_ga8_rgba8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_ga16_rgba16;
+#endif
+
+    iNewsamplesize = 4;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 8;
+#endif
+  }
+  else                                 /* rgb -> rgb */
+  if ((pBuf->iColortype == MNG_COLORTYPE_RGB) &&
+      (iColortype == MNG_COLORTYPE_RGB))
+  {
+    iNewsamplesize = 3;
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgb16;
+    if (iBitdepth == 16)
+      iNewsamplesize = 6;
+#endif
+  }
+  else                                 /* rgb -> rgba */
+  if ((pBuf->iColortype == MNG_COLORTYPE_RGB) &&
+      (iColortype == MNG_COLORTYPE_RGBA))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgba16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgba8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_rgb16_rgba16;
+#endif
+
+    iNewsamplesize = 4;
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 8;
+#endif
+  }
+  else                                 /* indexed -> rgb */
+  if ((pBuf->iColortype == MNG_COLORTYPE_INDEXED) &&
+      (iColortype == MNG_COLORTYPE_RGB))
+  {
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)
+      pData->fPromoterow = (mng_fptr)mng_promote_idx8_rgb16;
+    else
+#endif
+      pData->fPromoterow = (mng_fptr)mng_promote_idx8_rgb8;
+
+    iNewsamplesize = 3;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 6;
+#endif
+  }
+  else                                 /* indexed -> rgba */
+  if ((pBuf->iColortype == MNG_COLORTYPE_INDEXED) &&
+      (iColortype == MNG_COLORTYPE_RGBA))
+  {
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)
+      pData->fPromoterow = (mng_fptr)mng_promote_idx8_rgba16;
+    else
+#endif
+      pData->fPromoterow = (mng_fptr)mng_promote_idx8_rgba8;
+
+    iNewsamplesize = 4;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 8;
+#endif
+  }
+  else                                 /* rgba -> rgba */
+  if ((pBuf->iColortype == MNG_COLORTYPE_RGBA) &&
+      (iColortype == MNG_COLORTYPE_RGBA))
+  {
+    iNewsamplesize = 4;
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_rgba8_rgba16;
+    }
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 8;
+#endif
+  }
+#ifdef MNG_INCLUDE_JNG
+  else                                 /* JPEG g -> g */
+  if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY) &&
+      (iColortype == MNG_COLORTYPE_JPEGGRAY))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_g16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_g8;
+    }
+
+    iNewsamplesize = 1;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 2;
+#endif
+  }
+  else                                 /* JPEG g -> ga */
+  if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY) &&
+      (iColortype == MNG_COLORTYPE_JPEGGRAYA))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_ga16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_ga8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_g16_ga16;
+#endif
+
+    iNewsamplesize = 2;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 4;
+#endif
+  }
+  else                                 /* JPEG g -> rgb */
+  if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY) &&
+      (iColortype == MNG_COLORTYPE_JPEGCOLOR))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_rgb16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_rgb8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_g16_rgb16;
+#endif
+
+    iNewsamplesize = 3;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 6;
+#endif
+  }
+  else                                 /* JPEG g -> rgba */
+  if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY) &&
+      (iColortype == MNG_COLORTYPE_JPEGCOLORA))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_rgba16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_g8_rgba8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_g16_rgba16;
+#endif
+
+    iNewsamplesize = 4;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 8;
+#endif
+  }
+  else                                 /* JPEG ga -> ga */
+  if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAYA) &&
+      (iColortype == MNG_COLORTYPE_JPEGGRAYA))
+  {
+    iNewsamplesize = 2;
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_ga8_ga16;
+    if (iBitdepth == 16)
+      iNewsamplesize = 4;
+#endif
+
+  }
+  else                                 /* JPEG ga -> rgba */
+  if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAYA) &&
+      (iColortype == MNG_COLORTYPE_JPEGCOLORA))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_ga8_rgba16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_ga8_rgba8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_ga16_rgba16;
+#endif
+
+    iNewsamplesize = 4;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 8;
+#endif
+  }
+  else                                 /* JPEG rgb -> rgb */
+  if ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLOR) &&
+      (iColortype == MNG_COLORTYPE_JPEGCOLOR))
+  {
+    iNewsamplesize = 3;
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgb16;
+    if (iBitdepth == 16)
+      iNewsamplesize = 6;
+#endif
+
+  }
+  else                                 /* JPEG rgb -> rgba */
+  if ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLOR) &&
+      (iColortype == MNG_COLORTYPE_JPEGCOLORA))
+  {
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+    {
+#ifndef MNG_NO_16BIT_SUPPORT
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgba16;
+      else
+#endif
+        pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgba8;
+    }
+#ifndef MNG_NO_16BIT_SUPPORT
+    else                               /* source = 16 bits */
+      pData->fPromoterow = (mng_fptr)mng_promote_rgb16_rgba16;
+#endif
+
+    iNewsamplesize = 4;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (iBitdepth == 16)               /* 16-bit wide ? */
+      iNewsamplesize = 8;
+#endif
+  }
+  else                                 /* JPEG rgba -> rgba */
+  if ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLORA) &&
+      (iColortype == MNG_COLORTYPE_JPEGCOLORA))
+  {
+    iNewsamplesize = 4;
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (pBuf->iBitdepth <= 8)          /* source <= 8 bits */
+      if (iBitdepth == 16)
+        pData->fPromoterow = (mng_fptr)mng_promote_rgba8_rgba16;
+    if (iBitdepth == 16)
+      iNewsamplesize = 8;
+#endif
+  }
+#endif /* JNG */
+
+  /* found a proper promotion ? */
+  if (pData->fPromoterow)
+  {
+    pData->pPromBuf    = (mng_ptr)pBuf;
+    pData->iPromWidth  = pBuf->iWidth;
+    iNewrowsize        = iW * iNewsamplesize;
+    iNewbufsize        = iH * iNewrowsize;
+
+    MNG_ALLOC (pData, pNewbuf, iNewbufsize);
+
+    pData->pPromSrc    = (mng_ptr)pBuf->pImgdata;
+    pData->pPromDst    = (mng_ptr)pNewbuf;
+    iY                 = 0;
+
+    while ((!iRetcode) && (iY < iH))
+    {
+      iRetcode         = ((mng_promoterow)pData->fPromoterow) (pData);
+      pData->pPromSrc  = (mng_uint8p)pData->pPromSrc + pBuf->iRowsize;
+      pData->pPromDst  = (mng_uint8p)pData->pPromDst + iNewrowsize;
+/*      pData->pPromSrc  = (mng_ptr)((mng_uint32)pData->pPromSrc + pBuf->iRowsize); */
+/*      pData->pPromDst  = (mng_ptr)((mng_uint32)pData->pPromDst + iNewrowsize); */
+      iY++;
+    }
+
+    MNG_FREEX (pData, pBuf->pImgdata, pBuf->iImgdatasize);
+
+    pBuf->iBitdepth    = iBitdepth;
+    pBuf->iColortype   = iColortype;
+    pBuf->iSamplesize  = iNewsamplesize;
+    pBuf->iRowsize     = iNewrowsize;
+    pBuf->iImgdatasize = iNewbufsize;
+    pBuf->pImgdata     = pNewbuf;
+    pBuf->bHasPLTE     = MNG_FALSE;
+    pBuf->iPLTEcount   = 0;
+    pBuf->bHasTRNS     = MNG_FALSE;
+    pBuf->iTRNScount   = 0;
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IMGOBJECT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+mng_retcode mng_magnify_imageobject (mng_datap  pData,
+                                     mng_imagep pImage)
+{
+  mng_uint8p     pNewdata;
+  mng_uint8p     pSrcline1;
+  mng_uint8p     pSrcline2;
+  mng_uint8p     pTempline;
+  mng_uint8p     pDstline;
+  mng_uint32     iNewrowsize;
+  mng_uint32     iNewsize;
+  mng_uint32     iY;
+  mng_int32      iS, iM;
+  mng_retcode    iRetcode;
+
+  mng_imagedatap pBuf      = pImage->pImgbuf;
+  mng_uint32     iNewW     = pBuf->iWidth;
+  mng_uint32     iNewH     = pBuf->iHeight;
+  mng_magnify_x  fMagnifyX = MNG_NULL;
+  mng_magnify_y  fMagnifyY = MNG_NULL;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_IMGOBJECT, MNG_LC_START);
+#endif
+
+  if (pBuf->iColortype == MNG_COLORTYPE_INDEXED)           /* indexed color ? */
+  {                                    /* concrete buffer ? */
+    if ((pBuf->bConcrete) && (pImage->iId))
+      MNG_ERROR (pData, MNG_INVALIDCOLORTYPE);
+
+#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN
+    if (pBuf->iTRNScount)              /* with transparency ? */
+      iRetcode = mng_promote_imageobject (pData, pImage, 8, 6, 0);
+    else
+      iRetcode = mng_promote_imageobject (pData, pImage, 8, 2, 0);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+#endif
+  }
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_MAGN
+  /* Promote everything to RGBA, using fill method 0 (LBR) */
+  iRetcode = mng_promote_imageobject (pData, pImage, 8, 6, 0);           
+  if (iRetcode)                      /* on error bail out */
+    return iRetcode;
+#endif
+
+  if (pImage->iMAGN_MethodX)           /* determine new width */
+  {
+    if (pImage->iMAGN_MethodX == 1)
+    {
+      iNewW   = pImage->iMAGN_ML;
+      if (pBuf->iWidth  > 1)
+        iNewW = iNewW + pImage->iMAGN_MR;
+      if (pBuf->iWidth  > 2)
+        iNewW = iNewW + (pBuf->iWidth  - 2) * (pImage->iMAGN_MX);
+    }
+    else
+    {
+      iNewW   = pBuf->iWidth  + pImage->iMAGN_ML - 1;
+      if (pBuf->iWidth  > 2)
+        iNewW = iNewW + pImage->iMAGN_MR - 1;
+      if (pBuf->iWidth  > 3)
+        iNewW = iNewW + (pBuf->iWidth  - 3) * (pImage->iMAGN_MX - 1);
+    }
+  }
+
+  if (pImage->iMAGN_MethodY)           /* determine new height */
+  {
+    if (pImage->iMAGN_MethodY == 1)
+    {
+      iNewH   = pImage->iMAGN_MT;
+      if (pBuf->iHeight > 1)
+        iNewH = iNewH + pImage->iMAGN_ML;
+      if (pBuf->iHeight > 2)
+        iNewH = iNewH + (pBuf->iHeight - 2) * (pImage->iMAGN_MY);
+    }
+    else
+    {
+      iNewH   = pBuf->iHeight + pImage->iMAGN_MT - 1;
+      if (pBuf->iHeight > 2)
+        iNewH = iNewH + pImage->iMAGN_MB - 1;
+      if (pBuf->iHeight > 3)
+        iNewH = iNewH + (pBuf->iHeight - 3) * (pImage->iMAGN_MY - 1);
+    }
+  }
+                                       /* get new buffer */
+  iNewrowsize  = iNewW * pBuf->iSamplesize;
+  iNewsize     = iNewH * iNewrowsize;
+
+  MNG_ALLOC (pData, pNewdata, iNewsize);
+
+  switch (pBuf->iColortype)            /* determine magnification routines */
+  {
+#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN
+    case  0 : ;
+    case  8 : {
+                if (pBuf->iBitdepth <= 8)
+                {
+                  switch (pImage->iMAGN_MethodX)
+                  {
+                    case 1  : { fMagnifyX = mng_magnify_g8_x1; break; }
+                    case 2  : { fMagnifyX = mng_magnify_g8_x2; break; }
+                    case 3  : { fMagnifyX = mng_magnify_g8_x3; break; }
+                    case 4  : { fMagnifyX = mng_magnify_g8_x2; break; }
+                    case 5  : { fMagnifyX = mng_magnify_g8_x3; break; }
+                  }
+
+                  switch (pImage->iMAGN_MethodY)
+                  {
+                    case 1  : { fMagnifyY = mng_magnify_g8_y1; break; }
+                    case 2  : { fMagnifyY = mng_magnify_g8_y2; break; }
+                    case 3  : { fMagnifyY = mng_magnify_g8_y3; break; }
+                    case 4  : { fMagnifyY = mng_magnify_g8_y2; break; }
+                    case 5  : { fMagnifyY = mng_magnify_g8_y3; break; }
+                  }
+                }
+#ifndef MNG_NO_16BIT_SUPPORT
+                else
+                {
+                  switch (pImage->iMAGN_MethodX)
+                  {
+                    case 1  : { fMagnifyX = mng_magnify_g16_x1; break; }
+                    case 2  : { fMagnifyX = mng_magnify_g16_x2; break; }
+                    case 3  : { fMagnifyX = mng_magnify_g16_x3; break; }
+                    case 4  : { fMagnifyX = mng_magnify_g16_x2; break; }
+                    case 5  : { fMagnifyX = mng_magnify_g16_x3; break; }
+                  }
+
+                  switch (pImage->iMAGN_MethodY)
+                  {
+                    case 1  : { fMagnifyY = mng_magnify_g16_y1; break; }
+                    case 2  : { fMagnifyY = mng_magnify_g16_y2; break; }
+                    case 3  : { fMagnifyY = mng_magnify_g16_y3; break; }
+                    case 4  : { fMagnifyY = mng_magnify_g16_y2; break; }
+                    case 5  : { fMagnifyY = mng_magnify_g16_y3; break; }
+                  }
+                }
+#endif
+
+                break;
+              }
+
+    case  2 : ;
+    case 10 : {
+                if (pBuf->iBitdepth <= 8)
+                {
+                  switch (pImage->iMAGN_MethodX)
+                  {
+                    case 1  : { fMagnifyX = mng_magnify_rgb8_x1; break; }
+                    case 2  : { fMagnifyX = mng_magnify_rgb8_x2; break; }
+                    case 3  : { fMagnifyX = mng_magnify_rgb8_x3; break; }
+                    case 4  : { fMagnifyX = mng_magnify_rgb8_x2; break; }
+                    case 5  : { fMagnifyX = mng_magnify_rgb8_x3; break; }
+                  }
+
+                  switch (pImage->iMAGN_MethodY)
+                  {
+                    case 1  : { fMagnifyY = mng_magnify_rgb8_y1; break; }
+                    case 2  : { fMagnifyY = mng_magnify_rgb8_y2; break; }
+                    case 3  : { fMagnifyY = mng_magnify_rgb8_y3; break; }
+                    case 4  : { fMagnifyY = mng_magnify_rgb8_y2; break; }
+                    case 5  : { fMagnifyY = mng_magnify_rgb8_y3; break; }
+                  }
+                }
+#ifndef MNG_NO_16BIT_SUPPORT
+                else
+                {
+                  switch (pImage->iMAGN_MethodX)
+                  {
+                    case 1  : { fMagnifyX = mng_magnify_rgb16_x1; break; }
+                    case 2  : { fMagnifyX = mng_magnify_rgb16_x2; break; }
+                    case 3  : { fMagnifyX = mng_magnify_rgb16_x3; break; }
+                    case 4  : { fMagnifyX = mng_magnify_rgb16_x2; break; }
+                    case 5  : { fMagnifyX = mng_magnify_rgb16_x3; break; }
+                  }
+
+                  switch (pImage->iMAGN_MethodY)
+                  {
+                    case 1  : { fMagnifyY = mng_magnify_rgb16_y1; break; }
+                    case 2  : { fMagnifyY = mng_magnify_rgb16_y2; break; }
+                    case 3  : { fMagnifyY = mng_magnify_rgb16_y3; break; }
+                    case 4  : { fMagnifyY = mng_magnify_rgb16_y2; break; }
+                    case 5  : { fMagnifyY = mng_magnify_rgb16_y3; break; }
+                  }
+                }
+#endif
+
+                break;
+              }
+
+    case  4 : ;
+    case 12 : {
+                if (pBuf->iBitdepth <= 8)
+                {
+                  switch (pImage->iMAGN_MethodX)
+                  {
+                    case 1  : { fMagnifyX = mng_magnify_ga8_x1; break; }
+                    case 2  : { fMagnifyX = mng_magnify_ga8_x2; break; }
+                    case 3  : { fMagnifyX = mng_magnify_ga8_x3; break; }
+                    case 4  : { fMagnifyX = mng_magnify_ga8_x4; break; }
+                    case 5  : { fMagnifyX = mng_magnify_ga8_x5; break; }
+                  }
+
+                  switch (pImage->iMAGN_MethodY)
+                  {
+                    case 1  : { fMagnifyY = mng_magnify_ga8_y1; break; }
+                    case 2  : { fMagnifyY = mng_magnify_ga8_y2; break; }
+                    case 3  : { fMagnifyY = mng_magnify_ga8_y3; break; }
+                    case 4  : { fMagnifyY = mng_magnify_ga8_y4; break; }
+                    case 5  : { fMagnifyY = mng_magnify_ga8_y5; break; }
+                  }
+                }
+#ifndef MNG_NO_16BIT_SUPPORT
+                else
+                {
+                  switch (pImage->iMAGN_MethodX)
+                  {
+                    case 1  : { fMagnifyX = mng_magnify_ga16_x1; break; }
+                    case 2  : { fMagnifyX = mng_magnify_ga16_x2; break; }
+                    case 3  : { fMagnifyX = mng_magnify_ga16_x3; break; }
+                    case 4  : { fMagnifyX = mng_magnify_ga16_x4; break; }
+                    case 5  : { fMagnifyX = mng_magnify_ga16_x5; break; }
+                  }
+
+                  switch (pImage->iMAGN_MethodY)
+                  {
+                    case 1  : { fMagnifyY = mng_magnify_ga16_y1; break; }
+                    case 2  : { fMagnifyY = mng_magnify_ga16_y2; break; }
+                    case 3  : { fMagnifyY = mng_magnify_ga16_y3; break; }
+                    case 4  : { fMagnifyY = mng_magnify_ga16_y4; break; }
+                    case 5  : { fMagnifyY = mng_magnify_ga16_y5; break; }
+                  }
+                }
+#endif
+
+                break;
+              }
+#endif
+
+    case  6 : ;
+    case 14 : {
+                if (pBuf->iBitdepth <= 8)
+                {
+                  switch (pImage->iMAGN_MethodX)
+                  {
+                    case 1  : { fMagnifyX = mng_magnify_rgba8_x1; break; }
+                    case 2  : { fMagnifyX = mng_magnify_rgba8_x2; break; }
+                    case 3  : { fMagnifyX = mng_magnify_rgba8_x3; break; }
+                    case 4  : { fMagnifyX = mng_magnify_rgba8_x4; break; }
+                    case 5  : { fMagnifyX = mng_magnify_rgba8_x5; break; }
+                  }
+
+                  switch (pImage->iMAGN_MethodY)
+                  {
+                    case 1  : { fMagnifyY = mng_magnify_rgba8_y1; break; }
+                    case 2  : { fMagnifyY = mng_magnify_rgba8_y2; break; }
+                    case 3  : { fMagnifyY = mng_magnify_rgba8_y3; break; }
+                    case 4  : { fMagnifyY = mng_magnify_rgba8_y4; break; }
+                    case 5  : { fMagnifyY = mng_magnify_rgba8_y5; break; }
+                  }
+                }
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN
+                else
+                {
+                  switch (pImage->iMAGN_MethodX)
+                  {
+                    case 1  : { fMagnifyX = mng_magnify_rgba16_x1; break; }
+                    case 2  : { fMagnifyX = mng_magnify_rgba16_x2; break; }
+                    case 3  : { fMagnifyX = mng_magnify_rgba16_x3; break; }
+                    case 4  : { fMagnifyX = mng_magnify_rgba16_x4; break; }
+                    case 5  : { fMagnifyX = mng_magnify_rgba16_x5; break; }
+                  }
+
+                  switch (pImage->iMAGN_MethodY)
+                  {
+                    case 1  : { fMagnifyY = mng_magnify_rgba16_y1; break; }
+                    case 2  : { fMagnifyY = mng_magnify_rgba16_y2; break; }
+                    case 3  : { fMagnifyY = mng_magnify_rgba16_y3; break; }
+                    case 4  : { fMagnifyY = mng_magnify_rgba16_y4; break; }
+                    case 5  : { fMagnifyY = mng_magnify_rgba16_y5; break; }
+                  }
+                }
+#endif
+#endif
+                break;
+              }
+  }
+
+  pSrcline1 = pBuf->pImgdata;          /* initialize row-loop variables */
+  pDstline  = pNewdata;
+                                       /* allocate temporary row */
+  MNG_ALLOC (pData, pTempline, iNewrowsize);
+
+  for (iY = 0; iY < pBuf->iHeight; iY++)
+  {
+    pSrcline2 = pSrcline1 + pBuf->iRowsize;
+
+    if (fMagnifyX)                     /* magnifying in X-direction ? */
+    {
+      iRetcode = fMagnifyX (pData, pImage->iMAGN_MX,
+                            pImage->iMAGN_ML, pImage->iMAGN_MR,
+                            pBuf->iWidth, pSrcline1, pDstline);
+
+      if (iRetcode)                    /* on error bail out */
+      {
+        MNG_FREEX (pData, pTempline, iNewrowsize);
+        MNG_FREEX (pData, pNewdata,  iNewsize);
+        return iRetcode;
+      }
+    }
+    else
+    {
+      MNG_COPY (pDstline, pSrcline1, iNewrowsize);
+    }
+
+    pDstline += iNewrowsize;
+                                       /* magnifying in Y-direction ? */
+    if ((fMagnifyY) &&
+        ((iY < pBuf->iHeight - 1) || (pBuf->iHeight == 1) || (pImage->iMAGN_MethodY == 1)))
+    {
+      if (iY == 0)                     /* first interval ? */
+      {
+        if (pBuf->iHeight == 1)        /* single row ? */
+          pSrcline2 = MNG_NULL;
+
+        iM = (mng_int32)pImage->iMAGN_MT;
+      }
+      else                             /* last interval ? */
+      if (((pImage->iMAGN_MethodY == 1) && (iY == (pBuf->iHeight - 1))) ||
+          ((pImage->iMAGN_MethodY != 1) && (iY == (pBuf->iHeight - 2)))    )
+        iM = (mng_int32)pImage->iMAGN_MB;
+      else                             /* middle interval */
+        iM = (mng_int32)pImage->iMAGN_MY;
+
+      for (iS = 1; iS < iM; iS++)
+      {
+        iRetcode = fMagnifyY (pData, iS, iM, pBuf->iWidth,
+                              pSrcline1, pSrcline2, pTempline);
+
+        if (iRetcode)                  /* on error bail out */
+        {
+          MNG_FREEX (pData, pTempline, iNewrowsize);
+          MNG_FREEX (pData, pNewdata,  iNewsize);
+          return iRetcode;
+        }
+
+        if (fMagnifyX)                   /* magnifying in X-direction ? */
+        {
+          iRetcode = fMagnifyX (pData, pImage->iMAGN_MX,
+                                pImage->iMAGN_ML, pImage->iMAGN_MR,
+                                pBuf->iWidth, pTempline, pDstline);
+
+          if (iRetcode)                  /* on error bail out */
+          {
+            MNG_FREEX (pData, pTempline, iNewrowsize);
+            MNG_FREEX (pData, pNewdata,  iNewsize);
+            return iRetcode;
+          }
+        }
+        else
+        {
+          MNG_COPY (pDstline, pTempline, iNewrowsize);
+        }
+
+        pDstline  += iNewrowsize;
+      }
+    }
+
+    pSrcline1 += pBuf->iRowsize;
+  }
+                                       /* drop temporary row */
+  MNG_FREEX (pData, pTempline, iNewrowsize);
+                                       /* drop old pixel-data */
+  MNG_FREEX (pData, pBuf->pImgdata, pBuf->iImgdatasize);
+
+  pBuf->pImgdata     = pNewdata;       /* save new buffer dimensions */
+  pBuf->iRowsize     = iNewrowsize;
+  pBuf->iImgdatasize = iNewsize;
+  pBuf->iWidth       = iNewW;
+  pBuf->iHeight      = iNewH;
+
+  if (pImage->iId)                     /* real object ? */
+  {
+    pImage->iMAGN_MethodX = 0;         /* it's done; don't do it again !!! */
+    pImage->iMAGN_MethodY = 0;
+    pImage->iMAGN_MX      = 0;
+    pImage->iMAGN_MY      = 0;
+    pImage->iMAGN_ML      = 0;
+    pImage->iMAGN_MR      = 0;
+    pImage->iMAGN_MT      = 0;
+    pImage->iMAGN_MB      = 0;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_IMGOBJECT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_colorcorrect_object (mng_datap  pData,
+                                     mng_imagep pImage)
+{
+  mng_imagedatap pBuf = pImage->pImgbuf;
+  mng_retcode    iRetcode;
+  mng_uint32     iY;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COLORCORRECT_OBJECT, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_JNG
+  if ((pBuf->iBitdepth < 8) ||         /* we need 8- or 16-bit RGBA !!! */
+      ((pBuf->iColortype != MNG_COLORTYPE_RGBA      ) &&
+       (pBuf->iColortype != MNG_COLORTYPE_JPEGCOLORA)    ))
+#else
+  if (pBuf->iBitdepth < 8)         /* we need 8- or 16-bit RGBA !!! */
+#endif
+    MNG_ERROR (pData, MNG_OBJNOTABSTRACT);
+
+  if (!pBuf->bCorrected)               /* only if not already done ! */
+  {                                    /* so the row routines now to find it */
+    pData->pRetrieveobj   = (mng_objectp)pImage;
+    pData->pStoreobj      = (mng_objectp)pImage;
+    pData->pStorebuf      = (mng_objectp)pImage->pImgbuf;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (pBuf->iBitdepth > 8)
+    {
+      pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16;
+      pData->fStorerow    = (mng_fptr)mng_store_rgba16;
+    }
+    else
+#endif
+    {
+      pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8;
+      pData->fStorerow    = (mng_fptr)mng_store_rgba8;
+    }
+
+    pData->bIsOpaque      = MNG_FALSE;
+
+    pData->iPass          = -1;        /* these are the object's dimensions now */
+    pData->iRow           = 0;
+    pData->iRowinc        = 1;
+    pData->iCol           = 0;
+    pData->iColinc        = 1;
+    pData->iRowsamples    = pBuf->iWidth;
+    pData->iRowsize       = pData->iRowsamples << 2;
+    pData->iPixelofs      = 0;
+    pData->bIsRGBA16      = MNG_FALSE;
+                                       /* adjust for 16-bit object ? */
+#ifndef MNG_NO_16BIT_SUPPORT
+    if (pBuf->iBitdepth > 8)
+    {
+      pData->bIsRGBA16    = MNG_TRUE;
+      pData->iRowsize     = pData->iRowsamples << 3;
+    }
+#endif
+
+    pData->fCorrectrow    = MNG_NULL;  /* default no color-correction */
+
+#ifdef MNG_NO_CMS
+    iRetcode = MNG_NOERROR;
+#else
+#if defined(MNG_FULL_CMS)              /* determine color-management routine */
+    iRetcode = mng_init_full_cms   (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#elif defined(MNG_GAMMA_ONLY)
+    iRetcode = mng_init_gamma_only (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#elif defined(MNG_APP_CMS)
+    iRetcode = mng_init_app_cms    (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE);
+#endif
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+#endif /* MNG_NO_CMS */
+
+    if (pData->fCorrectrow)            /* really correct something ? */
+    {                                  /* get a temporary row-buffer */
+      MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize);
+
+      pData->pWorkrow = pData->pRGBArow;
+      iY              = 0;             /* start from the top */
+
+      while ((!iRetcode) && (iY < pBuf->iHeight))
+      {                                /* get a row */
+        iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData);
+
+        if (!iRetcode)                 /* color correct it */
+          iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData);
+
+        if (!iRetcode)                 /* store it back ! */
+          iRetcode = ((mng_storerow)pData->fStorerow) (pData);
+
+        if (!iRetcode)                 /* adjust variables for next row */
+          iRetcode = mng_next_row (pData);
+
+        iY++;                          /* and next line */
+      }
+                                       /* drop the temporary row-buffer */
+      MNG_FREEX (pData, pData->pRGBArow, pData->iRowsize);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+
+#if defined(MNG_FULL_CMS)              /* cleanup cms stuff */
+      iRetcode = mng_clear_cms (pData);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+#endif
+    }
+
+    pBuf->bCorrected = MNG_TRUE;       /* let's not go through that again ! */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COLORCORRECT_OBJECT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Animation-object routines                                              * */
+/* *                                                                        * */
+/* * these handle the animation objects used to re-run parts of a MNG.      * */
+/* * eg. during LOOP or TERM processing                                     * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+void mng_add_ani_object (mng_datap          pData,
+                         mng_object_headerp pObject)
+{
+  mng_object_headerp pLast = (mng_object_headerp)pData->pLastaniobj;
+
+  if (pLast)                           /* link it as last in the chain */
+  {
+    pObject->pPrev      = pLast;
+    pLast->pNext        = pObject;
+  }
+  else
+  {
+    pObject->pPrev      = MNG_NULL;    /* be on the safe side */
+    pData->pFirstaniobj = pObject;
+  }
+
+  pObject->pNext        = MNG_NULL;    /* be on the safe side */
+  pData->pLastaniobj    = pObject;
+                                       /* keep track for jumping */
+  pObject->iFramenr     = pData->iFrameseq;
+  pObject->iLayernr     = pData->iLayerseq;
+  pObject->iPlaytime    = pData->iFrametime;
+                                       /* save restart object ? */
+  if ((pData->bDisplaying) && (!pData->bRunning) && (!pData->pCurraniobj))
+    pData->pCurraniobj  = pObject;
+
+  return;
+}
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+mng_retcode mng_create_ani_image (mng_datap pData)
+{
+  mng_ani_imagep pImage;
+  mng_imagep     pCurrent;
+  mng_retcode    iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_IMAGE, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifndef MNG_NO_DELTA_PNG
+    if (pData->bHasDHDR)               /* processing delta-image ? */
+      pCurrent = (mng_imagep)pData->pObjzero;
+    else                               /* get the current object */
+#endif
+      pCurrent = (mng_imagep)pData->pCurrentobj;
+
+    if (!pCurrent)                     /* otherwise object 0 */
+      pCurrent = (mng_imagep)pData->pObjzero;
+                                       /* now just clone the object !!! */
+    iRetcode  = mng_clone_imageobject (pData, 0, MNG_FALSE, pCurrent->bVisible,
+                                       MNG_FALSE, MNG_FALSE, 0, 0, 0, pCurrent,
+                                       &pImage);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+
+    pImage->sHeader.fCleanup = mng_free_ani_image;
+    pImage->sHeader.fProcess = mng_process_ani_image;
+
+    mng_add_ani_object (pData, (mng_object_headerp)pImage);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_IMAGE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* okido */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_ani_image (mng_datap   pData,
+                                mng_objectp pObject)
+{
+  mng_ani_imagep pImage = (mng_ani_imagep)pObject;
+  mng_imagedatap pImgbuf = pImage->pImgbuf;
+  mng_retcode    iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_IMAGE, MNG_LC_START);
+#endif
+                                       /* unlink the image-data buffer */
+  iRetcode = mng_free_imagedataobject (pData, pImgbuf);
+                                       /* drop its own buffer */
+  MNG_FREEX (pData, pImage, sizeof (mng_ani_image));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_IMAGE, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_image (mng_datap   pData,
+                                   mng_objectp pObject)
+{
+  mng_retcode    iRetcode = MNG_NOERROR;
+  mng_ani_imagep pImage   = (mng_imagep)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IMAGE, MNG_LC_START);
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+  if (pData->bHasDHDR)                 /* processing delta-image ? */
+  {
+    mng_imagep pDelta = (mng_imagep)pData->pDeltaImage;
+
+    if (!pData->iBreakpoint)           /* only execute if not broken before */
+    {                                  /* make sure to process pixels as well */
+      pData->bDeltaimmediate = MNG_FALSE;
+                                       /* execute the delta process */
+      iRetcode = mng_execute_delta_image (pData, pDelta, (mng_imagep)pObject);
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+                                       /* now go and shoot it off (if required) */
+    if ((pDelta->bVisible) && (pDelta->bViewable))
+      iRetcode = mng_display_image (pData, pDelta, MNG_FALSE);
+
+    if (!pData->bTimerset)
+      pData->bHasDHDR = MNG_FALSE;     /* this image signifies IEND !! */
+
+  }
+  else
+#endif
+  if (pData->pCurrentobj)              /* active object ? */
+  {
+    mng_imagep     pCurrent = (mng_imagep)pData->pCurrentobj;
+    mng_imagedatap pBuf     = pCurrent->pImgbuf;
+
+    if (!pData->iBreakpoint)           /* don't copy it again ! */
+    {
+      if (pBuf->iImgdatasize)          /* buffer present in active object ? */
+                                       /* then drop it */
+        MNG_FREE (pData, pBuf->pImgdata, pBuf->iImgdatasize);
+
+#ifndef MNG_SKIPCHUNK_iCCP
+      if (pBuf->iProfilesize)          /* iCCP profile present ? */
+                                       /* then drop it */
+        MNG_FREE (pData, pBuf->pProfile, pBuf->iProfilesize);
+#endif
+                                       /* now blatently copy the animation buffer */
+      MNG_COPY (pBuf, pImage->pImgbuf, sizeof (mng_imagedata));
+                                       /* copy viewability */
+      pCurrent->bViewable = pImage->bViewable;
+
+      if (pBuf->iImgdatasize)          /* sample buffer present ? */
+      {                                /* then make a copy */
+        MNG_ALLOC (pData, pBuf->pImgdata, pBuf->iImgdatasize);
+        MNG_COPY (pBuf->pImgdata, pImage->pImgbuf->pImgdata, pBuf->iImgdatasize);
+      }
+
+#ifndef MNG_SKIPCHUNK_iCCP
+      if (pBuf->iProfilesize)          /* iCCP profile present ? */
+      {                                /* then make a copy */
+        MNG_ALLOC (pData, pBuf->pProfile, pBuf->iProfilesize);
+        MNG_COPY (pBuf->pProfile, pImage->pImgbuf->pProfile, pBuf->iProfilesize);
+      }
+#endif
+    }
+                                       /* now go and shoot it off (if required) */
+    if ((pCurrent->bVisible) && (pCurrent->bViewable))
+      iRetcode = mng_display_image (pData, pCurrent, MNG_FALSE);
+  }
+  else
+  {
+    mng_imagep     pObjzero = (mng_imagep)pData->pObjzero;
+    mng_imagedatap pBuf     = pObjzero->pImgbuf;
+
+    if (!pData->iBreakpoint)           /* don't copy it again ! */
+    {
+      if (pBuf->iImgdatasize)          /* buffer present in active object ? */
+                                       /* then drop it */
+        MNG_FREE (pData, pBuf->pImgdata, pBuf->iImgdatasize);
+
+#ifndef MNG_SKIPCHUNK_iCCP
+      if (pBuf->iProfilesize)          /* iCCP profile present ? */
+                                       /* then drop it */
+        MNG_FREE (pData, pBuf->pProfile, pBuf->iProfilesize);
+#endif
+                                       /* now blatently copy the animation buffer */
+      MNG_COPY (pBuf, pImage->pImgbuf, sizeof (mng_imagedata));
+                                       /* copy viewability */
+      pObjzero->bViewable = pImage->bViewable;
+
+      if (pBuf->iImgdatasize)          /* sample buffer present ? */
+      {                                /* then make a copy */
+        MNG_ALLOC (pData, pBuf->pImgdata, pBuf->iImgdatasize);
+        MNG_COPY (pBuf->pImgdata, pImage->pImgbuf->pImgdata, pBuf->iImgdatasize);
+      }
+
+#ifndef MNG_SKIPCHUNK_iCCP
+      if (pBuf->iProfilesize)          /* iCCP profile present ? */
+      {                                /* then make a copy */
+        MNG_ALLOC (pData, pBuf->pProfile, pBuf->iProfilesize);
+        MNG_COPY (pBuf->pProfile, pImage->pImgbuf->pProfile, pBuf->iProfilesize);
+      }
+#endif
+    }
+                                       /* now go and show it */
+    iRetcode = mng_display_image (pData, pObjzero, MNG_FALSE);
+  }
+
+  if (!iRetcode)                       /* all's well ? */
+  {
+    if (pData->bTimerset)              /* timer break ? */
+      pData->iBreakpoint = 99;         /* fictive number; no more processing needed! */
+    else
+      pData->iBreakpoint = 0;          /* else clear it */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IMAGE, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_plte (mng_datap      pData,
+                                 mng_uint32     iEntrycount,
+                                 mng_palette8ep paEntries)
+#else
+mng_retcode mng_create_ani_plte (mng_datap      pData)
+#endif
+{
+  mng_ani_pltep pPLTE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_PLTE, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_plte),
+                                               mng_free_obj_general,
+                                               mng_process_ani_plte,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pPLTE = (mng_ani_pltep)pTemp;
+#else
+    MNG_ALLOC (pData, pPLTE, sizeof (mng_ani_plte));
+
+    pPLTE->sHeader.fCleanup = mng_free_ani_plte;
+    pPLTE->sHeader.fProcess = mng_process_ani_plte;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pPLTE);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pPLTE->iEntrycount = iEntrycount;
+    MNG_COPY (pPLTE->aEntries, paEntries, sizeof (pPLTE->aEntries));
+#else
+    pPLTE->iEntrycount = pData->iGlobalPLTEcount;
+    MNG_COPY (pPLTE->aEntries, pData->aGlobalPLTEentries, sizeof (pPLTE->aEntries));
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_PLTE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_plte (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_PLTE, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_plte));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_PLTE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_plte (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_pltep pPLTE = (mng_ani_pltep)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PLTE, MNG_LC_START);
+#endif
+
+  pData->bHasglobalPLTE   = MNG_TRUE;
+  pData->iGlobalPLTEcount = pPLTE->iEntrycount;
+
+  MNG_COPY (pData->aGlobalPLTEentries, pPLTE->aEntries, sizeof (pPLTE->aEntries));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PLTE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_trns (mng_datap    pData,
+                                 mng_uint32   iRawlen,
+                                 mng_uint8p   pRawdata)
+#else
+mng_retcode mng_create_ani_trns (mng_datap    pData)
+#endif
+{
+  mng_ani_trnsp pTRNS;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_TRNS, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_trns),
+                                               mng_free_obj_general,
+                                               mng_process_ani_trns,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pTRNS = (mng_ani_trnsp)pTemp;
+#else
+    MNG_ALLOC (pData, pTRNS, sizeof (mng_ani_trns));
+
+    pTRNS->sHeader.fCleanup = mng_free_ani_trns;
+    pTRNS->sHeader.fProcess = mng_process_ani_trns;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pTRNS);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pTRNS->iRawlen = iRawlen;
+    MNG_COPY (pTRNS->aRawdata, pRawdata, sizeof (pTRNS->aRawdata));
+#else
+    pTRNS->iRawlen = pData->iGlobalTRNSrawlen;
+    MNG_COPY (pTRNS->aRawdata, pData->aGlobalTRNSrawdata, sizeof (pTRNS->aRawdata));
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_TRNS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_trns (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_TRNS, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_trns));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_TRNS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_trns (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_trnsp pTRNS = (mng_ani_trnsp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_TRNS, MNG_LC_START);
+#endif
+
+  pData->bHasglobalTRNS    = MNG_TRUE;
+  pData->iGlobalTRNSrawlen = pTRNS->iRawlen;
+
+  MNG_COPY (pData->aGlobalTRNSrawdata, pTRNS->aRawdata, sizeof (pTRNS->aRawdata));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_TRNS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_gAMA
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_gama (mng_datap  pData,
+                                 mng_bool   bEmpty,
+                                 mng_uint32 iGamma)
+#else
+mng_retcode mng_create_ani_gama (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_gamap pGAMA;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_GAMA, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_gama),
+                                               mng_free_obj_general,
+                                               mng_process_ani_gama,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pGAMA = (mng_ani_gamap)pTemp;
+#else
+    MNG_ALLOC (pData, pGAMA, sizeof (mng_ani_gama));
+
+    pGAMA->sHeader.fCleanup = mng_free_ani_gama;
+    pGAMA->sHeader.fProcess = mng_process_ani_gama;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pGAMA);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pGAMA->bEmpty = bEmpty;
+    pGAMA->iGamma = iGamma;
+#else
+    pGAMA->bEmpty = ((mng_gamap)pChunk)->bEmpty;
+    pGAMA->iGamma = ((mng_gamap)pChunk)->iGamma;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_GAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_gama (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_GAMA, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_gama));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_GAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_gama (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_gamap pGAMA = (mng_ani_gamap)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_GAMA, MNG_LC_START);
+#endif
+
+  if (pGAMA->bEmpty)                   /* empty chunk ? */
+  {                                    /* clear global gAMA */
+    pData->bHasglobalGAMA = MNG_FALSE;
+    pData->iGlobalGamma   = 0;
+  }
+  else
+  {                                    /* set global gAMA */
+    pData->bHasglobalGAMA = MNG_TRUE;
+    pData->iGlobalGamma   = pGAMA->iGamma;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_GAMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_chrm (mng_datap  pData,
+                                 mng_bool   bEmpty,
+                                 mng_uint32 iWhitepointx,
+                                 mng_uint32 iWhitepointy,
+                                 mng_uint32 iRedx,
+                                 mng_uint32 iRedy,
+                                 mng_uint32 iGreenx,
+                                 mng_uint32 iGreeny,
+                                 mng_uint32 iBluex,
+                                 mng_uint32 iBluey)
+#else
+mng_retcode mng_create_ani_chrm (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ptr       pTemp;
+  mng_ani_chrmp pCHRM;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_CHRM, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_chrm),
+                                               mng_free_obj_general,
+                                               mng_process_ani_chrm,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pCHRM = (mng_ani_chrmp)pTemp;
+#else
+    MNG_ALLOC (pData, pCHRM, sizeof (mng_ani_chrm));
+
+    pCHRM->sHeader.fCleanup = mng_free_ani_chrm;
+    pCHRM->sHeader.fProcess = mng_process_ani_chrm;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pCHRM);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pCHRM->bEmpty       = bEmpty;
+    pCHRM->iWhitepointx = iWhitepointx;
+    pCHRM->iWhitepointy = iWhitepointy;
+    pCHRM->iRedx        = iRedx;
+    pCHRM->iRedy        = iRedy;
+    pCHRM->iGreenx      = iGreenx;
+    pCHRM->iGreeny      = iGreeny;
+    pCHRM->iBluex       = iBluex;
+    pCHRM->iBluey       = iBluey;
+#else
+    pCHRM->bEmpty       = ((mng_chrmp)pChunk)->bEmpty;
+    pCHRM->iWhitepointx = ((mng_chrmp)pChunk)->iWhitepointx;
+    pCHRM->iWhitepointy = ((mng_chrmp)pChunk)->iWhitepointy;
+    pCHRM->iRedx        = ((mng_chrmp)pChunk)->iRedx;
+    pCHRM->iRedy        = ((mng_chrmp)pChunk)->iRedy;
+    pCHRM->iGreenx      = ((mng_chrmp)pChunk)->iGreenx;
+    pCHRM->iGreeny      = ((mng_chrmp)pChunk)->iGreeny;
+    pCHRM->iBluex       = ((mng_chrmp)pChunk)->iBluex;
+    pCHRM->iBluey       = ((mng_chrmp)pChunk)->iBluey;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_CHRM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_chrm (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_CHRM, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_chrm));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_CHRM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_chrm (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_chrmp pCHRM = (mng_ani_chrmp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CHRM, MNG_LC_START);
+#endif
+
+  if (pCHRM->bEmpty)                   /* empty chunk ? */
+  {                                    /* clear global cHRM */
+    pData->bHasglobalCHRM       = MNG_FALSE;
+    pData->iGlobalWhitepointx   = 0;
+    pData->iGlobalWhitepointy   = 0;
+    pData->iGlobalPrimaryredx   = 0;
+    pData->iGlobalPrimaryredy   = 0;
+    pData->iGlobalPrimarygreenx = 0;
+    pData->iGlobalPrimarygreeny = 0;
+    pData->iGlobalPrimarybluex  = 0;
+    pData->iGlobalPrimarybluey  = 0;
+  }
+  else
+  {                                    /* set global cHRM */
+    pData->bHasglobalCHRM       = MNG_TRUE;
+    pData->iGlobalWhitepointx   = pCHRM->iWhitepointx;
+    pData->iGlobalWhitepointy   = pCHRM->iWhitepointy;
+    pData->iGlobalPrimaryredx   = pCHRM->iRedx;
+    pData->iGlobalPrimaryredy   = pCHRM->iRedy;
+    pData->iGlobalPrimarygreenx = pCHRM->iGreenx;
+    pData->iGlobalPrimarygreeny = pCHRM->iGreeny;
+    pData->iGlobalPrimarybluex  = pCHRM->iBluex;
+    pData->iGlobalPrimarybluey  = pCHRM->iBluey;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CHRM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_sRGB
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_srgb (mng_datap pData,
+                                 mng_bool  bEmpty,
+                                 mng_uint8 iRenderingintent)
+#else
+mng_retcode mng_create_ani_srgb (mng_datap pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_srgbp pSRGB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_SRGB, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_srgb),
+                                               mng_free_obj_general,
+                                               mng_process_ani_srgb,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pSRGB = (mng_ani_srgbp)pTemp;
+#else
+    MNG_ALLOC (pData, pSRGB, sizeof (mng_ani_srgb));
+
+    pSRGB->sHeader.fCleanup = mng_free_ani_srgb;
+    pSRGB->sHeader.fProcess = mng_process_ani_srgb;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pSRGB);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pSRGB->bEmpty           = bEmpty;
+    pSRGB->iRenderingintent = iRenderingintent;
+#else
+    pSRGB->bEmpty           = ((mng_srgbp)pChunk)->bEmpty;
+    pSRGB->iRenderingintent = ((mng_srgbp)pChunk)->iRenderingintent;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_srgb (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_SRGB, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_srgb));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_srgb (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_srgbp pSRGB = (mng_ani_srgbp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SRGB, MNG_LC_START);
+#endif
+
+  if (pSRGB->bEmpty)                   /* empty chunk ? */
+  {                                    /* clear global sRGB */
+    pData->bHasglobalSRGB    = MNG_FALSE;
+    pData->iGlobalRendintent = 0;
+  }
+  else
+  {                                    /* set global sRGB */
+    pData->bHasglobalSRGB    = MNG_TRUE;
+    pData->iGlobalRendintent = pSRGB->iRenderingintent;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_iccp (mng_datap  pData,
+                                 mng_bool   bEmpty,
+                                 mng_uint32 iProfilesize,
+                                 mng_ptr    pProfile)
+#else
+mng_retcode mng_create_ani_iccp (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ptr       pTemp;
+  mng_ani_iccpp pICCP;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_ICCP, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_iccp),
+                                               mng_free_ani_iccp,
+                                               mng_process_ani_iccp,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pICCP = (mng_ani_iccpp)pTemp;
+#else
+    MNG_ALLOC (pData, pICCP, sizeof (mng_ani_iccp));
+
+    pICCP->sHeader.fCleanup = mng_free_ani_iccp;
+    pICCP->sHeader.fProcess = mng_process_ani_iccp;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pICCP);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pICCP->bEmpty       = bEmpty;
+    pICCP->iProfilesize = iProfilesize;
+
+    if (iProfilesize)
+    {
+      MNG_ALLOC (pData, pICCP->pProfile, iProfilesize);
+      MNG_COPY (pICCP->pProfile, pProfile, iProfilesize);
+    }
+#else
+    pICCP->bEmpty       = ((mng_iccpp)pChunk)->bEmpty;
+    pICCP->iProfilesize = ((mng_iccpp)pChunk)->iProfilesize;
+
+    if (pICCP->iProfilesize)
+    {
+      MNG_ALLOC (pData, pICCP->pProfile, pICCP->iProfilesize);
+      MNG_COPY (pICCP->pProfile, ((mng_iccpp)pChunk)->pProfile, pICCP->iProfilesize);
+    }
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_ICCP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_ani_iccp (mng_datap   pData,
+                               mng_objectp pObject)
+{
+  mng_ani_iccpp pICCP = (mng_ani_iccpp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_ICCP, MNG_LC_START);
+#endif
+
+  if (pICCP->iProfilesize)
+    MNG_FREEX (pData, pICCP->pProfile, pICCP->iProfilesize);
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_iccp));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_ICCP, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  return MNG_NOERROR;
+#else
+  return mng_free_obj_general(pData, pObject);
+#endif
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_iccp (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_iccpp pICCP = (mng_ani_iccpp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_ICCP, MNG_LC_START);
+#endif
+
+  if (pICCP->bEmpty)                   /* empty chunk ? */
+  {                                    /* clear global iCCP */
+    pData->bHasglobalICCP = MNG_FALSE;
+
+    if (pData->iGlobalProfilesize)
+      MNG_FREEX (pData, pData->pGlobalProfile, pData->iGlobalProfilesize);
+
+    pData->iGlobalProfilesize = 0;
+    pData->pGlobalProfile     = MNG_NULL;
+  }
+  else
+  {                                    /* set global iCCP */
+    pData->bHasglobalICCP     = MNG_TRUE;
+    pData->iGlobalProfilesize = pICCP->iProfilesize;
+
+    if (pICCP->iProfilesize)
+    {
+      MNG_ALLOC (pData, pData->pGlobalProfile, pICCP->iProfilesize);
+      MNG_COPY (pData->pGlobalProfile, pICCP->pProfile, pICCP->iProfilesize);
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_ICCP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_bKGD
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_bkgd (mng_datap  pData,
+                                 mng_uint16 iRed,
+                                 mng_uint16 iGreen,
+                                 mng_uint16 iBlue)
+#else
+mng_retcode mng_create_ani_bkgd (mng_datap  pData)
+#endif
+{
+  mng_ptr       pTemp;
+  mng_ani_bkgdp pBKGD;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_BKGD, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_bkgd),
+                                               mng_free_obj_general,
+                                               mng_process_ani_bkgd,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pBKGD = (mng_ani_bkgdp)pTemp;
+#else
+    MNG_ALLOC (pData, pBKGD, sizeof (mng_ani_bkgd));
+
+    pBKGD->sHeader.fCleanup = mng_free_ani_bkgd;
+    pBKGD->sHeader.fProcess = mng_process_ani_bkgd;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pBKGD);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pBKGD->iRed   = iRed;
+    pBKGD->iGreen = iGreen;
+    pBKGD->iBlue  = iBlue;
+#else
+    pBKGD->iRed   = pData->iGlobalBKGDred;
+    pBKGD->iGreen = pData->iGlobalBKGDgreen;
+    pBKGD->iBlue  = pData->iGlobalBKGDblue;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_bkgd (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_BKGD, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_bkgd));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_bkgd (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_bkgdp pBKGD = (mng_ani_bkgdp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BKGD, MNG_LC_START);
+#endif
+
+  pData->bHasglobalBKGD   = MNG_TRUE;
+  pData->iGlobalBKGDred   = pBKGD->iRed;
+  pData->iGlobalBKGDgreen = pBKGD->iGreen;
+  pData->iGlobalBKGDblue  = pBKGD->iBlue;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_LOOP
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_loop (mng_datap   pData,
+                                 mng_uint8   iLevel,
+                                 mng_uint32  iRepeatcount,
+                                 mng_uint8   iTermcond,
+                                 mng_uint32  iItermin,
+                                 mng_uint32  iItermax,
+                                 mng_uint32  iCount,
+                                 mng_uint32p pSignals)
+#else
+mng_retcode mng_create_ani_loop (mng_datap   pData,
+                                 mng_chunkp  pChunk)
+#endif
+{
+  mng_ani_loopp pLOOP;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_LOOP, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_loop),
+                                               mng_free_ani_loop,
+                                               mng_process_ani_loop,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pLOOP = (mng_ani_loopp)pTemp;
+#else
+    MNG_ALLOC (pData, pLOOP, sizeof (mng_ani_loop));
+
+    pLOOP->sHeader.fCleanup = mng_free_ani_loop;
+    pLOOP->sHeader.fProcess = mng_process_ani_loop;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pLOOP);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pLOOP->iLevel       = iLevel;
+    pLOOP->iRepeatcount = iRepeatcount;
+    pLOOP->iTermcond    = iTermcond;
+    pLOOP->iItermin     = iItermin;
+    pLOOP->iItermax     = iItermax;
+    pLOOP->iCount       = iCount;
+
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+    if (iCount)
+    {
+      MNG_ALLOC (pData, pLOOP->pSignals, (iCount << 1));
+      MNG_COPY (pLOOP->pSignals, pSignals, (iCount << 1));
+    }
+#endif
+#else /* MNG_OPTIMIZE_CHUNKREADER */
+    pLOOP->iLevel       = ((mng_loopp)pChunk)->iLevel;
+    pLOOP->iRepeatcount = ((mng_loopp)pChunk)->iRepeat;
+    pLOOP->iTermcond    = ((mng_loopp)pChunk)->iTermination;
+    pLOOP->iItermin     = ((mng_loopp)pChunk)->iItermin;
+    pLOOP->iItermax     = ((mng_loopp)pChunk)->iItermax;
+    pLOOP->iCount       = ((mng_loopp)pChunk)->iCount;
+
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+    if (pLOOP->iCount)
+    {
+      MNG_ALLOC (pData, pLOOP->pSignals, (pLOOP->iCount << 1));
+      MNG_COPY (pLOOP->pSignals, ((mng_loopp)pChunk)->pSignals, (pLOOP->iCount << 1));
+    }
+#endif
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+                                         /* running counter starts with repeat_count */
+    pLOOP->iRunningcount = pLOOP->iRepeatcount;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_LOOP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_ani_loop (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+  mng_ani_loopp pLOOP = (mng_ani_loopp)pObject;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_LOOP, MNG_LC_START);
+#endif
+
+#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED
+  if (pLOOP->iCount)                   /* drop signal buffer ? */
+    MNG_FREEX (pData, pLOOP->pSignals, (pLOOP->iCount << 1));
+#endif
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_loop));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_LOOP, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  return MNG_NOERROR;
+#else
+  return mng_free_obj_general(pData, pObject);
+#endif
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_loop (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_loopp pLOOP = (mng_ani_loopp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_LOOP, MNG_LC_START);
+#endif
+                                       /* just reset the running counter */
+  pLOOP->iRunningcount = pLOOP->iRepeatcount;
+                                       /* iteration=0 means we're skipping ! */
+  if ((!pData->bSkipping) && (pLOOP->iRepeatcount == 0))
+    pData->bSkipping = MNG_TRUE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_LOOP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+mng_retcode mng_create_ani_endl (mng_datap pData,
+                                 mng_uint8 iLevel)
+{
+  mng_ani_endlp pENDL;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_ENDL, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+    mng_retcode iRetcode;
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_endl),
+                                               mng_free_obj_general,
+                                               mng_process_ani_endl,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pENDL = (mng_ani_endlp)pTemp;
+#else
+    MNG_ALLOC (pData, pENDL, sizeof (mng_ani_endl));
+
+    pENDL->sHeader.fCleanup = mng_free_ani_endl;
+    pENDL->sHeader.fProcess = mng_process_ani_endl;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pENDL);
+
+    pENDL->iLevel = iLevel;
+
+    iRetcode = mng_process_ani_endl (pData, (mng_objectp)pENDL);
+    if (iRetcode)
+      return iRetcode;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_ENDL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_endl (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_ENDL, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_endl));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_ENDL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_endl (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_endlp pENDL = (mng_ani_endlp)pObject;
+  mng_ani_loopp pLOOP;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_ENDL, MNG_LC_START);
+#endif
+
+  if (((pData->bDisplaying) && ((pData->bRunning) || (pData->bSearching))) ||
+      (pData->bReading)                                                       )
+  {
+    pLOOP = pENDL->pLOOP;              /* determine matching LOOP */
+
+    if (!pLOOP)                        /* haven't got it yet ? */
+    {                                  /* go and look back in the list */
+      pLOOP = (mng_ani_loopp)pENDL->sHeader.pPrev;
+
+      while ((pLOOP) &&
+             ((pLOOP->sHeader.fCleanup != mng_free_ani_loop) ||
+              (pLOOP->iLevel           != pENDL->iLevel)        ))
+        pLOOP = pLOOP->sHeader.pPrev;
+    }
+                                       /* got it now ? */
+    if ((pLOOP) && (pLOOP->iLevel == pENDL->iLevel))
+    {
+      pENDL->pLOOP = pLOOP;            /* save for next time ! */
+                                       /* decrease running counter ? */
+      if ((pLOOP->iRunningcount) && (pLOOP->iRunningcount < 0x7fffffffL))
+        pLOOP->iRunningcount--;
+
+      if ((!pData->bDisplaying) && (pData->bReading) &&
+          (pLOOP->iRunningcount >= 0x7fffffffL))
+      {
+        pData->iTotalframes   = 0x7fffffffL;
+        pData->iTotallayers   = 0x7fffffffL;
+        pData->iTotalplaytime = 0x7fffffffL;
+      }
+      else
+      {
+        /* TODO: we're cheating out on the termination_condition,
+           iteration_min, iteration_max and possible signals;
+           the code is just not ready for that can of worms.... */
+
+        if (!pLOOP->iRunningcount)     /* reached zero ? */
+        {                              /* was this the outer LOOP ? */
+          if (pData->pFirstaniobj == (mng_objectp)pLOOP)  /* TODO: THIS IS WRONG!! */
+            pData->bHasLOOP = MNG_FALSE;
+        }
+        else
+        {
+          if (pData->pCurraniobj)      /* was we processing objects ? */
+            pData->pCurraniobj = pLOOP;/* then restart with LOOP */
+          else                         /* else restart behind LOOP !!! */
+            pData->pCurraniobj = pLOOP->sHeader.pNext;
+        }
+      }
+                                       /* does this match a 'skipping' LOOP? */
+      if ((pData->bSkipping) && (pLOOP->iRepeatcount == 0))
+        pData->bSkipping = MNG_FALSE;
+    }
+    else
+      MNG_ERROR (pData, MNG_NOMATCHINGLOOP);
+
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_ENDL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DEFI
+mng_retcode mng_create_ani_defi (mng_datap pData)
+{               
+  mng_ani_defip pDEFI;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_DEFI, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_defi),
+                                               mng_free_obj_general,
+                                               mng_process_ani_defi,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pDEFI = (mng_ani_defip)pTemp;
+#else
+    MNG_ALLOC (pData, pDEFI, sizeof (mng_ani_defi));
+
+    pDEFI->sHeader.fCleanup = mng_free_ani_defi;
+    pDEFI->sHeader.fProcess = mng_process_ani_defi;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pDEFI);
+
+    pDEFI->iId              = pData->iDEFIobjectid;
+    pDEFI->bHasdonotshow    = pData->bDEFIhasdonotshow;
+    pDEFI->iDonotshow       = pData->iDEFIdonotshow;
+    pDEFI->bHasconcrete     = pData->bDEFIhasconcrete;
+    pDEFI->iConcrete        = pData->iDEFIconcrete;
+    pDEFI->bHasloca         = pData->bDEFIhasloca;
+    pDEFI->iLocax           = pData->iDEFIlocax;
+    pDEFI->iLocay           = pData->iDEFIlocay;
+    pDEFI->bHasclip         = pData->bDEFIhasclip;
+    pDEFI->iClipl           = pData->iDEFIclipl;
+    pDEFI->iClipr           = pData->iDEFIclipr;
+    pDEFI->iClipt           = pData->iDEFIclipt;
+    pDEFI->iClipb           = pData->iDEFIclipb;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_defi (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_DEFI, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_defi));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_defi (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_defip pDEFI = (mng_ani_defip)pObject;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DEFI, MNG_LC_START);
+#endif
+
+  pData->iDEFIobjectid     = pDEFI->iId;
+  pData->bDEFIhasdonotshow = pDEFI->bHasdonotshow;
+  pData->iDEFIdonotshow    = pDEFI->iDonotshow;
+  pData->bDEFIhasconcrete  = pDEFI->bHasconcrete;
+  pData->iDEFIconcrete     = pDEFI->iConcrete;
+  pData->bDEFIhasloca      = pDEFI->bHasloca;
+  pData->iDEFIlocax        = pDEFI->iLocax;
+  pData->iDEFIlocay        = pDEFI->iLocay;
+  pData->bDEFIhasclip      = pDEFI->bHasclip;
+  pData->iDEFIclipl        = pDEFI->iClipl;
+  pData->iDEFIclipr        = pDEFI->iClipr;
+  pData->iDEFIclipt        = pDEFI->iClipt;
+  pData->iDEFIclipb        = pDEFI->iClipb;
+
+  iRetcode = mng_process_display_defi (pData);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DEFI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BASI
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_basi (mng_datap  pData,
+                                 mng_uint16 iRed,
+                                 mng_uint16 iGreen,
+                                 mng_uint16 iBlue,
+                                 mng_bool   bHasalpha,
+                                 mng_uint16 iAlpha,
+                                 mng_uint8  iViewable)
+#else
+mng_retcode mng_create_ani_basi (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_basip pBASI;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_BASI, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_basi),
+                                   mng_free_obj_general,
+                                   mng_process_ani_basi,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pBASI = (mng_ani_basip)pTemp;
+#else
+    MNG_ALLOC (pData, pBASI, sizeof (mng_ani_basi));
+
+    pBASI->sHeader.fCleanup = mng_free_ani_basi;
+    pBASI->sHeader.fProcess = mng_process_ani_basi;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pBASI);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pBASI->iRed      = iRed;
+    pBASI->iGreen    = iGreen;
+    pBASI->iBlue     = iBlue;
+    pBASI->bHasalpha = bHasalpha;
+    pBASI->iAlpha    = iAlpha;
+    pBASI->iViewable = iViewable;
+#else
+    pBASI->iRed      = ((mng_basip)pChunk)->iRed;
+    pBASI->iGreen    = ((mng_basip)pChunk)->iGreen;
+    pBASI->iBlue     = ((mng_basip)pChunk)->iBlue;
+    pBASI->bHasalpha = ((mng_basip)pChunk)->bHasalpha;
+    pBASI->iAlpha    = ((mng_basip)pChunk)->iAlpha;
+    pBASI->iViewable = ((mng_basip)pChunk)->iViewable;
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_basi (pData, iRed, iGreen, iBlue,
+                                       bHasalpha, iAlpha, iViewable);
+#else
+  iRetcode = mng_process_display_basi (pData,
+                                       ((mng_basip)pChunk)->iRed,
+                                       ((mng_basip)pChunk)->iGreen,
+                                       ((mng_basip)pChunk)->iBlue,
+                                       ((mng_basip)pChunk)->bHasalpha,
+                                       ((mng_basip)pChunk)->iAlpha,
+                                       ((mng_basip)pChunk)->iViewable);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iBASIred      = iRed;
+  pData->iBASIgreen    = iGreen;
+  pData->iBASIblue     = iBlue;
+  pData->bBASIhasalpha = bHasalpha;
+  pData->iBASIalpha    = iAlpha;
+  pData->iBASIviewable = iViewable;
+#else
+  pData->iBASIred      = ((mng_basip)pChunk)->iRed;
+  pData->iBASIgreen    = ((mng_basip)pChunk)->iGreen;
+  pData->iBASIblue     = ((mng_basip)pChunk)->iBlue;
+  pData->bBASIhasalpha = ((mng_basip)pChunk)->bHasalpha;
+  pData->iBASIalpha    = ((mng_basip)pChunk)->iAlpha;
+  pData->iBASIviewable = ((mng_basip)pChunk)->iViewable;
+#endif
+
+  iRetcode = mng_process_display_basi (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_BASI, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_basi (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_BASI, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_basi));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_BASI, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_basi (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_basip pBASI = (mng_ani_basip)pObject;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BASI, MNG_LC_START);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_basi (pData, pBASI->iRed, pBASI->iGreen, pBASI->iBlue,
+                                       pBASI->bHasalpha, pBASI->iAlpha, pBASI->iViewable);
+#else
+  pData->iBASIred      = pBASI->iRed;
+  pData->iBASIgreen    = pBASI->iGreen;
+  pData->iBASIblue     = pBASI->iBlue;
+  pData->bBASIhasalpha = pBASI->bHasalpha;
+  pData->iBASIalpha    = pBASI->iAlpha;
+  pData->iBASIviewable = pBASI->iViewable;
+
+  iRetcode = mng_process_display_basi (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BASI, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLON
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_clon (mng_datap  pData,
+                                 mng_uint16 iSourceid,
+                                 mng_uint16 iCloneid,
+                                 mng_uint8  iClonetype,
+                                 mng_bool   bHasdonotshow,
+                                 mng_uint8  iDonotshow,
+                                 mng_uint8  iConcrete,
+                                 mng_bool   bHasloca,
+                                 mng_uint8  iLocatype,
+                                 mng_int32  iLocax,
+                                 mng_int32  iLocay)
+#else
+mng_retcode mng_create_ani_clon (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_clonp pCLON;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_CLON, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_clon),
+                                   mng_free_obj_general,
+                                   mng_process_ani_clon,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pCLON = (mng_ani_clonp)pTemp;
+#else
+    MNG_ALLOC (pData, pCLON, sizeof (mng_ani_clon));
+
+    pCLON->sHeader.fCleanup = mng_free_ani_clon;
+    pCLON->sHeader.fProcess = mng_process_ani_clon;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pCLON);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pCLON->iSourceid     = iSourceid;
+    pCLON->iCloneid      = iCloneid;
+    pCLON->iClonetype    = iClonetype;
+    pCLON->bHasdonotshow = bHasdonotshow;
+    pCLON->iDonotshow    = iDonotshow;
+    pCLON->iConcrete     = iConcrete;
+    pCLON->bHasloca      = bHasloca;
+    pCLON->iLocatype     = iLocatype;
+    pCLON->iLocax        = iLocax;
+    pCLON->iLocay        = iLocay;
+#else
+    pCLON->iSourceid     = ((mng_clonp)pChunk)->iSourceid;
+    pCLON->iCloneid      = ((mng_clonp)pChunk)->iCloneid;
+    pCLON->iClonetype    = ((mng_clonp)pChunk)->iClonetype;
+    pCLON->bHasdonotshow = ((mng_clonp)pChunk)->bHasdonotshow;
+    pCLON->iDonotshow    = ((mng_clonp)pChunk)->iDonotshow;
+    pCLON->iConcrete     = ((mng_clonp)pChunk)->iConcrete;
+    pCLON->bHasloca      = ((mng_clonp)pChunk)->bHasloca;
+    pCLON->iLocatype     = ((mng_clonp)pChunk)->iLocationtype;
+    pCLON->iLocax        = ((mng_clonp)pChunk)->iLocationx;
+    pCLON->iLocay        = ((mng_clonp)pChunk)->iLocationy;
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_clon (pData, iSourceid, iCloneid, iClonetype,
+                                       bHasdonotshow, iDonotshow, iConcrete,
+                                       bHasloca, iLocatype, iLocax, iLocay);
+#else
+  iRetcode = mng_process_display_clon (pData,
+                                       ((mng_clonp)pChunk)->iSourceid,
+                                       ((mng_clonp)pChunk)->iCloneid,
+                                       ((mng_clonp)pChunk)->iClonetype,
+                                       ((mng_clonp)pChunk)->bHasdonotshow,
+                                       ((mng_clonp)pChunk)->iDonotshow,
+                                       ((mng_clonp)pChunk)->iConcrete,
+                                       ((mng_clonp)pChunk)->bHasloca,
+                                       ((mng_clonp)pChunk)->iLocationtype,
+                                       ((mng_clonp)pChunk)->iLocationx,
+                                       ((mng_clonp)pChunk)->iLocationy);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iCLONsourceid     = iSourceid;
+  pData->iCLONcloneid      = iCloneid;
+  pData->iCLONclonetype    = iClonetype;
+  pData->bCLONhasdonotshow = bHasdonotshow;
+  pData->iCLONdonotshow    = iDonotshow;
+  pData->iCLONconcrete     = iConcrete;
+  pData->bCLONhasloca      = bHasloca;
+  pData->iCLONlocationtype = iLocatype;
+  pData->iCLONlocationx    = iLocax;
+  pData->iCLONlocationy    = iLocay;
+#else
+  pData->iCLONsourceid     = ((mng_clonp)pChunk)->iSourceid;
+  pData->iCLONcloneid      = ((mng_clonp)pChunk)->iCloneid;
+  pData->iCLONclonetype    = ((mng_clonp)pChunk)->iClonetype;
+  pData->bCLONhasdonotshow = ((mng_clonp)pChunk)->bHasdonotshow;
+  pData->iCLONdonotshow    = ((mng_clonp)pChunk)->iDonotshow;
+  pData->iCLONconcrete     = ((mng_clonp)pChunk)->iConcrete;
+  pData->bCLONhasloca      = ((mng_clonp)pChunk)->bHasloca;
+  pData->iCLONlocationtype = ((mng_clonp)pChunk)->iLocationtype;
+  pData->iCLONlocationx    = ((mng_clonp)pChunk)->iLocationx;
+  pData->iCLONlocationy    = ((mng_clonp)pChunk)->iLocationy;
+#endif
+
+  iRetcode = mng_process_display_clon (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_CLON, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_clon (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_CLON, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_clon));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_CLON, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_clon (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_clonp pCLON = (mng_ani_clonp)pObject;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CLON, MNG_LC_START);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_clon (pData, pCLON->iSourceid, pCLON->iCloneid,
+                                       pCLON->iClonetype, pCLON->bHasdonotshow,
+                                       pCLON->iDonotshow, pCLON->iConcrete,
+                                       pCLON->bHasloca, pCLON->iLocatype,
+                                       pCLON->iLocax, pCLON->iLocay);
+#else
+  pData->iCLONcloneid      = pCLON->iCloneid;
+  pData->iCLONsourceid     = pCLON->iSourceid;
+  pData->iCLONclonetype    = pCLON->iClonetype;
+  pData->bCLONhasdonotshow = pCLON->bHasdonotshow;
+  pData->iCLONdonotshow    = pCLON->iDonotshow;
+  pData->iCLONconcrete     = pCLON->iConcrete;
+  pData->bCLONhasloca      = pCLON->bHasloca;
+  pData->iCLONlocationtype = pCLON->iLocatype;
+  pData->iCLONlocationx    = pCLON->iLocax;
+  pData->iCLONlocationy    = pCLON->iLocay;
+
+  iRetcode = mng_process_display_clon (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CLON, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_BACK
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_back (mng_datap  pData,
+                                 mng_uint16 iRed,
+                                 mng_uint16 iGreen,
+                                 mng_uint16 iBlue,
+                                 mng_uint8  iMandatory,
+                                 mng_uint16 iImageid,
+                                 mng_uint8  iTile)
+#else
+mng_retcode mng_create_ani_back (mng_datap  pData)
+#endif
+{
+  mng_ani_backp pBACK;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_BACK, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_back),
+                                               mng_free_obj_general,
+                                               mng_process_ani_back,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pBACK = (mng_ani_backp)pTemp;
+#else
+    MNG_ALLOC (pData, pBACK, sizeof (mng_ani_back));
+
+    pBACK->sHeader.fCleanup = mng_free_ani_back;
+    pBACK->sHeader.fProcess = mng_process_ani_back;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pBACK);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pBACK->iRed       = iRed;
+    pBACK->iGreen     = iGreen;
+    pBACK->iBlue      = iBlue;
+    pBACK->iMandatory = iMandatory;
+    pBACK->iImageid   = iImageid;
+    pBACK->iTile      = iTile;
+#else
+    pBACK->iRed       = pData->iBACKred;      
+    pBACK->iGreen     = pData->iBACKgreen;
+    pBACK->iBlue      = pData->iBACKblue;
+    pBACK->iMandatory = pData->iBACKmandatory;
+    pBACK->iImageid   = pData->iBACKimageid;
+    pBACK->iTile      = pData->iBACKtile;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_BACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_back (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_BACK, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_back));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_BACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_back (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_backp pBACK = (mng_ani_backp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BACK, MNG_LC_START);
+#endif
+
+  pData->iBACKred       = pBACK->iRed;
+  pData->iBACKgreen     = pBACK->iGreen;
+  pData->iBACKblue      = pBACK->iBlue;
+  pData->iBACKmandatory = pBACK->iMandatory;
+  pData->iBACKimageid   = pBACK->iImageid;
+  pData->iBACKtile      = pBACK->iTile;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_FRAM
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_fram (mng_datap  pData,
+                                 mng_uint8  iFramemode,
+                                 mng_uint8  iChangedelay,
+                                 mng_uint32 iDelay,
+                                 mng_uint8  iChangetimeout,
+                                 mng_uint32 iTimeout,
+                                 mng_uint8  iChangeclipping,
+                                 mng_uint8  iCliptype,
+                                 mng_int32  iClipl,
+                                 mng_int32  iClipr,
+                                 mng_int32  iClipt,
+                                 mng_int32  iClipb)
+#else
+mng_retcode mng_create_ani_fram (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_framp pFRAM;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_FRAM, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_fram),
+                                   mng_free_obj_general,
+                                   mng_process_ani_fram,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pFRAM = (mng_ani_framp)pTemp;
+#else
+    MNG_ALLOC (pData, pFRAM, sizeof (mng_ani_fram));
+
+    pFRAM->sHeader.fCleanup = mng_free_ani_fram;
+    pFRAM->sHeader.fProcess = mng_process_ani_fram;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pFRAM);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pFRAM->iFramemode      = iFramemode;
+    pFRAM->iChangedelay    = iChangedelay;
+    pFRAM->iDelay          = iDelay;
+    pFRAM->iChangetimeout  = iChangetimeout;
+    pFRAM->iTimeout        = iTimeout;
+    pFRAM->iChangeclipping = iChangeclipping;
+    pFRAM->iCliptype       = iCliptype;
+    pFRAM->iClipl          = iClipl;
+    pFRAM->iClipr          = iClipr;
+    pFRAM->iClipt          = iClipt;
+    pFRAM->iClipb          = iClipb;
+#else
+    pFRAM->iFramemode      = ((mng_framp)pChunk)->iMode;
+    pFRAM->iChangedelay    = ((mng_framp)pChunk)->iChangedelay;
+    pFRAM->iDelay          = ((mng_framp)pChunk)->iDelay;
+    pFRAM->iChangetimeout  = ((mng_framp)pChunk)->iChangetimeout;
+    pFRAM->iTimeout        = ((mng_framp)pChunk)->iTimeout;
+    pFRAM->iChangeclipping = ((mng_framp)pChunk)->iChangeclipping;
+    pFRAM->iCliptype       = ((mng_framp)pChunk)->iBoundarytype;
+    pFRAM->iClipl          = ((mng_framp)pChunk)->iBoundaryl;
+    pFRAM->iClipr          = ((mng_framp)pChunk)->iBoundaryr;
+    pFRAM->iClipt          = ((mng_framp)pChunk)->iBoundaryt;
+    pFRAM->iClipb          = ((mng_framp)pChunk)->iBoundaryb;
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_fram (pData, iFramemode,
+                                       iChangedelay, iDelay,
+                                       iChangetimeout, iTimeout,
+                                       iChangeclipping, iCliptype,
+                                       iClipl, iClipr,
+                                       iClipt, iClipb);
+#else
+  iRetcode = mng_process_display_fram (pData,
+                                       ((mng_framp)pChunk)->iMode,
+                                       ((mng_framp)pChunk)->iChangedelay,
+                                       ((mng_framp)pChunk)->iDelay,
+                                       ((mng_framp)pChunk)->iChangetimeout,
+                                       ((mng_framp)pChunk)->iTimeout,
+                                       ((mng_framp)pChunk)->iChangeclipping,
+                                       ((mng_framp)pChunk)->iBoundarytype,
+                                       ((mng_framp)pChunk)->iBoundaryl,
+                                       ((mng_framp)pChunk)->iBoundaryr,
+                                       ((mng_framp)pChunk)->iBoundaryt,
+                                       ((mng_framp)pChunk)->iBoundaryb);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iTempFramemode      = iFramemode;
+  pData->iTempChangedelay    = iChangedelay;
+  pData->iTempDelay          = iDelay;
+  pData->iTempChangetimeout  = iChangetimeout;
+  pData->iTempTimeout        = iTimeout;
+  pData->iTempChangeclipping = iChangeclipping;
+  pData->iTempCliptype       = iCliptype;
+  pData->iTempClipl          = iClipl;
+  pData->iTempClipr          = iClipr;
+  pData->iTempClipt          = iClipt;
+  pData->iTempClipb          = iClipb;
+#else
+  pData->iTempFramemode      = ((mng_framp)pChunk)->iMode;
+  pData->iTempChangedelay    = ((mng_framp)pChunk)->iChangedelay;
+  pData->iTempDelay          = ((mng_framp)pChunk)->iDelay;
+  pData->iTempChangetimeout  = ((mng_framp)pChunk)->iChangetimeout;
+  pData->iTempTimeout        = ((mng_framp)pChunk)->iTimeout;
+  pData->iTempChangeclipping = ((mng_framp)pChunk)->iChangeclipping;
+  pData->iTempCliptype       = ((mng_framp)pChunk)->iBoundarytype;
+  pData->iTempClipl          = ((mng_framp)pChunk)->iBoundaryl;
+  pData->iTempClipr          = ((mng_framp)pChunk)->iBoundaryr;
+  pData->iTempClipt          = ((mng_framp)pChunk)->iBoundaryt;
+  pData->iTempClipb          = ((mng_framp)pChunk)->iBoundaryb;
+#endif
+
+  iRetcode = mng_process_display_fram (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_FRAM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_fram (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_FRAM, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_fram));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_FRAM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_fram (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_framp pFRAM = (mng_ani_framp)pObject;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_FRAM, MNG_LC_START);
+#endif
+
+  if (pData->iBreakpoint)              /* previously broken ? */
+  {
+    iRetcode           = mng_process_display_fram2 (pData);
+    pData->iBreakpoint = 0;            /* not again */
+  }
+  else
+  {
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+    iRetcode = mng_process_display_fram (pData, pFRAM->iFramemode,
+                                         pFRAM->iChangedelay, pFRAM->iDelay,
+                                         pFRAM->iChangetimeout, pFRAM->iTimeout,
+                                         pFRAM->iChangeclipping, pFRAM->iCliptype,
+                                         pFRAM->iClipl, pFRAM->iClipr,
+                                         pFRAM->iClipt, pFRAM->iClipb);
+#else
+    pData->iTempFramemode      = pFRAM->iFramemode;
+    pData->iTempChangedelay    = pFRAM->iChangedelay;
+    pData->iTempDelay          = pFRAM->iDelay;
+    pData->iTempChangetimeout  = pFRAM->iChangetimeout;
+    pData->iTempTimeout        = pFRAM->iTimeout;
+    pData->iTempChangeclipping = pFRAM->iChangeclipping;
+    pData->iTempCliptype       = pFRAM->iCliptype;
+    pData->iTempClipl          = pFRAM->iClipl;
+    pData->iTempClipr          = pFRAM->iClipr;
+    pData->iTempClipt          = pFRAM->iClipt;
+    pData->iTempClipb          = pFRAM->iClipb;
+
+    iRetcode = mng_process_display_fram (pData);
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_FRAM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MOVE
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_move (mng_datap  pData,
+                                 mng_uint16 iFirstid,
+                                 mng_uint16 iLastid,
+                                 mng_uint8  iType,
+                                 mng_int32  iLocax,
+                                 mng_int32  iLocay)
+#else
+mng_retcode mng_create_ani_move (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_movep pMOVE;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_MOVE, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_move),
+                                   mng_free_obj_general,
+                                   mng_process_ani_move,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pMOVE = (mng_ani_movep)pTemp;
+#else
+    MNG_ALLOC (pData, pMOVE, sizeof (mng_ani_move));
+
+    pMOVE->sHeader.fCleanup = mng_free_ani_move;
+    pMOVE->sHeader.fProcess = mng_process_ani_move;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pMOVE);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pMOVE->iFirstid = iFirstid;
+    pMOVE->iLastid  = iLastid;
+    pMOVE->iType    = iType;
+    pMOVE->iLocax   = iLocax;
+    pMOVE->iLocay   = iLocay;
+#else
+    pMOVE->iFirstid = ((mng_movep)pChunk)->iFirstid;
+    pMOVE->iLastid  = ((mng_movep)pChunk)->iLastid;
+    pMOVE->iType    = ((mng_movep)pChunk)->iMovetype;
+    pMOVE->iLocax   = ((mng_movep)pChunk)->iMovex;
+    pMOVE->iLocay   = ((mng_movep)pChunk)->iMovey;
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_move (pData, iFirstid, iLastid,
+                                       iType, iLocax, iLocay);
+#else
+  iRetcode = mng_process_display_move (pData,
+                                       ((mng_movep)pChunk)->iFirstid,
+                                       ((mng_movep)pChunk)->iLastid,
+                                       ((mng_movep)pChunk)->iMovetype,
+                                       ((mng_movep)pChunk)->iMovex,
+                                       ((mng_movep)pChunk)->iMovey);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iMOVEfromid   = iFirstid;
+  pData->iMOVEtoid     = iLastid;
+  pData->iMOVEmovetype = iType;
+  pData->iMOVEmovex    = iLocax;
+  pData->iMOVEmovey    = iLocay;
+#else
+  pData->iMOVEfromid   = ((mng_movep)pChunk)->iFirstid;
+  pData->iMOVEtoid     = ((mng_movep)pChunk)->iLastid;
+  pData->iMOVEmovetype = ((mng_movep)pChunk)->iMovetype;
+  pData->iMOVEmovex    = ((mng_movep)pChunk)->iMovex;
+  pData->iMOVEmovey    = ((mng_movep)pChunk)->iMovey;
+#endif
+
+  iRetcode = mng_process_display_move (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_MOVE, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_move (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_MOVE, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_move));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_MOVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_move (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_retcode   iRetcode;
+  mng_ani_movep pMOVE = (mng_ani_movep)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_MOVE, MNG_LC_START);
+#endif
+                                       /* re-process the MOVE chunk */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_move (pData, pMOVE->iFirstid, pMOVE->iLastid,
+                                       pMOVE->iType, pMOVE->iLocax, pMOVE->iLocay);
+#else
+  pData->iMOVEfromid   = pMOVE->iFirstid;
+  pData->iMOVEtoid     = pMOVE->iLastid;
+  pData->iMOVEmovetype = pMOVE->iType;
+  pData->iMOVEmovex    = pMOVE->iLocax;
+  pData->iMOVEmovey    = pMOVE->iLocay;
+
+  iRetcode = mng_process_display_move (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_MOVE, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_CLIP
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_clip (mng_datap  pData,
+                                 mng_uint16 iFirstid,
+                                 mng_uint16 iLastid,
+                                 mng_uint8  iType,
+                                 mng_int32  iClipl,
+                                 mng_int32  iClipr,
+                                 mng_int32  iClipt,
+                                 mng_int32  iClipb)
+#else
+mng_retcode mng_create_ani_clip (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_clipp pCLIP;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_CLIP, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_clip),
+                                   mng_free_obj_general,
+                                   mng_process_ani_clip,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pCLIP = (mng_ani_clipp)pTemp;
+#else
+    MNG_ALLOC (pData, pCLIP, sizeof (mng_ani_clip));
+
+    pCLIP->sHeader.fCleanup = mng_free_ani_clip;
+    pCLIP->sHeader.fProcess = mng_process_ani_clip;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pCLIP);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pCLIP->iFirstid = iFirstid;
+    pCLIP->iLastid  = iLastid;
+    pCLIP->iType    = iType;
+    pCLIP->iClipl   = iClipl;
+    pCLIP->iClipr   = iClipr;
+    pCLIP->iClipt   = iClipt;
+    pCLIP->iClipb   = iClipb;
+#else
+    pCLIP->iFirstid = ((mng_clipp)pChunk)->iFirstid;
+    pCLIP->iLastid  = ((mng_clipp)pChunk)->iLastid;
+    pCLIP->iType    = ((mng_clipp)pChunk)->iCliptype;
+    pCLIP->iClipl   = ((mng_clipp)pChunk)->iClipl;
+    pCLIP->iClipr   = ((mng_clipp)pChunk)->iClipr;
+    pCLIP->iClipt   = ((mng_clipp)pChunk)->iClipt;
+    pCLIP->iClipb   = ((mng_clipp)pChunk)->iClipb;
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_clip (pData, iFirstid, iLastid,
+                                       iType, iClipl, iClipr,
+                                       iClipt, iClipb);
+#else
+  iRetcode = mng_process_display_clip (pData,
+                                       ((mng_clipp)pChunk)->iFirstid,
+                                       ((mng_clipp)pChunk)->iLastid,
+                                       ((mng_clipp)pChunk)->iCliptype,
+                                       ((mng_clipp)pChunk)->iClipl,
+                                       ((mng_clipp)pChunk)->iClipr,
+                                       ((mng_clipp)pChunk)->iClipt,
+                                       ((mng_clipp)pChunk)->iClipb);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iCLIPfromid   = iFirstid;
+  pData->iCLIPtoid     = iLastid;
+  pData->iCLIPcliptype = iType;
+  pData->iCLIPclipl    = iClipl;
+  pData->iCLIPclipr    = iClipr;
+  pData->iCLIPclipt    = iClipt;
+  pData->iCLIPclipb    = iClipb;
+#else
+  pData->iCLIPfromid   = ((mng_clipp)pChunk)->iFirstid;
+  pData->iCLIPtoid     = ((mng_clipp)pChunk)->iLastid;
+  pData->iCLIPcliptype = ((mng_clipp)pChunk)->iCliptype;
+  pData->iCLIPclipl    = ((mng_clipp)pChunk)->iClipl;
+  pData->iCLIPclipr    = ((mng_clipp)pChunk)->iClipr;
+  pData->iCLIPclipt    = ((mng_clipp)pChunk)->iClipt;
+  pData->iCLIPclipb    = ((mng_clipp)pChunk)->iClipb;
+#endif
+
+  iRetcode = mng_process_display_clip (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_CLIP, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_clip (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_CLIP, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_clip));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_CLIP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_clip (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_retcode   iRetcode;
+  mng_ani_clipp pCLIP = (mng_ani_clipp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CLIP, MNG_LC_START);
+#endif
+                                       /* re-process the CLIP chunk */
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_clip (pData, pCLIP->iFirstid, pCLIP->iLastid,
+                                       pCLIP->iType, pCLIP->iClipl, pCLIP->iClipr,
+                                       pCLIP->iClipt, pCLIP->iClipb);
+#else
+  pData->iCLIPfromid   = pCLIP->iFirstid;
+  pData->iCLIPtoid     = pCLIP->iLastid;
+  pData->iCLIPcliptype = pCLIP->iType;
+  pData->iCLIPclipl    = pCLIP->iClipl;
+  pData->iCLIPclipr    = pCLIP->iClipr;
+  pData->iCLIPclipt    = pCLIP->iClipt;
+  pData->iCLIPclipb    = pCLIP->iClipb;
+
+  iRetcode = mng_process_display_clip (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CLIP, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SHOW
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_show (mng_datap  pData,
+                                 mng_uint16 iFirstid,
+                                 mng_uint16 iLastid,
+                                 mng_uint8  iMode)
+#else
+mng_retcode mng_create_ani_show (mng_datap  pData)
+#endif
+{
+  mng_ani_showp pSHOW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_SHOW, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_show),
+                                               mng_free_obj_general,
+                                               mng_process_ani_show,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pSHOW = (mng_ani_showp)pTemp;
+#else
+    MNG_ALLOC (pData, pSHOW, sizeof (mng_ani_show));
+
+    pSHOW->sHeader.fCleanup = mng_free_ani_show;
+    pSHOW->sHeader.fProcess = mng_process_ani_show;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pSHOW);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pSHOW->iFirstid = iFirstid;
+    pSHOW->iLastid  = iLastid;
+    pSHOW->iMode    = iMode;
+#else
+    pSHOW->iFirstid = pData->iSHOWfromid;
+    pSHOW->iLastid  = pData->iSHOWtoid;
+    pSHOW->iMode    = pData->iSHOWmode;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_SHOW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_show (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_SHOW, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_show));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_SHOW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_show (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_retcode   iRetcode;
+  mng_ani_showp pSHOW = (mng_ani_showp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SHOW, MNG_LC_START);
+#endif
+
+  if (pData->iBreakpoint)              /* returning from breakpoint ? */
+  {
+    iRetcode           = mng_process_display_show (pData);
+  }
+  else
+  {                                    /* "re-run" SHOW chunk */
+    pData->iSHOWmode   = pSHOW->iMode;
+    pData->iSHOWfromid = pSHOW->iFirstid;
+    pData->iSHOWtoid   = pSHOW->iLastid;
+
+    iRetcode           = mng_process_display_show (pData);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SHOW, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_TERM
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_term (mng_datap  pData,
+                                 mng_uint8  iTermaction,
+                                 mng_uint8  iIteraction,
+                                 mng_uint32 iDelay,
+                                 mng_uint32 iItermax)
+#else
+mng_retcode mng_create_ani_term (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_termp pTERM;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_TERM, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_term),
+                                               mng_free_obj_general,
+                                               mng_process_ani_term,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pTERM = (mng_ani_termp)pTemp;
+#else
+    MNG_ALLOC (pData, pTERM, sizeof (mng_ani_term));
+
+    pTERM->sHeader.fCleanup = mng_free_ani_term;
+    pTERM->sHeader.fProcess = mng_process_ani_term;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pTERM);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pTERM->iTermaction = iTermaction;
+    pTERM->iIteraction = iIteraction;
+    pTERM->iDelay      = iDelay;
+    pTERM->iItermax    = iItermax;
+#else
+    pTERM->iTermaction = ((mng_termp)pChunk)->iTermaction;
+    pTERM->iIteraction = ((mng_termp)pChunk)->iIteraction;
+    pTERM->iDelay      = ((mng_termp)pChunk)->iDelay;
+    pTERM->iItermax    = ((mng_termp)pChunk)->iItermax;
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_TERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_term (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_TERM, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_term));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_TERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_term (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_TERM, MNG_LC_START);
+#endif
+
+  /* dummy: no action required! */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_TERM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode mng_create_ani_save (mng_datap pData)
+{
+  mng_ptr       pTemp;
+  mng_ani_savep pSAVE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_SAVE, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_save),
+                                               mng_free_obj_general,
+                                               mng_process_ani_save,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pSAVE = (mng_ani_savep)pTemp;
+#else
+    MNG_ALLOC (pData, pSAVE, sizeof (mng_ani_save));
+
+    pSAVE->sHeader.fCleanup = mng_free_ani_save;
+    pSAVE->sHeader.fProcess = mng_process_ani_save;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pSAVE);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_SAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_save (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_SAVE, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_save));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_SAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_save (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SAVE, MNG_LC_START);
+#endif
+
+  iRetcode = mng_process_display_save (pData);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SAVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_SEEK
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_seek (mng_datap  pData,
+                                 mng_uint32 iSegmentnamesize,
+                                 mng_pchar  zSegmentname)
+#else
+mng_retcode mng_create_ani_seek (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ptr       pTemp;
+  mng_ani_seekp pSEEK;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_SEEK, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_seek),
+                                               mng_free_ani_seek,
+                                               mng_process_ani_seek,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pSEEK = (mng_ani_seekp)pTemp;
+#else
+    MNG_ALLOC (pData, pSEEK, sizeof (mng_ani_seek));
+
+    pSEEK->sHeader.fCleanup = mng_free_ani_seek;
+    pSEEK->sHeader.fProcess = mng_process_ani_seek;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pSEEK);
+
+    pData->pLastseek = (mng_objectp)pSEEK;
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pSEEK->iSegmentnamesize = iSegmentnamesize;
+    if (iSegmentnamesize)
+    {
+      MNG_ALLOC (pData, pSEEK->zSegmentname, iSegmentnamesize + 1);
+      MNG_COPY (pSEEK->zSegmentname, zSegmentname, iSegmentnamesize);
+    }
+#else
+    pSEEK->iSegmentnamesize = ((mng_seekp)pChunk)->iNamesize;
+    if (pSEEK->iSegmentnamesize)
+    {
+      MNG_ALLOC (pData, pSEEK->zSegmentname, pSEEK->iSegmentnamesize + 1);
+      MNG_COPY (pSEEK->zSegmentname, ((mng_seekp)pChunk)->zName, pSEEK->iSegmentnamesize);
+    }
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_SEEK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_ani_seek (mng_datap   pData,
+                               mng_objectp pObject)
+{
+  mng_ani_seekp pSEEK = (mng_ani_seekp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_SEEK, MNG_LC_START);
+#endif
+
+  if (pSEEK->iSegmentnamesize)
+    MNG_FREEX (pData, pSEEK->zSegmentname, pSEEK->iSegmentnamesize + 1);
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_seek));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_SEEK, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  return MNG_NOERROR;
+#else
+  return mng_free_obj_general(pData, pObject);
+#endif
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_seek (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_seekp pSEEK = (mng_ani_seekp)pObject;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SEEK, MNG_LC_START);
+#endif
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+  if (!pData->bStopafterseek)          /* can we really process this one ? */
+#endif  
+  {
+    pData->pLastseek = pObject;
+
+    if (pData->fProcessseek)           /* inform the app ? */
+    {
+      mng_bool  bOke;
+      mng_pchar zName;
+
+      MNG_ALLOC (pData, zName, pSEEK->iSegmentnamesize + 1);
+
+      if (pSEEK->iSegmentnamesize)
+        MNG_COPY (zName, pSEEK->zSegmentname, pSEEK->iSegmentnamesize);
+
+      bOke = pData->fProcessseek ((mng_handle)pData, zName);
+
+      MNG_FREEX (pData, zName, pSEEK->iSegmentnamesize + 1);
+
+      if (!bOke)
+        MNG_ERROR (pData, MNG_APPMISCERROR);
+    }
+  }
+
+  iRetcode = mng_process_display_seek (pData);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SEEK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_dhdr (mng_datap  pData,
+                                 mng_uint16 iObjectid,
+                                 mng_uint8  iImagetype,
+                                 mng_uint8  iDeltatype,
+                                 mng_uint32 iBlockwidth,
+                                 mng_uint32 iBlockheight,
+                                 mng_uint32 iBlockx,
+                                 mng_uint32 iBlocky)
+#else
+mng_retcode mng_create_ani_dhdr (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_dhdrp pDHDR;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_DHDR, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_dhdr),
+                                   mng_free_obj_general,
+                                   mng_process_ani_dhdr,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pDHDR = (mng_ani_dhdrp)pTemp;
+#else
+    MNG_ALLOC (pData, pDHDR, sizeof (mng_ani_dhdr));
+
+    pDHDR->sHeader.fCleanup = mng_free_ani_dhdr;
+    pDHDR->sHeader.fProcess = mng_process_ani_dhdr;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pDHDR);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pDHDR->iObjectid    = iObjectid;
+    pDHDR->iImagetype   = iImagetype;
+    pDHDR->iDeltatype   = iDeltatype;
+    pDHDR->iBlockwidth  = iBlockwidth;
+    pDHDR->iBlockheight = iBlockheight;
+    pDHDR->iBlockx      = iBlockx;
+    pDHDR->iBlocky      = iBlocky;
+#else
+    pDHDR->iObjectid    = ((mng_dhdrp)pChunk)->iObjectid;
+    pDHDR->iImagetype   = ((mng_dhdrp)pChunk)->iImagetype;
+    pDHDR->iDeltatype   = ((mng_dhdrp)pChunk)->iDeltatype;
+    pDHDR->iBlockwidth  = ((mng_dhdrp)pChunk)->iBlockwidth;
+    pDHDR->iBlockheight = ((mng_dhdrp)pChunk)->iBlockheight;
+    pDHDR->iBlockx      = ((mng_dhdrp)pChunk)->iBlockx;
+    pDHDR->iBlocky      = ((mng_dhdrp)pChunk)->iBlocky;
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_dhdr (pData, iObjectid,
+                                       iImagetype, iDeltatype,
+                                       iBlockwidth, iBlockheight,
+                                       iBlockx, iBlocky);
+#else
+  iRetcode = mng_process_display_dhdr (pData,
+                                       ((mng_dhdrp)pChunk)->iObjectid,
+                                       ((mng_dhdrp)pChunk)->iImagetype,
+                                       ((mng_dhdrp)pChunk)->iDeltatype,
+                                       ((mng_dhdrp)pChunk)->iBlockwidth,
+                                       ((mng_dhdrp)pChunk)->iBlockheight,
+                                       ((mng_dhdrp)pChunk)->iBlockx,
+                                       ((mng_dhdrp)pChunk)->iBlocky);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iDHDRobjectid    = iObjectid;
+  pData->iDHDRimagetype   = iImagetype;
+  pData->iDHDRdeltatype   = iDeltatype;
+  pData->iDHDRblockwidth  = iBlockwidth;
+  pData->iDHDRblockheight = iBlockheight;
+  pData->iDHDRblockx      = iBlockx;
+  pData->iDHDRblocky      = iBlocky;
+#else
+  pData->iDHDRobjectid    = ((mng_dhdrp)pChunk)->iObjectid;
+  pData->iDHDRimagetype   = ((mng_dhdrp)pChunk)->iImagetype;
+  pData->iDHDRdeltatype   = ((mng_dhdrp)pChunk)->iDeltatype;
+  pData->iDHDRblockwidth  = ((mng_dhdrp)pChunk)->iBlockwidth;
+  pData->iDHDRblockheight = ((mng_dhdrp)pChunk)->iBlockheight;
+  pData->iDHDRblockx      = ((mng_dhdrp)pChunk)->iBlockx;
+  pData->iDHDRblocky      = ((mng_dhdrp)pChunk)->iBlocky;
+#endif
+
+  iRetcode = mng_process_display_dhdr (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_DHDR, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_dhdr (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_DHDR, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_dhdr));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_DHDR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_dhdr (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_dhdrp pDHDR = (mng_ani_dhdrp)pObject;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DHDR, MNG_LC_START);
+#endif
+
+  pData->bHasDHDR = MNG_TRUE;          /* let everyone know we're inside a DHDR */
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_dhdr (pData, pDHDR->iObjectid,
+                                       pDHDR->iImagetype, pDHDR->iDeltatype,
+                                       pDHDR->iBlockwidth, pDHDR->iBlockheight,
+                                       pDHDR->iBlockx, pDHDR->iBlocky);
+#else
+  pData->iDHDRobjectid    = pDHDR->iObjectid;
+  pData->iDHDRimagetype   = pDHDR->iImagetype;
+  pData->iDHDRdeltatype   = pDHDR->iDeltatype;
+  pData->iDHDRblockwidth  = pDHDR->iBlockwidth;
+  pData->iDHDRblockheight = pDHDR->iBlockheight;
+  pData->iDHDRblockx      = pDHDR->iBlockx;
+  pData->iDHDRblocky      = pDHDR->iBlocky;
+
+  iRetcode = mng_process_display_dhdr (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DHDR, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_prom (mng_datap pData,
+                                 mng_uint8 iBitdepth,
+                                 mng_uint8 iColortype,
+                                 mng_uint8 iFilltype)
+#else
+mng_retcode mng_create_ani_prom (mng_datap pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_promp pPROM=NULL;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_PROM, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_prom),
+                                   mng_free_obj_general,
+                                   mng_process_ani_prom,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pPROM = (mng_ani_promp)pTemp;
+#else
+    MNG_ALLOC (pData, pPROM, sizeof (mng_ani_prom));
+
+    pPROM->sHeader.fCleanup = mng_free_ani_prom;
+    pPROM->sHeader.fProcess = mng_process_ani_prom;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pPROM);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pPROM->iBitdepth  = iBitdepth;
+    pPROM->iColortype = iColortype;
+    pPROM->iFilltype  = iFilltype;
+#else
+    pPROM->iBitdepth  = ((mng_promp)pChunk)->iSampledepth;
+    pPROM->iColortype = ((mng_promp)pChunk)->iColortype;
+    pPROM->iFilltype  = ((mng_promp)pChunk)->iFilltype;
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_prom (pData, iBitdepth,
+                                       iColortype, iFilltype);
+#else
+  iRetcode = mng_process_display_prom (pData,
+                                       ((mng_promp)pChunk)->iSampledepth,
+                                       ((mng_promp)pChunk)->iColortype,
+                                       ((mng_promp)pChunk)->iFilltype);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iPROMbitdepth  = iBitdepth;
+  pData->iPROMcolortype = iColortype;
+  pData->iPROMfilltype  = iFilltype;
+#else
+  pData->iPROMbitdepth  = ((mng_promp)pChunk)->iSampledepth;
+  pData->iPROMcolortype = ((mng_promp)pChunk)->iColortype;
+  pData->iPROMfilltype  = ((mng_promp)pChunk)->iFilltype;
+#endif
+
+  iRetcode = mng_process_display_prom (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_PROM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_prom (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_PROM, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_prom));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_PROM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_prom (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_promp pPROM = (mng_ani_promp)pObject;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PROM, MNG_LC_START);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_prom (pData, pPROM->iBitdepth,
+                                       pPROM->iColortype, pPROM->iFilltype);
+#else
+  pData->iPROMbitdepth  = pPROM->iBitdepth;
+  pData->iPROMcolortype = pPROM->iColortype;
+  pData->iPROMfilltype  = pPROM->iFilltype;
+
+  iRetcode = mng_process_display_prom (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PROM, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_create_ani_ipng (mng_datap pData)
+{
+  mng_ani_ipngp pIPNG;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_IPNG, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_ipng),
+                                               mng_free_obj_general,
+                                               mng_process_ani_ipng,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pIPNG = (mng_ani_ipngp)pTemp;
+#else
+    MNG_ALLOC (pData, pIPNG, sizeof (mng_ani_ipng));
+
+    pIPNG->sHeader.fCleanup = mng_free_ani_ipng;
+    pIPNG->sHeader.fProcess = mng_process_ani_ipng;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pIPNG);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_IPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_ipng (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_IPNG, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_ipng));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_IPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_ipng (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IPNG, MNG_LC_START);
+#endif
+
+  iRetcode = mng_process_display_ipng (pData);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IPNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+mng_retcode mng_create_ani_ijng (mng_datap pData)
+{
+  mng_ani_ijngp pIJNG;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_IJNG, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_ijng),
+                                               mng_free_obj_general,
+                                               mng_process_ani_ijng,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pIJNG = (mng_ani_ijngp)pTemp;
+#else
+    MNG_ALLOC (pData, pIJNG, sizeof (mng_ani_ijng));
+
+    pIJNG->sHeader.fCleanup = mng_free_ani_ijng;
+    pIJNG->sHeader.fProcess = mng_process_ani_ijng;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pIJNG);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_IJNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_ijng (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_IJNG, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_ijng));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_IJNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_ijng (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IJNG, MNG_LC_START);
+#endif
+
+  iRetcode = mng_process_display_ijng (pData);
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IJNG, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_create_ani_pplt (mng_datap      pData,
+                                 mng_uint8      iType,
+                                 mng_uint32     iCount,
+                                 mng_palette8ep paIndexentries,
+                                 mng_uint8p     paAlphaentries,
+                                 mng_uint8p     paUsedentries)
+{
+  mng_ani_ppltp pPPLT;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_PPLT, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_pplt),
+                                   mng_free_obj_general,
+                                   mng_process_ani_pplt,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pPPLT = (mng_ani_ppltp)pTemp;
+#else
+    MNG_ALLOC (pData, pPPLT, sizeof (mng_ani_pplt));
+
+    pPPLT->sHeader.fCleanup = mng_free_ani_pplt;
+    pPPLT->sHeader.fProcess = mng_process_ani_pplt;
+#endif
+
+    pPPLT->iType            = iType;
+    pPPLT->iCount           = iCount;
+
+    MNG_COPY (pPPLT->aIndexentries, paIndexentries, sizeof (pPPLT->aIndexentries));
+    MNG_COPY (pPPLT->aAlphaentries, paAlphaentries, sizeof (pPPLT->aAlphaentries));
+    MNG_COPY (pPPLT->aUsedentries,  paUsedentries,  sizeof (pPPLT->aUsedentries ));
+
+    mng_add_ani_object (pData, (mng_object_headerp)pPPLT);
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_pplt (pData, iType, iCount,
+                                       paIndexentries, paAlphaentries, paUsedentries);
+#else
+  pData->iPPLTtype          = iType;
+  pData->iPPLTcount         = iCount;
+  pData->paPPLTindexentries = paIndexentries;
+  pData->paPPLTalphaentries = paAlphaentries;
+  pData->paPPLTusedentries  = paUsedentries;
+
+  iRetcode = mng_process_display_pplt (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_PPLT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_pplt (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_PPLT, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_pplt));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_PPLT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_pplt (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_ppltp pPPLT = (mng_ani_ppltp)pObject;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PPLT, MNG_LC_START);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_pplt (pData, pPPLT->iType, pPPLT->iCount,
+                                       pPPLT->aIndexentries, pPPLT->aAlphaentries,
+                                       pPPLT->aUsedentries);
+#else
+  pData->iPPLTtype          = pPPLT->iType;
+  pData->iPPLTcount         = pPPLT->iCount;
+  pData->paPPLTindexentries = &pPPLT->aIndexentries;
+  pData->paPPLTalphaentries = &pPPLT->aAlphaentries;
+  pData->paPPLTusedentries  = &pPPLT->aUsedentries;
+
+  iRetcode = mng_process_display_pplt (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PPLT, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_magn (mng_datap  pData,
+                                 mng_uint16 iFirstid,
+                                 mng_uint16 iLastid,
+                                 mng_uint8  iMethodX,
+                                 mng_uint16 iMX,
+                                 mng_uint16 iMY,
+                                 mng_uint16 iML,
+                                 mng_uint16 iMR,
+                                 mng_uint16 iMT,
+                                 mng_uint16 iMB,
+                                 mng_uint8  iMethodY)
+#else
+mng_retcode mng_create_ani_magn (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_magnp pMAGN=NULL;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_MAGN, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_magn),
+                                   mng_free_obj_general,
+                                   mng_process_ani_magn,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pMAGN = (mng_ani_magnp)pTemp;
+#else
+    MNG_ALLOC (pData, pMAGN, sizeof (mng_ani_magn));
+
+    pMAGN->sHeader.fCleanup = mng_free_ani_magn;
+    pMAGN->sHeader.fProcess = mng_process_ani_magn;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pMAGN);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pMAGN->iFirstid = iFirstid;
+    pMAGN->iLastid  = iLastid;
+    pMAGN->iMethodX = iMethodX;
+    pMAGN->iMX      = iMX;
+    pMAGN->iMY      = iMY;
+    pMAGN->iML      = iML;
+    pMAGN->iMR      = iMR;
+    pMAGN->iMT      = iMT;
+    pMAGN->iMB      = iMB;
+    pMAGN->iMethodY = iMethodY;
+#else
+    pMAGN->iFirstid = ((mng_magnp)pChunk)->iFirstid;
+    pMAGN->iLastid  = ((mng_magnp)pChunk)->iLastid;
+    pMAGN->iMethodX = ((mng_magnp)pChunk)->iMethodX;
+    pMAGN->iMX      = ((mng_magnp)pChunk)->iMX;
+    pMAGN->iMY      = ((mng_magnp)pChunk)->iMY;
+    pMAGN->iML      = ((mng_magnp)pChunk)->iML;
+    pMAGN->iMR      = ((mng_magnp)pChunk)->iMR;
+    pMAGN->iMT      = ((mng_magnp)pChunk)->iMT;
+    pMAGN->iMB      = ((mng_magnp)pChunk)->iMB;
+    pMAGN->iMethodY = ((mng_magnp)pChunk)->iMethodY;
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_magn (pData, pMAGN->iFirstid, pMAGN->iLastid,
+                                       pMAGN->iMethodX, pMAGN->iMX, pMAGN->iMY,
+                                       pMAGN->iML, pMAGN->iMR, pMAGN->iMT,
+                                       pMAGN->iMB, pMAGN->iMethodY);
+#else
+  iRetcode = mng_process_display_magn (pData,
+                                       ((mng_magnp)pChunk)->iFirstid,
+                                       ((mng_magnp)pChunk)->iLastid,
+                                       ((mng_magnp)pChunk)->iMethodX,
+                                       ((mng_magnp)pChunk)->iMX,
+                                       ((mng_magnp)pChunk)->iMY,
+                                       ((mng_magnp)pChunk)->iML,
+                                       ((mng_magnp)pChunk)->iMR,
+                                       ((mng_magnp)pChunk)->iMT,
+                                       ((mng_magnp)pChunk)->iMB,
+                                       ((mng_magnp)pChunk)->iMethodY);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iMAGNfirstid = iFirstid;
+  pData->iMAGNlastid  = iLastid;
+  pData->iMAGNmethodX = iMethodX;
+  pData->iMAGNmX      = iMX;
+  pData->iMAGNmY      = iMY;
+  pData->iMAGNmL      = iML;
+  pData->iMAGNmR      = iMR;
+  pData->iMAGNmT      = iMT;
+  pData->iMAGNmB      = iMB;
+  pData->iMAGNmethodY = iMethodY;
+#else
+  pData->iMAGNfirstid = ((mng_magnp)pChunk)->iFirstid;
+  pData->iMAGNlastid  = ((mng_magnp)pChunk)->iLastid;
+  pData->iMAGNmethodX = ((mng_magnp)pChunk)->iMethodX;
+  pData->iMAGNmX      = ((mng_magnp)pChunk)->iMX;
+  pData->iMAGNmY      = ((mng_magnp)pChunk)->iMY;
+  pData->iMAGNmL      = ((mng_magnp)pChunk)->iML;
+  pData->iMAGNmR      = ((mng_magnp)pChunk)->iMR;
+  pData->iMAGNmT      = ((mng_magnp)pChunk)->iMT;
+  pData->iMAGNmB      = ((mng_magnp)pChunk)->iMB;
+  pData->iMAGNmethodY = ((mng_magnp)pChunk)->iMethodY;
+#endif
+
+  iRetcode = mng_process_display_magn (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_MAGN, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+mng_retcode mng_free_ani_magn (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_MAGN, MNG_LC_START);
+#endif
+
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_magn));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_MAGN, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_magn (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_magnp pMAGN = (mng_ani_magnp)pObject;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_MAGN, MNG_LC_START);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_magn (pData, pMAGN->iFirstid, pMAGN->iLastid,
+                                       pMAGN->iMethodX, pMAGN->iMX, pMAGN->iMY,
+                                       pMAGN->iML, pMAGN->iMR, pMAGN->iMT,
+                                       pMAGN->iMB, pMAGN->iMethodY);
+#else
+  pData->iMAGNfirstid = pMAGN->iFirstid;
+  pData->iMAGNlastid  = pMAGN->iLastid;
+  pData->iMAGNmethodX = pMAGN->iMethodX;
+  pData->iMAGNmX      = pMAGN->iMX;
+  pData->iMAGNmY      = pMAGN->iMY;
+  pData->iMAGNmL      = pMAGN->iML;
+  pData->iMAGNmR      = pMAGN->iMR;
+  pData->iMAGNmT      = pMAGN->iMT;
+  pData->iMAGNmB      = pMAGN->iMB;
+  pData->iMAGNmethodY = pMAGN->iMethodY;
+
+  iRetcode = mng_process_display_magn (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_MAGN, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_past (mng_datap  pData,
+                                 mng_uint16 iTargetid,
+                                 mng_uint8  iTargettype,
+                                 mng_int32  iTargetx,
+                                 mng_int32  iTargety,
+                                 mng_uint32 iCount,
+                                 mng_ptr    pSources)
+#else
+mng_retcode mng_create_ani_past (mng_datap  pData,
+                                 mng_chunkp pChunk)
+#endif
+{
+  mng_ani_pastp pPAST;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_PAST, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_past),
+                                   mng_free_ani_past,
+                                   mng_process_ani_past,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pPAST = (mng_ani_pastp)pTemp;
+#else
+    MNG_ALLOC (pData, pPAST, sizeof (mng_ani_past));
+
+    pPAST->sHeader.fCleanup = mng_free_ani_past;
+    pPAST->sHeader.fProcess = mng_process_ani_past;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pPAST);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pPAST->iTargetid   = iTargetid;
+    pPAST->iTargettype = iTargettype;
+    pPAST->iTargetx    = iTargetx;
+    pPAST->iTargety    = iTargety;
+    pPAST->iCount      = iCount;
+
+    if (iCount)
+    {
+      MNG_ALLOC (pData, pPAST->pSources, (iCount * sizeof (mng_past_source)));
+      MNG_COPY (pPAST->pSources, pSources, (iCount * sizeof (mng_past_source)));
+    }
+#else
+    pPAST->iTargetid   = ((mng_pastp)pChunk)->iDestid;
+    pPAST->iTargettype = ((mng_pastp)pChunk)->iTargettype;
+    pPAST->iTargetx    = ((mng_pastp)pChunk)->iTargetx;
+    pPAST->iTargety    = ((mng_pastp)pChunk)->iTargety;
+    pPAST->iCount      = ((mng_pastp)pChunk)->iCount;
+
+    if (pPAST->iCount)
+    {
+      mng_size_t iSize = (mng_size_t)(pPAST->iCount * sizeof (mng_past_source));
+      MNG_ALLOC (pData, pPAST->pSources, iSize);
+      MNG_COPY (pPAST->pSources, ((mng_pastp)pChunk)->pSources, iSize);
+    }
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_past (pData, iTargetid, iTargettype,
+                                       iTargetx, iTargety,
+                                       iCount, pSources);
+#else
+  iRetcode = mng_process_display_past (pData,
+                                       ((mng_pastp)pChunk)->iDestid,
+                                       ((mng_pastp)pChunk)->iTargettype,
+                                       ((mng_pastp)pChunk)->iTargetx,
+                                       ((mng_pastp)pChunk)->iTargety,
+                                       ((mng_pastp)pChunk)->iCount,
+                                       ((mng_pastp)pChunk)->pSources);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iPASTtargetid   = iTargetid;
+  pData->iPASTtargettype = iTargettype;
+  pData->iPASTtargetx    = iTargetx;
+  pData->iPASTtargety    = iTargety;
+  pData->iPASTcount      = iCount;
+  pData->pPASTsources    = pSources;
+#else
+  pData->iPASTtargetid   = ((mng_pastp)pChunk)->iDestid;
+  pData->iPASTtargettype = ((mng_pastp)pChunk)->iTargettype;
+  pData->iPASTtargetx    = ((mng_pastp)pChunk)->iTargetx;
+  pData->iPASTtargety    = ((mng_pastp)pChunk)->iTargety;
+  pData->iPASTcount      = ((mng_pastp)pChunk)->iCount;
+  pData->pPASTsources    = ((mng_pastp)pChunk)->pSources;
+#endif
+
+  iRetcode = mng_process_display_past (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_PAST, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_free_ani_past (mng_datap   pData,
+                               mng_objectp pObject)
+{
+  mng_ani_pastp pPAST = (mng_ani_pastp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_PAST, MNG_LC_START);
+#endif
+
+  if (pPAST->iCount)
+    MNG_FREEX (pData, pPAST->pSources, (pPAST->iCount * sizeof (mng_past_source)));
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_past));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_PAST, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  return MNG_NOERROR;
+#else
+  return mng_free_obj_general(pData, pObject);
+#endif
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_process_ani_past (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_pastp pPAST = (mng_ani_pastp)pObject;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PAST, MNG_LC_START);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_past (pData, pPAST->iTargetid, pPAST->iTargettype,
+                                       pPAST->iTargetx, pPAST->iTargety,
+                                       pPAST->iCount, pPAST->pSources);
+#else
+  pData->iPASTtargetid   = pPAST->iTargetid;
+  pData->iPASTtargettype = pPAST->iTargettype;
+  pData->iPASTtargetx    = pPAST->iTargetx;
+  pData->iPASTtargety    = pPAST->iTargety;
+  pData->iPASTcount      = pPAST->iCount;
+  pData->pPASTsources    = pPAST->pSources;
+
+  iRetcode = mng_process_display_past (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PAST, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ani_disc (mng_datap   pData,
+                                 mng_uint32  iCount,
+                                 mng_uint16p pIds)
+#else
+mng_retcode mng_create_ani_disc (mng_datap   pData,
+                                 mng_chunkp  pChunk)
+#endif
+{
+  mng_ani_discp pDISC;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_DISC, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr pTemp;
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_disc),
+                                   mng_free_ani_disc,
+                                   mng_process_ani_disc,
+                                   &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pDISC = (mng_ani_discp)pTemp;
+#else
+    MNG_ALLOC (pData, pDISC, sizeof (mng_ani_disc));
+
+    pDISC->sHeader.fCleanup = mng_free_ani_disc;
+    pDISC->sHeader.fProcess = mng_process_ani_disc;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pDISC);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pDISC->iCount = iCount;
+
+    if (iCount)
+    {
+      MNG_ALLOC (pData, pDISC->pIds, (iCount << 1));
+      MNG_COPY (pDISC->pIds, pIds, (iCount << 1));
+    }
+#else
+    pDISC->iCount = ((mng_discp)pChunk)->iCount;
+
+    if (pDISC->iCount)
+    {
+      mng_size_t iSize = (mng_size_t)(pDISC->iCount << 1);
+      MNG_ALLOC (pData, pDISC->pIds, iSize);
+      MNG_COPY (pDISC->pIds, ((mng_discp)pChunk)->pObjectids, iSize);
+    }
+#endif
+  }
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  iRetcode = mng_process_display_disc (pData, iCount, pIds);
+#else
+  iRetcode = mng_process_display_disc (pData,
+                                       ((mng_discp)pChunk)->iCount,
+                                       ((mng_discp)pChunk)->pObjectids);
+#endif
+#else
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pData->iDISCcount = iCount;
+  pData->pDISCids   = pIds;
+#else
+  pData->iDISCcount = ((mng_discp)pChunk)->iCount;
+  pData->pDISCids   = ((mng_discp)pChunk)->pObjectids;
+#endif
+
+  iRetcode = mng_process_display_disc (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANI_DISC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_ani_disc (mng_datap   pData,
+                               mng_objectp pObject)
+{
+  mng_ani_discp pDISC = (mng_ani_discp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_DISC, MNG_LC_START);
+#endif
+
+  if (pDISC->iCount)
+    MNG_FREEX (pData, pDISC->pIds, (pDISC->iCount << 1));
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  MNG_FREEX (pData, pObject, sizeof (mng_ani_disc));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANI_DISC, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  return MNG_NOERROR;
+#else
+  return mng_free_obj_general(pData, pObject);
+#endif
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_disc (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  mng_ani_discp pDISC = (mng_ani_discp)pObject;
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DISC, MNG_LC_START);
+#endif
+
+#ifndef MNG_OPTIMIZE_DISPLAYCALLS
+  iRetcode = mng_process_display_disc (pData, pDISC->iCount, pDISC->pIds);
+#else
+  pData->iDISCcount = pDISC->iCount;
+  pData->pDISCids   = pDISC->pIds;
+
+  iRetcode = mng_process_display_disc (pData);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DISC, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+#endif
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_event (mng_datap  pData,
+                              mng_uint8  iEventtype,
+                              mng_uint8  iMasktype,
+                              mng_int32  iLeft,
+                              mng_int32  iRight,
+                              mng_int32  iTop,
+                              mng_int32  iBottom,
+                              mng_uint16 iObjectid,
+                              mng_uint8  iIndex,
+                              mng_uint32 iSegmentnamesize,
+                              mng_pchar  zSegmentname)
+#else
+mng_retcode mng_create_event (mng_datap  pData,
+                              mng_ptr    pEntry)
+#endif
+{
+  mng_eventp pEvent;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_EVENT, MNG_LC_START);
+#endif
+
+  if (pData->bCacheplayback)           /* caching playback info ? */
+  {
+    mng_object_headerp pLast;
+
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    mng_ptr     pTemp;
+    mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_event),
+                                               mng_free_event,
+                                               mng_process_event,
+                                               &pTemp);
+    if (iRetcode)
+      return iRetcode;
+    pEvent = (mng_eventp)pTemp;
+#else
+    MNG_ALLOC (pData, pEvent, sizeof (mng_event));
+
+    pEvent->sHeader.fCleanup = mng_free_event;
+    pEvent->sHeader.fProcess = mng_process_event;
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    pEvent->iEventtype       = iEventtype;
+    pEvent->iMasktype        = iMasktype;
+    pEvent->iLeft            = iLeft;
+    pEvent->iRight           = iRight;
+    pEvent->iTop             = iTop;
+    pEvent->iBottom          = iBottom;
+    pEvent->iObjectid        = iObjectid;
+    pEvent->iIndex           = iIndex;
+    pEvent->iSegmentnamesize = iSegmentnamesize;
+
+    if (iSegmentnamesize)
+    {
+      MNG_ALLOC (pData, pEvent->zSegmentname, iSegmentnamesize+1);
+      MNG_COPY (pEvent->zSegmentname, zSegmentname, iSegmentnamesize);
+    }
+#else
+    pEvent->iEventtype       = ((mng_evnt_entryp)pEntry)->iEventtype;
+    pEvent->iMasktype        = ((mng_evnt_entryp)pEntry)->iMasktype;
+    pEvent->iLeft            = ((mng_evnt_entryp)pEntry)->iLeft;
+    pEvent->iRight           = ((mng_evnt_entryp)pEntry)->iRight;
+    pEvent->iTop             = ((mng_evnt_entryp)pEntry)->iTop;
+    pEvent->iBottom          = ((mng_evnt_entryp)pEntry)->iBottom;
+    pEvent->iObjectid        = ((mng_evnt_entryp)pEntry)->iObjectid;
+    pEvent->iIndex           = ((mng_evnt_entryp)pEntry)->iIndex;
+    pEvent->iSegmentnamesize = ((mng_evnt_entryp)pEntry)->iSegmentnamesize;
+
+    if (pEvent->iSegmentnamesize)
+    {
+      MNG_ALLOC (pData, pEvent->zSegmentname, pEvent->iSegmentnamesize+1);
+      MNG_COPY (pEvent->zSegmentname, ((mng_evnt_entryp)pEntry)->zSegmentname, pEvent->iSegmentnamesize);
+    }
+#endif
+                                       /* fixup the double-linked list */
+    pLast                    = (mng_object_headerp)pData->pLastevent;
+
+    if (pLast)                         /* link it as last in the chain */
+    {
+      pEvent->sHeader.pPrev  = pLast;
+      pLast->pNext           = pEvent;
+    }
+    else
+    {
+      pData->pFirstevent     = pEvent;
+    }
+
+    pData->pLastevent        = pEvent;
+    pData->bDynamic          = MNG_TRUE;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_EVENT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_event (mng_datap   pData,
+                            mng_objectp pObject)
+{
+  mng_eventp pEvent = (mng_eventp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_EVENT, MNG_LC_START);
+#endif
+
+  if (pEvent->iSegmentnamesize)
+    MNG_FREEX (pData, pEvent->zSegmentname, pEvent->iSegmentnamesize + 1);
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  MNG_FREEX (pData, pEvent, sizeof (mng_event));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_EVENT, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  return MNG_NOERROR;
+#else
+  return mng_free_obj_general(pData, pObject);
+#endif
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_event (mng_datap   pData,
+                               mng_objectp pObject)
+{
+#ifndef MNG_SKIPCHUNK_SEEK
+  mng_eventp         pEvent  = (mng_eventp)pObject;
+  mng_object_headerp pAni;
+  mng_bool           bFound = MNG_FALSE;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_EVENT, MNG_LC_START);
+#endif
+
+#ifndef MNG_SKIPCHUNK_SEEK
+  if (!pEvent->pSEEK)                  /* need to find SEEK first ? */
+  {
+    pAni = (mng_object_headerp)pData->pFirstaniobj;
+
+    while ((pAni) && (!bFound))
+    {
+      if ((pAni->fCleanup == mng_free_ani_seek) &&
+          (strcmp(pEvent->zSegmentname, ((mng_ani_seekp)pAni)->zSegmentname) == 0))
+        bFound = MNG_TRUE;
+      else
+        pAni = (mng_object_headerp)pAni->pNext;
+    }
+
+    if (pAni)
+      pEvent->pSEEK = (mng_ani_seekp)pAni;
+  }
+
+  if (pEvent->pSEEK)                   /* anything to do ? */
+  {
+    pEvent->iLastx = pData->iEventx;
+    pEvent->iLasty = pData->iEventy;
+                                       /* let's start from this SEEK then */
+    pData->pCurraniobj   = (mng_objectp)pEvent->pSEEK;
+    pData->bRunningevent = MNG_TRUE;
+                                       /* wake-up the app ! */
+    if (!pData->fSettimer ((mng_handle)pData, 5))
+      MNG_ERROR (pData, MNG_APPTIMERERROR);
+
+  }
+  else
+    MNG_ERROR (pData, MNG_SEEKNOTFOUND);
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_EVENT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_SUPPORT_DYNAMICMNG */
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_mpng_obj (mng_datap  pData,
+                                 mng_uint32 iFramewidth,
+                                 mng_uint32 iFrameheight,
+                                 mng_uint16 iNumplays,
+                                 mng_uint16 iTickspersec,
+                                 mng_uint32 iFramessize,
+                                 mng_ptr    pFrames)
+#else
+mng_retcode mng_create_mpng_obj (mng_datap  pData,
+                                 mng_ptr    pEntry)
+#endif
+{
+  mng_mpng_objp pMPNG;
+  mng_ptr       pTemp;
+  mng_retcode   iRetcode;
+  mng_uint8p    pFrame;
+  mng_int32     iCnt, iMax;
+  mng_uint32    iX, iY, iWidth, iHeight;
+  mng_int32     iXoffset, iYoffset;
+  mng_uint16    iTicks;
+  mng_uint16    iDelay;
+  mng_bool      bNewframe;
+  mng_ani_loopp pLOOP;
+  mng_ani_endlp pENDL;
+  mng_ani_framp pFRAM;
+  mng_ani_movep pMOVE;
+  mng_ani_clipp pCLIP;
+  mng_ani_showp pSHOW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_MPNG_OBJ, MNG_LC_START);
+#endif
+
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+  iRetcode = create_obj_general (pData, sizeof (mng_mpng_obj), mng_free_mpng_obj,
+                                 mng_process_mpng_obj, &pTemp);
+  if (iRetcode)
+    return iRetcode;
+  pMPNG = (mng_mpng_objp)pTemp;
+#else
+  MNG_ALLOC (pData, pMPNG, sizeof (mng_mpng_obj));
+
+  pMPNG->sHeader.fCleanup = mng_free_mpng_obj;
+  pMPNG->sHeader.fProcess = mng_process_mpng_obj;
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pMPNG->iFramewidth  = iFramewidth;
+  pMPNG->iFrameheight = iFrameheight;
+  pMPNG->iNumplays    = iNumplays;
+  pMPNG->iTickspersec = iTickspersec;
+  pMPNG->iFramessize  = iFramessize;
+
+  if (iFramessize)
+  {
+    MNG_ALLOC (pData, pMPNG->pFrames, iFramessize);
+    MNG_COPY (pMPNG->pFrames, pFrames, iFramessize);
+  }
+#else
+  pMPNG->iFramewidth  = ((mng_mpngp)pEntry)->iFramewidth;
+  pMPNG->iFrameheight = ((mng_mpngp)pEntry)->iFrameheight;
+  pMPNG->iNumplays    = ((mng_mpngp)pEntry)->iNumplays;
+  pMPNG->iTickspersec = ((mng_mpngp)pEntry)->iTickspersec;
+  pMPNG->iFramessize  = ((mng_mpngp)pEntry)->iFramessize;
+
+  if (pMPNG->iFramessize)
+  {
+    MNG_ALLOC (pData, pMPNG->pFrames, pMPNG->iFramessize);
+    MNG_COPY (pMPNG->pFrames, ((mng_mpngp)pEntry)->pFrames, pMPNG->iFramessize);
+  }
+#endif
+
+  pData->pMPNG      = pMPNG;
+  pData->eImagetype = mng_it_mpng;
+
+  iRetcode = mng_process_display_mpng (pData);
+  if (iRetcode)
+    return iRetcode;
+
+  /* now let's create the MNG animation directives from this */
+
+  pFrame = (mng_uint8p)pMPNG->pFrames;
+  iMax   = pMPNG->iFramessize / 26;
+                                       /* set up MNG impersonation */
+  pData->iTicks      = pMPNG->iTickspersec;
+  pData->iLayercount = iMax;
+
+  if (pMPNG->iNumplays != 1)           /* create a LOOP/ENDL pair ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_loop),
+                                   mng_free_ani_loop, mng_process_ani_loop,
+                                   &((mng_ptr)pLOOP));
+    if (iRetcode)
+      return iRetcode;
+#else
+    MNG_ALLOC (pData, pLOOP, sizeof (mng_ani_loop));
+
+    pLOOP->sHeader.fCleanup = mng_free_ani_loop;
+    pLOOP->sHeader.fProcess = mng_process_ani_loop;
+#endif
+
+    pLOOP->iLevel = 1;
+    if (pMPNG->iNumplays)
+      pLOOP->iRepeatcount = pMPNG->iNumplays;
+    else
+      pLOOP->iRepeatcount = 0xFFFFFFFFl;
+
+    mng_add_ani_object (pData, (mng_object_headerp)pLOOP);
+  }
+
+  bNewframe = MNG_TRUE;                /* create the frame display objects */
+
+  for (iCnt = 0; iCnt < iMax; iCnt++)
+  {
+    iX       = mng_get_uint32 (pFrame);
+    iY       = mng_get_uint32 (pFrame+4);
+    iWidth   = mng_get_uint32 (pFrame+8);
+    iHeight  = mng_get_uint32 (pFrame+12);
+    iXoffset = mng_get_int32  (pFrame+16);
+    iYoffset = mng_get_int32  (pFrame+20);
+    iTicks   = mng_get_uint16 (pFrame+24);
+
+    iDelay = iTicks;
+    if (!iDelay)
+    {
+      mng_uint8p pTemp = pFrame+26;
+      mng_int32  iTemp = iCnt+1;
+
+      while ((iTemp < iMax) && (!iDelay))
+      {
+        iDelay = mng_get_uint16 (pTemp+24);
+        pTemp += 26;
+        iTemp++;
+      }
+    }
+
+    if (bNewframe)
+    {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+      iRetcode = create_obj_general (pData, sizeof (mng_ani_fram),
+                                     mng_free_obj_general, mng_process_ani_fram,
+                                     &((mng_ptr)pFRAM));
+      if (iRetcode)
+        return iRetcode;
+#else
+      MNG_ALLOC (pData, pFRAM, sizeof (mng_ani_fram));
+
+      pFRAM->sHeader.fCleanup = mng_free_ani_fram;
+      pFRAM->sHeader.fProcess = mng_process_ani_fram;
+#endif
+
+      pFRAM->iFramemode   = 4;
+      pFRAM->iChangedelay = 1;
+      pFRAM->iDelay       = iDelay;
+
+      mng_add_ani_object (pData, (mng_object_headerp)pFRAM);
+    }
+
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_move),
+                                   mng_free_obj_general,
+                                   mng_process_ani_move,
+                                   &((mng_ptr)pMOVE));
+    if (iRetcode)
+      return iRetcode;
+#else
+    MNG_ALLOC (pData, pMOVE, sizeof (mng_ani_move));
+
+    pMOVE->sHeader.fCleanup = mng_free_ani_move;
+    pMOVE->sHeader.fProcess = mng_process_ani_move;
+#endif
+
+    pMOVE->iLocax   = iXoffset - (mng_int32)iX;
+    pMOVE->iLocay   = iYoffset - (mng_int32)iY;
+
+    mng_add_ani_object (pData, (mng_object_headerp)pMOVE);
+
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_clip),
+                                   mng_free_obj_general,
+                                   mng_process_ani_clip,
+                                   &((mng_ptr)pCLIP));
+    if (iRetcode)
+      return iRetcode;
+#else
+    MNG_ALLOC (pData, pCLIP, sizeof (mng_ani_clip));
+
+    pCLIP->sHeader.fCleanup = mng_free_ani_clip;
+    pCLIP->sHeader.fProcess = mng_process_ani_clip;
+#endif
+
+    pCLIP->iClipl = iXoffset;
+    pCLIP->iClipr = iXoffset + (mng_int32)iWidth;
+    pCLIP->iClipt = iYoffset;
+    pCLIP->iClipb = iYoffset + (mng_int32)iHeight;
+
+    mng_add_ani_object (pData, (mng_object_headerp)pCLIP);
+
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_show),
+                                   mng_free_obj_general, mng_process_ani_show,
+                                   &((mng_ptr)pSHOW));
+    if (iRetcode)
+      return iRetcode;
+#else
+    MNG_ALLOC (pData, pSHOW, sizeof (mng_ani_show));
+
+    pSHOW->sHeader.fCleanup = mng_free_ani_show;
+    pSHOW->sHeader.fProcess = mng_process_ani_show;
+#endif
+
+    mng_add_ani_object (pData, (mng_object_headerp)pSHOW);
+
+    bNewframe = (mng_bool)iTicks;
+    pFrame += 26;
+  }
+
+  if (pMPNG->iNumplays != 1)           /* create a LOOP/ENDL pair ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_endl),
+                                   mng_free_obj_general, mng_process_ani_endl,
+                                   &((mng_ptr)pENDL));
+    if (iRetcode)
+      return iRetcode;
+#else
+    MNG_ALLOC (pData, pENDL, sizeof (mng_ani_endl));
+
+    pENDL->sHeader.fCleanup = mng_free_ani_endl;
+    pENDL->sHeader.fProcess = mng_process_ani_endl;
+#endif
+
+    pENDL->iLevel = 1;
+
+    mng_add_ani_object (pData, (mng_object_headerp)pENDL);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_MPNG_OBJ, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_mpng_obj (mng_datap   pData,
+                               mng_objectp pObject)
+{
+  mng_mpng_objp pMPNG = (mng_mpng_objp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MPNG_OBJ, MNG_LC_START);
+#endif
+
+  if (pMPNG->iFramessize)
+    MNG_FREEX (pData, pMPNG->pFrames, pMPNG->iFramessize);
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  MNG_FREEX (pData, pMPNG, sizeof (mng_mpng_obj));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_MPNG_OBJ, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  return MNG_NOERROR;
+#else
+  return mng_free_obj_general(pData, pObject);
+#endif
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_mpng_obj (mng_datap   pData,
+                                  mng_objectp pObject)
+{
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_MPNG_PROPOSAL */
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ang_obj (mng_datap  pData,
+                                mng_uint32 iNumframes,
+                                mng_uint32 iTickspersec,
+                                mng_uint32 iNumplays,
+                                mng_uint32 iTilewidth,
+                                mng_uint32 iTileheight,
+                                mng_uint8  iInterlace,
+                                mng_uint8  iStillused)
+#else
+mng_retcode mng_create_ang_obj (mng_datap  pData,
+                                mng_ptr    pEntry)
+#endif
+{
+  mng_ang_objp  pANG;
+  mng_ptr       pTemp;
+  mng_retcode   iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANG_OBJ, MNG_LC_START);
+#endif
+
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+  iRetcode = create_obj_general (pData, sizeof (mng_ang_obj), mng_free_ang_obj,
+                                 mng_process_ang_obj, &pTemp);
+  if (iRetcode)
+    return iRetcode;
+  pANG = (mng_ang_objp)pTemp;
+#else
+  MNG_ALLOC (pData, pANG, sizeof (mng_ang_obj));
+
+  pANG->sHeader.fCleanup = mng_free_ang_obj;
+  pANG->sHeader.fProcess = mng_process_ang_obj;
+#endif
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  pANG->iNumframes   = iNumframes;
+  pANG->iTickspersec = iTickspersec;
+  pANG->iNumplays    = iNumplays;
+  pANG->iTilewidth   = iTilewidth;
+  pANG->iTileheight  = iTileheight;
+  pANG->iInterlace   = iInterlace;
+  pANG->iStillused   = iStillused;
+#else
+  pANG->iNumframes   = ((mng_ahdrp)pEntry)->iNumframes;
+  pANG->iTickspersec = ((mng_ahdrp)pEntry)->iTickspersec;
+  pANG->iNumplays    = ((mng_ahdrp)pEntry)->iNumplays;
+  pANG->iTilewidth   = ((mng_ahdrp)pEntry)->iTilewidth;
+  pANG->iTileheight  = ((mng_ahdrp)pEntry)->iTileheight;
+  pANG->iInterlace   = ((mng_ahdrp)pEntry)->iInterlace;
+  pANG->iStillused   = ((mng_ahdrp)pEntry)->iStillused;
+#endif
+
+  pData->pANG       = pANG;
+  pData->eImagetype = mng_it_ang;
+
+  iRetcode = mng_process_display_ang (pData);
+  if (iRetcode)
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CREATE_ANG_OBJ, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_ang_obj (mng_datap   pData,
+                              mng_objectp pObject)
+{
+  mng_ang_objp pANG = (mng_ang_objp)pObject;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANG_OBJ, MNG_LC_START);
+#endif
+
+  if (pANG->iTilessize)
+    MNG_FREEX (pData, pANG->pTiles, pANG->iTilessize);
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  MNG_FREEX (pData, pANG, sizeof (mng_ang_obj));
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FREE_ANG_OBJ, MNG_LC_END);
+#endif
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+  return MNG_NOERROR;
+#else
+  return mng_free_obj_general(pData, pObject);
+#endif
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ang_obj (mng_datap   pData,
+                                 mng_objectp pObject)
+{
+  mng_ang_objp  pANG  = (mng_ang_objp)pObject;
+  mng_uint8p    pTile = (mng_uint8p)pANG->pTiles;
+  mng_retcode   iRetcode;
+  mng_int32     iCnt, iMax;
+  mng_uint32    iTicks;
+  mng_int32     iXoffset, iYoffset;
+  mng_uint8     iSource;
+  mng_ani_loopp pLOOP;
+  mng_ani_endlp pENDL;
+  mng_ani_framp pFRAM;
+  mng_ani_movep pMOVE;
+  mng_ani_showp pSHOW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANG_OBJ, MNG_LC_START);
+#endif
+
+  /* let's create the MNG animation directives from this */
+
+  iMax = pANG->iNumframes;
+                                       /* set up MNG impersonation */
+  pData->iTicks      = pANG->iTickspersec;
+  pData->iLayercount = iMax;
+
+  if (pANG->iNumplays != 1)            /* create a LOOP/ENDL pair ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_loop),
+                                   mng_free_ani_loop, mng_process_ani_loop,
+                                   &((mng_ptr)pLOOP));
+    if (iRetcode)
+      return iRetcode;
+#else
+    MNG_ALLOC (pData, pLOOP, sizeof (mng_ani_loop));
+
+    pLOOP->sHeader.fCleanup = mng_free_ani_loop;
+    pLOOP->sHeader.fProcess = mng_process_ani_loop;
+#endif
+
+    pLOOP->iLevel = 1;
+    if (pANG->iNumplays)
+      pLOOP->iRepeatcount = pANG->iNumplays;
+    else
+      pLOOP->iRepeatcount = 0xFFFFFFFFl;
+
+    mng_add_ani_object (pData, (mng_object_headerp)pLOOP);
+  }
+
+  for (iCnt = 0; iCnt < iMax; iCnt++)
+  {
+    iTicks   = mng_get_uint32 (pTile);
+    iXoffset = mng_get_int32  (pTile+4);
+    iYoffset = mng_get_int32  (pTile+8);
+    iSource  = *(pTile+12);
+
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_fram),
+                                   mng_free_obj_general, mng_process_ani_fram,
+                                   &((mng_ptr)pFRAM));
+    if (iRetcode)
+      return iRetcode;
+#else
+    MNG_ALLOC (pData, pFRAM, sizeof (mng_ani_fram));
+
+    pFRAM->sHeader.fCleanup = mng_free_ani_fram;
+    pFRAM->sHeader.fProcess = mng_process_ani_fram;
+#endif
+
+    pFRAM->iFramemode   = 4;
+    pFRAM->iChangedelay = 1;
+    pFRAM->iDelay       = iTicks;
+
+    mng_add_ani_object (pData, (mng_object_headerp)pFRAM);
+
+    if (!iSource)
+    {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+      iRetcode = create_obj_general (pData, sizeof (mng_ani_move),
+                                     mng_free_obj_general,
+                                     mng_process_ani_move,
+                                     &((mng_ptr)pMOVE));
+      if (iRetcode)
+        return iRetcode;
+#else
+      MNG_ALLOC (pData, pMOVE, sizeof (mng_ani_move));
+
+      pMOVE->sHeader.fCleanup = mng_free_ani_move;
+      pMOVE->sHeader.fProcess = mng_process_ani_move;
+#endif
+
+      pMOVE->iFirstid = 1;
+      pMOVE->iLastid  = 1;
+      pMOVE->iLocax   = -iXoffset;
+      pMOVE->iLocay   = -iYoffset;
+
+      mng_add_ani_object (pData, (mng_object_headerp)pMOVE);
+    }
+
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_show),
+                                   mng_free_obj_general, mng_process_ani_show,
+                                   &((mng_ptr)pSHOW));
+    if (iRetcode)
+      return iRetcode;
+#else
+    MNG_ALLOC (pData, pSHOW, sizeof (mng_ani_show));
+
+    pSHOW->sHeader.fCleanup = mng_free_ani_show;
+    pSHOW->sHeader.fProcess = mng_process_ani_show;
+#endif
+
+    if (iSource)
+      pSHOW->iFirstid = 0;
+    else
+      pSHOW->iFirstid = 1;
+    pSHOW->iLastid    = pSHOW->iFirstid;
+
+    mng_add_ani_object (pData, (mng_object_headerp)pSHOW);
+
+    pTile += sizeof(mng_adat_tile);
+  }
+
+  if (pANG->iNumplays != 1)            /* create a LOOP/ENDL pair ? */
+  {
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+    iRetcode = create_obj_general (pData, sizeof (mng_ani_endl),
+                                   mng_free_obj_general, mng_process_ani_endl,
+                                   &((mng_ptr)pENDL));
+    if (iRetcode)
+      return iRetcode;
+#else
+    MNG_ALLOC (pData, pENDL, sizeof (mng_ani_endl));
+
+    pENDL->sHeader.fCleanup = mng_free_ani_endl;
+    pENDL->sHeader.fProcess = mng_process_ani_endl;
+#endif
+
+    pENDL->iLevel = 1;
+
+    mng_add_ani_object (pData, (mng_object_headerp)pENDL);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_ANG_OBJ, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_ANG_PROPOSAL */
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_DISPLAY_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_object_prc.h b/files/Source/LibMNG/libmng_object_prc.h
new file mode 100644
index 0000000..ffd20c8
--- /dev/null
+++ b/files/Source/LibMNG/libmng_object_prc.h
@@ -0,0 +1,690 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_object_prc.h       copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Object processing routines (definition)                    * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the internal object processing routines      * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - added support for global color-chunks in animation       * */
+/* *             - added support for global PLTE,tRNS,bKGD in animation     * */
+/* *             - added SAVE & SEEK animation objects                      * */
+/* *             0.5.2 - 05/29/2000 - G.Juyn                                * */
+/* *             - changed ani_object create routines not to return the     * */
+/* *               created object (wasn't necessary)                        * */
+/* *             - added compression/filter/interlace fields to             * */
+/* *               object-buffer for delta-image processing                 * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/22/2000 - G.Juyn                                * */
+/* *             - added support for PPLT chunk                             * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added routine to discard "invalid" objects               * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/13/2002 - G.Juyn                                * */
+/* *             - fixed read/write of MAGN chunk                           * */
+/* *             1.0.5 - 09/15/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 09/23/2002 - G.Juyn                                * */
+/* *             - added in-memory color-correction of abstract images      * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - fixed DISC support                                       * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added conditionals around Delta-PNG code                 * */
+/* *             - added SKIPCHUNK feature                                  * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/24/2004 - G.R-P                                 * */
+/* *             - added more SKIPCHUNK conditionals                        * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_OBJCLEANUP                * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_object_prc_h_
+#define _libmng_object_prc_h_
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_DISPLAY_PROCS
+
+/* ************************************************************************** */
+
+mng_retcode mng_drop_invalid_objects   (mng_datap      pData);
+
+/* ************************************************************************** */
+
+mng_retcode mng_create_imagedataobject (mng_datap      pData,
+                                        mng_bool       bConcrete,
+                                        mng_bool       bViewable,
+                                        mng_uint32     iWidth,
+                                        mng_uint32     iHeight,
+                                        mng_uint8      iBitdepth,
+                                        mng_uint8      iColortype,
+                                        mng_uint8      iCompression,
+                                        mng_uint8      iFilter,
+                                        mng_uint8      iInterlace,
+                                        mng_imagedatap *ppObject);
+
+mng_retcode mng_free_imagedataobject   (mng_datap      pData,
+                                        mng_imagedatap pImagedata);
+
+mng_retcode mng_clone_imagedataobject  (mng_datap      pData,
+                                        mng_bool       bConcrete,
+                                        mng_imagedatap pSource,
+                                        mng_imagedatap *ppClone);
+
+/* ************************************************************************** */
+
+mng_retcode mng_create_imageobject   (mng_datap  pData,
+                                      mng_uint16 iId,
+                                      mng_bool   bConcrete,
+                                      mng_bool   bVisible,
+                                      mng_bool   bViewable,
+                                      mng_uint32 iWidth,
+                                      mng_uint32 iHeight,
+                                      mng_uint8  iBitdepth,
+                                      mng_uint8  iColortype,
+                                      mng_uint8  iCompression,
+                                      mng_uint8  iFilter,
+                                      mng_uint8  iInterlace,
+                                      mng_int32  iPosx,
+                                      mng_int32  iPosy,
+                                      mng_bool   bClipped,
+                                      mng_int32  iClipl,
+                                      mng_int32  iClipr,
+                                      mng_int32  iClipt,
+                                      mng_int32  iClipb,
+                                      mng_imagep *ppObject);
+
+mng_retcode mng_free_imageobject     (mng_datap  pData,
+                                      mng_imagep pImage);
+
+mng_imagep  mng_find_imageobject     (mng_datap  pData,
+                                      mng_uint16 iId);
+
+mng_retcode mng_clone_imageobject    (mng_datap  pData,
+                                      mng_uint16 iId,
+                                      mng_bool   bPartial,
+                                      mng_bool   bVisible,
+                                      mng_bool   bAbstract,
+                                      mng_bool   bHasloca,
+                                      mng_uint8  iLocationtype,
+                                      mng_int32  iLocationx,
+                                      mng_int32  iLocationy,
+                                      mng_imagep pSource,
+                                      mng_imagep *ppClone);
+
+mng_retcode mng_renum_imageobject    (mng_datap  pData,
+                                      mng_imagep pSource,
+                                      mng_uint16 iId,
+                                      mng_bool   bVisible,
+                                      mng_bool   bAbstract,
+                                      mng_bool   bHasloca,
+                                      mng_uint8  iLocationtype,
+                                      mng_int32  iLocationx,
+                                      mng_int32  iLocationy);
+
+mng_retcode mng_reset_object_details (mng_datap  pData,
+                                      mng_imagep pImage,
+                                      mng_uint32 iWidth,
+                                      mng_uint32 iHeight,
+                                      mng_uint8  iBitdepth,
+                                      mng_uint8  iColortype,
+                                      mng_uint8  iCompression,
+                                      mng_uint8  iFilter,
+                                      mng_uint8  iInterlace,
+                                      mng_bool   bResetall);
+
+mng_retcode mng_promote_imageobject  (mng_datap  pData,
+                                      mng_imagep pImage,
+                                      mng_uint8  iBitdepth,
+                                      mng_uint8  iColortype,
+                                      mng_uint8  iFilltype);
+
+mng_retcode mng_magnify_imageobject  (mng_datap  pData,
+                                      mng_imagep pImage);
+
+mng_retcode mng_colorcorrect_object  (mng_datap  pData,
+                                      mng_imagep pImage);
+
+/* ************************************************************************** */
+
+mng_retcode mng_create_ani_image  (mng_datap      pData);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+
+mng_retcode mng_create_ani_plte   (mng_datap      pData,
+                                   mng_uint32     iEntrycount,
+                                   mng_palette8ep paEntries);
+
+mng_retcode mng_create_ani_trns   (mng_datap      pData,
+                                   mng_uint32     iRawlen,
+                                   mng_uint8p     pRawdata);
+
+mng_retcode mng_create_ani_gama   (mng_datap      pData,
+                                   mng_bool       bEmpty,
+                                   mng_uint32     iGamma);
+
+mng_retcode mng_create_ani_chrm   (mng_datap      pData,
+                                   mng_bool       bEmpty,
+                                   mng_uint32     iWhitepointx,
+                                   mng_uint32     iWhitepointy,
+                                   mng_uint32     iRedx,
+                                   mng_uint32     iRedy,
+                                   mng_uint32     iGreenx,
+                                   mng_uint32     iGreeny,
+                                   mng_uint32     iBluex,
+                                   mng_uint32     iBluey);
+
+mng_retcode mng_create_ani_srgb   (mng_datap      pData,
+                                   mng_bool       bEmpty,
+                                   mng_uint8      iRenderinginent);
+
+mng_retcode mng_create_ani_iccp   (mng_datap      pData,
+                                   mng_bool       bEmpty,
+                                   mng_uint32     iProfilesize,
+                                   mng_ptr        pProfile);
+
+mng_retcode mng_create_ani_bkgd   (mng_datap      pData,
+                                   mng_uint16     iRed,
+                                   mng_uint16     iGreen,
+                                   mng_uint16     iBlue);
+
+mng_retcode mng_create_ani_loop   (mng_datap      pData,
+                                   mng_uint8      iLevel,
+                                   mng_uint32     iRepeatcount,
+                                   mng_uint8      iTermcond,
+                                   mng_uint32     iItermin,
+                                   mng_uint32     iItermax,
+                                   mng_uint32     iCount,
+                                   mng_uint32p    pSignals);
+
+mng_retcode mng_create_ani_endl   (mng_datap      pData,
+                                   mng_uint8      iLevel);
+
+mng_retcode mng_create_ani_defi   (mng_datap      pData);
+
+mng_retcode mng_create_ani_basi   (mng_datap      pData,
+                                   mng_uint16     iRed,
+                                   mng_uint16     iGreen,
+                                   mng_uint16     iBlue,
+                                   mng_bool       bHasalpha,
+                                   mng_uint16     iAlpha,
+                                   mng_uint8      iViewable);
+
+mng_retcode mng_create_ani_clon   (mng_datap      pData,
+                                   mng_uint16     iSourceid,
+                                   mng_uint16     iCloneid,
+                                   mng_uint8      iClonetype,
+                                   mng_bool       bHasdonotshow,
+                                   mng_uint8      iDonotshow,
+                                   mng_uint8      iConcrete,
+                                   mng_bool       bHasloca,
+                                   mng_uint8      iLocatype,
+                                   mng_int32      iLocax,
+                                   mng_int32      iLocay);
+
+mng_retcode mng_create_ani_back   (mng_datap      pData,
+                                   mng_uint16     iRed,
+                                   mng_uint16     iGreen,
+                                   mng_uint16     iBlue,
+                                   mng_uint8      iMandatory,
+                                   mng_uint16     iImageid,
+                                   mng_uint8      iTile);
+
+mng_retcode mng_create_ani_fram   (mng_datap      pData,
+                                   mng_uint8      iFramemode,
+                                   mng_uint8      iChangedelay,
+                                   mng_uint32     iDelay,
+                                   mng_uint8      iChangetimeout,
+                                   mng_uint32     iTimeout,
+                                   mng_uint8      iChangeclipping,
+                                   mng_uint8      iCliptype,
+                                   mng_int32      iClipl,
+                                   mng_int32      iClipr,
+                                   mng_int32      iClipt,
+                                   mng_int32      iClipb);
+
+mng_retcode mng_create_ani_move   (mng_datap      pData,
+                                   mng_uint16     iFirstid,
+                                   mng_uint16     iLastid,
+                                   mng_uint8      iType,
+                                   mng_int32      iLocax,
+                                   mng_int32      iLocay);
+
+mng_retcode mng_create_ani_clip   (mng_datap      pData,
+                                   mng_uint16     iFirstid,
+                                   mng_uint16     iLastid,
+                                   mng_uint8      iType,
+                                   mng_int32      iClipl,
+                                   mng_int32      iClipr,
+                                   mng_int32      iClipt,
+                                   mng_int32      iClipb);
+
+mng_retcode mng_create_ani_show   (mng_datap      pData,
+                                   mng_uint16     iFirstid,
+                                   mng_uint16     iLastid,
+                                   mng_uint8      iMode);
+
+mng_retcode mng_create_ani_term   (mng_datap      pData,
+                                   mng_uint8      iTermaction,
+                                   mng_uint8      iIteraction,
+                                   mng_uint32     iDelay,
+                                   mng_uint32     iItermax);
+
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode mng_create_ani_save   (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_retcode mng_create_ani_seek   (mng_datap      pData,
+                                   mng_uint32     iSegmentnamesize,
+                                   mng_pchar      zSegmentname);
+#endif
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_create_ani_dhdr   (mng_datap      pData,
+                                   mng_uint16     iObjectid,
+                                   mng_uint8      iImagetype,
+                                   mng_uint8      iDeltatype,
+                                   mng_uint32     iBlockwidth,
+                                   mng_uint32     iBlockheight,
+                                   mng_uint32     iBlockx,
+                                   mng_uint32     iBlocky);
+
+mng_retcode mng_create_ani_prom   (mng_datap      pData,
+                                   mng_uint8      iBitdepth,
+                                   mng_uint8      iColortype,
+                                   mng_uint8      iFilltype);
+
+mng_retcode mng_create_ani_ipng   (mng_datap      pData);
+mng_retcode mng_create_ani_ijng   (mng_datap      pData);
+
+mng_retcode mng_create_ani_pplt   (mng_datap      pData,
+                                   mng_uint8      iType,
+                                   mng_uint32     iCount,
+                                   mng_palette8ep paIndexentries,
+                                   mng_uint8p     paAlphaentries,
+                                   mng_uint8p     paUsedentries);
+#endif
+
+#ifndef MNG_SKIPCHUNK_MAGN
+mng_retcode mng_create_ani_magn   (mng_datap      pData,
+                                   mng_uint16     iFirstid,
+                                   mng_uint16     iLastid,
+                                   mng_uint8      iMethodX,
+                                   mng_uint16     iMX,
+                                   mng_uint16     iMY,
+                                   mng_uint16     iML,
+                                   mng_uint16     iMR,
+                                   mng_uint16     iMT,
+                                   mng_uint16     iMB,
+                                   mng_uint8      iMethodY);
+#endif
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_create_ani_past   (mng_datap      pData,
+                                   mng_uint16     iTargetid,
+                                   mng_uint8      iTargettype,
+                                   mng_int32      iTargetx,
+                                   mng_int32      iTargety,
+                                   mng_uint32     iCount,
+                                   mng_ptr        pSources);
+#endif
+
+#ifndef MNG_SKIPCHUNK_DISC
+mng_retcode mng_create_ani_disc   (mng_datap      pData,
+                                   mng_uint32     iCount,
+                                   mng_uint16p    pIds);
+#endif
+
+#else /* MNG_OPTIMIZE_CHUNKREADER */
+
+mng_retcode mng_create_ani_plte   (mng_datap      pData);
+mng_retcode mng_create_ani_trns   (mng_datap      pData);
+mng_retcode mng_create_ani_gama   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_chrm   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_srgb   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_iccp   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_bkgd   (mng_datap      pData);
+mng_retcode mng_create_ani_loop   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_endl   (mng_datap      pData,
+                                   mng_uint8      iLevel);
+mng_retcode mng_create_ani_defi   (mng_datap      pData);
+mng_retcode mng_create_ani_basi   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_clon   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_back   (mng_datap      pData);
+mng_retcode mng_create_ani_fram   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_move   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_clip   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_show   (mng_datap      pData);
+mng_retcode mng_create_ani_term   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode mng_create_ani_save   (mng_datap      pData);
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_retcode mng_create_ani_seek   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+#endif
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_create_ani_dhdr   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_prom   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+mng_retcode mng_create_ani_ipng   (mng_datap      pData);
+mng_retcode mng_create_ani_ijng   (mng_datap      pData);
+
+mng_retcode mng_create_ani_pplt   (mng_datap      pData,
+                                   mng_uint8      iType,
+                                   mng_uint32     iCount,
+                                   mng_palette8ep paIndexentries,
+                                   mng_uint8p     paAlphaentries,
+                                   mng_uint8p     paUsedentries);
+#endif
+
+#ifndef MNG_SKIPCHUNK_MAGN
+mng_retcode mng_create_ani_magn   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_create_ani_past   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+#endif
+#ifndef MNG_SKIPCHUNK_DISC
+mng_retcode mng_create_ani_disc   (mng_datap      pData,
+                                   mng_chunkp     pChunk);
+#endif
+
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+
+/* ************************************************************************** */
+
+mng_retcode mng_free_ani_image    (mng_datap    pData,
+                                   mng_objectp  pObject);
+
+#ifndef MNG_OPTIMIZE_OBJCLEANUP
+
+mng_retcode mng_free_ani_plte     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_trns     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_gama     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#ifndef MNG_SKIPCHUNK_cHRM
+mng_retcode mng_free_ani_chrm     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_sRGB
+mng_retcode mng_free_ani_srgb     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+mng_retcode mng_free_ani_bkgd     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_LOOP
+mng_retcode mng_free_ani_endl     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+mng_retcode mng_free_ani_defi     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_basi     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_clon     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_back     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_fram     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_move     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_clip     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_show     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_term     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode mng_free_ani_save     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_free_ani_dhdr     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_prom     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_ipng     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_ijng     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_free_ani_pplt     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_MAGN
+mng_retcode mng_free_ani_magn     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+
+#endif /* MNG_OPTIMIZE_OBJCLEANUP */
+
+
+#ifndef MNG_SKIPCHUNK_iCCP
+mng_retcode mng_free_ani_iccp     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_LOOP
+mng_retcode mng_free_ani_loop     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode mng_free_ani_seek     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_free_ani_past     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+mng_retcode mng_free_ani_disc     (mng_datap    pData,
+                                   mng_objectp  pObject);
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ani_image (mng_datap    pData,
+                                   mng_objectp  pObject);
+
+mng_retcode mng_process_ani_plte  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_trns  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_gama  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#ifndef MNG_SKIPCHUNK_cHRM
+mng_retcode mng_process_ani_chrm  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_sRGB
+mng_retcode mng_process_ani_srgb  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_iCCP
+mng_retcode mng_process_ani_iccp  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+mng_retcode mng_process_ani_bkgd  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_LOOP
+mng_retcode mng_process_ani_loop  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_endl  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+mng_retcode mng_process_ani_defi  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_basi  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_clon  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_back  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_fram  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_move  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_clip  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_show  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_term  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#ifndef MNG_SKIPCHUNK_SAVE
+mng_retcode mng_process_ani_save  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+mng_retcode mng_process_ani_seek  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_process_ani_dhdr  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_prom  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_ipng  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_ijng  (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ani_pplt  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+mng_retcode mng_process_ani_magn  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_process_ani_past  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+mng_retcode mng_process_ani_disc  (mng_datap    pData,
+                                   mng_objectp  pObject);
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_event      (mng_datap    pData,
+                                   mng_uint8    iEventtype,
+                                   mng_uint8    iMasktype,
+                                   mng_int32    iLeft,
+                                   mng_int32    iRight,
+                                   mng_int32    iTop,
+                                   mng_int32    iBottom,
+                                   mng_uint16   iObjectid,
+                                   mng_uint8    iIndex,
+                                   mng_uint32   iSegmentnamesize,
+                                   mng_pchar    zSegmentname);
+#else
+mng_retcode mng_create_event      (mng_datap    pData,
+                                   mng_ptr      pEntry);
+#endif
+mng_retcode mng_free_event        (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_event     (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_mpng_obj   (mng_datap    pData,
+                                   mng_uint32   iFramewidth,
+                                   mng_uint32   iFrameheight,
+                                   mng_uint16   iNumplays,
+                                   mng_uint16   iTickspersec,
+                                   mng_uint32   iFramessize,
+                                   mng_ptr      pFrames);
+#else
+mng_retcode mng_create_mpng_obj   (mng_datap    pData,
+                                   mng_ptr      pEntry);
+#endif
+mng_retcode mng_free_mpng_obj     (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_mpng_obj  (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+mng_retcode mng_create_ang_obj    (mng_datap    pData,
+                                   mng_uint32   iNumframes,
+                                   mng_uint32   iTickspersec,
+                                   mng_uint32   iNumplays,
+                                   mng_uint32   iTilewidth,
+                                   mng_uint32   iTileheight,
+                                   mng_uint8    iInterlace,
+                                   mng_uint8    iStillused);
+#else
+mng_retcode mng_create_ang_obj    (mng_datap    pData,
+                                   mng_ptr      pEntry);
+#endif
+mng_retcode mng_free_ang_obj      (mng_datap    pData,
+                                   mng_objectp  pObject);
+mng_retcode mng_process_ang_obj   (mng_datap    pData,
+                                   mng_objectp  pObject);
+#endif
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_DISPLAY_PROCS */
+
+/* ************************************************************************** */
+
+#endif /* _libmng_object_prc_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_objects.h b/files/Source/LibMNG/libmng_objects.h
new file mode 100644
index 0000000..053e6b4
--- /dev/null
+++ b/files/Source/LibMNG/libmng_objects.h
@@ -0,0 +1,635 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_objects.h          copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Internal object structures (definition)                    * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the internal object structures               * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/23/2000 - G.Juyn                                * */
+/* *             - changed inclusion to DISPLAY_PROCS                       * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - added global color-chunks for animations                 * */
+/* *             - added global PLTE,tRNS,bKGD chunks for animation         * */
+/* *             - added SAVE & SEEK animation objects                      * */
+/* *             0.5.2 - 05/29/2000 - G.Juyn                                * */
+/* *             - added framenr/layernr/playtime to object header          * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added ani-objects for delta-image processing             * */
+/* *             - added compression/filter/interlace fields to             * */
+/* *               object-buffer for delta-image processing                 * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/17/2000 - G.Juyn                                * */
+/* *             - changed definition of aTRNSentries                       * */
+/* *             0.5.3 - 06/22/2000 - G.Juyn                                * */
+/* *             - added definition for PPLT animation-processing           * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/10/2000 - G.Juyn                                * */
+/* *             - fixed DEFI behavior                                      * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added support for delta-JNG                              * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added valid-flag to stored objects for read() / display()* */
+/* *             0.9.3 - 10/19/2000 - G.Juyn                                * */
+/* *             - added storage for pixel-/alpha-sampledepth for delta's   * */
+/* *                                                                        * */
+/* *             1.0.5 - 09/13/2002 - G.Juyn                                * */
+/* *             - fixed read/write of MAGN chunk                           * */
+/* *             1.0.5 - 09/15/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 09/23/2002 - G.Juyn                                * */
+/* *             - added in-memory color-correction of abstract images      * */
+/* *             1.0.5 - 10/07/2002 - G.Juyn                                * */
+/* *             - fixed DISC support                                       * */
+/* *                                                                        * */
+/* *             1.0.6 - 10/07/2003 - G.R-P                                 * */
+/* *             - added SKIPCHUNK conditionals                             * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/24/2004 - G.R-P                                 * */
+/* *             - added more SKIPCHUNK conditionals                        * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_OBJCLEANUP                * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_objects_h_
+#define _libmng_objects_h_
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_DISPLAY_PROCS
+
+/* ************************************************************************** */
+
+typedef mng_retcode (*mng_cleanupobject) (mng_datap    pData,
+                                          mng_objectp  pHeader);
+
+typedef mng_retcode (*mng_processobject) (mng_datap    pData,
+                                          mng_objectp  pHeader);
+
+/* ************************************************************************** */
+
+typedef struct {
+           mng_cleanupobject fCleanup;
+           mng_processobject fProcess;
+           mng_objectp       pNext;              /* for double-linked list */
+           mng_objectp       pPrev;
+           mng_uint32        iFramenr;
+           mng_uint32        iLayernr;
+           mng_uint32        iPlaytime;
+#ifdef MNG_OPTIMIZE_OBJCLEANUP
+           mng_size_t        iObjsize;                    
+#endif
+        } mng_object_header;
+typedef mng_object_header * mng_object_headerp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* MNG specification "object-buffer" */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint32        iRefcount;          /* reference counter */
+           mng_bool          bFrozen;            /* frozen flag */
+           mng_bool          bConcrete;          /* concrete flag */
+           mng_bool          bViewable;          /* viewable flag */
+           mng_uint32        iWidth;             /* image specifics */
+           mng_uint32        iHeight;
+           mng_uint8         iBitdepth;
+           mng_uint8         iColortype;
+           mng_uint8         iCompression;
+           mng_uint8         iFilter;
+           mng_uint8         iInterlace;
+
+           mng_bool          bCorrected;         /* indicates if an abstract image
+                                                    has already been color-corrected */
+           
+           mng_uint8         iAlphabitdepth;     /* used only for JNG images */
+           mng_uint8         iJHDRcompression;
+           mng_uint8         iJHDRinterlace;
+
+           mng_uint8         iPixelsampledepth;  /* used with delta-images */
+           mng_uint8         iAlphasampledepth;
+
+           mng_bool          bHasPLTE;           /* PLTE chunk present */
+           mng_bool          bHasTRNS;           /* tRNS chunk present */
+           mng_bool          bHasGAMA;           /* gAMA chunk present */
+           mng_bool          bHasCHRM;           /* cHRM chunk present */
+           mng_bool          bHasSRGB;           /* sRGB chunk present */
+           mng_bool          bHasICCP;           /* iCCP chunk present */
+           mng_bool          bHasBKGD;           /* bKGD chunk present */
+
+           mng_uint32        iPLTEcount;         /* PLTE fields */
+           mng_rgbpaltab     aPLTEentries;
+
+           mng_uint16        iTRNSgray;          /* tRNS fields */
+           mng_uint16        iTRNSred;
+           mng_uint16        iTRNSgreen;
+           mng_uint16        iTRNSblue;
+           mng_uint32        iTRNScount;
+           mng_uint8arr      aTRNSentries;
+
+           mng_uint32        iGamma;             /* gAMA fields */
+
+           mng_uint32        iWhitepointx;       /* cHRM fields */
+           mng_uint32        iWhitepointy;
+           mng_uint32        iPrimaryredx;
+           mng_uint32        iPrimaryredy;
+           mng_uint32        iPrimarygreenx;
+           mng_uint32        iPrimarygreeny;
+           mng_uint32        iPrimarybluex;
+           mng_uint32        iPrimarybluey;
+
+           mng_uint8         iRenderingintent;   /* sRGB fields */
+
+           mng_uint32        iProfilesize;       /* iCCP fields */
+           mng_ptr           pProfile;
+
+           mng_uint8         iBKGDindex;         /* bKGD fields */
+           mng_uint16        iBKGDgray;
+           mng_uint16        iBKGDred;
+           mng_uint16        iBKGDgreen;
+           mng_uint16        iBKGDblue;
+
+           mng_uint32        iSamplesize;        /* size of a sample */
+           mng_uint32        iRowsize;           /* size of a row of samples */
+           mng_uint32        iImgdatasize;       /* size of the sample data buffer */
+           mng_uint8p        pImgdata;           /* actual sample data buffer */
+
+         } mng_imagedata;
+typedef mng_imagedata * mng_imagedatap;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* MNG specification "object" */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iId;                /* object-id */
+           mng_bool          bFrozen;            /* frozen flag */
+           mng_bool          bVisible;           /* potential visibility flag */
+           mng_bool          bViewable;          /* viewable flag */
+           mng_bool          bValid;             /* marks invalid when only reading */
+           mng_int32         iPosx;              /* location fields */
+           mng_int32         iPosy;
+           mng_bool          bClipped;           /* clipping fields */
+           mng_int32         iClipl;
+           mng_int32         iClipr;
+           mng_int32         iClipt;
+           mng_int32         iClipb;
+#ifndef MNG_SKIPCHUNK_MAGN
+           mng_uint8         iMAGN_MethodX;      /* magnification (MAGN) */
+           mng_uint8         iMAGN_MethodY;
+           mng_uint16        iMAGN_MX;
+           mng_uint16        iMAGN_MY;
+           mng_uint16        iMAGN_ML;
+           mng_uint16        iMAGN_MR;
+           mng_uint16        iMAGN_MT;
+           mng_uint16        iMAGN_MB;
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+           mng_int32         iPastx;             /* target x/y from previous PAST */
+           mng_int32         iPasty;
+#endif
+           mng_imagedatap    pImgbuf;            /* the image-data buffer */
+        } mng_image;
+typedef mng_image * mng_imagep;
+
+/* ************************************************************************** */
+
+                                                 /* "on-the-fly" image (= object 0) */       
+typedef mng_image mng_ani_image;                 /* let's (ab)use the general "object" */
+typedef mng_ani_image * mng_ani_imagep;          /* that's actualy crucial, so don't change it! */
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* global PLTE object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint32        iEntrycount;
+           mng_rgbpaltab     aEntries;
+        } mng_ani_plte;
+typedef mng_ani_plte * mng_ani_pltep;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* global tRNS object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint32        iRawlen;
+           mng_uint8arr      aRawdata;
+        } mng_ani_trns;
+typedef mng_ani_trns * mng_ani_trnsp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* global gAMA object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_bool          bEmpty;
+           mng_uint32        iGamma;
+        } mng_ani_gama;
+typedef mng_ani_gama * mng_ani_gamap;
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_cHRM
+typedef struct {                                 /* global cHRM object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_bool          bEmpty;
+           mng_uint32        iWhitepointx;
+           mng_uint32        iWhitepointy;
+           mng_uint32        iRedx;
+           mng_uint32        iRedy;
+           mng_uint32        iGreenx;
+           mng_uint32        iGreeny;
+           mng_uint32        iBluex;
+           mng_uint32        iBluey;
+        } mng_ani_chrm;
+typedef mng_ani_chrm * mng_ani_chrmp;
+#endif
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* global sRGB object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_bool          bEmpty;
+           mng_uint8         iRenderingintent;
+        } mng_ani_srgb;
+typedef mng_ani_srgb * mng_ani_srgbp;
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_iCCP
+typedef struct {                                 /* global iCCP object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_bool          bEmpty;
+           mng_uint32        iProfilesize;
+           mng_ptr           pProfile;
+        } mng_ani_iccp;
+typedef mng_ani_iccp * mng_ani_iccpp;
+#endif
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* global bKGD object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iRed;
+           mng_uint16        iGreen;
+           mng_uint16        iBlue;
+        } mng_ani_bkgd;
+typedef mng_ani_bkgd * mng_ani_bkgdp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* LOOP object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint8         iLevel;
+           mng_uint32        iRepeatcount;
+           mng_uint8         iTermcond;
+           mng_uint32        iItermin;
+           mng_uint32        iItermax;
+           mng_uint32        iCount;
+           mng_uint32p       pSignals;
+
+           mng_uint32        iRunningcount;      /* running counter */
+        } mng_ani_loop;
+typedef mng_ani_loop * mng_ani_loopp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* ENDL object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint8         iLevel;
+
+           mng_ani_loopp     pLOOP;              /* matching LOOP */
+        } mng_ani_endl;
+typedef mng_ani_endl * mng_ani_endlp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* DEFI object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iId;                
+           mng_bool          bHasdonotshow;
+           mng_uint8         iDonotshow;
+           mng_bool          bHasconcrete;
+           mng_uint8         iConcrete;
+           mng_bool          bHasloca;           
+           mng_int32         iLocax;
+           mng_int32         iLocay;
+           mng_bool          bHasclip;
+           mng_int32         iClipl;
+           mng_int32         iClipr;
+           mng_int32         iClipt;
+           mng_int32         iClipb;
+        } mng_ani_defi;
+typedef mng_ani_defi * mng_ani_defip;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* BASI object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iRed;               
+           mng_uint16        iGreen;             
+           mng_uint16        iBlue;              
+           mng_bool          bHasalpha;             
+           mng_uint16        iAlpha;
+           mng_uint8         iViewable;
+        } mng_ani_basi;
+typedef mng_ani_basi * mng_ani_basip;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* CLON object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iCloneid;
+           mng_uint16        iSourceid;
+           mng_uint8         iClonetype;
+           mng_bool          bHasdonotshow;
+           mng_uint8         iDonotshow;
+           mng_uint8         iConcrete;
+           mng_bool          bHasloca;
+           mng_uint8         iLocatype;
+           mng_int32         iLocax;
+           mng_int32         iLocay;
+        } mng_ani_clon;
+typedef mng_ani_clon * mng_ani_clonp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* BACK object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iRed;
+           mng_uint16        iGreen;
+           mng_uint16        iBlue;
+           mng_uint8         iMandatory;
+           mng_uint16        iImageid;
+           mng_uint8         iTile;
+        } mng_ani_back;
+typedef mng_ani_back * mng_ani_backp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* FRAM object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint8         iFramemode;
+           mng_uint8         iChangedelay;
+           mng_uint32        iDelay;
+           mng_uint8         iChangetimeout;
+           mng_uint32        iTimeout;
+           mng_uint8         iChangeclipping;
+           mng_uint8         iCliptype;
+           mng_int32         iClipl;
+           mng_int32         iClipr;
+           mng_int32         iClipt;
+           mng_int32         iClipb;
+        } mng_ani_fram;
+typedef mng_ani_fram * mng_ani_framp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* MOVE object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iFirstid;           
+           mng_uint16        iLastid;            
+           mng_uint8         iType;              
+           mng_int32         iLocax;             
+           mng_int32         iLocay;
+        } mng_ani_move;
+typedef mng_ani_move * mng_ani_movep;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* CLIP object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iFirstid;           
+           mng_uint16        iLastid;            
+           mng_uint8         iType;              
+           mng_int32         iClipl;             
+           mng_int32         iClipr;             
+           mng_int32         iClipt;             
+           mng_int32         iClipb;
+        } mng_ani_clip;
+typedef mng_ani_clip * mng_ani_clipp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* SHOW object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iFirstid;           
+           mng_uint16        iLastid;            
+           mng_uint8         iMode;
+        } mng_ani_show;
+typedef mng_ani_show * mng_ani_showp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* TERM object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint8         iTermaction;        
+           mng_uint8         iIteraction;        
+           mng_uint32        iDelay;             
+           mng_uint32        iItermax;
+        } mng_ani_term;
+typedef mng_ani_term * mng_ani_termp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* SAVE object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+        } mng_ani_save;
+typedef mng_ani_save * mng_ani_savep;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* SEEK object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint32        iSegmentnamesize;
+           mng_pchar         zSegmentname;
+        } mng_ani_seek;
+typedef mng_ani_seek * mng_ani_seekp;
+
+/* ************************************************************************** */
+#ifndef MNG_NO_DELTA_PNG
+typedef struct {                                 /* DHDR object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iObjectid;
+           mng_uint8         iImagetype;
+           mng_uint8         iDeltatype;
+           mng_uint32        iBlockwidth;
+           mng_uint32        iBlockheight;
+           mng_uint32        iBlockx;
+           mng_uint32        iBlocky;
+        } mng_ani_dhdr;
+typedef mng_ani_dhdr * mng_ani_dhdrp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* PROM object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint8         iBitdepth;
+           mng_uint8         iColortype;
+           mng_uint8         iFilltype;
+        } mng_ani_prom;
+typedef mng_ani_prom * mng_ani_promp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* IPNG object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+        } mng_ani_ipng;
+typedef mng_ani_ipng * mng_ani_ipngp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* IJNG object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+        } mng_ani_ijng;
+typedef mng_ani_ijng * mng_ani_ijngp;
+
+/* ************************************************************************** */
+
+typedef struct {                                 /* PPLT object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint8         iType;
+           mng_uint32        iCount;
+           mng_rgbpaltab     aIndexentries;
+           mng_uint8arr      aAlphaentries;
+           mng_uint8arr      aUsedentries;
+        } mng_ani_pplt;
+typedef mng_ani_pplt * mng_ani_ppltp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+typedef struct {                                 /* MAGN object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iFirstid;
+           mng_uint16        iLastid;
+           mng_uint8         iMethodX;
+           mng_uint16        iMX;
+           mng_uint16        iMY;
+           mng_uint16        iML;
+           mng_uint16        iMR;
+           mng_uint16        iMT;
+           mng_uint16        iMB;
+           mng_uint8         iMethodY;
+        } mng_ani_magn;
+typedef mng_ani_magn * mng_ani_magnp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+typedef struct {                                 /* PAST object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint16        iTargetid;
+           mng_uint8         iTargettype;
+           mng_int32         iTargetx;
+           mng_int32         iTargety;
+           mng_uint32        iCount;
+           mng_ptr           pSources;
+        } mng_ani_past;
+typedef mng_ani_past * mng_ani_pastp;
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_DISC
+typedef struct {                                 /* DISC object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint32        iCount;
+           mng_uint16p       pIds;
+        } mng_ani_disc;
+typedef mng_ani_disc * mng_ani_discp;
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+typedef struct {                                 /* event object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint8         iEventtype;
+           mng_uint8         iMasktype;
+           mng_int32         iLeft;
+           mng_int32         iRight;
+           mng_int32         iTop;
+           mng_int32         iBottom;
+           mng_uint16        iObjectid;
+           mng_uint8         iIndex;
+           mng_uint32        iSegmentnamesize;
+           mng_pchar         zSegmentname;
+
+           mng_ani_seekp     pSEEK;              /* SEEK ani object */
+           mng_int32         iLastx;             /* last X/Y coordinates */
+           mng_int32         iLasty;
+        } mng_event;
+typedef mng_event * mng_eventp;
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+typedef struct {                                 /* mPNG object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint32        iFramewidth;
+           mng_uint32        iFrameheight;
+           mng_uint32        iNumplays;
+           mng_uint16        iTickspersec;
+           mng_uint32        iFramessize;
+           mng_ptr           pFrames;
+        } mng_mpng_obj;
+typedef mng_mpng_obj * mng_mpng_objp;
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+typedef struct {                                 /* ANG object */
+           mng_object_header sHeader;            /* default header (DO NOT REMOVE) */
+           mng_uint32        iNumframes;
+           mng_uint32        iTickspersec;
+           mng_uint32        iNumplays;
+           mng_uint32        iTilewidth;
+           mng_uint32        iTileheight;
+           mng_uint8         iInterlace;
+           mng_uint8         iStillused;
+           mng_uint32        iTilessize;
+           mng_ptr           pTiles;
+        } mng_ang_obj;
+typedef mng_ang_obj * mng_ang_objp;
+#endif
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_DISPLAY_PROCS */
+
+/* ************************************************************************** */
+
+#endif /* _libmng_objects_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_pixels.c b/files/Source/LibMNG/libmng_pixels.c
new file mode 100644
index 0000000..ce5637b
--- /dev/null
+++ b/files/Source/LibMNG/libmng_pixels.c
@@ -0,0 +1,24610 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_pixels.c           copyright (c) 2000-2005 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Pixel-row management routines (implementation)             * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the pixel-row management routines        * */
+/* *                                                                        * */
+/* *             the dual alpha-composing for RGBA/BGRA/etc output-canvas'  * */
+/* *             is based on the Note on Compositing chapter of the         * */
+/* *             DOH-3 draft, noted to me by Adam M. Costello               * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added callback error-reporting support                   * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/22/2000 - G.Juyn                                * */
+/* *             - added JNG support                                        * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - fixed minor bugs 16-bit pixel-handling                   * */
+/* *             - added delta-image row-processing routines                * */
+/* *             0.5.2 - 06/02/2000 - G.Juyn                                * */
+/* *             - fixed endian support (hopefully)                         * */
+/* *             0.5.2 - 06/03/2000 - G.Juyn                                * */
+/* *             - fixed makeup for Linux gcc compile                       * */
+/* *             0.5.2 - 06/05/2000 - G.Juyn                                * */
+/* *             - implemented app bkgd restore routines                    * */
+/* *             - implemented RGBA8, ARGB8, BGRA8 & ABGR8 display routines * */
+/* *             - added support for RGB8_A8 canvasstyle                    * */
+/* *             0.5.2 - 06/09/2000 - G.Juyn                                * */
+/* *             - fixed alpha-handling for alpha canvasstyles              * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/16/2000 - G.Juyn                                * */
+/* *             - changed progressive-display processing                   * */
+/* *             0.5.3 - 06/17/2000 - G.Juyn                                * */
+/* *             - changed to support delta-images                          * */
+/* *             - optimized some store_xxx routines                        * */
+/* *             0.5.3 - 06/20/2000 - G.Juyn                                * */
+/* *             - fixed nasty bug with embedded PNG after delta-image      * */
+/* *             0.5.3 - 06/24/2000 - G.Juyn                                * */
+/* *             - fixed problem with 16-bit GA format                      * */
+/* *             0.5.3 - 06/25/2000 - G.Juyn                                * */
+/* *             - fixed problem with cheap transparency for 4-bit gray     * */
+/* *             - fixed display_xxxx routines for interlaced images        * */
+/* *             0.5.3 - 06/28/2000 - G.Juyn                                * */
+/* *             - fixed compiler-warning for non-initialized iB variable   * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/05/2000 - G.Juyn                                * */
+/* *             - fixed mandatory BACK color to be opaque                  * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/31/2000 - G.Juyn                                * */
+/* *             - B110547 - fixed bug in interlace code                    * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/20/2000 - G.Juyn                                * */
+/* *             - fixed app-supplied background restore                    * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *             0.9.3 - 09/30/2000 - G.Juyn                                * */
+/* *             - fixed MAGN rounding errors (thanks Matthias!)            * */
+/* *             0.9.3 - 10/10/2000 - G.Juyn                                * */
+/* *             - fixed alpha-blending for RGBA canvasstyle                * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - fixed alpha-blending for other alpha-canvasstyles        * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added optional support for bKGD for PNG images           * */
+/* *             - added support for JDAA                                   * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - fixed support for bKGD                                   * */
+/* *             0.9.3 - 10/19/2000 - G.Juyn                                * */
+/* *             - implemented delayed delta-processing                     * */
+/* *             0.9.3 - 10/28/2000 - G.Juyn                                * */
+/* *             - fixed tRNS processing for gray-image < 8-bits            * */
+/* *                                                                        * */
+/* *             0.9.4 - 12/16/2000 - G.Juyn                                * */
+/* *             - fixed mixup of data- & function-pointers (thanks Dimitri)* */
+/* *             0.9.4 -  1/18/2001 - G.Juyn                                * */
+/* *             - removed "old" MAGN methods 3 & 4                         * */
+/* *             - added "new" MAGN methods 3, 4 & 5                        * */
+/* *             - removed test filter-methods 1 & 65                       * */
+/* *                                                                        * */
+/* *             1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly)              * */
+/* *             - added BGRA8 canvas with premultiplied alpha              * */
+/* *             1.0.1 - 04/25/2001 - G.Juyn                                * */
+/* *             - moved mng_clear_cms to libmng_cms                        * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/25/2001 - G.Juyn                                * */
+/* *             - added option to turn off progressive refresh             * */
+/* *                                                                        * */
+/* *             1.0.4 - 11/04/2001 - G.Juyn                                * */
+/* *             - fixed possible compile-problem in cleanup_rowproc        * */
+/* *             1.0.4 - 06/22/2002 - G.Juyn                                * */
+/* *             - B558212 - off by one error                               * */
+/* *             - MNG subimage alpha composite wrong for rgba8 images      * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/07/2002 - G.Juyn                                * */
+/* *             - added test-option for PNG filter method 193 (=no filter) * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed PROM support                                   * */
+/* *             - completed delta-image support                            * */
+/* *             1.0.5 - 08/16/2002 - G.Juyn                                * */
+/* *             - completed MAGN support (16-bit functions)                * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/19/2002 - G.Juyn                                * */
+/* *             - optimized restore-background for bKGD cases              * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - finished support for BACK image & tiling                 * */
+/* *             1.0.5 - 09/22/2002 - G.Juyn                                * */
+/* *             - added bgrx8 canvas (filler byte)                         * */
+/* *             1.0.5 - 09/23/2002 - G.Juyn                                * */
+/* *             - added compose over/under routines for PAST processing    * */
+/* *             - added flip & tile routines for PAST processing           * */
+/* *                                                                        * */
+/* *             1.0.6 - 03/09/2003 - G.Juyn                                * */
+/* *             - hiding 12-bit JPEG stuff                                 * */
+/* *             1.0.6 - 05/11/2003 - Glenn RP                              * */
+/* *             - added size-optimization COMPOSE routine usage            * */
+/* *             1.0.6 - 05/11/2003 - G. Juyn                               * */
+/* *             - added conditionals around canvas update routines         * */
+/* *             1.0.6 - 05/25/2003 - Glenn RP                              * */
+/* *             - added size-optimization DIV255B8 routine usage           * */
+/* *             1.0.6 - 06/09/2003 - G. R-P                                * */
+/* *             - added conditionals around 8-bit magn routines            * */
+/* *             1.0.6 - 07/07/2003 - G. R-P                                * */
+/* *             - removed conditionals around 8-bit magn routines          * */
+/* *             - added MNG_NO_16BIT_SUPPORT and MNG_NO_DELTA_PNG          * */
+/* *               conditionals                                             * */
+/* *             - reversed many loops to use decrementing counter          * */
+/* *             - combined init functions                                  * */
+/* *             - converted some switches to array references              * */
+/* *             1.0.6 - 07/29/2003 - G.Juyn                                * */
+/* *             - fixed duplicate for-loop                                 * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added SKIPCHUNK conditionals around PAST chunk support   * */
+/* *             - fixed "FOOTPRINT_COMPOSEIV" typo (now "FOOTPRINT_DIV")   * */
+/* *             1.0.6 - 08/17/2003 - G.R-P                                 * */
+/* *             - added more conditionals around "promote" functions       * */
+/* *                                                                        * */
+/* *             1.0.7 - 11/27/2003 - R.A                                   * */
+/* *             - added CANVAS_RGB565 and CANVAS_BGR565                    * */
+/* *             1.0.7 - 12/06/2003 - R.A                                   * */
+/* *             - added CANVAS_RGBA565 and CANVAS_BGRA565                  * */
+/* *             1.0.7 - 01/25/2004 - J.S                                   * */
+/* *             - added premultiplied alpha canvas' for RGBA, ARGB, ABGR   * */
+/* *             1.0.7 - 03/08/2004 - G.R-P                                 * */
+/* *             - added more conditionals around 16-bit-supporting code    * */
+/* *             1.0.7 - 03/09/2004 - G.Juyn                                * */
+/* *             - fixed bug in promote_g8_g8 with 16bit support off        * */
+/* *             1.0.7 - 03/09/2004 - G.R-P                                 * */
+/* *             - more optimizations with 16bit support off                * */
+/* *             1.0.7 - 03/10/2004 - G.Juyn                                * */
+/* *             - fixed some warnings for 16bit optimizations              * */
+/* *             1.0.7 - 03/21/2004 - G.Juyn                                * */
+/* *             - fixed some 64-bit platform compiler warnings             * */
+/* *                                                                        * */
+/* *             1.0.8 - 06/20/2004 - G.Juyn                                * */
+/* *             - some speed optimizations (thanks to John Stiles)         * */
+/* *             1.0.8 - 08/01/2004 - G.Juyn                                * */
+/* *             - added support for 3+byte pixelsize for JPEG's            * */
+/* *                                                                        * */
+/* *             1.0.9 - 10/10/2004 - G.R-P.                                * */
+/* *             - added MNG_NO_1_2_4BIT_SUPPORT                            * */
+/* *             1.0.9 - 10/14/2004 - G.Juyn                                * */
+/* *             - added bgr565_a8 canvas-style (thanks to J. Elvander)     * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added LITTLEENDIAN/BIGENDIAN fixtures (thanks J.Stiles)  * */
+/* *             - fixed MNG_NO_1_2_4BIT_SUPPORT for TBBN1G04.PNG           * */
+/* *             1.0.9 - 12/31/2004 - G.R-P.                                * */
+/* *             - fixed warnings about C++ style (//) comments             * */
+/* *                                                                        * */
+/* *             1.0.10 - 07/06/2005 - G.R-P.                               * */
+/* *             - added MORE MNG_NO_1_2_4BIT_SUPPORT                       * */
+/* *             1.0.10 - 10/06/2005 - G.R-P.                               * */
+/* *             - alloc more memory for MNG_NO_1_2_4BIT_SUPPORT            * */
+/* *             1.0.10 - 12/07/2005 - G.R-P.                               * */
+/* *             - optimized footprint of 16bit support                     * */
+/* *             1.0.10 - 03/07/2006 - (thanks to W. Manthey)               * */
+/* *             - added CANVAS_RGB555 and CANVAS_BGR555                    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_objects.h"
+#include "libmng_object_prc.h"
+#include "libmng_memory.h"
+#include "libmng_cms.h"
+#include "libmng_filter.h"
+#include "libmng_pixels.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_DISPLAY_PROCS
+
+/* TODO: magnification & canvas-positioning/-clipping */
+
+/* TODO: major optimization of pixel-loops by using assembler (?) */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Interlace tables                                                       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+MNG_LOCAL mng_uint32 const interlace_row      [7] = { 0, 0, 4, 0, 2, 0, 1 };
+MNG_LOCAL mng_uint32 const interlace_rowskip  [7] = { 8, 8, 8, 4, 4, 2, 2 };
+MNG_LOCAL mng_uint32 const interlace_col      [7] = { 0, 4, 0, 2, 0, 1, 0 };
+MNG_LOCAL mng_uint32 const interlace_colskip  [7] = { 8, 8, 4, 4, 2, 2, 1 };
+MNG_LOCAL mng_uint32 const interlace_roundoff [7] = { 7, 7, 3, 3, 1, 1, 0 };
+MNG_LOCAL mng_uint32 const interlace_divider  [7] = { 3, 3, 2, 2, 1, 1, 0 };
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Alpha composing macros                                                 * */
+/* * the code below is slightly modified from the libpng package            * */
+/* * the original was last optimized by Greg Roelofs & Mark Adler           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_COMPOSE8(RET,FG,ALPHA,BG) {                                    \
+       mng_uint16 iH = (mng_uint16)((mng_uint16)(FG) * (mng_uint16)(ALPHA) \
+                        + (mng_uint16)(BG)*(mng_uint16)(255 -              \
+                          (mng_uint16)(ALPHA)) + (mng_uint16)128);         \
+       (RET) = (mng_uint8)((iH + (iH >> 8)) >> 8); }
+
+#define MNG_COMPOSE16(RET,FG,ALPHA,BG) {                                   \
+       mng_uint32 iH = (mng_uint32)((mng_uint32)(FG) * (mng_uint32)(ALPHA) \
+                        + (mng_uint32)(BG)*(mng_uint32)(65535L -           \
+                          (mng_uint32)(ALPHA)) + (mng_uint32)32768L);      \
+       (RET) = (mng_uint16)((iH + (iH >> 16)) >> 16); }
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Alpha blending macros                                                  * */
+/* * this code is based on Adam Costello's "Note on Compositing" from the   * */
+/* * mng-list which gives the following formula:                            * */
+/* *                                                                        * */
+/* * top pixel       = (Rt, Gt, Bt, At)                                     * */
+/* * bottom pixel    = (Rb, Gb, Bb, Ab)                                     * */
+/* * composite pixel = (Rc, Gc, Bc, Ac)                                     * */
+/* *                                                                        * */
+/* * all values in the range 0..1                                           * */
+/* *                                                                        * */
+/* * Ac = 1 - (1 - At)(1 - Ab)                                              * */
+/* * s = At / Ac                                                            * */
+/* * t = (1 - At) Ab / Ac                                                   * */
+/* * Rc = s Rt + t Rb                                                       * */
+/* * Gc = s Gt + t Gb                                                       * */
+/* * Bc = s Bt + t Bb                                                       * */
+/* *                                                                        * */
+/* * (I just hope I coded it correctly in integer arithmetic...)            * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#define MNG_BLEND8(RT, GT, BT, AT, RB, GB, BB, AB, RC, GC, BC, AC) {         \
+       mng_uint32 S, T;                                                      \
+       (AC) = (mng_uint8)((mng_uint32)255 -                                  \
+                          ((((mng_uint32)255 - (mng_uint32)(AT)) *           \
+                            ((mng_uint32)255 - (mng_uint32)(AB))   ) >> 8)); \
+       S    = (mng_uint32)(((mng_uint32)(AT) << 8) /                         \
+                           (mng_uint32)(AC));                                \
+       T    = (mng_uint32)(((mng_uint32)255 - (mng_uint32)(AT)) *            \
+                            (mng_uint32)(AB) / (mng_uint32)(AC));            \
+       (RC) = (mng_uint8)((S * (mng_uint32)(RT) +                            \
+                           T * (mng_uint32)(RB) + (mng_uint32)127) >> 8);    \
+       (GC) = (mng_uint8)((S * (mng_uint32)(GT) +                            \
+                           T * (mng_uint32)(GB) + (mng_uint32)127) >> 8);    \
+       (BC) = (mng_uint8)((S * (mng_uint32)(BT) +                            \
+                           T * (mng_uint32)(BB) + (mng_uint32)127) >> 8); }
+
+#define MNG_BLEND16(RT, GT, BT, AT, RB, GB, BB, AB, RC, GC, BC, AC) {            \
+       mng_uint32 S, T;                                                          \
+       (AC) = (mng_uint16)((mng_uint32)65535 -                                   \
+                           ((((mng_uint32)65535 - (mng_uint32)(AT)) *            \
+                             ((mng_uint32)65535 - (mng_uint32)(AB))   ) >> 16)); \
+       S    = (mng_uint32)(((mng_uint32)(AT) << 16) /                            \
+                            (mng_uint32)(AC));                                   \
+       T    = (mng_uint32)(((mng_uint32)65535 - (mng_uint32)(AT)) *              \
+                            (mng_uint32)(AB) / (mng_uint32)(AC));                \
+       (RC) = (mng_uint16)((S * (mng_uint32)(RT) +                               \
+                            T * (mng_uint32)(RB) + (mng_uint32)32767) >> 16);    \
+       (GC) = (mng_uint16)((S * (mng_uint32)(GT) +                               \
+                            T * (mng_uint32)(GB) + (mng_uint32)32767) >> 16);    \
+       (BC) = (mng_uint16)((S * (mng_uint32)(BT) +                               \
+                            T * (mng_uint32)(BB) + (mng_uint32)32767) >> 16); }
+
+/* ************************************************************************** */
+
+/* note a good optimizing compiler will optimize this */
+#define DIV255B8(x) (mng_uint8)(((x) + 127) / 255)
+#define DIV255B16(x) (mng_uint16)(((x) + 32767) / 65535)
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Progressive display check - checks to see if progressive display is    * */
+/* * in order & indicates so                                                * */
+/* *                                                                        * */
+/* * The routine is called after a call to one of the display_xxx routines  * */
+/* * if appropriate                                                         * */
+/* *                                                                        * */
+/* * The refresh is warrented in the read_chunk routine (mng_read.c)        * */
+/* * and only during read&display processing, since there's not much point  * */
+/* * doing it from memory!                                                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_display_progressive_check (mng_datap pData)
+{
+  if ((pData->bDoProgressive) &&       /* need progressive display? */
+      ((pData->eImagetype != mng_it_mng) || (pData->iDataheight > 300)) &&
+      (pData->iDestb - pData->iDestt > 50) && (!pData->pCurraniobj))
+  {
+    mng_int32 iC = pData->iRow + pData->iDestt - pData->iSourcet;
+
+    if (iC % 20 == 0)                  /* every 20th line */
+      pData->bNeedrefresh = MNG_TRUE;
+
+  }
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Display routines - convert rowdata (which is already color-corrected)  * */
+/* * to the output canvas, respecting the opacity information               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+MNG_LOCAL void check_update_region (mng_datap pData)
+{                                      /* determine actual canvas row */
+  mng_int32 iRow = pData->iRow + pData->iDestt - pData->iSourcet;
+                                       /* check for change in update-region */
+  if ((pData->iDestl < (mng_int32)pData->iUpdateleft) || (pData->iUpdateright == 0))
+    pData->iUpdateleft   = pData->iDestl;
+
+  if (pData->iDestr > (mng_int32)pData->iUpdateright)
+    pData->iUpdateright  = pData->iDestr;
+
+  if ((iRow < (mng_int32)pData->iUpdatetop) || (pData->iUpdatebottom == 0))
+    pData->iUpdatetop    = iRow;
+
+  if (iRow+1 > (mng_int32)pData->iUpdatebottom)
+    pData->iUpdatebottom = iRow+1;
+
+  return;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGB8
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_rgb8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+2);
+          *(pScanline+2) = *(pDataline+4);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *(pDataline+2);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *(pDataline+4);
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+              iBGr16 = (mng_uint16)(*pScanline    );
+              iBGg16 = (mng_uint16)(*(pScanline+1));
+              iBGb16 = (mng_uint16)(*(pScanline+2));
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *pScanline     = (mng_uint8)(iFGr16 >> 8);
+              *(pScanline+1) = (mng_uint8)(iFGg16 >> 8);
+              *(pScanline+2) = (mng_uint8)(iFGb16 >> 8);
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *(pDataline+2);
+            }
+            else
+            {                          /* do alpha composing */
+              MNG_COMPOSE8 (*pScanline,     *pDataline,     iA8, *pScanline    );
+              MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1));
+              MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iA8, *(pScanline+2));
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_rgb8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGg16;
+  mng_uint16 iBGg16;
+  mng_uint8  iA8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+iBps);
+          *(pScanline+2) = *(pDataline+2*iBps);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4*iBps;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *(pDataline+4);
+            }
+            else
+            {                          /* get the proper values */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                iFGg16 = mng_get_uint16 (pDataline+i+i);
+                                         /* scale background up */
+                iBGg16 = (mng_uint16)(*(pScanline+i));
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                                         /* now compose */
+                MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+                                         /* and return the composed values */
+                *(pScanline+i) = (mng_uint8)(iFGg16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *(pDataline+2);
+            }
+            else
+            {                          /* do alpha composing */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iA8, *(pScanline+i));
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_rgb8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *(pDataline+2);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *(pDataline+2);
+            }
+            else
+            {                          /* do alpha composing */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iA8, *(pScanline+i));
+              }
+#else
+              MNG_COMPOSE8 (*pScanline,     *pDataline,     iA8, *pScanline    );
+              MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1));
+              MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iA8, *(pScanline+2));
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_RGB8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGBA8
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_rgba8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+2);
+          *(pScanline+2) = *(pDataline+4);
+          *(pScanline+3) = *(pDataline+6);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *(pDataline+2);
+          *(pScanline+3) = *(pDataline+3);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*(pScanline+3));
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *(pDataline+4);
+              *(pScanline+3) = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+                iBGr16 = (mng_uint16)(*pScanline    );
+                iBGg16 = (mng_uint16)(*(pScanline+1));
+                iBGb16 = (mng_uint16)(*(pScanline+2));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iFGr16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iFGg16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iFGb16 >> 8);
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*pScanline    );
+                iBGg16 = (mng_uint16)(*(pScanline+1));
+                iBGb16 = (mng_uint16)(*(pScanline+2));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCr16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCb16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+3);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *(pDataline+2);
+              *(pScanline+3) = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                MNG_COMPOSE8 (*pScanline,     *pDataline,     iFGa8, *pScanline    );
+                MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1));
+                MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iFGa8, *(pScanline+2));
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            *pScanline, *(pScanline+1), *(pScanline+2), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCr8;
+                *(pScanline+1) = iCg8;
+                *(pScanline+2) = iCb8;
+                *(pScanline+3) = iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_rgba8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGg16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+iBps);
+          *(pScanline+2) = *(pDataline+2*iBps);
+          *(pScanline+3) = *(pDataline+3*iBps);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*(pScanline+3));
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *(pDataline+4);
+              *(pScanline+3) = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                iFGg16 = mng_get_uint16 (pDataline+i+i);
+                                       /* scale background up */
+                iBGg16 = (mng_uint16)(*(pScanline+i));
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                                       /* and return the composed values */
+                *(pScanline+i) = (mng_uint8)(iFGg16 >> 8);
+                                       /* alpha remains fully opaque !!! */
+              }
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*pScanline    );
+                iBGg16 = (mng_uint16)(*(pScanline+1));
+                iBGb16 = (mng_uint16)(*(pScanline+2));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCr16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCb16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+3);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *(pDataline+2);
+              *(pScanline+3) = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iFGa8, *(pScanline+i));
+              }
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            *pScanline, *(pScanline+1), *(pScanline+2), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCr8;
+                *(pScanline+1) = iCg8;
+                *(pScanline+2) = iCb8;
+                *(pScanline+3) = iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_rgba8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *(pDataline+2);
+          *(pScanline+3) = *(pDataline+3);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+3);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *(pDataline+2);
+              *(pScanline+3) = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iFGa8, *(pScanline+i));
+              }
+#else
+                MNG_COMPOSE8 (*pScanline,     *pDataline,     iFGa8, *pScanline    );
+                MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1));
+                MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iFGa8, *(pScanline+2));
+#endif
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            *pScanline, *(pScanline+1), *(pScanline+2), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCr8;
+                *(pScanline+1) = iCg8;
+                *(pScanline+2) = iCb8;
+                *(pScanline+3) = iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_RGBA8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGBA8_PM
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_rgba8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+		  if ((s = pDataline[6]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[2];
+		      pScanline[2] = pDataline[4];
+              pScanline[3] = 255;
+			}
+			else
+			{
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                pScanline[2-i] = DIV255B8(s * pDataline[4-i-i]);
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[2]);
+              pScanline[2] = DIV255B8(s * pDataline[4]);
+#endif
+              pScanline[3] = (mng_uint8)s;
+			}
+		  }
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+		  if ((s = pDataline[3]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+#ifdef MNG_BIGENDIAN_SUPPORTED
+              *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF;
+#else
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[1];
+		      pScanline[2] = pDataline[2];
+              pScanline[3] = 255;
+#endif
+			}
+			else
+			{
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                pScanline[2-i] = DIV255B8(s * pDataline[2-i]);
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[1]);
+		      pScanline[2] = DIV255B8(s * pDataline[2]);
+#endif
+              pScanline[3] = (mng_uint8)s;
+			}
+		  }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          if ((s = pDataline[6]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if (s == 255)
+            {                          /* plain copy it */
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[4];
+              pScanline[3] = 255;
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[2-i] = DIV255B8(s * pDataline[4-i-i] + t *
+                     pScanline[2-i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]);
+			  pScanline[2] = DIV255B8(s * pDataline[4] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0) /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+#ifdef MNG_BIGENDIAN_SUPPORTED
+              *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF;
+#else
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[1];
+              pScanline[2] = pDataline[2];
+              pScanline[3] = 255;
+#endif
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[2-i] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[2-i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]);
+			  pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_rgba8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_START);
+#endif
+                  
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+		  if ((s = pDataline[6]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[2];
+		      pScanline[2] = pDataline[4];
+              pScanline[3] = 255;
+			}
+			else
+			{
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                pScanline[2-i] = DIV255B8(s * pDataline[4-i-i]);
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[2]);
+              pScanline[2] = DIV255B8(s * pDataline[4]);
+#endif
+              pScanline[3] = (mng_uint8)s;
+			}
+		  }
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+		  if ((s = pDataline[3]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+#ifdef MNG_BIGENDIAN_SUPPORTED
+              *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF;
+#else
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[1];
+		      pScanline[2] = pDataline[2];
+              pScanline[3] = 255;
+#endif
+			}
+			else
+			{
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                pScanline[2-i] = DIV255B8(s * pDataline[2-i]);
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[1]);
+		      pScanline[2] = DIV255B8(s * pDataline[2]);
+#endif
+              pScanline[3] = (mng_uint8)s;
+			}
+		  }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          if ((s = pDataline[6]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if (s == 255)
+            {                          /* plain copy it */
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[4];
+              pScanline[3] = 255;
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[2-i] = DIV255B8(s * pDataline[4-i-i] + t *
+                     pScanline[2-i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]);
+			  pScanline[2] = DIV255B8(s * pDataline[4] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0) /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+#ifdef MNG_BIGENDIAN_SUPPORTED
+              *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF;
+#else
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[1];
+              pScanline[2] = pDataline[2];
+              pScanline[3] = 255;
+#endif
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[2-i] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[2-i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]);
+			  pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_rgba8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+		  if ((s = pDataline[3]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+#ifdef MNG_BIGENDIAN_SUPPORTED
+              *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF;
+#else
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[1];
+		      pScanline[2] = pDataline[2];
+              pScanline[3] = 255;
+#endif
+			}
+			else
+			{
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                pScanline[2-i] = DIV255B8(s * pDataline[2-i]);
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[1]);
+		      pScanline[2] = DIV255B8(s * pDataline[2]);
+#endif
+              pScanline[3] = (mng_uint8)s;
+			}
+		  }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0) /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+#ifdef MNG_BIGENDIAN_SUPPORTED
+              *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF;
+#else
+              pScanline[0] = pDataline[0];
+              pScanline[1] = pDataline[1];
+              pScanline[2] = pDataline[2];
+              pScanline[3] = 255;
+#endif
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[2-i] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[2-i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]);
+			  pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_RGBA8_PM */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_ARGB8
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_argb8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_START);
+#endif
+
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+6);
+          *(pScanline+1) = *pDataline;
+          *(pScanline+2) = *(pDataline+2);
+          *(pScanline+3) = *(pDataline+4);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+3);
+          *(pScanline+1) = *pDataline;
+          *(pScanline+2) = *(pDataline+1);
+          *(pScanline+3) = *(pDataline+2);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*pScanline);
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *(pDataline+6);
+              *(pScanline+1) = *pDataline;
+              *(pScanline+2) = *(pDataline+2);
+              *(pScanline+3) = *(pDataline+4);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+                iBGr16 = (mng_uint16)(*(pScanline+1));
+                iBGg16 = (mng_uint16)(*(pScanline+2));
+                iBGb16 = (mng_uint16)(*(pScanline+3));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16);
+                                       /* and return the composed values */
+                                       /* alpha remains fully opaque !!! */
+                *(pScanline+1) = (mng_uint8)(iFGr16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iFGg16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iFGb16 >> 8);
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*(pScanline+1));
+                iBGg16 = (mng_uint16)(*(pScanline+2));
+                iBGb16 = (mng_uint16)(*(pScanline+3));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCa16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCr16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iCb16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *pScanline;
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+3);
+              *(pScanline+1) = *pDataline;
+              *(pScanline+2) = *(pDataline+1);
+              *(pScanline+3) = *(pDataline+2);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do simple alpha composing */
+                                       /* alpha itself remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline,     *(pDataline+1), *(pDataline+2), iFGa8,
+                            *(pScanline+1), *(pScanline+2), *(pScanline+3), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCa8;
+                *(pScanline+1) = iCr8;
+                *(pScanline+2) = iCg8;
+                *(pScanline+3) = iCb8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_argb8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGg16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+3*iBps);
+          *(pScanline+1) = *pDataline;
+          *(pScanline+2) = *(pDataline+iBps);
+          *(pScanline+3) = *(pDataline+2*iBps);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*pScanline);
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *(pDataline+6);
+              *(pScanline+1) = *pDataline;
+              *(pScanline+2) = *(pDataline+2);
+              *(pScanline+3) = *(pDataline+4);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                iFGg16 = mng_get_uint16 (pDataline+i+i);
+                                       /* scale background up */
+                iBGg16 = (mng_uint16)(*(pScanline+i+1));
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                                       /* and return the composed values */
+                                       /* alpha remains fully opaque !!! */
+                *(pScanline+i+1) = (mng_uint8)(iFGg16 >> 8);
+              }
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*(pScanline+1));
+                iBGg16 = (mng_uint16)(*(pScanline+2));
+                iBGb16 = (mng_uint16)(*(pScanline+3));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCa16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCr16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iCb16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *pScanline;
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+3);
+              *(pScanline+1) = *pDataline;
+              *(pScanline+2) = *(pDataline+1);
+              *(pScanline+3) = *(pDataline+2);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do simple alpha composing */
+                                       /* alpha itself remains fully opaque !!! */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i+1), *(pDataline+i), iFGa8, *(pScanline+i+1));
+              }
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline,     *(pDataline+1), *(pDataline+2), iFGa8,
+                            *(pScanline+1), *(pScanline+2), *(pScanline+3), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCa8;
+                *(pScanline+1) = iCr8;
+                *(pScanline+2) = iCg8;
+                *(pScanline+3) = iCb8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_argb8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_START);
+#endif
+
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+3);
+          *(pScanline+1) = *pDataline;
+          *(pScanline+2) = *(pDataline+1);
+          *(pScanline+3) = *(pDataline+2);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *pScanline;
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+3);
+              *(pScanline+1) = *pDataline;
+              *(pScanline+2) = *(pDataline+1);
+              *(pScanline+3) = *(pDataline+2);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do simple alpha composing */
+                                       /* alpha itself remains fully opaque !!! */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i+1), *(pDataline+i), iFGa8, *(pScanline+i+1));
+              }
+#else
+                MNG_COMPOSE8 (*(pScanline+1), *pDataline,     iFGa8, *(pScanline+1));
+                MNG_COMPOSE8 (*(pScanline+2), *(pDataline+1), iFGa8, *(pScanline+2));
+                MNG_COMPOSE8 (*(pScanline+3), *(pDataline+2), iFGa8, *(pScanline+3));
+#endif
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline,     *(pDataline+1), *(pDataline+2), iFGa8,
+                            *(pScanline+1), *(pScanline+2), *(pScanline+3), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCa8;
+                *(pScanline+1) = iCr8;
+                *(pScanline+2) = iCg8;
+                *(pScanline+3) = iCb8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_ARGB8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_ARGB8_PM
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_argb8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+		  if ((s = pDataline[6]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[2];
+		      pScanline[3] = pDataline[4];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[4-i-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0]);
+              pScanline[2] = DIV255B8(s * pDataline[2]);
+              pScanline[3] = DIV255B8(s * pDataline[4]);
+#endif
+			}
+		  }
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+		  if ((s = pDataline[3]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[1];
+		      pScanline[3] = pDataline[2];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[2-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0]);
+              pScanline[2] = DIV255B8(s * pDataline[1]);
+		      pScanline[3] = DIV255B8(s * pDataline[2]);
+#endif
+			}
+		  }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          if ((s = pDataline[6]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if (s == 255)
+            {                          /* plain copy it */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[2];
+              pScanline[3] = pDataline[4];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[4-i-i] + t *
+                     pScanline[3-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]);
+			  pScanline[3] = DIV255B8(s * pDataline[4] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[1];
+              pScanline[3] = pDataline[2];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[3-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]);
+			  pScanline[3] = DIV255B8(s * pDataline[2] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_argb8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+		  if ((s = pDataline[6]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[2];
+		      pScanline[3] = pDataline[4];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[4-i-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0]);
+              pScanline[2] = DIV255B8(s * pDataline[2]);
+              pScanline[3] = DIV255B8(s * pDataline[4]);
+#endif
+			}
+		  }
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+		  if ((s = pDataline[3]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[1];
+		      pScanline[3] = pDataline[2];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[2-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0]);
+              pScanline[2] = DIV255B8(s * pDataline[1]);
+		      pScanline[3] = DIV255B8(s * pDataline[2]);
+#endif
+			}
+		  }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          if ((s = pDataline[6]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if (s == 255)
+            {                          /* plain copy it */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[2];
+              pScanline[3] = pDataline[4];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[4-i-i] + t *
+                     pScanline[3-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]);
+			  pScanline[3] = DIV255B8(s * pDataline[4] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[1];
+              pScanline[3] = pDataline[2];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[3-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]);
+			  pScanline[3] = DIV255B8(s * pDataline[2] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_argb8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+		  if ((s = pDataline[3]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[1];
+		      pScanline[3] = pDataline[2];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[2-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0]);
+              pScanline[2] = DIV255B8(s * pDataline[1]);
+		      pScanline[3] = DIV255B8(s * pDataline[2]);
+#endif
+			}
+		  }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[0];
+              pScanline[2] = pDataline[1];
+              pScanline[3] = pDataline[2];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[3-i] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[3-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]);
+			  pScanline[3] = DIV255B8(s * pDataline[2] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_ARGB8_PM */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGB8_A8
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_rgb8_a8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pAlphaline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination rows */
+    pScanline  = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                    pData->iRow + pData->iDestt -
+                                                    pData->iSourcet);
+    pAlphaline = (mng_uint8p)pData->fGetalphaline  (((mng_handle)pData),
+                                                    pData->iRow + pData->iDestt -
+                                                    pData->iSourcet);
+                                       /* adjust destination rows starting-point */
+    pScanline  = pScanline  + (pData->iCol * 3) + (pData->iDestl * 3);
+    pAlphaline = pAlphaline + pData->iCol + pData->iDestl;
+
+    pDataline  = pData->pRGBArow;      /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+2);
+          *(pScanline+2) = *(pDataline+4);
+          *pAlphaline    = *(pDataline+6);
+
+          pScanline  += (pData->iColinc * 3);
+          pAlphaline += pData->iColinc;
+          pDataline  += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *(pDataline+2);
+          *pAlphaline    = *(pDataline+3);
+
+          pScanline  += (pData->iColinc * 3);
+          pAlphaline += pData->iColinc;
+          pDataline  += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*pAlphaline);
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *(pDataline+4);
+              *pAlphaline    = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+                iBGr16 = (mng_uint16)(*pScanline    );
+                iBGg16 = (mng_uint16)(*(pScanline+1));
+                iBGb16 = (mng_uint16)(*(pScanline+2));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iFGr16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iFGg16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iFGb16 >> 8);
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*pScanline    );
+                iBGg16 = (mng_uint16)(*(pScanline+1));
+                iBGb16 = (mng_uint16)(*(pScanline+2));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCr16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCb16 >> 8);
+                *pAlphaline    = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline  += (pData->iColinc * 3);
+          pAlphaline += pData->iColinc;
+          pDataline  += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *pAlphaline;
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *(pDataline+2);
+              *pAlphaline    = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                MNG_COMPOSE8 (*pScanline,     *pDataline,     iFGa8, *pScanline    );
+                MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1));
+                MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iFGa8, *(pScanline+2));
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            *pScanline, *(pScanline+1), *(pScanline+2), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCr8;
+                *(pScanline+1) = iCg8;
+                *(pScanline+2) = iCb8;
+                *pAlphaline    = iCa8;
+              }
+            }
+          }
+
+          pScanline  += (pData->iColinc * 3);
+          pAlphaline += pData->iColinc;
+          pDataline  += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_rgb8_a8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pAlphaline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGg16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination rows */
+    pScanline  = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                    pData->iRow + pData->iDestt -
+                                                    pData->iSourcet);
+    pAlphaline = (mng_uint8p)pData->fGetalphaline  (((mng_handle)pData),
+                                                    pData->iRow + pData->iDestt -
+                                                    pData->iSourcet);
+                                       /* adjust destination rows starting-point */
+    pScanline  = pScanline  + (pData->iCol * 3) + (pData->iDestl * 3);
+    pAlphaline = pAlphaline + pData->iCol + pData->iDestl;
+
+    pDataline  = pData->pRGBArow;      /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+iBps);
+          *(pScanline+2) = *(pDataline+2*iBps);
+          *pAlphaline    = *(pDataline+3*iBps);
+
+          pScanline  += (pData->iColinc * 3);
+          pAlphaline += pData->iColinc;
+          pDataline  += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*pAlphaline);
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *(pDataline+4);
+              *pAlphaline    = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                iFGg16 = mng_get_uint16 (pDataline+i+i);
+                                       /* scale background up */
+                iBGg16 = (mng_uint16)(*(pScanline+i));
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                                       /* and return the composed values */
+                *(pScanline+i) = (mng_uint8)(iFGg16 >> 8);
+                                       /* alpha remains fully opaque !!! */
+              }
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*pScanline    );
+                iBGg16 = (mng_uint16)(*(pScanline+1));
+                iBGb16 = (mng_uint16)(*(pScanline+2));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCr16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCb16 >> 8);
+                *pAlphaline    = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline  += (pData->iColinc * 3);
+          pAlphaline += pData->iColinc;
+          pDataline  += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *pAlphaline;
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *(pDataline+2);
+              *pAlphaline    = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iFGa8, *(pScanline+i));
+              }
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            *pScanline, *(pScanline+1), *(pScanline+2), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCr8;
+                *(pScanline+1) = iCg8;
+                *(pScanline+2) = iCb8;
+                *pAlphaline    = iCa8;
+              }
+            }
+          }
+
+          pScanline  += (pData->iColinc * 3);
+          pAlphaline += pData->iColinc;
+          pDataline  += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_rgb8_a8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pAlphaline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination rows */
+    pScanline  = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                    pData->iRow + pData->iDestt -
+                                                    pData->iSourcet);
+    pAlphaline = (mng_uint8p)pData->fGetalphaline  (((mng_handle)pData),
+                                                    pData->iRow + pData->iDestt -
+                                                    pData->iSourcet);
+                                       /* adjust destination rows starting-point */
+    pScanline  = pScanline  + (pData->iCol * 3) + (pData->iDestl * 3);
+    pAlphaline = pAlphaline + pData->iCol + pData->iDestl;
+
+    pDataline  = pData->pRGBArow;      /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *pDataline;
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *(pDataline+2);
+          *pAlphaline    = *(pDataline+3);
+
+          pScanline  += (pData->iColinc * 3);
+          pAlphaline += pData->iColinc;
+          pDataline  += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *pAlphaline;
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *pDataline;
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *(pDataline+2);
+              *pAlphaline    = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iFGa8, *(pScanline+i));
+              }
+#else
+                MNG_COMPOSE8 (*pScanline,     *pDataline,     iFGa8, *pScanline    );
+                MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1));
+                MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iFGa8, *(pScanline+2));
+#endif
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            *pScanline, *(pScanline+1), *(pScanline+2), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCr8;
+                *(pScanline+1) = iCg8;
+                *(pScanline+2) = iCb8;
+                *pAlphaline    = iCa8;
+              }
+            }
+          }
+
+          pScanline  += (pData->iColinc * 3);
+          pAlphaline += pData->iColinc;
+          pDataline  += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_RGB8_A8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGR8
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_bgr8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 8;
+    else
+      pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 4;
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+4);
+          *(pScanline+1) = *(pDataline+2);
+          *(pScanline+2) = *pDataline;
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+2);
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *pDataline;
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha value */
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *pScanline     = *(pDataline+4);
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *pDataline;
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+              iBGr16 = (mng_uint16)(*(pScanline+2));
+              iBGg16 = (mng_uint16)(*(pScanline+1));
+              iBGb16 = (mng_uint16)(*pScanline    );
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *pScanline     = (mng_uint8)(iFGb16 >> 8);
+              *(pScanline+1) = (mng_uint8)(iFGg16 >> 8);
+              *(pScanline+2) = (mng_uint8)(iFGr16 >> 8);
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+2);
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *pDataline;
+            }
+            else
+            {                          /* do alpha composing */
+              MNG_COMPOSE8 (*pScanline,     *(pDataline+2), iA8, *pScanline    );
+              MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1));
+              MNG_COMPOSE8 (*(pScanline+2), *pDataline,     iA8, *(pScanline+2));
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_bgr8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGg16;
+  mng_uint16 iBGg16;
+  mng_uint8  iA8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+2*iBps);
+          *(pScanline+1) = *(pDataline+iBps);
+          *(pScanline+2) = *pDataline;
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha value */
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *pScanline     = *(pDataline+4);
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *pDataline;
+            }
+            else
+            {                          /* get the proper values */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              iFGg16 = mng_get_uint16 (pDataline+i+i);
+                                       /* scale background up */
+              iBGg16 = (mng_uint16)(*(pScanline+2-i));
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+                                       /* and return the composed values */
+              *(pScanline+2-i) = (mng_uint8)(iFGg16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+2);
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *pDataline;
+            }
+            else
+            {                          /* do alpha composing */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iA8, *(pScanline+i));
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_bgr8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 4;
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+2);
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *pDataline;
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+2);
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *pDataline;
+            }
+            else
+            {                          /* do alpha composing */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iA8, *(pScanline+i));
+              }
+#else
+              MNG_COMPOSE8 (*pScanline,     *(pDataline+2), iA8, *pScanline    );
+              MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1));
+              MNG_COMPOSE8 (*(pScanline+2), *pDataline,     iA8, *(pScanline+2));
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_BGR8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGRX8
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_bgrx8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 8;
+    else
+      pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 4;
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+4);
+          *(pScanline+1) = *(pDataline+2);
+          *(pScanline+2) = *pDataline;
+          *(pScanline+3) = 0xFF;       /* filler byte */
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+2);
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *pDataline;
+          *(pScanline+3) = 0xFF;       /* filler byte */
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha value */
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *pScanline     = *(pDataline+4);
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+              iBGr16 = (mng_uint16)(*(pScanline+2));
+              iBGg16 = (mng_uint16)(*(pScanline+1));
+              iBGb16 = (mng_uint16)(*pScanline    );
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *pScanline     = (mng_uint8)(iFGb16 >> 8);
+              *(pScanline+1) = (mng_uint8)(iFGg16 >> 8);
+              *(pScanline+2) = (mng_uint8)(iFGr16 >> 8);
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+2);
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+            else
+            {                          /* do alpha composing */
+              MNG_COMPOSE8 (*pScanline,     *(pDataline+2), iA8, *pScanline    );
+              MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1));
+              MNG_COMPOSE8 (*(pScanline+2), *pDataline,     iA8, *(pScanline+2));
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_bgrx8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGg16;
+  mng_uint16 iBGg16;
+  mng_uint8  iA8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+2*iBps);
+          *(pScanline+1) = *(pDataline+iBps);
+          *(pScanline+2) = *pDataline;
+          *(pScanline+3) = 0xFF;       /* filler byte */
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha value */
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *pScanline     = *(pDataline+4);
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+            else
+            {                          /* get the proper values */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              iFGg16 = mng_get_uint16 (pDataline+i+i);
+                                       /* scale background up */
+              iBGg16 = (mng_uint16)(*(pScanline+2-i));
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+                                       /* and return the composed values */
+              *(pScanline+2-i) = (mng_uint8)(iFGg16 >> 8);
+              }
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+2);
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+            else
+            {                          /* do alpha composing */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iA8, *(pScanline+i));
+              }
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_bgrx8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 4;
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+2);
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *pDataline;
+          *(pScanline+3) = 0xFF;       /* filler byte */
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+2);
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+            else
+            {                          /* do alpha composing */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+              MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iA8, *(pScanline+i));
+              }
+#else
+              MNG_COMPOSE8 (*pScanline,     *(pDataline+2), iA8, *pScanline    );
+              MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1));
+              MNG_COMPOSE8 (*(pScanline+2), *pDataline,     iA8, *(pScanline+2));
+#endif
+              *(pScanline+3) = 0xFF;   /* filler byte */
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_BGRX8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGRA8
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_bgra8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+4);
+          *(pScanline+1) = *(pDataline+2);
+          *(pScanline+2) = *pDataline;
+          *(pScanline+3) = *(pDataline+6);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+2);
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *pDataline;
+          *(pScanline+3) = *(pDataline+3);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*(pScanline+3));
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *(pDataline+4);
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+                iBGr16 = (mng_uint16)(*(pScanline+2));
+                iBGg16 = (mng_uint16)(*(pScanline+1));
+                iBGb16 = (mng_uint16)(*pScanline    );
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iFGb16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iFGg16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iFGr16 >> 8);
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*(pScanline+2));
+                iBGg16 = (mng_uint16)(*(pScanline+1));
+                iBGb16 = (mng_uint16)(*pScanline    );
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCb16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCr16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+3);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+2);
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                MNG_COMPOSE8 (*pScanline,     *(pDataline+2), iFGa8, *pScanline    );
+                MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1));
+                MNG_COMPOSE8 (*(pScanline+2), *pDataline,     iFGa8, *(pScanline+2));
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline,     *(pDataline+1), *(pDataline+2), iFGa8,
+                            *(pScanline+2), *(pScanline+1), *pScanline,     iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCb8;
+                *(pScanline+1) = iCg8;
+                *(pScanline+2) = iCr8;
+                *(pScanline+3) = iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_bgra8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGg16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+2*iBps);
+          *(pScanline+1) = *(pDataline+iBps);
+          *(pScanline+2) = *pDataline;
+          *(pScanline+3) = *(pDataline+3*iBps);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*(pScanline+3));
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *(pDataline+4);
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                iFGg16 = mng_get_uint16 (pDataline+i+i);
+                                       /* scale background up */
+                iBGg16 = (mng_uint16)(*(pScanline+2-i));
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                                       /* and return the composed values */
+                *(pScanline+2-i) = (mng_uint8)(iFGg16 >> 8);
+                                       /* alpha remains fully opaque !!! */
+              }
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*(pScanline+2));
+                iBGg16 = (mng_uint16)(*(pScanline+1));
+                iBGb16 = (mng_uint16)(*pScanline    );
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCb16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCr16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+3);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+2);
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iFGa8, *(pScanline+i));
+                }
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline,     *(pDataline+1), *(pDataline+2), iFGa8,
+                            *(pScanline+2), *(pScanline+1), *pScanline,     iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCb8;
+                *(pScanline+1) = iCg8;
+                *(pScanline+2) = iCr8;
+                *(pScanline+3) = iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_bgra8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+2);
+          *(pScanline+1) = *(pDataline+1);
+          *(pScanline+2) = *pDataline;
+          *(pScanline+3) = *(pDataline+3);
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+3);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+2);
+              *(pScanline+1) = *(pDataline+1);
+              *(pScanline+2) = *pDataline;
+              *(pScanline+3) = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iFGa8, *(pScanline+i));
+                }
+#else
+                MNG_COMPOSE8 (*pScanline,     *(pDataline+2), iFGa8, *pScanline    );
+                MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1));
+                MNG_COMPOSE8 (*(pScanline+2), *pDataline,     iFGa8, *(pScanline+2));
+#endif
+                                       /* alpha remains fully opaque !!! */
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline,     *(pDataline+1), *(pDataline+2), iFGa8,
+                            *(pScanline+2), *(pScanline+1), *pScanline,     iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCb8;
+                *(pScanline+1) = iCg8;
+                *(pScanline+2) = iCr8;
+                *(pScanline+3) = iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_BGRA8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGRA8_PM
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_bgra8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          if ((s = pDataline[6]) == 0)
+            *(mng_uint32*) pScanline = 0; /* set all components = 0 */
+          else
+          {
+            if (s == 255)
+            {
+              pScanline[0] = pDataline[4];
+              pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {
+              pScanline[0] = DIV255B8(s * pDataline[4]);
+              pScanline[1] = DIV255B8(s * pDataline[2]);
+              pScanline[2] = DIV255B8(s * pDataline[0]);
+              pScanline[3] = (mng_uint8)s;
+            }
+          }
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+          if ((s = pDataline[3]) == 0)
+            *(mng_uint32*) pScanline = 0; /* set all components = 0 */
+          else
+          {
+            if (s == 255)
+            {
+              pScanline[0] = pDataline[2];
+              pScanline[1] = pDataline[1];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {
+              pScanline[0] = DIV255B8(s * pDataline[2]);
+              pScanline[1] = DIV255B8(s * pDataline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[0]);
+              pScanline[3] = (mng_uint8)s;
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          if ((s = pDataline[6]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if (s == 255)
+            {                          /* plain copy it */
+              pScanline[0] = pDataline[4];
+              pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {                          /* now blend (premultiplied) */
+              t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i] = DIV255B8(s * pDataline[4-i-i] + t *
+                     pScanline[i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[4] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+              pScanline[0] = pDataline[2];
+              pScanline[1] = pDataline[1];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {                          /* now blend (premultiplied) */
+              t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[2] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_bgra8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          if ((s = pDataline[6]) == 0)
+            *(mng_uint32*) pScanline = 0; /* set all components = 0 */
+          else
+          {
+            if (s == 255)
+            {
+              pScanline[0] = pDataline[4];
+              pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                pScanline[i] = DIV255B8(s * pDataline[4-i-i]);
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[4]);
+              pScanline[1] = DIV255B8(s * pDataline[2]);
+              pScanline[2] = DIV255B8(s * pDataline[0]);
+#endif
+              pScanline[3] = (mng_uint8)s;
+            }
+          }
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+          if ((s = pDataline[3]) == 0)
+            *(mng_uint32*) pScanline = 0; /* set all components = 0 */
+          else
+          {
+            if (s == 255)
+            {
+              pScanline[0] = pDataline[2];
+              pScanline[1] = pDataline[1];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                pScanline[i] = DIV255B8(s * pDataline[2-i]);
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[2]);
+              pScanline[1] = DIV255B8(s * pDataline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[0]);
+#endif
+              pScanline[3] = (mng_uint8)s;
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          if ((s = pDataline[6]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if (s == 255)
+            {                          /* plain copy it */
+              pScanline[0] = pDataline[4];
+              pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {                          /* now blend (premultiplied) */
+              t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i] = DIV255B8(s * pDataline[4-i-i] + t *
+                     pScanline[i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[4] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+              pScanline[0] = pDataline[2];
+              pScanline[1] = pDataline[1];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {                          /* now blend (premultiplied) */
+              t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[2] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_bgra8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+          if ((s = pDataline[3]) == 0)
+            *(mng_uint32*) pScanline = 0; /* set all components = 0 */
+          else
+          {
+            if (s == 255)
+            {
+              pScanline[0] = pDataline[2];
+              pScanline[1] = pDataline[1];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                pScanline[i] = DIV255B8(s * pDataline[2-i]);
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[2]);
+              pScanline[1] = DIV255B8(s * pDataline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[0]);
+#endif
+              pScanline[3] = (mng_uint8)s;
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+              pScanline[0] = pDataline[2];
+              pScanline[1] = pDataline[1];
+              pScanline[2] = pDataline[0];
+              pScanline[3] = 255;
+            }
+            else
+            {                          /* now blend (premultiplied) */
+              t = 255 - s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[i]);
+                }
+              }
+#else
+              pScanline[0] = DIV255B8(s * pDataline[2] + t * pScanline[0]);
+              pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]);
+#endif
+              pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3])));
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_BGRA8_PM */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_ABGR8
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_abgr8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+6);
+          *(pScanline+1) = *(pDataline+4);
+          *(pScanline+2) = *(pDataline+2);
+          *(pScanline+3) = *pDataline;
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+3);
+          *(pScanline+1) = *(pDataline+2);
+          *(pScanline+2) = *(pDataline+1);
+          *(pScanline+3) = *pDataline;
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*pScanline);
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *(pDataline+6);
+              *(pScanline+1) = *(pDataline+4);
+              *(pScanline+2) = *(pDataline+2);
+              *(pScanline+3) = *pDataline;
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+                iBGr16 = (mng_uint16)(*(pScanline+3));
+                iBGg16 = (mng_uint16)(*(pScanline+2));
+                iBGb16 = (mng_uint16)(*(pScanline+1));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16);
+                                       /* and return the composed values */
+                                       /* alpha itself remains fully opaque !!! */
+                *(pScanline+1) = (mng_uint8)(iFGb16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iFGg16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iFGr16 >> 8);
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*(pScanline+3));
+                iBGg16 = (mng_uint16)(*(pScanline+2));
+                iBGb16 = (mng_uint16)(*(pScanline+1));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCa16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCb16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iCr16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *pScanline;
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+3);
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *(pDataline+1);
+              *(pScanline+3) = *pDataline;
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do simple alpha composing */
+                                       /* alpha itself remains fully opaque !!! */
+                MNG_COMPOSE8 (*(pScanline+1), *(pDataline+2), iFGa8, *(pScanline+1));
+                MNG_COMPOSE8 (*(pScanline+2), *(pDataline+1), iFGa8, *(pScanline+2));
+                MNG_COMPOSE8 (*(pScanline+3), *pDataline,     iFGa8, *(pScanline+3));
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline,     *(pDataline+1), *(pDataline+2), iFGa8,
+                            *(pScanline+3), *(pScanline+2), *(pScanline+1), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCa8;
+                *(pScanline+1) = iCb8;
+                *(pScanline+2) = iCg8;
+                *(pScanline+3) = iCr8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_abgr8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGg16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *pScanline     = *(pDataline+3*iBps);
+          *(pScanline+1) = *(pDataline+2*iBps);
+          *(pScanline+2) = *(pDataline+iBps);
+          *(pScanline+3) = *pDataline;
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*pScanline);
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *pScanline     = *(pDataline+6);
+              *(pScanline+1) = *(pDataline+4);
+              *(pScanline+2) = *(pDataline+2);
+              *(pScanline+3) = *pDataline;
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+              int i;
+              for (i=2; i >= 0; i--)
+              {
+                iFGg16 = mng_get_uint16 (pDataline+i+i);
+                                       /* scale background up */
+                iBGg16 = (mng_uint16)(*(pScanline+3-i));
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                                       /* now compose */
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                                       /* and return the composed values */
+                                       /* alpha itself remains fully opaque !!! */
+                *(pScanline+3-i) = (mng_uint8)(iFGg16 >> 8);
+              }
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)(*(pScanline+3));
+                iBGg16 = (mng_uint16)(*(pScanline+2));
+                iBGb16 = (mng_uint16)(*(pScanline+1));
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *pScanline     = (mng_uint8)(iCa16 >> 8);
+                *(pScanline+1) = (mng_uint8)(iCb16 >> 8);
+                *(pScanline+2) = (mng_uint8)(iCg16 >> 8);
+                *(pScanline+3) = (mng_uint8)(iCr16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *pScanline;
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+3);
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *(pDataline+1);
+              *(pScanline+3) = *pDataline;
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do simple alpha composing */
+                                       /* alpha itself remains fully opaque !!! */
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                MNG_COMPOSE8 (*(pScanline+i+1), *(pDataline+2-i), iFGa8, *(pScanline+i+1));
+                }
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline,     *(pDataline+1), *(pDataline+2), iFGa8,
+                            *(pScanline+3), *(pScanline+2), *(pScanline+1), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCa8;
+                *(pScanline+1) = iCb8;
+                *(pScanline+2) = iCg8;
+                *(pScanline+3) = iCr8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_abgr8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *pScanline     = *(pDataline+3);
+          *(pScanline+1) = *(pDataline+2);
+          *(pScanline+2) = *(pDataline+1);
+          *(pScanline+3) = *pDataline;
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *pScanline;
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *pScanline     = *(pDataline+3);
+              *(pScanline+1) = *(pDataline+2);
+              *(pScanline+2) = *(pDataline+1);
+              *(pScanline+3) = *pDataline;
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do simple alpha composing */
+                                       /* alpha itself remains fully opaque !!! */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                MNG_COMPOSE8 (*(pScanline+i+1), *(pDataline+2-i), iFGa8, *(pScanline+i+1));
+                }
+#else
+                MNG_COMPOSE8 (*(pScanline+1), *(pDataline+2), iFGa8, *(pScanline+1));
+                MNG_COMPOSE8 (*(pScanline+2), *(pDataline+1), iFGa8, *(pScanline+2));
+                MNG_COMPOSE8 (*(pScanline+3), *pDataline,     iFGa8, *(pScanline+3));
+#endif
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline,     *(pDataline+1), *(pDataline+2), iFGa8,
+                            *(pScanline+3), *(pScanline+2), *(pScanline+1), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *pScanline     = iCa8;
+                *(pScanline+1) = iCb8;
+                *(pScanline+2) = iCg8;
+                *(pScanline+3) = iCr8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_ABGR8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_ABGR8_PM
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_abgr8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+		  if ((s = pDataline[6]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+		      pScanline[1] = pDataline[4];
+              pScanline[2] = pDataline[2];
+              pScanline[3] = pDataline[0];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[4-i-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[4]);
+              pScanline[2] = DIV255B8(s * pDataline[2]);
+              pScanline[3] = DIV255B8(s * pDataline[0]);
+#endif
+			}
+		  }
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+		  if ((s = pDataline[3]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+		      pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[1];
+              pScanline[3] = pDataline[0];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[2-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[2]);
+              pScanline[2] = DIV255B8(s * pDataline[1]);
+		      pScanline[3] = DIV255B8(s * pDataline[0]);
+#endif
+			}
+		  }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          if ((s = pDataline[6]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if (s == 255)
+            {                          /* plain copy it */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[4];
+              pScanline[2] = pDataline[2];
+              pScanline[3] = pDataline[0];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[4-i-i] + t *
+                     pScanline[i+1]);
+                }
+              }
+#else
+			  pScanline[1] = DIV255B8(s * pDataline[4] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]);
+              pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[1];
+              pScanline[3] = pDataline[0];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[i+1]);
+                }
+              }
+#else
+			  pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]);
+              pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_abgr8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+		  if ((s = pDataline[6]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+		      pScanline[1] = pDataline[4];
+              pScanline[2] = pDataline[2];
+              pScanline[3] = pDataline[0];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[4-i-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[4]);
+              pScanline[2] = DIV255B8(s * pDataline[2]);
+              pScanline[3] = DIV255B8(s * pDataline[0]);
+#endif
+			}
+		  }
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+		  if ((s = pDataline[3]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+		      pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[1];
+              pScanline[3] = pDataline[0];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[2-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[2]);
+              pScanline[2] = DIV255B8(s * pDataline[1]);
+		      pScanline[3] = DIV255B8(s * pDataline[0]);
+#endif
+			}
+		  }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          if ((s = pDataline[6]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if (s == 255)
+            {                          /* plain copy it */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[4];
+              pScanline[2] = pDataline[2];
+              pScanline[3] = pDataline[0];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[4-i-i] + t *
+                     pScanline[i+1]);
+                }
+              }
+#else
+			  pScanline[1] = DIV255B8(s * pDataline[4] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]);
+              pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[1];
+              pScanline[3] = pDataline[0];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[i+1]);
+                }
+              }
+#else
+			  pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]);
+              pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_abgr8_pm (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint32 s, t;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values and premultiply */
+		  if ((s = pDataline[3]) == 0)
+			*(mng_uint32*) pScanline = 0; /* set all components = 0 */
+		  else
+		  {
+			if (s == 255)
+			{
+              pScanline[0] = 255;
+		      pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[1];
+              pScanline[3] = pDataline[0];
+			}
+			else
+			{
+              pScanline[0] = (mng_uint8)s;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[2-i]);
+                }
+              }
+#else
+              pScanline[1] = DIV255B8(s * pDataline[2]);
+              pScanline[2] = DIV255B8(s * pDataline[1]);
+		      pScanline[3] = DIV255B8(s * pDataline[0]);
+#endif
+			}
+		  }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          if ((s = pDataline[3]) != 0)       /* any opacity at all ? */
+          {                            /* fully opaque ? */
+            if (s == 255)
+            {                          /* then simply copy the values */
+              pScanline[0] = 255;
+              pScanline[1] = pDataline[2];
+              pScanline[2] = pDataline[1];
+              pScanline[3] = pDataline[0];
+            }
+            else
+            {                          /* now blend (premultiplied) */
+			  t = 255 - s;
+              pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0])));
+#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV
+              {
+                int i;
+                for (i=2; i >= 0; i--)
+                {
+                  pScanline[i+1] = DIV255B8(s * pDataline[2-i] + t *
+                     pScanline[i+1]);
+                }
+              }
+#else
+			  pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]);
+              pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]);
+              pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]);
+#endif
+            }
+          }
+
+          pScanline += (pData->iColinc << 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_ABGR8_PM */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGR565
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_bgr565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | (   (*(pDataline+2)>>5)       ) );
+          *pScanline     = (mng_uint8)( ( (*(pDataline+4)) >>3) | (   (*(pDataline+2)&0xFC) << 3) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 )  |  ( (*(pDataline+1)>>5   )     ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline+2) >>3 )  |  ( (*(pDataline+1)&0xFC ) << 3) );
+
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 )  |  (mng_uint8)( (*(pDataline+2)>>5  )     );
+              *pScanline     = (mng_uint8)( (*(pDataline+4)) >>3)  |  (mng_uint8)( (*(pDataline+2)&0xFC) << 3);
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+
+              iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+              iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+              iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 )  |  ( (mng_uint8)(iFGg16>>8) >> 5)      );
+              *pScanline     = (mng_uint8) ( ( (iFGb16>>11)       )  |  (((mng_uint8)(iFGg16>>8)&0xFC) << 3) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline)) &0xF8 )  |   (*(pDataline+1) >>5 )       );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline+2))>>3) )  |  ((*(pDataline+1)&0xFC) << 3) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+              iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0)>>3 ) );
+              iBlue  = (mng_uint8) ( (*pScanline << 3) );
+
+              MNG_COMPOSE8 (iRed,     *pDataline,     iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+2), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8) ( ( iRed  & 0xF8 )  |   (iGreen>>5) );
+              *pScanline     = (mng_uint8) ( ( iBlue >>  3  )  | ( (iGreen & 0xFC) << 3) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_bgr565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) |
+           (   (*(pDataline+iBps)>>5)       ) );
+          *pScanline     = (mng_uint8)( ( (*(pDataline+2*iBps)) >>3) |
+           (   (*(pDataline+iBps)&0xFC) << 3) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 )  |  (mng_uint8)( (*(pDataline+2)>>5  )     );
+              *pScanline     = (mng_uint8)( (*(pDataline+4)) >>3)  |  (mng_uint8)( (*(pDataline+2)&0xFC) << 3);
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+
+              iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+              iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+              iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 )  |  ( (mng_uint8)(iFGg16>>8) >> 5)      );
+              *pScanline     = (mng_uint8) ( ( (iFGb16>>11)       )  |  (((mng_uint8)(iFGg16>>8)&0xFC) << 3) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline)) &0xF8 )  |   (*(pDataline+1) >>5 )       );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline+2))>>3) )  |  ((*(pDataline+1)&0xFC) << 3) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+              iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0)>>3 ) );
+              iBlue  = (mng_uint8) ( (*pScanline << 3) );
+
+              MNG_COMPOSE8 (iRed,     *pDataline,     iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+2), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8) ( ( iRed  & 0xF8 )  |   (iGreen>>5) );
+              *pScanline     = (mng_uint8) ( ( iBlue >>  3  )  | ( (iGreen & 0xFC) << 3) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_bgr565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 )  |  ( (*(pDataline+1)>>5   )     ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline+2) >>3 )  |  ( (*(pDataline+1)&0xFC ) << 3) );
+
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline)) &0xF8 )  |   (*(pDataline+1) >>5 )       );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline+2))>>3) )  |  ((*(pDataline+1)&0xFC) << 3) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+              iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0)>>3 ) );
+              iBlue  = (mng_uint8) ( (*pScanline << 3) );
+
+              MNG_COMPOSE8 (iRed,     *pDataline,     iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+2), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8) ( ( iRed  & 0xF8 )  |   (iGreen>>5) );
+              *pScanline     = (mng_uint8) ( ( iBlue >>  3  )  | ( (iGreen & 0xFC) << 3) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_BGR565 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGB565
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_rgb565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( ( *(pDataline+4)) & 0xF8)  |   (*(pDataline+2) >> 5  )       );
+          *pScanline     = (mng_uint8)( ( ( *(pDataline  )) >> 3  )  |  ((*(pDataline+2) & 0xFC) << 3) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8)  |   (*(pDataline+1) >> 5        ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline  )  >> 3  )  |  ((*(pDataline+1) & 0xFC) << 3) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( ( (*(pDataline+4)) & 0xF8)  |   (*(pDataline+2)>>5) );
+              *pScanline     = (mng_uint8)( ( (*(pDataline  )) >> 3  )  |  ((*(pDataline+2)&0xFC) << 3) );
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+
+			                           /* scale background up */
+              iBGr16 = (mng_uint8)(  *(pScanline+1) & 0xF8 );
+              iBGg16 = (mng_uint8)( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0) >> 3 ) );
+              iBGb16 = (mng_uint8)(  *(pScanline  ) << 3   );
+
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *(pScanline+1) = (mng_uint8)( (mng_uint8)((iFGb16 >> 8) &0xF8) |   (   (mng_uint8)(iFGg16 >> 8) >> 5  )        );
+              *pScanline     = (mng_uint8)( (mng_uint8) (iFGr16 >>11)        |   ( ( (mng_uint8)(iFGg16 >> 8) & 0xFC) << 3)  );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8)  |  (  *(pDataline+1) >> 5         ) );
+              *pScanline     = (mng_uint8)( ( (*(pDataline  )) >> 3  )  |  ( (*(pDataline+1) & 0xFC) << 3 ) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8)(   *(pScanline+1) & 0xF8);
+              iGreen = (mng_uint8)( ( *(pScanline+1) << 5  )  |  ( ( (*pScanline)&0xE0)>>3 ) );
+              iBlue  = (mng_uint8)(   *(pScanline  ) << 3 );
+
+              MNG_COMPOSE8 (iRed,     *(pDataline+2), iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+0), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8)( ( iRed & 0xF8)  |  (  iGreen >> 5        ) );
+              *pScanline     = (mng_uint8)( (iBlue >> 3  )  |  ( (iGreen & 0xFC) << 3) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_rgb565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( ( *(pDataline+2*iBps)) & 0xF8)  |
+              (*(pDataline+iBps) >> 5  )       );
+          *pScanline     = (mng_uint8)( ( ( *(pDataline  )) >> 3  )  |
+             ((*(pDataline+iBps) & 0xFC) << 3) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( ( (*(pDataline+4)) & 0xF8)  |   (*(pDataline+2)>>5) );
+              *pScanline     = (mng_uint8)( ( (*(pDataline  )) >> 3  )  |  ((*(pDataline+2)&0xFC) << 3) );
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+
+			                           /* scale background up */
+              iBGr16 = (mng_uint8)(  *(pScanline+1) & 0xF8 );
+              iBGg16 = (mng_uint8)( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0) >> 3 ) );
+              iBGb16 = (mng_uint8)(  *(pScanline  ) << 3   );
+
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *(pScanline+1) = (mng_uint8)( (mng_uint8)((iFGb16 >> 8) &0xF8) |   (   (mng_uint8)(iFGg16 >> 8) >> 5  )        );
+              *pScanline     = (mng_uint8)( (mng_uint8) (iFGr16 >>11)        |   ( ( (mng_uint8)(iFGg16 >> 8) & 0xFC) << 3)  );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8)  |  (  *(pDataline+1) >> 5         ) );
+              *pScanline     = (mng_uint8)( ( (*(pDataline  )) >> 3  )  |  ( (*(pDataline+1) & 0xFC) << 3 ) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8)(   *(pScanline+1) & 0xF8);
+              iGreen = (mng_uint8)( ( *(pScanline+1) << 5  )  |  ( ( (*pScanline)&0xE0)>>3 ) );
+              iBlue  = (mng_uint8)(   *(pScanline  ) << 3 );
+
+              MNG_COMPOSE8 (iRed,     *(pDataline+2), iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+0), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8)( ( iRed & 0xF8)  |  (  iGreen >> 5        ) );
+              *pScanline     = (mng_uint8)( (iBlue >> 3  )  |  ( (iGreen & 0xFC) << 3) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_rgb565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8)  |   (*(pDataline+1) >> 5        ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline  )  >> 3  )  |  ((*(pDataline+1) & 0xFC) << 3) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8)  |  (  *(pDataline+1) >> 5         ) );
+              *pScanline     = (mng_uint8)( ( (*(pDataline  )) >> 3  )  |  ( (*(pDataline+1) & 0xFC) << 3 ) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8)(   *(pScanline+1) & 0xF8);
+              iGreen = (mng_uint8)( ( *(pScanline+1) << 5  )  |  ( ( (*pScanline)&0xE0)>>3 ) );
+              iBlue  = (mng_uint8)(   *(pScanline  ) << 3 );
+
+              MNG_COMPOSE8 (iRed,     *(pDataline+2), iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+0), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8)( ( iRed & 0xF8)  |  (  iGreen >> 5        ) );
+              *pScanline     = (mng_uint8)( (iBlue >> 3  )  |  ( (iGreen & 0xFC) << 3) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_RGB565 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGRA565
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_bgra565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | (   (*(pDataline+2)>>5)       ) );
+          *pScanline     = (mng_uint8)( ( (*(pDataline+4)) >>3) | (   (*(pDataline+2)&0xFC) << 3) );
+          *(pScanline+2) = *(pDataline+6);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 )  |  ( (*(pDataline+1)>>5   )     ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline+2) >>3 )  |  ( (*(pDataline+1)&0xFC ) << 3) );
+          *(pScanline+2) = *(pDataline+3);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*(pScanline+2));
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 )  |  (mng_uint8)( (*(pDataline+2)>>5  )     );
+              *pScanline     = (mng_uint8)( (*(pDataline+4)) >>3)  |  (mng_uint8)( (*(pDataline+2)&0xFC) << 3);
+			  *(pScanline+2) = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+                iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+				iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+
+                                       /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 )  |  ( (mng_uint8)(iFGg16>>8) >> 5)      );
+                *pScanline     = (mng_uint8) ( ( (iFGb16>>11)       )  |  (((mng_uint8)(iFGg16>>8)&0xFC) << 3) );
+              }
+              else
+              {                        /* scale background up */
+                iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+				iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iCr16 >>  8) & 0xF8 )  |  ( (mng_uint8)(iCg16 >> 8) >> 5  )       );
+                *pScanline     = (mng_uint8) ( ( (iCb16 >> 11)        )  |  (((mng_uint8)(iCg16 >> 8) & 0xFC) << 3) );
+                *(pScanline+2) = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+2);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline)) &0xF8 )  |   (*(pDataline+1) >>5 )       );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline+2))>>3) )  |  ((*(pDataline+1)&0xFC) << 3) );
+              *(pScanline+2) = *(pDataline+3);
+            }
+            else
+            {
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+              iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0)>>3 ) );
+              iBlue  = (mng_uint8) ( (*pScanline << 3) );
+
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                MNG_COMPOSE8 (iRed,   *pDataline,     iFGa8, iRed   );
+                MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen );
+                MNG_COMPOSE8 (iBlue,  *(pDataline+2), iFGa8, iBlue  );
+                                       /* alpha remains fully opaque !!! */
+                *(pScanline+1) = (mng_uint8) ( ( iRed  & 0xF8 )  |   (iGreen>>5) );
+                *pScanline     = (mng_uint8) ( ( iBlue >>  3  )  | ( (iGreen & 0xFC) << 3) );
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            iRed      , iGreen        , iBlue         , iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+
+
+                *pScanline     = (mng_uint8) ( ( iCb8 >>  3  )  | ( (iCg8 & 0xFC) << 3) );
+                *(pScanline+1) = (mng_uint8) ( ( iCr8  & 0xF8 )  |   (iCg8>>5) );
+				*(pScanline+2) = (mng_uint8) iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc *3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_bgra565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) |
+              (   (*(pDataline+iBps)>>5)       ) );
+          *pScanline     = (mng_uint8)( ( (*(pDataline+2*iBps)) >>3) |
+              (   (*(pDataline+iBps)&0xFC) << 3) );
+          *(pScanline+2) = *(pDataline+3*iBps);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*(pScanline+2));
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 )  |  (mng_uint8)( (*(pDataline+2)>>5  )     );
+              *pScanline     = (mng_uint8)( (*(pDataline+4)) >>3)  |  (mng_uint8)( (*(pDataline+2)&0xFC) << 3);
+			  *(pScanline+2) = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+                iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+				iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+
+                                       /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 )  |  ( (mng_uint8)(iFGg16>>8) >> 5)      );
+                *pScanline     = (mng_uint8) ( ( (iFGb16>>11)       )  |  (((mng_uint8)(iFGg16>>8)&0xFC) << 3) );
+              }
+              else
+              {                        /* scale background up */
+                iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+				iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iCr16 >>  8) & 0xF8 )  |  ( (mng_uint8)(iCg16 >> 8) >> 5  )       );
+                *pScanline     = (mng_uint8) ( ( (iCb16 >> 11)        )  |  (((mng_uint8)(iCg16 >> 8) & 0xFC) << 3) );
+                *(pScanline+2) = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+2);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline)) &0xF8 )  |   (*(pDataline+1) >>5 )       );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline+2))>>3) )  |  ((*(pDataline+1)&0xFC) << 3) );
+              *(pScanline+2) = *(pDataline+3);
+            }
+            else
+            {
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+              iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0)>>3 ) );
+              iBlue  = (mng_uint8) ( (*pScanline << 3) );
+
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                MNG_COMPOSE8 (iRed,   *pDataline,     iFGa8, iRed   );
+                MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen );
+                MNG_COMPOSE8 (iBlue,  *(pDataline+2), iFGa8, iBlue  );
+                                       /* alpha remains fully opaque !!! */
+                *(pScanline+1) = (mng_uint8) ( ( iRed  & 0xF8 )  |   (iGreen>>5) );
+                *pScanline     = (mng_uint8) ( ( iBlue >>  3  )  | ( (iGreen & 0xFC) << 3) );
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            iRed      , iGreen        , iBlue         , iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+
+
+                *pScanline     = (mng_uint8) ( ( iCb8 >>  3  )  | ( (iCg8 & 0xFC) << 3) );
+                *(pScanline+1) = (mng_uint8) ( ( iCr8  & 0xF8 )  |   (iCg8>>5) );
+				*(pScanline+2) = (mng_uint8) iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc *3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_bgra565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 )  |  ( (*(pDataline+1)>>5   )     ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline+2) >>3 )  |  ( (*(pDataline+1)&0xFC ) << 3) );
+          *(pScanline+2) = *(pDataline+3);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+2);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline)) &0xF8 )  |   (*(pDataline+1) >>5 )       );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline+2))>>3) )  |  ((*(pDataline+1)&0xFC) << 3) );
+              *(pScanline+2) = *(pDataline+3);
+            }
+            else
+            {
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+              iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0)>>3 ) );
+              iBlue  = (mng_uint8) ( (*pScanline << 3) );
+
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                MNG_COMPOSE8 (iRed,   *pDataline,     iFGa8, iRed   );
+                MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen );
+                MNG_COMPOSE8 (iBlue,  *(pDataline+2), iFGa8, iBlue  );
+                                       /* alpha remains fully opaque !!! */
+                *(pScanline+1) = (mng_uint8) ( ( iRed  & 0xF8 )  |   (iGreen>>5) );
+                *pScanline     = (mng_uint8) ( ( iBlue >>  3  )  | ( (iGreen & 0xFC) << 3) );
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            iRed      , iGreen        , iBlue         , iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+
+
+                *pScanline     = (mng_uint8) ( ( iCb8 >>  3  )  | ( (iCg8 & 0xFC) << 3) );
+                *(pScanline+1) = (mng_uint8) ( ( iCr8  & 0xF8 )  |   (iCg8>>5) );
+				*(pScanline+2) = (mng_uint8) iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc *3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_BGRA565 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGBA565
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_rgba565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline+4))&0xF8 ) | (   (*(pDataline+2)>>5)       ) );
+          *pScanline     = (mng_uint8)( ( (*(pDataline)) >>3) | (   (*(pDataline+2)&0xFC) << 3) );
+          *(pScanline+2) = *(pDataline+6);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2))&0xF8 )  |  ( (*(pDataline+1)>>5   )     ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline) >>3 )  |  ( (*(pDataline+1)&0xFC ) << 3) );
+          *(pScanline+2) = *(pDataline+3);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*(pScanline+2));
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *(pScanline+1) = (mng_uint8)( (*(pDataline+4))&0xF8 )  |  (mng_uint8)( (*(pDataline+2)>>5  )     );
+              *pScanline     = (mng_uint8)( (*(pDataline)) >>3)  |  (mng_uint8)( (*(pDataline+2)&0xFC) << 3);
+			  *(pScanline+2) = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+                iBGr16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGb16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+				iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+
+                                       /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iFGb16 >> 8)&0xF8 )  |  ( (mng_uint8)(iFGg16>>8) >> 5)      );
+                *pScanline     = (mng_uint8) ( ( (iFGr16>>11)       )  |  (((mng_uint8)(iFGg16>>8)&0xFC) << 3) );
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGb16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+				iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iCb16 >>  8) & 0xF8 )  |  ( (mng_uint8)(iCg16 >> 8) >> 5  )       );
+                *pScanline     = (mng_uint8) ( ( (iCr16 >> 11)        )  |  (((mng_uint8)(iCg16 >> 8) & 0xFC) << 3) );
+                *(pScanline+2) = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+2);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline+2)) &0xF8 )  |   (*(pDataline+1) >>5 )       );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline))>>3) )  |  ((*(pDataline+1)&0xFC) << 3) );
+              *(pScanline+2) = *(pDataline+3);
+            }
+            else
+            {
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iBlue   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+              iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0)>>3 ) );
+              iRed  = (mng_uint8) ( (*pScanline << 3) );
+
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                MNG_COMPOSE8 (iRed,   *pDataline,     iFGa8, iRed   );
+                MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen );
+                MNG_COMPOSE8 (iBlue,  *(pDataline+2), iFGa8, iBlue  );
+                                       /* alpha remains fully opaque !!! */
+                *(pScanline+1) = (mng_uint8) ( ( iBlue  & 0xF8 )  |   (iGreen>>5) );
+                *pScanline     = (mng_uint8) ( ( iRed >>  3  )  | ( (iGreen & 0xFC) << 3) );
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            iRed      , iGreen        , iBlue         , iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+
+
+                *pScanline     = (mng_uint8) ( ( iCr8 >>  3  )  | ( (iCg8 & 0xFC) << 3) );
+                *(pScanline+1) = (mng_uint8) ( ( iCb8  & 0xF8 )  |   (iCg8>>5) );
+				*(pScanline+2) = (mng_uint8) iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc *3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_rgba565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint16 iFGa16, iBGa16, iCa16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint16 iCr16, iCg16, iCb16;
+  mng_uint8  iCr8, iCg8, iCb8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2*iBps))&0xF8 ) |
+             (   (*(pDataline+iBps)>>5)       ) );
+          *pScanline     = (mng_uint8)( ( (*(pDataline)) >>3) |
+             (   (*(pDataline+iBps)&0xFC) << 3) );
+          *(pScanline+2) = *(pDataline+3*iBps);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* get alpha values */
+          iFGa16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*(pScanline+2));
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iFGa16)                  /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+            {                          /* plain copy it */
+              *(pScanline+1) = (mng_uint8)( (*(pDataline+4))&0xF8 )  |  (mng_uint8)( (*(pDataline+2)>>5  )     );
+              *pScanline     = (mng_uint8)( (*(pDataline)) >>3)  |  (mng_uint8)( (*(pDataline+2)&0xFC) << 3);
+			  *(pScanline+2) = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                       /* scale background up */
+                iBGr16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGb16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+				iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+
+                                       /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iFGb16 >> 8)&0xF8 )  |  ( (mng_uint8)(iFGg16>>8) >> 5)      );
+                *pScanline     = (mng_uint8) ( ( (iFGr16>>11)       )  |  (((mng_uint8)(iFGg16>>8)&0xFC) << 3) );
+              }
+              else
+              {                        /* scale background up */
+                iBGr16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  |  (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGb16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+				iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iFGa16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iCb16 >>  8) & 0xF8 )  |  ( (mng_uint8)(iCg16 >> 8) >> 5  )       );
+                *pScanline     = (mng_uint8) ( ( (iCr16 >> 11)        )  |  (((mng_uint8)(iCg16 >> 8) & 0xFC) << 3) );
+                *(pScanline+2) = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+2);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline+2)) &0xF8 )  |   (*(pDataline+1) >>5 )       );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline))>>3) )  |  ((*(pDataline+1)&0xFC) << 3) );
+              *(pScanline+2) = *(pDataline+3);
+            }
+            else
+            {
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iBlue   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+              iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0)>>3 ) );
+              iRed  = (mng_uint8) ( (*pScanline << 3) );
+
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                MNG_COMPOSE8 (iRed,   *pDataline,     iFGa8, iRed   );
+                MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen );
+                MNG_COMPOSE8 (iBlue,  *(pDataline+2), iFGa8, iBlue  );
+                                       /* alpha remains fully opaque !!! */
+                *(pScanline+1) = (mng_uint8) ( ( iBlue  & 0xF8 )  |   (iGreen>>5) );
+                *pScanline     = (mng_uint8) ( ( iRed >>  3  )  | ( (iGreen & 0xFC) << 3) );
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            iRed      , iGreen        , iBlue         , iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+
+
+                *pScanline     = (mng_uint8) ( ( iCr8 >>  3  )  | ( (iCg8 & 0xFC) << 3) );
+                *(pScanline+1) = (mng_uint8) ( ( iCb8  & 0xF8 )  |   (iCg8>>5) );
+				*(pScanline+2) = (mng_uint8) iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc *3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_rgba565 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iFGa8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2))&0xF8 )  |  ( (*(pDataline+1)>>5   )     ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline) >>3 )  |  ( (*(pDataline+1)&0xFC ) << 3) );
+          *(pScanline+2) = *(pDataline+3);
+
+          pScanline += (pData->iColinc * 3);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iFGa8 = *(pDataline+3);      /* get alpha values */
+          iBGa8 = *(pScanline+2);
+
+          if (iFGa8)                   /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline+2)) &0xF8 )  |   (*(pDataline+1) >>5 )       );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline))>>3) )  |  ((*(pDataline+1)&0xFC) << 3) );
+              *(pScanline+2) = *(pDataline+3);
+            }
+            else
+            {
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iBlue   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+              iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  ( ((*pScanline) & 0xE0)>>3 ) );
+              iRed  = (mng_uint8) ( (*pScanline << 3) );
+
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {                        /* do alpha composing */
+                MNG_COMPOSE8 (iRed,   *pDataline,     iFGa8, iRed   );
+                MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen );
+                MNG_COMPOSE8 (iBlue,  *(pDataline+2), iFGa8, iBlue  );
+                                       /* alpha remains fully opaque !!! */
+                *(pScanline+1) = (mng_uint8) ( ( iBlue  & 0xF8 )  |   (iGreen>>5) );
+                *pScanline     = (mng_uint8) ( ( iRed >>  3  )  | ( (iGreen & 0xFC) << 3) );
+              }
+              else
+              {                        /* now blend */
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8,
+                            iRed      , iGreen        , iBlue         , iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+
+
+                *pScanline     = (mng_uint8) ( ( iCr8 >>  3  )  | ( (iCg8 & 0xFC) << 3) );
+                *(pScanline+1) = (mng_uint8) ( ( iCb8  & 0xF8 )  |   (iCg8>>5) );
+				*(pScanline+2) = (mng_uint8) iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc *3);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_RGBA565 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGR565_A8
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_bgr565_a8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pAlphaline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16, iBGa16;
+  mng_uint16 iCr16,  iCg16,  iCb16,  iCa16;
+  mng_uint8  iA8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+    pAlphaline = (mng_uint8p)pData->fGetalphaline  (((mng_handle)pData),
+                                                    pData->iRow + pData->iDestt -
+                                                    pData->iSourcet);
+                                       /* adjust destination row
+starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pAlphaline = pAlphaline + pData->iCol + pData->iDestl;
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)       /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ((*(pDataline+2)>>5)       ) );
+          *pScanline     = (mng_uint8)( ( (*(pDataline+4)) >>3) | ((*(pDataline+2)&0xFC) << 3) );
+          *pAlphaline    = (mng_uint8)(*(pDataline+6));
+
+          pScanline += (pData->iColinc * 2);
+          pAlphaline += pData->iColinc;
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 )  |  ((*(pDataline+1)>>5   )     ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline+2) >>3 )  |  ((*(pDataline+1)&0xFC ) << 3) );
+          *pAlphaline    = (mng_uint8)(*(pDataline+3));
+
+          pScanline += (pData->iColinc * 2);
+          pAlphaline += pData->iColinc;
+          pDataline += 4;
+        }
+      }
+    }
+    else /* Not fully opaque */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*pAlphaline);
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if ((iA16 == 0xFFFF) || (iBGa16 == 0))       /* fully opaque or background fully transparent ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 )  | (mng_uint8)( (*(pDataline+2)>>5  )     );
+              *pScanline     = (mng_uint8)( (*(pDataline+4)) >>3)  | (mng_uint8)( (*(pDataline+2)&0xFC) << 3);
+              *pAlphaline    = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {
+                                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                         /* scale background up */
+
+                iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  | (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                         /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                         /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 )  | ( (mng_uint8)(iFGg16>>8) >> 5)       );
+                *pScanline     = (mng_uint8) ( ( (iFGb16>>11)       )  | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) );
+                *pAlphaline    = (mng_uint8)(iA16>>8);
+              }
+              else /* background is not fully opaque */
+              {                         /* scale background up */
+                iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  | (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iA16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iCr16 >> 8)&0xF8 )  | ( (mng_uint8)(iCg16>>8) >> 5)       );
+                *pScanline     = (mng_uint8) ( ( (iCb16>>11)       )  | (((mng_uint8)(iCg16>>8)&0xFC) << 3) );
+                *pAlphaline    = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pAlphaline += pData->iColinc;
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+          iBGa8 = *pAlphaline;
+
+          if (iA8)                     /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iA8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline)) &0xF8 )  | (*(pDataline+1) >>5 )        );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline+2))>>3) )  | ((*(pDataline+1)&0xFC) << 3) );
+              *pAlphaline    = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {
+                /* do alpha composing */
+                mng_uint8 iRed, iGreen, iBlue;
+
+                iRed   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+                iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  (((*pScanline) & 0xE0)>>3 ) );
+                iBlue  = (mng_uint8) ( (*pScanline << 3) );
+
+                MNG_COMPOSE8 (iRed,     *pDataline,     iA8, iRed    );
+                MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+                MNG_COMPOSE8 (iBlue,    *(pDataline+2), iA8, iBlue   );
+
+                *(pScanline+1) = (mng_uint8) ( ( iRed  & 0xF8 )  | (iGreen>>5) );
+                *pScanline     = (mng_uint8) ( ( iBlue >>  3  )  | ((iGreen & 0xFC) << 3) );
+                *pAlphaline    = iA8;
+              }
+              else /* background not fully opaque */
+              {
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iA8,
+                            *pScanline, *(pScanline+1), *(pScanline+2), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( iCr8  & 0xF8 )  | (iCg8>>5) );
+                *pScanline     = (mng_uint8) ( ( iCb8 >>  3  )  | ((iCg8 & 0xFC) << 3) );
+                *pAlphaline    = iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pAlphaline += pData->iColinc;
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_bgr565_a8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pAlphaline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16, iBGa16;
+  mng_uint16 iCr16,  iCg16,  iCb16,  iCa16;
+  mng_uint8  iA8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+  mng_uint8  iBps;
+
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+    pAlphaline = (mng_uint8p)pData->fGetalphaline  (((mng_handle)pData),
+                                                    pData->iRow + pData->iDestt -
+                                                    pData->iSourcet);
+                                       /* adjust destination row
+starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pAlphaline = pAlphaline + pData->iCol + pData->iDestl;
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) |
+              ((*(pDataline+iBps)>>5)       ) );
+          *pScanline     = (mng_uint8)( ( (*(pDataline+2*iBps)) >>3) |
+              ((*(pDataline+iBps)&0xFC) << 3) );
+          *pAlphaline    = (mng_uint8)(*(pDataline+6));
+
+          pScanline += (pData->iColinc * 2);
+          pAlphaline += pData->iColinc;
+          pDataline += 8;
+        }
+    }
+    else /* Not fully opaque */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+          iBGa16 = (mng_uint16)(*pAlphaline);
+          iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16;
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if ((iA16 == 0xFFFF) || (iBGa16 == 0))       /* fully opaque or background fully transparent ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 )  | (mng_uint8)( (*(pDataline+2)>>5  )     );
+              *pScanline     = (mng_uint8)( (*(pDataline+4)) >>3)  | (mng_uint8)( (*(pDataline+2)&0xFC) << 3);
+              *pAlphaline    = *(pDataline+6);
+            }
+            else
+            {
+              if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+              {
+                                        /* get the proper values */
+                iFGr16 = mng_get_uint16 (pDataline  );
+                iFGg16 = mng_get_uint16 (pDataline+2);
+                iFGb16 = mng_get_uint16 (pDataline+4);
+                                         /* scale background up */
+
+                iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  | (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                         /* now compose */
+                MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+                MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+                MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                         /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 )  | ( (mng_uint8)(iFGg16>>8) >> 5)       );
+                *pScanline     = (mng_uint8) ( ( (iFGb16>>11)       )  | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) );
+                *pAlphaline    = (mng_uint8)(iA16>>8);
+              }
+              else /* background is not fully opaque */
+              {                         /* scale background up */
+                iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 );
+                iBGg16 = (mng_uint16)( (*(pScanline+1) << 5)  | (((*(pScanline  )) & 0xE0) >>3 ) );
+                iBGr16 = (mng_uint16)( (*(pScanline  )) << 3   );
+
+                iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+                iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+                iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* let's blend */
+                MNG_BLEND16 (mng_get_uint16 (pDataline  ),
+                             mng_get_uint16 (pDataline+2),
+                             mng_get_uint16 (pDataline+4), iA16,
+                             iBGr16, iBGg16, iBGb16, iBGa16,
+                             iCr16,  iCg16,  iCb16,  iCa16);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( (iCr16 >> 8)&0xF8 )  | ( (mng_uint8)(iCg16>>8) >> 5)       );
+                *pScanline     = (mng_uint8) ( ( (iCb16>>11)       )  | (((mng_uint8)(iCg16>>8)&0xFC) << 3) );
+                *pAlphaline    = (mng_uint8)(iCa16 >> 8);
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pAlphaline += pData->iColinc;
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+          iBGa8 = *pAlphaline;
+
+          if (iA8)                     /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iA8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline)) &0xF8 )  | (*(pDataline+1) >>5 )        );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline+2))>>3) )  | ((*(pDataline+1)&0xFC) << 3) );
+              *pAlphaline    = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {
+                /* do alpha composing */
+                mng_uint8 iRed, iGreen, iBlue;
+
+                iRed   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+                iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  (((*pScanline) & 0xE0)>>3 ) );
+                iBlue  = (mng_uint8) ( (*pScanline << 3) );
+
+                MNG_COMPOSE8 (iRed,     *pDataline,     iA8, iRed    );
+                MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+                MNG_COMPOSE8 (iBlue,    *(pDataline+2), iA8, iBlue   );
+
+                *(pScanline+1) = (mng_uint8) ( ( iRed  & 0xF8 )  | (iGreen>>5) );
+                *pScanline     = (mng_uint8) ( ( iBlue >>  3  )  | ((iGreen & 0xFC) << 3) );
+                *pAlphaline    = iA8;
+              }
+              else /* background not fully opaque */
+              {
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iA8,
+                            *pScanline, *(pScanline+1), *(pScanline+2), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( iCr8  & 0xF8 )  | (iCg8>>5) );
+                *pScanline     = (mng_uint8) ( ( iCb8 >>  3  )  | ((iCg8 & 0xFC) << 3) );
+                *pAlphaline    = iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pAlphaline += pData->iColinc;
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_bgr565_a8 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pAlphaline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iA8, iBGa8, iCa8;
+  mng_uint8  iCr8, iCg8, iCb8;
+
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+    pAlphaline = (mng_uint8p)pData->fGetalphaline  (((mng_handle)pData),
+                                                    pData->iRow + pData->iDestt -
+                                                    pData->iSourcet);
+                                       /* adjust destination row
+starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pAlphaline = pAlphaline + pData->iCol + pData->iDestl;
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 )  |  ((*(pDataline+1)>>5   )     ) );
+          *pScanline     = (mng_uint8)( (  *(pDataline+2) >>3 )  |  ((*(pDataline+1)&0xFC ) << 3) );
+          *pAlphaline    = (mng_uint8)(*(pDataline+3));
+
+          pScanline += (pData->iColinc * 2);
+          pAlphaline += pData->iColinc;
+          pDataline += 4;
+        }
+      }
+    }
+    else /* Not fully opaque */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+          iBGa8 = *pAlphaline;
+
+          if (iA8)                     /* any opacity at all ? */
+          {                            /* fully opaque or background fully transparent ? */
+            if ((iA8 == 0xFF) || (iBGa8 == 0))
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( (  (*(pDataline)) &0xF8 )  | (*(pDataline+1) >>5 )        );
+              *pScanline     = (mng_uint8)( ( ((*(pDataline+2))>>3) )  | ((*(pDataline+1)&0xFC) << 3) );
+              *pAlphaline    = *(pDataline+3);
+            }
+            else
+            {
+              if (iBGa8 == 0xFF)       /* background fully opaque ? */
+              {
+                /* do alpha composing */
+                mng_uint8 iRed, iGreen, iBlue;
+
+                iRed   = (mng_uint8) (  *(pScanline+1) & 0xF8 );
+                iGreen = (mng_uint8) ( (*(pScanline+1) << 5)  |  (((*pScanline) & 0xE0)>>3 ) );
+                iBlue  = (mng_uint8) ( (*pScanline << 3) );
+
+                MNG_COMPOSE8 (iRed,     *pDataline,     iA8, iRed    );
+                MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+                MNG_COMPOSE8 (iBlue,    *(pDataline+2), iA8, iBlue   );
+
+                *(pScanline+1) = (mng_uint8) ( ( iRed  & 0xF8 )  | (iGreen>>5) );
+                *pScanline     = (mng_uint8) ( ( iBlue >>  3  )  | ((iGreen & 0xFC) << 3) );
+                *pAlphaline    = iA8;
+              }
+              else /* background not fully opaque */
+              {
+                MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iA8,
+                            *pScanline, *(pScanline+1), *(pScanline+2), iBGa8,
+                            iCr8, iCg8, iCb8, iCa8);
+                                       /* and return the composed values */
+                *(pScanline+1) = (mng_uint8) ( ( iCr8  & 0xF8 )  | (iCg8>>5) );
+                *pScanline     = (mng_uint8) ( ( iCb8 >>  3  )  | ((iCg8 & 0xFC) << 3) );
+                *pAlphaline    = iCa8;
+              }
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pAlphaline += pData->iColinc;
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_BGR565_A8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGB555
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_rgb555 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ((*(pDataline+4) & 0xF8) >> 1 ) |  (*(pDataline+2)         >> 6 ) );
+          *pScanline     = (mng_uint8)( ( *(pDataline  )         >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+          *pScanline     = (mng_uint8)( ( *(pDataline  )         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline+4) & 0xF8) >> 1 ) |  (*(pDataline+2)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline  )         >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+
+			                           /* scale background up */
+              iBGr16 = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iBGg16 = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBGb16 = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *(pScanline+1) = (mng_uint8)( (mng_uint8)(((iFGb16 >> 8) & 0xF8) >> 1 ) | (   (mng_uint8)(iFGg16 >> 8)         >> 6 ) );
+              *pScanline     = (mng_uint8)( (mng_uint8) ((iFGr16 >>11)         >> 3 ) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline  )         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iGreen = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBlue  = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              MNG_COMPOSE8 (iRed,     *(pDataline+2), iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+0), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 )  |  (  iGreen         >> 6 ) );
+              *pScanline     = (mng_uint8)(   (iBlue        >> 3 )  |  ( (iGreen & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_rgb555 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ((*(pDataline+2*iBps) & 0xF8) >> 1 ) |  (*(pDataline+iBps)         >> 6 ) );
+          *pScanline     = (mng_uint8)( ( *(pDataline       )         >> 3 ) | ((*(pDataline+iBps) & 0xF8) << 2 ) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline+4) & 0xF8) >> 1 ) |  (*(pDataline+2)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline  )         >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+
+			                           /* scale background up */
+              iBGr16 = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iBGg16 = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBGb16 = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *(pScanline+1) = (mng_uint8)( (mng_uint8)(((iFGb16 >> 8) & 0xF8) >> 1 ) | (   (mng_uint8)(iFGg16 >> 8)         >> 6 ) );
+              *pScanline     = (mng_uint8)( (mng_uint8) ((iFGr16 >>11)         >> 3 ) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline  )         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iGreen = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBlue  = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              MNG_COMPOSE8 (iRed,     *(pDataline+2), iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+0), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 )  |  (  iGreen         >> 6 ) );
+              *pScanline     = (mng_uint8)(   (iBlue        >> 3 )  |  ( (iGreen & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_rgb555 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+          *pScanline     = (mng_uint8)( ( *(pDataline  )         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline  )         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iGreen = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBlue  = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              MNG_COMPOSE8 (iRed,     *(pDataline+2), iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+0), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 )  |  (  iGreen         >> 6 ) );
+              *pScanline     = (mng_uint8)(   (iBlue        >> 3 )  |  ( (iGreen & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_RGB555 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGR555
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+mng_retcode mng_display_bgr555 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    if (pData->bIsRGBA16)              /* adjust source row starting-point */
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3);
+    else
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ((*(pDataline  ) & 0xF8) >> 1 ) |  (*(pDataline+2)         >> 6 ) );
+          *pScanline     = (mng_uint8)( ( *(pDataline+4)         >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ((*(pDataline  ) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+          *pScanline     = (mng_uint8)( ( *(pDataline+2)         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline  ) & 0xF8) >> 1 ) |  (*(pDataline+2)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline+4)         >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+
+			                           /* scale background up */
+              iBGb16 = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iBGg16 = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBGr16 = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *(pScanline+1) = (mng_uint8)( (mng_uint8)(((iFGr16 >> 8) & 0xF8) >> 1 ) | (   (mng_uint8)(iFGg16 >> 8)         >> 6 ) );
+              *pScanline     = (mng_uint8)( (mng_uint8) ((iFGb16 >>11)         >> 3 ) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline  ) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline+2)         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iGreen = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBlue  = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              MNG_COMPOSE8 (iRed,     *(pDataline+0), iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+2), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 )  |  (  iGreen         >> 6 ) );
+              *pScanline     = (mng_uint8)(   (iBlue        >> 3 )  |  ( (iGreen & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+mng_retcode mng_display_bgr555 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint16 iA16;
+  mng_uint16 iFGr16, iFGg16, iFGb16;
+  mng_uint16 iBGr16, iBGg16, iBGb16;
+  mng_uint8  iA8;
+  mng_uint8  iBps;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_START);
+#endif
+
+  iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1);
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+    /* adjust source row starting-point */
+    pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1));
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* scale down by dropping the LSB */
+          *(pScanline+1) = (mng_uint8)( ((*(pDataline       ) & 0xF8) >> 1 ) |  (*(pDataline+iBps)         >> 6 ) );
+          *pScanline     = (mng_uint8)( ( *(pDataline+2*iBps)         >> 3 ) | ((*(pDataline+iBps) & 0xF8) << 2 ) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4*iBps;
+        }
+    }
+    else
+    {
+      if (pData->bIsRGBA16)            /* 16-bit input row ? */
+      {
+
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA16 = mng_get_uint16 (pDataline+6);
+
+          if (iA16)                    /* any opacity at all ? */
+          {
+            if (iA16 == 0xFFFF)        /* fully opaque ? */
+            {                          /* scale down by dropping the LSB */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline  ) & 0xF8) >> 1 ) |  (*(pDataline+2)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline+4)         >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* get the proper values */
+              iFGr16 = mng_get_uint16 (pDataline  );
+              iFGg16 = mng_get_uint16 (pDataline+2);
+              iFGb16 = mng_get_uint16 (pDataline+4);
+
+			                           /* scale background up */
+              iBGb16 = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iBGg16 = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBGr16 = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16;
+              iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16;
+              iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16;
+                                       /* now compose */
+              MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16);
+              MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16);
+              MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16);
+                                       /* and return the composed values */
+              *(pScanline+1) = (mng_uint8)( (mng_uint8)(((iFGr16 >> 8) & 0xF8) >> 1 ) | (   (mng_uint8)(iFGg16 >> 8)         >> 6 ) );
+              *pScanline     = (mng_uint8)( (mng_uint8) ((iFGb16 >>11)         >> 3 ) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 8;
+        }
+      }
+      else
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline  ) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline+2)         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iGreen = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBlue  = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              MNG_COMPOSE8 (iRed,     *(pDataline+0), iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+2), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 )  |  (  iGreen         >> 6 ) );
+              *pScanline     = (mng_uint8)(   (iBlue        >> 3 )  |  ( (iGreen & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */
+#else /* MNG_NO_16BIT_SUPPORT */
+mng_retcode mng_display_bgr555 (mng_datap pData)
+{
+  mng_uint8p pScanline;
+  mng_uint8p pDataline;
+  mng_int32  iX;
+  mng_uint8  iA8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_START);
+#endif
+                                       /* viewable row ? */
+  if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb))
+  {                                    /* address destination row */
+    pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData),
+                                                   pData->iRow + pData->iDestt -
+                                                   pData->iSourcet);
+                                       /* adjust destination row starting-point */
+    pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2);
+    pDataline = pData->pRGBArow;       /* address source row */
+
+      pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2);
+
+    if (pData->bIsOpaque)              /* forget about transparency ? */
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {                              /* copy the values */
+          *(pScanline+1) = (mng_uint8)( ((*(pDataline  ) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+          *pScanline     = (mng_uint8)( ( *(pDataline+2)         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+    else
+    {
+      {
+        for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer;
+             iX += pData->iColinc)
+        {
+          iA8 = *(pDataline+3);        /* get alpha value */
+
+          if (iA8)                     /* any opacity at all ? */
+          {
+            if (iA8 == 0xFF)           /* fully opaque ? */
+            {                          /* then simply copy the values */
+              *(pScanline+1) = (mng_uint8)( ((*(pDataline  ) & 0xF8) >> 1 ) |  (*(pDataline+1)         >> 6 ) );
+              *pScanline     = (mng_uint8)( ( *(pDataline+2)         >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) );
+            }
+            else
+            {                          /* do alpha composing */
+              mng_uint8 iRed, iGreen, iBlue;
+
+              iRed   = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 );
+              iGreen = (mng_uint8)( (*(pScanline+1)         << 6 )  |  ( ((*pScanline) & 0xE0) >> 2 ) );
+              iBlue  = (mng_uint8)(  *(pScanline  )         << 3 );
+
+              MNG_COMPOSE8 (iRed,     *(pDataline+0), iA8, iRed    );
+              MNG_COMPOSE8 (iGreen,   *(pDataline+1), iA8, iGreen  );
+              MNG_COMPOSE8 (iBlue,    *(pDataline+2), iA8, iBlue   );
+
+              *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 )  |  (  iGreen         >> 6 ) );
+              *pScanline     = (mng_uint8)(   (iBlue        >> 3 )  |  ( (iGreen & 0xF8) << 2 ) );
+            }
+          }
+
+          pScanline += (pData->iColinc * 2);
+          pDataline += 4;
+        }
+      }
+    }
+  }
+
+  check_update_region (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_SKIPCANVAS_BGR555 */
+
+
+#ifndef MNG_SKIPCHUNK_BACK
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Background restore routines - restore the background with info from    * */
+/* * the BACK and/or bKGD chunk or the app's background canvas              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_restore_bkgd_backimage (mng_datap pData)
+{
+                                       /* save some stuff */
+  mng_uint8p  pRGBArow    = pData->pRGBArow;
+  mng_int32   iRow        = pData->iRow;
+  mng_int32   iRowsamples = pData->iRowsamples;
+
+  mng_retcode iRetcode;                /* work variables */
+  mng_uint8p  pTemp;
+  mng_uint8p  pWork       = pRGBArow;
+  mng_uint32  iX;
+  mng_int32   iZ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BACKIMAGE, MNG_LC_START);
+#endif
+                                       /* determine row to retrieve */
+  pData->iRow        = pData->iDestt + iRow + pData->iBackimgoffsy;
+
+  while (pData->iRow >= (mng_int32)pData->iBackimgheight)
+    pData->iRow -= (mng_int32)pData->iBackimgheight;
+                                       /* set width to that of background image */
+  pData->iRowsamples = pData->iBackimgwidth;
+                                       /* retrieve into alternate buffer ! */
+  pData->pRGBArow    = pData->pPrevrow;
+                                       /* get it then */
+  iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData);
+
+  if (iRetcode)                        /* on error; bail out */
+    return iRetcode;
+                                       /* we got the full row; but now need to
+                                          paste it into the proper location */
+  iX = pData->iDestl - pData->iBackimgoffsx;
+
+  while (iX >= pData->iBackimgwidth)
+    iX -= pData->iBackimgwidth;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+  if (pData->bIsRGBA16)                /* 16-bit buffer ? */
+  {
+    pTemp = pData->pPrevrow + (iX << 3);
+
+    for (iZ = (pData->iDestr - pData->iDestl); iZ > 0; iZ--)
+    {
+      MNG_COPY (pWork, pTemp, 8);
+
+      pWork += 8;
+      pTemp += 8;
+      iX++;
+                                       /* reached end of bkgd-image line ? */
+      if (iX >= pData->iBackimgwidth)
+      {
+        iX    = 0;
+        pTemp = pData->pPrevrow;
+      }
+    }
+  }
+  else
+#endif
+  {
+    pTemp = pData->pPrevrow + (iX << 2);
+
+    for (iZ = (pData->iDestr - pData->iDestl); iZ > 0; iZ--)
+    {
+      MNG_COPY (pWork, pTemp, 4);
+
+      pWork += 4;
+      pTemp += 4;
+      iX++;
+                                       /* reached end of bkgd-image line ? */
+      if (iX >= pData->iBackimgwidth)
+      {
+        iX    = 0;
+        pTemp = pData->pPrevrow;
+      }
+    }
+  }
+
+  pData->pRGBArow    = pRGBArow;       /* restore original values */
+  pData->iRow        = iRow;
+  pData->iRowsamples = iRowsamples;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BACKIMAGE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_restore_bkgd_backcolor (mng_datap pData)
+{
+  mng_int32   iX;
+  mng_uint32p pWork32 = (mng_uint32p)pData->pRGBArow;
+  mng_uint32  iWrite;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BACKCOLOR, MNG_LC_START);
+#endif
+
+#ifdef MNG_BIGENDIAN_SUPPORTED
+  /* fast way for big endian */
+  iWrite = (((mng_uint8)(pData->iBACKred   >> 8)) << 24) |
+		   (((mng_uint8)(pData->iBACKgreen >> 8)) << 16) |
+		   (((mng_uint8)(pData->iBACKblue  >> 8)) <<  8) |
+           ( 0xFF                                      );
+#elif defined(MNG_LITTLEENDIAN_SUPPORTED)
+  /* fast way for little endian */
+  iWrite = ( 0xFF                                 << 24) |
+           (((mng_uint8)(pData->iBACKblue  >> 8)) << 16) |
+		   (((mng_uint8)(pData->iBACKgreen >> 8)) <<  8) |
+		   (((mng_uint8)(pData->iBACKred   >> 8))      );
+#else
+  /* generic way, works on all platforms */
+  /* put the data in memory in the correct order */
+  {
+    mng_uint8 aBytes[4];
+    aBytes[0] = (mng_uint8)(pData->iBACKred   >> 8);
+    aBytes[1] = (mng_uint8)(pData->iBACKgreen >> 8);
+    aBytes[2] = (mng_uint8)(pData->iBACKblue  >> 8);
+    aBytes[3] = 0xFF;
+    /* load that data into a register */
+    iWrite = *(mng_uint32*) aBytes;
+  }
+#endif
+                                       /* ok; drop the background-color in there */
+  for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--)
+    *pWork32++ = iWrite;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BACKCOLOR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_bKGD
+mng_retcode mng_restore_bkgd_bkgd (mng_datap pData)
+{
+  mng_int32      iX;
+  mng_uint8p     pWork   = pData->pRGBArow;
+  mng_imagep     pImage  = (mng_imagep)pData->pCurrentobj;
+  mng_imagedatap pBuf    = pImage->pImgbuf;
+  mng_uint8      iRed    = 0;
+  mng_uint8      iGreen  = 0;
+  mng_uint8      iBlue   = 0;
+  mng_uint32p    pWork32 = (mng_uint32p)pWork;
+  mng_uint32     iWrite;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BKGD, MNG_LC_START);
+#endif
+
+  switch (pBuf->iColortype)
+  {
+    case 0 : ;                         /* gray types */
+    case 4 : {
+               mng_uint8 iGray;
+
+#ifndef MNG_NO_16BIT_SUPPORT
+               if (pBuf->iBitdepth > 8)
+                 iGray = (mng_uint8)(pBuf->iBKGDgray >> 8);
+               else
+#endif
+               {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+                 /* LBR scaling */
+                 mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1};
+                 iGray = (mng_uint8)(multiplier[pBuf->iBitdepth] * pBuf->iBKGDgray);
+#else
+                 iGray = (mng_uint8)pBuf->iBKGDgray;
+#endif
+               }
+
+               iRed   = iGray;
+               iGreen = iGray;
+               iBlue  = iGray;
+
+               break;
+             }
+
+    case 3 : {                         /* indexed type */
+               iRed   = pBuf->aPLTEentries [pBuf->iBKGDindex].iRed;
+               iGreen = pBuf->aPLTEentries [pBuf->iBKGDindex].iGreen;
+               iBlue  = pBuf->aPLTEentries [pBuf->iBKGDindex].iBlue;
+
+               break;
+             }
+
+    case 2 : ;                         /* rgb types */
+    case 6 : {
+#ifndef MNG_NO_16BIT_SUPPORT
+               if (pBuf->iBitdepth > 8)
+               {
+                 iRed   = (mng_uint8)(pBuf->iBKGDred   >> 8);
+                 iGreen = (mng_uint8)(pBuf->iBKGDgreen >> 8);
+                 iBlue  = (mng_uint8)(pBuf->iBKGDblue  >> 8);
+               }
+               else
+#endif
+               {
+                 iRed   = (mng_uint8)(pBuf->iBKGDred  );
+                 iGreen = (mng_uint8)(pBuf->iBKGDgreen);
+                 iBlue  = (mng_uint8)(pBuf->iBKGDblue );
+               }
+
+               break;
+             }
+  }
+
+#ifdef MNG_BIGENDIAN_SUPPORTED
+  /* fast way for big endian */
+  iWrite = (iRed   << 24) |
+		   (iGreen << 16) |
+		   (iBlue  <<  8);
+#elif defined(MNG_LITTLEENDIAN_SUPPORTED)
+  /* fast way for little endian */
+  iWrite = (iBlue  << 16) |
+		   (iGreen <<  8) |
+		   (iRed        );
+#else
+  /* generic way, works on all platforms */
+  /* put the data in memory in the correct order */
+  {
+    mng_uint8 aBytes[4];
+    aBytes[0] = (mng_uint8)(iRed);
+    aBytes[1] = (mng_uint8)(iGreen);
+    aBytes[2] = (mng_uint8)(iBlue);
+    aBytes[3] = 0x00;
+    /* load that data into a register */
+    iWrite = *(mng_uint32*) aBytes;
+  }
+#endif
+                                       /* ok; drop it in there */
+  for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--)
+    *pWork32++ = iWrite;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_restore_bkgd_bgcolor (mng_datap pData)
+{
+  mng_int32   iX;
+  mng_uint32p pWork32 = (mng_uint32p)pData->pRGBArow;
+  mng_uint32  iWrite;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BGCOLOR, MNG_LC_START);
+#endif
+
+#ifdef MNG_BIGENDIAN_SUPPORTED
+  /* fast way for big endian */
+  iWrite = (((mng_uint8)(pData->iBGred   >> 8)) << 24) |
+		   (((mng_uint8)(pData->iBGgreen >> 8)) << 16) |
+		   (((mng_uint8)(pData->iBGblue  >> 8)) <<  8);
+#elif defined(MNG_LITTLEENDIAN_SUPPORTED)
+  /* fast way for little endian */
+  iWrite = (((mng_uint8)(pData->iBGblue  >> 8)) << 16) |
+		   (((mng_uint8)(pData->iBGgreen >> 8)) <<  8) |
+		   (((mng_uint8)(pData->iBGred   >> 8))      );
+#else
+  /* generic way, works on all platforms */
+  /* put the data in memory in the correct order */
+  {
+    mng_uint8 aBytes[4];
+    aBytes[0] = (mng_uint8)(pData->iBGred   >> 8);
+    aBytes[1] = (mng_uint8)(pData->iBGgreen >> 8);
+    aBytes[2] = (mng_uint8)(pData->iBGblue  >> 8);
+    aBytes[3] = 0x00;
+    /* load that data into a register */
+    iWrite = *(mng_uint32*) aBytes;
+  }
+#endif
+                                       /* ok; drop the background-color in there */
+  for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--)
+    *pWork32++ = iWrite;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BGCOLOR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGB8
+mng_retcode mng_restore_bkgd_rgb8 (mng_datap pData)
+{
+  mng_int32  iX;
+  mng_uint8p pBkgd;
+  mng_uint8p pWork = pData->pRGBArow;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_RGB8, MNG_LC_START);
+#endif
+
+  if (pData->fGetbkgdline)             /* can we access the background ? */
+  {                                    /* point to the right pixel then */
+    pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData,
+                                             pData->iRow + pData->iDestt) +
+            (3 * pData->iDestl);
+
+    for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--)
+    {
+      *pWork     = *pBkgd;             /* ok; copy the pixel */
+      *(pWork+1) = *(pBkgd+1);
+      *(pWork+2) = *(pBkgd+2);
+      *(pWork+3) = 0x00;               /* transparant for alpha-canvasses */
+
+      pWork += 4;
+      pBkgd += 3;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SKIPCANVAS_RGB8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGR8
+mng_retcode mng_restore_bkgd_bgr8 (mng_datap pData)
+{
+  mng_int32  iX;
+  mng_uint8p pBkgd;
+  mng_uint8p pWork = pData->pRGBArow;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BGR8, MNG_LC_START);
+#endif
+
+  if (pData->fGetbkgdline)             /* can we access the background ? */
+  {                                    /* point to the right pixel then */
+    pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData,
+                                             pData->iRow + pData->iDestt) +
+            (3 * pData->iDestl);
+
+    for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--)
+    {
+      *pWork     = *(pBkgd+2);         /* ok; copy the pixel */
+      *(pWork+1) = *(pBkgd+1);
+      *(pWork+2) = *pBkgd;
+      *(pWork+3) = 0x00;               /* transparant for alpha-canvasses */
+
+      pWork += 4;
+      pBkgd += 3;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BGR8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SKIPCANVAS_BGR8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGRX8
+mng_retcode mng_restore_bkgd_bgrx8 (mng_datap pData)
+{
+  mng_int32  iX;
+  mng_uint8p pBkgd;
+  mng_uint8p pWork = pData->pRGBArow;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BGRX8, MNG_LC_START);
+#endif
+
+  if (pData->fGetbkgdline)             /* can we access the background ? */
+  {                                    /* point to the right pixel then */
+    pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData,
+                                             pData->iRow + pData->iDestt) +
+            (3 * pData->iDestl);
+
+    for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--)
+    {
+      *pWork     = *(pBkgd+2);         /* ok; copy the pixel */
+      *(pWork+1) = *(pBkgd+1);
+      *(pWork+2) = *pBkgd;
+      *(pWork+3) = 0x00;               /* transparant for alpha-canvasses */
+
+      pWork += 4;
+      pBkgd += 4;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BGRX8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SKIPCANVAS_BGRX8 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_BGR565
+mng_retcode mng_restore_bkgd_bgr565 (mng_datap pData)
+{
+  mng_int32  iX;
+  mng_uint8p pBkgd;
+  mng_uint8p pWork = pData->pRGBArow;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BGR565, MNG_LC_START);
+#endif
+
+  if (pData->fGetbkgdline)             /* can we access the background ? */
+  {                                    /* point to the right pixel then */
+    pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData,
+                                             pData->iRow + pData->iDestt) +
+            (3 * pData->iDestl);
+
+    for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--)
+    {
+      *pWork     = (mng_uint8)(  *(pBkgd+1) & 0xF8);             /* ok; copy the pixel */
+      *(pWork+1) = (mng_uint8)( (*(pBkgd+1) << 5 )  |  ( ((*pBkgd)&0xE0)>>3 ) );
+      *(pWork+2) = (mng_uint8)(  *(pBkgd) << 3 );
+      *(pWork+3) = 0x00;               /* transparant for alpha-canvasses */
+
+      pWork += 4;
+      pBkgd += 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_BGR565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SKIPCANVAS_BGR565 */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGB565
+mng_retcode mng_restore_bkgd_rgb565 (mng_datap pData)
+{
+  mng_int32  iX;
+  mng_uint8p pBkgd;
+  mng_uint8p pWork = pData->pRGBArow;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_RGB565, MNG_LC_START);
+#endif
+
+  if (pData->fGetbkgdline)             /* can we access the background ? */
+  {                                    /* point to the right pixel then */
+    pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData,
+                                             pData->iRow + pData->iDestt) +
+            (3 * pData->iDestl);
+
+    for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--)
+    {
+      *pWork     = (mng_uint8)(  *(pBkgd)&0xF8);             /* ok; copy the pixel */
+      *(pWork+1) = (mng_uint8)( (*(pBkgd+1) << 5)  |  ( ((*pBkgd)&0xE0)>>3 ) );
+      *(pWork+2) = (mng_uint8)(  *(pBkgd+1) << 3);
+      *(pWork+3) = 0x00;               /* transparant for alpha-canvasses */
+
+      pWork += 4;
+      pBkgd += 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RESTORE_RGB565, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SKIPCANVAS_RBB565 */
+
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row retrieval routines - retrieve processed & uncompressed row-data    * */
+/* * from the current "object"                                              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* TODO: a serious optimization is to retrieve only those pixels that will
+         actually be displayed; this would require changes in
+         the "display_image" routine (in mng_display.c) &
+         all the "retrieve_xxx" routines below &
+         the "display_xxx" routines above !!!!!
+         NOTE that "correct_xxx" routines would not require modification */
+
+mng_retcode mng_retrieve_g8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iG;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_G8, MNG_LC_START);
+#endif
+
+  pRGBArow = pData->pRGBArow;          /* temporary work pointers */
+  pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize);
+
+  if (pBuf->bHasTRNS)                  /* tRNS in buffer ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iG = *pWorkrow;                  /* get the gray-value */
+                                       /* is it transparent ? */
+      if ((mng_uint16)iG == pBuf->iTRNSgray)
+      {
+        *pRGBArow     = 0x00;          /* nuttin to display */
+        *(pRGBArow+1) = 0x00;
+        *(pRGBArow+2) = 0x00;
+        *(pRGBArow+3) = 0x00;
+      }
+      else
+      {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+        mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1};
+        iG = (mng_uint8)(iG * multiplier[pBuf->iBitdepth]);
+#endif
+
+        *pRGBArow     = iG;            /* put in intermediate row */
+        *(pRGBArow+1) = iG;
+        *(pRGBArow+2) = iG;
+        *(pRGBArow+3) = 0xFF;
+      }
+
+      pWorkrow++;                      /* next pixel */
+      pRGBArow += 4;
+    }
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+      mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1};   /* LBR scaling */
+      iG = (mng_uint8)(multiplier[pBuf->iBitdepth] * *pWorkrow);
+#else
+      iG = *pWorkrow;                  /* get the gray-value */
+#endif
+
+      *pRGBArow     = iG;              /* put in intermediate row */
+      *(pRGBArow+1) = iG;
+      *(pRGBArow+2) = iG;
+      *(pRGBArow+3) = 0xFF;
+
+      pWorkrow++;                      /* next pixel */
+      pRGBArow += 4;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_retrieve_g16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint16     iG;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_G16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pRGBArow = pData->pRGBArow;
+  pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize);
+
+  if (pBuf->bHasTRNS)                  /* tRNS in buffer ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iG = mng_get_uint16 (pWorkrow);  /* get the gray-value */
+                                       /* is it transparent ? */
+      if (iG == pBuf->iTRNSgray)
+      {                                /* nuttin to display */
+        mng_put_uint16 (pRGBArow,   0x0000);
+        mng_put_uint16 (pRGBArow+2, 0x0000);
+        mng_put_uint16 (pRGBArow+4, 0x0000);
+        mng_put_uint16 (pRGBArow+6, 0x0000);
+      }
+      else
+      {                                /* put in intermediate row */
+        mng_put_uint16 (pRGBArow,   iG);
+        mng_put_uint16 (pRGBArow+2, iG);
+        mng_put_uint16 (pRGBArow+4, iG);
+        mng_put_uint16 (pRGBArow+6, 0xFFFF);
+      }
+
+      pWorkrow += 2;                   /* next pixel */
+      pRGBArow += 8;
+    }
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iG = mng_get_uint16 (pWorkrow);  /* get the gray-value */
+
+      mng_put_uint16 (pRGBArow,   iG); /* and put in intermediate row */
+      mng_put_uint16 (pRGBArow+2, iG);
+      mng_put_uint16 (pRGBArow+4, iG);
+      mng_put_uint16 (pRGBArow+6, 0xFFFF);
+
+      pWorkrow += 2;                  /* next pixel */
+      pRGBArow += 8;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_retrieve_rgb8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iR, iG, iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_RGB8, MNG_LC_START);
+#endif
+
+  pRGBArow = pData->pRGBArow;          /* temporary work pointers */
+  pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize);
+
+  if (pBuf->bHasTRNS)                  /* tRNS in buffer ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iR = *pWorkrow;                  /* get the rgb-values */
+      iG = *(pWorkrow+1);
+      iB = *(pWorkrow+2);
+                                       /* is it transparent ? */
+      if (((mng_uint16)iR == pBuf->iTRNSred  ) &&
+          ((mng_uint16)iG == pBuf->iTRNSgreen) &&
+          ((mng_uint16)iB == pBuf->iTRNSblue )    )
+      {
+        *pRGBArow     = 0x00;          /* nothing to display */
+        *(pRGBArow+1) = 0x00;
+        *(pRGBArow+2) = 0x00;
+        *(pRGBArow+3) = 0x00;
+      }
+      else
+      {
+        *pRGBArow     = iR;            /* put in intermediate row */
+        *(pRGBArow+1) = iG;
+        *(pRGBArow+2) = iB;
+        *(pRGBArow+3) = 0xFF;
+      }
+
+      pWorkrow += 3;                   /* next pixel */
+      pRGBArow += 4;
+    }
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pRGBArow     = *pWorkrow;       /* just copy the pixel */
+      *(pRGBArow+1) = *(pWorkrow+1);
+      *(pRGBArow+2) = *(pWorkrow+2);
+      *(pRGBArow+3) = 0xFF;
+
+      pWorkrow += 3;                   /* next pixel */
+      pRGBArow += 4;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_retrieve_rgb16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint16     iR, iG, iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_RGB16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pRGBArow = pData->pRGBArow;
+  pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize);
+
+  if (pBuf->bHasTRNS)                  /* tRNS in buffer ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iR = mng_get_uint16 (pWorkrow);  /* get the rgb-values */
+      iG = mng_get_uint16 (pWorkrow+2);
+      iB = mng_get_uint16 (pWorkrow+4);
+                                       /* is it transparent ? */
+      if ((iR == pBuf->iTRNSred  ) &&
+          (iG == pBuf->iTRNSgreen) &&
+          (iB == pBuf->iTRNSblue )    )
+      {                                /* nothing to display */
+        mng_put_uint16 (pRGBArow,   0x0000);
+        mng_put_uint16 (pRGBArow+2, 0x0000);
+        mng_put_uint16 (pRGBArow+4, 0x0000);
+        mng_put_uint16 (pRGBArow+6, 0x0000);
+      }
+      else
+      {                                /* put in intermediate row */
+        mng_put_uint16 (pRGBArow,   iR);
+        mng_put_uint16 (pRGBArow+2, iG);
+        mng_put_uint16 (pRGBArow+4, iB);
+        mng_put_uint16 (pRGBArow+6, 0xFFFF);
+      }
+
+      pWorkrow += 6;                   /* next pixel */
+      pRGBArow += 8;
+    }
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* just copy the pixel */
+      mng_put_uint16 (pRGBArow,   mng_get_uint16 (pWorkrow  ));
+      mng_put_uint16 (pRGBArow+2, mng_get_uint16 (pWorkrow+2));
+      mng_put_uint16 (pRGBArow+4, mng_get_uint16 (pWorkrow+4));
+      mng_put_uint16 (pRGBArow+6, 0xFFFF);
+
+      pWorkrow += 6;                   /* next pixel */
+      pRGBArow += 8;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_retrieve_idx8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_IDX8, MNG_LC_START);
+#endif
+
+  pRGBArow = pData->pRGBArow;          /* temporary work pointers */
+  pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize);
+
+  if (pBuf->bHasTRNS)                  /* tRNS in buffer ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iQ = *pWorkrow;                  /* get the index */
+                                       /* is it valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        *pRGBArow     = pBuf->aPLTEentries [iQ].iRed;
+        *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen;
+        *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue;
+                                       /* transparency for this index ? */
+        if ((mng_uint32)iQ < pBuf->iTRNScount)
+          *(pRGBArow+3) = pBuf->aTRNSentries [iQ];
+        else
+          *(pRGBArow+3) = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pWorkrow++;                      /* next pixel */
+      pRGBArow += 4;
+    }
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iQ = *pWorkrow;                  /* get the index */
+                                       /* is it valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        *pRGBArow     = pBuf->aPLTEentries [iQ].iRed;
+        *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen;
+        *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue;
+        *(pRGBArow+3) = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pWorkrow++;                      /* next pixel */
+      pRGBArow += 4;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_IDX8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_retrieve_ga8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iG;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_GA8, MNG_LC_START);
+#endif
+
+  pRGBArow = pData->pRGBArow;          /* temporary work pointers */
+  pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    iG = *pWorkrow;                    /* get the gray-value */
+    *pRGBArow     = iG;                /* put in intermediate row */
+    *(pRGBArow+1) = iG;
+    *(pRGBArow+2) = iG;
+    *(pRGBArow+3) = *(pWorkrow+1);
+
+    pWorkrow += 2;                     /* next pixel */
+    pRGBArow += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_GA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_retrieve_ga16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint16     iG;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_GA16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pRGBArow = pData->pRGBArow;
+  pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    iG = mng_get_uint16 (pWorkrow);    /* get the gray-value */
+
+    mng_put_uint16 (pRGBArow,   iG);   /* and put in intermediate row */
+    mng_put_uint16 (pRGBArow+2, iG);
+    mng_put_uint16 (pRGBArow+4, iG);
+    mng_put_uint16 (pRGBArow+6, mng_get_uint16 (pWorkrow+2));
+
+    pWorkrow += 4;                     /* next pixel */
+    pRGBArow += 8;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_GA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_retrieve_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_RGBA8, MNG_LC_START);
+#endif
+
+  pRGBArow = pData->pRGBArow;          /* temporary work pointers */
+  pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize);
+                                       /* can't be easier than this ! */
+  MNG_COPY (pRGBArow, pWorkrow, pBuf->iRowsize);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_retrieve_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_RGBA16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pRGBArow = pData->pRGBArow;
+  pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize);
+                                       /* can't be easier than this ! */
+  MNG_COPY (pRGBArow, pWorkrow, pBuf->iRowsize);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RETRIEVE_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row storage routines - store processed & uncompressed row-data         * */
+/* * into the current "object"                                              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_store_g1 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G1, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0x80;
+    }
+
+    if (iB & iM)                       /* is it white ? */
+      *pOutrow = 0x01;                 /* white */
+    else
+      *pOutrow = 0x00;                 /* black */
+
+    pOutrow += pData->iColinc;         /* next pixel */
+    iM >>=  1;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_g2 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G2, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xC0;
+      iS = 6;
+    }
+
+    iQ = (mng_uint8)((iB & iM) >> iS); /* get the gray level */
+    *pOutrow = iQ;                     /* put in object buffer */
+
+    pOutrow += pData->iColinc;         /* next pixel */
+    iM >>=  2;
+    iS -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_g4 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G4, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xF0;
+      iS = 4;
+    }
+
+    iQ = (mng_uint8)((iB & iM) >> iS); /* get the gray level */
+    *pOutrow = iQ;                     /* put in object buffer */
+
+    pOutrow += pData->iColinc;         /* next pixel */
+    iM >>=  4;
+    iS -= 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_g8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = *pWorkrow;              /* put in object buffer */
+
+    pOutrow += pData->iColinc;         /* next pixel */
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_g16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {                                    /* copy into object buffer */
+    mng_put_uint16 (pOutrow, mng_get_uint16 (pWorkrow));
+
+    pOutrow  += (pData->iColinc << 1); /* next pixel */
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_rgb8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_RGB8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow     = *pWorkrow;          /* copy the RGB bytes */
+    *(pOutrow+1) = *(pWorkrow+1);
+    *(pOutrow+2) = *(pWorkrow+2);
+
+    pWorkrow += 3;                     /* next pixel */
+    pOutrow  += (pData->iColinc * 3);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_rgb16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_RGB16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    MNG_COPY (pOutrow, pWorkrow, 6);   /* copy the RGB bytes */
+
+    pWorkrow += 6;                     /* next pixel */
+    pOutrow  += (pData->iColinc * 6);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_store_idx1 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_IDX1, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0x80;
+    }
+
+    if (iB & iM)                       /* store the index */
+      *pOutrow = 0x01;
+    else
+      *pOutrow = 0x00;
+
+    pOutrow += pData->iColinc;         /* next pixel */
+    iM >>=  1;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_IDX1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_idx2 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_IDX2, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xC0;
+      iS = 6;
+    }
+                                       /* store the index */
+    *pOutrow = (mng_uint8)((iB & iM) >> iS);
+
+    pOutrow += pData->iColinc;         /* next pixel */
+    iM >>=  2;
+    iS -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_IDX2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_idx4 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_IDX4, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xF0;
+      iS = 4;
+    }
+                                       /* store the index */
+    *pOutrow = (mng_uint8)((iB & iM) >> iS);
+
+    pOutrow += pData->iColinc;         /* next pixel */
+    iM >>=  4;
+    iS -= 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_IDX4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_idx8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_IDX8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = *pWorkrow;              /* put in object buffer */
+
+    pOutrow += pData->iColinc;         /* next pixel */
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_IDX8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_ga8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_GA8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow     = *pWorkrow;          /* copy the GA bytes */
+    *(pOutrow+1) = *(pWorkrow+1);
+
+    pWorkrow += 2;                     /* next pixel */
+    pOutrow  += (pData->iColinc << 1);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_GA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_ga16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_GA16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    MNG_COPY (pOutrow, pWorkrow, 4);   /* copy the GA bytes */
+
+    pWorkrow += 4;                     /* next pixel */
+    pOutrow  += (pData->iColinc << 2);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_GA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_RGBA8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow     = *pWorkrow;          /* copy the RGBA bytes */
+    *(pOutrow+1) = *(pWorkrow+1);
+    *(pOutrow+2) = *(pWorkrow+2);
+    *(pOutrow+3) = *(pWorkrow+3);
+
+    pWorkrow += 4;                     /* next pixel */
+    pOutrow  += (pData->iColinc << 2);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_RGBA16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    MNG_COPY (pOutrow, pWorkrow, 8);   /* copy the RGBA bytes */
+
+    pWorkrow += 8;                     /* next pixel */
+    pOutrow  += (pData->iColinc << 3);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row storage routines (JPEG) - store processed & uncompressed row-data  * */
+/* * into the current "object"                                              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_g8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pJPEGrow;          /* temporary work pointers */
+  pOutrow  = pBuf->pImgdata + (pData->iJPEGrow * pBuf->iRowsize);
+                                       /* easy as pie ... */
+  MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8, MNG_LC_END);
+#endif
+
+  return mng_next_jpeg_row (pData);    /* we've got one more row of gray-samples */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_rgb8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+#if RGB_PIXELSIZE != 3
+  mng_int32      iX;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pJPEGrow;          /* temporary work pointers */
+  pOutrow  = pBuf->pImgdata + (pData->iJPEGrow * pBuf->iRowsize);
+
+#if RGB_PIXELSIZE == 3
+                                       /* easy as pie ... */
+  MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples * 3);
+#else
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow     = *pWorkrow;          /* copy pixel into object buffer */
+    *(pOutrow+1) = *(pWorkrow+1);
+    *(pOutrow+2) = *(pWorkrow+2);
+
+    pOutrow  += 3;                     /* next pixel */
+    pWorkrow += RGB_PIXELSIZE;
+  }
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8, MNG_LC_END);
+#endif
+
+  return mng_next_jpeg_row (pData);    /* we've got one more row of rgb-samples */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_ga8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_GA8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pJPEGrow;          /* temporary work pointers */
+  pOutrow  = pBuf->pImgdata + (pData->iJPEGrow * pBuf->iRowsize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = *pWorkrow;              /* copy into object buffer */
+
+    pOutrow += 2;                      /* next pixel */
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_GA8, MNG_LC_END);
+#endif
+
+  return mng_next_jpeg_row (pData);    /* we've got one more row of gray-samples */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGBA8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pJPEGrow;          /* temporary work pointers */
+  pOutrow  = pBuf->pImgdata + (pData->iJPEGrow * pBuf->iRowsize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow     = *pWorkrow;          /* copy pixel into object buffer */
+    *(pOutrow+1) = *(pWorkrow+1);
+    *(pOutrow+2) = *(pWorkrow+2);
+
+    pOutrow  += 4;                     /* next pixel */
+    pWorkrow += RGB_PIXELSIZE;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGBA8, MNG_LC_END);
+#endif
+
+  return mng_next_jpeg_row (pData);    /* we've got one more row of rgb-samples */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_g8_alpha (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_ALPHA, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pJPEGrow2;
+  pOutrow  = pBuf->pImgdata + (pData->iJPEGalpharow * pBuf->iRowsize) + 1;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = *pWorkrow;              /* put in object buffer */
+
+    pOutrow += 2;                      /* next pixel */
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_ALPHA, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_rgb8_alpha (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_ALPHA, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pJPEGrow2;
+  pOutrow  = pBuf->pImgdata + (pData->iJPEGalpharow * pBuf->iRowsize) + 3;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = *pWorkrow;              /* put in object buffer */
+
+    pOutrow += 4;                      /* next pixel */
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_ALPHA, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_store_jpeg_g8_a1 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A1, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 1;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0x80;
+    }
+
+    if (iB & iM)                       /* is it opaque ? */
+      *pOutrow = 0xFF;                 /* opaque */
+    else
+      *pOutrow = 0x00;                 /* transparent */
+
+    pOutrow += 2;                      /* next pixel */
+    iM >>=  1;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A1, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_g8_a2 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A2, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 1;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xC0;
+      iS = 6;
+    }
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH
+    {
+      const mng_uint8  alpha_level[4] = { 0x00, 0x55, 0xAA, 0xFF};
+        *pOutrow = alpha_level[((iB & iM) >> iS)] ;
+    }
+#else
+    switch ((iB & iM) >> iS)           /* determine the alpha level */
+    {
+      case 0x03 : { *pOutrow = 0xFF; break; }
+      case 0x02 : { *pOutrow = 0xAA; break; }
+      case 0x01 : { *pOutrow = 0x55; break; }
+      default   : { *pOutrow = 0x00; }
+    }
+#endif
+
+    pOutrow += 2;                      /* next pixel */
+    iM >>=  2;
+    iS -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A2, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_g8_a4 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A4, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 1;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xF0;
+      iS = 4;
+    }
+                                       /* get the alpha level */
+    iQ = (mng_uint8)((iB & iM) >> iS);
+    iQ = (mng_uint8)(iQ + (iQ << 4));  /* expand to 8-bit by replication */
+
+    *pOutrow = iQ;                     /* put in object buffer */
+
+    pOutrow += 2;                      /* next pixel */
+    iM >>=  4;
+    iS -= 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A4, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_g8_a8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 1;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = *pWorkrow;              /* put in object buffer */
+
+    pOutrow += 2;                      /* next pixel */
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A8, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_jpeg_g8_a16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 1;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = *pWorkrow;              /* only high-order byte! */
+
+    pOutrow  += 2;                     /* next pixel */
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A16, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_store_jpeg_rgb8_a1 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A1, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 3;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0x80;
+    }
+
+    if (iB & iM)                       /* is it opaque ? */
+      *pOutrow = 0xFF;                 /* opaque */
+    else
+      *pOutrow = 0x00;                 /* transparent */
+
+    pOutrow += 4;                      /* next pixel */
+    iM >>=  1;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A1, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_rgb8_a2 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A2, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 3;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xC0;
+      iS = 6;
+    }
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH
+    {
+      const mng_uint8  alpha_level[4] = { 0x00, 0x55, 0xAA, 0xFF};
+      *pOutrow = alpha_level[((iB & iM) >> iS)] ;
+    }
+#else
+    switch ((iB & iM) >> iS)           /* determine the alpha level */
+    {
+      case 0x03 : { *pOutrow = 0xFF; break; }
+      case 0x02 : { *pOutrow = 0xAA; break; }
+      case 0x01 : { *pOutrow = 0x55; break; }
+      default   : { *pOutrow = 0x00; }
+    }
+#endif
+
+    pOutrow += 4;                      /* next pixel */
+    iM >>=  2;
+    iS -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A2, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_rgb8_a4 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A4, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 3;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xF0;
+      iS = 4;
+    }
+                                       /* get the alpha level */
+    iQ = (mng_uint8)((iB & iM) >> iS);
+    iQ = (mng_uint8)(iQ + (iQ << 4));  /* expand to 8-bit by replication */
+
+    *pOutrow = iQ;                     /* put in object buffer */
+
+    pOutrow += 4;                      /* next pixel */
+    iM >>=  4;
+    iS -= 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A4, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_store_jpeg_rgb8_a8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 3;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = *pWorkrow;              /* put in buffer */
+
+    pOutrow += 4;                      /* next pixel */
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A8, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_jpeg_rgb8_a16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 3;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = *pWorkrow;              /* only high-order byte */
+
+    pOutrow  += 4;                     /* next pixel */
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A16, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_JPEG12
+mng_retcode mng_store_jpeg_g12_a1 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A1, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 2;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0x80;
+    }
+
+    if (iB & iM)                       /* opaque ? */
+      mng_put_uint16 (pOutrow, 0xFFFF);/* opaque */
+    else
+      mng_put_uint16 (pOutrow, 0x0000);/* transparent */
+
+    pOutrow += 4;                      /* next pixel */
+    iM >>=  1;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A1, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+#endif /* MNG_SUPPORT_JPEG12 */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_JPEG12
+mng_retcode mng_store_jpeg_g12_a2 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A2, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 2;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xC0;
+      iS = 6;
+    }
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH
+    {
+      const mng_uint16  gray_level[4] = { 0x0000, 0x5555, 0xAAAA, 0xFFFF};
+      mng_put_uint16 (pOutrow, gray_level[((iB & iM) >> iS)]) ;
+    }
+#else
+    switch ((iB & iM) >> iS)           /* determine the gray level */
+    {
+      case 0x03 : { mng_put_uint16 (pOutrow, 0xFFFF); break; }
+      case 0x02 : { mng_put_uint16 (pOutrow, 0xAAAA); break; }
+      case 0x01 : { mng_put_uint16 (pOutrow, 0x5555); break; }
+      default   : { mng_put_uint16 (pOutrow, 0x0000); }
+    }
+#endif
+
+    pOutrow += 4;                      /* next pixel */
+    iM >>=  2;
+    iS -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A2, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+#endif /* MNG_SUPPORT_JPEG12 */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_JPEG12
+mng_retcode mng_store_jpeg_g12_a4 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint16     iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A4, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 2;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    if (!iM)                           /* mask underflow ? */
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+      pWorkrow++;
+      iM = 0xF0;
+      iS = 4;
+    }
+                                       /* get the gray level */
+    iQ = (mng_uint16)((iB & iM) >> iS);
+    iQ = (mng_uint16)(iQ + (iQ << 4)); /* expand to 16-bit by replication */
+    iQ = (mng_uint16)(iQ + (iQ << 8));
+                                       /* put in object buffer */
+    mng_put_uint16 (pOutrow, iQ);
+
+    pOutrow += 4;                      /* next pixel */
+    iM >>=  4;
+    iS -= 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A4, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+#endif /* MNG_SUPPORT_JPEG12 */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_JPEG12
+mng_retcode mng_store_jpeg_g12_a8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint16     iW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 2;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    iW = (mng_uint16)(*pWorkrow);      /* get input byte */
+    iW = (mng_uint16)(iW + (iW << 8)); /* expand to 16-bit by replication */
+
+    mng_put_uint16 (pOutrow, iW);      /* put in object buffer */
+
+    pOutrow += 4;                      /* next pixel */
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A8, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+#endif /* MNG_SUPPORT_JPEG12 */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_JPEG12
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_jpeg_g12_a16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 2;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {                                    /* copy it */
+    mng_put_uint16 (pOutrow, mng_get_uint16 (pWorkrow));
+
+    pOutrow  += 4;                     /* next pixel */
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A16, MNG_LC_END);
+#endif
+                                       /* we've got one more row of alpha-samples */
+  return mng_next_jpeg_alpharow (pData);
+}
+#endif
+#endif /* MNG_SUPPORT_JPEG12 */
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_JNG */
+
+#ifndef MNG_NO_DELTA_PNG
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image row routines - apply the processed & uncompressed row-data * */
+/* * onto the target "object"                                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_delta_g1 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G1, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0x80;
+      }
+
+      if (iB & iM)                     /* is it white ? */
+        *pOutrow = 0xFF;               /* white */
+      else
+        *pOutrow = 0x00;               /* black */
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  1;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0x80;
+      }
+
+      if (iB & iM)                     /* invert if it is white ? */
+        *pOutrow = (mng_uint8)(*pOutrow ^ 0xFF);
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  1;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G1, MNG_LC_END);
+#endif
+
+  return mng_store_g1 (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_g2 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH
+  const mng_uint8  level[4] = { 0x00, 0x55, 0xAA, 0xFF};
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G2, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xC0;
+        iS = 6;
+      }
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH
+    *pOutrow = level[((iB & iM) >> iS)] ;
+#else
+    switch ((iB & iM) >> iS)           /* determine the alpha level */
+    {
+      case 0x03 : { *pOutrow = 0xFF; break; }
+      case 0x02 : { *pOutrow = 0xAA; break; }
+      case 0x01 : { *pOutrow = 0x55; break; }
+      default   : { *pOutrow = 0x00; }
+    }
+#endif
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  2;
+      iS -= 2;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xC0;
+        iS = 6;
+      }
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH
+      *pOutrow = level[((*pOutrow >> 6) + ((iB & iM) >> iS)) & 0x03] ;
+#else
+      switch (((*pOutrow >> 6) + ((iB & iM) >> iS)) & 0x03)
+      {
+        case 0x03 : { *pOutrow = 0xFF; break; }
+        case 0x02 : { *pOutrow = 0xAA; break; }
+        case 0x01 : { *pOutrow = 0x55; break; }
+        default   : { *pOutrow = 0x00; }
+      }
+#endif
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  2;
+      iS -= 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G2, MNG_LC_END);
+#endif
+
+  return mng_store_g2 (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_g4 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G4, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xF0;
+        iS = 4;
+      }
+                                       /* get the gray level */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+                                       /* expand to 8-bit by replication */
+      iQ = (mng_uint8)(iQ + (iQ << 4));
+
+      *pOutrow = iQ;                   /* put in object buffer */
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  4;
+      iS -= 4;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xF0;
+        iS = 4;
+      }
+                                       /* get the gray level */
+      iQ = (mng_uint8)(((*pOutrow >> 4) + ((iB & iM) >> iS)) & 0x0F);
+                                       /* expand to 8-bit by replication */
+      iQ = (mng_uint8)(iQ + (iQ << 4));
+
+      *pOutrow = iQ;                   /* put in object buffer */
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  4;
+      iS -= 4;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G4, MNG_LC_END);
+#endif
+
+  return mng_store_g4 (pData);
+}
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_g8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = *pWorkrow;            /* put in object buffer */
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      pWorkrow++;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* add to object buffer */
+      *pOutrow = (mng_uint8)(*pOutrow + *pWorkrow);
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G8, MNG_LC_END);
+#endif
+
+  return mng_store_g8 (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_g16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow     = *pWorkrow;        /* put in object buffer */
+      *(pOutrow+1) = *(pWorkrow+1);
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 1);
+      pWorkrow += 2;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* add to object buffer */
+      mng_put_uint16 (pOutrow, (mng_uint16)(mng_get_uint16 (pOutrow ) +
+                                            mng_get_uint16 (pWorkrow)   ));
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 1);
+      pWorkrow += 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G16, MNG_LC_END);
+#endif
+
+  return mng_store_g16 (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_rgb8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGB8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow     = *pWorkrow;        /* put in object buffer */
+      *(pOutrow+1) = *(pWorkrow+1);
+      *(pOutrow+2) = *(pWorkrow+2);
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc * 3);
+      pWorkrow += 3;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* add to object buffer */
+      *pOutrow     = (mng_uint8)(*pOutrow     + *pWorkrow    );
+      *(pOutrow+1) = (mng_uint8)(*(pOutrow+1) + *(pWorkrow+1));
+      *(pOutrow+2) = (mng_uint8)(*(pOutrow+2) + *(pWorkrow+2));
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc * 3);
+      pWorkrow += 3;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGB8, MNG_LC_END);
+#endif
+
+  return mng_store_rgb8 (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_rgb16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGB16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow     = *pWorkrow;        /* put in object buffer */
+      *(pOutrow+1) = *(pWorkrow+1);
+      *(pOutrow+2) = *(pWorkrow+2);
+      *(pOutrow+3) = *(pWorkrow+3);
+      *(pOutrow+4) = *(pWorkrow+4);
+      *(pOutrow+5) = *(pWorkrow+5);
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc * 6);
+      pWorkrow += 6;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* add to object buffer */
+      mng_put_uint16 (pOutrow,   (mng_uint16)(mng_get_uint16 (pOutrow   ) +
+                                              mng_get_uint16 (pWorkrow  )   ));
+      mng_put_uint16 (pOutrow+2, (mng_uint16)(mng_get_uint16 (pOutrow+2 ) +
+                                              mng_get_uint16 (pWorkrow+2)   ));
+      mng_put_uint16 (pOutrow+4, (mng_uint16)(mng_get_uint16 (pOutrow+4 ) +
+                                              mng_get_uint16 (pWorkrow+4)   ));
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc * 6);
+      pWorkrow += 6;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGB16, MNG_LC_END);
+#endif
+
+  return mng_store_rgb16 (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_delta_idx1 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_IDX1, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0x80;
+      }
+
+      if (iB & iM)                     /* put the right index value */
+        *pOutrow = 1;
+      else
+        *pOutrow = 0;
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  1;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0x80;
+      }
+
+      if (iB & iM)                     /* invert if it is non-zero index */
+        *pOutrow = (mng_uint8)(*pOutrow ^ 0x01);
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  1;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_IDX1, MNG_LC_END);
+#endif
+
+  return mng_store_idx1 (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_idx2 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_IDX2, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xC0;
+        iS = 6;
+      }
+                                       /* put the index */
+      *pOutrow = (mng_uint8)((iB & iM) >> iS);
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  2;
+      iS -= 2;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xC0;
+        iS = 6;
+      }
+                                       /* calculate the index */
+      *pOutrow = (mng_uint8)((*pOutrow + ((iB & iM) >> iS)) & 0x03);
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  2;
+      iS -= 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_IDX2, MNG_LC_END);
+#endif
+
+  return mng_store_idx2 (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_idx4 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_IDX4, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xF0;
+        iS = 4;
+      }
+                                       /* put the index */
+      *pOutrow = (mng_uint8)((iB & iM) >> iS);
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  4;
+      iS -= 4;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xF0;
+        iS = 4;
+      }
+                                       /* calculate the index */
+      *pOutrow = (mng_uint8)((*pOutrow + ((iB & iM) >> iS)) & 0x0F);
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      iM >>=  4;
+      iS -= 4;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_IDX4, MNG_LC_END);
+#endif
+
+  return mng_store_idx4 (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_idx8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_IDX8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = *pWorkrow;            /* put in object buffer */
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      pWorkrow++;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* add to object buffer */
+      *pOutrow = (mng_uint8)(*pOutrow + *pWorkrow);
+
+      pOutrow += pData->iColinc;       /* next pixel */
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_IDX8, MNG_LC_END);
+#endif
+
+  return mng_store_idx8 (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_ga8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow     = *pWorkrow;        /* put in object buffer */
+      *(pOutrow+1) = *(pWorkrow+1);
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 1);
+      pWorkrow += 2;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* add to object buffer */
+      *pOutrow     = (mng_uint8)(*pOutrow     + *pWorkrow    );
+      *(pOutrow+1) = (mng_uint8)(*(pOutrow+1) + *(pWorkrow+1));
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 1);
+      pWorkrow += 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA8, MNG_LC_END);
+#endif
+
+  return mng_store_ga8 (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_ga16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow     = *pWorkrow;        /* put in object buffer */
+      *(pOutrow+1) = *(pWorkrow+1);
+      *(pOutrow+2) = *(pWorkrow+2);
+      *(pOutrow+3) = *(pWorkrow+3);
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 2);
+      pWorkrow += 4;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* add to object buffer */
+      mng_put_uint16 (pOutrow,   (mng_uint16)(mng_get_uint16 (pOutrow   ) +
+                                              mng_get_uint16 (pWorkrow  )   ));
+      mng_put_uint16 (pOutrow+2, (mng_uint16)(mng_get_uint16 (pOutrow+2 ) +
+                                              mng_get_uint16 (pWorkrow+2)   ));
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 2);
+      pWorkrow += 4;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA16, MNG_LC_END);
+#endif
+
+  return mng_store_ga16 (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow     = *pWorkrow;        /* put in object buffer */
+      *(pOutrow+1) = *(pWorkrow+1);
+      *(pOutrow+2) = *(pWorkrow+2);
+      *(pOutrow+3) = *(pWorkrow+3);
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 2);
+      pWorkrow += 4;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* add to object buffer */
+      *pOutrow     = (mng_uint8)(*pOutrow     + *pWorkrow    );
+      *(pOutrow+1) = (mng_uint8)(*(pOutrow+1) + *(pWorkrow+1));
+      *(pOutrow+2) = (mng_uint8)(*(pOutrow+2) + *(pWorkrow+2));
+      *(pOutrow+3) = (mng_uint8)(*(pOutrow+3) + *(pWorkrow+3));
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 2);
+      pWorkrow += 4;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA8, MNG_LC_END);
+#endif
+
+  return mng_store_rgba8 (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pOutrow  = pBuf->pImgdata + (pData->iRow         * pBuf->iRowsize   ) +
+                              (pData->iDeltaBlocky * pBuf->iRowsize   ) +
+                              (pData->iCol         * pBuf->iSamplesize) +
+                              (pData->iDeltaBlockx * pBuf->iSamplesize);
+                                       /* pixel replace ? */
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      MNG_COPY (pOutrow, pWorkrow, 8); /* put in object buffer */
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 3);
+      pWorkrow += 8;
+    }
+  }
+  else
+  {                                    /* pixel add ! */
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* add to object buffer */
+      mng_put_uint16 (pOutrow,   (mng_uint16)(mng_get_uint16 (pOutrow   ) +
+                                              mng_get_uint16 (pWorkrow  )   ));
+      mng_put_uint16 (pOutrow+2, (mng_uint16)(mng_get_uint16 (pOutrow+2 ) +
+                                              mng_get_uint16 (pWorkrow+2)   ));
+      mng_put_uint16 (pOutrow+4, (mng_uint16)(mng_get_uint16 (pOutrow+4 ) +
+                                              mng_get_uint16 (pWorkrow+4)   ));
+      mng_put_uint16 (pOutrow+6, (mng_uint16)(mng_get_uint16 (pOutrow+6 ) +
+                                              mng_get_uint16 (pWorkrow+6)   ));
+                                       /* next pixel */
+      pOutrow  += (pData->iColinc << 3);
+      pWorkrow += 8;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA16, MNG_LC_END);
+#endif
+
+  return mng_store_rgba16 (pData);
+}
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image row routines - apply the source row onto the target        * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_delta_g1_g1 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G1_G1, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples);
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0x01);
+
+      pOutrow++;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G1_G1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_g2_g2 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G2_G2, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples);
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0x03);
+
+      pOutrow++;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G2_G2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_g4_g4 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G4_G4, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples);
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0x0F);
+
+      pOutrow++;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G4_G4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_g8_g8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G8_G8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples);
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0xFF);
+
+      pOutrow++;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G8_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_g16_g16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G16_G16, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, (pData->iRowsamples << 1));
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow, (mng_uint16)((mng_get_uint16 (pOutrow) +
+                                             mng_get_uint16 (pWorkrow)) & 0xFFFF));
+
+      pOutrow  += 2;
+      pWorkrow += 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_G16_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_NO_DELTA_PNG */
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_rgb8_rgb8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGB8_RGB8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples * 3);
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples*3; iX > 0; iX--)
+#else
+    for (iX = 0; iX < (pData->iRowsamples * 3); iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0xFF);
+
+      pOutrow++;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGB8_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_rgb16_rgb16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGB16_RGB16, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, (pData->iRowsamples * 6));
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow,   (mng_uint16)((mng_get_uint16 (pOutrow  ) +
+                                               mng_get_uint16 (pWorkrow  )) & 0xFFFF));
+      mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) +
+                                               mng_get_uint16 (pWorkrow+2)) & 0xFFFF));
+      mng_put_uint16 (pOutrow+4, (mng_uint16)((mng_get_uint16 (pOutrow+4) +
+                                               mng_get_uint16 (pWorkrow+4)) & 0xFFFF));
+
+      pOutrow  += 6;
+      pWorkrow += 6;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGB16_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_delta_ga8_ga8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA8_GA8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples << 1);
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = (pData->iRowsamples<<1); iX > 0; iX--)
+#else
+    for (iX = 0; iX < (pData->iRowsamples << 1); iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0xFF);
+
+      pOutrow++;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA8_GA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_ga8_g8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA8_G8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = *pWorkrow;
+
+      pOutrow += 2;
+      pWorkrow++;
+    }
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0xFF);
+
+      pOutrow += 2;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA8_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_ga8_a8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA8_A8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 1;
+
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = *pWorkrow;
+
+      pOutrow += 2;
+      pWorkrow++;
+    }
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0xFF);
+
+      pOutrow += 2;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA8_A8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_ga16_ga16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA16_GA16, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, (pData->iRowsamples << 2));
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow,   (mng_uint16)((mng_get_uint16 (pOutrow  ) +
+                                               mng_get_uint16 (pWorkrow  )) & 0xFFFF));
+      mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) +
+                                               mng_get_uint16 (pWorkrow+2)) & 0xFFFF));
+
+      pOutrow  += 4;
+      pWorkrow += 4;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA16_GA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_ga16_g16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA16_G16, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow, mng_get_uint16 (pWorkrow));
+
+      pOutrow  += 4;
+      pWorkrow += 2;
+    }
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow, (mng_uint16)((mng_get_uint16 (pOutrow) +
+                                             mng_get_uint16 (pWorkrow)) & 0xFFFF));
+
+      pOutrow  += 4;
+      pWorkrow += 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA16_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_ga16_a16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA16_A16, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow+2, mng_get_uint16 (pWorkrow));
+
+      pOutrow  += 4;
+      pWorkrow += 2;
+    }
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) +
+                                               mng_get_uint16 (pWorkrow)) & 0xFFFF));
+
+      pOutrow  += 4;
+      pWorkrow += 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_GA16_A16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_NO_DELTA_PNG */
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_rgba8_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_RGBA8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples << 2);
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = (pData->iRowsamples << 2); iX > 0; iX--)
+#else
+    for (iX = 0; iX < (pData->iRowsamples << 2); iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0xFF);
+
+      pOutrow++;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_retcode mng_delta_rgba8_rgb8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_RGB8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow     = *pWorkrow;
+      *(pOutrow+1) = *(pWorkrow+1);
+      *(pOutrow+2) = *(pWorkrow+2);
+
+      pOutrow  += 4;
+      pWorkrow += 3;
+    }
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow     = (mng_uint8)(((mng_uint16)*pOutrow     +
+                                  (mng_uint16)*pWorkrow    ) & 0xFF);
+      *(pOutrow+1) = (mng_uint8)(((mng_uint16)*(pOutrow+1) +
+                                  (mng_uint16)*(pWorkrow+1)) & 0xFF);
+      *(pOutrow+2) = (mng_uint8)(((mng_uint16)*(pOutrow+2) +
+                                  (mng_uint16)*(pWorkrow+2)) & 0xFF);
+
+      pOutrow  += 4;
+      pWorkrow += 3;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_delta_rgba8_a8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_A8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize) + 3;
+
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = *pWorkrow;
+
+      pOutrow += 4;
+      pWorkrow++;
+    }
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow +
+                              (mng_uint16)*pWorkrow) & 0xFF);
+
+      pOutrow += 4;
+      pWorkrow++;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_A8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_DELTA_PNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_rgba16_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_RGBA16, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE          ) ||
+      (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE)    )
+  {
+    MNG_COPY (pOutrow, pWorkrow, (pData->iRowsamples << 3));
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow,   (mng_uint16)((mng_get_uint16 (pOutrow  ) +
+                                               mng_get_uint16 (pWorkrow  )) & 0xFFFF));
+      mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) +
+                                               mng_get_uint16 (pWorkrow+2)) & 0xFFFF));
+      mng_put_uint16 (pOutrow+4, (mng_uint16)((mng_get_uint16 (pOutrow+4) +
+                                               mng_get_uint16 (pWorkrow+4)) & 0xFFFF));
+      mng_put_uint16 (pOutrow+6, (mng_uint16)((mng_get_uint16 (pOutrow+6) +
+                                               mng_get_uint16 (pWorkrow+6)) & 0xFFFF));
+
+      pOutrow  += 8;
+      pWorkrow += 8;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_rgba16_rgb16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_RGB16, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow,   mng_get_uint16 (pWorkrow  ));
+      mng_put_uint16 (pOutrow+2, mng_get_uint16 (pWorkrow+2));
+      mng_put_uint16 (pOutrow+4, mng_get_uint16 (pWorkrow+4));
+
+      pOutrow  += 8;
+      pWorkrow += 6;
+    }
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow,   (mng_uint16)((mng_get_uint16 (pOutrow  ) +
+                                               mng_get_uint16 (pWorkrow  )) & 0xFFFF));
+      mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) +
+                                               mng_get_uint16 (pWorkrow+2)) & 0xFFFF));
+      mng_put_uint16 (pOutrow+4, (mng_uint16)((mng_get_uint16 (pOutrow+4) +
+                                               mng_get_uint16 (pWorkrow+4)) & 0xFFFF));
+
+      pOutrow  += 8;
+      pWorkrow += 6;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_rgba16_a16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_A16, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow+6, mng_get_uint16 (pWorkrow));
+
+      pOutrow  += 8;
+      pWorkrow += 2;
+    }
+  }
+  else
+  if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD)
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      mng_put_uint16 (pOutrow+6, (mng_uint16)((mng_get_uint16 (pOutrow+6) +
+                                               mng_get_uint16 (pWorkrow)) & 0xFFFF));
+
+      pOutrow  += 8;
+      pWorkrow += 2;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_A16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_NO_DELTA_PNG */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image row routines - scale the delta to bitdepth of target       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_scale_g1_g2 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G1_G2, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow << 1);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G1_G2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g1_g4 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G1_G4, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow << 3);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G1_G4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g1_g8 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G1_G8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow << 7);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G1_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_g1_g16 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G1_G16, MNG_LC_START);
+#endif
+
+  pWorkrow = pWorkrow + (pData->iRowsamples - 1);
+  pOutrow  = pOutrow  + ((pData->iRowsamples - 1) << 1);
+/*  pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + pData->iRowsamples - 1); */
+/*  pOutrow  = (mng_uint8p)((mng_uint32)pOutrow  + ((pData->iRowsamples - 1) << 1)); */
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pOutrow+1) = 0;
+    *pOutrow     = (mng_uint8)(*pWorkrow << 7);
+
+    pWorkrow--;
+    pOutrow -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G1_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g2_g4 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G2_G4, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow << 2);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G2_G4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g2_g8 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G2_G8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow << 6);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G2_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_g2_g16 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G2_G16, MNG_LC_START);
+#endif
+
+  pWorkrow = pWorkrow + (pData->iRowsamples - 1);
+  pOutrow  = pOutrow  + ((pData->iRowsamples - 1) << 1);
+/*  pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + pData->iRowsamples - 1); */
+/*  pOutrow  = (mng_uint8p)((mng_uint32)pOutrow  + ((pData->iRowsamples - 1) << 1)); */
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pOutrow+1) = 0;
+    *pOutrow     = (mng_uint8)(*pWorkrow << 6);
+
+    pWorkrow--;
+    pOutrow -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G2_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g4_g8 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G4_G8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow << 4);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G4_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_g4_g16 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G4_G16, MNG_LC_START);
+#endif
+
+  pWorkrow = pWorkrow + (pData->iRowsamples - 1);
+  pOutrow  = pOutrow  + ((pData->iRowsamples - 1) << 1);
+/*  pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + pData->iRowsamples - 1); */
+/*  pOutrow  = (mng_uint8p)((mng_uint32)pOutrow  + ((pData->iRowsamples - 1) << 1)); */
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pOutrow+1) = 0;
+    *pOutrow     = (mng_uint8)(*pWorkrow << 4);
+
+    pWorkrow--;
+    pOutrow -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G4_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_g8_g16 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G8_G16, MNG_LC_START);
+#endif
+
+  pWorkrow = pWorkrow + (pData->iRowsamples - 1);
+  pOutrow  = pOutrow  + ((pData->iRowsamples - 1) << 1);
+/*  pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + pData->iRowsamples - 1); */
+/*  pOutrow  = (mng_uint8p)((mng_uint32)pOutrow  + ((pData->iRowsamples - 1) << 1)); */
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pOutrow+1) = 0;
+    *pOutrow     = *pWorkrow;
+
+    pWorkrow--;
+    pOutrow -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G8_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_ga8_ga16 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_GA8_GA16, MNG_LC_START);
+#endif
+
+  pWorkrow = pWorkrow + ((pData->iRowsamples - 1) << 1);
+  pOutrow  = pOutrow  + ((pData->iRowsamples - 1) << 2);
+/*  pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + ((pData->iRowsamples - 1) << 1)); */
+/*  pOutrow  = (mng_uint8p)((mng_uint32)pOutrow  + ((pData->iRowsamples - 1) << 2)); */
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pOutrow+3) = 0;
+    *(pOutrow+2) = *(pWorkrow+1);
+    *(pOutrow+1) = 0;
+    *pOutrow     = *pWorkrow;
+
+    pWorkrow -= 2;
+    pOutrow  -= 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_GA8_GA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_rgb8_rgb16 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_RGB8_RGB16, MNG_LC_START);
+#endif
+
+  pWorkrow = pWorkrow + (3 * (pData->iRowsamples - 1));
+  pOutrow  = pOutrow  + (6 * (pData->iRowsamples - 1));
+/*  pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + 3 * (pData->iRowsamples - 1)); */
+/*  pOutrow  = (mng_uint8p)((mng_uint32)pOutrow  + 6 * (pData->iRowsamples - 1)); */
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pOutrow+5) = 0;
+    *(pOutrow+4) = *(pWorkrow+2);
+    *(pOutrow+3) = 0;
+    *(pOutrow+2) = *(pWorkrow+1);
+    *(pOutrow+1) = 0;
+    *pOutrow     = *pWorkrow;
+
+    pWorkrow -= 3;
+    pOutrow  -= 6;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_RGB8_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_rgba8_rgba16 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_RGBA8_RGBA16, MNG_LC_START);
+#endif
+
+  pWorkrow = pWorkrow + ((pData->iRowsamples - 1) << 2);
+  pOutrow  = pOutrow  + ((pData->iRowsamples - 1) << 3);
+/*  pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + ((pData->iRowsamples - 1) << 2)); */
+/*  pOutrow  = (mng_uint8p)((mng_uint32)pOutrow  + ((pData->iRowsamples - 1) << 3)); */
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *(pOutrow+7) = 0;
+    *(pOutrow+6) = *(pWorkrow+3);
+    *(pOutrow+5) = 0;
+    *(pOutrow+4) = *(pWorkrow+2);
+    *(pOutrow+3) = 0;
+    *(pOutrow+2) = *(pWorkrow+1);
+    *(pOutrow+1) = 0;
+    *pOutrow     = *pWorkrow;
+
+    pWorkrow -= 4;
+    pOutrow  -= 8;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_RGBA8_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_scale_g2_g1 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G2_G1, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow >> 1);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G2_G1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g4_g1 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G4_G1, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow >> 3);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G4_G1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g8_g1 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G8_G1, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow >> 7);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G8_G1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_g16_g1 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G16_G1, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 15);
+    pOutrow++;
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G16_G1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g4_g2 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G4_G2, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow >> 2);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G4_G2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g8_g2 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G8_G2, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow >> 6);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G8_G2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_g16_g2 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G16_G2, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 14);
+    pOutrow++;
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G16_G2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g8_g4 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G8_G4, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pWorkrow = (mng_uint8)(*pWorkrow >> 4);
+    pWorkrow++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G8_G4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_scale_g16_g4 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G16_G4, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 12);
+    pOutrow++;
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G16_G4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_g16_g8 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G16_G8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_G16_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_ga16_ga8 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_GA16_GA8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_GA16_GA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_rgb16_rgb8 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_RGB16_RGB8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_RGB16_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_scale_rgba16_rgba8 (mng_datap pData)
+{
+  mng_uint8p pWorkrow = pData->pRGBArow;
+  mng_uint8p pOutrow  = pData->pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_RGBA16_RGBA8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+    *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8);
+    pOutrow++;
+    pWorkrow += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_SCALE_RGBA16_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image bit routines - promote bit_depth                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_uint8 mng_promote_replicate_1_2 (mng_uint8 iB)
+{
+  return (mng_uint8)((iB << 1) | iB);
+}
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_replicate_1_4 (mng_uint8 iB)
+{
+  iB = (mng_uint8)((iB << 1) + iB);
+  return (mng_uint8)((iB << 2) + iB);
+}
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_replicate_1_8 (mng_uint8 iB)
+{
+  iB = (mng_uint8)((iB << 1) + iB);
+  iB = (mng_uint8)((iB << 2) + iB);
+  return (mng_uint8)((iB << 4) + iB);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16 mng_promote_replicate_1_16 (mng_uint8 iB)
+{
+  iB = (mng_uint8)((iB << 1) + iB);
+  iB = (mng_uint8)((iB << 2) + iB);
+  iB = (mng_uint8)((iB << 4) + iB);
+  return (mng_uint16)(((mng_uint16)iB << 8) + (mng_uint16)iB);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_replicate_2_4 (mng_uint8 iB)
+{
+  return (mng_uint8)((iB << 2) + iB);
+}
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_replicate_2_8 (mng_uint8 iB)
+{
+  iB = (mng_uint8)((iB << 2) + iB);
+  return (mng_uint8)((iB << 4) + iB);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16 mng_promote_replicate_2_16 (mng_uint8 iB)
+{
+  iB = (mng_uint8)((iB << 2) + iB);
+  iB = (mng_uint8)((iB << 4) + iB);
+  return (mng_uint16)(((mng_uint16)iB << 8) + (mng_uint16)iB);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_replicate_4_8 (mng_uint8 iB)
+{
+  return (mng_uint8)((iB << 4) + iB);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16 mng_promote_replicate_4_16 (mng_uint8 iB)
+{
+  iB = (mng_uint8)((iB << 4) + iB);
+  return (mng_uint16)(((mng_uint16)iB << 8) + (mng_uint16)iB);
+}
+#endif
+#endif /* NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16 mng_promote_replicate_8_16 (mng_uint8 iB)
+{
+  return (mng_uint16)(((mng_uint16)iB << 8) + (mng_uint16)iB);
+}
+#endif
+
+/* ************************************************************************** */
+
+#if !defined(MNG_NO_DELTA_PNG)
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_uint8 mng_promote_zerofill_1_2 (mng_uint8 iB)
+{
+  return (mng_uint8)(iB << 1);
+}
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_zerofill_1_4 (mng_uint8 iB)
+{
+  return (mng_uint8)(iB << 3);
+}
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_zerofill_1_8 (mng_uint8 iB)
+{
+  return (mng_uint8)(iB << 7);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16 mng_promote_zerofill_1_16 (mng_uint8 iB)
+{
+  return (mng_uint16)((mng_uint16)iB << 15);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_zerofill_2_4 (mng_uint8 iB)
+{
+  return (mng_uint8)(iB << 2);
+}
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_zerofill_2_8 (mng_uint8 iB)
+{
+  return (mng_uint8)(iB << 6);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16 mng_promote_zerofill_2_16 (mng_uint8 iB)
+{
+  return (mng_uint16)((mng_uint16)iB << 14);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_uint8 mng_promote_zerofill_4_8 (mng_uint8 iB)
+{
+  return (mng_uint8)(iB << 4);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16 mng_promote_zerofill_4_16 (mng_uint8 iB)
+{
+  return (mng_uint16)((mng_uint16)iB << 12);
+}
+#endif
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16 mng_promote_zerofill_8_16 (mng_uint8 iB)
+{
+  return (mng_uint16)((mng_uint16)iB << 8);
+}
+#endif
+#endif /* MNG_NO_DELTA_PNG */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image row routines - promote color_type                          * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if !defined(MNG_NO_DELTA_PNG) || !defined(MNG_SKIPCHUNK_PAST) || !defined(MNG_SKIPCHUNK_MAGN)
+mng_retcode mng_promote_g8_g8 (mng_datap pData)
+{
+  mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32 iX;
+  mng_uint8  iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_G8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+    if (pData->fPromBitdepth)      /* bitdepth promoted ? */
+      iB = ((mng_bitdepth_8)pData->fPromBitdepth) (iB);
+    *pDstline = iB;
+
+    pSrcline++;
+    pDstline++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g8_g16 (mng_datap pData)
+{
+  mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32 iX;
+  mng_uint16 iW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_G16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iW = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline);
+
+    *pDstline     = (mng_uint8)(iW >> 8);
+    *(pDstline+1) = (mng_uint8)(iW && 0xFF);
+
+    pSrcline++;
+    pDstline += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_promote_g16_g16 (mng_datap pData)
+{
+  mng_uint16p pSrcline = (mng_uint16p)pData->pPromSrc;
+  mng_uint16p pDstline = (mng_uint16p)pData->pPromDst;
+  mng_uint32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G16_G16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    *pDstline = *pSrcline;
+    pSrcline++;
+    pDstline++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G16_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_promote_g8_ga8 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_GA8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+                                   /* no cheap transparency ? */
+    if ((!pBuf->bHasTRNS) || ((mng_uint16)iB != pBuf->iTRNSgray))
+      *(pDstline+1) = 0xFF;
+
+    if (pData->fPromBitdepth)      /* bitdepth promoted ? */
+      iB = ((mng_bitdepth_8)pData->fPromBitdepth) (iB);
+
+    *pDstline = iB;
+
+    pSrcline++;
+    pDstline += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_GA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g8_ga16 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iB;
+  mng_uint16     iW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_GA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+                                   /* no cheap transparency ? */
+    if ((!pBuf->bHasTRNS) || ((mng_uint16)iB != pBuf->iTRNSgray))
+    {
+      *(pDstline+2) = 0xFF;
+      *(pDstline+3) = 0xFF;
+    }
+
+    iW = ((mng_bitdepth_16)pData->fPromBitdepth) (iB);
+
+    *pDstline     = (mng_uint8)(iW >> 8);
+    *(pDstline+1) = (mng_uint8)(iW && 0xFF);
+
+    pSrcline++;
+    pDstline += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_GA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g16_ga16 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint16p    pSrcline = (mng_uint16p)pData->pPromSrc;
+  mng_uint16p    pDstline = (mng_uint16p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint16     iW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G16_GA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iW = *pSrcline;
+                                   /* no cheap transparency ? */
+    if ((!pBuf->bHasTRNS) || ((mng_uint16)iW != pBuf->iTRNSgray))
+      *(pDstline+1) = 0xFFFF;
+
+    *pDstline = iW;
+
+    pSrcline++;
+    pDstline += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G16_GA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_promote_g8_rgb8 (mng_datap pData)
+{
+  mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32 iX;
+  mng_uint8  iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGB8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+
+    if (pData->fPromBitdepth)      /* bitdepth promoted ? */
+      iB = ((mng_bitdepth_8)pData->fPromBitdepth) (iB);
+
+    *pDstline     = iB;
+    *(pDstline+1) = iB;
+    *(pDstline+2) = iB;
+
+    pSrcline++;
+    pDstline += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g8_rgb16 (mng_datap pData)
+{
+  mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32 iX;
+  mng_uint8  iB;
+  mng_uint16 iW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGB16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+    iW = ((mng_bitdepth_16)pData->fPromBitdepth) (iB);
+
+    iB            = (mng_uint8)(iW >> 8);
+    *pDstline     = iB;
+    *(pDstline+2) = iB;
+    *(pDstline+4) = iB;
+    iB            = (mng_uint8)(iW && 0xFF);
+    *(pDstline+1) = iB;
+    *(pDstline+3) = iB;
+    *(pDstline+5) = iB;
+
+    pSrcline++;
+    pDstline += 6;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g16_rgb16 (mng_datap pData)
+{
+  mng_uint16p pSrcline = (mng_uint16p)pData->pPromSrc;
+  mng_uint16p pDstline = (mng_uint16p)pData->pPromDst;
+  mng_uint32  iX;
+  mng_uint16  iW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G16_RGB16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iW = *pSrcline;
+
+    *pDstline     = iW;
+    *(pDstline+1) = iW;
+    *(pDstline+2) = iW;
+
+    pSrcline++;
+    pDstline += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G16_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_promote_g8_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGBA8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+                                   /* no cheap transparency ? */
+    if ((!pBuf->bHasTRNS) || ((mng_uint16)iB != pBuf->iTRNSgray))
+      *(pDstline+3) = 0xFF;
+
+    if (pData->fPromBitdepth)      /* bitdepth promoted ? */
+      iB = ((mng_bitdepth_8)pData->fPromBitdepth) (iB);
+
+    *pDstline     = iB;
+    *(pDstline+1) = iB;
+    *(pDstline+2) = iB;
+
+    pSrcline++;
+    pDstline += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g8_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iB;
+  mng_uint16     iW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGBA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+                                   /* no cheap transparency ? */
+    if ((!pBuf->bHasTRNS) || ((mng_uint16)iB != pBuf->iTRNSgray))
+    {
+      *(pDstline+6) = 0xFF;
+      *(pDstline+7) = 0xFF;
+    }
+
+    iW            = ((mng_bitdepth_16)pData->fPromBitdepth) (iB);
+
+    iB            = (mng_uint8)(iW >> 8);
+    *pDstline     = iB;
+    *(pDstline+2) = iB;
+    *(pDstline+4) = iB;
+    iB            = (mng_uint8)(iW && 0xFF);
+    *(pDstline+1) = iB;
+    *(pDstline+3) = iB;
+    *(pDstline+5) = iB;;
+
+    pSrcline++;
+    pDstline += 8;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g16_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint16p    pSrcline = (mng_uint16p)pData->pPromSrc;
+  mng_uint16p    pDstline = (mng_uint16p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint16     iW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G16_RGBA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iW = *pSrcline;
+                                   /* no cheap transparency ? */
+    if ((!pBuf->bHasTRNS) || (iW != pBuf->iTRNSgray))
+      *(pDstline+3) = 0xFFFF;
+
+    *pDstline     = iW;
+    *(pDstline+1) = iW;
+    *(pDstline+2) = iW;
+
+    pSrcline++;
+    pDstline += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_G16_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_ga8_ga16 (mng_datap pData)
+{
+  mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32 iX;
+  mng_uint16 iW;
+  mng_uint16 iA;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_GA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iW = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline);
+    iA = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+1));
+
+    *pDstline     = (mng_uint8)(iW >> 8);
+    *(pDstline+1) = (mng_uint8)(iW && 0xFF);
+    *(pDstline+2) = (mng_uint8)(iA >> 8);
+    *(pDstline+3) = (mng_uint8)(iA && 0xFF);
+
+    pSrcline += 2;
+    pDstline += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_GA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_promote_ga8_rgba8 (mng_datap pData)
+{
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iB;
+  mng_uint8      iA;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_RGBA8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+    iA = *(pSrcline+1);
+
+    *pDstline     = iB;
+    *(pDstline+1) = iB;
+    *(pDstline+2) = iB;
+    *(pDstline+3) = iA;
+
+    pSrcline += 2;
+    pDstline += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_ga8_rgba16 (mng_datap pData)
+{
+  mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32 iX;
+  mng_uint8  iB;
+  mng_uint16 iW;
+  mng_uint16 iA;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_RGBA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iW = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline);
+    iA = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+1));
+
+    iB            = (mng_uint8)(iW >> 8);
+    *pDstline     = iB;
+    *(pDstline+2) = iB;
+    *(pDstline+4) = iB;
+    iB            = (mng_uint8)(iW && 0xFF);
+    *(pDstline+1) = iB;
+    *(pDstline+3) = iB;
+    *(pDstline+5) = iB;
+    *(pDstline+6) = (mng_uint8)(iA >> 8);
+    *(pDstline+7) = (mng_uint8)(iA && 0xFF);
+
+    pSrcline += 2;
+    pDstline += 8;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_ga16_rgba16 (mng_datap pData)
+{
+  mng_uint16p pSrcline = (mng_uint16p)pData->pPromSrc;
+  mng_uint16p pDstline = (mng_uint16p)pData->pPromDst;
+  mng_uint32 iX;
+  mng_uint16 iW;
+  mng_uint16 iA;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_GA16_RGBA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iW = *pSrcline;
+    iA = *(pSrcline+1);
+
+    *pDstline     = iW;
+    *(pDstline+1) = iW;
+    *(pDstline+2) = iW;
+    *(pDstline+3) = iA;
+
+    pSrcline += 2;
+    pDstline += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_GA16_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_rgb8_rgb16 (mng_datap pData)
+{
+  mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32 iX;
+  mng_uint16 iR;
+  mng_uint16 iG;
+  mng_uint16 iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGB16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iR            = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline);
+    iG            = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+1));
+    iB            = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+2));
+
+    *pDstline     = (mng_uint8)(iR >> 8);
+    *(pDstline+1) = (mng_uint8)(iR && 0xFF);
+    *(pDstline+2) = (mng_uint8)(iG >> 8);
+    *(pDstline+3) = (mng_uint8)(iG && 0xFF);
+    *(pDstline+4) = (mng_uint8)(iB >> 8);
+    *(pDstline+5) = (mng_uint8)(iB && 0xFF);
+
+    pSrcline += 3;
+    pDstline += 6;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_promote_rgb8_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iR;
+  mng_uint8      iG;
+  mng_uint8      iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGBA8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iR = *pSrcline;
+    iG = *(pSrcline+1);
+    iB = *(pSrcline+2);
+                                   /* no cheap transparency ? */
+    if ((!pBuf->bHasTRNS) || ((mng_uint16)iR != pBuf->iTRNSred) ||
+        ((mng_uint16)iG != pBuf->iTRNSgreen) || ((mng_uint16)iB != pBuf->iTRNSblue))
+      *(pDstline+3) = 0xFF;
+
+    *pDstline     = iR;
+    *(pDstline+1) = iG;
+    *(pDstline+2) = iB;
+
+    pSrcline += 3;
+    pDstline += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_rgb8_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iR;
+  mng_uint8      iG;
+  mng_uint8      iB;
+  mng_uint16     iRw;
+  mng_uint16     iGw;
+  mng_uint16     iBw;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGBA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iR = *pSrcline;
+    iG = *(pSrcline+1);
+    iB = *(pSrcline+2);
+                                   /* no cheap transparency ? */
+    if ((!pBuf->bHasTRNS) || ((mng_uint16)iR != pBuf->iTRNSred) ||
+        ((mng_uint16)iG != pBuf->iTRNSgreen) || ((mng_uint16)iB != pBuf->iTRNSblue))
+    {
+      *(pDstline+6) = 0xFF;
+      *(pDstline+7) = 0xFF;
+    }
+
+    iRw           = ((mng_bitdepth_16)pData->fPromBitdepth) (iR);
+    iGw           = ((mng_bitdepth_16)pData->fPromBitdepth) (iG);
+    iBw           = ((mng_bitdepth_16)pData->fPromBitdepth) (iB);
+
+    *pDstline     = (mng_uint8)(iRw >> 8);
+    *(pDstline+1) = (mng_uint8)(iRw && 0xFF);
+    *(pDstline+2) = (mng_uint8)(iGw >> 8);
+    *(pDstline+3) = (mng_uint8)(iGw && 0xFF);
+    *(pDstline+4) = (mng_uint8)(iBw >> 8);
+    *(pDstline+5) = (mng_uint8)(iBw && 0xFF);
+
+    pSrcline += 3;
+    pDstline += 8;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_rgb16_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint16p    pSrcline = (mng_uint16p)pData->pPromSrc;
+  mng_uint16p    pDstline = (mng_uint16p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint16     iR;
+  mng_uint16     iG;
+  mng_uint16     iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGB16_RGBA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iR = *pSrcline;
+    iG = *(pSrcline+1);
+    iB = *(pSrcline+2);
+                                   /* no cheap transparency ? */
+    if ((!pBuf->bHasTRNS) || (iR != pBuf->iTRNSred) ||
+        (iG != pBuf->iTRNSgreen) || (iB != pBuf->iTRNSblue))
+      *(pDstline+3) = 0xFFFF;
+
+    *pDstline     = iR;
+    *(pDstline+1) = iG;
+    *(pDstline+2) = iB;
+
+    pSrcline += 3;
+    pDstline += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGB16_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_promote_idx8_rgb8 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGB8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+
+    if ((mng_uint32)iB < pBuf->iPLTEcount)
+    {
+      *pDstline     = pBuf->aPLTEentries [iB].iRed;
+      *(pDstline+1) = pBuf->aPLTEentries [iB].iGreen;
+      *(pDstline+2) = pBuf->aPLTEentries [iB].iBlue;
+    }
+
+    pSrcline++;
+    pDstline += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_idx8_rgb16 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iN;
+  mng_uint16     iR;
+  mng_uint16     iG;
+  mng_uint16     iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGB16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iN = *pSrcline;
+
+    if ((mng_uint32)iN < pBuf->iPLTEcount)
+    {
+      iR              = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iRed);
+      iG              = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iGreen);
+      iB              = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iBlue);
+      *pDstline       = (mng_uint8)(iR >> 8);
+      *(pDstline+1)   = (mng_uint8)(iR && 0xFF);
+      *(pDstline+2)   = (mng_uint8)(iG >> 8);
+      *(pDstline+3)   = (mng_uint8)(iG && 0xFF);
+      *(pDstline+4)   = (mng_uint8)(iB >> 8);
+      *(pDstline+5)   = (mng_uint8)(iB && 0xFF);
+    }
+
+    pSrcline++;
+    pDstline += 6;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_promote_idx8_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGBA8, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iB = *pSrcline;
+
+    if ((mng_uint32)iB < pBuf->iPLTEcount)
+    {
+      *pDstline       = pBuf->aPLTEentries [iB].iRed;
+      *(pDstline+1)   = pBuf->aPLTEentries [iB].iGreen;
+      *(pDstline+2)   = pBuf->aPLTEentries [iB].iBlue;
+
+      if ((pBuf->bHasTRNS) && ((mng_uint32)iB < pBuf->iTRNScount))
+        *(pDstline+3) = pBuf->aTRNSentries [iB];
+      else
+        *(pDstline+3) = 0xFF;
+    }
+
+    pSrcline++;
+    pDstline += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_idx8_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf     = (mng_imagedatap)pData->pPromBuf;
+  mng_uint8p     pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p     pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32     iX;
+  mng_uint8      iN;
+  mng_uint16     iR;
+  mng_uint16     iG;
+  mng_uint16     iB;
+  mng_uint16     iA;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGBA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iN = *pSrcline;
+
+    if ((mng_uint32)iN < pBuf->iPLTEcount)
+    {
+      iR            = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iRed);
+      iG            = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iGreen);
+      iB            = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iBlue);
+
+      if ((pBuf->bHasTRNS) && ((mng_uint32)iN < pBuf->iTRNScount))
+        iA          = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aTRNSentries [iN]);
+      else
+        iA          = 0xFFFF;
+
+      *pDstline     = (mng_uint8)(iR >> 8);
+      *(pDstline+1) = (mng_uint8)(iR && 0xFF);
+      *(pDstline+2) = (mng_uint8)(iG >> 8);
+      *(pDstline+3) = (mng_uint8)(iG && 0xFF);
+      *(pDstline+4) = (mng_uint8)(iB >> 8);
+      *(pDstline+5) = (mng_uint8)(iB && 0xFF);
+      *(pDstline+6) = (mng_uint8)(iA >> 8);
+      *(pDstline+7) = (mng_uint8)(iA && 0xFF);
+    }
+
+    pSrcline++;
+    pDstline += 8;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_promote_rgba8_rgba16 (mng_datap pData)
+{
+  mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc;
+  mng_uint8p pDstline = (mng_uint8p)pData->pPromDst;
+  mng_uint32 iX;
+  mng_uint16 iR;
+  mng_uint16 iG;
+  mng_uint16 iB;
+  mng_uint16 iA;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGBA8_RGBA16, MNG_LC_START);
+#endif
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iPromWidth; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iPromWidth; iX++)
+#endif
+  {
+    iR            = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline);
+    iG            = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+1));
+    iB            = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+2));
+    iA            = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+3));
+
+    *pDstline     = (mng_uint8)(iR >> 8);
+    *(pDstline+1) = (mng_uint8)(iR && 0xFF);
+    *(pDstline+2) = (mng_uint8)(iG >> 8);
+    *(pDstline+3) = (mng_uint8)(iG && 0xFF);
+    *(pDstline+4) = (mng_uint8)(iB >> 8);
+    *(pDstline+5) = (mng_uint8)(iB && 0xFF);
+    *(pDstline+6) = (mng_uint8)(iA >> 8);
+    *(pDstline+7) = (mng_uint8)(iA && 0xFF);
+
+    pSrcline += 4;
+    pDstline += 8;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROMOTE_RGBA8_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* !defined(MNG_NO_DELTA_PNG) || !defined(MNG_SKIPCHUNK_PAST) || !defined(MNG_SKIPCHUNK_MAGN) */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row processing routines - convert uncompressed data from zlib to       * */
+/* * managable row-data which serves as input to the color-management       * */
+/* * routines                                                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_process_g1 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G1, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+    if (pBuf->iTRNSgray)               /* white transparent ? */
+    {
+#ifdef MNG_DECREMENT_LOOPS
+      for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+      for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+      {
+        if (!iM)                       /* mask underflow ? */
+        {
+          iB = *pWorkrow;              /* get next input-byte */
+          pWorkrow++;
+          iM = 0x80;
+        }
+
+        if (iB & iM)                   /* is it white ? */
+                                       /* transparent ! */
+          mng_put_uint32 (pRGBArow, 0x00000000);
+        else                           /* opaque black */
+          mng_put_uint32 (pRGBArow, 0x000000FF);
+
+        pRGBArow += 4;                 /* next pixel */
+        iM >>=  1;
+      }
+    }
+    else                               /* black transparent */
+    {
+#ifdef MNG_DECREMENT_LOOPS
+      for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+      for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+      {
+        if (!iM)                       /* mask underflow ? */
+        {
+          iB = *pWorkrow;              /* get next input-byte */
+          pWorkrow++;
+          iM = 0x80;
+        }
+
+        if (iB & iM)                   /* is it white ? */
+                                       /* opaque white */
+          mng_put_uint32 (pRGBArow, 0xFFFFFFFF);
+        else                           /* transparent */
+          mng_put_uint32 (pRGBArow, 0x00000000);
+
+        pRGBArow += 4;                 /* next pixel */
+        iM >>=  1;
+      }
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else                                 /* no transparency */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0x80;
+      }
+
+      if (iB & iM)                     /* is it white ? */
+                                       /* opaque white */
+        mng_put_uint32 (pRGBArow, 0xFFFFFFFF);
+      else                             /* opaque black */
+        mng_put_uint32 (pRGBArow, 0x000000FF);
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  1;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_g2 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH
+  const mng_uint32  level[4] = { 0x000000FF, 0x555555FF,
+          0xAAAAAAFF, 0xFFFFFFFF};
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G2, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xC0;
+        iS = 6;
+      }
+                                       /* determine gray level */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+
+      if (iQ == pBuf->iTRNSgray)       /* transparent ? */
+        mng_put_uint32 (pRGBArow, 0x00000000);
+      else
+      {
+#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH
+        mng_put_uint32 (pRGBArow, level[iQ]);
+#else
+        switch (iQ)                    /* determine the gray level */
+        {
+          case 0x03 : { mng_put_uint32 (pRGBArow, 0xFFFFFFFF); break; }
+          case 0x02 : { mng_put_uint32 (pRGBArow, 0xAAAAAAFF); break; }
+          case 0x01 : { mng_put_uint32 (pRGBArow, 0x555555FF); break; }
+          default   : { mng_put_uint32 (pRGBArow, 0x000000FF); }
+        }
+#endif
+      }
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  2;
+      iS -= 2;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xC0;
+        iS = 6;
+      }
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH
+      mng_put_uint32 (pRGBArow, level[((iB & iM) >> iS)] );
+#else
+      switch ((iB & iM) >> iS)         /* determine the gray level */
+      {
+        case 0x03 : { mng_put_uint32 (pRGBArow, 0xFFFFFFFF); break; }
+        case 0x02 : { mng_put_uint32 (pRGBArow, 0xAAAAAAFF); break; }
+        case 0x01 : { mng_put_uint32 (pRGBArow, 0x555555FF); break; }
+        default   : { mng_put_uint32 (pRGBArow, 0x000000FF); }
+      }
+#endif
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  2;
+      iS -= 2;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_g4 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G4, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xF0;
+        iS = 4;
+      }
+                                       /* get the gray level */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+
+      if (iQ == pBuf->iTRNSgray)       /* transparent ? */
+      {
+        *pRGBArow     = 0;             /* put in intermediate row */
+        *(pRGBArow+1) = 0;
+        *(pRGBArow+2) = 0;
+        *(pRGBArow+3) = 0;
+      }
+      else
+      {                                /* expand to 8-bit by replication */
+        iQ = (mng_uint8)(iQ + (iQ << 4));
+
+        *pRGBArow     = iQ;            /* put in intermediate row */
+        *(pRGBArow+1) = iQ;
+        *(pRGBArow+2) = iQ;
+        *(pRGBArow+3) = 0xFF;
+      }
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  4;
+      iS -= 4;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xF0;
+        iS = 4;
+      }
+                                       /* get the gray level */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+      iQ = (mng_uint8)(iQ + (iQ << 4));/* expand to 8-bit by replication */
+
+      *pRGBArow     = iQ;              /* put in intermediate row */
+      *(pRGBArow+1) = iQ;
+      *(pRGBArow+2) = iQ;
+      *(pRGBArow+3) = 0xFF;
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  4;
+      iS -= 4;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_g8 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G8, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+
+      if (iB == pBuf->iTRNSgray)       /* transparent ? */
+      {
+        *pRGBArow     = 0;             /* put in intermediate row */
+        *(pRGBArow+1) = 0;
+        *(pRGBArow+2) = 0;
+        *(pRGBArow+3) = 0;
+      }
+      else
+      {
+        *pRGBArow     = iB;            /* put in intermediate row */
+        *(pRGBArow+1) = iB;
+        *(pRGBArow+2) = iB;
+        *(pRGBArow+3) = 0xFF;
+      }
+
+      pRGBArow += 4;                   /* next pixel */
+      pWorkrow++;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iB = *pWorkrow;                  /* get next input-byte */
+
+      *pRGBArow     = iB;              /* put in intermediate row */
+      *(pRGBArow+1) = iB;
+      *(pRGBArow+2) = iB;
+      *(pRGBArow+3) = 0xFF;
+
+      pRGBArow += 4;                   /* next pixel */
+      pWorkrow++;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_process_g16 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint16     iW;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G16, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iW = mng_get_uint16 (pWorkrow);  /* get input */
+
+      if (iW == pBuf->iTRNSgray)       /* transparent ? */
+      {                                /* put in intermediate row */
+        mng_put_uint16 (pRGBArow,   0);
+        mng_put_uint16 (pRGBArow+2, 0);
+        mng_put_uint16 (pRGBArow+4, 0);
+        mng_put_uint16 (pRGBArow+6, 0);
+      }
+      else
+      {                                /* put in intermediate row */
+        mng_put_uint16 (pRGBArow,   iW);
+        mng_put_uint16 (pRGBArow+2, iW);
+        mng_put_uint16 (pRGBArow+4, iW);
+        mng_put_uint16 (pRGBArow+6, 0xFFFF);
+      }
+
+      pRGBArow += 8;                   /* next pixel */
+      pWorkrow += 2;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iW = mng_get_uint16 (pWorkrow);  /* get input */
+
+      mng_put_uint16 (pRGBArow,   iW); /* and put in intermediate row */
+      mng_put_uint16 (pRGBArow+2, iW);
+      mng_put_uint16 (pRGBArow+4, iW);
+      mng_put_uint16 (pRGBArow+6, 0xFFFF);
+
+      pRGBArow += 8;                   /* next pixel */
+      pWorkrow += 2;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_G16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_rgb8 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iR, iG, iB;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RGB8, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iR = *pWorkrow;                  /* get the RGB values */
+      iG = *(pWorkrow+1);
+      iB = *(pWorkrow+2);
+                                       /* transparent ? */
+      if ((iR == pBuf->iTRNSred) && (iG == pBuf->iTRNSgreen) &&
+          (iB == pBuf->iTRNSblue))
+      {
+        *pRGBArow     = 0;             /* this pixel is transparent ! */
+        *(pRGBArow+1) = 0;
+        *(pRGBArow+2) = 0;
+        *(pRGBArow+3) = 0;
+      }
+      else
+      {
+        *pRGBArow     = iR;            /* copy the RGB values */
+        *(pRGBArow+1) = iG;
+        *(pRGBArow+2) = iB;
+        *(pRGBArow+3) = 0xFF;          /* this one isn't transparent */
+      }
+
+      pWorkrow += 3;                   /* next pixel */
+      pRGBArow += 4;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      *pRGBArow     = *pWorkrow;       /* copy the RGB bytes */
+      *(pRGBArow+1) = *(pWorkrow+1);
+      *(pRGBArow+2) = *(pWorkrow+2);
+      *(pRGBArow+3) = 0xFF;            /* no alpha; so always fully opaque */
+
+      pWorkrow += 3;                   /* next pixel */
+      pRGBArow += 4;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RGB8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_process_rgb16 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint16     iR, iG, iB;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RGB16, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iR = mng_get_uint16 (pWorkrow);  /* get the RGB values */
+      iG = mng_get_uint16 (pWorkrow+2);
+      iB = mng_get_uint16 (pWorkrow+4);
+                                       /* transparent ? */
+      if ((iR == pBuf->iTRNSred) && (iG == pBuf->iTRNSgreen) &&
+          (iB == pBuf->iTRNSblue))
+      {                                /* transparent then */
+        mng_put_uint16 (pRGBArow,   0);
+        mng_put_uint16 (pRGBArow+2, 0);
+        mng_put_uint16 (pRGBArow+4, 0);
+        mng_put_uint16 (pRGBArow+6, 0);
+      }
+      else
+      {                                /* put in intermediate row */
+        mng_put_uint16 (pRGBArow,   iR);
+        mng_put_uint16 (pRGBArow+2, iG);
+        mng_put_uint16 (pRGBArow+4, iB);
+        mng_put_uint16 (pRGBArow+6, 0xFFFF);
+      }
+
+      pWorkrow += 6;                   /* next pixel */
+      pRGBArow += 8;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {                                  /* copy the RGB values */
+      mng_put_uint16 (pRGBArow,   mng_get_uint16 (pWorkrow  ));
+      mng_put_uint16 (pRGBArow+2, mng_get_uint16 (pWorkrow+2));
+      mng_put_uint16 (pRGBArow+4, mng_get_uint16 (pWorkrow+4));
+      mng_put_uint16 (pRGBArow+6, 0xFFFF);
+
+      pWorkrow += 6;                   /* next pixel */
+      pRGBArow += 8;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RGB16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_process_idx1 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_IDX1, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0x80;
+        iS = 7;
+      }
+                                       /* get the index */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+                                       /* index valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        *pRGBArow     = pBuf->aPLTEentries [iQ].iRed;
+        *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen;
+        *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue;
+                                       /* transparency for this index ? */
+        if ((mng_uint32)iQ < pBuf->iTRNScount)
+          *(pRGBArow+3) = pBuf->aTRNSentries [iQ];
+        else
+          *(pRGBArow+3) = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  1;
+      iS -= 1;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0x80;
+        iS = 7;
+      }
+                                       /* get the index */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+                                       /* index valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        *pRGBArow     = pBuf->aPLTEentries [iQ].iRed;
+        *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen;
+        *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue;
+        *(pRGBArow+3) = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  1;
+      iS -= 1;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_IDX1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_idx2 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_IDX2, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xC0;
+        iS = 6;
+      }
+                                       /* get the index */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+                                       /* index valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        *pRGBArow     = pBuf->aPLTEentries [iQ].iRed;
+        *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen;
+        *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue;
+                                       /* transparency for this index ? */
+        if ((mng_uint32)iQ < pBuf->iTRNScount)
+          *(pRGBArow+3) = pBuf->aTRNSentries [iQ];
+        else
+          *(pRGBArow+3) = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  2;
+      iS -= 2;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = *pWorkrow;                /* get next input-byte */
+        pWorkrow++;
+        iM = 0xC0;
+        iS = 6;
+      }
+                                       /* get the index */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+                                       /* index valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        *pRGBArow     = pBuf->aPLTEentries [iQ].iRed;
+        *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen;
+        *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue;
+        *(pRGBArow+3) = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  2;
+      iS -= 2;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_IDX2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_idx4 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iB;
+  mng_uint8      iM;
+  mng_uint32     iS;
+  mng_uint8      iQ;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_IDX4, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+  iM       = 0;                        /* start at pixel 0 */
+  iB       = 0;
+  iS       = 0;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = pWorkrow [0];             /* get next input-byte */
+        pWorkrow++;
+        iM = 0xF0;
+        iS = 4;
+      }
+                                       /* get the index */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+                                       /* index valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        pRGBArow [0] = pBuf->aPLTEentries [iQ].iRed;
+        pRGBArow [1] = pBuf->aPLTEentries [iQ].iGreen;
+        pRGBArow [2] = pBuf->aPLTEentries [iQ].iBlue;
+                                       /* transparency for this index ? */
+        if ((mng_uint32)iQ < pBuf->iTRNScount)
+          pRGBArow [3] = pBuf->aTRNSentries [iQ];
+        else
+          pRGBArow [3] = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  4;
+      iS -= 4;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      if (!iM)                         /* mask underflow ? */
+      {
+        iB = pWorkrow [0];             /* get next input-byte */
+        pWorkrow++;
+        iM = 0xF0;
+        iS = 4;
+      }
+                                       /* get the index */
+      iQ = (mng_uint8)((iB & iM) >> iS);
+                                       /* index valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        pRGBArow [0] = pBuf->aPLTEentries [iQ].iRed;
+        pRGBArow [1] = pBuf->aPLTEentries [iQ].iGreen;
+        pRGBArow [2] = pBuf->aPLTEentries [iQ].iBlue;
+        pRGBArow [3] = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pRGBArow += 4;                   /* next pixel */
+      iM >>=  4;
+      iS -= 4;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_IDX4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_idx8 (mng_datap pData)
+{
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pRGBArow;
+  mng_int32      iX;
+  mng_uint8      iQ;
+  mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_IDX8, MNG_LC_START);
+#endif
+
+  if (!pBuf)                           /* no object? then use obj 0 */
+    pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf;
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+
+  if (pBuf->bHasTRNS)                  /* tRNS encountered ? */
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iQ = *pWorkrow;                  /* get input byte */
+                                       /* index valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        pRGBArow [0] = pBuf->aPLTEentries [iQ].iRed;
+        pRGBArow [1] = pBuf->aPLTEentries [iQ].iGreen;
+        pRGBArow [2] = pBuf->aPLTEentries [iQ].iBlue;
+                                       /* transparency for this index ? */
+        if ((mng_uint32)iQ < pBuf->iTRNScount)
+          pRGBArow [3] = pBuf->aTRNSentries [iQ];
+        else
+          pRGBArow [3] = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pRGBArow += 4;                   /* next pixel */
+      pWorkrow++;
+    }
+
+    pData->bIsOpaque = MNG_FALSE;      /* it's not fully opaque */
+  }
+  else
+  {
+#ifdef MNG_DECREMENT_LOOPS
+    for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+    for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+    {
+      iQ = *pWorkrow;                  /* get input byte */
+                                       /* index valid ? */
+      if ((mng_uint32)iQ < pBuf->iPLTEcount)
+      {                                /* put in intermediate row */
+        pRGBArow [0] = pBuf->aPLTEentries [iQ].iRed;
+        pRGBArow [1] = pBuf->aPLTEentries [iQ].iGreen;
+        pRGBArow [2] = pBuf->aPLTEentries [iQ].iBlue;
+        pRGBArow [3] = 0xFF;
+      }
+      else
+        MNG_ERROR (pData, MNG_PLTEINDEXERROR);
+
+      pRGBArow += 4;                   /* next pixel */
+      pWorkrow++;
+    }
+
+    pData->bIsOpaque = MNG_TRUE;       /* it's fully opaque */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_IDX8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_ga8 (mng_datap pData)
+{
+  mng_uint8p pWorkrow;
+  mng_uint8p pRGBArow;
+  mng_int32  iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_GA8, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    *pRGBArow     = *pWorkrow;         /* copy the gray value */
+    *(pRGBArow+1) = *pWorkrow;
+    *(pRGBArow+2) = *pWorkrow;
+    *(pRGBArow+3) = *(pWorkrow+1);     /* copy the alpha value */
+
+    pWorkrow += 2;                     /* next pixel */
+    pRGBArow += 4;
+  }
+
+  pData->bIsOpaque = MNG_FALSE;        /* it's definitely not fully opaque */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_GA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_process_ga16 (mng_datap pData)
+{
+  mng_uint8p  pWorkrow;
+  mng_uint8p  pRGBArow;
+  mng_int32  iX;
+  mng_uint16 iW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_GA16, MNG_LC_START);
+#endif
+                                       /* temporary work pointers */
+  pWorkrow = pData->pWorkrow + pData->iPixelofs;
+  pRGBArow = pData->pRGBArow;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    iW = mng_get_uint16 (pWorkrow);    /* copy the gray value */
+    mng_put_uint16 (pRGBArow,   iW);
+    mng_put_uint16 (pRGBArow+2, iW);
+    mng_put_uint16 (pRGBArow+4, iW);
+                                       /* copy the alpha value */
+    mng_put_uint16 (pRGBArow+6, mng_get_uint16 (pWorkrow+2));
+
+    pWorkrow += 4;                     /* next pixel */
+    pRGBArow += 8;
+  }
+
+  pData->bIsOpaque = MNG_FALSE;        /* it's definitely not fully opaque */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_GA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_rgba8 (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RGBA8, MNG_LC_START);
+#endif
+                                       /* this is the easiest transform */
+  MNG_COPY (pData->pRGBArow, pData->pWorkrow + pData->iPixelofs, pData->iRowsize);
+
+  pData->bIsOpaque = MNG_FALSE;        /* it's definitely not fully opaque */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_process_rgba16 (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RGBA16, MNG_LC_START);
+#endif
+                                       /* this is the easiest transform */
+  MNG_COPY (pData->pRGBArow, pData->pWorkrow + pData->iPixelofs, pData->iRowsize);
+
+  pData->bIsOpaque = MNG_FALSE;        /* it's definitely not fully opaque */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row processing initialization routines - set up the variables needed   * */
+/* * to process uncompressed row-data                                       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_init_g1_ni     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G1_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g1;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g1;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g1;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g1;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 7;
+  pData->iSamplediv  = 3;
+  pData->iRowsize    = (pData->iRowsamples + 7) >> 3;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G1_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_g1_i      (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G1_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g1;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g1;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g1;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g1;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 7;
+  pData->iSamplediv  = 3;
+  pData->iRowsize    = ((pData->iRowsamples + 7) >> 3);
+  pData->iRowmax     = ((pData->iDatawidth + 7) >> 3) + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G1_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_g2_ni     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G2_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g2;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g2;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g2;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g2;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 3;
+  pData->iSamplediv  = 2;
+  pData->iRowsize    = (pData->iRowsamples + 3) >> 2;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G2_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_g2_i      (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G2_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g2;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g2;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g2;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g2;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 3;
+  pData->iSamplediv  = 2;
+  pData->iRowsize    = ((pData->iRowsamples + 3) >> 2);
+  pData->iRowmax     = ((pData->iDatawidth + 3) >> 2) + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G2_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_g4_ni     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G4_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g4;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g4;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g4;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g4;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 1;
+  pData->iSamplediv  = 1;
+  pData->iRowsize    = (pData->iRowsamples + 1) >> 1;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G4_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_g4_i      (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G4_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g4;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g4;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g4;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g4;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 1;
+  pData->iSamplediv  = 1;
+  pData->iRowsize    = ((pData->iRowsamples + 1) >> 1);
+  pData->iRowmax     = ((pData->iDatawidth + 1) >> 1) + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G4_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_g8_ni     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G8_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g8;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G8_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_g8_i      (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G8_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g8;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples;
+  pData->iRowmax     = pData->iDatawidth + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G8_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_g16_ni    (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G16_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g16;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g16;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g16;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g16;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 2;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 1;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 2;
+  pData->bIsRGBA16   = MNG_TRUE;       /* intermediate row is 16-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G16_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_g16_i     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G16_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_g16;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_g16;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_g16;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g16;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 2;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 1;
+  pData->iRowmax     = (pData->iDatawidth << 1) + pData->iPixelofs;
+  pData->iFilterbpp  = 2;
+  pData->bIsRGBA16   = MNG_TRUE;       /* intermediate row is 16-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_G16_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_rgb8_ni   (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGB8_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_rgb8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_rgb8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_rgb8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_rgb8;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 3;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples * 3;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 3;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGB8_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_rgb8_i    (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGB8_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_rgb8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_rgb8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_rgb8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_rgb8;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 3;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples * 3;
+  pData->iRowmax     = (pData->iDatawidth * 3) + pData->iPixelofs;
+  pData->iFilterbpp  = 3;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGB8_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_rgb16_ni  (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGB16_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_rgb16;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_rgb16;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_rgb16;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_rgb16;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 6;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples * 6;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 6;
+  pData->bIsRGBA16   = MNG_TRUE;       /* intermediate row is 16-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGB16_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_rgb16_i   (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGB16_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_rgb16;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_rgb16;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_rgb16;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_rgb16;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 6;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples * 6;
+  pData->iRowmax     = (pData->iDatawidth * 6) + pData->iPixelofs;
+  pData->iFilterbpp  = 6;
+  pData->bIsRGBA16   = MNG_TRUE;       /* intermediate row is 16-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGB16_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_init_idx1_ni   (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX1_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_idx1;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_idx1;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_idx1;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_idx1;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 7;
+  pData->iSamplediv  = 3;
+  pData->iRowsize    = (pData->iRowsamples + 7) >> 3;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX1_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_idx1_i    (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX1_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_idx1;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_idx1;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_idx1;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_idx1;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 7;
+  pData->iSamplediv  = 3;
+  pData->iRowsize    = (pData->iRowsamples + 7) >> 3;
+  pData->iRowmax     = ((pData->iDatawidth + 7) >> 3) + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX1_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_idx2_ni   (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX2_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_idx2;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_idx2;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_idx2;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_idx2;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 3;
+  pData->iSamplediv  = 2;
+  pData->iRowsize    = (pData->iRowsamples + 3) >> 2;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX2_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_idx2_i    (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX2_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_idx2;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_idx2;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_idx2;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_idx2;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 3;
+  pData->iSamplediv  = 2;
+  pData->iRowsize    = (pData->iRowsamples + 3) >> 2;
+  pData->iRowmax     = ((pData->iDatawidth + 3) >> 2) + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX2_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_idx4_ni   (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX4_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_idx4;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_idx4;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_idx4;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_idx4;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 1;
+  pData->iSamplediv  = 1;
+  pData->iRowsize    = (pData->iRowsamples + 1) >> 1;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX4_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_idx4_i    (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX4_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_idx4;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_idx4;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_idx4;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_idx4;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 1;
+  pData->iSamplediv  = 1;
+  pData->iRowsize    = (pData->iRowsamples + 1) >> 1;
+  pData->iRowmax     = ((pData->iDatawidth + 1) >> 1) + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX4_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_idx8_ni   (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX8_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_idx8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_idx8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_idx8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_idx8;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX8_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_idx8_i    (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX8_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_idx8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_idx8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_idx8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_idx8;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples;
+  pData->iRowmax     = pData->iDatawidth + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_IDX8_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_ga8_ni    (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GA8_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_ga8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_ga8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_ga8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_ga8;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 2;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 1;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 2;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GA8_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_ga8_i     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GA8_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_ga8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_ga8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_ga8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_ga8;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 2;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 1;
+  pData->iRowmax     = (pData->iDatawidth << 1) + pData->iPixelofs;
+  pData->iFilterbpp  = 2;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GA8_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_ga16_ni   (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GA16_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_ga16;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_ga16;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_ga16;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_ga16;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 4;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 2;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 4;
+  pData->bIsRGBA16   = MNG_TRUE;       /* intermediate row is 16-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GA16_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_ga16_i    (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GA16_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_ga16;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_ga16;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_ga16;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_ga16;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 4;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 2;
+  pData->iRowmax     = (pData->iDatawidth << 2) + pData->iPixelofs;
+  pData->iFilterbpp  = 4;
+  pData->bIsRGBA16   = MNG_TRUE;       /* intermediate row is 16-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_GA16_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_rgba8_ni  (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGBA8_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_rgba8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_rgba8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_rgba8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_rgba8;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 4;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 2;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 4;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGBA8_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+/* ************************************************************************** */
+
+mng_retcode mng_init_rgba8_i   (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGBA8_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_rgba8;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_rgba8;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_rgba8;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_rgba8;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; is 1..7 in specifications */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 4;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 2;
+  pData->iRowmax     = (pData->iDatawidth << 2) + pData->iPixelofs;
+  pData->iFilterbpp  = 4;
+  pData->bIsRGBA16   = MNG_FALSE;      /* intermediate row is 8-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGBA8_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_rgba16_ni (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGBA16_NI, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_rgba16;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_rgba16;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_rgba16;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_rgba16;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 8;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 3;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 8;
+  pData->bIsRGBA16   = MNG_TRUE;       /* intermediate row is 16-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGBA16_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_rgba16_i  (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGBA16_I, MNG_LC_START);
+#endif
+
+  if (pData->fDisplayrow)
+    pData->fProcessrow = (mng_fptr)mng_process_rgba16;
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {                                    /* immediate delta ? */
+#ifndef MNG_NO_DELTA_PNG
+    if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+      pData->fStorerow = (mng_fptr)mng_delta_rgba16;
+    else
+#endif
+      pData->fStorerow = (mng_fptr)mng_store_rgba16;
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_rgba16;
+#endif
+
+  pData->iPass       = 0;              /* from 0..6; (1..7 in specification) */
+  pData->iRow        = interlace_row     [0];
+  pData->iRowinc     = interlace_rowskip [0];
+  pData->iCol        = interlace_col     [0];
+  pData->iColinc     = interlace_colskip [0];
+  pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0];
+  pData->iSamplemul  = 8;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 3;
+  pData->iRowmax     = (pData->iDatawidth << 3) + pData->iPixelofs;
+  pData->iFilterbpp  = 8;
+  pData->bIsRGBA16   = MNG_TRUE;       /* intermediate row is 16-bit deep */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_RGBA16_I, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row processing initialization routines (JPEG) - set up the variables   * */
+/* * needed to process uncompressed row-data                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_init_jpeg_a1_ni     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A1_NI, MNG_LC_START);
+#endif
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {
+    if (pData->iJHDRimgbitdepth == 8)
+    {
+      switch (pData->iJHDRcolortype)
+      {
+        case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a1;   break; }
+        case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a1; break; }
+      }
+    }
+
+    /* TODO: bitdepth 12 & 20 */
+
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g1;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 7;
+  pData->iSamplediv  = 3;
+  pData->iRowsize    = (pData->iRowsamples + 7) >> 3;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A1_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_jpeg_a2_ni     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A2_NI, MNG_LC_START);
+#endif
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {
+    if (pData->iJHDRimgbitdepth == 8)
+    {
+      switch (pData->iJHDRcolortype)
+      {
+        case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a2;   break; }
+        case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a2; break; }
+      }
+    }
+
+    /* TODO: bitdepth 12 & 20 */
+
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g2;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 3;
+  pData->iSamplediv  = 2;
+  pData->iRowsize    = (pData->iRowsamples + 3) >> 2;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A2_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_jpeg_a4_ni     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A4_NI, MNG_LC_START);
+#endif
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {
+    if (pData->iJHDRimgbitdepth == 8)
+    {
+      switch (pData->iJHDRcolortype)
+      {
+        case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a4;   break; }
+        case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a4; break; }
+      }
+    }
+
+    /* TODO: bitdepth 12 & 20 */
+
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g4;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 1;
+  pData->iSamplediv  = 1;
+  pData->iRowsize    = (pData->iRowsamples + 1) >> 1;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A4_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_init_jpeg_a8_ni     (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A8_NI, MNG_LC_START);
+#endif
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {
+    if (pData->iJHDRimgbitdepth == 8)
+    {
+      switch (pData->iJHDRcolortype)
+      {
+        case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a8;   break; }
+        case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a8; break; }
+      }
+    }
+
+    /* TODO: bitdepth 12 & 20 */
+
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g8;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 1;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 1;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A8_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_jpeg_a16_ni    (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A16_NI, MNG_LC_START);
+#endif
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {
+    if (pData->iJHDRimgbitdepth == 8)
+    {
+      switch (pData->iJHDRcolortype)
+      {
+        case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a16;   break; }
+        case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a16; break; }
+      }
+    }
+
+    /* TODO: bitdepth 12 & 20 */
+
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+    pData->fDifferrow  = (mng_fptr)mng_differ_g16;
+#endif
+
+  pData->iPass       = -1;
+  pData->iRow        = 0;
+  pData->iRowinc     = 1;
+  pData->iCol        = 0;
+  pData->iColinc     = 1;
+  pData->iRowsamples = pData->iDatawidth;
+  pData->iSamplemul  = 2;
+  pData->iSampleofs  = 0;
+  pData->iSamplediv  = 0;
+  pData->iRowsize    = pData->iRowsamples << 1;
+  pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+  pData->iFilterbpp  = 2;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_JPEG_A16_NI, MNG_LC_END);
+#endif
+
+  return mng_init_rowproc (pData);
+}
+#endif
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_JNG */
+#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */
+
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Generic row processing initialization & cleanup routines               * */
+/* * - initialize the buffers used by the row processing routines           * */
+/* * - cleanup the buffers used by the row processing routines              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_init_rowproc (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ROWPROC, MNG_LC_START);
+#endif
+
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+  if (pData->ePng_imgtype != png_none)
+  {
+  if (pData->fDisplayrow)
+    switch (pData->ePng_imgtype)
+    {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_g1:
+      pData->fProcessrow = (mng_fptr)mng_process_g1;
+      break;
+    case png_g2:
+      pData->fProcessrow = (mng_fptr)mng_process_g2;
+      break;
+    case png_g4:
+      pData->fProcessrow = (mng_fptr)mng_process_g4;
+      break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+    case png_g8:
+      pData->fProcessrow = (mng_fptr)mng_process_g8;
+      break;
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_idx1:
+      pData->fProcessrow = (mng_fptr)mng_process_idx1;
+      break;
+    case png_idx2:
+      pData->fProcessrow = (mng_fptr)mng_process_idx2;
+      break;
+    case png_idx4:
+      pData->fProcessrow = (mng_fptr)mng_process_idx4;
+      break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+    case png_idx8:
+      pData->fProcessrow = (mng_fptr)mng_process_idx8;
+      break;
+    case png_ga8:
+      pData->fProcessrow = (mng_fptr)mng_process_ga8;
+      break;
+    case png_rgb8:
+      pData->fProcessrow = (mng_fptr)mng_process_rgb8;
+      break;
+    case png_rgba8:
+      pData->fProcessrow = (mng_fptr)mng_process_rgba8;
+      break;
+#ifndef MNG_NO_16BIT_SUPPORT
+    case png_g16:
+      pData->fProcessrow = (mng_fptr)mng_process_g16;
+      break;
+    case png_ga16:
+      pData->fProcessrow = (mng_fptr)mng_process_ga16;
+      break;
+    case png_rgb16:
+      pData->fProcessrow = (mng_fptr)mng_process_rgb16;
+      break;
+    case png_rgba16:
+      pData->fProcessrow = (mng_fptr)mng_process_rgba16;
+      break;
+#endif
+    default:
+      break;
+    }
+
+  if (pData->pStoreobj)                /* store in object too ? */
+  {
+#ifndef MNG_NO_DELTA_PNG
+  if ((pData->bHasDHDR) && (pData->bDeltaimmediate))
+    switch (pData->ePng_imgtype)
+    {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_g1:
+      pData->fStorerow = (mng_fptr)mng_delta_g1;
+      break;
+    case png_g2:
+      pData->fStorerow = (mng_fptr)mng_delta_g2;
+      break;
+    case png_g4:
+      pData->fStorerow = (mng_fptr)mng_delta_g4;
+      break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+    case png_g8:
+      pData->fStorerow = (mng_fptr)mng_delta_g8;
+      break;
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_idx1:
+      pData->fStorerow = (mng_fptr)mng_delta_idx1;
+      break;
+    case png_idx2:
+      pData->fStorerow = (mng_fptr)mng_delta_idx2;
+      break;
+    case png_idx4:
+      pData->fStorerow = (mng_fptr)mng_delta_idx4;
+      break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+    case png_idx8:
+      pData->fStorerow = (mng_fptr)mng_delta_idx8;
+      break;
+    case png_ga8:
+      pData->fStorerow = (mng_fptr)mng_delta_ga8;
+      break;
+    case png_rgb8:
+      pData->fStorerow = (mng_fptr)mng_delta_rgb8;
+      break;
+    case png_rgba8:
+      pData->fStorerow = (mng_fptr)mng_delta_rgba8;
+      break;
+#ifndef MNG_NO_16BIT_SUPPORT
+    case png_g16:
+      pData->fStorerow = (mng_fptr)mng_delta_g16;
+      break;
+    case png_ga16:
+      pData->fStorerow = (mng_fptr)mng_delta_ga16;
+      break;
+    case png_rgb16:
+      pData->fStorerow = (mng_fptr)mng_delta_rgb16;
+      break;
+    case png_rgba16:
+      pData->fStorerow = (mng_fptr)mng_delta_rgba16;
+      break;
+#endif
+    default:
+      break;
+    }
+  else
+#endif  /* MNG_NO_DELTA_PNG */
+    switch (pData->ePng_imgtype)
+    {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_g1:
+      pData->fStorerow = (mng_fptr)mng_store_g1;
+      break;
+    case png_g2:
+      pData->fStorerow = (mng_fptr)mng_store_g2;
+      break;
+    case png_g4:
+      pData->fStorerow = (mng_fptr)mng_store_g4;
+      break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+    case png_g8:
+      pData->fStorerow = (mng_fptr)mng_store_g8;
+      break;
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_idx1:
+      pData->fStorerow = (mng_fptr)mng_store_idx1;
+      break;
+    case png_idx2:
+      pData->fStorerow = (mng_fptr)mng_store_idx2;
+      break;
+    case png_idx4:
+      pData->fStorerow = (mng_fptr)mng_store_idx4;
+      break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+    case png_idx8:
+      pData->fStorerow = (mng_fptr)mng_store_idx8;
+      break;
+    case png_ga8:
+      pData->fStorerow = (mng_fptr)mng_store_ga8;
+      break;
+    case png_rgb8:
+      pData->fStorerow = (mng_fptr)mng_store_rgb8;
+      break;
+    case png_rgba8:
+      pData->fStorerow = (mng_fptr)mng_store_rgba8;
+      break;
+#ifndef MNG_NO_16BIT_SUPPORT
+    case png_g16:
+      pData->fStorerow = (mng_fptr)mng_store_g16;
+      break;
+    case png_ga16:
+      pData->fStorerow = (mng_fptr)mng_store_ga16;
+      break;
+    case png_rgb16:
+      pData->fStorerow = (mng_fptr)mng_store_rgb16;
+      break;
+    case png_rgba16:
+      pData->fStorerow = (mng_fptr)mng_store_rgba16;
+      break;
+#endif
+
+#ifdef MNG_INCLUDE_JNG
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_jpeg_a1:
+/*  if (pData->iJHDRimgbitdepth == 8) */
+      {
+        switch (pData->iJHDRcolortype)
+        {
+        case 12 :
+          { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a1;   break; }
+        case 14 :
+          { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a1; break; }
+        }
+      }
+      /* TODO: bitdepth 12 & 20 */
+      break;
+    case png_jpeg_a2:
+/*  if (pData->iJHDRimgbitdepth == 8) */
+      {
+        switch (pData->iJHDRcolortype)
+        {
+          case 12 :
+            { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a2;   break; }
+          case 14 :
+            { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a2; break; }
+        }
+      }
+      break;
+      /* TODO: bitdepth 12 & 20 */
+    case png_jpeg_a4:
+/*  if (pData->iJHDRimgbitdepth == 8) */
+      {
+        switch (pData->iJHDRcolortype)
+        {
+          case 12 :
+           { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a4;   break; }
+          case 14 :
+           { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a4; break; }
+        }
+      }
+      break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+      /* TODO: bitdepth 12 & 20 */
+    case png_jpeg_a8:
+/*  if (pData->iJHDRimgbitdepth == 8) */
+      {
+        switch (pData->iJHDRcolortype)
+        {
+          case 12 :
+            { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a8;   break; }
+          case 14 :
+            { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a8; break; }
+        }
+      }
+      break;
+      /* TODO: bitdepth 12 & 20 */
+#ifndef MNG_NO_16BIT_SUPPORT
+    case png_jpeg_a16:
+/*  if (pData->iJHDRimgbitdepth == 8) */
+      {
+        switch (pData->iJHDRcolortype)
+        {
+          case 12 :
+            { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a16;   break; }
+          case 14 :
+            { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a16; break; }
+        }
+      }
+      break;
+      /* TODO: bitdepth 12 & 20 */
+#endif
+#endif /* MNG_INCLUDE_JNG */
+    default:
+      break;
+    }
+  }
+
+#ifdef FILTER192                       /* leveling & differing ? */
+  if (pData->iFilter == MNG_FILTER_DIFFERING)
+  switch (pData->ePng_imgtype)
+  {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_g1:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a1:
+#endif
+      pData->fDifferrow  = (mng_fptr)mng_differ_g1;
+      break;
+    case png_g2:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a2:
+#endif
+      pData->fDifferrow  = (mng_fptr)mng_differ_g2;
+      break;
+    case png_g4:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a4:
+#endif
+      pData->fDifferrow  = (mng_fptr)mng_differ_g4;
+      break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+    case png_g8:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a8:
+#endif
+      pData->fDifferrow  = (mng_fptr)mng_differ_g8;
+      break;
+    case png_rgb8:
+      pData->fDifferrow  = (mng_fptr)mng_differ_rgb8;
+      break;
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_idx1:
+      pData->fDifferrow  = (mng_fptr)mng_differ_idx1;
+      break;
+    case png_idx2:
+      pData->fDifferrow  = (mng_fptr)mng_differ_idx2;
+      break;
+    case png_idx4:
+      pData->fDifferrow  = (mng_fptr)mng_differ_idx4;
+      break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+    case png_idx8:
+      pData->fDifferrow  = (mng_fptr)mng_differ_idx8;
+      break;
+    case png_ga8:
+      pData->fDifferrow  = (mng_fptr)mng_differ_ga8;
+      break;
+    case png_rgb8:
+      pData->fDifferrow  = (mng_fptr)mng_differ_rgb8;
+      break;
+    case png_rgba8:
+      pData->fDifferrow  = (mng_fptr)mng_differ_rgba8;
+      break;
+#ifndef MNG_NO_16BIT_SUPPORT
+    case png_g16:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a16:
+#endif
+      pData->fDifferrow  = (mng_fptr)mng_differ_g16;
+      break;
+    case png_ga16:
+      pData->fDifferrow  = (mng_fptr)mng_differ_ga16;
+      break;
+    case png_rgb16:
+      pData->fDifferrow  = (mng_fptr)mng_differ_rgb16;
+      break;
+    case png_rgba16:
+      pData->fDifferrow  = (mng_fptr)mng_differ_rgba16;
+      break;
+#endif
+    default:
+      break;
+  }
+#endif
+
+  switch (pData->ePng_imgtype)
+  {
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+    case png_g1:
+    case png_idx1:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a1:
+#endif
+        pData->iSamplemul  = 1;
+        pData->iSampleofs  = 7;
+        pData->iSamplediv  = 3;
+        pData->iFilterbpp  = 1;
+        break;
+    case png_g2:
+    case png_idx2:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a2:
+#endif
+        pData->iSamplemul  = 1;
+        pData->iSampleofs  = 3;
+        pData->iSamplediv  = 2;
+        pData->iFilterbpp  = 1;
+        break;
+    case png_g4:
+    case png_idx4:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a4:
+#endif
+        pData->iSamplemul  = 1;
+        pData->iSampleofs  = 1;
+        pData->iSamplediv  = 1;
+        pData->iFilterbpp  = 1;
+        break;
+#endif /* MNG_NO_1_2_4BIT_SUPPORT */
+    case png_g8:
+    case png_idx8:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a8:
+#endif
+        pData->iSamplemul  = 1;
+        pData->iSampleofs  = 0;
+        pData->iSamplediv  = 0;
+        pData->iFilterbpp  = 1;
+        break;
+    case png_ga8:
+#ifndef MNG_NO_16BIT_SUPPORT
+    case png_g16:
+#ifdef MNG_INCLUDE_JNG
+    case png_jpeg_a16:
+#endif
+#endif
+        pData->iSamplemul  = 2;
+        pData->iSampleofs  = 0;
+        pData->iSamplediv  = 0;
+        pData->iFilterbpp  = 2;
+        break;
+    case png_rgb8:
+        pData->iSamplemul  = 3;
+        pData->iSampleofs  = 0;
+        pData->iSamplediv  = 0;
+        pData->iFilterbpp  = 3;
+        break;
+#ifndef MNG_NO_16BIT_SUPPORT
+    case png_ga16:
+#endif
+    case png_rgba8:
+        pData->iSamplemul  = 4;
+        pData->iSampleofs  = 0;
+        pData->iSamplediv  = 0;
+        pData->iFilterbpp  = 4;
+        break;
+#ifndef MNG_NO_16BIT_SUPPORT
+    case png_rgb16:
+        pData->iSamplemul  = 6;
+        pData->iSampleofs  = 0;
+        pData->iSamplediv  = 0;
+        pData->iFilterbpp  = 6;
+        break;
+    case png_rgba16:
+        pData->iSamplemul  = 8;
+        pData->iSampleofs  = 0;
+        pData->iSamplediv  = 0;
+        pData->iFilterbpp  = 8;
+        break;
+#endif
+    default:
+        break;
+  }
+
+  if (pData->iInterlace)               /* noninterlaced */
+  {
+    pData->iPass       = 0;      /* from 0..6; (1..7 in specification) */
+    pData->iRow        = interlace_row     [0];
+    pData->iRowinc     = interlace_rowskip [0];
+    pData->iCol        = interlace_col     [0];
+    pData->iColinc     = interlace_colskip [0];
+    pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >>
+       interlace_divider [0];
+    pData->iRowmax     = ((pData->iDatawidth * pData->iSamplemul +
+       pData->iSampleofs) >> pData->iSamplediv) + pData->iPixelofs;
+  }
+  else                                 /* interlaced */
+  {
+    pData->iPass       = -1;
+    pData->iRow        = 0;
+    pData->iRowinc     = 1;
+    pData->iCol        = 0;
+    pData->iColinc     = 1;
+    pData->iRowsamples = pData->iDatawidth;
+  }
+  if (pData->iSamplediv > 0)
+     pData->iRowsize    = (pData->iRowsamples + pData->iSampleofs) >>
+         pData->iSamplediv;
+  else
+     pData->iRowsize    = (pData->iRowsamples * pData->iSamplemul);
+
+  if (!pData->iInterlace)               /* noninterlaced */
+     pData->iRowmax     = pData->iRowsize + pData->iPixelofs;
+
+#ifdef MNG_NO_16BIT_SUPPORT
+  pData->bIsRGBA16 = MNG_FALSE;
+#else
+  switch (pData->ePng_imgtype)
+  {
+    case png_g16:
+    case png_ga16:
+    case png_rgb16:
+    case png_rgba16:
+       pData->bIsRGBA16 = MNG_TRUE;
+       break;
+    default:
+       pData->bIsRGBA16 = MNG_FALSE;
+       break;
+  }
+#endif
+
+  }
+#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */
+
+  if (pData->pStoreobj)                /* storage object selected ? */
+  {
+    pData->pStorebuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+                                       /* and so it becomes viewable ! */
+    ((mng_imagep)pData->pStoreobj)->bViewable     = MNG_TRUE;
+    ((mng_imagedatap)pData->pStorebuf)->bViewable = MNG_TRUE;
+  }
+
+  /* allocate the buffers; the individual init routines have already
+     calculated the required maximum size; except in the case of a JNG
+     without alpha!!! */
+  if (pData->iRowmax)
+  {
+#if defined(MNG_NO_16BIT_SUPPORT) || defined (MNG_NO_1_2_4BIT_SUPPORT)
+    mng_uint8 iRowadd = 0;
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+    if (pData->iPNGdepth < 8)
+       iRowadd=(pData->iPNGdepth*pData->iRowmax+7)/8;
+#endif
+#ifdef MNG_NO_16BIT_SUPPORT
+    if (pData->iPNGdepth > 8)
+       iRowadd=pData->iRowmax;
+#endif
+    MNG_ALLOC (pData, pData->pWorkrow, pData->iRowmax+iRowadd);
+    MNG_ALLOC (pData, pData->pPrevrow, pData->iRowmax+iRowadd);
+#else
+    MNG_ALLOC (pData, pData->pWorkrow, pData->iRowmax);
+    MNG_ALLOC (pData, pData->pPrevrow, pData->iRowmax);
+#endif
+  }
+
+  /* allocate an RGBA16 row for intermediate processing */
+  MNG_ALLOC (pData, pData->pRGBArow, (pData->iDatawidth << 3));
+
+#ifndef MNG_NO_CMS
+  if (pData->fDisplayrow)              /* display "on-the-fly" ? */
+  {
+#if defined(MNG_FULL_CMS)              /* determine color-management initialization */
+    mng_retcode iRetcode = mng_init_full_cms   (pData, MNG_TRUE, MNG_TRUE, MNG_FALSE);
+#elif defined(MNG_GAMMA_ONLY)
+    mng_retcode iRetcode = mng_init_gamma_only (pData, MNG_TRUE, MNG_TRUE, MNG_FALSE);
+#elif defined(MNG_APP_CMS)
+    mng_retcode iRetcode = mng_init_app_cms    (pData, MNG_TRUE, MNG_TRUE, MNG_FALSE);
+#endif
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* !MNG_NO_CMS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_INIT_ROWPROC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_next_row (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_ROW, MNG_LC_START);
+#endif
+
+  pData->iRow += pData->iRowinc;       /* increase the row counter */
+
+  if (pData->iPass >= 0)               /* interlaced ? */
+  {
+    while ((pData->iPass < 7) &&       /* went 'outside' the image ? */
+           ((pData->iRow >= (mng_int32)pData->iDataheight) ||
+            (pData->iCol >= (mng_int32)pData->iDatawidth )    ))
+    {
+      pData->iPass++;                  /* next pass ! */
+
+      if (pData->iPass < 7)            /* there's only 7 passes ! */
+      {
+        pData->iRow        = interlace_row     [pData->iPass];
+        pData->iRowinc     = interlace_rowskip [pData->iPass];
+        pData->iCol        = interlace_col     [pData->iPass];
+        pData->iColinc     = interlace_colskip [pData->iPass];
+        pData->iRowsamples = (pData->iDatawidth - pData->iCol + interlace_roundoff [pData->iPass])
+                                 >> interlace_divider [pData->iPass];
+
+        if (pData->iSamplemul > 1)     /* recalculate row dimension */
+          pData->iRowsize  = pData->iRowsamples * pData->iSamplemul;
+        else
+        if (pData->iSamplediv > 0)
+          pData->iRowsize  = (pData->iRowsamples + pData->iSampleofs) >> pData->iSamplediv;
+        else
+          pData->iRowsize  = pData->iRowsamples;
+
+      }
+
+      if ((pData->iPass < 7) &&        /* reset previous row to zeroes ? */
+          (pData->iRow  < (mng_int32)pData->iDataheight) &&
+          (pData->iCol  < (mng_int32)pData->iDatawidth )    )
+      {                                /* making sure the filters will work properly! */
+        mng_int32  iX;
+        mng_uint8p pTemp = pData->pPrevrow;
+
+#ifdef MNG_NO_16BIT_SUPPORT
+#ifdef MNG_DECREMENT_LOOPS
+        for (iX = pData->iPNGmult*pData->iRowsize; iX > 0; iX--)
+#else
+        for (iX = 0; iX < pData->iPNGmult*pData->iRowsize; iX++)
+#endif
+#else
+#ifdef MNG_DECREMENT_LOOPS
+        for (iX = pData->iRowsize; iX > 0; iX--)
+#else
+        for (iX = 0; iX < pData->iRowsize; iX++)
+#endif
+#endif
+        {
+          *pTemp = 0;
+          pTemp++;
+        }
+      }
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_ROW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_cleanup_rowproc (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLEANUP_ROWPROC, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_LCMS                /* cleanup cms profile/transform */
+  {
+    mng_retcode iRetcode = mng_clear_cms (pData);
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif /* MNG_INCLUDE_LCMS */
+
+  if (pData->pRGBArow)                 /* cleanup buffer for intermediate row */
+    MNG_FREEX (pData, pData->pRGBArow, (pData->iDatawidth << 3));
+  if (pData->pPrevrow)                 /* cleanup buffer for previous row */
+    MNG_FREEX (pData, pData->pPrevrow, pData->iRowmax);
+  if (pData->pWorkrow)                 /* cleanup buffer for working row */
+    MNG_FREEX (pData, pData->pWorkrow, pData->iRowmax);
+
+  pData->pWorkrow = MNG_NULL;          /* propogate uninitialized buffers */
+  pData->pPrevrow = MNG_NULL;
+  pData->pRGBArow = MNG_NULL;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_CLEANUP_ROWPROC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* woohiii */
+}
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Generic row processing routines for JNG                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+/* ************************************************************************** */
+
+mng_retcode mng_display_jpeg_rows (mng_datap pData)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_JPEG_ROWS, MNG_LC_START);
+#endif
+                                       /* any completed rows ? */
+  if ((pData->iJPEGrow      > pData->iJPEGdisprow) &&
+      (pData->iJPEGalpharow > pData->iJPEGdisprow)    )
+  {
+    mng_uint32 iX, iMax;
+    mng_uint32 iSaverow = pData->iRow; /* save alpha decompression row-count */
+                                       /* determine the highest complete(!) row */
+    if (pData->iJPEGrow > pData->iJPEGalpharow)
+      iMax = pData->iJPEGalpharow;
+    else
+      iMax = pData->iJPEGrow;
+                                       /* display the rows */
+    for (iX = pData->iJPEGdisprow; iX < iMax; iX++)
+    {
+      pData->iRow = iX;                /* make sure we all know which row to handle */
+                                       /* makeup an intermediate row from the buffer */
+      iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData);
+                                       /* color-correct it if necessary */
+      if ((!iRetcode) && (pData->fCorrectrow))
+        iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData);
+
+      if (!iRetcode)                   /* and display it */
+      {
+        iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData);
+
+        if (!iRetcode)                 /* check progressive display refresh */
+          iRetcode = mng_display_progressive_check (pData);
+      }
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+
+    pData->iJPEGdisprow = iMax;        /* keep track of the last displayed row */
+    pData->iRow         = iSaverow;    /* restore alpha decompression row-count */
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DISPLAY_JPEG_ROWS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_next_jpeg_alpharow (mng_datap pData)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_JPEG_ALPHAROW, MNG_LC_START);
+#endif
+
+  pData->iJPEGalpharow++;              /* count the row */
+
+  if (pData->fDisplayrow)              /* display "on-the-fly" ? */
+  {                                    /* try to display what you can */
+    iRetcode = mng_display_jpeg_rows (pData);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_JPEG_ALPHAROW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_next_jpeg_row (mng_datap pData)
+{
+  mng_retcode iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_JPEG_ROW, MNG_LC_START);
+#endif
+
+  pData->iJPEGrow++;                   /* increase the row-counter */
+
+  if (pData->fDisplayrow)              /* display "on-the-fly" ? */
+  {                                    /* has alpha channel ? */
+    if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) ||
+        (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA)    )
+    {                                  /* try to display what you can */
+      iRetcode = mng_display_jpeg_rows (pData);
+    }
+    else
+    {                                  /* make sure we all know which row to handle */
+      pData->iRow = pData->iJPEGrow - 1;
+                                       /* makeup an intermediate row from the buffer */
+      iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData);
+                                       /* color-correct it if necessary */
+      if ((!iRetcode) && (pData->fCorrectrow))
+        iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData);
+
+      if (!iRetcode)                   /* and display it */
+      {
+        iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData);
+
+        if (!iRetcode)                 /* check progressive display refresh */
+          iRetcode = mng_display_progressive_check (pData);
+      }
+    }
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+
+                                       /* surpassed last filled row ? */
+  if (pData->iJPEGrow > pData->iJPEGrgbrow)
+    pData->iJPEGrgbrow = pData->iJPEGrow;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_NEXT_JPEG_ROW, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_MAGN
+#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN
+#ifndef MNG_NO_GRAY_SUPPORT
+mng_retcode mng_magnify_g8_x1 (mng_datap  pData,
+                               mng_uint16 iMX,
+                               mng_uint16 iML,
+                               mng_uint16 iMR,
+                               mng_uint32 iWidth,
+                               mng_uint8p pSrcline,
+                               mng_uint8p pDstline)
+{
+  mng_uint32 iX, iS, iM;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X1, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+      iM = iML;
+    else
+    if (iX == (iWidth - 1))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+
+    for (iS = 1; iS < iM; iS++)        /* fill interval */
+    {
+      *pTempdst = *pTempsrc1;          /* copy original source pixel */
+      pTempdst++;
+    }
+
+    pTempsrc1++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_g8_x2 (mng_datap  pData,
+                               mng_uint16 iMX,
+                               mng_uint16 iML,
+                               mng_uint16 iMR,
+                               mng_uint32 iWidth,
+                               mng_uint8p pSrcline,
+                               mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 1;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {                                /* is it same as first ? */
+        if (*pTempsrc1 == *pTempsrc2)
+        {
+          for (iS = 1; iS < iM; iS++)  /* then just repeat the first */
+          {
+            *pTempdst = *pTempsrc1;
+            pTempdst++;
+          }
+        }
+        else
+        {
+          for (iS = 1; iS < iM; iS++)  /* calculate the distances */
+          {
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2)     -
+                                                 (mng_int32)(*pTempsrc1)     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*pTempsrc1)             );
+            pTempdst++;
+          }
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_g8_x3 (mng_datap  pData,
+                               mng_uint16 iMX,
+                               mng_uint16 iML,
+                               mng_uint16 iMR,
+                               mng_uint32 iWidth,
+                               mng_uint8p pSrcline,
+                               mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM, iH;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X3, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 1;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {                                /* is it same as first ? */
+        if (*pTempsrc1 == *pTempsrc2)
+        {
+          for (iS = 1; iS < iM; iS++)  /* then just repeat the first */
+          {
+            *pTempdst = *pTempsrc1;
+            pTempdst++;
+          }
+        }
+        else
+        {
+          iH = (iM+1) / 2;             /* calculate halfway point */
+
+          for (iS = 1; iS < iH; iS++)  /* replicate first half */
+          {
+            *pTempdst = *pTempsrc1;
+            pTempdst++;
+          }
+
+          for (iS = iH; iS < iM; iS++) /* replicate second half */
+          {
+            *pTempdst = *pTempsrc2;
+            pTempdst++;
+          }
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_GRAY_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb8_x1 (mng_datap  pData,
+                                 mng_uint16 iMX,
+                                 mng_uint16 iML,
+                                 mng_uint16 iMR,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32 iX, iS, iM;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X1, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+      iM = iML;
+    else
+    if (iX == (iWidth - 1))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+
+    for (iS = 1; iS < iM; iS++)        /* fill interval */
+    {
+      *pTempdst = *pTempsrc1;          /* copy original source pixel */
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+1);
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+2);
+      pTempdst++;
+    }
+
+    pTempsrc1 += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb8_x2 (mng_datap  pData,
+                                 mng_uint16 iMX,
+                                 mng_uint16 iML,
+                                 mng_uint16 iMR,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 3;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2)     -
+                                                 (mng_int32)(*pTempsrc1)     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*pTempsrc1)             );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) -
+                                                 (mng_int32)(*(pTempsrc1+1)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+1))         );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+2) == *(pTempsrc2+2))
+            *pTempdst = *(pTempsrc1+2);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+2)) -
+                                                 (mng_int32)(*(pTempsrc1+2)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+2))         );
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb8_x3 (mng_datap  pData,
+                                 mng_uint16 iMX,
+                                 mng_uint16 iML,
+                                 mng_uint16 iMR,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM, iH;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X3, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 3;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* replicate first half */
+        {
+          *pTempdst     = *pTempsrc1;
+          *(pTempdst+1) = *(pTempsrc1+1);
+          *(pTempdst+2) = *(pTempsrc1+2);
+
+          pTempdst += 3;
+        }
+
+        for (iS = iH; iS < iM; iS++)    /* replicate second half */
+        {
+          *pTempdst     = *pTempsrc2;
+          *(pTempdst+1) = *(pTempsrc2+1);
+          *(pTempdst+2) = *(pTempsrc2+2);
+
+          pTempdst += 3;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_GRAY_SUPPORT
+mng_retcode mng_magnify_ga8_x1 (mng_datap  pData,
+                                mng_uint16 iMX,
+                                mng_uint16 iML,
+                                mng_uint16 iMR,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline,
+                                mng_uint8p pDstline)
+{
+  mng_uint32 iX, iS, iM;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X1, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+      iM = iML;
+    else
+    if (iX == (iWidth - 1))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+
+    for (iS = 1; iS < iM; iS++)        /* fill interval */
+    {
+      *pTempdst = *pTempsrc1;          /* copy original source pixel */
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+1);
+      pTempdst++;
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga8_x2 (mng_datap  pData,
+                                mng_uint16 iMX,
+                                mng_uint16 iML,
+                                mng_uint16 iMR,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline,
+                                mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 2;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2)     -
+                                                 (mng_int32)(*pTempsrc1)     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*pTempsrc1)             );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) -
+                                                 (mng_int32)(*(pTempsrc1+1)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+1))         );
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga8_x3 (mng_datap  pData,
+                                mng_uint16 iMX,
+                                mng_uint16 iML,
+                                mng_uint16 iMR,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline,
+                                mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM, iH;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X3, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 2;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* replicate first half */
+        {
+          *pTempdst     = *pTempsrc1;
+          *(pTempdst+1) = *(pTempsrc1+1);
+
+          pTempdst += 2;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* replicate second half */
+        {
+          *pTempdst     = *pTempsrc2;
+          *(pTempdst+1) = *(pTempsrc2+1);
+
+          pTempdst += 2;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga8_x4 (mng_datap  pData,
+                                mng_uint16 iMX,
+                                mng_uint16 iML,
+                                mng_uint16 iMR,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline,
+                                mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM, iH;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X4, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 2;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* first half */
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2)     -
+                                                 (mng_int32)(*pTempsrc1)     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*pTempsrc1)             );
+
+          pTempdst++;
+
+          *pTempdst = *(pTempsrc1+1);  /* replicate alpha from left */
+
+          pTempdst++;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* second half */
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2)     -
+                                                 (mng_int32)(*pTempsrc1)     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*pTempsrc1)             );
+
+          pTempdst++;
+
+          *pTempdst = *(pTempsrc2+1);  /* replicate alpha from right */
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga8_x5 (mng_datap  pData,
+                                mng_uint16 iMX,
+                                mng_uint16 iML,
+                                mng_uint16 iMR,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline,
+                                mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM, iH;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X5, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 2;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* first half */
+        {
+          *pTempdst = *pTempsrc1;      /* replicate gray from left */
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);/* just repeat the first */
+          else                         /* calculate the distance */
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1))     -
+                                                 (mng_int32)(*(pTempsrc1+1))     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+1))             );
+
+          pTempdst++;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* second half */
+        {
+          *pTempdst = *pTempsrc2;      /* replicate gray from right */
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);/* just repeat the first */
+          else                         /* calculate the distance */
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1))     -
+                                                 (mng_int32)(*(pTempsrc1+1))     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+1))             );
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X5, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_GRAY_SUPPORT */
+#endif /* MNG_OPTIMIZE_FOOTPRINT_MAGN */
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_x1 (mng_datap  pData,
+                                  mng_uint16 iMX,
+                                  mng_uint16 iML,
+                                  mng_uint16 iMR,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32 iX, iS, iM;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X1, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+      iM = iML;
+    else
+    if (iX == (iWidth - 1))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+
+    for (iS = 1; iS < iM; iS++)        /* fill interval */
+    {
+      *pTempdst = *pTempsrc1;          /* copy original source pixel */
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+1);
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+2);
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+3);
+      pTempdst++;
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_x2 (mng_datap  pData,
+                                  mng_uint16 iMX,
+                                  mng_uint16 iML,
+                                  mng_uint16 iMR,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 4;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2)     -
+                                                 (mng_int32)(*pTempsrc1)     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*pTempsrc1)             );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) -
+                                                 (mng_int32)(*(pTempsrc1+1)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+1))         );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+2) == *(pTempsrc2+2))
+            *pTempdst = *(pTempsrc1+2);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+2)) -
+                                                 (mng_int32)(*(pTempsrc1+2)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+2))         );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+3) == *(pTempsrc2+3))
+            *pTempdst = *(pTempsrc1+3);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+3)) -
+                                                 (mng_int32)(*(pTempsrc1+3)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+3))         );
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+3);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_x3 (mng_datap  pData,
+                                  mng_uint16 iMX,
+                                  mng_uint16 iML,
+                                  mng_uint16 iMR,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM, iH;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X3, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 4;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* replicate first half */
+        {
+          *pTempdst     = *pTempsrc1;
+          *(pTempdst+1) = *(pTempsrc1+1);
+          *(pTempdst+2) = *(pTempsrc1+2);
+          *(pTempdst+3) = *(pTempsrc1+3);
+
+          pTempdst += 4;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* replicate second half */
+        {
+          *pTempdst     = *pTempsrc2;
+          *(pTempdst+1) = *(pTempsrc2+1);
+          *(pTempdst+2) = *(pTempsrc2+2);
+          *(pTempdst+3) = *(pTempsrc2+3);
+
+          pTempdst += 4;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+3);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_x4 (mng_datap  pData,
+                                  mng_uint16 iMX,
+                                  mng_uint16 iML,
+                                  mng_uint16 iMR,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM, iH;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X4, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 4;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* first half */
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2)     -
+                                                 (mng_int32)(*pTempsrc1)     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*pTempsrc1)             );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) -
+                                                 (mng_int32)(*(pTempsrc1+1)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+1))         );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+2) == *(pTempsrc2+2))
+            *pTempdst = *(pTempsrc1+2);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+2)) -
+                                                 (mng_int32)(*(pTempsrc1+2)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+2))         );
+
+          pTempdst++;
+                                       /* replicate alpha from left */
+          *pTempdst     = *(pTempsrc1+3);
+
+          pTempdst++;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* second half */
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2)     -
+                                                 (mng_int32)(*pTempsrc1)     ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*pTempsrc1)             );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) -
+                                                 (mng_int32)(*(pTempsrc1+1)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+1))         );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+2) == *(pTempsrc2+2))
+            *pTempdst = *(pTempsrc1+2);
+          else
+            *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+2)) -
+                                                 (mng_int32)(*(pTempsrc1+2)) ) + iM) /
+                                     (iM * 2)) + (mng_int32)(*(pTempsrc1+2))         );
+
+          pTempdst++;
+                                       /* replicate alpha from right */
+          *pTempdst     = *(pTempsrc2+3);
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+3);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_x5 (mng_datap  pData,
+                                  mng_uint16 iMX,
+                                  mng_uint16 iML,
+                                  mng_uint16 iMR,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_int32  iS, iM, iH;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X5, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline;                /* initialize pixel-loop */
+  pTempdst  = pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 4;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* first half */
+        {
+          *pTempdst     = *pTempsrc1;  /* replicate color from left */
+          *(pTempdst+1) = *(pTempsrc1+1);
+          *(pTempdst+2) = *(pTempsrc1+2);
+
+          if (*(pTempsrc1+3) == *(pTempsrc2+3))
+            *(pTempdst+3) = *(pTempsrc1+3);
+          else
+            *(pTempdst+3) = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+3)) -
+                                                   (mng_int32)(*(pTempsrc1+3)) ) + iM) /
+                                       (iM * 2)) + (mng_int32)(*(pTempsrc1+3))         );
+
+          pTempdst += 4;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* second half */
+        {
+          *pTempdst     = *pTempsrc2;  /* replicate color from right */
+          *(pTempdst+1) = *(pTempsrc2+1);
+          *(pTempdst+2) = *(pTempsrc2+2);
+
+          if (*(pTempsrc1+3) == *(pTempsrc2+3))
+            *(pTempdst+3) = *(pTempsrc1+3);
+          else
+            *(pTempdst+3) = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+3)) -
+                                                   (mng_int32)(*(pTempsrc1+3)) ) + iM) /
+                                       (iM * 2)) + (mng_int32)(*(pTempsrc1+3))         );
+
+          pTempdst += 4;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+3);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN
+#ifndef MNG_NO_GRAY_SUPPORT
+mng_retcode mng_magnify_g8_y1 (mng_datap  pData,
+                               mng_int32  iS,
+                               mng_int32  iM,
+                               mng_uint32 iWidth,
+                               mng_uint8p pSrcline1,
+                               mng_uint8p pSrcline2,
+                               mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y1, MNG_LC_START);
+#endif
+
+  MNG_COPY (pDstline, pSrcline1, iWidth);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+mng_retcode mng_magnify_g8_y2 (mng_datap  pData,
+                               mng_int32  iS,
+                               mng_int32  iM,
+                               mng_uint32 iWidth,
+                               mng_uint8p pSrcline1,
+                               mng_uint8p pSrcline2,
+                               mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline1;               /* initialize pixel-loop */
+  pTempsrc2 = pSrcline2;
+  pTempdst  = pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    for (iX = 0; iX < iWidth; iX++)
+    {                                  /* calculate the distances */
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, iWidth);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_g8_y3 (mng_datap  pData,
+                               mng_int32  iS,
+                               mng_int32  iM,
+                               mng_uint32 iWidth,
+                               mng_uint8p pSrcline1,
+                               mng_uint8p pSrcline2,
+                               mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y3, MNG_LC_START);
+#endif
+
+  if (pSrcline2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+      MNG_COPY (pDstline, pSrcline1, iWidth)
+    else
+      MNG_COPY (pDstline, pSrcline2, iWidth);
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pDstline, pSrcline1, iWidth);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_GRAY_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb8_y1 (mng_datap  pData,
+                                 mng_int32  iS,
+                                 mng_int32  iM,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline1,
+                                 mng_uint8p pSrcline2,
+                                 mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y1, MNG_LC_START);
+#endif
+
+  MNG_COPY (pDstline, pSrcline1, iWidth * 3);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb8_y2 (mng_datap  pData,
+                                 mng_int32  iS,
+                                 mng_int32  iM,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline1,
+                                 mng_uint8p pSrcline2,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline1;               /* initialize pixel-loop */
+  pTempsrc2 = pSrcline2;
+  pTempdst  = pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    for (iX = 0; iX < iWidth; iX++)
+    {                                  /* calculate the distances */
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, iWidth * 3);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb8_y3 (mng_datap  pData,
+                                 mng_int32  iS,
+                                 mng_int32  iM,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline1,
+                                 mng_uint8p pSrcline2,
+                                 mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y3, MNG_LC_START);
+#endif
+
+  if (pSrcline2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+      MNG_COPY (pDstline, pSrcline1, iWidth * 3)
+    else
+      MNG_COPY (pDstline, pSrcline2, iWidth * 3);
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pDstline, pSrcline1, iWidth * 3);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_GRAY_SUPPORT
+mng_retcode mng_magnify_ga8_y1 (mng_datap  pData,
+                                mng_int32  iS,
+                                mng_int32  iM,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline1,
+                                mng_uint8p pSrcline2,
+                                mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y1, MNG_LC_START);
+#endif
+
+  MNG_COPY (pDstline, pSrcline1, iWidth << 1);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga8_y2 (mng_datap  pData,
+                                mng_int32  iS,
+                                mng_int32  iM,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline1,
+                                mng_uint8p pSrcline2,
+                                mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline1;               /* initialize pixel-loop */
+  pTempsrc2 = pSrcline2;
+  pTempdst  = pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    for (iX = 0; iX < iWidth; iX++)
+    {                                  /* calculate the distances */
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, iWidth << 1);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga8_y3 (mng_datap  pData,
+                                mng_int32  iS,
+                                mng_int32  iM,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline1,
+                                mng_uint8p pSrcline2,
+                                mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y3, MNG_LC_START);
+#endif
+
+  if (pSrcline2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+      MNG_COPY (pDstline, pSrcline1, iWidth << 1)
+    else
+      MNG_COPY (pDstline, pSrcline2, iWidth << 1);
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pDstline, pSrcline1, iWidth << 1);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga8_y4 (mng_datap  pData,
+                                mng_int32  iS,
+                                mng_int32  iM,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline1,
+                                mng_uint8p pSrcline2,
+                                mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y4, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline1;               /* initialize pixel-loop */
+  pTempsrc2 = pSrcline2;
+  pTempdst  = pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {                                /* calculate the distances */
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2 += 2;
+
+        *pTempdst++ = *pTempsrc1++;    /* replicate alpha from top */
+      }
+    }
+    else
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {                                /* calculate the distances */
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1 += 2;
+        pTempsrc2++;
+
+        *pTempdst++ = *pTempsrc2++;    /* replicate alpha from bottom */
+      }
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, iWidth << 1);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga8_y5 (mng_datap  pData,
+                                mng_int32  iS,
+                                mng_int32  iM,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline1,
+                                mng_uint8p pSrcline2,
+                                mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y5, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline1;               /* initialize pixel-loop */
+  pTempsrc2 = pSrcline2;
+  pTempdst  = pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {
+        *pTempdst = *pTempsrc1;        /* replicate gray from top */
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)  /* calculate the distances */
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+      }
+    }
+    else
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {
+        *pTempdst = *pTempsrc2;        /* replicate gray from bottom */
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)  /* calculate the distances */
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+      }
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, iWidth << 1);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y5, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_GRAY_SUPPORT */
+#endif /* MNG_OPTIMIZE_FOOTPRINT_MAGN */
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_y1 (mng_datap  pData,
+                                  mng_int32  iS,
+                                  mng_int32  iM,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline1,
+                                  mng_uint8p pSrcline2,
+                                  mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y1, MNG_LC_START);
+#endif
+
+  MNG_COPY (pDstline, pSrcline1, iWidth << 2);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_y2 (mng_datap  pData,
+                                  mng_int32  iS,
+                                  mng_int32  iM,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline1,
+                                  mng_uint8p pSrcline2,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline1;               /* initialize pixel-loop */
+  pTempsrc2 = pSrcline2;
+  pTempdst  = pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    for (iX = 0; iX < iWidth; iX++)
+    {                                  /* calculate the distances */
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                               (mng_int32)(*pTempsrc1) ) + iM) /
+                                   (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, iWidth << 2);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_y3 (mng_datap  pData,
+                                  mng_int32  iS,
+                                  mng_int32  iM,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline1,
+                                  mng_uint8p pSrcline2,
+                                  mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y3, MNG_LC_START);
+#endif
+
+  if (pSrcline2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+      MNG_COPY (pDstline, pSrcline1, iWidth << 2)
+    else
+      MNG_COPY (pDstline, pSrcline2, iWidth << 2);
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pDstline, pSrcline1, iWidth << 2);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_y4 (mng_datap  pData,
+                                  mng_int32  iS,
+                                  mng_int32  iM,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline1,
+                                  mng_uint8p pSrcline2,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y4, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline1;               /* initialize pixel-loop */
+  pTempsrc2 = pSrcline2;
+  pTempdst  = pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {                                /* calculate the distances */
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2 += 2;
+
+        *pTempdst++ = *pTempsrc1++;    /* replicate alpha from top */
+      }
+    }
+    else
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {                                /* calculate the distances */
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1 += 2;
+        pTempsrc2++;
+
+        *pTempdst++ = *pTempsrc2++;    /* replicate alpha from bottom */
+      }
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, iWidth << 2);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba8_y5 (mng_datap  pData,
+                                  mng_int32  iS,
+                                  mng_int32  iM,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline1,
+                                  mng_uint8p pSrcline2,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32 iX;
+  mng_uint8p pTempsrc1;
+  mng_uint8p pTempsrc2;
+  mng_uint8p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y5, MNG_LC_START);
+#endif
+
+  pTempsrc1 = pSrcline1;               /* initialize pixel-loop */
+  pTempsrc2 = pSrcline2;
+  pTempdst  = pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {
+        *pTempdst++ = *pTempsrc1++;    /* replicate color from top */
+        *pTempdst++ = *pTempsrc1++;
+        *pTempdst++ = *pTempsrc1++;
+
+        pTempsrc2 += 3;
+
+        if (*pTempsrc1 == *pTempsrc2)  /* calculate the distances */
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+      }
+    }
+    else
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {
+        *pTempdst++ = *pTempsrc2++;    /* replicate color from bottom */
+        *pTempdst++ = *pTempsrc2++;
+        *pTempdst++ = *pTempsrc2++;
+
+        pTempsrc1 += 3;
+
+        if (*pTempsrc1 == *pTempsrc2)  /* calculate the distances */
+          *pTempdst = *pTempsrc1;
+        else
+          *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) -
+                                                 (mng_int32)(*pTempsrc1) ) + iM) /
+                                     (iM * 2) ) + (mng_int32)(*pTempsrc1) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+      }
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, iWidth << 2);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y5, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_NO_GRAY_SUPPORT
+mng_retcode mng_magnify_g16_x1 (mng_datap  pData,
+                                mng_uint16 iMX,
+                                mng_uint16 iML,
+                                mng_uint16 iMR,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline,
+                                mng_uint8p pDstline)
+{
+  mng_uint32  iX, iS, iM;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X1, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+      iM = iML;
+    else
+    if (iX == (iWidth - 1))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+
+    for (iS = 1; iS < iM; iS++)        /* fill interval */
+    {
+      *pTempdst = *pTempsrc1;          /* copy original source pixel */
+      pTempdst++;
+    }
+
+    pTempsrc1++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_g16_x2 (mng_datap  pData,
+                                mng_uint16 iMX,
+                                mng_uint16 iML,
+                                mng_uint16 iMR,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline,
+                                mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 1;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {                                /* is it same as first ? */
+        if (*pTempsrc1 == *pTempsrc2)
+        {
+          for (iS = 1; iS < iM; iS++)  /* then just repeat the first */
+          {
+            *pTempdst = *pTempsrc1;
+            pTempdst++;
+          }
+        }
+        else
+        {
+          for (iS = 1; iS < iM; iS++)  /* calculate the distances */
+          {
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2))   -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))          ));
+            pTempdst++;
+          }
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_g16_x3 (mng_datap  pData,
+                                mng_uint16 iMX,
+                                mng_uint16 iML,
+                                mng_uint16 iMR,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline,
+                                mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM, iH;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X3, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 1;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {                                /* is it same as first ? */
+        if (*pTempsrc1 == *pTempsrc2)
+        {
+          for (iS = 1; iS < iM; iS++)  /* then just repeat the first */
+          {
+            *pTempdst = *pTempsrc1;
+            pTempdst++;
+          }
+        }
+        else
+        {
+          iH = (iM+1) / 2;             /* calculate halfway point */
+
+          for (iS = 1; iS < iH; iS++)  /* replicate first half */
+          {
+            *pTempdst = *pTempsrc1;
+            pTempdst++;
+          }
+
+          for (iS = iH; iS < iM; iS++) /* replicate second half */
+          {
+            *pTempdst = *pTempsrc2;
+            pTempdst++;
+          }
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1++;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_GRAY_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb16_x1 (mng_datap  pData,
+                                  mng_uint16 iMX,
+                                  mng_uint16 iML,
+                                  mng_uint16 iMR,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32  iX, iS, iM;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X1, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+      iM = iML;
+    else
+    if (iX == (iWidth - 1))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+
+    for (iS = 1; iS < iM; iS++)        /* fill interval */
+    {
+      *pTempdst = *pTempsrc1;          /* copy original source pixel */
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+1);
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+2);
+      pTempdst++;
+    }
+
+    pTempsrc1 += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb16_x2 (mng_datap  pData,
+                                  mng_uint16 iMX,
+                                  mng_uint16 iML,
+                                  mng_uint16 iMR,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 3;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1)))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+2) == *(pTempsrc2+2))
+            *pTempdst = *(pTempsrc1+2);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+2))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2)))         ) );
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb16_x3 (mng_datap  pData,
+                                  mng_uint16 iMX,
+                                  mng_uint16 iML,
+                                  mng_uint16 iMR,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM, iH;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X3, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 3;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* replicate first half */
+        {
+          *pTempdst     = *pTempsrc1;
+          *(pTempdst+1) = *(pTempsrc1+1);
+          *(pTempdst+2) = *(pTempsrc1+2);
+
+          pTempdst += 3;
+        }
+
+        for (iS = iH; iS < iM; iS++)    /* replicate second half */
+        {
+          *pTempdst     = *pTempsrc2;
+          *(pTempdst+1) = *(pTempsrc2+1);
+          *(pTempdst+2) = *(pTempsrc2+2);
+
+          pTempdst += 3;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 3;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_GRAY_SUPPORT
+mng_retcode mng_magnify_ga16_x1 (mng_datap  pData,
+                                 mng_uint16 iMX,
+                                 mng_uint16 iML,
+                                 mng_uint16 iMR,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32  iX, iS, iM;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X1, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p) pSrcline;  /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+      iM = iML;
+    else
+    if (iX == (iWidth - 1))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+
+    for (iS = 1; iS < iM; iS++)        /* fill interval */
+    {
+      *pTempdst = *pTempsrc1;          /* copy original source pixel */
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+1);
+      pTempdst++;
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga16_x2 (mng_datap  pData,
+                                 mng_uint16 iMX,
+                                 mng_uint16 iML,
+                                 mng_uint16 iMR,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 2;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1)))         ) );
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga16_x3 (mng_datap  pData,
+                                 mng_uint16 iMX,
+                                 mng_uint16 iML,
+                                 mng_uint16 iMR,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM, iH;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X3, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 2;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* replicate first half */
+        {
+          *pTempdst     = *pTempsrc1;
+          *(pTempdst+1) = *(pTempsrc1+1);
+
+          pTempdst += 2;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* replicate second half */
+        {
+          *pTempdst     = *pTempsrc2;
+          *(pTempdst+1) = *(pTempsrc2+1);
+
+          pTempdst += 2;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga16_x4 (mng_datap  pData,
+                                 mng_uint16 iMX,
+                                 mng_uint16 iML,
+                                 mng_uint16 iMR,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM, iH;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X4, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 2;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* first half */
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+          pTempdst++;
+
+          *pTempdst = *(pTempsrc1+1);  /* replicate alpha from left */
+
+          pTempdst++;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* second half */
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+          pTempdst++;
+
+          *pTempdst = *(pTempsrc2+1);  /* replicate alpha from right */
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga16_x5 (mng_datap  pData,
+                                 mng_uint16 iMX,
+                                 mng_uint16 iML,
+                                 mng_uint16 iMR,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM, iH;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X5, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 2;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* first half */
+        {
+          *pTempdst = *pTempsrc1;      /* replicate gray from left */
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);/* just repeat the first */
+          else                         /* calculate the distance */
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1)))         ) );
+
+          pTempdst++;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* second half */
+        {
+          *pTempdst = *pTempsrc2;      /* replicate gray from right */
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);/* just repeat the first */
+          else                         /* calculate the distance */
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1)))         ) );
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X5, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_GRAY_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_x1 (mng_datap  pData,
+                                   mng_uint16 iMX,
+                                   mng_uint16 iML,
+                                   mng_uint16 iMR,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline,
+                                   mng_uint8p pDstline)
+{
+  mng_uint32  iX, iS, iM;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X1, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+      iM = iML;
+    else
+    if (iX == (iWidth - 1))            /* last interval ? */
+      iM = iMR;
+    else
+      iM = iMX;
+
+    for (iS = 1; iS < iM; iS++)        /* fill interval */
+    {
+      *pTempdst = *pTempsrc1;          /* copy original source pixel */
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+1);
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+2);
+      pTempdst++;
+      *pTempdst = *(pTempsrc1+3);
+      pTempdst++;
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_x2 (mng_datap  pData,
+                                   mng_uint16 iMX,
+                                   mng_uint16 iML,
+                                   mng_uint16 iMR,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline,
+                                   mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 4;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1)))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+2) == *(pTempsrc2+2))
+            *pTempdst = *(pTempsrc1+2);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+2))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2)))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+3) == *(pTempsrc2+3))
+            *pTempdst = *(pTempsrc1+3);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+3))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3)))         ) );
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+3);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_x3 (mng_datap  pData,
+                                   mng_uint16 iMX,
+                                   mng_uint16 iML,
+                                   mng_uint16 iMR,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline,
+                                   mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM, iH;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X3, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 4;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* replicate first half */
+        {
+          *pTempdst     = *pTempsrc1;
+          *(pTempdst+1) = *(pTempsrc1+1);
+          *(pTempdst+2) = *(pTempsrc1+2);
+          *(pTempdst+3) = *(pTempsrc1+3);
+
+          pTempdst += 4;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* replicate second half */
+        {
+          *pTempdst     = *pTempsrc2;
+          *(pTempdst+1) = *(pTempsrc2+1);
+          *(pTempdst+2) = *(pTempsrc2+2);
+          *(pTempdst+3) = *(pTempsrc2+3);
+
+          pTempdst += 4;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+3);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_x4 (mng_datap  pData,
+                                   mng_uint16 iMX,
+                                   mng_uint16 iML,
+                                   mng_uint16 iMR,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline,
+                                   mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM, iH;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X4, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 4;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* first half */
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1)))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+2) == *(pTempsrc2+2))
+            *pTempdst = *(pTempsrc1+2);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+2))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2)))         ) );
+
+          pTempdst++;
+                                       /* replicate alpha from left */
+          *pTempdst     = *(pTempsrc1+3);
+
+          pTempdst++;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* second half */
+        {
+          if (*pTempsrc1 == *pTempsrc2)
+            *pTempdst = *pTempsrc1;    /* just repeat the first */
+          else                         /* calculate the distance */
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+1) == *(pTempsrc2+1))
+            *pTempdst = *(pTempsrc1+1);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1)))         ) );
+
+          pTempdst++;
+
+          if (*(pTempsrc1+2) == *(pTempsrc2+2))
+            *pTempdst = *(pTempsrc1+2);
+          else
+            mng_put_uint16 ((mng_uint8p)pTempdst,
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+2))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2)))         ) );
+
+          pTempdst++;
+                                       /* replicate alpha from right */
+          *pTempdst     = *(pTempsrc2+3);
+
+          pTempdst++;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+3);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_x5 (mng_datap  pData,
+                                   mng_uint16 iMX,
+                                   mng_uint16 iML,
+                                   mng_uint16 iMR,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline,
+                                   mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_int32   iS, iM, iH;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X5, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline;   /* initialize pixel-loop */
+  pTempdst  = (mng_uint16p)pDstline;
+
+  for (iX = 0; iX < iWidth; iX++)
+  {
+    pTempsrc2 = pTempsrc1 + 4;
+
+    *pTempdst = *pTempsrc1;            /* copy original source pixel */
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+1);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+2);
+    pTempdst++;
+    *pTempdst = *(pTempsrc1+3);
+    pTempdst++;
+
+    if (iX == 0)                       /* first interval ? */
+    {
+      if (iWidth == 1)                 /* single pixel ? */
+        pTempsrc2 = MNG_NULL;
+
+      iM = (mng_int32)iML;
+    }
+    else
+    if (iX == (iWidth - 2))            /* last interval ? */
+      iM = (mng_int32)iMR;
+    else
+      iM = (mng_int32)iMX;
+                                       /* fill interval ? */
+    if ((iX < iWidth - 1) || (iWidth == 1))
+    {
+      if (pTempsrc2)                   /* do we have the second pixel ? */
+      {
+        iH = (iM+1) / 2;               /* calculate halfway point */
+
+        for (iS = 1; iS < iH; iS++)    /* first half */
+        {
+          *pTempdst     = *pTempsrc1;  /* replicate color from left */
+          *(pTempdst+1) = *(pTempsrc1+1);
+          *(pTempdst+2) = *(pTempsrc1+2);
+
+          if (*(pTempsrc1+3) == *(pTempsrc2+3))
+            *(pTempdst+3) = *(pTempsrc1+3);
+          else
+            mng_put_uint16 ((mng_uint8p)(pTempdst+3),
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+3))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3)))         ) );
+
+          pTempdst += 4;
+        }
+
+        for (iS = iH; iS < iM; iS++)   /* second half */
+        {
+          *pTempdst     = *pTempsrc2;  /* replicate color from right */
+          *(pTempdst+1) = *(pTempsrc2+1);
+          *(pTempdst+2) = *(pTempsrc2+2);
+
+          if (*(pTempsrc1+3) == *(pTempsrc2+3))
+            *(pTempdst+3) = *(pTempsrc1+3);
+          else
+            mng_put_uint16 ((mng_uint8p)(pTempdst+3),
+                            (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+3))) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3))) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3)))         ) );
+
+          pTempdst += 4;
+        }
+      }
+      else
+      {
+        for (iS = 1; iS < iM; iS++)
+        {
+          *pTempdst = *pTempsrc1;      /* repeat first source pixel */
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+1);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+2);
+          pTempdst++;
+          *pTempdst = *(pTempsrc1+3);
+          pTempdst++;
+        }
+      }
+    }
+
+    pTempsrc1 += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_GRAY_SUPPORT
+mng_retcode mng_magnify_g16_y1 (mng_datap  pData,
+                                mng_int32  iS,
+                                mng_int32  iM,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline1,
+                                mng_uint8p pSrcline2,
+                                mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y1, MNG_LC_START);
+#endif
+
+  MNG_COPY (pDstline, pSrcline1, (iWidth << 1));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_g16_y2 (mng_datap  pData,
+                                mng_int32  iS,
+                                mng_int32  iM,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline1,
+                                mng_uint8p pSrcline2,
+                                mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline1;  /* initialize pixel-loop */
+  pTempsrc2 = (mng_uint16p)pSrcline2;
+  pTempdst  = (mng_uint16p)pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    for (iX = 0; iX < iWidth; iX++)
+    {                                  /* calculate the distances */
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, (iWidth << 1));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_g16_y3 (mng_datap  pData,
+                                mng_int32  iS,
+                                mng_int32  iM,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline1,
+                                mng_uint8p pSrcline2,
+                                mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y3, MNG_LC_START);
+#endif
+
+  if (pSrcline2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+      MNG_COPY (pDstline, pSrcline1, (iWidth << 1))
+    else
+      MNG_COPY (pDstline, pSrcline2, (iWidth << 1));
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pDstline, pSrcline1, (iWidth << 1));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_GRAY_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb16_y1 (mng_datap  pData,
+                                  mng_int32  iS,
+                                  mng_int32  iM,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline1,
+                                  mng_uint8p pSrcline2,
+                                  mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y1, MNG_LC_START);
+#endif
+
+  MNG_COPY (pDstline, pSrcline1, iWidth * 6);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb16_y2 (mng_datap  pData,
+                                  mng_int32  iS,
+                                  mng_int32  iM,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline1,
+                                  mng_uint8p pSrcline2,
+                                  mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline1;  /* initialize pixel-loop */
+  pTempsrc2 = (mng_uint16p)pSrcline2;
+  pTempdst  = (mng_uint16p)pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    for (iX = 0; iX < iWidth; iX++)
+    {                                  /* calculate the distances */
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, iWidth * 6);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgb16_y3 (mng_datap  pData,
+                                  mng_int32  iS,
+                                  mng_int32  iM,
+                                  mng_uint32 iWidth,
+                                  mng_uint8p pSrcline1,
+                                  mng_uint8p pSrcline2,
+                                  mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y3, MNG_LC_START);
+#endif
+
+  if (pSrcline2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+      MNG_COPY (pDstline, pSrcline1, iWidth * 6)
+    else
+      MNG_COPY (pDstline, pSrcline2, iWidth * 6);
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pDstline, pSrcline1, iWidth * 6);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y3, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_GRAY_SUPPORT
+mng_retcode mng_magnify_ga16_y1 (mng_datap  pData,
+                                 mng_int32  iS,
+                                 mng_int32  iM,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline1,
+                                 mng_uint8p pSrcline2,
+                                 mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y1, MNG_LC_START);
+#endif
+
+  MNG_COPY (pDstline, pSrcline1, (iWidth << 2));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga16_y2 (mng_datap  pData,
+                                 mng_int32  iS,
+                                 mng_int32  iM,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline1,
+                                 mng_uint8p pSrcline2,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline1;  /* initialize pixel-loop */
+  pTempsrc2 = (mng_uint16p)pSrcline2;
+  pTempdst  = (mng_uint16p)pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    for (iX = 0; iX < iWidth; iX++)
+    {                                  /* calculate the distances */
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, (iWidth << 2));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga16_y3 (mng_datap  pData,
+                                mng_int32  iS,
+                                mng_int32  iM,
+                                mng_uint32 iWidth,
+                                mng_uint8p pSrcline1,
+                                mng_uint8p pSrcline2,
+                                mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y3, MNG_LC_START);
+#endif
+
+  if (pSrcline2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+      MNG_COPY (pDstline, pSrcline1, (iWidth << 2))
+    else
+      MNG_COPY (pDstline, pSrcline2, (iWidth << 2));
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pDstline, pSrcline1, (iWidth << 2));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga16_y4 (mng_datap  pData,
+                                 mng_int32  iS,
+                                 mng_int32  iM,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline1,
+                                 mng_uint8p pSrcline2,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y4, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline1;  /* initialize pixel-loop */
+  pTempsrc2 = (mng_uint16p)pSrcline2;
+  pTempdst  = (mng_uint16p)pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {                                /* calculate the distances */
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2 += 2;
+
+        *pTempdst++ = *pTempsrc1++;    /* replicate alpha from top */
+      }
+    }
+    else
+    {
+       for (iX = 0; iX < iWidth; iX++)
+      {                                /* calculate the distances */
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1 += 2;
+        pTempsrc2++;
+
+        *pTempdst++ = *pTempsrc2++;    /* replicate alpha from bottom */
+      }
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, (iWidth << 2));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_ga16_y5 (mng_datap  pData,
+                                 mng_int32  iS,
+                                 mng_int32  iM,
+                                 mng_uint32 iWidth,
+                                 mng_uint8p pSrcline1,
+                                 mng_uint8p pSrcline2,
+                                 mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y5, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline1;  /* initialize pixel-loop */
+  pTempsrc2 = (mng_uint16p)pSrcline2;
+  pTempdst  = (mng_uint16p)pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {
+        *pTempdst = *pTempsrc1;        /* replicate gray from top */
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)  /* calculate the distances */
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+      }
+    }
+    else
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {
+        *pTempdst = *pTempsrc2;        /* replicate gray from bottom */
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)  /* calculate the distances */
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+      }
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, (iWidth << 2));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y5, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_GRAY_SUPPORT */
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_y1 (mng_datap  pData,
+                                   mng_int32  iS,
+                                   mng_int32  iM,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline1,
+                                   mng_uint8p pSrcline2,
+                                   mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y1, MNG_LC_START);
+#endif
+
+  MNG_COPY (pDstline, pSrcline1, (iWidth << 3));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y1, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_y2 (mng_datap  pData,
+                                   mng_int32  iS,
+                                   mng_int32  iM,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline1,
+                                   mng_uint8p pSrcline2,
+                                   mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y2, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline1;  /* initialize pixel-loop */
+  pTempsrc2 = (mng_uint16p)pSrcline2;
+  pTempdst  = (mng_uint16p)pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    for (iX = 0; iX < iWidth; iX++)
+    {                                  /* calculate the distances */
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+
+      if (*pTempsrc1 == *pTempsrc2)
+        *pTempdst = *pTempsrc1;
+      else
+        mng_put_uint16 ((mng_uint8p)pTempdst,
+                        (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                    (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                        (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+      pTempdst++;
+      pTempsrc1++;
+      pTempsrc2++;
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, (iWidth << 3));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_y3 (mng_datap  pData,
+                                   mng_int32  iS,
+                                   mng_int32  iM,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline1,
+                                   mng_uint8p pSrcline2,
+                                   mng_uint8p pDstline)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y3, MNG_LC_START);
+#endif
+
+  if (pSrcline2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+      MNG_COPY (pDstline, pSrcline1, (iWidth << 3))
+    else
+      MNG_COPY (pDstline, pSrcline2, (iWidth << 3));
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pDstline, pSrcline1, (iWidth << 3));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_y4 (mng_datap  pData,
+                                   mng_int32  iS,
+                                   mng_int32  iM,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline1,
+                                   mng_uint8p pSrcline2,
+                                   mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y4, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline1;  /* initialize pixel-loop */
+  pTempsrc2 = (mng_uint16p)pSrcline2;
+  pTempdst  = (mng_uint16p)pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {                                /* calculate the distances */
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2 += 2;
+
+        *pTempdst++ = *pTempsrc1++;    /* replicate alpha from top */
+      }
+    }
+    else
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {                                /* calculate the distances */
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+
+        if (*pTempsrc1 == *pTempsrc2)
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1 += 2;
+        pTempsrc2++;
+
+        *pTempdst++ = *pTempsrc2++;    /* replicate alpha from bottom */
+      }
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, (iWidth << 3));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y4, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_magnify_rgba16_y5 (mng_datap  pData,
+                                   mng_int32  iS,
+                                   mng_int32  iM,
+                                   mng_uint32 iWidth,
+                                   mng_uint8p pSrcline1,
+                                   mng_uint8p pSrcline2,
+                                   mng_uint8p pDstline)
+{
+  mng_uint32  iX;
+  mng_uint16p pTempsrc1;
+  mng_uint16p pTempsrc2;
+  mng_uint16p pTempdst;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y5, MNG_LC_START);
+#endif
+
+  pTempsrc1 = (mng_uint16p)pSrcline1;  /* initialize pixel-loop */
+  pTempsrc2 = (mng_uint16p)pSrcline2;
+  pTempdst  = (mng_uint16p)pDstline;
+
+  if (pTempsrc2)                       /* do we have a second line ? */
+  {
+    if (iS < (iM+1) / 2)               /* top half ? */
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {
+        *pTempdst++ = *pTempsrc1++;    /* replicate color from top */
+        *pTempdst++ = *pTempsrc1++;
+        *pTempdst++ = *pTempsrc1++;
+
+        pTempsrc2 += 3;
+
+        if (*pTempsrc1 == *pTempsrc2)  /* calculate the distances */
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+      }
+    }
+    else
+    {
+      for (iX = 0; iX < iWidth; iX++)
+      {
+        *pTempdst++ = *pTempsrc2++;    /* replicate color from bottom */
+        *pTempdst++ = *pTempsrc2++;
+        *pTempdst++ = *pTempsrc2++;
+
+        pTempsrc1 += 3;
+
+        if (*pTempsrc1 == *pTempsrc2)  /* calculate the distances */
+          *pTempdst = *pTempsrc1;
+        else
+          mng_put_uint16 ((mng_uint8p)pTempdst,
+                          (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) -
+                                                      (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) /
+                                          (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1))         ) );
+
+        pTempdst++;
+        pTempsrc1++;
+        pTempsrc2++;
+      }
+    }
+  }
+  else
+  {                                    /* just repeat the entire line */
+    MNG_COPY (pTempdst, pTempsrc1, (iWidth << 3));
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y5, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_NO_16BIT_SUPPORT */
+#endif /* MNG_OPTIMIZE_FOOTPRINT_MAGN */
+#endif /* MNG_SKIPCHUNK_MAGN */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * PAST composition routines - compose over/under with a target object    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_composeover_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iFGa8, iBGa8;
+  mng_uint8      iCr8, iCg8, iCb8, iCa8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COMPOSEOVER_RGBA8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    iFGa8 = *(pWorkrow+3);       /* get alpha values */
+    iBGa8 = *(pOutrow+3);
+
+    if (iFGa8)                   /* any opacity at all ? */
+    {                            /* fully opaque or background fully transparent ? */
+      if ((iFGa8 == 0xFF) || (iBGa8 == 0))
+      {                          /* then simply copy the values */
+        *pOutrow     = *pWorkrow;
+        *(pOutrow+1) = *(pWorkrow+1);
+        *(pOutrow+2) = *(pWorkrow+2);
+        *(pOutrow+3) = iFGa8;
+      }
+      else
+      {
+        if (iBGa8 == 0xFF)       /* background fully opaque ? */
+        {                        /* do alpha composing */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+          int i;
+          for (i=2; i >= 0; i--)
+          {
+          MNG_COMPOSE8 (*(pOutrow+i), *(pWorkrow+i), iFGa8, *(pOutrow+i));
+          }
+#else
+          MNG_COMPOSE8 (*pOutrow,     *pWorkrow,     iFGa8, *pOutrow    );
+          MNG_COMPOSE8 (*(pOutrow+1), *(pWorkrow+1), iFGa8, *(pOutrow+1));
+          MNG_COMPOSE8 (*(pOutrow+2), *(pWorkrow+2), iFGa8, *(pOutrow+2));
+#endif
+                                 /* alpha remains fully opaque !!! */
+        }
+        else
+        {                        /* here we'll have to blend */
+          MNG_BLEND8 (*pWorkrow, *(pWorkrow+1), *(pWorkrow+2), iFGa8,
+                      *pOutrow, *(pOutrow+1), *(pOutrow+2), iBGa8,
+                      iCr8, iCg8, iCb8, iCa8);
+                                 /* and return the composed values */
+          *pOutrow     = iCr8;
+          *(pOutrow+1) = iCg8;
+          *(pOutrow+2) = iCb8;
+          *(pOutrow+3) = iCa8;
+        }
+      }
+    }
+
+    pOutrow  += 4;
+    pWorkrow += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COMPOSEOVER_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_composeover_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint16p    pWorkrow;
+  mng_uint16p    pOutrow;
+  mng_int32      iX;
+  mng_uint16     iFGa16, iFGr16, iFGg16, iFGb16;
+  mng_uint16     iBGa16, iBGr16, iBGg16, iBGb16;
+  mng_uint16     iCr16, iCg16, iCb16, iCa16;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COMPOSEOVER_RGBA16, MNG_LC_START);
+#endif
+
+  pWorkrow = (mng_uint16p)pData->pRGBArow;
+  pOutrow  = (mng_uint16p)(pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                                            (pData->iCol * pBuf->iSamplesize));
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {                              /* get alpha values */
+    iFGa16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+3));
+    iBGa16 = mng_get_uint16 ((mng_uint8p)(pOutrow+3));
+
+    if (iFGa16)                  /* any opacity at all ? */
+    {                            /* fully opaque or background fully transparent ? */
+      if ((iFGa16 == 0xFFFF) || (iBGa16 == 0))
+      {                          /* then simply copy the values */
+        *pOutrow     = *pWorkrow;
+        *(pOutrow+1) = *(pWorkrow+1);
+        *(pOutrow+2) = *(pWorkrow+2);
+        *(pOutrow+3) = *(pWorkrow+3);
+      }
+      else
+      {                          /* get color values */
+        iFGr16 = mng_get_uint16 ((mng_uint8p)pWorkrow);
+        iFGg16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+1));
+        iFGb16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+2));
+        iBGr16 = mng_get_uint16 ((mng_uint8p)pOutrow);
+        iBGg16 = mng_get_uint16 ((mng_uint8p)(pOutrow+1));
+        iBGb16 = mng_get_uint16 ((mng_uint8p)(pOutrow+2));
+
+        if (iBGa16 == 0xFFFF)    /* background fully opaque ? */
+        {                        /* do alpha composing */
+          MNG_COMPOSE16 (iFGr16, iFGr16, iFGa16, iBGr16);
+          MNG_COMPOSE16 (iFGg16, iFGg16, iFGa16, iBGg16);
+          MNG_COMPOSE16 (iFGb16, iFGb16, iFGa16, iBGb16);
+
+          mng_put_uint16 ((mng_uint8p)pOutrow,     iFGr16);
+          mng_put_uint16 ((mng_uint8p)(pOutrow+1), iFGg16);
+          mng_put_uint16 ((mng_uint8p)(pOutrow+2), iFGb16);
+                                 /* alpha remains fully opaque !!! */
+        }
+        else
+        {                        /* here we'll have to blend */
+          MNG_BLEND16 (iFGr16, iFGg16, iFGb16, iFGa16,
+                       iBGr16, iBGg16, iBGb16, iBGa16,
+                       iCr16,  iCg16,  iCb16,  iCa16);
+                                 /* and return the composed values */
+          mng_put_uint16 ((mng_uint8p)pOutrow,     iCr16);
+          mng_put_uint16 ((mng_uint8p)(pOutrow+1), iCg16);
+          mng_put_uint16 ((mng_uint8p)(pOutrow+2), iCb16);
+          mng_put_uint16 ((mng_uint8p)(pOutrow+3), iCa16);
+        }
+      }
+    }
+
+    pOutrow  += 4;
+    pWorkrow += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COMPOSEOVER_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_composeunder_rgba8 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint8p     pWorkrow;
+  mng_uint8p     pOutrow;
+  mng_int32      iX;
+  mng_uint8      iFGa8, iBGa8;
+  mng_uint8      iCr8, iCg8, iCb8, iCa8;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COMPOSEUNDER_RGBA8, MNG_LC_START);
+#endif
+
+  pWorkrow = pData->pRGBArow;
+  pOutrow  = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                              (pData->iCol * pBuf->iSamplesize);
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {
+    iFGa8 = *(pOutrow+3);        /* get alpha values */
+    iBGa8 = *(pWorkrow+3);
+                                 /* anything to do at all ? */
+    if ((iBGa8) && (iFGa8 != 0xFF))
+    {
+      if (iBGa8 == 0xFF)         /* background fully opaque ? */
+      {                          /* do alpha composing */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE
+        int i;
+        for (i=2; i >= 0; i--)
+        {
+        MNG_COMPOSE8 (*(pOutrow+i), *(pOutrow+i), iFGa8, *(pWorkrow+i));
+        }
+#else
+        MNG_COMPOSE8 (*pOutrow,     *pOutrow,     iFGa8, *pWorkrow    );
+        MNG_COMPOSE8 (*(pOutrow+1), *(pOutrow+1), iFGa8, *(pWorkrow+1));
+        MNG_COMPOSE8 (*(pOutrow+2), *(pOutrow+2), iFGa8, *(pWorkrow+2));
+#endif
+        *(pOutrow+3) = 0xFF;     /* alpha becomes fully opaque !!! */
+      }
+      else
+      {                          /* here we'll have to blend */
+        MNG_BLEND8 (*pOutrow, *(pOutrow+1), *(pOutrow+2), iFGa8,
+                    *pWorkrow, *(pWorkrow+1), *(pWorkrow+2), iBGa8,
+                    iCr8, iCg8, iCb8, iCa8);
+                                 /* and return the composed values */
+        *pOutrow     = iCr8;
+        *(pOutrow+1) = iCg8;
+        *(pOutrow+2) = iCb8;
+        *(pOutrow+3) = iCa8;
+      }
+    }
+
+    pOutrow  += 4;
+    pWorkrow += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COMPOSEUNDER_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_composeunder_rgba16 (mng_datap pData)
+{
+  mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf;
+  mng_uint16p    pWorkrow;
+  mng_uint16p    pOutrow;
+  mng_int32      iX;
+  mng_uint16     iFGa16, iFGr16, iFGg16, iFGb16;
+  mng_uint16     iBGa16, iBGr16, iBGg16, iBGb16;
+  mng_uint16     iCr16, iCg16, iCb16, iCa16;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COMPOSEUNDER_RGBA16, MNG_LC_START);
+#endif
+
+  pWorkrow = (mng_uint16p)pData->pRGBArow;
+  pOutrow  = (mng_uint16p)(pBuf->pImgdata + (pData->iRow * pBuf->iRowsize   ) +
+                                            (pData->iCol * pBuf->iSamplesize));
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {                              /* get alpha values */
+    iFGa16 = mng_get_uint16 ((mng_uint8p)(pOutrow+3));
+    iBGa16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+3));
+                                 /* anything to do at all ? */
+    if ((iBGa16) && (iFGa16 != 0xFFFF))
+    {
+      iFGr16 = mng_get_uint16 ((mng_uint8p)pOutrow);
+      iFGg16 = mng_get_uint16 ((mng_uint8p)(pOutrow+1));
+      iFGb16 = mng_get_uint16 ((mng_uint8p)(pOutrow+2));
+      iBGr16 = mng_get_uint16 ((mng_uint8p)pWorkrow);
+      iBGg16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+1));
+      iBGb16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+2));
+
+      if (iBGa16 == 0xFFFF)      /* background fully opaque ? */
+      {                          /* do alpha composing */
+        MNG_COMPOSE16 (iFGr16, iFGr16, iFGa16, iBGr16);
+        MNG_COMPOSE16 (iFGg16, iFGg16, iFGa16, iBGg16);
+        MNG_COMPOSE16 (iFGb16, iFGb16, iFGa16, iBGb16);
+
+        mng_put_uint16 ((mng_uint8p)pOutrow,     iFGr16);
+        mng_put_uint16 ((mng_uint8p)(pOutrow+1), iFGg16);
+        mng_put_uint16 ((mng_uint8p)(pOutrow+2), iFGb16);
+        *(pOutrow+3) = 0xFFFF;   /* alpha becomes fully opaque !!! */
+      }
+      else
+      {                          /* here we'll have to blend */
+        MNG_BLEND16 (iFGr16, iFGg16, iFGb16, iFGa16,
+                     iBGr16, iBGg16, iBGb16, iBGa16,
+                     iCr16,  iCg16,  iCb16,  iCa16);
+                                 /* and return the composed values */
+        mng_put_uint16 ((mng_uint8p)pOutrow,     iCr16);
+        mng_put_uint16 ((mng_uint8p)(pOutrow+1), iCg16);
+        mng_put_uint16 ((mng_uint8p)(pOutrow+2), iCb16);
+        mng_put_uint16 ((mng_uint8p)(pOutrow+3), iCa16);
+      }
+    }
+
+    pOutrow  += 4;
+    pWorkrow += 4;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_COMPOSEUNDER_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * PAST flip & tile routines - flip or tile a row of pixels               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_flip_rgba8 (mng_datap pData)
+{
+  mng_uint32p pWorkrow;
+  mng_uint32p pOutrow;
+  mng_int32   iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FLIP_RGBA8, MNG_LC_START);
+#endif
+                                       /* setup temp pointers */
+  pWorkrow        = (mng_uint32p)pData->pRGBArow + pData->iRowsamples - 1;
+  pOutrow         = (mng_uint32p)pData->pWorkrow;
+                                       /* swap original buffers */
+  pData->pWorkrow = pData->pRGBArow;
+  pData->pRGBArow = (mng_uint8p)pOutrow;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {                                    /* let's flip */
+    *pOutrow = *pWorkrow;
+    pOutrow++;
+    pWorkrow--;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FLIP_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_flip_rgba16 (mng_datap pData)
+{
+  mng_uint32p pWorkrow;
+  mng_uint32p pOutrow;
+  mng_int32   iX;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FLIP_RGBA16, MNG_LC_START);
+#endif
+                                       /* setup temp pointers */
+  pWorkrow        = (mng_uint32p)pData->pRGBArow + ((pData->iRowsamples - 1) << 1);
+  pOutrow         = (mng_uint32p)pData->pWorkrow;
+                                       /* swap original buffers */
+  pData->pWorkrow = pData->pRGBArow;
+  pData->pRGBArow = (mng_uint8p)pOutrow;
+
+#ifdef MNG_DECREMENT_LOOPS
+  for (iX = pData->iRowsamples; iX > 0; iX--)
+#else
+  for (iX = 0; iX < pData->iRowsamples; iX++)
+#endif
+  {                                    /* let's flip */
+    *pOutrow       = *pWorkrow;
+    *(pOutrow + 1) = *(pWorkrow + 1);
+
+    pOutrow  += 2;
+    pWorkrow -= 2;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_FLIP_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode mng_tile_rgba8 (mng_datap pData)
+{
+  mng_uint32p pWorkrow;
+  mng_uint32p pOutrow;
+  mng_int32   iX;
+  mng_uint32  iZ, iMax;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_TILE_RGBA8, MNG_LC_START);
+#endif
+
+  iZ              = pData->iSourcel;   /* indent ? */
+                                       /* what's our source-length */
+  iMax            = ((mng_imagep)pData->pRetrieveobj)->pImgbuf->iWidth;
+                                       /* setup temp pointers */
+  pWorkrow        = (mng_uint32p)pData->pRGBArow + iZ;
+  pOutrow         = (mng_uint32p)pData->pWorkrow;
+                                       /* swap original buffers */
+  pData->pWorkrow = pData->pRGBArow;
+  pData->pRGBArow = (mng_uint8p)pOutrow;
+
+  for (iX = pData->iDestl; iX < pData->iDestr; iX++)
+  {                                    /* tiiiile */
+    *pOutrow = *pWorkrow;
+
+    pWorkrow++;
+    pOutrow++;
+    iZ++;
+
+    if (iZ >= iMax)                    /* end of source ? */
+    {
+      iZ       = 0;
+      pWorkrow = (mng_uint32p)pData->pWorkrow;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_TILE_RGBA8, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_tile_rgba16 (mng_datap pData)
+{
+  mng_uint32p pWorkrow;
+  mng_uint32p pOutrow;
+  mng_int32   iX;
+  mng_uint32  iZ, iMax;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_TILE_RGBA16, MNG_LC_START);
+#endif
+
+  iZ              = pData->iSourcel;   /* indent ? */
+                                       /* what's our source-length */
+  iMax            = ((mng_imagep)pData->pRetrieveobj)->pImgbuf->iWidth;
+                                       /* setup temp pointers */
+  pWorkrow        = (mng_uint32p)pData->pRGBArow + (iZ << 1);
+  pOutrow         = (mng_uint32p)pData->pWorkrow;
+                                       /* swap original buffers */
+  pData->pWorkrow = pData->pRGBArow;
+  pData->pRGBArow = (mng_uint8p)pOutrow;
+
+  for (iX = pData->iDestl; iX < pData->iDestr; iX++)
+  {                                    /* tiiiile */
+    *pOutrow       = *pWorkrow;
+    *(pOutrow + 1) = *(pWorkrow + 1);
+
+    pWorkrow += 2;
+    pOutrow  += 2;
+    iZ++;
+
+    if (iZ >= iMax)                    /* end of source ? */
+    {
+      iZ       = 0;
+      pWorkrow = (mng_uint32p)pData->pWorkrow;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_TILE_RGBA16, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SKIPCHUNK_PAST */
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_DISPLAY_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_pixels.h b/files/Source/LibMNG/libmng_pixels.h
new file mode 100644
index 0000000..5a0281e
--- /dev/null
+++ b/files/Source/LibMNG/libmng_pixels.h
@@ -0,0 +1,1147 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_pixels.h           copyright (c) 2000-2006 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Pixel-row management routines (definition)                 * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the pixel-row management routines            * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/22/2000 - G.Juyn                                * */
+/* *             - added some JNG definitions                               * */
+/* *             - added delta-image row-processing routines                * */
+/* *             0.5.2 - 06/05/2000 - G.Juyn                                * */
+/* *             - added support for RGB8_A8 canvasstyle                    * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/16/2000 - G.Juyn                                * */
+/* *             - changed progressive-display processing                   * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN support                                       * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added optional support for bKGD for PNG images           * */
+/* *             - added support for JDAA                                   * */
+/* *             0.9.3 - 10/19/2000 - G.Juyn                                * */
+/* *             - implemented delayed delta-processing                     * */
+/* *                                                                        * */
+/* *             0.9.4 -  1/18/2001 - G.Juyn                                * */
+/* *             - added "new" MAGN methods 3, 4 & 5                        * */
+/* *                                                                        * */
+/* *             1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly)              * */
+/* *             - added BGRA8 canvas with premultiplied alpha              * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed PROM support                                   * */
+/* *             - completed delta-image support                            * */
+/* *             1.0.5 - 08/16/2002 - G.Juyn                                * */
+/* *             - completed MAGN support (16-bit functions)                * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/22/2002 - G.Juyn                                * */
+/* *             - added bgrx8 canvas (filler byte)                         * */
+/* *             1.0.5 - 09/23/2002 - G.Juyn                                * */
+/* *             - added compose over/under routines for PAST processing    * */
+/* *             - added flip & tile routines for PAST processing           * */
+/* *                                                                        * */
+/* *             1.0.6 - 03/09/2003 - G.Juyn                                * */
+/* *             - hiding 12-bit JPEG stuff                                 * */
+/* *             1.0.6 - 05/11/2003 - G. Juyn                               * */
+/* *             - added conditionals around canvas update routines         * */
+/* *             1.0.6 - 06/09/2003 - G. R-P                                * */
+/* *             - added conditionals around 8-bit magn routines            * */
+/* *             1.0.6 - 07/07/2003 - G. R-P                                * */
+/* *             - removed conditionals around 8-bit magn routines          * */
+/* *             - added conditionals around 16-bit and delta-PNG           * */
+/* *               supporting code                                          * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added SKIPCHUNK conditionals around PAST chunk support   * */
+/* *             1.0.6 - 08/18/2003 - G.R-P                                 * */
+/* *             - added conditionals around 1, 2, and 4-bit prototypes     * */
+/* *                                                                        * */
+/* *             1.0.7 - 11/27/2003 - R.A                                   * */
+/* *             - added CANVAS_RGB565 and CANVAS_BGR565                    * */
+/* *             1.0.7 - 12/06/2003 - R.A                                   * */
+/* *             - added CANVAS_RGBA565 and CANVAS_BGRA565                  * */
+/* *             1.0.7 - 01/25/2004 - J.S                                   * */
+/* *             - added premultiplied alpha canvas' for RGBA, ARGB, ABGR   * */
+/* *                                                                        * */
+/* *             1.0.9 - 10/10/2004 - G.R-P.                                * */
+/* *             - added MNG_NO_1_2_4BIT_SUPPORT                            * */
+/* *             1.0.9 - 10/14/2004 - G.Juyn                                * */
+/* *             - added bgr565_a8 canvas-style (thanks to J. Elvander)     * */
+/* *                                                                        * */
+/* *             1.0.10 - 03/07/2006 - (thanks to W. Manthey)               * */
+/* *             - added CANVAS_RGB555 and CANVAS_BGR555                    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_pixels_h_
+#define _libmng_pixels_h_
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Progressive display check - checks to see if progressive display is    * */
+/* * in order & indicates so                                                * */
+/* *                                                                        * */
+/* * The routine is called after a call to one of the display_xxx routines  * */
+/* * if appropriate                                                         * */
+/* *                                                                        * */
+/* * The refresh is warrented in the read_chunk routine (mng_read.c)        * */
+/* * and only during read&display processing, since there's not much point  * */
+/* * doing it from memory!                                                  * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_display_progressive_check (mng_datap pData);
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Display routines - convert rowdata (which is already color-corrected)  * */
+/* * to the output canvas, respecting any transparency information          * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCANVAS_RGB8
+mng_retcode mng_display_rgb8           (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_RGBA8
+mng_retcode mng_display_rgba8          (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_RGBA8_PM
+mng_retcode mng_display_rgba8_pm       (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_ARGB8
+mng_retcode mng_display_argb8          (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_ARGB8_PM
+mng_retcode mng_display_argb8_pm       (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_RGB8_A8
+mng_retcode mng_display_rgb8_a8        (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGR8
+mng_retcode mng_display_bgr8           (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGRX8
+mng_retcode mng_display_bgrx8          (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGRA8
+mng_retcode mng_display_bgra8          (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGRA8_PM
+mng_retcode mng_display_bgra8_pm       (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_ABGR8
+mng_retcode mng_display_abgr8          (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_ABGR8_PM
+mng_retcode mng_display_abgr8_pm       (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_RGB565
+mng_retcode mng_display_rgb565         (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_RGBA565
+mng_retcode mng_display_rgba565        (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGR565
+mng_retcode mng_display_bgr565         (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGRA565
+mng_retcode mng_display_bgra565        (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGR565_A8
+mng_retcode mng_display_bgr565_a8      (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_RGB555
+mng_retcode mng_display_rgb555         (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGR555
+mng_retcode mng_display_bgr555         (mng_datap  pData);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Background restore routines - restore the background with info from    * */
+/* * the BACK and/or bKGD chunk and/or the app's background canvas          * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_restore_bkgd_backimage (mng_datap  pData);
+mng_retcode mng_restore_bkgd_backcolor (mng_datap  pData);
+mng_retcode mng_restore_bkgd_bkgd      (mng_datap  pData);
+mng_retcode mng_restore_bkgd_bgcolor   (mng_datap  pData);
+#ifndef MNG_SKIPCANVAS_RGB8
+mng_retcode mng_restore_bkgd_rgb8      (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGR8
+mng_retcode mng_restore_bkgd_bgr8      (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGRX8
+mng_retcode mng_restore_bkgd_bgrx8     (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_RGB565
+mng_retcode mng_restore_bkgd_rgb565    (mng_datap  pData);
+#endif
+#ifndef MNG_SKIPCANVAS_BGR565
+mng_retcode mng_restore_bkgd_bgr565    (mng_datap  pData);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row retrieval routines - retrieve processed & uncompressed row-data    * */
+/* * from the current "object"                                              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_retrieve_g8            (mng_datap  pData);
+mng_retcode mng_retrieve_rgb8          (mng_datap  pData);
+mng_retcode mng_retrieve_idx8          (mng_datap  pData);
+mng_retcode mng_retrieve_ga8           (mng_datap  pData);
+mng_retcode mng_retrieve_rgba8         (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_retrieve_g16           (mng_datap  pData);
+mng_retcode mng_retrieve_ga16          (mng_datap  pData);
+mng_retcode mng_retrieve_rgb16         (mng_datap  pData);
+mng_retcode mng_retrieve_rgba16        (mng_datap  pData);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row storage routines - store processed & uncompressed row-data         * */
+/* * into the current "object"                                              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_store_g1               (mng_datap  pData);
+mng_retcode mng_store_g2               (mng_datap  pData);
+mng_retcode mng_store_g4               (mng_datap  pData);
+mng_retcode mng_store_idx1             (mng_datap  pData);
+mng_retcode mng_store_idx2             (mng_datap  pData);
+mng_retcode mng_store_idx4             (mng_datap  pData);
+#endif
+mng_retcode mng_store_idx8             (mng_datap  pData);
+mng_retcode mng_store_rgb8             (mng_datap  pData);
+mng_retcode mng_store_g8               (mng_datap  pData);
+mng_retcode mng_store_ga8              (mng_datap  pData);
+mng_retcode mng_store_rgba8            (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_g16              (mng_datap  pData);
+mng_retcode mng_store_ga16             (mng_datap  pData);
+mng_retcode mng_store_rgb16            (mng_datap  pData);
+mng_retcode mng_store_rgba16           (mng_datap  pData);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row storage routines (JPEG) - store processed & uncompressed row-data  * */
+/* * into the current "object"                                              * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+mng_retcode mng_store_jpeg_g8          (mng_datap  pData);
+mng_retcode mng_store_jpeg_rgb8        (mng_datap  pData);
+mng_retcode mng_store_jpeg_ga8         (mng_datap  pData);
+mng_retcode mng_store_jpeg_rgba8       (mng_datap  pData);
+
+#ifdef MNG_SUPPORT_JPEG12
+mng_retcode mng_store_jpeg_g12         (mng_datap  pData);
+mng_retcode mng_store_jpeg_rgb12       (mng_datap  pData);
+mng_retcode mng_store_jpeg_ga12        (mng_datap  pData);
+mng_retcode mng_store_jpeg_rgba12      (mng_datap  pData);
+#endif
+
+mng_retcode mng_store_jpeg_g8_alpha    (mng_datap  pData);
+mng_retcode mng_store_jpeg_rgb8_alpha  (mng_datap  pData);
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_store_jpeg_g8_a1       (mng_datap  pData);
+mng_retcode mng_store_jpeg_g8_a2       (mng_datap  pData);
+mng_retcode mng_store_jpeg_g8_a4       (mng_datap  pData);
+#endif
+mng_retcode mng_store_jpeg_g8_a8       (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_jpeg_g8_a16      (mng_datap  pData);
+#endif
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_store_jpeg_rgb8_a1     (mng_datap  pData);
+mng_retcode mng_store_jpeg_rgb8_a2     (mng_datap  pData);
+mng_retcode mng_store_jpeg_rgb8_a4     (mng_datap  pData);
+#endif
+mng_retcode mng_store_jpeg_rgb8_a8     (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_jpeg_rgb8_a16    (mng_datap  pData);
+#endif
+
+#ifdef MNG_SUPPORT_JPEG12
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_store_jpeg_g12_a1      (mng_datap  pData);
+mng_retcode mng_store_jpeg_g12_a2      (mng_datap  pData);
+mng_retcode mng_store_jpeg_g12_a4      (mng_datap  pData);
+#endif
+mng_retcode mng_store_jpeg_g12_a8      (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_jpeg_g12_a16     (mng_datap  pData);
+#endif
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_store_jpeg_rgb12_a1    (mng_datap  pData);
+mng_retcode mng_store_jpeg_rgb12_a2    (mng_datap  pData);
+mng_retcode mng_store_jpeg_rgb12_a4    (mng_datap  pData);
+#endif
+mng_retcode mng_store_jpeg_rgb12_a8    (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_store_jpeg_rgb12_a16   (mng_datap  pData);
+#endif
+#endif
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image row routines - apply the processed & uncompressed row-data * */
+/* * onto the target "object"                                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_delta_g1               (mng_datap  pData);
+mng_retcode mng_delta_g2               (mng_datap  pData);
+mng_retcode mng_delta_g4               (mng_datap  pData);
+#endif
+mng_retcode mng_delta_g8               (mng_datap  pData);
+mng_retcode mng_delta_g16              (mng_datap  pData);
+mng_retcode mng_delta_rgb8             (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_rgb16            (mng_datap  pData);
+#endif
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_delta_idx1             (mng_datap  pData);
+mng_retcode mng_delta_idx2             (mng_datap  pData);
+mng_retcode mng_delta_idx4             (mng_datap  pData);
+#endif
+mng_retcode mng_delta_idx8             (mng_datap  pData);
+mng_retcode mng_delta_ga8              (mng_datap  pData);
+mng_retcode mng_delta_rgba8            (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_ga16             (mng_datap  pData);
+mng_retcode mng_delta_rgba16           (mng_datap  pData);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image row routines - apply the source row onto the target        * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_delta_g1_g1            (mng_datap  pData);
+mng_retcode mng_delta_g2_g2            (mng_datap  pData);
+mng_retcode mng_delta_g4_g4            (mng_datap  pData);
+#endif
+mng_retcode mng_delta_g8_g8            (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_g16_g16          (mng_datap  pData);
+#endif
+mng_retcode mng_delta_ga8_ga8          (mng_datap  pData);
+mng_retcode mng_delta_ga8_g8           (mng_datap  pData);
+mng_retcode mng_delta_ga8_a8           (mng_datap  pData);
+mng_retcode mng_delta_rgba8_rgb8       (mng_datap  pData);
+mng_retcode mng_delta_rgba8_a8         (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_ga16_ga16        (mng_datap  pData);
+mng_retcode mng_delta_ga16_g16         (mng_datap  pData);
+mng_retcode mng_delta_ga16_a16         (mng_datap  pData);
+mng_retcode mng_delta_rgba16_a16       (mng_datap  pData);
+mng_retcode mng_delta_rgba16_rgb16     (mng_datap  pData);
+#endif
+#endif /* MNG_NO_DELTA_PNG */
+mng_retcode mng_delta_rgb8_rgb8        (mng_datap  pData); /* Used for PAST */
+mng_retcode mng_delta_rgba8_rgba8      (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_delta_rgb16_rgb16      (mng_datap  pData);
+mng_retcode mng_delta_rgba16_rgba16    (mng_datap  pData);
+#endif
+
+#ifndef MNG_NO_DELTA_PNG
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image row routines - scale the delta to bitdepth of target       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_scale_g1_g2            (mng_datap  pData);
+mng_retcode mng_scale_g1_g4            (mng_datap  pData);
+mng_retcode mng_scale_g1_g8            (mng_datap  pData);
+mng_retcode mng_scale_g2_g4            (mng_datap  pData);
+mng_retcode mng_scale_g2_g8            (mng_datap  pData);
+mng_retcode mng_scale_g4_g8            (mng_datap  pData);
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_scale_g1_g16           (mng_datap  pData);
+mng_retcode mng_scale_g2_g16           (mng_datap  pData);
+mng_retcode mng_scale_g4_g16           (mng_datap  pData);
+#endif
+mng_retcode mng_scale_g8_g16           (mng_datap  pData);
+mng_retcode mng_scale_ga8_ga16         (mng_datap  pData);
+mng_retcode mng_scale_rgb8_rgb16       (mng_datap  pData);
+mng_retcode mng_scale_rgba8_rgba16     (mng_datap  pData);
+#endif
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_scale_g2_g1            (mng_datap  pData);
+mng_retcode mng_scale_g4_g1            (mng_datap  pData);
+mng_retcode mng_scale_g8_g1            (mng_datap  pData);
+mng_retcode mng_scale_g4_g2            (mng_datap  pData);
+mng_retcode mng_scale_g8_g2            (mng_datap  pData);
+mng_retcode mng_scale_g8_g4            (mng_datap  pData);
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_scale_g16_g1           (mng_datap  pData);
+mng_retcode mng_scale_g16_g2           (mng_datap  pData);
+mng_retcode mng_scale_g16_g4           (mng_datap  pData);
+#endif
+mng_retcode mng_scale_g16_g8           (mng_datap  pData);
+mng_retcode mng_scale_ga16_ga8         (mng_datap  pData);
+mng_retcode mng_scale_rgb16_rgb8       (mng_datap  pData);
+mng_retcode mng_scale_rgba16_rgba8     (mng_datap  pData);
+#endif
+#endif /* MNG_NO_DELTA_PNG */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image bit routines - promote bit_depth                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_uint8   mng_promote_replicate_1_2  (mng_uint8  iB);
+mng_uint8   mng_promote_replicate_1_4  (mng_uint8  iB);
+mng_uint8   mng_promote_replicate_1_8  (mng_uint8  iB);
+mng_uint8   mng_promote_replicate_2_4  (mng_uint8  iB);
+mng_uint8   mng_promote_replicate_2_8  (mng_uint8  iB);
+mng_uint8   mng_promote_replicate_4_8  (mng_uint8  iB);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16  mng_promote_replicate_1_16 (mng_uint8  iB);
+mng_uint16  mng_promote_replicate_2_16 (mng_uint8  iB);
+mng_uint16  mng_promote_replicate_4_16 (mng_uint8  iB);
+mng_uint16  mng_promote_replicate_8_16 (mng_uint8  iB);
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DELTA_PNG
+mng_uint8   mng_promote_zerofill_1_2   (mng_uint8  iB);
+mng_uint8   mng_promote_zerofill_1_4   (mng_uint8  iB);
+mng_uint8   mng_promote_zerofill_1_8   (mng_uint8  iB);
+mng_uint8   mng_promote_zerofill_2_4   (mng_uint8  iB);
+mng_uint8   mng_promote_zerofill_2_8   (mng_uint8  iB);
+mng_uint8   mng_promote_zerofill_4_8   (mng_uint8  iB);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_uint16  mng_promote_zerofill_1_16  (mng_uint8  iB);
+mng_uint16  mng_promote_zerofill_2_16  (mng_uint8  iB);
+mng_uint16  mng_promote_zerofill_4_16  (mng_uint8  iB);
+mng_uint16  mng_promote_zerofill_8_16  (mng_uint8  iB);
+#endif
+#endif /* MNG_NO_DELTA_PNG */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Delta-image row routines - promote color_type                          * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_promote_g8_g8          (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g8_g16         (mng_datap  pData);
+mng_retcode mng_promote_g16_g16        (mng_datap  pData);
+#endif
+
+mng_retcode mng_promote_g8_ga8         (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g8_ga16        (mng_datap  pData);
+mng_retcode mng_promote_g16_ga16       (mng_datap  pData);
+#endif
+
+mng_retcode mng_promote_g8_rgb8        (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g8_rgb16       (mng_datap  pData);
+mng_retcode mng_promote_g16_rgb16      (mng_datap  pData);
+#endif
+
+mng_retcode mng_promote_g8_rgba8       (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_g8_rgba16      (mng_datap  pData);
+mng_retcode mng_promote_g16_rgba16     (mng_datap  pData);
+
+mng_retcode mng_promote_ga8_ga16       (mng_datap  pData);
+#endif
+
+mng_retcode mng_promote_ga8_rgba8      (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_ga8_rgba16     (mng_datap  pData);
+mng_retcode mng_promote_ga16_rgba16    (mng_datap  pData);
+#endif
+
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_rgb8_rgb16     (mng_datap  pData);
+#endif
+
+mng_retcode mng_promote_rgb8_rgba8     (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_rgb8_rgba16    (mng_datap  pData);
+mng_retcode mng_promote_rgb16_rgba16   (mng_datap  pData);
+#endif
+
+mng_retcode mng_promote_idx8_rgb8      (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_idx8_rgb16     (mng_datap  pData);
+#endif
+
+mng_retcode mng_promote_idx8_rgba8     (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_promote_idx8_rgba16    (mng_datap  pData);
+
+mng_retcode mng_promote_rgba8_rgba16   (mng_datap  pData);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row processing routines - convert uncompressed data from zlib to       * */
+/* * managable row-data which serves as input to the color-management       * */
+/* * routines                                                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_process_g1             (mng_datap  pData);
+mng_retcode mng_process_g2             (mng_datap  pData);
+mng_retcode mng_process_g4             (mng_datap  pData);
+#endif
+mng_retcode mng_process_g8             (mng_datap  pData);
+mng_retcode mng_process_rgb8           (mng_datap  pData);
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_process_idx1           (mng_datap  pData);
+mng_retcode mng_process_idx2           (mng_datap  pData);
+mng_retcode mng_process_idx4           (mng_datap  pData);
+#endif
+mng_retcode mng_process_idx8           (mng_datap  pData);
+mng_retcode mng_process_ga8            (mng_datap  pData);
+mng_retcode mng_process_rgba8          (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_process_g16            (mng_datap  pData);
+mng_retcode mng_process_ga16           (mng_datap  pData);
+mng_retcode mng_process_rgb16          (mng_datap  pData);
+mng_retcode mng_process_rgba16         (mng_datap  pData);
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row processing initialization routines - set up the variables needed   * */
+/* * to process uncompressed row-data                                       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_init_g1_i              (mng_datap  pData);
+mng_retcode mng_init_g2_i              (mng_datap  pData);
+mng_retcode mng_init_g4_i              (mng_datap  pData);
+#endif
+mng_retcode mng_init_g8_i              (mng_datap  pData);
+mng_retcode mng_init_rgb8_i            (mng_datap  pData);
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_init_idx1_i            (mng_datap  pData);
+mng_retcode mng_init_idx2_i            (mng_datap  pData);
+mng_retcode mng_init_idx4_i            (mng_datap  pData);
+#endif
+mng_retcode mng_init_idx8_i            (mng_datap  pData);
+mng_retcode mng_init_ga8_i             (mng_datap  pData);
+mng_retcode mng_init_rgba8_i           (mng_datap  pData);
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_init_g1_ni             (mng_datap  pData);
+mng_retcode mng_init_g2_ni             (mng_datap  pData);
+mng_retcode mng_init_g4_ni             (mng_datap  pData);
+#endif
+mng_retcode mng_init_g8_ni             (mng_datap  pData);
+mng_retcode mng_init_rgb8_ni           (mng_datap  pData);
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_init_idx1_ni           (mng_datap  pData);
+mng_retcode mng_init_idx2_ni           (mng_datap  pData);
+mng_retcode mng_init_idx4_ni           (mng_datap  pData);
+#endif
+mng_retcode mng_init_idx8_ni           (mng_datap  pData);
+mng_retcode mng_init_ga8_ni            (mng_datap  pData);
+mng_retcode mng_init_rgba8_ni          (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_g16_i             (mng_datap  pData);
+mng_retcode mng_init_rgb16_i           (mng_datap  pData);
+mng_retcode mng_init_ga16_i            (mng_datap  pData);
+mng_retcode mng_init_rgba16_i          (mng_datap  pData);
+mng_retcode mng_init_g16_ni            (mng_datap  pData);
+mng_retcode mng_init_rgb16_ni          (mng_datap  pData);
+mng_retcode mng_init_ga16_ni           (mng_datap  pData);
+mng_retcode mng_init_rgba16_ni         (mng_datap  pData);
+#endif
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Row processing initialization routines (JPEG) - set up the variables   * */
+/* * needed to process uncompressed row-data                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT
+#ifdef MNG_INCLUDE_JNG
+#ifndef MNG_NO_1_2_4BIT_SUPPORT
+mng_retcode mng_init_jpeg_a1_ni        (mng_datap  pData);
+mng_retcode mng_init_jpeg_a2_ni        (mng_datap  pData);
+mng_retcode mng_init_jpeg_a4_ni        (mng_datap  pData);
+#endif
+mng_retcode mng_init_jpeg_a8_ni        (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_init_jpeg_a16_ni       (mng_datap  pData);
+#endif
+#endif
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * General row processing routines                                        * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_init_rowproc           (mng_datap  pData);
+mng_retcode mng_next_row               (mng_datap  pData);
+#ifdef MNG_INCLUDE_JNG
+mng_retcode mng_next_jpeg_alpharow     (mng_datap  pData);
+mng_retcode mng_next_jpeg_row          (mng_datap  pData);
+#endif
+mng_retcode mng_cleanup_rowproc        (mng_datap  pData);
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Magnification row routines - apply magnification transforms            * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN
+mng_retcode mng_magnify_g8_x1          (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_g8_x2          (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_g8_x3          (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb8_x1        (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb8_x2        (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb8_x3        (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_x1         (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_x2         (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_x3         (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_x4         (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_x5         (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+#endif
+mng_retcode mng_magnify_rgba8_x1       (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba8_x2       (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba8_x3       (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba8_x4       (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba8_x5       (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN
+mng_retcode mng_magnify_g8_y1          (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_g8_y2          (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_g8_y3          (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb8_y1        (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb8_y2        (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb8_y3        (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_y1         (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_y2         (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_y3         (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_y4         (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga8_y5         (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+#endif
+mng_retcode mng_magnify_rgba8_y1       (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba8_y2       (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba8_y3       (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba8_y4       (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba8_y5       (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+
+/* ************************************************************************** */
+#ifndef MNG_NO_16BIT_SUPPORT
+#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN
+mng_retcode mng_magnify_g16_x1         (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_g16_x2         (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_g16_x3         (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb16_x1       (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb16_x2       (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb16_x3       (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_x1        (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_x2        (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_x3        (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_x4        (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_x5        (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_x1      (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_x2      (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_x3      (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_x4      (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_x5      (mng_datap  pData,
+                                        mng_uint16 iMX,
+                                        mng_uint16 iML,
+                                        mng_uint16 iMR,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline,
+                                        mng_uint8p pDstline);
+
+mng_retcode mng_magnify_g16_y1         (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_g16_y2         (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_g16_y3         (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb16_y1       (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb16_y2       (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgb16_y3       (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_y1        (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_y2        (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_y3        (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_y4        (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_ga16_y5        (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_y1      (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_y2      (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_y3      (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_y4      (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+mng_retcode mng_magnify_rgba16_y5      (mng_datap  pData,
+                                        mng_int32  iS,
+                                        mng_int32  iM,
+                                        mng_uint32 iWidth,
+                                        mng_uint8p pSrcline1,
+                                        mng_uint8p pSrcline2,
+                                        mng_uint8p pDstline);
+#endif
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * PAST composition routines - compose over/under with a target object    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode mng_composeover_rgba8      (mng_datap  pData);
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_composeunder_rgba8     (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_composeover_rgba16     (mng_datap  pData);
+mng_retcode mng_composeunder_rgba16    (mng_datap  pData);
+#endif
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * PAST flip & tile routines - flip or tile a row of pixels               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef MNG_SKIPCHUNK_PAST
+mng_retcode mng_flip_rgba8             (mng_datap  pData);
+mng_retcode mng_tile_rgba8             (mng_datap  pData);
+#ifndef MNG_NO_16BIT_SUPPORT
+mng_retcode mng_flip_rgba16            (mng_datap  pData);
+mng_retcode mng_tile_rgba16            (mng_datap  pData);
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#endif /* _libmng_pixels_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_prop_xs.c b/files/Source/LibMNG/libmng_prop_xs.c
new file mode 100644
index 0000000..88e416d
--- /dev/null
+++ b/files/Source/LibMNG/libmng_prop_xs.c
@@ -0,0 +1,2799 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_prop_xs.c          copyright (c) 2000-2006 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : property get/set interface (implementation)                * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the property get/set functions           * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - fixed calling convention                                 * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added set_outputprofile2 & set_srgbprofile2              * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/23/2000 - G.Juyn                                * */
+/* *             - changed inclusion of cms-routines                        * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - added support for get/set default zlib/IJG parms         * */
+/* *             0.5.2 - 05/31/2000 - G.Juyn                                * */
+/* *             - fixed up punctuation (contribution by Tim Rowley)        * */
+/* *             0.5.2 - 06/05/2000 - G.Juyn                                * */
+/* *             - added support for RGB8_A8 canvasstyle                    * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added get/set for speedtype to facilitate testing        * */
+/* *             - added get for imagelevel during processtext callback     * */
+/* *             0.5.3 - 06/26/2000 - G.Juyn                                * */
+/* *             - changed userdata variable to mng_ptr                     * */
+/* *             0.5.3 - 06/29/2000 - G.Juyn                                * */
+/* *             - fixed incompatible return-types                          * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/08/2000 - G.Juyn                                * */
+/* *             - added get routines for internal display variables        * */
+/* *             - added get/set routines for suspensionmode variable       * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added get/set routines for sectionbreak variable         * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/31/2000 - G.Juyn                                * */
+/* *             - added status_xxxx functions                              * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 10/10/2000 - G.Juyn                                * */
+/* *             - added support for alpha-depth prediction                 * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added functions to retrieve PNG/JNG specific header-info * */
+/* *             0.9.3 - 10/20/2000 - G.Juyn                                * */
+/* *             - added get/set for bKGD preference setting                * */
+/* *             0.9.3 - 10/21/2000 - G.Juyn                                * */
+/* *             - added get function for interlace/progressive display     * */
+/* *                                                                        * */
+/* *             1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly)              * */
+/* *             - added BGRA8 canvas with premultiplied alpha              * */
+/* *             1.0.1 - 05/02/2001 - G.Juyn                                * */
+/* *             - added "default" sRGB generation (Thanks Marti!)          * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added optimization option for MNG-video playback         * */
+/* *             1.0.2 - 06/25/2001 - G.Juyn                                * */
+/* *             - added option to turn off progressive refresh             * */
+/* *                                                                        * */
+/* *             1.0.3 - 08/06/2001 - G.Juyn                                * */
+/* *             - added get function for last processed BACK chunk         * */
+/* *                                                                        * */
+/* *             1.0.4 - 06/22/2002 - G.Juyn                                * */
+/* *             - B495442 - invalid returnvalue in mng_get_suspensionmode  * */
+/* *                                                                        * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 09/22/2002 - G.Juyn                                * */
+/* *             - added bgrx8 canvas (filler byte)                         * */
+/* *             1.0.5 - 11/07/2002 - G.Juyn                                * */
+/* *             - added support to get totals after mng_read()             * */
+/* *                                                                        * */
+/* *             1.0.6 - 05/11/2003 - G. Juyn                               * */
+/* *             - added conditionals around canvas update routines         * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added conditionals around some JNG-supporting code       * */
+/* *             1.0.6 - 07/11/2003 - G.R-P                                 * */
+/* *             - added conditionals zlib and jpeg property accessors      * */
+/* *             1.0.6 - 07/14/2003 - G.R-P                                 * */
+/* *             - added conditionals around various unused functions       * */
+/* *                                                                        * */
+/* *             1.0.7 - 11/27/2003 - R.A                                   * */
+/* *             - added CANVAS_RGB565 and CANVAS_BGR565                    * */
+/* *             1.0.7 - 12/06/2003 - R.A                                   * */
+/* *             - added CANVAS_RGBA565 and CANVAS_BGRA565                  * */
+/* *             1.0.7 - 01/25/2004 - J.S                                   * */
+/* *             - added premultiplied alpha canvas' for RGBA, ARGB, ABGR   * */
+/* *             1.0.7 - 03/07/2004 - G.R-P.                                * */
+/* *             - put gamma, cms-related functions inside #ifdef           * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/02/2004 - G.Juyn                                * */
+/* *             - added CRC existence & checking flags                     * */
+/* *                                                                        * */
+/* *             1.0.9 - 09/18/2004 - G.R-P.                                * */
+/* *             - added some MNG_SUPPORT_WRITE conditionals                * */
+/* *             1.0.9 - 10/03/2004 - G.Juyn                                * */
+/* *             - added function to retrieve current FRAM delay            * */
+/* *             1.0.9 - 10/14/2004 - G.Juyn                                * */
+/* *             - added bgr565_a8 canvas-style (thanks to J. Elvander)     * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* *             1.0.10 - 03/07/2006 - (thanks to W. Manthey)               * */
+/* *             - added CANVAS_RGB555 and CANVAS_BGR555                    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_objects.h"
+#include "libmng_memory.h"
+#include "libmng_cms.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Property set functions                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_userdata (mng_handle hHandle,
+                                       mng_ptr    pUserdata)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_USERDATA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->pUserdata = pUserdata;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_USERDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_canvasstyle (mng_handle hHandle,
+                                          mng_uint32 iStyle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CANVASSTYLE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+
+  switch (iStyle)
+  {
+#ifndef MNG_SKIPCANVAS_RGB8
+    case MNG_CANVAS_RGB8    : break;
+#endif
+#ifndef MNG_SKIPCANVAS_RGBA8
+    case MNG_CANVAS_RGBA8   : break;
+#endif
+#ifndef MNG_SKIPCANVAS_RGBA8_PM
+    case MNG_CANVAS_RGBA8_PM: break;
+#endif
+#ifndef MNG_SKIPCANVAS_ARGB8
+    case MNG_CANVAS_ARGB8   : break;
+#endif
+#ifndef MNG_SKIPCANVAS_ARGB8_PM
+    case MNG_CANVAS_ARGB8_PM: break;
+#endif
+#ifndef MNG_SKIPCANVAS_RGB8_A8
+    case MNG_CANVAS_RGB8_A8 : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGR8
+    case MNG_CANVAS_BGR8    : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGRX8
+    case MNG_CANVAS_BGRX8   : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGRA8
+    case MNG_CANVAS_BGRA8   : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGRA8_PM
+    case MNG_CANVAS_BGRA8_PM: break;
+#endif
+#ifndef MNG_SKIPCANVAS_ABGR8
+    case MNG_CANVAS_ABGR8   : break;
+#endif
+#ifndef MNG_SKIPCANVAS_ABGR8_PM
+    case MNG_CANVAS_ABGR8_PM: break;
+#endif
+#ifndef MNG_SKIPCANVAS_RGB565
+    case MNG_CANVAS_RGB565  : break;
+#endif
+#ifndef MNG_SKIPCANVAS_RGBA565
+    case MNG_CANVAS_RGBA565 : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGR565
+    case MNG_CANVAS_BGR565  : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGRA565
+    case MNG_CANVAS_BGRA565 : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGR565_A8
+    case MNG_CANVAS_BGR565_A8 : break;
+#endif
+#ifndef MNG_SKIPCANVAS_RGB555
+    case MNG_CANVAS_RGB555  : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGR555
+    case MNG_CANVAS_BGR555  : break;
+#endif
+/*    case MNG_CANVAS_RGB16   : break; */
+/*    case MNG_CANVAS_RGBA16  : break; */
+/*    case MNG_CANVAS_ARGB16  : break; */
+/*    case MNG_CANVAS_BGR16   : break; */
+/*    case MNG_CANVAS_BGRA16  : break; */
+/*    case MNG_CANVAS_ABGR16  : break; */
+/*    case MNG_CANVAS_INDEX8  : break; */
+/*    case MNG_CANVAS_INDEXA8 : break; */
+/*    case MNG_CANVAS_AINDEX8 : break; */
+/*    case MNG_CANVAS_GRAY8   : break; */
+/*    case MNG_CANVAS_GRAY16  : break; */
+/*    case MNG_CANVAS_GRAYA8  : break; */
+/*    case MNG_CANVAS_GRAYA16 : break; */
+/*    case MNG_CANVAS_AGRAY8  : break; */
+/*    case MNG_CANVAS_AGRAY16 : break; */
+/*    case MNG_CANVAS_DX15    : break; */
+/*    case MNG_CANVAS_DX16    : break; */
+    default                 : { MNG_ERROR (((mng_datap)hHandle), MNG_INVALIDCNVSTYLE) };
+  }
+
+  ((mng_datap)hHandle)->iCanvasstyle = iStyle;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CANVASSTYLE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_bkgdstyle (mng_handle hHandle,
+                                        mng_uint32 iStyle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_BKGDSTYLE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+
+  switch (iStyle)                      /* alpha-modes not supported */
+  {
+#ifndef MNG_SKIPCANVAS_RGB8
+    case MNG_CANVAS_RGB8    : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGR8
+    case MNG_CANVAS_BGR8    : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGRX8
+    case MNG_CANVAS_BGRX8   : break;
+#endif
+#ifndef MNG_SKIPCANVAS_RGB565
+    case MNG_CANVAS_RGB565  : break;
+#endif
+#ifndef MNG_SKIPCANVAS_BGR565
+    case MNG_CANVAS_BGR565  : break;
+#endif
+/*    case MNG_CANVAS_RGB16   : break; */
+/*    case MNG_CANVAS_BGR16   : break; */
+/*    case MNG_CANVAS_INDEX8  : break; */
+/*    case MNG_CANVAS_GRAY8   : break; */
+/*    case MNG_CANVAS_GRAY16  : break; */
+/*    case MNG_CANVAS_DX15    : break; */
+/*    case MNG_CANVAS_DX16    : break; */
+    default                 : MNG_ERROR (((mng_datap)hHandle), MNG_INVALIDCNVSTYLE);
+  }
+
+  ((mng_datap)hHandle)->iBkgdstyle = iStyle;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_BKGDSTYLE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_bgcolor (mng_handle hHandle,
+                                      mng_uint16 iRed,
+                                      mng_uint16 iGreen,
+                                      mng_uint16 iBlue)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_BGCOLOR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iBGred   = iRed;
+  ((mng_datap)hHandle)->iBGgreen = iGreen;
+  ((mng_datap)hHandle)->iBGblue  = iBlue;
+  ((mng_datap)hHandle)->bUseBKGD = MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_BGCOLOR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_usebkgd (mng_handle hHandle,
+                                      mng_bool   bUseBKGD)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_USEBKGD, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->bUseBKGD = bUseBKGD;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_USEBKGD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_storechunks (mng_handle hHandle,
+                                          mng_bool   bStorechunks)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_STORECHUNKS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->bStorechunks = bStorechunks;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_STORECHUNKS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_sectionbreaks (mng_handle hHandle,
+                                            mng_bool   bSectionbreaks)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SECTIONBREAKS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->bSectionbreaks = bSectionbreaks;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SECTIONBREAKS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_cacheplayback (mng_handle hHandle,
+                                            mng_bool   bCacheplayback)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CACHEPLAYBACK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+
+  if (((mng_datap)hHandle)->bHasheader)
+    MNG_ERROR (((mng_datap)hHandle), MNG_FUNCTIONINVALID);
+
+  ((mng_datap)hHandle)->bCacheplayback = bCacheplayback;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CACHEPLAYBACK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_doprogressive (mng_handle hHandle,
+                                            mng_bool   bDoProgressive)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DOPROGRESSIVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+
+  ((mng_datap)hHandle)->bDoProgressive = bDoProgressive;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DOPROGRESSIVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_crcmode (mng_handle hHandle,
+                                      mng_uint32 iCrcmode)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CRCMODE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+
+  ((mng_datap)hHandle)->iCrcmode = iCrcmode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CRCMODE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_set_srgb (mng_handle hHandle,
+                                   mng_bool   bIssRGB)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGB, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->bIssRGB = bIssRGB;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_SKIPCHUNK_iCCP
+mng_retcode MNG_DECL mng_set_outputprofile (mng_handle hHandle,
+                                            mng_pchar  zFilename)
+{
+#ifdef MNG_INCLUDE_LCMS
+  mng_datap pData;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTPROFILE, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_LCMS
+  MNG_VALIDHANDLE (hHandle)
+
+  pData = (mng_datap)hHandle;          /* address the structure */
+
+  if (pData->hProf2)                   /* previously defined ? */
+    mnglcms_freeprofile (pData->hProf2);
+                                       /* allocate new CMS profile handle */
+  pData->hProf2 = mnglcms_createfileprofile (zFilename);
+
+  if (!pData->hProf2)                  /* handle error ? */
+    MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+#endif /* MNG_INCLUDE_LCMS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTPROFILE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_SKIPCHUNK_iCCP
+mng_retcode MNG_DECL mng_set_outputprofile2 (mng_handle hHandle,
+                                             mng_uint32 iProfilesize,
+                                             mng_ptr    pProfile)
+{
+#ifdef MNG_INCLUDE_LCMS
+  mng_datap pData;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTPROFILE2, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_LCMS
+  MNG_VALIDHANDLE (hHandle)
+
+  pData = (mng_datap)hHandle;          /* address the structure */
+
+  if (pData->hProf2)                   /* previously defined ? */
+    mnglcms_freeprofile (pData->hProf2);
+                                       /* allocate new CMS profile handle */
+  pData->hProf2 = mnglcms_creatememprofile (iProfilesize, pProfile);
+
+  if (!pData->hProf2)                  /* handle error ? */
+    MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+#endif /* MNG_INCLUDE_LCMS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTPROFILE2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_outputsrgb (mng_handle hHandle)
+{
+#ifdef MNG_INCLUDE_LCMS
+  mng_datap pData;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTSRGB, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_LCMS
+  MNG_VALIDHANDLE (hHandle)
+
+  pData = (mng_datap)hHandle;          /* address the structure */
+
+  if (pData->hProf2)                   /* previously defined ? */
+    mnglcms_freeprofile (pData->hProf2);
+                                       /* allocate new CMS profile handle */
+  pData->hProf2 = mnglcms_createsrgbprofile ();
+
+  if (!pData->hProf2)                  /* handle error ? */
+    MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+#endif /* MNG_INCLUDE_LCMS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTSRGB, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_set_srgbprofile (mng_handle hHandle,
+                                          mng_pchar  zFilename)
+{
+#ifdef MNG_INCLUDE_LCMS
+  mng_datap pData;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBPROFILE2, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_LCMS
+  MNG_VALIDHANDLE (hHandle)
+
+  pData = (mng_datap)hHandle;          /* address the structure */
+
+  if (pData->hProf3)                   /* previously defined ? */
+    mnglcms_freeprofile (pData->hProf3);
+                                       /* allocate new CMS profile handle */
+  pData->hProf3 = mnglcms_createfileprofile (zFilename);
+
+  if (!pData->hProf3)                  /* handle error ? */
+    MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+#endif /* MNG_INCLUDE_LCMS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBPROFILE2, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_set_srgbprofile2 (mng_handle hHandle,
+                                           mng_uint32 iProfilesize,
+                                           mng_ptr    pProfile)
+{
+#ifdef MNG_INCLUDE_LCMS
+  mng_datap pData;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBPROFILE, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_LCMS
+  MNG_VALIDHANDLE (hHandle)
+
+  pData = (mng_datap)hHandle;          /* address the structure */
+
+  if (pData->hProf3)                   /* previously defined ? */
+    mnglcms_freeprofile (pData->hProf3);
+                                       /* allocate new CMS profile handle */
+  pData->hProf3 = mnglcms_creatememprofile (iProfilesize, pProfile);
+
+  if (!pData->hProf3)                  /* handle error ? */
+    MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+#endif /* MNG_INCLUDE_LCMS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBPROFILE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_srgbimplicit (mng_handle hHandle)
+{
+#ifdef MNG_INCLUDE_LCMS
+  mng_datap pData;
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBIMPLICIT, MNG_LC_START);
+#endif
+
+#ifdef MNG_INCLUDE_LCMS
+  MNG_VALIDHANDLE (hHandle)
+
+  pData = (mng_datap)hHandle;          /* address the structure */
+
+  if (pData->hProf3)                   /* previously defined ? */
+    mnglcms_freeprofile (pData->hProf3);
+                                       /* allocate new CMS profile handle */
+  pData->hProf3 = mnglcms_createsrgbprofile ();
+
+  if (!pData->hProf3)                  /* handle error ? */
+    MNG_ERRORL (pData, MNG_LCMS_NOHANDLE);
+#endif /* MNG_INCLUDE_LCMS */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBIMPLICIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+mng_retcode MNG_DECL mng_set_viewgamma (mng_handle hHandle,
+                                        mng_float  dGamma)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_VIEWGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->dViewgamma = dGamma;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_VIEWGAMMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_displaygamma (mng_handle hHandle,
+                                           mng_float  dGamma)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DISPLAYGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->dDisplaygamma = dGamma;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DISPLAYGAMMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_dfltimggamma (mng_handle hHandle,
+                                           mng_float  dGamma)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DFLTIMGGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->dDfltimggamma = dGamma;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DFLTIMGGAMMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+mng_retcode MNG_DECL mng_set_viewgammaint (mng_handle hHandle,
+                                           mng_uint32 iGamma)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_VIEWGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->dViewgamma = (mng_float)iGamma / 100000;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_VIEWGAMMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_displaygammaint (mng_handle hHandle,
+                                              mng_uint32 iGamma)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DISPLAYGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->dDisplaygamma = (mng_float)iGamma / 100000;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DISPLAYGAMMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DFLT_INFO
+mng_retcode MNG_DECL mng_set_dfltimggammaint (mng_handle hHandle,
+                                              mng_uint32 iGamma)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DFLTIMGGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->dDfltimggamma = (mng_float)iGamma / 100000;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DFLTIMGGAMMA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIP_MAXCANVAS
+mng_retcode MNG_DECL mng_set_maxcanvaswidth (mng_handle hHandle,
+                                             mng_uint32 iMaxwidth)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASWIDTH, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iMaxwidth = iMaxwidth;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASWIDTH, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_maxcanvasheight (mng_handle hHandle,
+                                              mng_uint32 iMaxheight)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASHEIGHT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iMaxheight = iMaxheight;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASHEIGHT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_set_maxcanvassize (mng_handle hHandle,
+                                            mng_uint32 iMaxwidth,
+                                            mng_uint32 iMaxheight)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASSIZE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iMaxwidth  = iMaxwidth;
+  ((mng_datap)hHandle)->iMaxheight = iMaxheight;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASSIZE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_zlib_level (mng_handle hHandle,
+                                         mng_int32  iZlevel)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_LEVEL, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iZlevel = iZlevel;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_LEVEL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_zlib_method (mng_handle hHandle,
+                                          mng_int32  iZmethod)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_METHOD, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iZmethod = iZmethod;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_METHOD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_zlib_windowbits (mng_handle hHandle,
+                                              mng_int32  iZwindowbits)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_WINDOWBITS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iZwindowbits = iZwindowbits;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_WINDOWBITS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_zlib_memlevel (mng_handle hHandle,
+                                            mng_int32  iZmemlevel)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_MEMLEVEL, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iZmemlevel = iZmemlevel;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_MEMLEVEL, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_zlib_strategy (mng_handle hHandle,
+                                            mng_int32  iZstrategy)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_STRATEGY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iZstrategy = iZstrategy;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_STRATEGY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_zlib_maxidat (mng_handle hHandle,
+                                           mng_uint32 iMaxIDAT)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_MAXIDAT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iMaxIDAT = iMaxIDAT;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_MAXIDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_jpeg_dctmethod (mng_handle        hHandle,
+                                             mngjpeg_dctmethod eJPEGdctmethod)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_DCTMETHOD, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->eJPEGdctmethod = eJPEGdctmethod;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_DCTMETHOD, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif MNG_SUPPORT_WRITE
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_jpeg_quality (mng_handle hHandle,
+                                           mng_int32  iJPEGquality)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_QUALITY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iJPEGquality = iJPEGquality;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_QUALITY, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_jpeg_smoothing (mng_handle hHandle,
+                                             mng_int32  iJPEGsmoothing)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_SMOOTHING, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iJPEGsmoothing = iJPEGsmoothing;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_SMOOTHING, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_jpeg_progressive (mng_handle hHandle,
+                                               mng_bool   bJPEGprogressive)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_PROGRESSIVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->bJPEGcompressprogr = bJPEGprogressive;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_PROGRESSIVE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_jpeg_optimized (mng_handle hHandle,
+                                             mng_bool   bJPEGoptimized)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_OPTIMIZED, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->bJPEGcompressopt = bJPEGoptimized;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_OPTIMIZED, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+#ifdef MNG_SUPPORT_WRITE
+mng_retcode MNG_DECL mng_set_jpeg_maxjdat (mng_handle hHandle,
+                                           mng_uint32 iMaxJDAT)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_MAXJDAT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iMaxJDAT = iMaxJDAT;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_MAXJDAT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_WRITE */
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_retcode MNG_DECL mng_set_suspensionmode (mng_handle hHandle,
+                                             mng_bool   bSuspensionmode)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SUSPENSIONMODE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+
+  if (((mng_datap)hHandle)->bReading)  /* we must NOT be reading !!! */
+    MNG_ERROR ((mng_datap)hHandle, MNG_FUNCTIONINVALID);
+
+  ((mng_datap)hHandle)->bSuspensionmode = bSuspensionmode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SUSPENSIONMODE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_set_speed (mng_handle    hHandle,
+                                    mng_speedtype iSpeed)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SPEED, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  ((mng_datap)hHandle)->iSpeed = iSpeed;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SPEED, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* *  Property get functions                                                * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+mng_ptr MNG_DECL mng_get_userdata (mng_handle hHandle)
+{                            /* no tracing in here to prevent recursive calls */
+  MNG_VALIDHANDLEX (hHandle)
+  return ((mng_datap)hHandle)->pUserdata;
+}
+
+/* ************************************************************************** */
+
+mng_imgtype MNG_DECL mng_get_sigtype (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SIGTYPE, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_it_unknown;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SIGTYPE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->eSigtype;
+}
+
+/* ************************************************************************** */
+
+mng_imgtype MNG_DECL mng_get_imagetype (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGETYPE, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_it_unknown;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGETYPE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->eImagetype;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_imagewidth (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGEWIDTH, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGEWIDTH, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iWidth;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_imageheight (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGEWIDTH, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGEHEIGHT, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iHeight;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_ticks (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TICKS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TICKS, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iTicks;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_framecount (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_FRAMECOUNT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_FRAMECOUNT, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iFramecount;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_layercount (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LAYERCOUNT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LAYERCOUNT, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iLayercount;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_playtime (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_PLAYTIME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_PLAYTIME, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iPlaytime;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_simplicity (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SIMPLICITY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SIMPLICITY, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iSimplicity;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_get_bitdepth (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_BITDEPTH, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+  if (((mng_datap)hHandle)->eImagetype == mng_it_png)
+    iRslt = ((mng_datap)hHandle)->iBitdepth;
+  else
+#ifdef MNG_INCLUDE_JNG
+  if (((mng_datap)hHandle)->eImagetype == mng_it_jng)
+    iRslt = ((mng_datap)hHandle)->iJHDRimgbitdepth;
+  else
+#endif
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_BITDEPTH, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_get_colortype (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_COLORTYPE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+  if (((mng_datap)hHandle)->eImagetype == mng_it_png)
+    iRslt = ((mng_datap)hHandle)->iColortype;
+  else
+#ifdef MNG_INCLUDE_JNG
+  if (((mng_datap)hHandle)->eImagetype == mng_it_jng)
+    iRslt = ((mng_datap)hHandle)->iJHDRcolortype;
+  else
+#endif
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_COLORTYPE, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_get_compression (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_COMPRESSION, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+  if (((mng_datap)hHandle)->eImagetype == mng_it_png)
+    iRslt = ((mng_datap)hHandle)->iCompression;
+  else
+#ifdef MNG_INCLUDE_JNG
+  if (((mng_datap)hHandle)->eImagetype == mng_it_jng)
+    iRslt = ((mng_datap)hHandle)->iJHDRimgcompression;
+  else
+#endif
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_COMPRESSION, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_get_filter (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_FILTER, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+  if (((mng_datap)hHandle)->eImagetype == mng_it_png)
+    iRslt = ((mng_datap)hHandle)->iFilter;
+  else
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_FILTER, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_get_interlace (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_INTERLACE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+  if (((mng_datap)hHandle)->eImagetype == mng_it_png)
+    iRslt = ((mng_datap)hHandle)->iInterlace;
+  else
+#ifdef MNG_INCLUDE_JNG
+  if (((mng_datap)hHandle)->eImagetype == mng_it_jng)
+    iRslt = ((mng_datap)hHandle)->iJHDRimginterlace;
+  else
+#endif
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_INTERLACE, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_get_alphabitdepth (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHABITDEPTH, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_INCLUDE_JNG
+  if (((mng_datap)hHandle)->eImagetype == mng_it_jng)
+    iRslt = ((mng_datap)hHandle)->iJHDRalphabitdepth;
+  else
+#endif
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHABITDEPTH, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_uint8 MNG_DECL mng_get_refreshpass (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+  mng_datap pData;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_REFRESHPASS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+  pData = (mng_datap)hHandle;
+                                       /* for PNG we know the exact pass */
+  if ((pData->eImagetype == mng_it_png) && (pData->iPass >= 0))
+    iRslt = pData->iPass;
+#ifdef MNG_INCLUDE_JNG
+  else                                 /* for JNG we'll fake it... */
+  if ((pData->eImagetype == mng_it_jng) &&
+      (pData->bJPEGhasheader) && (pData->bJPEGdecostarted) &&
+      (pData->bJPEGprogressive))
+  {
+    if (pData->pJPEGdinfo->input_scan_number <= 1)
+      iRslt = 0;                       /* first pass (I think...) */
+    else
+    if (jpeg_input_complete (pData->pJPEGdinfo))
+      iRslt = 7;                       /* input complete; aka final pass */
+    else
+      iRslt = 3;                       /* anything between 0 and 7 will do */
+
+  }
+#endif
+  else
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_REFRESHPASS, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+mng_uint8 MNG_DECL mng_get_alphacompression (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHACOMPRESSION, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_INCLUDE_JNG
+  if (((mng_datap)hHandle)->eImagetype == mng_it_jng)
+    iRslt = ((mng_datap)hHandle)->iJHDRalphacompression;
+  else
+#endif
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHACOMPRESSION, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_get_alphafilter (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHAFILTER, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_INCLUDE_JNG
+  if (((mng_datap)hHandle)->eImagetype == mng_it_jng)
+    iRslt = ((mng_datap)hHandle)->iJHDRalphafilter;
+  else
+#endif
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHAFILTER, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_get_alphainterlace (mng_handle hHandle)
+{
+  mng_uint8 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHAINTERLACE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_INCLUDE_JNG
+  if (((mng_datap)hHandle)->eImagetype == mng_it_jng)
+    iRslt = ((mng_datap)hHandle)->iJHDRalphainterlace;
+  else
+#endif
+    iRslt = 0;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHAINTERLACE, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+
+/* ************************************************************************** */
+
+mng_uint8 MNG_DECL mng_get_alphadepth (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHADEPTH, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHADEPTH, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iAlphadepth;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_canvasstyle (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CANVASSTYLE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CANVASSTYLE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iCanvasstyle;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_bkgdstyle (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_BKGDSTYLE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_BKGDSTYLE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iBkgdstyle;
+}
+
+/* ************************************************************************** */
+
+mng_retcode MNG_DECL mng_get_bgcolor (mng_handle  hHandle,
+                                      mng_uint16* iRed,
+                                      mng_uint16* iGreen,
+                                      mng_uint16* iBlue)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GET_BGCOLOR, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+  *iRed   = ((mng_datap)hHandle)->iBGred;
+  *iGreen = ((mng_datap)hHandle)->iBGgreen;
+  *iBlue  = ((mng_datap)hHandle)->iBGblue;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (((mng_datap)hHandle), MNG_FN_GET_BGCOLOR, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_bool MNG_DECL mng_get_usebkgd (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_USEBKGD, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_USEBKGD, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bUseBKGD;
+}
+
+/* ************************************************************************** */
+
+mng_bool MNG_DECL mng_get_storechunks (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_STORECHUNKS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_STORECHUNKS, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bStorechunks;
+}
+
+/* ************************************************************************** */
+
+mng_bool MNG_DECL mng_get_sectionbreaks (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_SECTIONBREAKS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_SECTIONBREAKS, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bSectionbreaks;
+}
+
+/* ************************************************************************** */
+
+mng_bool MNG_DECL mng_get_cacheplayback (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_CACHEPLAYBACK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_CACHEPLAYBACK, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bCacheplayback;
+}
+
+/* ************************************************************************** */
+
+mng_bool MNG_DECL mng_get_doprogressive (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_DOPROGRESSIVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_DOPROGRESSIVE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bDoProgressive;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_crcmode (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_CRCMODE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_CRCMODE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iCrcmode;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_bool MNG_DECL mng_get_srgb (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_SRGB, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_SRGB, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bIssRGB;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+mng_float MNG_DECL mng_get_viewgamma (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_VIEWGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_VIEWGAMMA, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->dViewgamma;
+}
+#endif
+
+/* ************************************************************************** */
+
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+mng_float MNG_DECL mng_get_displaygamma (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DISPLAYGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DISPLAYGAMMA, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->dDisplaygamma;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DFLT_INFO
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+mng_float MNG_DECL mng_get_dfltimggamma (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DFLTIMGGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DFLTIMGGAMMA, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->dDfltimggamma;
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+mng_uint32 MNG_DECL mng_get_viewgammaint (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_VIEWGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_VIEWGAMMA, MNG_LC_END);
+#endif
+
+  return (mng_uint32)(((mng_datap)hHandle)->dViewgamma * 100000);
+}
+#endif
+
+/* ************************************************************************** */
+
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+mng_uint32 MNG_DECL mng_get_displaygammaint (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DISPLAYGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DISPLAYGAMMA, MNG_LC_END);
+#endif
+
+  return (mng_uint32)(((mng_datap)hHandle)->dDisplaygamma * 100000);
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_NO_DFLT_INFO
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+mng_uint32 MNG_DECL mng_get_dfltimggammaint (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DFLTIMGGAMMA, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DFLTIMGGAMMA, MNG_LC_END);
+#endif
+
+  return (mng_uint32)(((mng_datap)hHandle)->dDfltimggamma * 100000);
+}
+#endif
+#endif
+
+/* ************************************************************************** */
+
+#ifndef MNG_SKIP_MAXCANVAS
+mng_uint32 MNG_DECL mng_get_maxcanvaswidth (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_MAXCANVASWIDTH, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_MAXCANVASWIDTH, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iMaxwidth;
+}
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_maxcanvasheight (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_MAXCANVASHEIGHT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_MAXCANVASHEIGHT, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iMaxheight;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+mng_int32 MNG_DECL mng_get_zlib_level (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_LEVEL, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_LEVEL, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iZlevel;
+}
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+mng_int32 MNG_DECL mng_get_zlib_method (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_METHOD, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_METHOD, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iZmethod;
+}
+
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+mng_int32 MNG_DECL mng_get_zlib_windowbits (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_WINDOWBITS, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_WINDOWBITS, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iZwindowbits;
+}
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+mng_int32 MNG_DECL mng_get_zlib_memlevel (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_MEMLEVEL, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_MEMLEVEL, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iZmemlevel;
+}
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+mng_int32 MNG_DECL mng_get_zlib_strategy (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_STRATEGY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_STRATEGY, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iZstrategy;
+}
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+#ifdef MNG_ACCESS_ZLIB
+mng_uint32 MNG_DECL mng_get_zlib_maxidat (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_MAXIDAT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_MAXIDAT, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iMaxIDAT;
+}
+#endif /* MNG_ACCESS_ZLIB */
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+mngjpeg_dctmethod MNG_DECL mng_get_jpeg_dctmethod (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_DCTMETHOD, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return JDCT_ISLOW;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_DCTMETHOD, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->eJPEGdctmethod;
+}
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+mng_int32 MNG_DECL mng_get_jpeg_quality (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_QUALITY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_QUALITY, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iJPEGquality;
+}
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+mng_int32 MNG_DECL mng_get_jpeg_smoothing (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_SMOOTHING, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_SMOOTHING, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iJPEGsmoothing;
+}
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+mng_bool MNG_DECL mng_get_jpeg_progressive (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_PROGRESSIVE, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_PROGRESSIVE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bJPEGcompressprogr;
+}
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+mng_bool MNG_DECL mng_get_jpeg_optimized (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_OPTIMIZED, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_OPTIMIZED, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bJPEGcompressopt;
+}
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+#ifdef MNG_ACCESS_JPEG
+mng_uint32 MNG_DECL mng_get_jpeg_maxjdat (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_MAXJDAT, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_MAXJDAT, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iMaxJDAT;
+}
+#endif /* MNG_ACCESS_JPEG */
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_bool MNG_DECL mng_get_suspensionmode (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SUSPENSIONMODE, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SUSPENSIONMODE, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bSuspensionmode;
+}
+#endif /* MNG_SUPPORT_READ */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_speedtype MNG_DECL mng_get_speed (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SPEED, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_st_normal;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SPEED, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iSpeed;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+mng_uint32 MNG_DECL mng_get_imagelevel (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGELEVEL, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLEX (hHandle)
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGELEVEL, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iImagelevel;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_get_lastbackchunk (mng_handle  hHandle,
+                                            mng_uint16* iRed,
+                                            mng_uint16* iGreen,
+                                            mng_uint16* iBlue,
+                                            mng_uint8*  iMandatory)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LASTBACKCHUNK, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+
+  if (((mng_datap)hHandle)->eImagetype != mng_it_mng)
+    MNG_ERROR (((mng_datap)hHandle), MNG_FUNCTIONINVALID);
+
+  *iRed       = ((mng_datap)hHandle)->iBACKred;
+  *iGreen     = ((mng_datap)hHandle)->iBACKgreen;
+  *iBlue      = ((mng_datap)hHandle)->iBACKblue;
+  *iMandatory = ((mng_datap)hHandle)->iBACKmandatory;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LASTBACKCHUNK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode MNG_DECL mng_get_lastseekname (mng_handle hHandle,
+                                           mng_pchar  zSegmentname)
+{
+  mng_datap pData;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LASTSEEKNAME, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+
+  pData = (mng_datap)hHandle;
+                                       /* only allowed for MNG ! */
+  if (pData->eImagetype != mng_it_mng)
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  if (pData->pLastseek)                /* is there a last SEEK ? */
+  {
+    mng_ani_seekp pSEEK = (mng_ani_seekp)pData->pLastseek;
+
+    if (pSEEK->iSegmentnamesize)       /* copy the name if there is one */
+      MNG_COPY (zSegmentname, pSEEK->zSegmentname, pSEEK->iSegmentnamesize);
+
+    *(((mng_uint8p)zSegmentname) + pSEEK->iSegmentnamesize) = 0;
+  }
+  else
+  {                                    /* return an empty string */
+    *((mng_uint8p)zSegmentname) = 0;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LASTSEEKNAME, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_uint32 MNG_DECL mng_get_currframdelay (mng_handle hHandle)
+{
+  mng_datap  pData;
+  mng_uint32 iRslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRFRAMDELAY, MNG_LC_START);
+#endif
+
+  MNG_VALIDHANDLE (hHandle)
+
+  pData = (mng_datap)hHandle;
+                                       /* only allowed for MNG ! */
+  if (pData->eImagetype != mng_it_mng)
+    MNG_ERROR (pData, MNG_FUNCTIONINVALID);
+
+  iRslt = pData->iFramedelay;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRFRAMDELAY, MNG_LC_END);
+#endif
+
+  return iRslt;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_uint32 MNG_DECL mng_get_starttime (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_STARTTIME, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_st_normal;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_STARTTIME, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iStarttime;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_uint32 MNG_DECL mng_get_runtime (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_RUNTIME, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_st_normal;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_RUNTIME, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iRuntime;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_NO_CURRENT_INFO
+mng_uint32 MNG_DECL mng_get_currentframe (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTFRAME, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_st_normal;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTFRAME, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iFrameseq;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_NO_CURRENT_INFO
+mng_uint32 MNG_DECL mng_get_currentlayer (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTLAYER, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_st_normal;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTLAYER, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iLayerseq;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_NO_CURRENT_INFO
+mng_uint32 MNG_DECL mng_get_currentplaytime (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTPLAYTIME, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_st_normal;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTPLAYTIME, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iFrametime;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_NO_CURRENT_INFO
+mng_uint32 MNG_DECL mng_get_totalframes (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALFRAMES, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_st_normal;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALFRAMES, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iTotalframes;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_NO_CURRENT_INFO
+mng_uint32 MNG_DECL mng_get_totallayers (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALLAYERS, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_st_normal;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALLAYERS, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iTotallayers;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+#ifndef MNG_NO_CURRENT_INFO
+mng_uint32 MNG_DECL mng_get_totalplaytime (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALPLAYTIME, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return mng_st_normal;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALPLAYTIME, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->iTotalplaytime;
+}
+#endif
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+mng_bool MNG_DECL mng_status_error (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_ERROR, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_ERROR, MNG_LC_END);
+#endif
+
+  return (mng_bool)((mng_datap)hHandle)->iErrorcode;
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_bool MNG_DECL mng_status_reading (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_READING, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_READING, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bReading;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_READ
+mng_bool MNG_DECL mng_status_suspendbreak (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_SUSPENDBREAK, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_SUSPENDBREAK, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bSuspended;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_WRITE
+mng_bool MNG_DECL mng_status_creating (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_CREATING, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_CREATING, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bCreating;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_WRITE
+mng_bool MNG_DECL mng_status_writing (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_WRITING, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_WRITING, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bWriting;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_bool MNG_DECL mng_status_displaying (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_DISPLAYING, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_DISPLAYING, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bDisplaying;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_bool MNG_DECL mng_status_running (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_RUNNING, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_RUNNING, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bRunning;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_bool MNG_DECL mng_status_timerbreak (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_TIMERBREAK, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_TIMERBREAK, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bTimerset;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+mng_bool MNG_DECL mng_status_dynamic (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_DYNAMIC, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_DYNAMIC, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bDynamic;
+}
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DYNAMICMNG
+mng_bool MNG_DECL mng_status_runningevent (mng_handle hHandle)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_RUNNINGEVENT, MNG_LC_START);
+#endif
+
+  if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC))
+    return MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_RUNNINGEVENT, MNG_LC_END);
+#endif
+
+  return ((mng_datap)hHandle)->bRunningevent;
+}
+#endif
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_read.c b/files/Source/LibMNG/libmng_read.c
new file mode 100644
index 0000000..c922e19
--- /dev/null
+++ b/files/Source/LibMNG/libmng_read.c
@@ -0,0 +1,1369 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_read.c             copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Read logic (implementation)                                * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the high-level read logic                * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - added callback error-reporting support                   * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/19/2000 - G.Juyn                                * */
+/* *             - cleaned up some code regarding mixed support             * */
+/* *             0.5.2 - 05/20/2000 - G.Juyn                                * */
+/* *             - added support for JNG                                    * */
+/* *             0.5.2 - 05/31/2000 - G.Juyn                                * */
+/* *             - fixed up punctuation (contribution by Tim Rowley)        * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/16/2000 - G.Juyn                                * */
+/* *             - changed progressive-display processing                   * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/08/2000 - G.Juyn                                * */
+/* *             - changed read-processing for improved I/O-suspension      * */
+/* *             0.9.1 - 07/14/2000 - G.Juyn                                * */
+/* *             - changed EOF processing behavior                          * */
+/* *             0.9.1 - 07/14/2000 - G.Juyn                                * */
+/* *             - changed default readbuffer size from 1024 to 4200        * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/27/2000 - G.Juyn                                * */
+/* *             - B110320 - fixed GCC warning about mix-sized pointer math * */
+/* *             0.9.2 - 07/31/2000 - G.Juyn                                * */
+/* *             - B110546 - fixed for improperly returning UNEXPECTEDEOF   * */
+/* *             0.9.2 - 08/04/2000 - G.Juyn                                * */
+/* *             - B111096 - fixed large-buffer read-suspension             * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - removed test-MaGN                                        * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added support for JDAA                                   * */
+/* *                                                                        * */
+/* *             0.9.5 - 01/23/2001 - G.Juyn                                * */
+/* *             - fixed timing-problem with switching framing_modes        * */
+/* *                                                                        * */
+/* *             1.0.4 - 06/22/2002 - G.Juyn                                * */
+/* *             - B495443 - incorrect suspend check in read_databuffer     * */
+/* *                                                                        * */
+/* *             1.0.5 - 07/04/2002 - G.Juyn                                * */
+/* *             - added errorcode for extreme chunk-sizes                  * */
+/* *             1.0.5 - 07/08/2002 - G.Juyn                                * */
+/* *             - B578572 - removed eMNGma hack (thanks Dimitri!)          * */
+/* *             1.0.5 - 07/16/2002 - G.Juyn                                * */
+/* *             - B581625 - large chunks fail with suspension reads        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             - added HLAPI function to copy chunks                      * */
+/* *             1.0.5 - 09/16/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *                                                                        * */
+/* *             1.0.6 - 05/25/2003 - G.R-P                                 * */
+/* *             - added MNG_SKIPCHUNK_cHNK footprint optimizations         * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added MNG_NO_DELTA_PNG reduction                         * */
+/* *             - skip additional code when MNG_INCLUDE_JNG is not enabled * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *             1.0.6 - 08/17/2003 - G.R-P                                 * */
+/* *             - added conditionals around non-VLC chunk support          * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/10/2004 - G.R-P                                 * */
+/* *             - added conditionals around openstream/closestream         * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/08/2004 - G.Juyn                                * */
+/* *             - added CRC existence & checking flags                     * */
+/* *             1.0.8 - 04/11/2004 - G.Juyn                                * */
+/* *             - added data-push mechanisms for specialized decoders      * */
+/* *             1.0.8 - 07/06/2004 - G.R-P                                 * */
+/* *             - defend against using undefined closestream function      * */
+/* *             1.0.8 - 07/28/2004 - G.R-P                                 * */
+/* *             - added check for extreme chunk-lengths                    * */
+/* *                                                                        * */
+/* *             1.0.9 - 09/16/2004 - G.Juyn                                * */
+/* *             - fixed chunk pushing mechanism                            * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKINITFREE             * */
+/* *             1.0.9 - 12/06/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKASSIGN               * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKREADER               * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *             1.0.9 - 12/31/2004 - G.R-P                                 * */
+/* *             - removed stray characters from #ifdef directive           * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_memory.h"
+#include "libmng_objects.h"
+#include "libmng_object_prc.h"
+#include "libmng_chunks.h"
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+#include "libmng_chunk_descr.h"
+#endif
+#include "libmng_chunk_prc.h"
+#include "libmng_chunk_io.h"
+#include "libmng_display.h"
+#include "libmng_read.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_READ_PROCS
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_eof (mng_datap pData)
+{
+  if (!pData->bEOF)                    /* haven't closed the stream yet ? */
+  {
+    pData->bEOF = MNG_TRUE;            /* now we do! */
+
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+    if (pData->fClosestream && !pData->fClosestream ((mng_handle)pData))
+    {
+      MNG_ERROR (pData, MNG_APPIOERROR);
+    }
+#endif
+  }
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_release_pushdata (mng_datap pData)
+{
+  mng_pushdatap pFirst  = pData->pFirstpushdata;
+  mng_pushdatap pNext   = pFirst->pNext;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RELEASE_PUSHDATA, MNG_LC_START);
+#endif
+
+  pData->pFirstpushdata = pNext;       /* next becomes the first */
+
+  if (!pNext)                          /* no next? => no last! */
+    pData->pLastpushdata = MNG_NULL;
+                                       /* buffer owned and release callback defined? */
+  if ((pFirst->bOwned) && (pData->fReleasedata))
+    pData->fReleasedata ((mng_handle)pData, pFirst->pData, pFirst->iLength);
+  else                                 /* otherwise use internal free mechanism */
+    MNG_FREEX (pData, pFirst->pData, pFirst->iLength);
+                                       /* and free it */
+  MNG_FREEX (pData, pFirst, sizeof(mng_pushdata));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RELEASE_PUSHDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_release_pushchunk (mng_datap pData)
+{
+  mng_pushdatap pFirst  = pData->pFirstpushchunk;
+  mng_pushdatap pNext   = pFirst->pNext;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RELEASE_PUSHCHUNK, MNG_LC_START);
+#endif
+
+  pData->pFirstpushchunk = pNext;      /* next becomes the first */
+
+  if (!pNext)                          /* no next? => no last! */
+    pData->pLastpushchunk = MNG_NULL;
+                                       /* buffer owned and release callback defined? */
+  if ((pFirst->bOwned) && (pData->fReleasedata))
+    pData->fReleasedata ((mng_handle)pData, pFirst->pData, pFirst->iLength);
+  else                                 /* otherwise use internal free mechanism */
+    MNG_FREEX (pData, pFirst->pData, pFirst->iLength);
+                                       /* and free it */
+  MNG_FREEX (pData, pFirst, sizeof(mng_pushdata));
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_RELEASE_PUSHCHUNK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode read_data (mng_datap    pData,
+                                 mng_uint8p   pBuf,
+                                 mng_uint32   iSize,
+                                 mng_uint32 * iRead)
+{
+  mng_retcode   iRetcode;
+  mng_uint32    iTempsize = iSize;
+  mng_uint8p    pTempbuf  = pBuf;
+  mng_pushdatap pPush     = pData->pFirstpushdata;
+  mng_uint32    iPushsize = 0;
+  *iRead                  = 0;         /* nothing yet */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DATA, MNG_LC_START);
+#endif
+
+  while (pPush)                        /* calculate size of pushed data */
+  {
+    iPushsize += pPush->iRemaining;
+    pPush      = pPush->pNext;
+  }
+
+  if (iTempsize <= iPushsize)          /* got enough push data? */
+  {
+    while (iTempsize)
+    {
+      pPush = pData->pFirstpushdata;
+                                       /* enough data remaining in this buffer? */
+      if (pPush->iRemaining <= iTempsize)
+      {                                /* no: then copy what we've got */
+        MNG_COPY (pTempbuf, pPush->pDatanext, pPush->iRemaining);
+                                       /* move pointers & lengths */
+        pTempbuf  += pPush->iRemaining;
+        *iRead    += pPush->iRemaining;
+        iTempsize -= pPush->iRemaining;
+                                       /* release the depleted buffer */
+        iRetcode = mng_release_pushdata (pData);
+        if (iRetcode)
+          return iRetcode;
+      }
+      else
+      {                                /* copy the needed bytes */
+        MNG_COPY (pTempbuf, pPush->pDatanext, iTempsize);
+                                       /* move pointers & lengths */
+        pPush->iRemaining -= iTempsize;
+        pPush->pDatanext  += iTempsize;
+        pTempbuf          += iTempsize;
+        *iRead            += iTempsize;
+        iTempsize         = 0;         /* all done!!! */
+      }
+    }
+  }
+  else
+  {
+    mng_uint32 iTempread = 0;
+                                       /* get it from the app then */
+    if (!pData->fReaddata (((mng_handle)pData), pTempbuf, iTempsize, &iTempread))
+      MNG_ERROR (pData, MNG_APPIOERROR);
+
+    *iRead += iTempread;
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode read_databuffer (mng_datap    pData,
+                                       mng_uint8p   pBuf,
+                                       mng_uint8p * pBufnext,
+                                       mng_uint32   iSize,
+                                       mng_uint32 * iRead)
+{
+  mng_retcode iRetcode;
+  
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DATABUFFER, MNG_LC_START);
+#endif
+
+  if (pData->bSuspensionmode)
+  {
+    mng_uint8p pTemp;
+    mng_uint32 iTemp;
+
+    *iRead = 0;                        /* let's be negative about the outcome */
+
+    if (!pData->pSuspendbuf)           /* need to create a suspension buffer ? */
+    {
+      pData->iSuspendbufsize = MNG_SUSPENDBUFFERSIZE;
+                                       /* so, create it */
+      MNG_ALLOC (pData, pData->pSuspendbuf, pData->iSuspendbufsize);
+
+      pData->iSuspendbufleft = 0;      /* make sure to fill it first time */
+      pData->pSuspendbufnext = pData->pSuspendbuf;
+    }
+                                       /* more than our buffer can hold ? */
+    if (iSize > pData->iSuspendbufsize)
+    {
+      mng_uint32 iRemain;
+
+      if (!*pBufnext)                  /* first time ? */
+      {
+        if (pData->iSuspendbufleft)    /* do we have some data left ? */
+        {                              /* then copy it */
+          MNG_COPY (pBuf, pData->pSuspendbufnext, pData->iSuspendbufleft);
+                                       /* fixup variables */
+          *pBufnext              = pBuf + pData->iSuspendbufleft;
+          pData->pSuspendbufnext = pData->pSuspendbuf;
+          pData->iSuspendbufleft = 0;
+        }
+        else
+        {
+          *pBufnext              = pBuf;
+        }
+      }
+                                       /* calculate how much to get */
+      iRemain = iSize - (mng_uint32)(*pBufnext - pBuf);
+                                       /* let's go get it */
+      iRetcode = read_data (pData, *pBufnext, iRemain, &iTemp);
+      if (iRetcode)
+        return iRetcode;
+                                       /* first read after suspension return 0 means EOF */
+      if ((pData->iSuspendpoint) && (iTemp == 0))
+      {                                /* that makes it final */
+        mng_retcode iRetcode = mng_process_eof (pData);
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+                                       /* indicate the source is depleted */
+        *iRead = iSize - iRemain + iTemp;
+      }
+      else
+      {
+        if (iTemp < iRemain)           /* suspension required ? */
+        {
+          *pBufnext         = *pBufnext + iTemp;
+          pData->bSuspended = MNG_TRUE;
+        }
+        else
+        {
+          *iRead = iSize;              /* got it all now ! */
+        }
+      }
+    }
+    else
+    {                                  /* need to read some more ? */
+      while ((!pData->bSuspended) && (!pData->bEOF) && (iSize > pData->iSuspendbufleft))
+      {                                /* not enough space left in buffer ? */
+        if (pData->iSuspendbufsize - pData->iSuspendbufleft -
+            (mng_uint32)(pData->pSuspendbufnext - pData->pSuspendbuf) <
+                                                          MNG_SUSPENDREQUESTSIZE)
+        {
+          if (pData->iSuspendbufleft)  /* then lets shift (if there's anything left) */
+            MNG_COPY (pData->pSuspendbuf, pData->pSuspendbufnext, pData->iSuspendbufleft);
+                                       /* adjust running pointer */
+          pData->pSuspendbufnext = pData->pSuspendbuf;
+        }
+                                       /* still not enough room ? */
+        if (pData->iSuspendbufsize - pData->iSuspendbufleft < MNG_SUSPENDREQUESTSIZE)
+          MNG_ERROR (pData, MNG_INTERNALERROR);
+                                       /* now read some more data */
+        pTemp = pData->pSuspendbufnext + pData->iSuspendbufleft;
+
+        iRetcode = read_data (pData, pTemp, MNG_SUSPENDREQUESTSIZE, &iTemp);
+        if (iRetcode)
+          return iRetcode;
+                                       /* adjust fill-counter */
+        pData->iSuspendbufleft += iTemp;
+                                       /* first read after suspension returning 0 means EOF */
+        if ((pData->iSuspendpoint) && (iTemp == 0))
+        {                              /* that makes it final */
+          mng_retcode iRetcode = mng_process_eof (pData);
+          if (iRetcode)                /* on error bail out */
+            return iRetcode;
+
+          if (pData->iSuspendbufleft)  /* return the leftover scraps */
+            MNG_COPY (pBuf, pData->pSuspendbufnext, pData->iSuspendbufleft);
+                                       /* and indicate so */
+          *iRead = pData->iSuspendbufleft;
+          pData->pSuspendbufnext = pData->pSuspendbuf;
+          pData->iSuspendbufleft = 0;
+        }
+        else
+        {                              /* suspension required ? */
+          if ((iSize > pData->iSuspendbufleft) && (iTemp < MNG_SUSPENDREQUESTSIZE))
+            pData->bSuspended = MNG_TRUE;
+
+        }
+
+        pData->iSuspendpoint = 0;      /* reset it here in case we loop back */
+      }
+
+      if ((!pData->bSuspended) && (!pData->bEOF))
+      {                                /* return the data ! */
+        MNG_COPY (pBuf, pData->pSuspendbufnext, iSize);
+
+        *iRead = iSize;                /* returned it all */
+                                       /* adjust suspension-buffer variables */
+        pData->pSuspendbufnext += iSize;
+        pData->iSuspendbufleft -= iSize;
+      }
+    }
+  }
+  else
+  {
+    iRetcode = read_data (pData, (mng_ptr)pBuf, iSize, iRead);
+    if (iRetcode)
+      return iRetcode;
+    if (*iRead == 0)                   /* suspension required ? */
+      pData->bSuspended = MNG_TRUE;
+  }
+
+  pData->iSuspendpoint = 0;            /* safely reset it here ! */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_DATABUFFER, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode process_raw_chunk (mng_datap  pData,
+                                         mng_uint8p pBuf,
+                                         mng_uint32 iBuflen)
+{
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  /* the table-idea & binary search code was adapted from
+     libpng 1.1.0 (pngread.c) */
+  /* NOTE1: the table must remain sorted by chunkname, otherwise the binary
+     search will break !!! (ps. watch upper-/lower-case chunknames !!) */
+  /* NOTE2: the layout must remain equal to the header part of all the
+     chunk-structures (yes, that means even the pNext and pPrev fields;
+     it's wasting a bit of space, but hey, the code is a lot easier) */
+
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+  mng_chunk_header mng_chunk_unknown = {MNG_UINT_HUH, mng_init_general, mng_free_unknown,
+                                        mng_read_unknown, mng_write_unknown, mng_assign_unknown, 0, 0, sizeof(mng_unknown_chunk)};
+#else
+  mng_chunk_header mng_chunk_unknown = {MNG_UINT_HUH, mng_init_unknown, mng_free_unknown,
+                                        mng_read_unknown, mng_write_unknown, mng_assign_unknown, 0, 0};
+#endif
+
+#ifdef MNG_OPTIMIZE_CHUNKINITFREE
+
+  mng_chunk_header mng_chunk_table [] =
+  {
+#ifndef MNG_SKIPCHUNK_BACK
+    {MNG_UINT_BACK, mng_init_general, mng_free_general, mng_read_back, mng_write_back, mng_assign_general, 0, 0, sizeof(mng_back)},
+#endif
+#ifndef MNG_SKIPCHUNK_BASI
+    {MNG_UINT_BASI, mng_init_general, mng_free_general, mng_read_basi, mng_write_basi, mng_assign_general, 0, 0, sizeof(mng_basi)},
+#endif
+#ifndef MNG_SKIPCHUNK_CLIP
+    {MNG_UINT_CLIP, mng_init_general, mng_free_general, mng_read_clip, mng_write_clip, mng_assign_general, 0, 0, sizeof(mng_clip)},
+#endif
+#ifndef MNG_SKIPCHUNK_CLON
+    {MNG_UINT_CLON, mng_init_general, mng_free_general, mng_read_clon, mng_write_clon, mng_assign_general, 0, 0, sizeof(mng_clon)},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+    {MNG_UINT_DBYK, mng_init_general, mng_free_dbyk,    mng_read_dbyk, mng_write_dbyk, mng_assign_dbyk,    0, 0, sizeof(mng_dbyk)},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_DEFI
+    {MNG_UINT_DEFI, mng_init_general, mng_free_general, mng_read_defi, mng_write_defi, mng_assign_general, 0, 0, sizeof(mng_defi)},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_UINT_DHDR, mng_init_general, mng_free_general, mng_read_dhdr, mng_write_dhdr, mng_assign_general, 0, 0, sizeof(mng_dhdr)},
+#endif
+#ifndef MNG_SKIPCHUNK_DISC
+    {MNG_UINT_DISC, mng_init_general, mng_free_disc,    mng_read_disc, mng_write_disc, mng_assign_disc,    0, 0, sizeof(mng_disc)},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DROP
+    {MNG_UINT_DROP, mng_init_general, mng_free_drop,    mng_read_drop, mng_write_drop, mng_assign_drop,    0, 0, sizeof(mng_drop)},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_LOOP
+    {MNG_UINT_ENDL, mng_init_general, mng_free_general, mng_read_endl, mng_write_endl, mng_assign_general, 0, 0, sizeof(mng_endl)},
+#endif
+#ifndef MNG_SKIPCHUNK_FRAM
+    {MNG_UINT_FRAM, mng_init_general, mng_free_fram,    mng_read_fram, mng_write_fram, mng_assign_fram,    0, 0, sizeof(mng_fram)},
+#endif
+    {MNG_UINT_IDAT, mng_init_general, mng_free_idat,    mng_read_idat, mng_write_idat, mng_assign_idat,    0, 0, sizeof(mng_idat)},  /* 12-th element! */
+    {MNG_UINT_IEND, mng_init_general, mng_free_general, mng_read_iend, mng_write_iend, mng_assign_general, 0, 0, sizeof(mng_iend)},
+    {MNG_UINT_IHDR, mng_init_general, mng_free_general, mng_read_ihdr, mng_write_ihdr, mng_assign_general, 0, 0, sizeof(mng_ihdr)},
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+    {MNG_UINT_IJNG, mng_init_general, mng_free_general, mng_read_ijng, mng_write_ijng, mng_assign_general, 0, 0, sizeof(mng_ijng)},
+#endif
+    {MNG_UINT_IPNG, mng_init_general, mng_free_general, mng_read_ipng, mng_write_ipng, mng_assign_general, 0, 0, sizeof(mng_ipng)},
+#endif
+#ifdef MNG_INCLUDE_JNG
+    {MNG_UINT_JDAA, mng_init_general, mng_free_jdaa,    mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa,    0, 0, sizeof(mng_jdaa)},
+    {MNG_UINT_JDAT, mng_init_general, mng_free_jdat,    mng_read_jdat, mng_write_jdat, mng_assign_jdat,    0, 0, sizeof(mng_jdat)},
+    {MNG_UINT_JHDR, mng_init_general, mng_free_general, mng_read_jhdr, mng_write_jhdr, mng_assign_general, 0, 0, sizeof(mng_jhdr)},
+    {MNG_UINT_JSEP, mng_init_general, mng_free_general, mng_read_jsep, mng_write_jsep, mng_assign_general, 0, 0, sizeof(mng_jsep)},
+    {MNG_UINT_JdAA, mng_init_general, mng_free_jdaa,    mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa,    0, 0, sizeof(mng_jdaa)},
+#endif
+#ifndef MNG_SKIPCHUNK_LOOP
+    {MNG_UINT_LOOP, mng_init_general, mng_free_loop,    mng_read_loop, mng_write_loop, mng_assign_loop,    0, 0, sizeof(mng_loop)},
+#endif
+#ifndef MNG_SKIPCHUNK_MAGN
+    {MNG_UINT_MAGN, mng_init_general, mng_free_general, mng_read_magn, mng_write_magn, mng_assign_general, 0, 0, sizeof(mng_magn)},
+#endif
+    {MNG_UINT_MEND, mng_init_general, mng_free_general, mng_read_mend, mng_write_mend, mng_assign_general, 0, 0, sizeof(mng_mend)},
+    {MNG_UINT_MHDR, mng_init_general, mng_free_general, mng_read_mhdr, mng_write_mhdr, mng_assign_general, 0, 0, sizeof(mng_mhdr)},
+#ifndef MNG_SKIPCHUNK_MOVE
+    {MNG_UINT_MOVE, mng_init_general, mng_free_general, mng_read_move, mng_write_move, mng_assign_general, 0, 0, sizeof(mng_move)},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+    {MNG_UINT_ORDR, mng_init_general, mng_free_ordr,    mng_read_ordr, mng_write_ordr, mng_assign_ordr,    0, 0, sizeof(mng_ordr)},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_UINT_PAST, mng_init_general, mng_free_past,    mng_read_past, mng_write_past, mng_assign_past,    0, 0, sizeof(mng_past)},
+#endif
+    {MNG_UINT_PLTE, mng_init_general, mng_free_general, mng_read_plte, mng_write_plte, mng_assign_general, 0, 0, sizeof(mng_plte)},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_UINT_PPLT, mng_init_general, mng_free_general, mng_read_pplt, mng_write_pplt, mng_assign_general, 0, 0, sizeof(mng_pplt)},
+    {MNG_UINT_PROM, mng_init_general, mng_free_general, mng_read_prom, mng_write_prom, mng_assign_general, 0, 0, sizeof(mng_prom)},
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+    {MNG_UINT_SAVE, mng_init_general, mng_free_save,    mng_read_save, mng_write_save, mng_assign_save,    0, 0, sizeof(mng_save)},
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+    {MNG_UINT_SEEK, mng_init_general, mng_free_seek,    mng_read_seek, mng_write_seek, mng_assign_seek,    0, 0, sizeof(mng_seek)},
+#endif
+#ifndef MNG_SKIPCHUNK_SHOW
+    {MNG_UINT_SHOW, mng_init_general, mng_free_general, mng_read_show, mng_write_show, mng_assign_general, 0, 0, sizeof(mng_show)},
+#endif
+#ifndef MNG_SKIPCHUNK_TERM
+    {MNG_UINT_TERM, mng_init_general, mng_free_general, mng_read_term, mng_write_term, mng_assign_general, 0, 0, sizeof(mng_term)},
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+    {MNG_UINT_bKGD, mng_init_general, mng_free_general, mng_read_bkgd, mng_write_bkgd, mng_assign_general, 0, 0, sizeof(mng_bkgd)},
+#endif
+#ifndef MNG_SKIPCHUNK_cHRM
+    {MNG_UINT_cHRM, mng_init_general, mng_free_general, mng_read_chrm, mng_write_chrm, mng_assign_general, 0, 0, sizeof(mng_chrm)},
+#endif
+#ifndef MNG_SKIPCHUNK_eXPI
+    {MNG_UINT_eXPI, mng_init_general, mng_free_expi,    mng_read_expi, mng_write_expi, mng_assign_expi,    0, 0, sizeof(mng_expi)},
+#endif
+#ifndef MNG_SKIPCHUNK_evNT
+    {MNG_UINT_evNT, mng_init_general, mng_free_evnt,    mng_read_evnt, mng_write_evnt, mng_assign_evnt,    0, 0, sizeof(mng_evnt)},
+#endif
+#ifndef MNG_SKIPCHUNK_fPRI
+    {MNG_UINT_fPRI, mng_init_general, mng_free_general, mng_read_fpri, mng_write_fpri, mng_assign_general, 0, 0, sizeof(mng_fpri)},
+#endif
+#ifndef MNG_SKIPCHUNK_gAMA
+    {MNG_UINT_gAMA, mng_init_general, mng_free_general, mng_read_gama, mng_write_gama, mng_assign_general, 0, 0, sizeof(mng_gama)},
+#endif
+#ifndef MNG_SKIPCHUNK_hIST
+    {MNG_UINT_hIST, mng_init_general, mng_free_general, mng_read_hist, mng_write_hist, mng_assign_general, 0, 0, sizeof(mng_hist)},
+#endif
+#ifndef MNG_SKIPCHUNK_iCCP
+    {MNG_UINT_iCCP, mng_init_general, mng_free_iccp,    mng_read_iccp, mng_write_iccp, mng_assign_iccp,    0, 0, sizeof(mng_iccp)},
+#endif
+#ifndef MNG_SKIPCHUNK_iTXt
+    {MNG_UINT_iTXt, mng_init_general, mng_free_itxt,    mng_read_itxt, mng_write_itxt, mng_assign_itxt,    0, 0, sizeof(mng_itxt)},
+#endif
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_UINT_mpNG, mng_init_general, mng_free_mpng,    mng_read_mpng, mng_write_mpng, mng_assign_mpng,    0, 0, sizeof(mng_mpng)},
+#endif
+#ifndef MNG_SKIPCHUNK_nEED
+    {MNG_UINT_nEED, mng_init_general, mng_free_need,    mng_read_need, mng_write_need, mng_assign_need,    0, 0, sizeof(mng_need)},
+#endif
+/* TODO:     {MNG_UINT_oFFs, 0, 0, 0, 0, 0, 0},  */
+/* TODO:     {MNG_UINT_pCAL, 0, 0, 0, 0, 0, 0},  */
+#ifndef MNG_SKIPCHUNK_pHYg
+    {MNG_UINT_pHYg, mng_init_general, mng_free_general, mng_read_phyg, mng_write_phyg, mng_assign_general, 0, 0, sizeof(mng_phyg)},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYs
+    {MNG_UINT_pHYs, mng_init_general, mng_free_general, mng_read_phys, mng_write_phys, mng_assign_general, 0, 0, sizeof(mng_phys)},
+#endif
+#ifndef MNG_SKIPCHUNK_sBIT
+    {MNG_UINT_sBIT, mng_init_general, mng_free_general, mng_read_sbit, mng_write_sbit, mng_assign_general, 0, 0, sizeof(mng_sbit)},
+#endif
+/* TODO:     {MNG_UINT_sCAL, 0, 0, 0, 0, 0, 0},  */
+#ifndef MNG_SKIPCHUNK_sPLT
+    {MNG_UINT_sPLT, mng_init_general, mng_free_splt,    mng_read_splt, mng_write_splt, mng_assign_splt,    0, 0, sizeof(mng_splt)},
+#endif
+    {MNG_UINT_sRGB, mng_init_general, mng_free_general, mng_read_srgb, mng_write_srgb, mng_assign_general, 0, 0, sizeof(mng_srgb)},
+#ifndef MNG_SKIPCHUNK_tEXt
+    {MNG_UINT_tEXt, mng_init_general, mng_free_text,    mng_read_text, mng_write_text, mng_assign_text,    0, 0, sizeof(mng_text)},
+#endif
+#ifndef MNG_SKIPCHUNK_tIME
+    {MNG_UINT_tIME, mng_init_general, mng_free_general, mng_read_time, mng_write_time, mng_assign_general, 0, 0, sizeof(mng_time)},
+#endif
+    {MNG_UINT_tRNS, mng_init_general, mng_free_general, mng_read_trns, mng_write_trns, mng_assign_general, 0, 0, sizeof(mng_trns)},
+#ifndef MNG_SKIPCHUNK_zTXt
+    {MNG_UINT_zTXt, mng_init_general, mng_free_ztxt,    mng_read_ztxt, mng_write_ztxt, mng_assign_ztxt,    0, 0, sizeof(mng_ztxt)},
+#endif
+  };
+
+#else                        /* MNG_OPTIMIZE_CHUNKINITFREE */
+
+  mng_chunk_header mng_chunk_table [] =
+  {
+#ifndef MNG_SKIPCHUNK_BACK
+    {MNG_UINT_BACK, mng_init_back, mng_free_back, mng_read_back, mng_write_back, mng_assign_back, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_BASI
+    {MNG_UINT_BASI, mng_init_basi, mng_free_basi, mng_read_basi, mng_write_basi, mng_assign_basi, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_CLIP
+    {MNG_UINT_CLIP, mng_init_clip, mng_free_clip, mng_read_clip, mng_write_clip, mng_assign_clip, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_CLON
+    {MNG_UINT_CLON, mng_init_clon, mng_free_clon, mng_read_clon, mng_write_clon, mng_assign_clon, 0, 0},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DBYK
+    {MNG_UINT_DBYK, mng_init_dbyk, mng_free_dbyk, mng_read_dbyk, mng_write_dbyk, mng_assign_dbyk, 0, 0},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_DEFI
+    {MNG_UINT_DEFI, mng_init_defi, mng_free_defi, mng_read_defi, mng_write_defi, mng_assign_defi, 0, 0},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_UINT_DHDR, mng_init_dhdr, mng_free_dhdr, mng_read_dhdr, mng_write_dhdr, mng_assign_dhdr, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_DISC
+    {MNG_UINT_DISC, mng_init_disc, mng_free_disc, mng_read_disc, mng_write_disc, mng_assign_disc, 0, 0},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_DROP
+    {MNG_UINT_DROP, mng_init_drop, mng_free_drop, mng_read_drop, mng_write_drop, mng_assign_drop, 0, 0},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_LOOP
+    {MNG_UINT_ENDL, mng_init_endl, mng_free_endl, mng_read_endl, mng_write_endl, mng_assign_endl, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_FRAM
+    {MNG_UINT_FRAM, mng_init_fram, mng_free_fram, mng_read_fram, mng_write_fram, mng_assign_fram, 0, 0},
+#endif
+    {MNG_UINT_IDAT, mng_init_idat, mng_free_idat, mng_read_idat, mng_write_idat, mng_assign_idat, 0, 0},  /* 12-th element! */
+    {MNG_UINT_IEND, mng_init_iend, mng_free_iend, mng_read_iend, mng_write_iend, mng_assign_iend, 0, 0},
+    {MNG_UINT_IHDR, mng_init_ihdr, mng_free_ihdr, mng_read_ihdr, mng_write_ihdr, mng_assign_ihdr, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+#ifdef MNG_INCLUDE_JNG
+    {MNG_UINT_IJNG, mng_init_ijng, mng_free_ijng, mng_read_ijng, mng_write_ijng, mng_assign_ijng, 0, 0},
+#endif
+    {MNG_UINT_IPNG, mng_init_ipng, mng_free_ipng, mng_read_ipng, mng_write_ipng, mng_assign_ipng, 0, 0},
+#endif
+#ifdef MNG_INCLUDE_JNG
+    {MNG_UINT_JDAA, mng_init_jdaa, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0},
+    {MNG_UINT_JDAT, mng_init_jdat, mng_free_jdat, mng_read_jdat, mng_write_jdat, mng_assign_jdat, 0, 0},
+    {MNG_UINT_JHDR, mng_init_jhdr, mng_free_jhdr, mng_read_jhdr, mng_write_jhdr, mng_assign_jhdr, 0, 0},
+    {MNG_UINT_JSEP, mng_init_jsep, mng_free_jsep, mng_read_jsep, mng_write_jsep, mng_assign_jsep, 0, 0},
+    {MNG_UINT_JdAA, mng_init_jdaa, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_LOOP
+    {MNG_UINT_LOOP, mng_init_loop, mng_free_loop, mng_read_loop, mng_write_loop, mng_assign_loop, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_MAGN
+    {MNG_UINT_MAGN, mng_init_magn, mng_free_magn, mng_read_magn, mng_write_magn, mng_assign_magn, 0, 0},
+#endif
+    {MNG_UINT_MEND, mng_init_mend, mng_free_mend, mng_read_mend, mng_write_mend, mng_assign_mend, 0, 0},
+    {MNG_UINT_MHDR, mng_init_mhdr, mng_free_mhdr, mng_read_mhdr, mng_write_mhdr, mng_assign_mhdr, 0, 0},
+#ifndef MNG_SKIPCHUNK_MOVE
+    {MNG_UINT_MOVE, mng_init_move, mng_free_move, mng_read_move, mng_write_move, mng_assign_move, 0, 0},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+#ifndef MNG_SKIPCHUNK_ORDR
+    {MNG_UINT_ORDR, mng_init_ordr, mng_free_ordr, mng_read_ordr, mng_write_ordr, mng_assign_ordr, 0, 0},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_UINT_PAST, mng_init_past, mng_free_past, mng_read_past, mng_write_past, mng_assign_past, 0, 0},
+#endif
+    {MNG_UINT_PLTE, mng_init_plte, mng_free_plte, mng_read_plte, mng_write_plte, mng_assign_plte, 0, 0},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_UINT_PPLT, mng_init_pplt, mng_free_pplt, mng_read_pplt, mng_write_pplt, mng_assign_pplt, 0, 0},
+    {MNG_UINT_PROM, mng_init_prom, mng_free_prom, mng_read_prom, mng_write_prom, mng_assign_prom, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+    {MNG_UINT_SAVE, mng_init_save, mng_free_save, mng_read_save, mng_write_save, mng_assign_save, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+    {MNG_UINT_SEEK, mng_init_seek, mng_free_seek, mng_read_seek, mng_write_seek, mng_assign_seek, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_SHOW
+    {MNG_UINT_SHOW, mng_init_show, mng_free_show, mng_read_show, mng_write_show, mng_assign_show, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_TERM
+    {MNG_UINT_TERM, mng_init_term, mng_free_term, mng_read_term, mng_write_term, mng_assign_term, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+    {MNG_UINT_bKGD, mng_init_bkgd, mng_free_bkgd, mng_read_bkgd, mng_write_bkgd, mng_assign_bkgd, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_cHRM
+    {MNG_UINT_cHRM, mng_init_chrm, mng_free_chrm, mng_read_chrm, mng_write_chrm, mng_assign_chrm, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_eXPI
+    {MNG_UINT_eXPI, mng_init_expi, mng_free_expi, mng_read_expi, mng_write_expi, mng_assign_expi, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_evNT
+    {MNG_UINT_evNT, mng_init_evnt, mng_free_evnt, mng_read_evnt, mng_write_evnt, mng_assign_evnt, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_fPRI
+    {MNG_UINT_fPRI, mng_init_fpri, mng_free_fpri, mng_read_fpri, mng_write_fpri, mng_assign_fpri, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_gAMA
+    {MNG_UINT_gAMA, mng_init_gama, mng_free_gama, mng_read_gama, mng_write_gama, mng_assign_gama, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_hIST
+    {MNG_UINT_hIST, mng_init_hist, mng_free_hist, mng_read_hist, mng_write_hist, mng_assign_hist, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_iCCP
+    {MNG_UINT_iCCP, mng_init_iccp, mng_free_iccp, mng_read_iccp, mng_write_iccp, mng_assign_iccp, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_iTXt
+    {MNG_UINT_iTXt, mng_init_itxt, mng_free_itxt, mng_read_itxt, mng_write_itxt, mng_assign_itxt, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_nEED
+    {MNG_UINT_nEED, mng_init_need, mng_free_need, mng_read_need, mng_write_need, mng_assign_need, 0, 0},
+#endif
+/* TODO:     {MNG_UINT_oFFs, 0, 0, 0, 0, 0, 0},  */
+/* TODO:     {MNG_UINT_pCAL, 0, 0, 0, 0, 0, 0},  */
+#ifndef MNG_SKIPCHUNK_pHYg
+    {MNG_UINT_pHYg, mng_init_phyg, mng_free_phyg, mng_read_phyg, mng_write_phyg, mng_assign_phyg, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYs
+    {MNG_UINT_pHYs, mng_init_phys, mng_free_phys, mng_read_phys, mng_write_phys, mng_assign_phys, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_sBIT
+    {MNG_UINT_sBIT, mng_init_sbit, mng_free_sbit, mng_read_sbit, mng_write_sbit, mng_assign_sbit, 0, 0},
+#endif
+/* TODO:     {MNG_UINT_sCAL, 0, 0, 0, 0, 0, 0},  */
+#ifndef MNG_SKIPCHUNK_sPLT
+    {MNG_UINT_sPLT, mng_init_splt, mng_free_splt, mng_read_splt, mng_write_splt, mng_assign_splt, 0, 0},
+#endif
+    {MNG_UINT_sRGB, mng_init_srgb, mng_free_srgb, mng_read_srgb, mng_write_srgb, mng_assign_srgb, 0, 0},
+#ifndef MNG_SKIPCHUNK_tEXt
+    {MNG_UINT_tEXt, mng_init_text, mng_free_text, mng_read_text, mng_write_text, mng_assign_text, 0, 0},
+#endif
+#ifndef MNG_SKIPCHUNK_tIME
+    {MNG_UINT_tIME, mng_init_time, mng_free_time, mng_read_time, mng_write_time, mng_assign_time, 0, 0},
+#endif
+    {MNG_UINT_tRNS, mng_init_trns, mng_free_trns, mng_read_trns, mng_write_trns, mng_assign_trns, 0, 0},
+#ifndef MNG_SKIPCHUNK_zTXt
+    {MNG_UINT_zTXt, mng_init_ztxt, mng_free_ztxt, mng_read_ztxt, mng_write_ztxt, mng_assign_ztxt, 0, 0},
+#endif
+  };
+
+#endif                       /* MNG_OPTIMIZE_CHUNKINITFREE */
+
+                                       /* binary search variables */
+  mng_int32         iTop, iLower, iUpper, iMiddle;
+  mng_chunk_headerp pEntry;            /* pointer to found entry */
+#else
+  mng_chunk_header  sEntry;            /* temp chunk-header */
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+
+  mng_chunkid       iChunkname;        /* the chunk's tag */
+  mng_chunkp        pChunk;            /* chunk structure (if #define MNG_STORE_CHUNKS) */
+  mng_retcode       iRetcode;          /* temporary error-code */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RAW_CHUNK, MNG_LC_START);
+#endif
+                                       /* reset timer indicator on read-cycle */
+  if ((pData->bReading) && (!pData->bDisplaying))
+    pData->bTimerset = MNG_FALSE;
+                                       /* get the chunkname */
+  iChunkname = (mng_chunkid)(mng_get_uint32 (pBuf));
+
+  pBuf += sizeof (mng_chunkid);        /* adjust the buffer */
+  iBuflen -= sizeof (mng_chunkid);
+  pChunk = 0;
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+                                       /* determine max index of table */
+  iTop = (sizeof (mng_chunk_table) / sizeof (mng_chunk_table [0])) - 1;
+
+  /* binary search; with 54 chunks, worst-case is 7 comparisons */
+  iLower  = 0;
+#ifndef MNG_NO_DELTA_PNG
+  iMiddle = 11;                        /* start with the IDAT entry */
+#else
+  iMiddle = 8;
+#endif
+  iUpper  = iTop;
+  pEntry  = 0;                         /* no goods yet! */
+
+  do                                   /* the binary search itself */
+    {
+      if (mng_chunk_table [iMiddle].iChunkname < iChunkname)
+        iLower = iMiddle + 1;
+      else if (mng_chunk_table [iMiddle].iChunkname > iChunkname)
+        iUpper = iMiddle - 1;
+      else
+      {
+        pEntry = &mng_chunk_table [iMiddle];
+        break;
+      }
+
+      iMiddle = (iLower + iUpper) >> 1;
+    }
+  while (iLower <= iUpper);
+
+  if (!pEntry)                         /* unknown chunk ? */
+    pEntry = &mng_chunk_unknown;       /* make it so! */
+
+#else /* MNG_OPTIMIZE_CHUNKREADER */
+
+  mng_get_chunkheader (iChunkname, &sEntry);
+
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+
+  pData->iChunkname = iChunkname;      /* keep track of where we are */
+  pData->iChunkseq++;
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+  if (pEntry->fRead)                   /* read-callback available ? */
+  {
+    iRetcode = pEntry->fRead (pData, pEntry, iBuflen, (mng_ptr)pBuf, &pChunk);
+
+    if (!iRetcode)                     /* everything oke ? */
+    {                                  /* remember unknown chunk's id */
+      if ((pChunk) && (pEntry->iChunkname == MNG_UINT_HUH))
+        ((mng_chunk_headerp)pChunk)->iChunkname = iChunkname;
+    }
+  }
+#else /* MNG_OPTIMIZE_CHUNKREADER */
+  if (sEntry.fRead)                    /* read-callback available ? */
+  {
+    iRetcode = sEntry.fRead (pData, &sEntry, iBuflen, (mng_ptr)pBuf, &pChunk);
+
+#ifndef MNG_OPTIMIZE_CHUNKREADER
+    if (!iRetcode)                     /* everything oke ? */
+    {                                  /* remember unknown chunk's id */
+      if ((pChunk) && (sEntry.iChunkname == MNG_UINT_HUH))
+        ((mng_chunk_headerp)pChunk)->iChunkname = iChunkname;
+    }
+#endif
+  }
+#endif /* MNG_OPTIMIZE_CHUNKREADER */
+  else
+    iRetcode = MNG_NOERROR;
+
+  if (pChunk)                          /* store this chunk ? */
+    mng_add_chunk (pData, pChunk);     /* do it */
+
+#ifdef MNG_INCLUDE_JNG                 /* implicit EOF ? */
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && (!pData->bHasJHDR))
+#else
+  if ((!pData->bHasMHDR) && (!pData->bHasIHDR))
+#endif
+    iRetcode = mng_process_eof (pData);/* then do some EOF processing */
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_PROCESS_RAW_CHUNK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode check_chunk_crc (mng_datap  pData,
+                                       mng_uint8p pBuf,
+                                       mng_uint32 iBuflen)
+{
+  mng_uint32  iCrc;                    /* calculated CRC */
+  mng_bool    bDiscard = MNG_FALSE;
+  mng_retcode iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CHUNK_CRC, MNG_LC_START);
+#endif
+
+  if (pData->iCrcmode & MNG_CRC_INPUT) /* crc included ? */
+  {
+    mng_bool bCritical = (mng_bool)((*pBuf & 0x20) == 0);
+    mng_uint32 iL = iBuflen - (mng_uint32)(sizeof (iCrc));
+
+    if (((bCritical ) && (pData->iCrcmode & MNG_CRC_CRITICAL )) ||
+        ((!bCritical) && (pData->iCrcmode & MNG_CRC_ANCILLARY)))
+    {                                  /* calculate the crc */
+      iCrc = mng_crc (pData, pBuf, iL);
+                                       /* and check it */
+      if (!(iCrc == mng_get_uint32 (pBuf + iL)))
+      {
+        mng_bool bWarning = MNG_FALSE;
+        mng_bool bError   = MNG_FALSE;
+
+        if (bCritical)
+        {
+          switch (pData->iCrcmode & MNG_CRC_CRITICAL)
+          {
+            case MNG_CRC_CRITICAL_WARNING  : { bWarning = MNG_TRUE; break; }
+            case MNG_CRC_CRITICAL_ERROR    : { bError   = MNG_TRUE; break; }
+          }
+        }
+        else
+        {
+          switch (pData->iCrcmode & MNG_CRC_ANCILLARY)
+          {
+            case MNG_CRC_ANCILLARY_DISCARD : { bDiscard = MNG_TRUE; break; }
+            case MNG_CRC_ANCILLARY_WARNING : { bWarning = MNG_TRUE; break; }
+            case MNG_CRC_ANCILLARY_ERROR   : { bError   = MNG_TRUE; break; }
+          }
+        }
+
+        if (bWarning)
+          MNG_WARNING (pData, MNG_INVALIDCRC);
+        if (bError)
+          MNG_ERROR (pData, MNG_INVALIDCRC);
+      }
+    }
+
+    if (!bDiscard)                     /* still processing ? */
+      iRetcode = process_raw_chunk (pData, pBuf, iL);
+  }
+  else
+  {                                    /* no crc => straight onto processing */
+    iRetcode = process_raw_chunk (pData, pBuf, iBuflen);
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CHUNK_CRC, MNG_LC_END);
+#endif
+
+  return iRetcode;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode read_chunk (mng_datap  pData)
+{
+  mng_uint32  iBufmax   = pData->iReadbufsize;
+  mng_uint8p  pBuf      = pData->pReadbuf;
+  mng_uint32  iBuflen   = 0;           /* number of bytes requested */
+  mng_uint32  iRead     = 0;           /* number of bytes read */
+  mng_retcode iRetcode  = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CHUNK, MNG_LC_START);
+#endif
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if (pData->pCurraniobj)              /* processing an animation object ? */
+  {
+    do                                 /* process it then */
+    {
+      iRetcode = ((mng_object_headerp)pData->pCurraniobj)->fProcess (pData, pData->pCurraniobj);
+                                       /* refresh needed ? */
+/*      if ((!iRetcode) && (!pData->bTimerset) && (pData->bNeedrefresh))
+        iRetcode = display_progressive_refresh (pData, 1); */
+                                       /* can we advance to next object ? */
+      if ((!iRetcode) && (pData->pCurraniobj) &&
+          (!pData->bTimerset) && (!pData->bSectionwait))
+      {                                /* reset timer indicator on read-cycle */
+        if ((pData->bReading) && (!pData->bDisplaying))
+          pData->bTimerset = MNG_FALSE;
+
+        pData->pCurraniobj = ((mng_object_headerp)pData->pCurraniobj)->pNext;
+                                       /* TERM processing to be done ? */
+        if ((!pData->pCurraniobj) && (pData->bHasTERM) && (!pData->bHasMHDR))
+          iRetcode = mng_process_display_mend (pData);
+      }
+    }                                  /* until error or a break or no more objects */
+    while ((!iRetcode) && (pData->pCurraniobj) &&
+           (!pData->bTimerset) && (!pData->bSectionwait) && (!pData->bFreezing));
+  }
+  else
+  {
+    if (pData->iBreakpoint)            /* do we need to finish something first ? */
+    {
+      switch (pData->iBreakpoint)      /* return to broken display routine */
+      {
+#ifndef MNG_SKIPCHUNK_FRAM
+        case  1 : { iRetcode = mng_process_display_fram2 (pData); break; }
+#endif
+        case  2 : { iRetcode = mng_process_display_ihdr  (pData); break; }
+#ifndef MNG_SKIPCHUNK_SHOW
+        case  3 : ;                     /* same as 4 !!! */
+        case  4 : { iRetcode = mng_process_display_show  (pData); break; }
+#endif
+#ifndef MNG_SKIPCHUNK_CLON
+        case  5 : { iRetcode = mng_process_display_clon2 (pData); break; }
+#endif
+#ifdef MNG_INCLUDE_JNG
+        case  7 : { iRetcode = mng_process_display_jhdr  (pData); break; }
+#endif
+        case  6 : ;                     /* same as 8 !!! */
+        case  8 : { iRetcode = mng_process_display_iend  (pData); break; }
+#ifndef MNG_SKIPCHUNK_MAGN
+        case  9 : { iRetcode = mng_process_display_magn2 (pData); break; }
+#endif
+        case 10 : { iRetcode = mng_process_display_mend2 (pData); break; }
+#ifndef MNG_SKIPCHUNK_PAST
+        case 11 : { iRetcode = mng_process_display_past2 (pData); break; }
+#endif
+      }
+    }
+  }
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#endif /* MNG_SUPPORT_DISPLAY */
+                                       /* can we continue processing now, or do we */
+                                       /* need to wait for the timer to finish (again) ? */
+#ifdef MNG_SUPPORT_DISPLAY
+  if ((!pData->bTimerset) && (!pData->bSectionwait) && (!pData->bEOF))
+#else
+  if (!pData->bEOF)
+#endif
+  {
+#ifdef MNG_SUPPORT_DISPLAY
+                                       /* freezing in progress ? */
+    if ((pData->bFreezing) && (pData->iSuspendpoint == 0))
+      pData->bRunning = MNG_FALSE;     /* then this is the right moment to do it */
+#endif
+
+    if (pData->iSuspendpoint <= 2)
+    {
+      iBuflen  = sizeof (mng_uint32);  /* read length */
+      iRetcode = read_databuffer (pData, pBuf, &pData->pReadbufnext, iBuflen, &iRead);
+
+      if (iRetcode)                    /* bail on errors */
+        return iRetcode;
+
+      if (pData->bSuspended)           /* suspended ? */
+        pData->iSuspendpoint = 2;
+      else                             /* save the length */
+      {
+        pData->iChunklen = mng_get_uint32 (pBuf);
+        if (pData->iChunklen > 0x7ffffff)
+           return MNG_INVALIDLENGTH;
+      }
+
+    }
+
+    if (!pData->bSuspended)            /* still going ? */
+    {                                  /* previously suspended or not eof ? */
+      if ((pData->iSuspendpoint > 2) || (iRead == iBuflen))
+      {                                /* determine length chunkname + data (+ crc) */
+        if (pData->iCrcmode & MNG_CRC_INPUT)
+          iBuflen = pData->iChunklen + (mng_uint32)(sizeof (mng_chunkid) + sizeof (mng_uint32));
+        else
+          iBuflen = pData->iChunklen + (mng_uint32)(sizeof (mng_chunkid));
+
+                                       /* do we have enough data in the current push buffer ? */
+        if ((pData->pFirstpushdata) && (iBuflen <= pData->pFirstpushdata->iRemaining))
+        {
+          mng_pushdatap pPush  = pData->pFirstpushdata;
+          pBuf                 = pPush->pDatanext;
+          pPush->pDatanext    += iBuflen;
+          pPush->iRemaining   -= iBuflen;
+          pData->iSuspendpoint = 0;    /* safely reset this here ! */
+
+          iRetcode = check_chunk_crc (pData, pBuf, iBuflen);
+          if (iRetcode)
+            return iRetcode;
+
+          if (!pPush->iRemaining)      /* buffer depleted? then release it */
+            iRetcode = mng_release_pushdata (pData);
+        }
+        else
+        {
+          if (iBuflen < iBufmax)       /* does it fit in default buffer ? */
+          {                            /* note that we don't use the full size
+                                          so there's always a zero-byte at the
+                                          very end !!! */
+            iRetcode = read_databuffer (pData, pBuf, &pData->pReadbufnext, iBuflen, &iRead);
+            if (iRetcode)              /* bail on errors */
+              return iRetcode;
+
+            if (pData->bSuspended)     /* suspended ? */
+              pData->iSuspendpoint = 3;
+            else
+            {
+              if (iRead != iBuflen)    /* did we get all the data ? */
+                MNG_ERROR (pData, MNG_UNEXPECTEDEOF);
+              iRetcode = check_chunk_crc (pData, pBuf, iBuflen);
+            }
+          }
+          else
+          {
+            if (iBuflen > 16777216)    /* is the length incredible? */
+              MNG_ERROR (pData, MNG_IMPROBABLELENGTH);
+
+            if (!pData->iSuspendpoint) /* create additional large buffer ? */
+            {                          /* again reserve space for the last zero-byte */
+              pData->iLargebufsize = iBuflen + 1;
+              pData->pLargebufnext = MNG_NULL;
+              MNG_ALLOC (pData, pData->pLargebuf, pData->iLargebufsize);
+            }
+
+            iRetcode = read_databuffer (pData, pData->pLargebuf, &pData->pLargebufnext, iBuflen, &iRead);
+            if (iRetcode)
+              return iRetcode;
+
+            if (pData->bSuspended)     /* suspended ? */
+              pData->iSuspendpoint = 4;
+            else
+            {
+              if (iRead != iBuflen)    /* did we get all the data ? */
+                MNG_ERROR (pData, MNG_UNEXPECTEDEOF);
+              iRetcode = check_chunk_crc (pData, pData->pLargebuf, iBuflen);
+                                       /* cleanup additional large buffer */
+              MNG_FREE (pData, pData->pLargebuf, pData->iLargebufsize);
+            }
+          }
+        }
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+
+      }
+      else
+      {                                /* that's final */
+        iRetcode = mng_process_eof (pData);
+
+        if (iRetcode)                  /* on error bail out */
+          return iRetcode;
+
+        if ((iRead != 0) ||            /* did we get an unexpected eof ? */
+#ifdef MNG_INCLUDE_JNG
+            (pData->bHasIHDR || pData->bHasMHDR || pData->bHasJHDR))
+#else
+            (pData->bHasIHDR || pData->bHasMHDR))
+#endif
+          MNG_ERROR (pData, MNG_UNEXPECTEDEOF);
+      } 
+    }
+  }
+
+#ifdef MNG_SUPPORT_DISPLAY             /* refresh needed ? */
+  if ((!pData->bTimerset) && (!pData->bSuspended) && (pData->bNeedrefresh))
+  {
+    iRetcode = mng_display_progressive_refresh (pData, 1);
+
+    if (iRetcode)                      /* on error bail out */
+      return iRetcode;
+  }
+#endif
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_CHUNK, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+MNG_LOCAL mng_retcode process_pushedchunk (mng_datap pData)
+{
+  mng_pushdatap pPush;
+  mng_retcode   iRetcode = MNG_NOERROR;
+
+#ifdef MNG_SUPPORT_DISPLAY
+  if (pData->pCurraniobj)              /* processing an animation object ? */
+  {
+    do                                 /* process it then */
+    {
+      iRetcode = ((mng_object_headerp)pData->pCurraniobj)->fProcess (pData, pData->pCurraniobj);
+                                       /* refresh needed ? */
+/*      if ((!iRetcode) && (!pData->bTimerset) && (pData->bNeedrefresh))
+        iRetcode = display_progressive_refresh (pData, 1); */
+                                       /* can we advance to next object ? */
+      if ((!iRetcode) && (pData->pCurraniobj) &&
+          (!pData->bTimerset) && (!pData->bSectionwait))
+      {                                /* reset timer indicator on read-cycle */
+        if ((pData->bReading) && (!pData->bDisplaying))
+          pData->bTimerset = MNG_FALSE;
+
+        pData->pCurraniobj = ((mng_object_headerp)pData->pCurraniobj)->pNext;
+                                       /* TERM processing to be done ? */
+        if ((!pData->pCurraniobj) && (pData->bHasTERM) && (!pData->bHasMHDR))
+          iRetcode = mng_process_display_mend (pData);
+      }
+    }                                  /* until error or a break or no more objects */
+    while ((!iRetcode) && (pData->pCurraniobj) &&
+           (!pData->bTimerset) && (!pData->bSectionwait) && (!pData->bFreezing));
+  }
+  else
+  {
+    if (pData->iBreakpoint)            /* do we need to finish something first ? */
+    {
+      switch (pData->iBreakpoint)      /* return to broken display routine */
+      {
+#ifndef MNG_SKIPCHUNK_FRAM
+        case  1 : { iRetcode = mng_process_display_fram2 (pData); break; }
+#endif
+        case  2 : { iRetcode = mng_process_display_ihdr  (pData); break; }
+#ifndef MNG_SKIPCHUNK_SHOW
+        case  3 : ;                     /* same as 4 !!! */
+        case  4 : { iRetcode = mng_process_display_show  (pData); break; }
+#endif
+#ifndef MNG_SKIPCHUNK_CLON
+        case  5 : { iRetcode = mng_process_display_clon2 (pData); break; }
+#endif
+#ifdef MNG_INCLUDE_JNG
+        case  7 : { iRetcode = mng_process_display_jhdr  (pData); break; }
+#endif
+        case  6 : ;                     /* same as 8 !!! */
+        case  8 : { iRetcode = mng_process_display_iend  (pData); break; }
+#ifndef MNG_SKIPCHUNK_MAGN
+        case  9 : { iRetcode = mng_process_display_magn2 (pData); break; }
+#endif
+        case 10 : { iRetcode = mng_process_display_mend2 (pData); break; }
+#ifndef MNG_SKIPCHUNK_PAST
+        case 11 : { iRetcode = mng_process_display_past2 (pData); break; }
+#endif
+      }
+    }
+  }
+
+  if (iRetcode)                        /* on error bail out */
+    return iRetcode;
+
+#endif /* MNG_SUPPORT_DISPLAY */
+                                       /* can we continue processing now, or do we */
+                                       /* need to wait for the timer to finish (again) ? */
+#ifdef MNG_SUPPORT_DISPLAY
+  if ((!pData->bTimerset) && (!pData->bSectionwait) && (!pData->bEOF))
+#else
+  if (!pData->bEOF)
+#endif
+  {
+    pData->iSuspendpoint = 0;            /* safely reset it here ! */
+    pPush = pData->pFirstpushchunk;
+
+    iRetcode = process_raw_chunk (pData, pPush->pData, pPush->iLength);
+    if (iRetcode)
+      return iRetcode;
+
+#ifdef MNG_SUPPORT_DISPLAY             /* refresh needed ? */
+    if ((!pData->bTimerset) && (!pData->bSuspended) && (pData->bNeedrefresh))
+    {
+      iRetcode = mng_display_progressive_refresh (pData, 1);
+      if (iRetcode)                      /* on error bail out */
+        return iRetcode;
+    }
+#endif
+  }
+
+  return mng_release_pushchunk (pData);
+}
+
+/* ************************************************************************** */
+
+mng_retcode mng_read_graphic (mng_datap pData)
+{
+  mng_uint32  iBuflen;                 /* number of bytes requested */
+  mng_uint32  iRead;                   /* number of bytes read */
+  mng_retcode iRetcode;                /* temporary error-code */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_GRAPHIC, MNG_LC_START);
+#endif
+
+  if (!pData->pReadbuf)                /* buffer allocated ? */
+  {
+    pData->iReadbufsize = 4200;        /* allocate a default read buffer */
+    MNG_ALLOC (pData, pData->pReadbuf, pData->iReadbufsize);
+  }
+                                       /* haven't processed the signature ? */
+  if ((!pData->bHavesig) || (pData->iSuspendpoint == 1))
+  {
+    iBuflen = 2 * sizeof (mng_uint32); /* read signature */
+
+    iRetcode = read_databuffer (pData, pData->pReadbuf, &pData->pReadbufnext, iBuflen, &iRead);
+
+    if (iRetcode)
+      return iRetcode;
+
+    if (pData->bSuspended)             /* input suspension ? */
+      pData->iSuspendpoint = 1;
+    else
+    {
+      if (iRead != iBuflen)            /* full signature received ? */
+        MNG_ERROR (pData, MNG_UNEXPECTEDEOF);
+                                       /* is it a valid signature ? */
+      if (mng_get_uint32 (pData->pReadbuf) == PNG_SIG)
+        pData->eSigtype = mng_it_png;
+      else
+#ifdef MNG_INCLUDE_JNG
+      if (mng_get_uint32 (pData->pReadbuf) == JNG_SIG)
+        pData->eSigtype = mng_it_jng;
+      else
+#endif
+      if (mng_get_uint32 (pData->pReadbuf) == MNG_SIG)
+        pData->eSigtype = mng_it_mng;
+      else
+        MNG_ERROR (pData, MNG_INVALIDSIG);
+                                       /* all of it ? */
+      if (mng_get_uint32 (pData->pReadbuf+4) != POST_SIG)
+        MNG_ERROR (pData, MNG_INVALIDSIG);
+
+      pData->bHavesig = MNG_TRUE;
+    }
+  }
+
+  if (!pData->bSuspended)              /* still going ? */
+  {
+    do
+    {                                  /* reset timer during mng_read() ? */
+      if ((pData->bReading) && (!pData->bDisplaying))
+        pData->bTimerset = MNG_FALSE;
+
+      if (pData->pFirstpushchunk)      /* chunks pushed ? */
+        iRetcode = process_pushedchunk (pData); /* process the pushed chunk */
+      else
+        iRetcode = read_chunk (pData); /* read & process a chunk */
+
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+#ifdef MNG_SUPPORT_DISPLAY             /* until EOF or a break-request */
+    while (((!pData->bEOF) || (pData->pCurraniobj)) &&
+           (!pData->bSuspended) && (!pData->bSectionwait) &&
+           ((!pData->bTimerset) || ((pData->bReading) && (!pData->bDisplaying))));
+#else
+    while ((!pData->bEOF) && (!pData->bSuspended));
+#endif
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_READ_GRAPHIC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_READ_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_read.h b/files/Source/LibMNG/libmng_read.h
new file mode 100644
index 0000000..119cc3e
--- /dev/null
+++ b/files/Source/LibMNG/libmng_read.h
@@ -0,0 +1,53 @@
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_read.h             copyright (c) 2000-2004 G.Juyn   * */
+/* * version   : 1.0.8                                                      * */
+/* *                                                                        * */
+/* * purpose   : Read management (definition)                               * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the read management routines                 * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 10/18/2000 - G.Juyn                                * */
+/* *             - added closestream() processing for mng_cleanup()         * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/12/2004 - G.Juyn                                * */
+/* *             - added data-push mechanisms for specialized decoders      * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_read_h_
+#define _libmng_read_h_
+
+/* ************************************************************************** */
+
+mng_retcode mng_process_eof       (mng_datap pData);
+
+mng_retcode mng_release_pushdata  (mng_datap pData);
+
+mng_retcode mng_release_pushchunk (mng_datap pData);
+
+mng_retcode mng_read_graphic      (mng_datap pData);
+
+/* ************************************************************************** */
+
+#endif /* _libmng_read_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_trace.c b/files/Source/LibMNG/libmng_trace.c
new file mode 100644
index 0000000..a6a2cab
--- /dev/null
+++ b/files/Source/LibMNG/libmng_trace.c
@@ -0,0 +1,1683 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_trace.c            copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Trace functions (implementation)                           * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the trace functions                      * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - added callback error-reporting support                   * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/23/2000 - G.Juyn                                * */
+/* *             - added trace telltale reporting                           * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - added tracestrings for global animation color-chunks     * */
+/* *             - added tracestrings for get/set of default ZLIB/IJG parms * */
+/* *             - added tracestrings for global PLTE,tRNS,bKGD             * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added tracestrings for image-object promotion            * */
+/* *             - added tracestrings for delta-image processing            * */
+/* *             0.5.2 - 06/02/2000 - G.Juyn                                * */
+/* *             - added tracestrings for getalphaline callback             * */
+/* *             0.5.2 - 06/05/2000 - G.Juyn                                * */
+/* *             - added tracestring for RGB8_A8 canvasstyle                * */
+/* *             0.5.2 - 06/06/2000 - G.Juyn                                * */
+/* *             - added tracestring for mng_read_resume HLAPI function     * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added tracestrings for get/set speedtype                 * */
+/* *             - added tracestring for get imagelevel                     * */
+/* *             0.5.3 - 06/22/2000 - G.Juyn                                * */
+/* *             - added tracestring for delta-image processing             * */
+/* *             - added tracestrings for PPLT chunk processing             * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/07/2000 - G.Juyn                                * */
+/* *             - added tracecodes for special display processing          * */
+/* *             0.9.1 - 07/08/2000 - G.Juyn                                * */
+/* *             - added tracestring for get/set suspensionmode             * */
+/* *             - added tracestrings for get/set display variables         * */
+/* *             - added tracecode for read_databuffer (I/O-suspension)     * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added tracestrings for SAVE/SEEK callbacks               * */
+/* *             - added tracestrings for get/set sectionbreaks             * */
+/* *             - added tracestring for special error routine              * */
+/* *             0.9.1 - 07/19/2000 - G.Juyn                                * */
+/* *             - added tracestring for updatemngheader                    * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/31/2000 - G.Juyn                                * */
+/* *             - added tracestrings for status_xxxxx functions            * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *             - added tracestring for updatemngsimplicity                * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *             0.9.3 - 10/10/2000 - G.Juyn                                * */
+/* *             - added support for alpha-depth prediction                 * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - added JDAA chunk                                         * */
+/* *             - added support for nEED                                   * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added functions to retrieve PNG/JNG specific header-info * */
+/* *             - added optional support for bKGD for PNG images           * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added callback to process non-critical unknown chunks    * */
+/* *             - added routine to discard "invalid" objects               * */
+/* *             0.9.3 - 10/19/2000 - G.Juyn                                * */
+/* *             - implemented delayed delta-processing                     * */
+/* *             0.9.3 - 10/20/2000 - G.Juyn                                * */
+/* *             - added get/set for bKGD preference setting                * */
+/* *             0.9.3 - 10/21/2000 - G.Juyn                                * */
+/* *             - added get function for interlace/progressive display     * */
+/* *                                                                        * */
+/* *             0.9.4 -  1/18/2001 - G.Juyn                                * */
+/* *             - added "new" MAGN methods 3, 4 & 5                        * */
+/* *                                                                        * */
+/* *             1.0.1 - 02/08/2001 - G.Juyn                                * */
+/* *             - added MEND processing callback                           * */
+/* *             1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly)              * */
+/* *             - added BGRA8 canvas with premultiplied alpha              * */
+/* *             1.0.1 - 05/02/2001 - G.Juyn                                * */
+/* *             - added "default" sRGB generation (Thanks Marti!)          * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added optimization option for MNG-video playback         * */
+/* *             - added processterm callback                               * */
+/* *             1.0.2 - 06/25/2001 - G.Juyn                                * */
+/* *             - added option to turn off progressive refresh             * */
+/* *                                                                        * */
+/* *             1.0.3 - 08/06/2001 - G.Juyn                                * */
+/* *             - added get function for last processed BACK chunk         * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed PROM support                                   * */
+/* *             - completed delta-image support                            * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             - added HLAPI function to copy chunks                      * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 09/22/2002 - G.Juyn                                * */
+/* *             - added bgrx8 canvas (filler byte)                         * */
+/* *             1.0.5 - 09/23/2002 - G.Juyn                                * */
+/* *             - added in-memory color-correction of abstract images      * */
+/* *             - added compose over/under routines for PAST processing    * */
+/* *             - added flip & tile routines for PAST processing           * */
+/* *             1.0.5 - 10/09/2002 - G.Juyn                                * */
+/* *             - fixed trace-constants for PAST chunk                     * */
+/* *             1.0.5 - 11/07/2002 - G.Juyn                                * */
+/* *             - added support to get totals after mng_read()             * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added conditionals around JNG and Delta-PNG code         * */
+/* *             1.0.6 - 07/14/2003 - G.R-P                                 * */
+/* *             - added conditionals around various unused functions       * */
+/* *             1.0.6 - 07/29/2003 - G.R-P                                 * */
+/* *             - added conditionals around PAST chunk support             * */
+/* *                                                                        * */
+/* *             1.0.7 - 11/27/2003 - R.A                                   * */
+/* *             - added CANVAS_RGB565 and CANVAS_BGR565                    * */
+/* *             1.0.7 - 01/25/2004 - J.S                                   * */
+/* *             - added premultiplied alpha canvas' for RGBA, ARGB, ABGR   * */
+/* *             1.0.7 - 03/07/2004 - G. Randers-Pehrson                    * */
+/* *             - put gamma, cms-related declarations inside #ifdef        * */
+/* *             1.0.7 - 03/10/2004 - G.R-P                                 * */
+/* *             - added conditionals around openstream/closestream         * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/02/2004 - G.Juyn                                * */
+/* *             - added CRC existence & checking flags                     * */
+/* *             1.0.8 - 04/11/2004 - G.Juyn                                * */
+/* *             - added data-push mechanisms for specialized decoders      * */
+/* *                                                                        * */
+/* *             1.0.9 - 10/03/2004 - G.Juyn                                * */
+/* *             - added function to retrieve current FRAM delay            * */
+/* *             1.0.9 - 10/14/2004 - G.Juyn                                * */
+/* *             - added bgr565_a8 canvas-style (thanks to J. Elvander)     * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 07/06/2007 - G.R-P bugfix by Lucas Quintana       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_TRACE_PROCS
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_TRACE_STRINGS
+MNG_LOCAL mng_trace_entry const trace_table [] =
+  {
+    {MNG_FN_INITIALIZE,                "initialize"},
+    {MNG_FN_RESET,                     "reset"},
+    {MNG_FN_CLEANUP,                   "cleanup"},
+    {MNG_FN_READ,                      "read"},
+    {MNG_FN_WRITE,                     "write"},
+    {MNG_FN_CREATE,                    "create"},
+    {MNG_FN_READDISPLAY,               "readdisplay"},
+    {MNG_FN_DISPLAY,                   "display"},
+    {MNG_FN_DISPLAY_RESUME,            "display_resume"},
+    {MNG_FN_DISPLAY_FREEZE,            "display_freeze"},
+    {MNG_FN_DISPLAY_RESET,             "display_reset"},
+#ifndef MNG_NO_DISPLAY_GO_SUPPORTED
+    {MNG_FN_DISPLAY_GOFRAME,           "display_goframe"},
+    {MNG_FN_DISPLAY_GOLAYER,           "display_golayer"},
+    {MNG_FN_DISPLAY_GOTIME,            "display_gotime"},
+#endif
+    {MNG_FN_GETLASTERROR,              "getlasterror"},
+    {MNG_FN_READ_RESUME,               "read_resume"},
+    {MNG_FN_TRAPEVENT,                 "trapevent"},
+    {MNG_FN_READ_PUSHDATA,             "read_pushdata"},
+    {MNG_FN_READ_PUSHSIG,              "read_pushsig"},
+    {MNG_FN_READ_PUSHCHUNK,            "read_pushchunk"},
+
+    {MNG_FN_SETCB_MEMALLOC,            "setcb_memalloc"},
+    {MNG_FN_SETCB_MEMFREE,             "setcb_memfree"},
+    {MNG_FN_SETCB_READDATA,            "setcb_readdata"},
+    {MNG_FN_SETCB_WRITEDATA,           "setcb_writedata"},
+    {MNG_FN_SETCB_ERRORPROC,           "setcb_errorproc"},
+    {MNG_FN_SETCB_TRACEPROC,           "setcb_traceproc"},
+    {MNG_FN_SETCB_PROCESSHEADER,       "setcb_processheader"},
+    {MNG_FN_SETCB_PROCESSTEXT,         "setcb_processtext"},
+    {MNG_FN_SETCB_GETCANVASLINE,       "setcb_getcanvasline"},
+    {MNG_FN_SETCB_GETBKGDLINE,         "setcb_getbkgdline"},
+    {MNG_FN_SETCB_REFRESH,             "setcb_refresh"},
+    {MNG_FN_SETCB_GETTICKCOUNT,        "setcb_gettickcount"},
+    {MNG_FN_SETCB_SETTIMER,            "setcb_settimer"},
+    {MNG_FN_SETCB_PROCESSGAMMA,        "setcb_processgamma"},
+    {MNG_FN_SETCB_PROCESSCHROMA,       "setcb_processchroma"},
+    {MNG_FN_SETCB_PROCESSSRGB,         "setcb_processsrgb"},
+    {MNG_FN_SETCB_PROCESSICCP,         "setcb_processiccp"},
+    {MNG_FN_SETCB_PROCESSAROW,         "setcb_processarow"},
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+    {MNG_FN_SETCB_OPENSTREAM,          "setcb_openstream"},
+    {MNG_FN_SETCB_CLOSESTREAM,         "setcb_closestream"},
+#endif
+    {MNG_FN_SETCB_GETALPHALINE,        "setcb_getalphaline"},
+    {MNG_FN_SETCB_PROCESSSAVE,         "setcb_processsave"},
+    {MNG_FN_SETCB_PROCESSSEEK,         "setcb_processseek"},
+    {MNG_FN_SETCB_PROCESSNEED,         "setcb_processneed"},
+    {MNG_FN_SETCB_PROCESSUNKNOWN,      "setcb_processunknown"},
+    {MNG_FN_SETCB_PROCESSMEND,         "setcb_processmend"},
+    {MNG_FN_SETCB_PROCESSTERM,         "setcb_processterm"},
+    {MNG_FN_SETCB_RELEASEDATA,         "setcb_releasedata"},
+
+    {MNG_FN_GETCB_MEMALLOC,            "getcb_memalloc"},
+    {MNG_FN_GETCB_MEMFREE,             "getcb_memfree"},
+    {MNG_FN_GETCB_READDATA,            "getcb_readdata,"},
+    {MNG_FN_GETCB_WRITEDATA,           "getcb_writedata"},
+    {MNG_FN_GETCB_ERRORPROC,           "getcb_errorproc"},
+    {MNG_FN_GETCB_TRACEPROC,           "getcb_traceproc"},
+    {MNG_FN_GETCB_PROCESSHEADER,       "getcb_processheader"},
+    {MNG_FN_GETCB_PROCESSTEXT,         "getcb_processtext"},
+    {MNG_FN_GETCB_GETCANVASLINE,       "getcb_getcanvasline"},
+    {MNG_FN_GETCB_GETBKGDLINE,         "getcb_getbkgdline"},
+    {MNG_FN_GETCB_REFRESH,             "getcb_refresh"},
+    {MNG_FN_GETCB_GETTICKCOUNT,        "getcb_gettickcount"},
+    {MNG_FN_GETCB_SETTIMER,            "getcb_settimer"},
+    {MNG_FN_GETCB_PROCESSGAMMA,        "getcb_processgamma"},
+    {MNG_FN_GETCB_PROCESSCHROMA,       "getcb_processchroma"},
+    {MNG_FN_GETCB_PROCESSSRGB,         "getcb_processsrgb"},
+    {MNG_FN_GETCB_PROCESSICCP,         "getcb_processiccp"},
+    {MNG_FN_GETCB_PROCESSAROW,         "getcb_processarow"},
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+    {MNG_FN_GETCB_OPENSTREAM,          "getcb_openstream"},
+    {MNG_FN_GETCB_CLOSESTREAM,         "getcb_closestream"},
+#endif
+    {MNG_FN_GETCB_GETALPHALINE,        "getcb_getalphaline"},
+    {MNG_FN_GETCB_PROCESSSAVE,         "getcb_processsave"},
+    {MNG_FN_GETCB_PROCESSSEEK,         "getcb_processseek"},
+    {MNG_FN_GETCB_PROCESSNEED,         "getcb_processneed"},
+    {MNG_FN_GETCB_PROCESSUNKNOWN,      "getcb_processunknown"},
+    {MNG_FN_GETCB_PROCESSMEND,         "getcb_processmend"},
+    {MNG_FN_GETCB_PROCESSTERM,         "getcb_processterm"},
+    {MNG_FN_GETCB_RELEASEDATA,         "getcb_releasedata"},
+
+    {MNG_FN_SET_USERDATA,              "set_userdata"},
+    {MNG_FN_SET_CANVASSTYLE,           "set_canvasstyle"},
+    {MNG_FN_SET_BKGDSTYLE,             "set_bkgdstyle"},
+    {MNG_FN_SET_BGCOLOR,               "set_bgcolor"},
+    {MNG_FN_SET_STORECHUNKS,           "set_storechunks"},
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+    {MNG_FN_SET_VIEWGAMMA,             "set_viewgamma"},
+#ifndef MNG_NO_DFLT_INFO
+    {MNG_FN_SET_DISPLAYGAMMA,          "set_displaygamma"},
+#endif
+    {MNG_FN_SET_DFLTIMGGAMMA,          "set_dfltimggamma"},
+#endif
+    {MNG_FN_SET_SRGB,                  "set_srgb"},
+    {MNG_FN_SET_OUTPUTPROFILE,         "set_outputprofile"},
+    {MNG_FN_SET_SRGBPROFILE,           "set_srgbprofile"},
+#ifndef MNG_SKIP_MAXCANVAS
+    {MNG_FN_SET_MAXCANVASWIDTH,        "set_maxcanvaswidth"},
+    {MNG_FN_SET_MAXCANVASHEIGHT,       "set_maxcanvasheight"},
+    {MNG_FN_SET_MAXCANVASSIZE,         "set_maxcanvassize"},
+#endif
+#ifndef MNG_NO_ACCESS_ZLIB
+    {MNG_FN_SET_ZLIB_LEVEL,            "set_zlib_level"},
+    {MNG_FN_SET_ZLIB_METHOD,           "set_zlib_method"},
+    {MNG_FN_SET_ZLIB_WINDOWBITS,       "set_zlib_windowbits"},
+    {MNG_FN_SET_ZLIB_MEMLEVEL,         "set_zlib_memlevel"},
+    {MNG_FN_SET_ZLIB_STRATEGY,         "set_zlib_strategy"},
+    {MNG_FN_SET_ZLIB_MAXIDAT,          "set_zlib_maxidat"},
+#endif
+#ifndef MNG_NO_ACCESS_JPEG
+    {MNG_FN_SET_JPEG_DCTMETHOD,        "set_jpeg_dctmethod"},
+    {MNG_FN_SET_JPEG_QUALITY,          "set_jpeg_quality"},
+    {MNG_FN_SET_JPEG_SMOOTHING,        "set_jpeg_smoothing"},
+    {MNG_FN_SET_JPEG_PROGRESSIVE,      "set_jpeg_progressive"},
+    {MNG_FN_SET_JPEG_OPTIMIZED,        "set_jpeg_optimized"},
+    {MNG_FN_SET_JPEG_MAXJDAT,          "set_jpeg_maxjdat"},
+#endif
+    {MNG_FN_SET_SPEED,                 "set_speed"},
+    {MNG_FN_SET_SUSPENSIONMODE,        "set_suspensionmode"},
+    {MNG_FN_SET_SECTIONBREAKS,         "set_sectionbreaks"},
+    {MNG_FN_SET_USEBKGD,               "set_usebkgd"},
+    {MNG_FN_SET_OUTPUTPROFILE2,        "set_outputprofile2"},
+    {MNG_FN_SET_SRGBPROFILE2,          "set_srgbprofile2"},
+    {MNG_FN_SET_OUTPUTSRGB,            "set_outputsrgb"},
+    {MNG_FN_SET_SRGBIMPLICIT,          "set_srgbimplicit"},
+    {MNG_FN_SET_CACHEPLAYBACK,         "set_cacheplayback"},
+    {MNG_FN_SET_DOPROGRESSIVE,         "set_doprogressive"},
+    {MNG_FN_SET_CRCMODE,               "set_crcmode"},
+
+    {MNG_FN_GET_USERDATA,              "get_userdata"},
+    {MNG_FN_GET_SIGTYPE,               "get_sigtype"},
+    {MNG_FN_GET_IMAGETYPE,             "get_imagetype"},
+    {MNG_FN_GET_IMAGEWIDTH,            "get_imagewidth"},
+    {MNG_FN_GET_IMAGEHEIGHT,           "get_imageheight"},
+    {MNG_FN_GET_TICKS,                 "get_ticks"},
+    {MNG_FN_GET_FRAMECOUNT,            "get_framecount"},
+    {MNG_FN_GET_LAYERCOUNT,            "get_layercount"},
+    {MNG_FN_GET_PLAYTIME,              "get_playtime"},
+    {MNG_FN_GET_SIMPLICITY,            "get_simplicity"},
+    {MNG_FN_GET_CANVASSTYLE,           "get_canvasstyle"},
+    {MNG_FN_GET_BKGDSTYLE,             "get_bkgdstyle"},
+    {MNG_FN_GET_BGCOLOR,               "get_bgcolor"},
+    {MNG_FN_GET_STORECHUNKS,           "get_storechunks"},
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+    {MNG_FN_GET_VIEWGAMMA,             "get_viewgamma"},
+    {MNG_FN_GET_DISPLAYGAMMA,          "get_displaygamma"},
+#ifndef MNG_NO_DFLT_INFO
+    {MNG_FN_GET_DFLTIMGGAMMA,          "get_dfltimggamma"},
+#endif
+#endif
+    {MNG_FN_GET_SRGB,                  "get_srgb"},
+#ifndef MNG_SKIP_MAXCANVAS
+    {MNG_FN_GET_MAXCANVASWIDTH,        "get_maxcanvaswidth"},
+    {MNG_FN_GET_MAXCANVASHEIGHT,       "get_maxcanvasheight"},
+#endif
+#ifndef MNG_NO_ACCESS_ZLIB
+    {MNG_FN_GET_ZLIB_LEVEL,            "get_zlib_level"},
+    {MNG_FN_GET_ZLIB_METHOD,           "get_zlib_method"},
+    {MNG_FN_GET_ZLIB_WINDOWBITS,       "get_zlib_windowbits"},
+    {MNG_FN_GET_ZLIB_MEMLEVEL,         "get_zlib_memlevel"},
+    {MNG_FN_GET_ZLIB_STRATEGY,         "get_zlib_strategy"},
+    {MNG_FN_GET_ZLIB_MAXIDAT,          "get_zlib_maxidat"},
+#endif
+#ifndef MNG_NO_ACCESS_JPEG
+    {MNG_FN_GET_JPEG_DCTMETHOD,        "get_jpeg_dctmethod"},
+    {MNG_FN_GET_JPEG_QUALITY,          "get_jpeg_quality"},
+    {MNG_FN_GET_JPEG_SMOOTHING,        "get_jpeg_smoothing"},
+    {MNG_FN_GET_JPEG_PROGRESSIVE,      "get_jpeg_progressive"},
+    {MNG_FN_GET_JPEG_OPTIMIZED,        "get_jpeg_optimized"},
+    {MNG_FN_GET_JPEG_MAXJDAT,          "get_jpeg_maxjdat"},
+#endif
+    {MNG_FN_GET_SPEED,                 "get_speed"},
+    {MNG_FN_GET_IMAGELEVEL,            "get_imagelevel"},
+    {MNG_FN_GET_SUSPENSIONMODE,        "get_speed"},
+    {MNG_FN_GET_STARTTIME,             "get_starttime"},
+    {MNG_FN_GET_RUNTIME,               "get_runtime"},
+#ifndef MNG_NO_CURRENT_INFO
+    {MNG_FN_GET_CURRENTFRAME,          "get_currentframe"},
+    {MNG_FN_GET_CURRENTLAYER,          "get_currentlayer"},
+    {MNG_FN_GET_CURRENTPLAYTIME,       "get_currentplaytime"},
+#endif
+    {MNG_FN_GET_SECTIONBREAKS,         "get_sectionbreaks"},
+    {MNG_FN_GET_ALPHADEPTH,            "get_alphadepth"},
+    {MNG_FN_GET_BITDEPTH,              "get_bitdepth"},
+    {MNG_FN_GET_COLORTYPE,             "get_colortype"},
+    {MNG_FN_GET_COMPRESSION,           "get_compression"},
+    {MNG_FN_GET_FILTER,                "get_filter"},
+    {MNG_FN_GET_INTERLACE,             "get_interlace"},
+    {MNG_FN_GET_ALPHABITDEPTH,         "get_alphabitdepth"},
+    {MNG_FN_GET_ALPHACOMPRESSION,      "get_alphacompression"},
+    {MNG_FN_GET_ALPHAFILTER,           "get_alphafilter"},
+    {MNG_FN_GET_ALPHAINTERLACE,        "get_alphainterlace"},
+    {MNG_FN_GET_USEBKGD,               "get_usebkgd"},
+    {MNG_FN_GET_REFRESHPASS,           "get_refreshpass"},
+    {MNG_FN_GET_CACHEPLAYBACK,         "get_cacheplayback"},
+    {MNG_FN_GET_DOPROGRESSIVE,         "get_doprogressive"},
+    {MNG_FN_GET_LASTBACKCHUNK,         "get_lastbackchunk"},
+    {MNG_FN_GET_LASTSEEKNAME,          "get_lastseekname"},
+#ifndef MNG_NO_CURRENT_INFO
+    {MNG_FN_GET_TOTALFRAMES,           "get_totalframes"},
+    {MNG_FN_GET_TOTALLAYERS,           "get_totallayers"},
+    {MNG_FN_GET_TOTALPLAYTIME,         "get_totalplaytime"},
+#endif
+    {MNG_FN_GET_CRCMODE,               "get_crcmode"},
+    {MNG_FN_GET_CURRFRAMDELAY,         "get_currframdelay"},
+
+    {MNG_FN_STATUS_ERROR,              "status_error"},
+    {MNG_FN_STATUS_READING,            "status_reading"},
+    {MNG_FN_STATUS_SUSPENDBREAK,       "status_suspendbreak"},
+    {MNG_FN_STATUS_CREATING,           "status_creating"},
+    {MNG_FN_STATUS_WRITING,            "status_writing"},
+    {MNG_FN_STATUS_DISPLAYING,         "status_displaying"},
+    {MNG_FN_STATUS_RUNNING,            "status_running"},
+    {MNG_FN_STATUS_TIMERBREAK,         "status_timerbreak"},
+    {MNG_FN_STATUS_DYNAMIC,            "status_dynamic"},
+    {MNG_FN_STATUS_RUNNINGEVENT,       "status_runningevent"},
+
+    {MNG_FN_ITERATE_CHUNKS,            "iterate_chunks"},
+    {MNG_FN_COPY_CHUNK,                "copy_chunk"},
+
+    {MNG_FN_GETCHUNK_IHDR,             "getchunk_ihdr"},
+    {MNG_FN_GETCHUNK_PLTE,             "getchunk_plte"},
+    {MNG_FN_GETCHUNK_IDAT,             "getchunk_idat"},
+    {MNG_FN_GETCHUNK_IEND,             "getchunk_iend"},
+    {MNG_FN_GETCHUNK_TRNS,             "getchunk_trns"},
+#ifndef MNG_SKIPCHUNK_gAMA
+    {MNG_FN_GETCHUNK_GAMA,             "getchunk_gama"},
+#endif
+#ifndef MNG_SKIPCHUNK_cHRM
+    {MNG_FN_GETCHUNK_CHRM,             "getchunk_chrm"},
+#endif
+#ifndef MNG_SKIPCHUNK_sRGB
+    {MNG_FN_GETCHUNK_SRGB,             "getchunk_srgb"},
+#endif
+#ifndef MNG_SKIPCHUNK_iCCP
+    {MNG_FN_GETCHUNK_ICCP,             "getchunk_iccp"},
+#endif
+#ifndef MNG_SKIPCHUNK_tEXt
+    {MNG_FN_GETCHUNK_TEXT,             "getchunk_text"},
+#endif
+#ifndef MNG_SKIPCHUNK_zTXt
+    {MNG_FN_GETCHUNK_ZTXT,             "getchunk_ztxt"},
+#endif
+#ifndef MNG_SKIPCHUNK_iTXt
+    {MNG_FN_GETCHUNK_ITXT,             "getchunk_itxt"},
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+    {MNG_FN_GETCHUNK_BKGD,             "getchunk_bkgd"},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYs
+    {MNG_FN_GETCHUNK_PHYS,             "getchunk_phys"},
+#endif
+#ifndef MNG_SKIPCHUNK_sBIT
+    {MNG_FN_GETCHUNK_SBIT,             "getchunk_sbit"},
+#endif
+#ifndef MNG_SKIPCHUNK_sPLT
+    {MNG_FN_GETCHUNK_SPLT,             "getchunk_splt"},
+#endif
+#ifndef MNG_SKIPCHUNK_hIST
+    {MNG_FN_GETCHUNK_HIST,             "getchunk_hist"},
+#endif
+#ifndef MNG_SKIPCHUNK_tIME
+    {MNG_FN_GETCHUNK_TIME,             "getchunk_time"},
+#endif
+    {MNG_FN_GETCHUNK_MHDR,             "getchunk_mhdr"},
+    {MNG_FN_GETCHUNK_MEND,             "getchunk_mend"},
+#ifndef MNG_SKIPCHUNK_LOOP
+    {MNG_FN_GETCHUNK_LOOP,             "getchunk_loop"},
+    {MNG_FN_GETCHUNK_ENDL,             "getchunk_endl"},
+#endif
+    {MNG_FN_GETCHUNK_DEFI,             "getchunk_defi"},
+#ifndef MNG_SKIPCHUNK_BASI
+    {MNG_FN_GETCHUNK_BASI,             "getchunk_basi"},
+#endif
+    {MNG_FN_GETCHUNK_CLON,             "getchunk_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_GETCHUNK_PAST,             "getchunk_past"},
+#endif
+    {MNG_FN_GETCHUNK_DISC,             "getchunk_disc"},
+    {MNG_FN_GETCHUNK_BACK,             "getchunk_back"},
+    {MNG_FN_GETCHUNK_FRAM,             "getchunk_fram"},
+    {MNG_FN_GETCHUNK_MOVE,             "getchunk_move"},
+    {MNG_FN_GETCHUNK_CLIP,             "getchunk_clip"},
+    {MNG_FN_GETCHUNK_SHOW,             "getchunk_show"},
+    {MNG_FN_GETCHUNK_TERM,             "getchunk_term"},
+#ifndef MNG_SKIPCHUNK_SAVE
+    {MNG_FN_GETCHUNK_SAVE,             "getchunk_save"},
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+    {MNG_FN_GETCHUNK_SEEK,             "getchunk_seek"},
+#endif
+#ifndef MNG_SKIPCHUNK_eXPI
+    {MNG_FN_GETCHUNK_EXPI,             "getchunk_expi"},
+#endif
+#ifndef MNG_SKIPCHUNK_fPRI
+    {MNG_FN_GETCHUNK_FPRI,             "getchunk_fpri"},
+#endif
+#ifndef MNG_SKIPCHUNK_nEED
+    {MNG_FN_GETCHUNK_NEED,             "getchunk_need"},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYg
+    {MNG_FN_GETCHUNK_PHYG,             "getchunk_phyg"},
+#endif
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_GETCHUNK_JHDR,             "getchunk_jhdr"},
+    {MNG_FN_GETCHUNK_JDAT,             "getchunk_jdat"},
+    {MNG_FN_GETCHUNK_JSEP,             "getchunk_jsep"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_GETCHUNK_DHDR,             "getchunk_dhdr"},
+    {MNG_FN_GETCHUNK_PROM,             "getchunk_prom"},
+    {MNG_FN_GETCHUNK_IPNG,             "getchunk_ipng"},
+    {MNG_FN_GETCHUNK_PPLT,             "getchunk_pplt"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_GETCHUNK_IJNG,             "getchunk_ijng"},
+#endif
+#ifndef MNG_SKIPCHUNK_DROP
+    {MNG_FN_GETCHUNK_DROP,             "getchunk_drop"},
+#endif
+#ifndef MNG_SKIPCHUNK_DBYK
+    {MNG_FN_GETCHUNK_DBYK,             "getchunk_dbyk"},
+#endif
+#ifndef MNG_SKIPCHUNK_ORDR
+    {MNG_FN_GETCHUNK_ORDR,             "getchunk_ordr"},
+#endif
+#endif
+    {MNG_FN_GETCHUNK_UNKNOWN,          "getchunk_unknown"},
+    {MNG_FN_GETCHUNK_MAGN,             "getchunk_magn"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_GETCHUNK_JDAA,             "getchunk_jdaa"},
+#endif
+#ifndef MNG_SKIPCHUNK_evNT
+    {MNG_FN_GETCHUNK_EVNT,             "getchunk_evnt"},
+#endif
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_FN_GETCHUNK_MPNG,             "getchunk_mpng"},
+#endif
+
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_GETCHUNK_PAST_SRC,         "getchunk_past_src"},
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+    {MNG_FN_GETCHUNK_SAVE_ENTRY,       "getchunk_save_entry"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_GETCHUNK_PPLT_ENTRY,       "getchunk_pplt_entry"},
+    {MNG_FN_GETCHUNK_ORDR_ENTRY,       "getchunk_ordr_entry"},
+#endif
+#ifndef MNG_SKIPCHUNK_evNT
+    {MNG_FN_GETCHUNK_EVNT_ENTRY,       "getchunk_evnt_entry"},
+#endif
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_FN_GETCHUNK_MPNG_FRAME,       "getchunk_mpng_frame"},
+#endif
+
+    {MNG_FN_PUTCHUNK_IHDR,             "putchunk_ihdr"},
+    {MNG_FN_PUTCHUNK_PLTE,             "putchunk_plte"},
+    {MNG_FN_PUTCHUNK_IDAT,             "putchunk_idat"},
+    {MNG_FN_PUTCHUNK_IEND,             "putchunk_iend"},
+    {MNG_FN_PUTCHUNK_TRNS,             "putchunk_trns"},
+#ifndef MNG_SKIPCHUNK_gAMA
+    {MNG_FN_PUTCHUNK_GAMA,             "putchunk_gama"},
+#endif
+#ifndef MNG_SKIPCHUNK_cHRM
+    {MNG_FN_PUTCHUNK_CHRM,             "putchunk_chrm"},
+#endif
+#ifndef MNG_SKIPCHUNK_sRGB
+    {MNG_FN_PUTCHUNK_SRGB,             "putchunk_srgb"},
+#endif
+#ifndef MNG_SKIPCHUNK_iCCP
+    {MNG_FN_PUTCHUNK_ICCP,             "putchunk_iccp"},
+#endif
+#ifndef MNG_SKIPCHUNK_tEXt
+    {MNG_FN_PUTCHUNK_TEXT,             "putchunk_text"},
+#endif
+#ifndef MNG_SKIPCHUNK_zTXt
+    {MNG_FN_PUTCHUNK_ZTXT,             "putchunk_ztxt"},
+#endif
+#ifndef MNG_SKIPCHUNK_iTXt
+    {MNG_FN_PUTCHUNK_ITXT,             "putchunk_itxt"},
+#endif
+#ifndef MNG_SKIPCHUNK_bKGD
+    {MNG_FN_PUTCHUNK_BKGD,             "putchunk_bkgd"},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYs
+    {MNG_FN_PUTCHUNK_PHYS,             "putchunk_phys"},
+#endif
+#ifndef MNG_SKIPCHUNK_sBIT
+    {MNG_FN_PUTCHUNK_SBIT,             "putchunk_sbit"},
+#endif
+#ifndef MNG_SKIPCHUNK_sPLT
+    {MNG_FN_PUTCHUNK_SPLT,             "putchunk_splt"},
+#endif
+#ifndef MNG_SKIPCHUNK_hIST
+    {MNG_FN_PUTCHUNK_HIST,             "putchunk_hist"},
+#endif
+#ifndef MNG_SKIPCHUNK_tIME
+    {MNG_FN_PUTCHUNK_TIME,             "putchunk_time"},
+#endif
+    {MNG_FN_PUTCHUNK_MHDR,             "putchunk_mhdr"},
+    {MNG_FN_PUTCHUNK_MEND,             "putchunk_mend"},
+    {MNG_FN_PUTCHUNK_LOOP,             "putchunk_loop"},
+    {MNG_FN_PUTCHUNK_ENDL,             "putchunk_endl"},
+    {MNG_FN_PUTCHUNK_DEFI,             "putchunk_defi"},
+    {MNG_FN_PUTCHUNK_BASI,             "putchunk_basi"},
+    {MNG_FN_PUTCHUNK_CLON,             "putchunk_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_PUTCHUNK_PAST,             "putchunk_past"},
+#endif
+    {MNG_FN_PUTCHUNK_DISC,             "putchunk_disc"},
+    {MNG_FN_PUTCHUNK_BACK,             "putchunk_back"},
+    {MNG_FN_PUTCHUNK_FRAM,             "putchunk_fram"},
+    {MNG_FN_PUTCHUNK_MOVE,             "putchunk_move"},
+    {MNG_FN_PUTCHUNK_CLIP,             "putchunk_clip"},
+    {MNG_FN_PUTCHUNK_SHOW,             "putchunk_show"},
+    {MNG_FN_PUTCHUNK_TERM,             "putchunk_term"},
+#ifndef MNG_SKIPCHUNK_SAVE
+    {MNG_FN_PUTCHUNK_SAVE,             "putchunk_save"},
+#endif
+#ifndef MNG_SKIPCHUNK_SEEK
+    {MNG_FN_PUTCHUNK_SEEK,             "putchunk_seek"},
+#endif
+#ifndef MNG_SKIPCHUNK_eXPI
+    {MNG_FN_PUTCHUNK_EXPI,             "putchunk_expi"},
+#endif
+#ifndef MNG_SKIPCHUNK_fPRI
+    {MNG_FN_PUTCHUNK_FPRI,             "putchunk_fpri"},
+#endif
+#ifndef MNG_SKIPCHUNK_nEED
+    {MNG_FN_PUTCHUNK_NEED,             "putchunk_need"},
+#endif
+#ifndef MNG_SKIPCHUNK_pHYg
+    {MNG_FN_PUTCHUNK_PHYG,             "putchunk_phyg"},
+#endif
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_PUTCHUNK_JHDR,             "putchunk_jhdr"},
+    {MNG_FN_PUTCHUNK_JDAT,             "putchunk_jdat"},
+    {MNG_FN_PUTCHUNK_JSEP,             "putchunk_jsep"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_PUTCHUNK_DHDR,             "putchunk_dhdr"},
+    {MNG_FN_PUTCHUNK_PROM,             "putchunk_prom"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_PUTCHUNK_IPNG,             "putchunk_ipng"},
+#endif
+    {MNG_FN_PUTCHUNK_PPLT,             "putchunk_pplt"},
+    {MNG_FN_PUTCHUNK_IJNG,             "putchunk_ijng"},
+#ifndef MNG_SKIPCHUNK_DROP
+    {MNG_FN_PUTCHUNK_DROP,             "putchunk_drop"},
+#endif
+#ifndef MNG_SKIPCHUNK_DBYK
+    {MNG_FN_PUTCHUNK_DBYK,             "putchunk_dbyk"},
+#endif
+#ifndef MNG_SKIPCHUNK_ORDR
+    {MNG_FN_PUTCHUNK_ORDR,             "putchunk_ordr"},
+#endif
+#endif
+    {MNG_FN_PUTCHUNK_UNKNOWN,          "putchunk_unknown"},
+    {MNG_FN_PUTCHUNK_MAGN,             "putchunk_magn"},
+    {MNG_FN_PUTCHUNK_JDAA,             "putchunk_jdaa"},
+#ifndef MNG_SKIPCHUNK_evNT
+    {MNG_FN_PUTCHUNK_EVNT,             "putchunk_evnt"},
+#endif
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_FN_PUTCHUNK_MPNG,             "putchunk_mpng"},
+#endif
+
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_PUTCHUNK_PAST_SRC,         "putchunk_past_src"},
+#endif
+#ifndef MNG_SKIPCHUNK_SAVE
+    {MNG_FN_PUTCHUNK_SAVE_ENTRY,       "putchunk_save_entry"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_PUTCHUNK_PPLT_ENTRY,       "putchunk_pplt_entry"},
+#ifndef MNG_SKIPCHUNK_ORDR
+    {MNG_FN_PUTCHUNK_ORDR_ENTRY,       "putchunk_ordr_entry"},
+#endif
+#endif
+#ifndef MNG_SKIPCHUNK_evNT
+    {MNG_FN_PUTCHUNK_EVNT_ENTRY,       "putchunk_evnt_entry"},
+#endif
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_FN_PUTCHUNK_MPNG_FRAME,       "putchunk_mpng_frame"},
+#endif
+
+    {MNG_FN_GETIMGDATA_SEQ,            "getimgdata_seq"},
+    {MNG_FN_GETIMGDATA_CHUNKSEQ,       "getimgdata_chunkseq"},
+    {MNG_FN_GETIMGDATA_CHUNK,          "getimgdata_chunk"},
+
+    {MNG_FN_PUTIMGDATA_IHDR,           "putimgdata_ihdr"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_PUTIMGDATA_JHDR,           "putimgdata_jhdr"},
+    {MNG_FN_PUTIMGDATA_BASI,           "putimgdata_basi"},
+    {MNG_FN_PUTIMGDATA_DHDR,           "putimgdata_dhdr"},
+#endif
+
+    {MNG_FN_UPDATEMNGHEADER,           "updatemngheader"},
+    {MNG_FN_UPDATEMNGSIMPLICITY,       "updatemngsimplicity"},
+
+    {MNG_FN_PROCESS_RAW_CHUNK,         "process_raw_chunk"},
+    {MNG_FN_READ_GRAPHIC,              "read_graphic"},
+    {MNG_FN_DROP_CHUNKS,               "drop_chunks"},
+    {MNG_FN_PROCESS_ERROR,             "process_error"},
+    {MNG_FN_CLEAR_CMS,                 "clear_cms"},
+    {MNG_FN_DROP_OBJECTS,              "drop_objects"},
+    {MNG_FN_READ_CHUNK,                "read_chunk"},
+    {MNG_FN_LOAD_BKGDLAYER,            "load_bkgdlayer"},
+    {MNG_FN_NEXT_FRAME,                "next_frame"},
+    {MNG_FN_NEXT_LAYER,                "next_layer"},
+    {MNG_FN_INTERFRAME_DELAY,          "interframe_delay"},
+    {MNG_FN_DISPLAY_IMAGE,             "display_image"},
+    {MNG_FN_DROP_IMGOBJECTS,           "drop_imgobjects"},
+    {MNG_FN_DROP_ANIOBJECTS,           "drop_aniobjects"},
+    {MNG_FN_INFLATE_BUFFER,            "inflate_buffer"},
+    {MNG_FN_DEFLATE_BUFFER,            "deflate_buffer"},
+    {MNG_FN_WRITE_RAW_CHUNK,           "write_raw_chunk"},
+    {MNG_FN_WRITE_GRAPHIC,             "write_graphic"},
+    {MNG_FN_SAVE_STATE,                "save_state"},
+    {MNG_FN_RESTORE_STATE,             "restore_state"},
+    {MNG_FN_DROP_SAVEDATA,             "drop_savedata"},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_EXECUTE_DELTA_IMAGE,       "execute_delta_image"},
+#endif
+    {MNG_FN_PROCESS_DISPLAY,           "process_display"},
+    {MNG_FN_CLEAR_CANVAS,              "clear_canvas"},
+    {MNG_FN_READ_DATABUFFER,           "read_databuffer"},
+    {MNG_FN_STORE_ERROR,               "store_error"},
+    {MNG_FN_DROP_INVALID_OBJECTS,      "drop_invalid_objects"},
+    {MNG_FN_RELEASE_PUSHDATA,          "release_pushdata"},
+    {MNG_FN_READ_DATA,                 "read_data"},
+    {MNG_FN_READ_CHUNK_CRC,            "read_chunk_crc"},
+    {MNG_FN_RELEASE_PUSHCHUNK,         "release_pushchunk"},
+
+    {MNG_FN_DISPLAY_RGB8,              "display_rgb8"},
+    {MNG_FN_DISPLAY_RGBA8,             "display_rgba8"},
+    {MNG_FN_DISPLAY_ARGB8,             "display_argb8"},
+    {MNG_FN_DISPLAY_BGR8,              "display_bgr8"},
+    {MNG_FN_DISPLAY_BGRA8,             "display_bgra8"},
+    {MNG_FN_DISPLAY_ABGR8,             "display_abgr8"},
+    {MNG_FN_DISPLAY_RGB16,             "display_rgb16"},
+    {MNG_FN_DISPLAY_RGBA16,            "display_rgba16"},
+    {MNG_FN_DISPLAY_ARGB16,            "display_argb16"},
+    {MNG_FN_DISPLAY_BGR16,             "display_bgr16"},
+    {MNG_FN_DISPLAY_BGRA16,            "display_bgra16"},
+    {MNG_FN_DISPLAY_ABGR16,            "display_abgr16"},
+    {MNG_FN_DISPLAY_INDEX8,            "display_index8"},
+    {MNG_FN_DISPLAY_INDEXA8,           "display_indexa8"},
+    {MNG_FN_DISPLAY_AINDEX8,           "display_aindex8"},
+    {MNG_FN_DISPLAY_GRAY8,             "display_gray8"},
+    {MNG_FN_DISPLAY_GRAY16,            "display_gray16"},
+    {MNG_FN_DISPLAY_GRAYA8,            "display_graya8"},
+    {MNG_FN_DISPLAY_GRAYA16,           "display_graya16"},
+    {MNG_FN_DISPLAY_AGRAY8,            "display_agray8"},
+    {MNG_FN_DISPLAY_AGRAY16,           "display_agray16"},
+    {MNG_FN_DISPLAY_DX15,              "display_dx15"},
+    {MNG_FN_DISPLAY_DX16,              "display_dx16"},
+    {MNG_FN_DISPLAY_RGB8_A8,           "display_rgb8_a8"},
+    {MNG_FN_DISPLAY_BGRA8PM,           "display_bgra8_pm"},
+    {MNG_FN_DISPLAY_BGRX8,             "display_bgrx8"},
+    {MNG_FN_DISPLAY_RGB565,            "display_rgb565"},
+    {MNG_FN_DISPLAY_RGBA565,           "display_rgba565"},
+    {MNG_FN_DISPLAY_BGR565,            "display_bgr565"},
+    {MNG_FN_DISPLAY_BGRA565,           "display_bgra565"},
+    {MNG_FN_DISPLAY_RGBA8_PM,          "display_rgba8_pm"},
+    {MNG_FN_DISPLAY_ARGB8_PM,          "display_argb8_pm"},
+    {MNG_FN_DISPLAY_ABGR8_PM,          "display_abgr8_pm"},
+    {MNG_FN_DISPLAY_BGR565_A8,         "display_bgr565_a8"},
+
+    {MNG_FN_INIT_FULL_CMS,             "init_full_cms"},
+    {MNG_FN_CORRECT_FULL_CMS,          "correct_full_cms"},
+    {MNG_FN_INIT_GAMMA_ONLY,           "init_gamma_only"},
+    {MNG_FN_CORRECT_GAMMA_ONLY,        "correct_gamma_only"},
+    {MNG_FN_CORRECT_APP_CMS,           "correct_app_cms"},
+    {MNG_FN_INIT_FULL_CMS_OBJ,         "init_full_cms_obj"},
+    {MNG_FN_INIT_GAMMA_ONLY_OBJ,       "init_gamma_only_obj"},
+    {MNG_FN_INIT_APP_CMS,              "init_app_cms"},
+    {MNG_FN_INIT_APP_CMS_OBJ,          "init_app_cms_obj"},
+
+    {MNG_FN_PROCESS_G1,                "process_g1"},
+    {MNG_FN_PROCESS_G2,                "process_g2"},
+    {MNG_FN_PROCESS_G4,                "process_g4"},
+    {MNG_FN_PROCESS_G8,                "process_g8"},
+    {MNG_FN_PROCESS_G16,               "process_g16"},
+    {MNG_FN_PROCESS_RGB8,              "process_rgb8"},
+    {MNG_FN_PROCESS_RGB16,             "process_rgb16"},
+    {MNG_FN_PROCESS_IDX1,              "process_idx1"},
+    {MNG_FN_PROCESS_IDX2,              "process_idx2"},
+    {MNG_FN_PROCESS_IDX4,              "process_idx4"},
+    {MNG_FN_PROCESS_IDX8,              "process_idx8"},
+    {MNG_FN_PROCESS_GA8,               "process_ga8"},
+    {MNG_FN_PROCESS_GA16,              "process_ga16"},
+    {MNG_FN_PROCESS_RGBA8,             "process_rgba8"},
+    {MNG_FN_PROCESS_RGBA16,            "process_rgba16"},
+
+    {MNG_FN_INIT_G1_I,                 "init_g1_i"},
+    {MNG_FN_INIT_G2_I,                 "init_g2_i"},
+    {MNG_FN_INIT_G4_I,                 "init_g4_i"},
+    {MNG_FN_INIT_G8_I,                 "init_g8_i"},
+    {MNG_FN_INIT_G16_I,                "init_g16_i"},
+    {MNG_FN_INIT_RGB8_I,               "init_rgb8_i"},
+    {MNG_FN_INIT_RGB16_I,              "init_rgb16_i"},
+    {MNG_FN_INIT_IDX1_I,               "init_idx1_i"},
+    {MNG_FN_INIT_IDX2_I,               "init_idx2_i"},
+    {MNG_FN_INIT_IDX4_I,               "init_idx4_i"},
+    {MNG_FN_INIT_IDX8_I,               "init_idx8_i"},
+    {MNG_FN_INIT_GA8_I,                "init_ga8_i"},
+    {MNG_FN_INIT_GA16_I,               "init_ga16_i"},
+    {MNG_FN_INIT_RGBA8_I,              "init_rgba8_i"},
+    {MNG_FN_INIT_RGBA16_I,             "init_rgba16_i"},
+#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT
+    {MNG_FN_INIT_G1_NI,                "init_g1_ni"},
+    {MNG_FN_INIT_G2_NI,                "init_g2_ni"},
+    {MNG_FN_INIT_G4_NI,                "init_g4_ni"},
+    {MNG_FN_INIT_G8_NI,                "init_g8_ni"},
+    {MNG_FN_INIT_G16_NI,               "init_g16_ni"},
+    {MNG_FN_INIT_RGB8_NI,              "init_rgb8_ni"},
+    {MNG_FN_INIT_RGB16_NI,             "init_rgb16_ni"},
+    {MNG_FN_INIT_IDX1_NI,              "init_idx1_ni"},
+    {MNG_FN_INIT_IDX2_NI,              "init_idx2_ni"},
+    {MNG_FN_INIT_IDX4_NI,              "init_idx4_ni"},
+    {MNG_FN_INIT_IDX8_NI,              "init_idx8_ni"},
+    {MNG_FN_INIT_GA8_NI,               "init_ga8_ni"},
+    {MNG_FN_INIT_GA16_NI,              "init_ga16_ni"},
+    {MNG_FN_INIT_RGBA8_NI,             "init_rgba8_ni"},
+    {MNG_FN_INIT_RGBA16_NI,            "init_rgba16_ni"},
+#endif
+
+    {MNG_FN_INIT_ROWPROC,              "init_rowproc"},
+    {MNG_FN_NEXT_ROW,                  "next_row"},
+    {MNG_FN_CLEANUP_ROWPROC,           "cleanup_rowproc"},
+
+    {MNG_FN_FILTER_A_ROW,              "filter_a_row"},
+    {MNG_FN_FILTER_SUB,                "filter_sub"},
+    {MNG_FN_FILTER_UP,                 "filter_up"},
+    {MNG_FN_FILTER_AVERAGE,            "filter_average"},
+    {MNG_FN_FILTER_PAETH,              "filter_paeth"},
+
+    {MNG_FN_INIT_ROWDIFFERING,         "init_rowdiffering"},
+    {MNG_FN_DIFFER_G1,                 "differ_g1"},
+    {MNG_FN_DIFFER_G2,                 "differ_g2"},
+    {MNG_FN_DIFFER_G4,                 "differ_g4"},
+    {MNG_FN_DIFFER_G8,                 "differ_g8"},
+    {MNG_FN_DIFFER_G16,                "differ_g16"},
+    {MNG_FN_DIFFER_RGB8,               "differ_rgb8"},
+    {MNG_FN_DIFFER_RGB16,              "differ_rgb16"},
+    {MNG_FN_DIFFER_IDX1,               "differ_idx1"},
+    {MNG_FN_DIFFER_IDX2,               "differ_idx2"},
+    {MNG_FN_DIFFER_IDX4,               "differ_idx4"},
+    {MNG_FN_DIFFER_IDX8,               "differ_idx8"},
+    {MNG_FN_DIFFER_GA8,                "differ_ga8"},
+    {MNG_FN_DIFFER_GA16,               "differ_ga16"},
+    {MNG_FN_DIFFER_RGBA8,              "differ_rgba8"},
+    {MNG_FN_DIFFER_RGBA16,             "differ_rgba16"},
+
+    {MNG_FN_CREATE_IMGDATAOBJECT,      "create_imgdataobject"},
+    {MNG_FN_FREE_IMGDATAOBJECT,        "free_imgdataobject"},
+    {MNG_FN_CLONE_IMGDATAOBJECT,       "clone_imgdataobject"},
+    {MNG_FN_CREATE_IMGOBJECT,          "create_imgobject"},
+    {MNG_FN_FREE_IMGOBJECT,            "free_imgobject"},
+    {MNG_FN_FIND_IMGOBJECT,            "find_imgobject"},
+    {MNG_FN_CLONE_IMGOBJECT,           "clone_imgobject"},
+    {MNG_FN_RESET_OBJECTDETAILS,       "reset_objectdetails"},
+    {MNG_FN_RENUM_IMGOBJECT,           "renum_imgobject"},
+    {MNG_FN_PROMOTE_IMGOBJECT,         "promote_imgobject"},
+    {MNG_FN_MAGNIFY_IMGOBJECT,         "magnify_imgobject"},
+    {MNG_FN_COLORCORRECT_OBJECT,       "colorcorrect_object"},
+
+    {MNG_FN_STORE_G1,                  "store_g1"},
+    {MNG_FN_STORE_G2,                  "store_g2"},
+    {MNG_FN_STORE_G4,                  "store_g4"},
+    {MNG_FN_STORE_G8,                  "store_g8"},
+    {MNG_FN_STORE_G16,                 "store_g16"},
+    {MNG_FN_STORE_RGB8,                "store_rgb8"},
+    {MNG_FN_STORE_RGB16,               "store_rgb16"},
+    {MNG_FN_STORE_IDX1,                "store_idx1"},
+    {MNG_FN_STORE_IDX2,                "store_idx2"},
+    {MNG_FN_STORE_IDX4,                "store_idx4"},
+    {MNG_FN_STORE_IDX8,                "store_idx8"},
+    {MNG_FN_STORE_GA8,                 "store_ga8"},
+    {MNG_FN_STORE_GA16,                "store_ga16"},
+    {MNG_FN_STORE_RGBA8,               "store_rgba8"},
+    {MNG_FN_STORE_RGBA16,              "store_rgba16"},
+
+    {MNG_FN_RETRIEVE_G8,               "retrieve_g8"},
+    {MNG_FN_RETRIEVE_G16,              "retrieve_g16"},
+    {MNG_FN_RETRIEVE_RGB8,             "retrieve_rgb8"},
+    {MNG_FN_RETRIEVE_RGB16,            "retrieve_rgb16"},
+    {MNG_FN_RETRIEVE_IDX8,             "retrieve_idx8"},
+    {MNG_FN_RETRIEVE_GA8,              "retrieve_ga8"},
+    {MNG_FN_RETRIEVE_GA16,             "retrieve_ga16"},
+    {MNG_FN_RETRIEVE_RGBA8,            "retrieve_rgba8"},
+    {MNG_FN_RETRIEVE_RGBA16,           "retrieve_rgba16"},
+
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_DELTA_G1,                  "delta_g1"},
+    {MNG_FN_DELTA_G2,                  "delta_g2"},
+    {MNG_FN_DELTA_G4,                  "delta_g4"},
+    {MNG_FN_DELTA_G8,                  "delta_g8"},
+    {MNG_FN_DELTA_G16,                 "delta_g16"},
+    {MNG_FN_DELTA_RGB8,                "delta_rgb8"},
+    {MNG_FN_DELTA_RGB16,               "delta_rgb16"},
+    {MNG_FN_DELTA_IDX1,                "delta_idx1"},
+    {MNG_FN_DELTA_IDX2,                "delta_idx2"},
+    {MNG_FN_DELTA_IDX4,                "delta_idx4"},
+    {MNG_FN_DELTA_IDX8,                "delta_idx8"},
+    {MNG_FN_DELTA_GA8,                 "delta_ga8"},
+    {MNG_FN_DELTA_GA16,                "delta_ga16"},
+    {MNG_FN_DELTA_RGBA8,               "delta_rgba8"},
+    {MNG_FN_DELTA_RGBA16,              "delta_rgba16"},
+#endif
+
+    {MNG_FN_CREATE_ANI_LOOP,           "create_ani_loop"},
+    {MNG_FN_CREATE_ANI_ENDL,           "create_ani_endl"},
+    {MNG_FN_CREATE_ANI_DEFI,           "create_ani_defi"},
+    {MNG_FN_CREATE_ANI_BASI,           "create_ani_basi"},
+    {MNG_FN_CREATE_ANI_CLON,           "create_ani_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_CREATE_ANI_PAST,           "create_ani_past"},
+#endif
+    {MNG_FN_CREATE_ANI_DISC,           "create_ani_disc"},
+    {MNG_FN_CREATE_ANI_BACK,           "create_ani_back"},
+    {MNG_FN_CREATE_ANI_FRAM,           "create_ani_fram"},
+    {MNG_FN_CREATE_ANI_MOVE,           "create_ani_move"},
+    {MNG_FN_CREATE_ANI_CLIP,           "create_ani_clip"},
+    {MNG_FN_CREATE_ANI_SHOW,           "create_ani_show"},
+    {MNG_FN_CREATE_ANI_TERM,           "create_ani_term"},
+    {MNG_FN_CREATE_ANI_SAVE,           "create_ani_save"},
+    {MNG_FN_CREATE_ANI_SEEK,           "create_ani_seek"},
+    {MNG_FN_CREATE_ANI_GAMA,           "create_ani_gama"},
+    {MNG_FN_CREATE_ANI_CHRM,           "create_ani_chrm"},
+    {MNG_FN_CREATE_ANI_SRGB,           "create_ani_srgb"},
+    {MNG_FN_CREATE_ANI_ICCP,           "create_ani_iccp"},
+    {MNG_FN_CREATE_ANI_PLTE,           "create_ani_plte"},
+    {MNG_FN_CREATE_ANI_TRNS,           "create_ani_trns"},
+    {MNG_FN_CREATE_ANI_BKGD,           "create_ani_bkgd"},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_CREATE_ANI_DHDR,           "create_ani_dhdr"},
+    {MNG_FN_CREATE_ANI_PROM,           "create_ani_prom"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_CREATE_ANI_IPNG,           "create_ani_ipng"},
+#endif
+    {MNG_FN_CREATE_ANI_IJNG,           "create_ani_ijng"},
+    {MNG_FN_CREATE_ANI_PPLT,           "create_ani_pplt"},
+#endif
+    {MNG_FN_CREATE_ANI_MAGN,           "create_ani_magn"},
+
+    {MNG_FN_CREATE_ANI_IMAGE,          "create_ani_image"},
+    {MNG_FN_CREATE_EVENT,              "create_event"},
+
+    {MNG_FN_FREE_ANI_LOOP,             "free_ani_loop"},
+    {MNG_FN_FREE_ANI_ENDL,             "free_ani_endl"},
+    {MNG_FN_FREE_ANI_DEFI,             "free_ani_defi"},
+    {MNG_FN_FREE_ANI_BASI,             "free_ani_basi"},
+    {MNG_FN_FREE_ANI_CLON,             "free_ani_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_FREE_ANI_PAST,             "free_ani_past"},
+#endif
+    {MNG_FN_FREE_ANI_DISC,             "free_ani_disc"},
+    {MNG_FN_FREE_ANI_BACK,             "free_ani_back"},
+    {MNG_FN_FREE_ANI_FRAM,             "free_ani_fram"},
+    {MNG_FN_FREE_ANI_MOVE,             "free_ani_move"},
+    {MNG_FN_FREE_ANI_CLIP,             "free_ani_clip"},
+    {MNG_FN_FREE_ANI_SHOW,             "free_ani_show"},
+    {MNG_FN_FREE_ANI_TERM,             "free_ani_term"},
+    {MNG_FN_FREE_ANI_SAVE,             "free_ani_save"},
+    {MNG_FN_FREE_ANI_SEEK,             "free_ani_seek"},
+    {MNG_FN_FREE_ANI_GAMA,             "free_ani_gama"},
+    {MNG_FN_FREE_ANI_CHRM,             "free_ani_chrm"},
+    {MNG_FN_FREE_ANI_SRGB,             "free_ani_srgb"},
+    {MNG_FN_FREE_ANI_ICCP,             "free_ani_iccp"},
+    {MNG_FN_FREE_ANI_PLTE,             "free_ani_plte"},
+    {MNG_FN_FREE_ANI_TRNS,             "free_ani_trns"},
+    {MNG_FN_FREE_ANI_BKGD,             "free_ani_bkgd"},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_FREE_ANI_DHDR,             "free_ani_dhdr"},
+    {MNG_FN_FREE_ANI_PROM,             "free_ani_prom"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_FREE_ANI_IPNG,             "free_ani_ipng"},
+#endif
+    {MNG_FN_FREE_ANI_IJNG,             "free_ani_ijng"},
+    {MNG_FN_FREE_ANI_PPLT,             "free_ani_pplt"},
+#endif
+    {MNG_FN_FREE_ANI_MAGN,             "free_ani_magn"},
+
+    {MNG_FN_FREE_ANI_IMAGE,            "free_ani_image"},
+    {MNG_FN_FREE_EVENT,                "free_event"},
+
+    {MNG_FN_PROCESS_ANI_LOOP,          "process_ani_loop"},
+    {MNG_FN_PROCESS_ANI_ENDL,          "process_ani_endl"},
+    {MNG_FN_PROCESS_ANI_DEFI,          "process_ani_defi"},
+    {MNG_FN_PROCESS_ANI_BASI,          "process_ani_basi"},
+    {MNG_FN_PROCESS_ANI_CLON,          "process_ani_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_PROCESS_ANI_PAST,          "process_ani_past"},
+#endif
+    {MNG_FN_PROCESS_ANI_DISC,          "process_ani_disc"},
+    {MNG_FN_PROCESS_ANI_BACK,          "process_ani_back"},
+    {MNG_FN_PROCESS_ANI_FRAM,          "process_ani_fram"},
+    {MNG_FN_PROCESS_ANI_MOVE,          "process_ani_move"},
+    {MNG_FN_PROCESS_ANI_CLIP,          "process_ani_clip"},
+    {MNG_FN_PROCESS_ANI_SHOW,          "process_ani_show"},
+    {MNG_FN_PROCESS_ANI_TERM,          "process_ani_term"},
+    {MNG_FN_PROCESS_ANI_SAVE,          "process_ani_save"},
+    {MNG_FN_PROCESS_ANI_SEEK,          "process_ani_seek"},
+    {MNG_FN_PROCESS_ANI_GAMA,          "process_ani_gama"},
+    {MNG_FN_PROCESS_ANI_CHRM,          "process_ani_chrm"},
+    {MNG_FN_PROCESS_ANI_SRGB,          "process_ani_srgb"},
+    {MNG_FN_PROCESS_ANI_ICCP,          "process_ani_iccp"},
+    {MNG_FN_PROCESS_ANI_PLTE,          "process_ani_plte"},
+    {MNG_FN_PROCESS_ANI_TRNS,          "process_ani_trns"},
+    {MNG_FN_PROCESS_ANI_BKGD,          "process_ani_bkgd"},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_PROCESS_ANI_DHDR,          "process_ani_dhdr"},
+    {MNG_FN_PROCESS_ANI_PROM,          "process_ani_prom"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_PROCESS_ANI_IPNG,          "process_ani_ipng"},
+#endif
+    {MNG_FN_PROCESS_ANI_IJNG,          "process_ani_ijng"},
+    {MNG_FN_PROCESS_ANI_PPLT,          "process_ani_pplt"},
+#endif
+    {MNG_FN_PROCESS_ANI_MAGN,          "process_ani_magn"},
+
+    {MNG_FN_PROCESS_ANI_IMAGE,         "process_ani_image"},
+    {MNG_FN_PROCESS_EVENT,             "process_event"},
+
+    {MNG_FN_RESTORE_BACKIMAGE,         "restore_backimage"},
+    {MNG_FN_RESTORE_BACKCOLOR,         "restore_backcolor"},
+    {MNG_FN_RESTORE_BGCOLOR,           "restore_bgcolor"},
+    {MNG_FN_RESTORE_RGB8,              "restore_rgb8"},
+    {MNG_FN_RESTORE_BGR8,              "restore_bgr8"},
+    {MNG_FN_RESTORE_BKGD,              "restore_bkgd"},
+    {MNG_FN_RESTORE_BGRX8,             "restore_bgrx8"},
+    {MNG_FN_RESTORE_RGB565,            "restore_rgb565"},
+
+    {MNG_FN_INIT_IHDR,                 "init_ihdr"},
+    {MNG_FN_INIT_PLTE,                 "init_plte"},
+    {MNG_FN_INIT_IDAT,                 "init_idat"},
+    {MNG_FN_INIT_IEND,                 "init_iend"},
+    {MNG_FN_INIT_TRNS,                 "init_trns"},
+    {MNG_FN_INIT_GAMA,                 "init_gama"},
+    {MNG_FN_INIT_CHRM,                 "init_chrm"},
+    {MNG_FN_INIT_SRGB,                 "init_srgb"},
+    {MNG_FN_INIT_ICCP,                 "init_iccp"},
+    {MNG_FN_INIT_TEXT,                 "init_text"},
+    {MNG_FN_INIT_ZTXT,                 "init_ztxt"},
+    {MNG_FN_INIT_ITXT,                 "init_itxt"},
+    {MNG_FN_INIT_BKGD,                 "init_bkgd"},
+    {MNG_FN_INIT_PHYS,                 "init_phys"},
+    {MNG_FN_INIT_SBIT,                 "init_sbit"},
+    {MNG_FN_INIT_SPLT,                 "init_splt"},
+    {MNG_FN_INIT_HIST,                 "init_hist"},
+    {MNG_FN_INIT_TIME,                 "init_time"},
+    {MNG_FN_INIT_MHDR,                 "init_mhdr"},
+    {MNG_FN_INIT_MEND,                 "init_mend"},
+    {MNG_FN_INIT_LOOP,                 "init_loop"},
+    {MNG_FN_INIT_ENDL,                 "init_endl"},
+    {MNG_FN_INIT_DEFI,                 "init_defi"},
+    {MNG_FN_INIT_BASI,                 "init_basi"},
+    {MNG_FN_INIT_CLON,                 "init_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_INIT_PAST,                 "init_past"},
+#endif
+    {MNG_FN_INIT_DISC,                 "init_disc"},
+    {MNG_FN_INIT_BACK,                 "init_back"},
+    {MNG_FN_INIT_FRAM,                 "init_fram"},
+    {MNG_FN_INIT_MOVE,                 "init_move"},
+    {MNG_FN_INIT_CLIP,                 "init_clip"},
+    {MNG_FN_INIT_SHOW,                 "init_show"},
+    {MNG_FN_INIT_TERM,                 "init_term"},
+    {MNG_FN_INIT_SAVE,                 "init_save"},
+    {MNG_FN_INIT_SEEK,                 "init_seek"},
+    {MNG_FN_INIT_EXPI,                 "init_expi"},
+    {MNG_FN_INIT_FPRI,                 "init_fpri"},
+    {MNG_FN_INIT_NEED,                 "init_need"},
+    {MNG_FN_INIT_PHYG,                 "init_phyg"},
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_INIT_JHDR,                 "init_jhdr"},
+    {MNG_FN_INIT_JDAT,                 "init_jdat"},
+    {MNG_FN_INIT_JSEP,                 "init_jsep"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_INIT_DHDR,                 "init_dhdr"},
+    {MNG_FN_INIT_PROM,                 "init_prom"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_INIT_IPNG,                 "init_ipng"},
+#endif
+    {MNG_FN_INIT_PPLT,                 "init_pplt"},
+    {MNG_FN_INIT_IJNG,                 "init_ijng"},
+    {MNG_FN_INIT_DROP,                 "init_drop"},
+    {MNG_FN_INIT_DBYK,                 "init_dbyk"},
+    {MNG_FN_INIT_ORDR,                 "init_ordr"},
+#endif
+    {MNG_FN_INIT_UNKNOWN,              "init_unknown"},
+    {MNG_FN_INIT_MAGN,                 "init_magn"},
+    {MNG_FN_INIT_JDAA,                 "init_jdaa"},
+    {MNG_FN_INIT_EVNT,                 "init_evnt"},
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_FN_INIT_MPNG,                 "init_mpng"},
+#endif
+
+    {MNG_FN_ASSIGN_IHDR,               "assign_ihdr"},
+    {MNG_FN_ASSIGN_PLTE,               "assign_plte"},
+    {MNG_FN_ASSIGN_IDAT,               "assign_idat"},
+    {MNG_FN_ASSIGN_IEND,               "assign_iend"},
+    {MNG_FN_ASSIGN_TRNS,               "assign_trns"},
+    {MNG_FN_ASSIGN_GAMA,               "assign_gama"},
+    {MNG_FN_ASSIGN_CHRM,               "assign_chrm"},
+    {MNG_FN_ASSIGN_SRGB,               "assign_srgb"},
+    {MNG_FN_ASSIGN_ICCP,               "assign_iccp"},
+    {MNG_FN_ASSIGN_TEXT,               "assign_text"},
+    {MNG_FN_ASSIGN_ZTXT,               "assign_ztxt"},
+    {MNG_FN_ASSIGN_ITXT,               "assign_itxt"},
+    {MNG_FN_ASSIGN_BKGD,               "assign_bkgd"},
+    {MNG_FN_ASSIGN_PHYS,               "assign_phys"},
+    {MNG_FN_ASSIGN_SBIT,               "assign_sbit"},
+    {MNG_FN_ASSIGN_SPLT,               "assign_splt"},
+    {MNG_FN_ASSIGN_HIST,               "assign_hist"},
+    {MNG_FN_ASSIGN_TIME,               "assign_time"},
+    {MNG_FN_ASSIGN_MHDR,               "assign_mhdr"},
+    {MNG_FN_ASSIGN_MEND,               "assign_mend"},
+    {MNG_FN_ASSIGN_LOOP,               "assign_loop"},
+    {MNG_FN_ASSIGN_ENDL,               "assign_endl"},
+    {MNG_FN_ASSIGN_DEFI,               "assign_defi"},
+    {MNG_FN_ASSIGN_BASI,               "assign_basi"},
+    {MNG_FN_ASSIGN_CLON,               "assign_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_ASSIGN_PAST,               "assign_past"},
+#endif
+    {MNG_FN_ASSIGN_DISC,               "assign_disc"},
+    {MNG_FN_ASSIGN_BACK,               "assign_back"},
+    {MNG_FN_ASSIGN_FRAM,               "assign_fram"},
+    {MNG_FN_ASSIGN_MOVE,               "assign_move"},
+    {MNG_FN_ASSIGN_CLIP,               "assign_clip"},
+    {MNG_FN_ASSIGN_SHOW,               "assign_show"},
+    {MNG_FN_ASSIGN_TERM,               "assign_term"},
+    {MNG_FN_ASSIGN_SAVE,               "assign_save"},
+    {MNG_FN_ASSIGN_SEEK,               "assign_seek"},
+    {MNG_FN_ASSIGN_EXPI,               "assign_expi"},
+    {MNG_FN_ASSIGN_FPRI,               "assign_fpri"},
+    {MNG_FN_ASSIGN_NEED,               "assign_need"},
+    {MNG_FN_ASSIGN_PHYG,               "assign_phyg"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_ASSIGN_JHDR,               "assign_jhdr"},
+    {MNG_FN_ASSIGN_JDAT,               "assign_jdat"},
+    {MNG_FN_ASSIGN_JSEP,               "assign_jsep"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_ASSIGN_DHDR,               "assign_dhdr"},
+    {MNG_FN_ASSIGN_PROM,               "assign_prom"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_ASSIGN_IPNG,               "assign_ipng"},
+#endif
+    {MNG_FN_ASSIGN_PPLT,               "assign_pplt"},
+    {MNG_FN_ASSIGN_IJNG,               "assign_ijng"},
+    {MNG_FN_ASSIGN_DROP,               "assign_drop"},
+    {MNG_FN_ASSIGN_DBYK,               "assign_dbyk"},
+    {MNG_FN_ASSIGN_ORDR,               "assign_ordr"},
+#endif
+    {MNG_FN_ASSIGN_UNKNOWN,            "assign_unknown"},
+    {MNG_FN_ASSIGN_MAGN,               "assign_magn"},
+    {MNG_FN_ASSIGN_JDAA,               "assign_jdaa"},
+    {MNG_FN_ASSIGN_EVNT,               "assign_evnt"},
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_FN_ASSIGN_MPNG,               "assign_mpng"},
+#endif
+
+    {MNG_FN_FREE_IHDR,                 "free_ihdr"},
+    {MNG_FN_FREE_PLTE,                 "free_plte"},
+    {MNG_FN_FREE_IDAT,                 "free_idat"},
+    {MNG_FN_FREE_IEND,                 "free_iend"},
+    {MNG_FN_FREE_TRNS,                 "free_trns"},
+    {MNG_FN_FREE_GAMA,                 "free_gama"},
+    {MNG_FN_FREE_CHRM,                 "free_chrm"},
+    {MNG_FN_FREE_SRGB,                 "free_srgb"},
+    {MNG_FN_FREE_ICCP,                 "free_iccp"},
+    {MNG_FN_FREE_TEXT,                 "free_text"},
+    {MNG_FN_FREE_ZTXT,                 "free_ztxt"},
+    {MNG_FN_FREE_ITXT,                 "free_itxt"},
+    {MNG_FN_FREE_BKGD,                 "free_bkgd"},
+    {MNG_FN_FREE_PHYS,                 "free_phys"},
+    {MNG_FN_FREE_SBIT,                 "free_sbit"},
+    {MNG_FN_FREE_SPLT,                 "free_splt"},
+    {MNG_FN_FREE_HIST,                 "free_hist"},
+    {MNG_FN_FREE_TIME,                 "free_time"},
+    {MNG_FN_FREE_MHDR,                 "free_mhdr"},
+    {MNG_FN_FREE_MEND,                 "free_mend"},
+    {MNG_FN_FREE_LOOP,                 "free_loop"},
+    {MNG_FN_FREE_ENDL,                 "free_endl"},
+    {MNG_FN_FREE_DEFI,                 "free_defi"},
+    {MNG_FN_FREE_BASI,                 "free_basi"},
+    {MNG_FN_FREE_CLON,                 "free_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_FREE_PAST,                 "free_past"},
+#endif
+    {MNG_FN_FREE_DISC,                 "free_disc"},
+    {MNG_FN_FREE_BACK,                 "free_back"},
+    {MNG_FN_FREE_FRAM,                 "free_fram"},
+    {MNG_FN_FREE_MOVE,                 "free_move"},
+    {MNG_FN_FREE_CLIP,                 "free_clip"},
+    {MNG_FN_FREE_SHOW,                 "free_show"},
+    {MNG_FN_FREE_TERM,                 "free_term"},
+    {MNG_FN_FREE_SAVE,                 "free_save"},
+    {MNG_FN_FREE_SEEK,                 "free_seek"},
+    {MNG_FN_FREE_EXPI,                 "free_expi"},
+    {MNG_FN_FREE_FPRI,                 "free_fpri"},
+    {MNG_FN_FREE_NEED,                 "free_need"},
+    {MNG_FN_FREE_PHYG,                 "free_phyg"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_FREE_JHDR,                 "free_jhdr"},
+    {MNG_FN_FREE_JDAT,                 "free_jdat"},
+    {MNG_FN_FREE_JSEP,                 "free_jsep"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_FREE_DHDR,                 "free_dhdr"},
+    {MNG_FN_FREE_PROM,                 "free_prom"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_FREE_IPNG,                 "free_ipng"},
+#endif
+    {MNG_FN_FREE_PPLT,                 "free_pplt"},
+    {MNG_FN_FREE_IJNG,                 "free_ijng"},
+    {MNG_FN_FREE_DROP,                 "free_drop"},
+    {MNG_FN_FREE_DBYK,                 "free_dbyk"},
+    {MNG_FN_FREE_ORDR,                 "free_ordr"},
+#endif
+    {MNG_FN_FREE_UNKNOWN,              "free_unknown"},
+    {MNG_FN_FREE_MAGN,                 "free_magn"},
+    {MNG_FN_FREE_JDAA,                 "free_jdaa"},
+    {MNG_FN_FREE_EVNT,                 "free_evnt"},
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_FN_FREE_MPNG,                 "free_mpng"},
+#endif
+
+    {MNG_FN_READ_IHDR,                 "read_ihdr"},
+    {MNG_FN_READ_PLTE,                 "read_plte"},
+    {MNG_FN_READ_IDAT,                 "read_idat"},
+    {MNG_FN_READ_IEND,                 "read_iend"},
+    {MNG_FN_READ_TRNS,                 "read_trns"},
+    {MNG_FN_READ_GAMA,                 "read_gama"},
+    {MNG_FN_READ_CHRM,                 "read_chrm"},
+    {MNG_FN_READ_SRGB,                 "read_srgb"},
+    {MNG_FN_READ_ICCP,                 "read_iccp"},
+    {MNG_FN_READ_TEXT,                 "read_text"},
+    {MNG_FN_READ_ZTXT,                 "read_ztxt"},
+    {MNG_FN_READ_ITXT,                 "read_itxt"},
+    {MNG_FN_READ_BKGD,                 "read_bkgd"},
+    {MNG_FN_READ_PHYS,                 "read_phys"},
+    {MNG_FN_READ_SBIT,                 "read_sbit"},
+    {MNG_FN_READ_SPLT,                 "read_splt"},
+    {MNG_FN_READ_HIST,                 "read_hist"},
+    {MNG_FN_READ_TIME,                 "read_time"},
+    {MNG_FN_READ_MHDR,                 "read_mhdr"},
+    {MNG_FN_READ_MEND,                 "read_mend"},
+    {MNG_FN_READ_LOOP,                 "read_loop"},
+    {MNG_FN_READ_ENDL,                 "read_endl"},
+    {MNG_FN_READ_DEFI,                 "read_defi"},
+    {MNG_FN_READ_BASI,                 "read_basi"},
+    {MNG_FN_READ_CLON,                 "read_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_READ_PAST,                 "read_past"},
+#endif
+    {MNG_FN_READ_DISC,                 "read_disc"},
+    {MNG_FN_READ_BACK,                 "read_back"},
+    {MNG_FN_READ_FRAM,                 "read_fram"},
+    {MNG_FN_READ_MOVE,                 "read_move"},
+    {MNG_FN_READ_CLIP,                 "read_clip"},
+    {MNG_FN_READ_SHOW,                 "read_show"},
+    {MNG_FN_READ_TERM,                 "read_term"},
+    {MNG_FN_READ_SAVE,                 "read_save"},
+    {MNG_FN_READ_SEEK,                 "read_seek"},
+    {MNG_FN_READ_EXPI,                 "read_expi"},
+    {MNG_FN_READ_FPRI,                 "read_fpri"},
+    {MNG_FN_READ_NEED,                 "read_need"},
+    {MNG_FN_READ_PHYG,                 "read_phyg"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_READ_JHDR,                 "read_jhdr"},
+    {MNG_FN_READ_JDAT,                 "read_jdat"},
+    {MNG_FN_READ_JSEP,                 "read_jsep"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_READ_DHDR,                 "read_dhdr"},
+    {MNG_FN_READ_PROM,                 "read_prom"},
+    {MNG_FN_READ_IPNG,                 "read_ipng"},
+    {MNG_FN_READ_PPLT,                 "read_pplt"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_READ_IJNG,                 "read_ijng"},
+#endif
+    {MNG_FN_READ_DROP,                 "read_drop"},
+    {MNG_FN_READ_DBYK,                 "read_dbyk"},
+    {MNG_FN_READ_ORDR,                 "read_ordr"},
+#endif
+    {MNG_FN_READ_UNKNOWN,              "read_unknown"},
+    {MNG_FN_READ_MAGN,                 "read_magn"},
+    {MNG_FN_READ_JDAA,                 "read_jdaa"},
+    {MNG_FN_READ_EVNT,                 "read_evnt"},
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_FN_READ_MPNG,                 "read_mpng"},
+#endif
+
+    {MNG_FN_WRITE_IHDR,                "write_ihdr"},
+    {MNG_FN_WRITE_PLTE,                "write_plte"},
+    {MNG_FN_WRITE_IDAT,                "write_idat"},
+    {MNG_FN_WRITE_IEND,                "write_iend"},
+    {MNG_FN_WRITE_TRNS,                "write_trns"},
+    {MNG_FN_WRITE_GAMA,                "write_gama"},
+    {MNG_FN_WRITE_CHRM,                "write_chrm"},
+    {MNG_FN_WRITE_SRGB,                "write_srgb"},
+    {MNG_FN_WRITE_ICCP,                "write_iccp"},
+    {MNG_FN_WRITE_TEXT,                "write_text"},
+    {MNG_FN_WRITE_ZTXT,                "write_ztxt"},
+    {MNG_FN_WRITE_ITXT,                "write_itxt"},
+    {MNG_FN_WRITE_BKGD,                "write_bkgd"},
+    {MNG_FN_WRITE_PHYS,                "write_phys"},
+    {MNG_FN_WRITE_SBIT,                "write_sbit"},
+    {MNG_FN_WRITE_SPLT,                "write_splt"},
+    {MNG_FN_WRITE_HIST,                "write_hist"},
+    {MNG_FN_WRITE_TIME,                "write_time"},
+    {MNG_FN_WRITE_MHDR,                "write_mhdr"},
+    {MNG_FN_WRITE_MEND,                "write_mend"},
+    {MNG_FN_WRITE_LOOP,                "write_loop"},
+    {MNG_FN_WRITE_ENDL,                "write_endl"},
+    {MNG_FN_WRITE_DEFI,                "write_defi"},
+    {MNG_FN_WRITE_BASI,                "write_basi"},
+    {MNG_FN_WRITE_CLON,                "write_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_WRITE_PAST,                "write_past"},
+#endif
+    {MNG_FN_WRITE_DISC,                "write_disc"},
+    {MNG_FN_WRITE_BACK,                "write_back"},
+    {MNG_FN_WRITE_FRAM,                "write_fram"},
+    {MNG_FN_WRITE_MOVE,                "write_move"},
+    {MNG_FN_WRITE_CLIP,                "write_clip"},
+    {MNG_FN_WRITE_SHOW,                "write_show"},
+    {MNG_FN_WRITE_TERM,                "write_term"},
+    {MNG_FN_WRITE_SAVE,                "write_save"},
+    {MNG_FN_WRITE_SEEK,                "write_seek"},
+    {MNG_FN_WRITE_EXPI,                "write_expi"},
+    {MNG_FN_WRITE_FPRI,                "write_fpri"},
+    {MNG_FN_WRITE_NEED,                "write_need"},
+    {MNG_FN_WRITE_PHYG,                "write_phyg"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_WRITE_JHDR,                "write_jhdr"},
+    {MNG_FN_WRITE_JDAT,                "write_jdat"},
+    {MNG_FN_WRITE_JSEP,                "write_jsep"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_WRITE_DHDR,                "write_dhdr"},
+    {MNG_FN_WRITE_PROM,                "write_prom"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_WRITE_IPNG,                "write_ipng"},
+#endif
+    {MNG_FN_WRITE_PPLT,                "write_pplt"},
+    {MNG_FN_WRITE_IJNG,                "write_ijng"},
+    {MNG_FN_WRITE_DROP,                "write_drop"},
+    {MNG_FN_WRITE_DBYK,                "write_dbyk"},
+    {MNG_FN_WRITE_ORDR,                "write_ordr"},
+#endif
+    {MNG_FN_WRITE_UNKNOWN,             "write_unknown"},
+    {MNG_FN_WRITE_MAGN,                "write_magn"},
+    {MNG_FN_WRITE_JDAA,                "write_jdaa"},
+    {MNG_FN_WRITE_EVNT,                "write_evnt"},
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+    {MNG_FN_WRITE_MPNG,                "write_mpng"},
+#endif
+
+    {MNG_FN_ZLIB_INITIALIZE,           "zlib_initialize"},
+    {MNG_FN_ZLIB_CLEANUP,              "zlib_cleanup"},
+    {MNG_FN_ZLIB_INFLATEINIT,          "zlib_inflateinit"},
+    {MNG_FN_ZLIB_INFLATEROWS,          "zlib_inflaterows"},
+    {MNG_FN_ZLIB_INFLATEDATA,          "zlib_inflatedata"},
+    {MNG_FN_ZLIB_INFLATEFREE,          "zlib_inflatefree"},
+    {MNG_FN_ZLIB_DEFLATEINIT,          "zlib_deflateinit"},
+    {MNG_FN_ZLIB_DEFLATEROWS,          "zlib_deflaterows"},
+    {MNG_FN_ZLIB_DEFLATEDATA,          "zlib_deflatedata"},
+    {MNG_FN_ZLIB_DEFLATEFREE,          "zlib_deflatefree"},
+
+    {MNG_FN_PROCESS_DISPLAY_IHDR,      "process_display_ihdr"},
+    {MNG_FN_PROCESS_DISPLAY_PLTE,      "process_display_plte"},
+    {MNG_FN_PROCESS_DISPLAY_IDAT,      "process_display_idat"},
+    {MNG_FN_PROCESS_DISPLAY_IEND,      "process_display_iend"},
+    {MNG_FN_PROCESS_DISPLAY_TRNS,      "process_display_trns"},
+    {MNG_FN_PROCESS_DISPLAY_GAMA,      "process_display_gama"},
+    {MNG_FN_PROCESS_DISPLAY_CHRM,      "process_display_chrm"},
+    {MNG_FN_PROCESS_DISPLAY_SRGB,      "process_display_srgb"},
+    {MNG_FN_PROCESS_DISPLAY_ICCP,      "process_display_iccp"},
+    {MNG_FN_PROCESS_DISPLAY_BKGD,      "process_display_bkgd"},
+    {MNG_FN_PROCESS_DISPLAY_PHYS,      "process_display_phys"},
+    {MNG_FN_PROCESS_DISPLAY_SBIT,      "process_display_sbit"},
+    {MNG_FN_PROCESS_DISPLAY_SPLT,      "process_display_splt"},
+    {MNG_FN_PROCESS_DISPLAY_HIST,      "process_display_hist"},
+    {MNG_FN_PROCESS_DISPLAY_MHDR,      "process_display_mhdr"},
+    {MNG_FN_PROCESS_DISPLAY_MEND,      "process_display_mend"},
+    {MNG_FN_PROCESS_DISPLAY_LOOP,      "process_display_loop"},
+    {MNG_FN_PROCESS_DISPLAY_ENDL,      "process_display_endl"},
+    {MNG_FN_PROCESS_DISPLAY_DEFI,      "process_display_defi"},
+    {MNG_FN_PROCESS_DISPLAY_BASI,      "process_display_basi"},
+    {MNG_FN_PROCESS_DISPLAY_CLON,      "process_display_clon"},
+#ifndef MNG_SKIPCHUNK_PAST
+    {MNG_FN_PROCESS_DISPLAY_PAST,      "process_display_past"},
+#endif
+    {MNG_FN_PROCESS_DISPLAY_DISC,      "process_display_disc"},
+    {MNG_FN_PROCESS_DISPLAY_BACK,      "process_display_back"},
+    {MNG_FN_PROCESS_DISPLAY_FRAM,      "process_display_fram"},
+    {MNG_FN_PROCESS_DISPLAY_MOVE,      "process_display_move"},
+    {MNG_FN_PROCESS_DISPLAY_CLIP,      "process_display_clip"},
+    {MNG_FN_PROCESS_DISPLAY_SHOW,      "process_display_show"},
+    {MNG_FN_PROCESS_DISPLAY_TERM,      "process_display_term"},
+    {MNG_FN_PROCESS_DISPLAY_SAVE,      "process_display_save"},
+    {MNG_FN_PROCESS_DISPLAY_SEEK,      "process_display_seek"},
+    {MNG_FN_PROCESS_DISPLAY_EXPI,      "process_display_expi"},
+    {MNG_FN_PROCESS_DISPLAY_FPRI,      "process_display_fpri"},
+    {MNG_FN_PROCESS_DISPLAY_NEED,      "process_display_need"},
+    {MNG_FN_PROCESS_DISPLAY_PHYG,      "process_display_phyg"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_PROCESS_DISPLAY_JHDR,      "process_display_jhdr"},
+    {MNG_FN_PROCESS_DISPLAY_JDAT,      "process_display_jdat"},
+    {MNG_FN_PROCESS_DISPLAY_JSEP,      "process_display_jsep"},
+#endif
+#ifndef MNG_NO_DELTA_PNG
+    {MNG_FN_PROCESS_DISPLAY_DHDR,      "process_display_dhdr"},
+    {MNG_FN_PROCESS_DISPLAY_PROM,      "process_display_prom"},
+#ifdef MNG_INCLUDE_JNG
+    {MNG_FN_PROCESS_DISPLAY_IPNG,      "process_display_ipng"},
+#endif
+    {MNG_FN_PROCESS_DISPLAY_PPLT,      "process_display_pplt"},
+    {MNG_FN_PROCESS_DISPLAY_IJNG,      "process_display_ijng"},
+    {MNG_FN_PROCESS_DISPLAY_DROP,      "process_display_drop"},
+    {MNG_FN_PROCESS_DISPLAY_DBYK,      "process_display_dbyk"},
+    {MNG_FN_PROCESS_DISPLAY_ORDR,      "process_display_ordr"},
+#endif
+    {MNG_FN_PROCESS_DISPLAY_MAGN,      "process_display_magn"},
+    {MNG_FN_PROCESS_DISPLAY_JDAA,      "process_display_jdaa"},
+
+    {MNG_FN_JPEG_INITIALIZE,           "jpeg_initialize"},
+    {MNG_FN_JPEG_CLEANUP,              "jpeg_cleanup"},
+    {MNG_FN_JPEG_DECOMPRESSINIT,       "jpeg_decompressinit"},
+    {MNG_FN_JPEG_DECOMPRESSDATA,       "jpeg_decompressdata"},
+    {MNG_FN_JPEG_DECOMPRESSFREE,       "jpeg_decompressfree"},
+
+    {MNG_FN_STORE_JPEG_G8,             "store_jpeg_g8"},
+    {MNG_FN_STORE_JPEG_RGB8,           "store_jpeg_rgb8"},
+    {MNG_FN_STORE_JPEG_G12,            "store_jpeg_g12"},
+    {MNG_FN_STORE_JPEG_RGB12,          "store_jpeg_rgb12"},
+    {MNG_FN_STORE_JPEG_GA8,            "store_jpeg_ga8"},
+    {MNG_FN_STORE_JPEG_RGBA8,          "store_jpeg_rgba8"},
+    {MNG_FN_STORE_JPEG_GA12,           "store_jpeg_ga12"},
+    {MNG_FN_STORE_JPEG_RGBA12,         "store_jpeg_rgba12"},
+    {MNG_FN_STORE_JPEG_G8_ALPHA,       "store_jpeg_g8_alpha"},
+    {MNG_FN_STORE_JPEG_RGB8_ALPHA,     "store_jpeg_rgb8_alpha"},
+
+    {MNG_FN_INIT_JPEG_A1_NI,           "init_jpeg_a1_ni"},
+    {MNG_FN_INIT_JPEG_A2_NI,           "init_jpeg_a2_ni"},
+    {MNG_FN_INIT_JPEG_A4_NI,           "init_jpeg_a4_ni"},
+    {MNG_FN_INIT_JPEG_A8_NI,           "init_jpeg_a8_ni"},
+    {MNG_FN_INIT_JPEG_A16_NI,          "init_jpeg_a16_ni"},
+
+    {MNG_FN_STORE_JPEG_G8_A1,          "store_jpeg_g8_a1"},
+    {MNG_FN_STORE_JPEG_G8_A2,          "store_jpeg_g8_a2"},
+    {MNG_FN_STORE_JPEG_G8_A4,          "store_jpeg_g8_a4"},
+    {MNG_FN_STORE_JPEG_G8_A8,          "store_jpeg_g8_a8"},
+    {MNG_FN_STORE_JPEG_G8_A16,         "store_jpeg_g8_a16"},
+
+    {MNG_FN_STORE_JPEG_RGB8_A1,        "store_jpeg_rgb8_a1"},
+    {MNG_FN_STORE_JPEG_RGB8_A2,        "store_jpeg_rgb8_a2"},
+    {MNG_FN_STORE_JPEG_RGB8_A4,        "store_jpeg_rgb8_a4"},
+    {MNG_FN_STORE_JPEG_RGB8_A8,        "store_jpeg_rgb8_a8"},
+    {MNG_FN_STORE_JPEG_RGB8_A16,       "store_jpeg_rgb8_a16"},
+
+    {MNG_FN_STORE_JPEG_G12_A1,         "store_jpeg_g12_a1"},
+    {MNG_FN_STORE_JPEG_G12_A2,         "store_jpeg_g12_a2"},
+    {MNG_FN_STORE_JPEG_G12_A4,         "store_jpeg_g12_a4"},
+    {MNG_FN_STORE_JPEG_G12_A8,         "store_jpeg_g12_a8"},
+    {MNG_FN_STORE_JPEG_G12_A16,        "store_jpeg_g12_a16"},
+
+    {MNG_FN_STORE_JPEG_RGB12_A1,       "store_jpeg_rgb12_a1"},
+    {MNG_FN_STORE_JPEG_RGB12_A2,       "store_jpeg_rgb12_a2"},
+    {MNG_FN_STORE_JPEG_RGB12_A4,       "store_jpeg_rgb12_a4"},
+    {MNG_FN_STORE_JPEG_RGB12_A8,       "store_jpeg_rgb12_a8"},
+    {MNG_FN_STORE_JPEG_RGB12_A16,      "store_jpeg_rgb12_a16"},
+
+    {MNG_FN_NEXT_JPEG_ALPHAROW,        "next_jpeg_alpharow"},
+    {MNG_FN_NEXT_JPEG_ROW,             "next_jpeg_row"},
+    {MNG_FN_DISPLAY_JPEG_ROWS,         "display_jpeg_rows"},
+
+    {MNG_FN_MAGNIFY_G8_X1,             "magnify_g8_x1"},
+    {MNG_FN_MAGNIFY_G8_X2,             "magnify_g8_x2"},
+    {MNG_FN_MAGNIFY_RGB8_X1,           "magnify_rgb8_x1"},
+    {MNG_FN_MAGNIFY_RGB8_X2,           "magnify_rgb8_x2"},
+    {MNG_FN_MAGNIFY_GA8_X1,            "magnify_ga8_x1"},
+    {MNG_FN_MAGNIFY_GA8_X2,            "magnify_ga8_x2"},
+    {MNG_FN_MAGNIFY_GA8_X3,            "magnify_ga8_x3"},
+    {MNG_FN_MAGNIFY_GA8_X4,            "magnify_ga8_x4"},
+    {MNG_FN_MAGNIFY_RGBA8_X1,          "magnify_rgba8_x1"},
+    {MNG_FN_MAGNIFY_RGBA8_X2,          "magnify_rgba8_x2"},
+    {MNG_FN_MAGNIFY_RGBA8_X3,          "magnify_rgba8_x3"},
+    {MNG_FN_MAGNIFY_RGBA8_X4,          "magnify_rgba8_x4"},
+    {MNG_FN_MAGNIFY_G8_X3,             "magnify_g8_x3"},
+    {MNG_FN_MAGNIFY_RGB8_X3,           "magnify_rgb8_x3"},
+    {MNG_FN_MAGNIFY_GA8_X5,            "magnify_ga8_x5"},
+    {MNG_FN_MAGNIFY_RGBA8_X5,          "magnify_rgba8_x5"},
+
+    {MNG_FN_MAGNIFY_G8_Y1,             "magnify_g8_y1"},
+    {MNG_FN_MAGNIFY_G8_Y2,             "magnify_g8_y2"},
+    {MNG_FN_MAGNIFY_RGB8_Y1,           "magnify_rgb8_y1"},
+    {MNG_FN_MAGNIFY_RGB8_Y2,           "magnify_rgb8_y2"},
+    {MNG_FN_MAGNIFY_GA8_Y1,            "magnify_ga8_y1"},
+    {MNG_FN_MAGNIFY_GA8_Y2,            "magnify_ga8_y2"},
+    {MNG_FN_MAGNIFY_GA8_Y3,            "magnify_ga8_y3"},
+    {MNG_FN_MAGNIFY_GA8_Y4,            "magnify_ga8_y4"},
+    {MNG_FN_MAGNIFY_RGBA8_Y1,          "magnify_rgba8_y1"},
+    {MNG_FN_MAGNIFY_RGBA8_Y2,          "magnify_rgba8_y2"},
+    {MNG_FN_MAGNIFY_RGBA8_Y3,          "magnify_rgba8_y3"},
+    {MNG_FN_MAGNIFY_RGBA8_Y4,          "magnify_rgba8_y4"},
+    {MNG_FN_MAGNIFY_G8_Y3,             "magnify_g8_y3"},
+    {MNG_FN_MAGNIFY_RGB8_Y3,           "magnify_rgb8_y3"},
+    {MNG_FN_MAGNIFY_GA8_Y5,            "magnify_ga8_y5"},
+    {MNG_FN_MAGNIFY_RGBA8_Y5,          "magnify_rgba8_y5"},
+
+    {MNG_FN_MAGNIFY_G8_X1,             "magnify_g8_x1"},
+    {MNG_FN_MAGNIFY_G8_X2,             "magnify_g8_x2"},
+    {MNG_FN_MAGNIFY_RGB8_X1,           "magnify_rgb8_x1"},
+    {MNG_FN_MAGNIFY_RGB8_X2,           "magnify_rgb8_x2"},
+    {MNG_FN_MAGNIFY_GA8_X1,            "magnify_ga8_x1"},
+    {MNG_FN_MAGNIFY_GA8_X2,            "magnify_ga8_x2"},
+    {MNG_FN_MAGNIFY_GA8_X3,            "magnify_ga8_x3"},
+    {MNG_FN_MAGNIFY_GA8_X4,            "magnify_ga8_x4"},
+    {MNG_FN_MAGNIFY_RGBA8_X1,          "magnify_rgba8_x1"},
+    {MNG_FN_MAGNIFY_RGBA8_X2,          "magnify_rgba8_x2"},
+    {MNG_FN_MAGNIFY_RGBA8_X3,          "magnify_rgba8_x3"},
+    {MNG_FN_MAGNIFY_RGBA8_X4,          "magnify_rgba8_x4"},
+    {MNG_FN_MAGNIFY_G8_X3,             "magnify_g8_x3"},
+    {MNG_FN_MAGNIFY_RGB8_X3,           "magnify_rgb8_x3"},
+    {MNG_FN_MAGNIFY_GA8_X5,            "magnify_ga8_x5"},
+    {MNG_FN_MAGNIFY_RGBA8_X5,          "magnify_rgba8_x5"},
+
+    {MNG_FN_MAGNIFY_G8_Y1,             "magnify_g8_y1"},
+    {MNG_FN_MAGNIFY_G8_Y2,             "magnify_g8_y2"},
+    {MNG_FN_MAGNIFY_RGB8_Y1,           "magnify_rgb8_y1"},
+    {MNG_FN_MAGNIFY_RGB8_Y2,           "magnify_rgb8_y2"},
+    {MNG_FN_MAGNIFY_GA8_Y1,            "magnify_ga8_y1"},
+    {MNG_FN_MAGNIFY_GA8_Y2,            "magnify_ga8_y2"},
+    {MNG_FN_MAGNIFY_GA8_Y3,            "magnify_ga8_y3"},
+    {MNG_FN_MAGNIFY_GA8_Y4,            "magnify_ga8_y4"},
+    {MNG_FN_MAGNIFY_RGBA8_Y1,          "magnify_rgba8_y1"},
+    {MNG_FN_MAGNIFY_RGBA8_Y2,          "magnify_rgba8_y2"},
+    {MNG_FN_MAGNIFY_RGBA8_Y3,          "magnify_rgba8_y3"},
+    {MNG_FN_MAGNIFY_RGBA8_Y4,          "magnify_rgba8_y4"},
+    {MNG_FN_MAGNIFY_G8_Y3,             "magnify_g8_y3"},
+    {MNG_FN_MAGNIFY_RGB8_Y3,           "magnify_rgb8_y3"},
+    {MNG_FN_MAGNIFY_GA8_Y5,            "magnify_ga8_y5"},
+    {MNG_FN_MAGNIFY_RGBA8_Y5,          "magnify_rgba8_y5"},
+
+    {MNG_FN_DELTA_G1_G1,               "delta_g1_g1"},
+    {MNG_FN_DELTA_G2_G2,               "delta_g2_g2"},
+    {MNG_FN_DELTA_G4_G4,               "delta_g4_g4"},
+    {MNG_FN_DELTA_G8_G8,               "delta_g8_g8"},
+    {MNG_FN_DELTA_G16_G16,             "delta_g16_g16"},
+    {MNG_FN_DELTA_RGB8_RGB8,           "delta_rgb8_rgb8"},
+    {MNG_FN_DELTA_RGB16_RGB16,         "delta_rgb16_rgb16"},
+    {MNG_FN_DELTA_GA8_GA8,             "delta_ga8_ga8"},
+    {MNG_FN_DELTA_GA8_G8,              "delta_ga8_g8"},
+    {MNG_FN_DELTA_GA8_A8,              "delta_ga8_a8"},
+    {MNG_FN_DELTA_GA16_GA16,           "delta_ga16_ga16"},
+    {MNG_FN_DELTA_GA16_G16,            "delta_ga16_g16"},
+    {MNG_FN_DELTA_GA16_A16,            "delta_ga16_a16"},
+    {MNG_FN_DELTA_RGBA8_RGBA8,         "delta_rgba8_rgba8"},
+    {MNG_FN_DELTA_RGBA8_RGB8,          "delta_rgba8_rgb8"},
+    {MNG_FN_DELTA_RGBA8_A8,            "delta_rgba8_a8"},
+    {MNG_FN_DELTA_RGBA16_RGBA16,       "delta_rgba16_rgba16"},
+    {MNG_FN_DELTA_RGBA16_RGB16,        "delta_rgba16_rgb16"},
+    {MNG_FN_DELTA_RGBA16_A16,          "delta_rgba16_a16"},
+
+    {MNG_FN_PROMOTE_G8_G8,             "promote_g8_g8"},
+    {MNG_FN_PROMOTE_G8_G16,            "promote_g8_g16"},
+    {MNG_FN_PROMOTE_G16_G16,           "promote_g8_g16"},
+    {MNG_FN_PROMOTE_G8_GA8,            "promote_g8_ga8"},
+    {MNG_FN_PROMOTE_G8_GA16,           "promote_g8_ga16"},
+    {MNG_FN_PROMOTE_G16_GA16,          "promote_g16_ga16"},
+    {MNG_FN_PROMOTE_G8_RGB8,           "promote_g8_rgb8"},
+    {MNG_FN_PROMOTE_G8_RGB16,          "promote_g8_rgb16"},
+    {MNG_FN_PROMOTE_G16_RGB16,         "promote_g16_rgb16"},
+    {MNG_FN_PROMOTE_G8_RGBA8,          "promote_g8_rgba8"},
+    {MNG_FN_PROMOTE_G8_RGBA16,         "promote_g8_rgba16"},
+    {MNG_FN_PROMOTE_G16_RGBA16,        "promote_g16_rgba16"},
+    {MNG_FN_PROMOTE_GA8_GA16,          "promote_ga8_ga16"},
+    {MNG_FN_PROMOTE_GA8_RGBA8,         "promote_ga8_rgba8"},
+    {MNG_FN_PROMOTE_GA8_RGBA16,        "promote_ga8_rgba16"},
+    {MNG_FN_PROMOTE_GA16_RGBA16,       "promote_ga16_rgba16"},
+    {MNG_FN_PROMOTE_RGB8_RGB16,        "promote_rgb8_rgb16"},
+    {MNG_FN_PROMOTE_RGB8_RGBA8,        "promote_rgb8_rgba8"},
+    {MNG_FN_PROMOTE_RGB8_RGBA16,       "promote_rgb8_rgba16"},
+    {MNG_FN_PROMOTE_RGB16_RGBA16,      "promote_rgb16_rgba16"},
+    {MNG_FN_PROMOTE_RGBA8_RGBA16,      "promote_rgba8_rgba16"},
+    {MNG_FN_PROMOTE_IDX8_RGB8,         "promote_idx8_rgb8"},
+    {MNG_FN_PROMOTE_IDX8_RGB16,        "promote_idx8_rgb16"},
+    {MNG_FN_PROMOTE_IDX8_RGBA8,        "promote_idx8_rgba8"},
+    {MNG_FN_PROMOTE_IDX8_RGBA16,       "promote_idx8_rgba16"},
+
+    {MNG_FN_SCALE_G1_G2,               "scale_g1_g2"},
+    {MNG_FN_SCALE_G1_G4,               "scale_g1_g4"},
+    {MNG_FN_SCALE_G1_G8,               "scale_g1_g8"},
+    {MNG_FN_SCALE_G1_G16,              "scale_g1_g16"},
+    {MNG_FN_SCALE_G2_G4,               "scale_g2_g4"},
+    {MNG_FN_SCALE_G2_G8,               "scale_g2_g8"},
+    {MNG_FN_SCALE_G2_G16,              "scale_g2_g16"},
+    {MNG_FN_SCALE_G4_G8,               "scale_g4_g8"},
+    {MNG_FN_SCALE_G4_G16,              "scale_g4_g16"},
+    {MNG_FN_SCALE_G8_G16,              "scale_g8_g16"},
+    {MNG_FN_SCALE_GA8_GA16,            "scale_ga8_ga16"},
+    {MNG_FN_SCALE_RGB8_RGB16,          "scale_rgb8_rgb16"},
+    {MNG_FN_SCALE_RGBA8_RGBA16,        "scale_rgba8_rgba16"},
+
+    {MNG_FN_SCALE_G2_G1,               "scale_g2_g1"},
+    {MNG_FN_SCALE_G4_G1,               "scale_g4_g1"},
+    {MNG_FN_SCALE_G8_G1,               "scale_g8_g1"},
+    {MNG_FN_SCALE_G16_G1,              "scale_g16_g1"},
+    {MNG_FN_SCALE_G4_G2,               "scale_g4_g2"},
+    {MNG_FN_SCALE_G8_G2,               "scale_g8_g2"},
+    {MNG_FN_SCALE_G16_G2,              "scale_g16_g2"},
+    {MNG_FN_SCALE_G8_G4,               "scale_g8_g4"},
+    {MNG_FN_SCALE_G16_G4,              "scale_g16_g4"},
+    {MNG_FN_SCALE_G16_G8,              "scale_g16_g8"},
+    {MNG_FN_SCALE_GA16_GA8,            "scale_ga16_ga8"},
+    {MNG_FN_SCALE_RGB16_RGB8,          "scale_rgb16_rgb8"},
+    {MNG_FN_SCALE_RGBA16_RGBA8,        "scale_rgba16_rgba8"},
+
+    {MNG_FN_COMPOSEOVER_RGBA8,         "composeover_rgba8"},
+    {MNG_FN_COMPOSEOVER_RGBA16,        "composeover_rgba16"},
+    {MNG_FN_COMPOSEUNDER_RGBA8,        "composeunder_rgba8"},
+    {MNG_FN_COMPOSEUNDER_RGBA16,       "composeunder_rgba16"},
+
+    {MNG_FN_FLIP_RGBA8,                "flip_rgba8"},
+    {MNG_FN_FLIP_RGBA16,               "flip_rgba16"},
+    {MNG_FN_TILE_RGBA8,                "tile_rgba8"},
+    {MNG_FN_TILE_RGBA16,               "tile_rgba16"}
+
+  };
+#endif /* MNG_INCLUDE_TRACE_STINGS */
+
+/* ************************************************************************** */
+
+mng_retcode mng_trace (mng_datap  pData,
+                       mng_uint32 iFunction,
+                       mng_uint32 iLocation)
+{
+  mng_pchar zName = 0;                 /* bufferptr for tracestring */
+
+  if ((pData == 0) || (pData->iMagic != MNG_MAGIC))
+    return MNG_INVALIDHANDLE;          /* no good if the handle is corrupt */
+
+  if (pData->fTraceproc)               /* report back to user ? */
+  {
+#ifdef MNG_INCLUDE_TRACE_STRINGS
+    {                                  /* binary search variables */
+      mng_int32        iTop, iLower, iUpper, iMiddle;
+      mng_trace_entryp pEntry;         /* pointer to found entry */
+                                       /* determine max index of table */
+      iTop = (sizeof (trace_table) / sizeof (trace_table [0])) - 1;
+
+      iLower  = 0;                     /* initialize binary search */
+      iMiddle = iTop >> 1;             /* start in the middle */
+      iUpper  = iTop;
+      pEntry  = 0;                     /* no goods yet! */
+
+      do                               /* the binary search itself */
+        {
+          if (trace_table [iMiddle].iFunction < iFunction)
+            iLower = iMiddle + 1;
+          else if (trace_table [iMiddle].iFunction > iFunction)
+            iUpper = iMiddle - 1;
+          else
+          {
+            pEntry = &trace_table [iMiddle];
+            break;
+          };
+
+          iMiddle = (iLower + iUpper) >> 1;
+        }
+      while (iLower <= iUpper);
+
+      if (pEntry)                      /* found it ? */
+        zName = pEntry->zTracetext;
+
+    }
+#endif
+                                       /* oke, now tell */
+    if (!pData->fTraceproc (((mng_handle)pData), iFunction, iLocation, zName))
+      return MNG_APPTRACEABORT;
+
+  }
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_TRACE_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_trace.h b/files/Source/LibMNG/libmng_trace.h
new file mode 100644
index 0000000..0c749d9
--- /dev/null
+++ b/files/Source/LibMNG/libmng_trace.h
@@ -0,0 +1,1474 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_trace.h            copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : Trace functions (definition)                               * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the trace functions                          * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - added chunk-access function trace-codes                  * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *             0.5.1 - 05/13/2000 - G.Juyn                                * */
+/* *             - added save_state & restore_state trace-codes             * */
+/* *             0.5.1 - 05/15/2000 - G.Juyn                                * */
+/* *             - added getimgdata & putimgdata trace-codes                * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/20/2000 - G.Juyn                                * */
+/* *             - added JNG tracecodes                                     * */
+/* *             0.5.2 - 05/23/2000 - G.Juyn                                * */
+/* *             - added trace-table entry definition                       * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - added tracecodes for global animation color-chunks       * */
+/* *             - added tracecodes for get/set of default ZLIB/IJG parms   * */
+/* *             - added tracecodes for global PLTE,tRNS,bKGD               * */
+/* *             0.5.2 - 05/30/2000 - G.Juyn                                * */
+/* *             - added tracecodes for image-object promotion              * */
+/* *             - added tracecodes for delta-image processing              * */
+/* *             0.5.2 - 06/02/2000 - G.Juyn                                * */
+/* *             - added tracecodes for getalphaline callback               * */
+/* *             0.5.2 - 06/05/2000 - G.Juyn                                * */
+/* *             - added tracecode for RGB8_A8 canvasstyle                  * */
+/* *             0.5.2 - 06/06/2000 - G.Juyn                                * */
+/* *             - added tracecode for mng_read_resume HLAPI function       * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/06/2000 - G.Juyn                                * */
+/* *             - added tracecodes for tracing JPEG progression            * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added tracecodes for get/set speedtype                   * */
+/* *             - added tracecodes for get imagelevel                      * */
+/* *             0.5.3 - 06/22/2000 - G.Juyn                                * */
+/* *             - added tracecode for delta-image processing               * */
+/* *             - added tracecodes for PPLT chunk processing               * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/07/2000 - G.Juyn                                * */
+/* *             - added tracecodes for special display processing          * */
+/* *             0.9.1 - 07/08/2000 - G.Juyn                                * */
+/* *             - added tracecode for get/set suspensionmode               * */
+/* *             - added tracecodes for get/set display variables           * */
+/* *             - added tracecode for read_databuffer (I/O-suspension)     * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added tracecodes for SAVE/SEEK callbacks                 * */
+/* *             - added tracecodes for get/set sectionbreaks               * */
+/* *             - added tracecode for special error routine                * */
+/* *             0.9.1 - 07/19/2000 - G.Juyn                                * */
+/* *             - added tracecode for updatemngheader                      * */
+/* *                                                                        * */
+/* *             0.9.2 - 07/31/2000 - G.Juyn                                * */
+/* *             - added tracecodes for status_xxxxx functions              * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *             - added tracecode for updatemngsimplicity                  * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/26/2000 - G.Juyn                                * */
+/* *             - added MAGN chunk                                         * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *             0.9.3 - 10/10/2000 - G.Juyn                                * */
+/* *             - added support for alpha-depth prediction                 * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - added JDAA chunk                                         * */
+/* *             - added support for nEED                                   * */
+/* *             0.9.3 - 10/16/2000 - G.Juyn                                * */
+/* *             - added functions to retrieve PNG/JNG specific header-info * */
+/* *             - added optional support for bKGD for PNG images           * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added callback to process non-critical unknown chunks    * */
+/* *             - added routine to discard "invalid" objects               * */
+/* *             0.9.3 - 10/19/2000 - G.Juyn                                * */
+/* *             - implemented delayed delta-processing                     * */
+/* *             0.9.3 - 10/20/2000 - G.Juyn                                * */
+/* *             - added get/set for bKGD preference setting                * */
+/* *             0.9.3 - 10/21/2000 - G.Juyn                                * */
+/* *             - added get function for interlace/progressive display     * */
+/* *                                                                        * */
+/* *             0.9.4 -  1/18/2001 - G.Juyn                                * */
+/* *             - added "new" MAGN methods 3, 4 & 5                        * */
+/* *                                                                        * */
+/* *             1.0.1 - 02/08/2001 - G.Juyn                                * */
+/* *             - added MEND processing callback                           * */
+/* *             1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly)              * */
+/* *             - added BGRA8 canvas with premultiplied alpha              * */
+/* *             1.0.1 - 05/02/2001 - G.Juyn                                * */
+/* *             - added "default" sRGB generation (Thanks Marti!)          * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added optimization option for MNG-video playback         * */
+/* *             - added processterm callback                               * */
+/* *             1.0.2 - 06/25/2001 - G.Juyn                                * */
+/* *             - added option to turn off progressive refresh             * */
+/* *                                                                        * */
+/* *             1.0.3 - 08/06/2001 - G.Juyn                                * */
+/* *             - added get function for last processed BACK chunk         * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/15/2002 - G.Juyn                                * */
+/* *             - completed PROM support                                   * */
+/* *             - completed delta-image support                            * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             - added HLAPI function to copy chunks                      * */
+/* *             1.0.5 - 09/14/2002 - G.Juyn                                * */
+/* *             - added event handling for dynamic MNG                     * */
+/* *             1.0.5 - 09/20/2002 - G.Juyn                                * */
+/* *             - added support for PAST                                   * */
+/* *             1.0.5 - 09/22/2002 - G.Juyn                                * */
+/* *             - added bgrx8 canvas (filler byte)                         * */
+/* *             1.0.5 - 09/23/2002 - G.Juyn                                * */
+/* *             - added in-memory color-correction of abstract images      * */
+/* *             - added compose over/under routines for PAST processing    * */
+/* *             - added flip & tile routines for PAST processing           * */
+/* *             1.0.5 - 10/09/2002 - G.Juyn                                * */
+/* *             - fixed trace-constants for PAST chunk                     * */
+/* *             1.0.5 - 11/07/2002 - G.Juyn                                * */
+/* *             - added support to get totals after mng_read()             * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/14/2003 - G.Randers-Pehrson                     * */
+/* *             - added conditionals around rarely used features           * */
+/* *                                                                        * */
+/* *             1.0.7 - 11/27/2003 - R.A                                   * */
+/* *             - added CANVAS_RGB565 and CANVAS_BGR565                    * */
+/* *             1.0.7 - 01/25/2004 - J.S                                   * */
+/* *             - added premultiplied alpha canvas' for RGBA, ARGB, ABGR   * */
+/* *             1.0.7 - 03/10/2004 - G.R-P                                 * */
+/* *             - added conditionals around openstream/closestream         * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/02/2004 - G.Juyn                                * */
+/* *             - added CRC existence & checking flags                     * */
+/* *             1.0.8 - 04/11/2004 - G.Juyn                                * */
+/* *             - added data-push mechanisms for specialized decoders      * */
+/* *                                                                        * */
+/* *             1.0.9 - 10/03/2004 - G.Juyn                                * */
+/* *             - added function to retrieve current FRAM delay            * */
+/* *             1.0.9 - 10/14/2004 - G.Juyn                                * */
+/* *             - added bgr565_a8 canvas-style (thanks to J. Elvander)     * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 07/06/2007 - G.R-P bugfix by Lucas Quintana       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_trace_h_
+#define _libmng_trace_h_
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_TRACE_PROCS
+
+/* ************************************************************************** */
+
+/* TODO: add a trace-mask so certain functions can be excluded */
+
+mng_retcode mng_trace (mng_datap  pData,
+                       mng_uint32 iFunction,
+                       mng_uint32 iLocation);
+
+/* ************************************************************************** */
+
+#define MNG_TRACE(D,F,L)  { mng_retcode iR = mng_trace (D,F,L); \
+                            if (iR) return iR; }
+
+#define MNG_TRACEB(D,F,L) { if (mng_trace (D,F,L)) return MNG_FALSE; }
+
+#define MNG_TRACEX(D,F,L) { if (mng_trace (D,F,L)) return 0; }
+
+/* ************************************************************************** */
+
+#define MNG_LC_START                    1
+#define MNG_LC_END                      2
+#define MNG_LC_INITIALIZE               3
+#define MNG_LC_CLEANUP                  4
+
+/* ************************************************************************** */
+
+#define MNG_LC_JPEG_CREATE_DECOMPRESS   101
+#define MNG_LC_JPEG_READ_HEADER         102
+#define MNG_LC_JPEG_START_DECOMPRESS    103
+#define MNG_LC_JPEG_START_OUTPUT        104
+#define MNG_LC_JPEG_READ_SCANLINES      105
+#define MNG_LC_JPEG_FINISH_OUTPUT       106
+#define MNG_LC_JPEG_FINISH_DECOMPRESS   107
+#define MNG_LC_JPEG_DESTROY_DECOMPRESS  108
+
+/* ************************************************************************** */
+
+#define MNG_FN_INITIALIZE               1
+#define MNG_FN_RESET                    2
+#define MNG_FN_CLEANUP                  3
+#define MNG_FN_READ                     4
+#define MNG_FN_WRITE                    5
+#define MNG_FN_CREATE                   6
+#define MNG_FN_READDISPLAY              7
+#define MNG_FN_DISPLAY                  8
+#define MNG_FN_DISPLAY_RESUME           9
+#define MNG_FN_DISPLAY_FREEZE          10
+#define MNG_FN_DISPLAY_RESET           11
+#ifndef MNG_NO_DISPLAY_GO_SUPPORTED
+#define MNG_FN_DISPLAY_GOFRAME         12
+#define MNG_FN_DISPLAY_GOLAYER         13
+#define MNG_FN_DISPLAY_GOTIME          14
+#endif
+#define MNG_FN_GETLASTERROR            15
+#define MNG_FN_READ_RESUME             16
+#define MNG_FN_TRAPEVENT               17
+#define MNG_FN_READ_PUSHDATA           18
+#define MNG_FN_READ_PUSHSIG            19
+#define MNG_FN_READ_PUSHCHUNK          20
+
+#define MNG_FN_SETCB_MEMALLOC         101
+#define MNG_FN_SETCB_MEMFREE          102
+#define MNG_FN_SETCB_READDATA         103
+#define MNG_FN_SETCB_WRITEDATA        104
+#define MNG_FN_SETCB_ERRORPROC        105
+#define MNG_FN_SETCB_TRACEPROC        106
+#define MNG_FN_SETCB_PROCESSHEADER    107
+#define MNG_FN_SETCB_PROCESSTEXT      108
+#define MNG_FN_SETCB_GETCANVASLINE    109
+#define MNG_FN_SETCB_GETBKGDLINE      110
+#define MNG_FN_SETCB_REFRESH          111
+#define MNG_FN_SETCB_GETTICKCOUNT     112
+#define MNG_FN_SETCB_SETTIMER         113
+#define MNG_FN_SETCB_PROCESSGAMMA     114
+#define MNG_FN_SETCB_PROCESSCHROMA    115
+#define MNG_FN_SETCB_PROCESSSRGB      116
+#define MNG_FN_SETCB_PROCESSICCP      117
+#define MNG_FN_SETCB_PROCESSAROW      118
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+#define MNG_FN_SETCB_OPENSTREAM       119
+#define MNG_FN_SETCB_CLOSESTREAM      120
+#endif
+#define MNG_FN_SETCB_GETALPHALINE     121
+#define MNG_FN_SETCB_PROCESSSAVE      122
+#define MNG_FN_SETCB_PROCESSSEEK      123
+#define MNG_FN_SETCB_PROCESSNEED      124
+#define MNG_FN_SETCB_PROCESSUNKNOWN   125
+#define MNG_FN_SETCB_PROCESSMEND      126
+#define MNG_FN_SETCB_PROCESSTERM      127
+#define MNG_FN_SETCB_RELEASEDATA      128
+
+#define MNG_FN_GETCB_MEMALLOC         201
+#define MNG_FN_GETCB_MEMFREE          202
+#define MNG_FN_GETCB_READDATA         203
+#define MNG_FN_GETCB_WRITEDATA        204
+#define MNG_FN_GETCB_ERRORPROC        205
+#define MNG_FN_GETCB_TRACEPROC        206
+#define MNG_FN_GETCB_PROCESSHEADER    207
+#define MNG_FN_GETCB_PROCESSTEXT      208
+#define MNG_FN_GETCB_GETCANVASLINE    209
+#define MNG_FN_GETCB_GETBKGDLINE      210
+#define MNG_FN_GETCB_REFRESH          211
+#define MNG_FN_GETCB_GETTICKCOUNT     212
+#define MNG_FN_GETCB_SETTIMER         213
+#define MNG_FN_GETCB_PROCESSGAMMA     214
+#define MNG_FN_GETCB_PROCESSCHROMA    215
+#define MNG_FN_GETCB_PROCESSSRGB      216
+#define MNG_FN_GETCB_PROCESSICCP      217
+#define MNG_FN_GETCB_PROCESSAROW      218
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+#define MNG_FN_GETCB_OPENSTREAM       219
+#define MNG_FN_GETCB_CLOSESTREAM      220
+#endif
+#define MNG_FN_GETCB_GETALPHALINE     221
+#define MNG_FN_GETCB_PROCESSSAVE      222
+#define MNG_FN_GETCB_PROCESSSEEK      223
+#define MNG_FN_GETCB_PROCESSNEED      224
+#define MNG_FN_GETCB_PROCESSUNKNOWN   225
+#define MNG_FN_GETCB_PROCESSMEND      226
+#define MNG_FN_GETCB_PROCESSTERM      227
+#define MNG_FN_GETCB_RELEASEDATA      228
+
+#define MNG_FN_SET_USERDATA           301
+#define MNG_FN_SET_CANVASSTYLE        302
+#define MNG_FN_SET_BKGDSTYLE          303
+#define MNG_FN_SET_BGCOLOR            304
+#define MNG_FN_SET_STORECHUNKS        305
+#define MNG_FN_SET_VIEWGAMMA          306
+#define MNG_FN_SET_DISPLAYGAMMA       307
+#define MNG_FN_SET_DFLTIMGGAMMA       308
+#define MNG_FN_SET_SRGB               309
+#define MNG_FN_SET_OUTPUTPROFILE      310
+#define MNG_FN_SET_SRGBPROFILE        311
+#define MNG_FN_SET_MAXCANVASWIDTH     312
+#define MNG_FN_SET_MAXCANVASHEIGHT    313
+#define MNG_FN_SET_MAXCANVASSIZE      314
+#define MNG_FN_SET_ZLIB_LEVEL         315
+#define MNG_FN_SET_ZLIB_METHOD        316
+#define MNG_FN_SET_ZLIB_WINDOWBITS    317
+#define MNG_FN_SET_ZLIB_MEMLEVEL      318
+#define MNG_FN_SET_ZLIB_STRATEGY      319
+#define MNG_FN_SET_ZLIB_MAXIDAT       320
+#define MNG_FN_SET_JPEG_DCTMETHOD     321
+#define MNG_FN_SET_JPEG_QUALITY       322
+#define MNG_FN_SET_JPEG_SMOOTHING     323
+#define MNG_FN_SET_JPEG_PROGRESSIVE   324
+#define MNG_FN_SET_JPEG_OPTIMIZED     325
+#define MNG_FN_SET_JPEG_MAXJDAT       326
+#define MNG_FN_SET_SPEED              327
+#define MNG_FN_SET_SUSPENSIONMODE     328
+#define MNG_FN_SET_SECTIONBREAKS      329
+#define MNG_FN_SET_USEBKGD            330
+#define MNG_FN_SET_OUTPUTPROFILE2     331
+#define MNG_FN_SET_SRGBPROFILE2       332
+#define MNG_FN_SET_OUTPUTSRGB         333
+#define MNG_FN_SET_SRGBIMPLICIT       334
+#define MNG_FN_SET_CACHEPLAYBACK      335
+#define MNG_FN_SET_DOPROGRESSIVE      336
+#define MNG_FN_SET_CRCMODE            337
+
+#define MNG_FN_GET_USERDATA           401
+#define MNG_FN_GET_SIGTYPE            402
+#define MNG_FN_GET_IMAGETYPE          403
+#define MNG_FN_GET_IMAGEWIDTH         404
+#define MNG_FN_GET_IMAGEHEIGHT        405
+#define MNG_FN_GET_TICKS              406
+#define MNG_FN_GET_FRAMECOUNT         407
+#define MNG_FN_GET_LAYERCOUNT         408
+#define MNG_FN_GET_PLAYTIME           409
+#define MNG_FN_GET_SIMPLICITY         410
+#define MNG_FN_GET_CANVASSTYLE        411
+#define MNG_FN_GET_BKGDSTYLE          412
+#define MNG_FN_GET_BGCOLOR            413
+#define MNG_FN_GET_STORECHUNKS        414
+#define MNG_FN_GET_VIEWGAMMA          415
+#define MNG_FN_GET_DISPLAYGAMMA       416
+#define MNG_FN_GET_DFLTIMGGAMMA       417
+#define MNG_FN_GET_SRGB               418
+#define MNG_FN_GET_MAXCANVASWIDTH     419
+#define MNG_FN_GET_MAXCANVASHEIGHT    420
+#define MNG_FN_GET_ZLIB_LEVEL         421
+#define MNG_FN_GET_ZLIB_METHOD        422
+#define MNG_FN_GET_ZLIB_WINDOWBITS    423
+#define MNG_FN_GET_ZLIB_MEMLEVEL      424
+#define MNG_FN_GET_ZLIB_STRATEGY      425
+#define MNG_FN_GET_ZLIB_MAXIDAT       426
+#define MNG_FN_GET_JPEG_DCTMETHOD     427
+#define MNG_FN_GET_JPEG_QUALITY       428
+#define MNG_FN_GET_JPEG_SMOOTHING     429
+#define MNG_FN_GET_JPEG_PROGRESSIVE   430
+#define MNG_FN_GET_JPEG_OPTIMIZED     431
+#define MNG_FN_GET_JPEG_MAXJDAT       432
+#define MNG_FN_GET_SPEED              433
+#define MNG_FN_GET_IMAGELEVEL         434
+#define MNG_FN_GET_SUSPENSIONMODE     435
+#define MNG_FN_GET_STARTTIME          436
+#define MNG_FN_GET_RUNTIME            437
+#define MNG_FN_GET_CURRENTFRAME       438
+#define MNG_FN_GET_CURRENTLAYER       439
+#define MNG_FN_GET_CURRENTPLAYTIME    440
+#define MNG_FN_GET_SECTIONBREAKS      441
+#define MNG_FN_GET_ALPHADEPTH         442
+#define MNG_FN_GET_BITDEPTH           443
+#define MNG_FN_GET_COLORTYPE          444
+#define MNG_FN_GET_COMPRESSION        445
+#define MNG_FN_GET_FILTER             446
+#define MNG_FN_GET_INTERLACE          447
+#define MNG_FN_GET_ALPHABITDEPTH      448
+#define MNG_FN_GET_ALPHACOMPRESSION   449
+#define MNG_FN_GET_ALPHAFILTER        450
+#define MNG_FN_GET_ALPHAINTERLACE     451
+#define MNG_FN_GET_USEBKGD            452
+#define MNG_FN_GET_REFRESHPASS        453
+#define MNG_FN_GET_CACHEPLAYBACK      454
+#define MNG_FN_GET_DOPROGRESSIVE      455
+#define MNG_FN_GET_LASTBACKCHUNK      456
+#define MNG_FN_GET_LASTSEEKNAME       457
+#define MNG_FN_GET_TOTALFRAMES        458
+#define MNG_FN_GET_TOTALLAYERS        459
+#define MNG_FN_GET_TOTALPLAYTIME      460
+#define MNG_FN_GET_CRCMODE            461
+#define MNG_FN_GET_CURRFRAMDELAY      462
+
+#define MNG_FN_STATUS_ERROR           481
+#define MNG_FN_STATUS_READING         482
+#define MNG_FN_STATUS_SUSPENDBREAK    483
+#define MNG_FN_STATUS_CREATING        484
+#define MNG_FN_STATUS_WRITING         485
+#define MNG_FN_STATUS_DISPLAYING      486
+#define MNG_FN_STATUS_RUNNING         487
+#define MNG_FN_STATUS_TIMERBREAK      488
+#define MNG_FN_STATUS_DYNAMIC         489
+#define MNG_FN_STATUS_RUNNINGEVENT    490
+
+/* ************************************************************************** */
+
+#define MNG_FN_ITERATE_CHUNKS         601
+#define MNG_FN_COPY_CHUNK             602
+
+#define MNG_FN_GETCHUNK_IHDR          701
+#define MNG_FN_GETCHUNK_PLTE          702
+#define MNG_FN_GETCHUNK_IDAT          703
+#define MNG_FN_GETCHUNK_IEND          704
+#define MNG_FN_GETCHUNK_TRNS          705
+#define MNG_FN_GETCHUNK_GAMA          706
+#define MNG_FN_GETCHUNK_CHRM          707
+#define MNG_FN_GETCHUNK_SRGB          708
+#define MNG_FN_GETCHUNK_ICCP          709
+#define MNG_FN_GETCHUNK_TEXT          710
+#define MNG_FN_GETCHUNK_ZTXT          711
+#define MNG_FN_GETCHUNK_ITXT          712
+#define MNG_FN_GETCHUNK_BKGD          713
+#define MNG_FN_GETCHUNK_PHYS          714
+#define MNG_FN_GETCHUNK_SBIT          715
+#define MNG_FN_GETCHUNK_SPLT          716
+#define MNG_FN_GETCHUNK_HIST          717
+#define MNG_FN_GETCHUNK_TIME          718
+#define MNG_FN_GETCHUNK_MHDR          719
+#define MNG_FN_GETCHUNK_MEND          720
+#define MNG_FN_GETCHUNK_LOOP          721
+#define MNG_FN_GETCHUNK_ENDL          722
+#define MNG_FN_GETCHUNK_DEFI          723
+#define MNG_FN_GETCHUNK_BASI          724
+#define MNG_FN_GETCHUNK_CLON          725
+#define MNG_FN_GETCHUNK_PAST          726
+#define MNG_FN_GETCHUNK_DISC          727
+#define MNG_FN_GETCHUNK_BACK          728
+#define MNG_FN_GETCHUNK_FRAM          729
+#define MNG_FN_GETCHUNK_MOVE          730
+#define MNG_FN_GETCHUNK_CLIP          731
+#define MNG_FN_GETCHUNK_SHOW          732
+#define MNG_FN_GETCHUNK_TERM          733
+#define MNG_FN_GETCHUNK_SAVE          734
+#define MNG_FN_GETCHUNK_SEEK          735
+#define MNG_FN_GETCHUNK_EXPI          736
+#define MNG_FN_GETCHUNK_FPRI          737
+#define MNG_FN_GETCHUNK_NEED          738
+#define MNG_FN_GETCHUNK_PHYG          739
+#define MNG_FN_GETCHUNK_JHDR          740
+#define MNG_FN_GETCHUNK_JDAT          741
+#define MNG_FN_GETCHUNK_JSEP          742
+#define MNG_FN_GETCHUNK_DHDR          743
+#define MNG_FN_GETCHUNK_PROM          744
+#define MNG_FN_GETCHUNK_IPNG          745
+#define MNG_FN_GETCHUNK_PPLT          746
+#define MNG_FN_GETCHUNK_IJNG          747
+#define MNG_FN_GETCHUNK_DROP          748
+#define MNG_FN_GETCHUNK_DBYK          749
+#define MNG_FN_GETCHUNK_ORDR          750
+#define MNG_FN_GETCHUNK_UNKNOWN       751
+#define MNG_FN_GETCHUNK_MAGN          752
+#define MNG_FN_GETCHUNK_JDAA          753
+#define MNG_FN_GETCHUNK_EVNT          754
+#define MNG_FN_GETCHUNK_MPNG          755
+
+#define MNG_FN_GETCHUNK_PAST_SRC      781
+#define MNG_FN_GETCHUNK_SAVE_ENTRY    782
+#define MNG_FN_GETCHUNK_PPLT_ENTRY    783
+#define MNG_FN_GETCHUNK_ORDR_ENTRY    784
+#define MNG_FN_GETCHUNK_EVNT_ENTRY    785
+#define MNG_FN_GETCHUNK_MPNG_FRAME    786
+
+#define MNG_FN_PUTCHUNK_IHDR          801
+#define MNG_FN_PUTCHUNK_PLTE          802
+#define MNG_FN_PUTCHUNK_IDAT          803
+#define MNG_FN_PUTCHUNK_IEND          804
+#define MNG_FN_PUTCHUNK_TRNS          805
+#define MNG_FN_PUTCHUNK_GAMA          806
+#define MNG_FN_PUTCHUNK_CHRM          807
+#define MNG_FN_PUTCHUNK_SRGB          808
+#define MNG_FN_PUTCHUNK_ICCP          809
+#define MNG_FN_PUTCHUNK_TEXT          810
+#define MNG_FN_PUTCHUNK_ZTXT          811
+#define MNG_FN_PUTCHUNK_ITXT          812
+#define MNG_FN_PUTCHUNK_BKGD          813
+#define MNG_FN_PUTCHUNK_PHYS          814
+#define MNG_FN_PUTCHUNK_SBIT          815
+#define MNG_FN_PUTCHUNK_SPLT          816
+#define MNG_FN_PUTCHUNK_HIST          817
+#define MNG_FN_PUTCHUNK_TIME          818
+#define MNG_FN_PUTCHUNK_MHDR          819
+#define MNG_FN_PUTCHUNK_MEND          820
+#define MNG_FN_PUTCHUNK_LOOP          821
+#define MNG_FN_PUTCHUNK_ENDL          822
+#define MNG_FN_PUTCHUNK_DEFI          823
+#define MNG_FN_PUTCHUNK_BASI          824
+#define MNG_FN_PUTCHUNK_CLON          825
+#define MNG_FN_PUTCHUNK_PAST          826
+#define MNG_FN_PUTCHUNK_DISC          827
+#define MNG_FN_PUTCHUNK_BACK          828
+#define MNG_FN_PUTCHUNK_FRAM          829
+#define MNG_FN_PUTCHUNK_MOVE          830
+#define MNG_FN_PUTCHUNK_CLIP          831
+#define MNG_FN_PUTCHUNK_SHOW          832
+#define MNG_FN_PUTCHUNK_TERM          833
+#define MNG_FN_PUTCHUNK_SAVE          834
+#define MNG_FN_PUTCHUNK_SEEK          835
+#define MNG_FN_PUTCHUNK_EXPI          836
+#define MNG_FN_PUTCHUNK_FPRI          837
+#define MNG_FN_PUTCHUNK_NEED          838
+#define MNG_FN_PUTCHUNK_PHYG          839
+#define MNG_FN_PUTCHUNK_JHDR          840
+#define MNG_FN_PUTCHUNK_JDAT          841
+#define MNG_FN_PUTCHUNK_JSEP          842
+#define MNG_FN_PUTCHUNK_DHDR          843
+#define MNG_FN_PUTCHUNK_PROM          844
+#define MNG_FN_PUTCHUNK_IPNG          845
+#define MNG_FN_PUTCHUNK_PPLT          846
+#define MNG_FN_PUTCHUNK_IJNG          847
+#define MNG_FN_PUTCHUNK_DROP          848
+#define MNG_FN_PUTCHUNK_DBYK          849
+#define MNG_FN_PUTCHUNK_ORDR          850
+#define MNG_FN_PUTCHUNK_UNKNOWN       851
+#define MNG_FN_PUTCHUNK_MAGN          852
+#define MNG_FN_PUTCHUNK_JDAA          853
+#define MNG_FN_PUTCHUNK_EVNT          854
+#define MNG_FN_PUTCHUNK_MPNG          855
+
+#define MNG_FN_PUTCHUNK_PAST_SRC      881
+#define MNG_FN_PUTCHUNK_SAVE_ENTRY    882
+#define MNG_FN_PUTCHUNK_PPLT_ENTRY    883
+#define MNG_FN_PUTCHUNK_ORDR_ENTRY    884
+#define MNG_FN_PUTCHUNK_EVNT_ENTRY    885
+#define MNG_FN_PUTCHUNK_MPNG_FRAME    886
+
+/* ************************************************************************** */
+
+#define MNG_FN_GETIMGDATA_SEQ         901
+#define MNG_FN_GETIMGDATA_CHUNKSEQ    902
+#define MNG_FN_GETIMGDATA_CHUNK       903
+
+#define MNG_FN_PUTIMGDATA_IHDR        951
+#define MNG_FN_PUTIMGDATA_JHDR        952
+#define MNG_FN_PUTIMGDATA_BASI        953
+#define MNG_FN_PUTIMGDATA_DHDR        954
+
+#define MNG_FN_UPDATEMNGHEADER        981
+#define MNG_FN_UPDATEMNGSIMPLICITY    982
+
+/* ************************************************************************** */
+
+#define MNG_FN_PROCESS_RAW_CHUNK     1001
+#define MNG_FN_READ_GRAPHIC          1002
+#define MNG_FN_DROP_CHUNKS           1003
+#define MNG_FN_PROCESS_ERROR         1004
+#define MNG_FN_CLEAR_CMS             1005
+#define MNG_FN_DROP_OBJECTS          1006
+#define MNG_FN_READ_CHUNK            1007
+#define MNG_FN_LOAD_BKGDLAYER        1008
+#define MNG_FN_NEXT_FRAME            1009
+#define MNG_FN_NEXT_LAYER            1010
+#define MNG_FN_INTERFRAME_DELAY      1011
+#define MNG_FN_DISPLAY_IMAGE         1012
+#define MNG_FN_DROP_IMGOBJECTS       1013
+#define MNG_FN_DROP_ANIOBJECTS       1014
+#define MNG_FN_INFLATE_BUFFER        1015
+#define MNG_FN_DEFLATE_BUFFER        1016
+#define MNG_FN_WRITE_RAW_CHUNK       1017
+#define MNG_FN_WRITE_GRAPHIC         1018
+#define MNG_FN_SAVE_STATE            1019
+#define MNG_FN_RESTORE_STATE         1020
+#define MNG_FN_DROP_SAVEDATA         1021
+#define MNG_FN_EXECUTE_DELTA_IMAGE   1022
+#define MNG_FN_PROCESS_DISPLAY       1023
+#define MNG_FN_CLEAR_CANVAS          1024
+#define MNG_FN_READ_DATABUFFER       1025
+#define MNG_FN_STORE_ERROR           1026
+#define MNG_FN_DROP_INVALID_OBJECTS  1027
+#define MNG_FN_RELEASE_PUSHDATA      1028
+#define MNG_FN_READ_DATA             1029
+#define MNG_FN_READ_CHUNK_CRC        1030
+#define MNG_FN_RELEASE_PUSHCHUNK     1031
+
+/* ************************************************************************** */
+
+#define MNG_FN_DISPLAY_RGB8          1101
+#define MNG_FN_DISPLAY_RGBA8         1102
+#define MNG_FN_DISPLAY_ARGB8         1103
+#define MNG_FN_DISPLAY_BGR8          1104
+#define MNG_FN_DISPLAY_BGRA8         1105
+#define MNG_FN_DISPLAY_ABGR8         1106
+#define MNG_FN_DISPLAY_RGB16         1107
+#define MNG_FN_DISPLAY_RGBA16        1108
+#define MNG_FN_DISPLAY_ARGB16        1109
+#define MNG_FN_DISPLAY_BGR16         1110
+#define MNG_FN_DISPLAY_BGRA16        1111
+#define MNG_FN_DISPLAY_ABGR16        1112
+#define MNG_FN_DISPLAY_INDEX8        1113
+#define MNG_FN_DISPLAY_INDEXA8       1114
+#define MNG_FN_DISPLAY_AINDEX8       1115
+#define MNG_FN_DISPLAY_GRAY8         1116
+#define MNG_FN_DISPLAY_GRAY16        1117
+#define MNG_FN_DISPLAY_GRAYA8        1118
+#define MNG_FN_DISPLAY_GRAYA16       1119
+#define MNG_FN_DISPLAY_AGRAY8        1120
+#define MNG_FN_DISPLAY_AGRAY16       1121
+#define MNG_FN_DISPLAY_DX15          1122
+#define MNG_FN_DISPLAY_DX16          1123
+#define MNG_FN_DISPLAY_RGB8_A8       1124
+#define MNG_FN_DISPLAY_BGRA8PM       1125
+#define MNG_FN_DISPLAY_BGRX8         1126
+#define MNG_FN_DISPLAY_RGB565        1127
+#define MNG_FN_DISPLAY_RGBA565       1128
+#define MNG_FN_DISPLAY_BGR565        1129
+#define MNG_FN_DISPLAY_BGRA565       1130
+#define MNG_FN_DISPLAY_RGBA8_PM      1131
+#define MNG_FN_DISPLAY_ARGB8_PM      1132
+#define MNG_FN_DISPLAY_ABGR8_PM      1133
+#define MNG_FN_DISPLAY_BGR565_A8     1134
+#define MNG_FN_DISPLAY_RGB555        1135
+#define MNG_FN_DISPLAY_BGR555        1136
+
+/* ************************************************************************** */
+
+#define MNG_FN_INIT_FULL_CMS         1201
+#define MNG_FN_CORRECT_FULL_CMS      1202
+#define MNG_FN_INIT_GAMMA_ONLY       1204
+#define MNG_FN_CORRECT_GAMMA_ONLY    1205
+#define MNG_FN_CORRECT_APP_CMS       1206
+#define MNG_FN_INIT_FULL_CMS_OBJ     1207
+#define MNG_FN_INIT_GAMMA_ONLY_OBJ   1208
+#define MNG_FN_INIT_APP_CMS          1209
+#define MNG_FN_INIT_APP_CMS_OBJ      1210
+
+/* ************************************************************************** */
+
+#define MNG_FN_PROCESS_G1            1301
+#define MNG_FN_PROCESS_G2            1302
+#define MNG_FN_PROCESS_G4            1303
+#define MNG_FN_PROCESS_G8            1304
+#define MNG_FN_PROCESS_G16           1305
+#define MNG_FN_PROCESS_RGB8          1306
+#define MNG_FN_PROCESS_RGB16         1307
+#define MNG_FN_PROCESS_IDX1          1308
+#define MNG_FN_PROCESS_IDX2          1309
+#define MNG_FN_PROCESS_IDX4          1310
+#define MNG_FN_PROCESS_IDX8          1311
+#define MNG_FN_PROCESS_GA8           1312
+#define MNG_FN_PROCESS_GA16          1313
+#define MNG_FN_PROCESS_RGBA8         1314
+#define MNG_FN_PROCESS_RGBA16        1315
+
+/* ************************************************************************** */
+
+#define MNG_FN_INIT_G1_NI            1401
+#define MNG_FN_INIT_G1_I             1402
+#define MNG_FN_INIT_G2_NI            1403
+#define MNG_FN_INIT_G2_I             1404
+#define MNG_FN_INIT_G4_NI            1405
+#define MNG_FN_INIT_G4_I             1406
+#define MNG_FN_INIT_G8_NI            1407
+#define MNG_FN_INIT_G8_I             1408
+#define MNG_FN_INIT_G16_NI           1409
+#define MNG_FN_INIT_G16_I            1410
+#define MNG_FN_INIT_RGB8_NI          1411
+#define MNG_FN_INIT_RGB8_I           1412
+#define MNG_FN_INIT_RGB16_NI         1413
+#define MNG_FN_INIT_RGB16_I          1414
+#define MNG_FN_INIT_IDX1_NI          1415
+#define MNG_FN_INIT_IDX1_I           1416
+#define MNG_FN_INIT_IDX2_NI          1417
+#define MNG_FN_INIT_IDX2_I           1418
+#define MNG_FN_INIT_IDX4_NI          1419
+#define MNG_FN_INIT_IDX4_I           1420
+#define MNG_FN_INIT_IDX8_NI          1421
+#define MNG_FN_INIT_IDX8_I           1422
+#define MNG_FN_INIT_GA8_NI           1423
+#define MNG_FN_INIT_GA8_I            1424
+#define MNG_FN_INIT_GA16_NI          1425
+#define MNG_FN_INIT_GA16_I           1426
+#define MNG_FN_INIT_RGBA8_NI         1427
+#define MNG_FN_INIT_RGBA8_I          1428
+#define MNG_FN_INIT_RGBA16_NI        1429
+#define MNG_FN_INIT_RGBA16_I         1430
+
+#define MNG_FN_INIT_ROWPROC          1497
+#define MNG_FN_NEXT_ROW              1498
+#define MNG_FN_CLEANUP_ROWPROC       1499
+
+/* ************************************************************************** */
+
+#define MNG_FN_FILTER_A_ROW          1501
+#define MNG_FN_FILTER_SUB            1502
+#define MNG_FN_FILTER_UP             1503
+#define MNG_FN_FILTER_AVERAGE        1504
+#define MNG_FN_FILTER_PAETH          1505
+
+#define MNG_FN_INIT_ROWDIFFERING     1551
+#define MNG_FN_DIFFER_G1             1552
+#define MNG_FN_DIFFER_G2             1553
+#define MNG_FN_DIFFER_G4             1554
+#define MNG_FN_DIFFER_G8             1555
+#define MNG_FN_DIFFER_G16            1556
+#define MNG_FN_DIFFER_RGB8           1557
+#define MNG_FN_DIFFER_RGB16          1558
+#define MNG_FN_DIFFER_IDX1           1559
+#define MNG_FN_DIFFER_IDX2           1560
+#define MNG_FN_DIFFER_IDX4           1561
+#define MNG_FN_DIFFER_IDX8           1562
+#define MNG_FN_DIFFER_GA8            1563
+#define MNG_FN_DIFFER_GA16           1564
+#define MNG_FN_DIFFER_RGBA8          1565
+#define MNG_FN_DIFFER_RGBA16         1566
+
+/* ************************************************************************** */
+
+#define MNG_FN_CREATE_IMGDATAOBJECT  1601
+#define MNG_FN_FREE_IMGDATAOBJECT    1602
+#define MNG_FN_CLONE_IMGDATAOBJECT   1603
+#define MNG_FN_CREATE_IMGOBJECT      1604
+#define MNG_FN_FREE_IMGOBJECT        1605
+#define MNG_FN_FIND_IMGOBJECT        1606
+#define MNG_FN_CLONE_IMGOBJECT       1607
+#define MNG_FN_RESET_OBJECTDETAILS   1608
+#define MNG_FN_RENUM_IMGOBJECT       1609
+#define MNG_FN_PROMOTE_IMGOBJECT     1610
+#define MNG_FN_MAGNIFY_IMGOBJECT     1611
+#define MNG_FN_COLORCORRECT_OBJECT   1612
+
+/* ************************************************************************** */
+
+#define MNG_FN_STORE_G1              1701
+#define MNG_FN_STORE_G2              1702
+#define MNG_FN_STORE_G4              1703
+#define MNG_FN_STORE_G8              1704
+#define MNG_FN_STORE_G16             1705
+#define MNG_FN_STORE_RGB8            1706
+#define MNG_FN_STORE_RGB16           1707
+#define MNG_FN_STORE_IDX1            1708
+#define MNG_FN_STORE_IDX2            1709
+#define MNG_FN_STORE_IDX4            1710
+#define MNG_FN_STORE_IDX8            1711
+#define MNG_FN_STORE_GA8             1712
+#define MNG_FN_STORE_GA16            1713
+#define MNG_FN_STORE_RGBA8           1714
+#define MNG_FN_STORE_RGBA16          1715
+
+#define MNG_FN_RETRIEVE_G8           1751
+#define MNG_FN_RETRIEVE_G16          1752
+#define MNG_FN_RETRIEVE_RGB8         1753
+#define MNG_FN_RETRIEVE_RGB16        1754
+#define MNG_FN_RETRIEVE_IDX8         1755
+#define MNG_FN_RETRIEVE_GA8          1756
+#define MNG_FN_RETRIEVE_GA16         1757
+#define MNG_FN_RETRIEVE_RGBA8        1758
+#define MNG_FN_RETRIEVE_RGBA16       1759
+
+#define MNG_FN_DELTA_G1              1771
+#define MNG_FN_DELTA_G2              1772
+#define MNG_FN_DELTA_G4              1773
+#define MNG_FN_DELTA_G8              1774
+#define MNG_FN_DELTA_G16             1775
+#define MNG_FN_DELTA_RGB8            1776
+#define MNG_FN_DELTA_RGB16           1777
+#define MNG_FN_DELTA_IDX1            1778
+#define MNG_FN_DELTA_IDX2            1779
+#define MNG_FN_DELTA_IDX4            1780
+#define MNG_FN_DELTA_IDX8            1781
+#define MNG_FN_DELTA_GA8             1782
+#define MNG_FN_DELTA_GA16            1783
+#define MNG_FN_DELTA_RGBA8           1784
+#define MNG_FN_DELTA_RGBA16          1785
+
+/* ************************************************************************** */
+
+#define MNG_FN_CREATE_ANI_LOOP       1801
+#define MNG_FN_CREATE_ANI_ENDL       1802
+#define MNG_FN_CREATE_ANI_DEFI       1803
+#define MNG_FN_CREATE_ANI_BASI       1804
+#define MNG_FN_CREATE_ANI_CLON       1805
+#define MNG_FN_CREATE_ANI_PAST       1806
+#define MNG_FN_CREATE_ANI_DISC       1807
+#define MNG_FN_CREATE_ANI_BACK       1808
+#define MNG_FN_CREATE_ANI_FRAM       1809
+#define MNG_FN_CREATE_ANI_MOVE       1810
+#define MNG_FN_CREATE_ANI_CLIP       1811
+#define MNG_FN_CREATE_ANI_SHOW       1812
+#define MNG_FN_CREATE_ANI_TERM       1813
+#define MNG_FN_CREATE_ANI_SAVE       1814
+#define MNG_FN_CREATE_ANI_SEEK       1815
+#define MNG_FN_CREATE_ANI_GAMA       1816
+#define MNG_FN_CREATE_ANI_CHRM       1817
+#define MNG_FN_CREATE_ANI_SRGB       1818
+#define MNG_FN_CREATE_ANI_ICCP       1819
+#define MNG_FN_CREATE_ANI_PLTE       1820
+#define MNG_FN_CREATE_ANI_TRNS       1821
+#define MNG_FN_CREATE_ANI_BKGD       1822
+#define MNG_FN_CREATE_ANI_DHDR       1823
+#define MNG_FN_CREATE_ANI_PROM       1824
+#define MNG_FN_CREATE_ANI_IPNG       1825
+#define MNG_FN_CREATE_ANI_IJNG       1826
+#define MNG_FN_CREATE_ANI_PPLT       1827
+#define MNG_FN_CREATE_ANI_MAGN       1828
+
+#define MNG_FN_CREATE_ANI_IMAGE      1891
+#define MNG_FN_CREATE_EVENT          1892
+
+/* ************************************************************************** */
+
+#define MNG_FN_FREE_ANI_LOOP         1901
+#define MNG_FN_FREE_ANI_ENDL         1902
+#define MNG_FN_FREE_ANI_DEFI         1903
+#define MNG_FN_FREE_ANI_BASI         1904
+#define MNG_FN_FREE_ANI_CLON         1905
+#define MNG_FN_FREE_ANI_PAST         1906
+#define MNG_FN_FREE_ANI_DISC         1907
+#define MNG_FN_FREE_ANI_BACK         1908
+#define MNG_FN_FREE_ANI_FRAM         1909
+#define MNG_FN_FREE_ANI_MOVE         1910
+#define MNG_FN_FREE_ANI_CLIP         1911
+#define MNG_FN_FREE_ANI_SHOW         1912
+#define MNG_FN_FREE_ANI_TERM         1913
+#define MNG_FN_FREE_ANI_SAVE         1914
+#define MNG_FN_FREE_ANI_SEEK         1915
+#define MNG_FN_FREE_ANI_GAMA         1916
+#define MNG_FN_FREE_ANI_CHRM         1917
+#define MNG_FN_FREE_ANI_SRGB         1918
+#define MNG_FN_FREE_ANI_ICCP         1919
+#define MNG_FN_FREE_ANI_PLTE         1920
+#define MNG_FN_FREE_ANI_TRNS         1921
+#define MNG_FN_FREE_ANI_BKGD         1922
+#define MNG_FN_FREE_ANI_DHDR         1923
+#define MNG_FN_FREE_ANI_PROM         1924
+#define MNG_FN_FREE_ANI_IPNG         1925
+#define MNG_FN_FREE_ANI_IJNG         1926
+#define MNG_FN_FREE_ANI_PPLT         1927
+#define MNG_FN_FREE_ANI_MAGN         1928
+
+#define MNG_FN_FREE_ANI_IMAGE        1991
+#define MNG_FN_FREE_EVENT            1992
+
+/* ************************************************************************** */
+
+#define MNG_FN_PROCESS_ANI_LOOP      2001
+#define MNG_FN_PROCESS_ANI_ENDL      2002
+#define MNG_FN_PROCESS_ANI_DEFI      2003
+#define MNG_FN_PROCESS_ANI_BASI      2004
+#define MNG_FN_PROCESS_ANI_CLON      2005
+#define MNG_FN_PROCESS_ANI_PAST      2006
+#define MNG_FN_PROCESS_ANI_DISC      2007
+#define MNG_FN_PROCESS_ANI_BACK      2008
+#define MNG_FN_PROCESS_ANI_FRAM      2009
+#define MNG_FN_PROCESS_ANI_MOVE      2010
+#define MNG_FN_PROCESS_ANI_CLIP      2011
+#define MNG_FN_PROCESS_ANI_SHOW      2012
+#define MNG_FN_PROCESS_ANI_TERM      2013
+#define MNG_FN_PROCESS_ANI_SAVE      2014
+#define MNG_FN_PROCESS_ANI_SEEK      2015
+#define MNG_FN_PROCESS_ANI_GAMA      2016
+#define MNG_FN_PROCESS_ANI_CHRM      2017
+#define MNG_FN_PROCESS_ANI_SRGB      2018
+#define MNG_FN_PROCESS_ANI_ICCP      2019
+#define MNG_FN_PROCESS_ANI_PLTE      2020
+#define MNG_FN_PROCESS_ANI_TRNS      2021
+#define MNG_FN_PROCESS_ANI_BKGD      2022
+#define MNG_FN_PROCESS_ANI_DHDR      2023
+#define MNG_FN_PROCESS_ANI_PROM      2024
+#define MNG_FN_PROCESS_ANI_IPNG      2025
+#define MNG_FN_PROCESS_ANI_IJNG      2026
+#define MNG_FN_PROCESS_ANI_PPLT      2027
+#define MNG_FN_PROCESS_ANI_MAGN      2028
+
+#define MNG_FN_PROCESS_ANI_IMAGE     2091
+#define MNG_FN_PROCESS_EVENT         2092
+
+/* ************************************************************************** */
+
+#define MNG_FN_RESTORE_BACKIMAGE     2101
+#define MNG_FN_RESTORE_BACKCOLOR     2102
+#define MNG_FN_RESTORE_BGCOLOR       2103
+#define MNG_FN_RESTORE_RGB8          2104
+#define MNG_FN_RESTORE_BGR8          2105
+#define MNG_FN_RESTORE_BKGD          2106
+#define MNG_FN_RESTORE_BGRX8         2107
+#define MNG_FN_RESTORE_RGB565        2108
+#define MNG_FN_RESTORE_BGR565        2109
+
+/* ************************************************************************** */
+
+#define MNG_FN_INIT_IHDR             2201
+#define MNG_FN_INIT_PLTE             2202
+#define MNG_FN_INIT_IDAT             2203
+#define MNG_FN_INIT_IEND             2204
+#define MNG_FN_INIT_TRNS             2205
+#define MNG_FN_INIT_GAMA             2206
+#define MNG_FN_INIT_CHRM             2207
+#define MNG_FN_INIT_SRGB             2208
+#define MNG_FN_INIT_ICCP             2209
+#define MNG_FN_INIT_TEXT             2210
+#define MNG_FN_INIT_ZTXT             2211
+#define MNG_FN_INIT_ITXT             2212
+#define MNG_FN_INIT_BKGD             2213
+#define MNG_FN_INIT_PHYS             2214
+#define MNG_FN_INIT_SBIT             2215
+#define MNG_FN_INIT_SPLT             2216
+#define MNG_FN_INIT_HIST             2217
+#define MNG_FN_INIT_TIME             2218
+#define MNG_FN_INIT_MHDR             2219
+#define MNG_FN_INIT_MEND             2220
+#define MNG_FN_INIT_LOOP             2221
+#define MNG_FN_INIT_ENDL             2222
+#define MNG_FN_INIT_DEFI             2223
+#define MNG_FN_INIT_BASI             2224
+#define MNG_FN_INIT_CLON             2225
+#define MNG_FN_INIT_PAST             2226
+#define MNG_FN_INIT_DISC             2227
+#define MNG_FN_INIT_BACK             2228
+#define MNG_FN_INIT_FRAM             2229
+#define MNG_FN_INIT_MOVE             2230
+#define MNG_FN_INIT_CLIP             2231
+#define MNG_FN_INIT_SHOW             2232
+#define MNG_FN_INIT_TERM             2233
+#define MNG_FN_INIT_SAVE             2234
+#define MNG_FN_INIT_SEEK             2235
+#define MNG_FN_INIT_EXPI             2236
+#define MNG_FN_INIT_FPRI             2237
+#define MNG_FN_INIT_NEED             2238
+#define MNG_FN_INIT_PHYG             2239
+#define MNG_FN_INIT_JHDR             2240
+#define MNG_FN_INIT_JDAT             2241
+#define MNG_FN_INIT_JSEP             2242
+#define MNG_FN_INIT_DHDR             2243
+#define MNG_FN_INIT_PROM             2244
+#define MNG_FN_INIT_IPNG             2245
+#define MNG_FN_INIT_PPLT             2246
+#define MNG_FN_INIT_IJNG             2247
+#define MNG_FN_INIT_DROP             2248
+#define MNG_FN_INIT_DBYK             2249
+#define MNG_FN_INIT_ORDR             2250
+#define MNG_FN_INIT_UNKNOWN          2251
+#define MNG_FN_INIT_MAGN             2252
+#define MNG_FN_INIT_JDAA             2253
+#define MNG_FN_INIT_EVNT             2254
+#define MNG_FN_INIT_MPNG             2255
+
+/* ************************************************************************** */
+
+#define MNG_FN_ASSIGN_IHDR           2301
+#define MNG_FN_ASSIGN_PLTE           2302
+#define MNG_FN_ASSIGN_IDAT           2303
+#define MNG_FN_ASSIGN_IEND           2304
+#define MNG_FN_ASSIGN_TRNS           2305
+#define MNG_FN_ASSIGN_GAMA           2306
+#define MNG_FN_ASSIGN_CHRM           2307
+#define MNG_FN_ASSIGN_SRGB           2308
+#define MNG_FN_ASSIGN_ICCP           2309
+#define MNG_FN_ASSIGN_TEXT           2310
+#define MNG_FN_ASSIGN_ZTXT           2311
+#define MNG_FN_ASSIGN_ITXT           2312
+#define MNG_FN_ASSIGN_BKGD           2313
+#define MNG_FN_ASSIGN_PHYS           2314
+#define MNG_FN_ASSIGN_SBIT           2315
+#define MNG_FN_ASSIGN_SPLT           2316
+#define MNG_FN_ASSIGN_HIST           2317
+#define MNG_FN_ASSIGN_TIME           2318
+#define MNG_FN_ASSIGN_MHDR           2319
+#define MNG_FN_ASSIGN_MEND           2320
+#define MNG_FN_ASSIGN_LOOP           2321
+#define MNG_FN_ASSIGN_ENDL           2322
+#define MNG_FN_ASSIGN_DEFI           2323
+#define MNG_FN_ASSIGN_BASI           2324
+#define MNG_FN_ASSIGN_CLON           2325
+#define MNG_FN_ASSIGN_PAST           2326
+#define MNG_FN_ASSIGN_DISC           2327
+#define MNG_FN_ASSIGN_BACK           2328
+#define MNG_FN_ASSIGN_FRAM           2329
+#define MNG_FN_ASSIGN_MOVE           2330
+#define MNG_FN_ASSIGN_CLIP           2331
+#define MNG_FN_ASSIGN_SHOW           2332
+#define MNG_FN_ASSIGN_TERM           2333
+#define MNG_FN_ASSIGN_SAVE           2334
+#define MNG_FN_ASSIGN_SEEK           2335
+#define MNG_FN_ASSIGN_EXPI           2336
+#define MNG_FN_ASSIGN_FPRI           2337
+#define MNG_FN_ASSIGN_NEED           2338
+#define MNG_FN_ASSIGN_PHYG           2339
+#define MNG_FN_ASSIGN_JHDR           2340
+#define MNG_FN_ASSIGN_JDAT           2341
+#define MNG_FN_ASSIGN_JSEP           2342
+#define MNG_FN_ASSIGN_DHDR           2343
+#define MNG_FN_ASSIGN_PROM           2344
+#define MNG_FN_ASSIGN_IPNG           2345
+#define MNG_FN_ASSIGN_PPLT           2346
+#define MNG_FN_ASSIGN_IJNG           2347
+#define MNG_FN_ASSIGN_DROP           2348
+#define MNG_FN_ASSIGN_DBYK           2349
+#define MNG_FN_ASSIGN_ORDR           2350
+#define MNG_FN_ASSIGN_UNKNOWN        2351
+#define MNG_FN_ASSIGN_MAGN           2352
+#define MNG_FN_ASSIGN_JDAA           2353
+#define MNG_FN_ASSIGN_EVNT           2354
+#define MNG_FN_ASSIGN_MPNG           2355
+
+/* ************************************************************************** */
+
+#define MNG_FN_FREE_IHDR             2401
+#define MNG_FN_FREE_PLTE             2402
+#define MNG_FN_FREE_IDAT             2403
+#define MNG_FN_FREE_IEND             2404
+#define MNG_FN_FREE_TRNS             2405
+#define MNG_FN_FREE_GAMA             2406
+#define MNG_FN_FREE_CHRM             2407
+#define MNG_FN_FREE_SRGB             2408
+#define MNG_FN_FREE_ICCP             2409
+#define MNG_FN_FREE_TEXT             2410
+#define MNG_FN_FREE_ZTXT             2411
+#define MNG_FN_FREE_ITXT             2412
+#define MNG_FN_FREE_BKGD             2413
+#define MNG_FN_FREE_PHYS             2414
+#define MNG_FN_FREE_SBIT             2415
+#define MNG_FN_FREE_SPLT             2416
+#define MNG_FN_FREE_HIST             2417
+#define MNG_FN_FREE_TIME             2418
+#define MNG_FN_FREE_MHDR             2419
+#define MNG_FN_FREE_MEND             2420
+#define MNG_FN_FREE_LOOP             2421
+#define MNG_FN_FREE_ENDL             2422
+#define MNG_FN_FREE_DEFI             2423
+#define MNG_FN_FREE_BASI             2424
+#define MNG_FN_FREE_CLON             2425
+#define MNG_FN_FREE_PAST             2426
+#define MNG_FN_FREE_DISC             2427
+#define MNG_FN_FREE_BACK             2428
+#define MNG_FN_FREE_FRAM             2429
+#define MNG_FN_FREE_MOVE             2430
+#define MNG_FN_FREE_CLIP             2431
+#define MNG_FN_FREE_SHOW             2432
+#define MNG_FN_FREE_TERM             2433
+#define MNG_FN_FREE_SAVE             2434
+#define MNG_FN_FREE_SEEK             2435
+#define MNG_FN_FREE_EXPI             2436
+#define MNG_FN_FREE_FPRI             2437
+#define MNG_FN_FREE_NEED             2438
+#define MNG_FN_FREE_PHYG             2439
+#define MNG_FN_FREE_JHDR             2440
+#define MNG_FN_FREE_JDAT             2441
+#define MNG_FN_FREE_JSEP             2442
+#define MNG_FN_FREE_DHDR             2443
+#define MNG_FN_FREE_PROM             2444
+#define MNG_FN_FREE_IPNG             2445
+#define MNG_FN_FREE_PPLT             2446
+#define MNG_FN_FREE_IJNG             2447
+#define MNG_FN_FREE_DROP             2448
+#define MNG_FN_FREE_DBYK             2449
+#define MNG_FN_FREE_ORDR             2450
+#define MNG_FN_FREE_UNKNOWN          2451
+#define MNG_FN_FREE_MAGN             2452
+#define MNG_FN_FREE_JDAA             2453
+#define MNG_FN_FREE_EVNT             2454
+#define MNG_FN_FREE_MPNG             2455
+
+/* ************************************************************************** */
+
+#define MNG_FN_READ_IHDR             2601
+#define MNG_FN_READ_PLTE             2602
+#define MNG_FN_READ_IDAT             2603
+#define MNG_FN_READ_IEND             2604
+#define MNG_FN_READ_TRNS             2605
+#define MNG_FN_READ_GAMA             2606
+#define MNG_FN_READ_CHRM             2607
+#define MNG_FN_READ_SRGB             2608
+#define MNG_FN_READ_ICCP             2609
+#define MNG_FN_READ_TEXT             2610
+#define MNG_FN_READ_ZTXT             2611
+#define MNG_FN_READ_ITXT             2612
+#define MNG_FN_READ_BKGD             2613
+#define MNG_FN_READ_PHYS             2614
+#define MNG_FN_READ_SBIT             2615
+#define MNG_FN_READ_SPLT             2616
+#define MNG_FN_READ_HIST             2617
+#define MNG_FN_READ_TIME             2618
+#define MNG_FN_READ_MHDR             2619
+#define MNG_FN_READ_MEND             2620
+#define MNG_FN_READ_LOOP             2621
+#define MNG_FN_READ_ENDL             2622
+#define MNG_FN_READ_DEFI             2623
+#define MNG_FN_READ_BASI             2624
+#define MNG_FN_READ_CLON             2625
+#define MNG_FN_READ_PAST             2626
+#define MNG_FN_READ_DISC             2627
+#define MNG_FN_READ_BACK             2628
+#define MNG_FN_READ_FRAM             2629
+#define MNG_FN_READ_MOVE             2630
+#define MNG_FN_READ_CLIP             2631
+#define MNG_FN_READ_SHOW             2632
+#define MNG_FN_READ_TERM             2633
+#define MNG_FN_READ_SAVE             2634
+#define MNG_FN_READ_SEEK             2635
+#define MNG_FN_READ_EXPI             2636
+#define MNG_FN_READ_FPRI             2637
+#define MNG_FN_READ_NEED             2638
+#define MNG_FN_READ_PHYG             2639
+#define MNG_FN_READ_JHDR             2640
+#define MNG_FN_READ_JDAT             2641
+#define MNG_FN_READ_JSEP             2642
+#define MNG_FN_READ_DHDR             2643
+#define MNG_FN_READ_PROM             2644
+#define MNG_FN_READ_IPNG             2645
+#define MNG_FN_READ_PPLT             2646
+#define MNG_FN_READ_IJNG             2647
+#define MNG_FN_READ_DROP             2648
+#define MNG_FN_READ_DBYK             2649
+#define MNG_FN_READ_ORDR             2650
+#define MNG_FN_READ_UNKNOWN          2651
+#define MNG_FN_READ_MAGN             2652
+#define MNG_FN_READ_JDAA             2653
+#define MNG_FN_READ_EVNT             2654
+#define MNG_FN_READ_MPNG             2655
+
+/* ************************************************************************** */
+
+#define MNG_FN_WRITE_IHDR            2801
+#define MNG_FN_WRITE_PLTE            2802
+#define MNG_FN_WRITE_IDAT            2803
+#define MNG_FN_WRITE_IEND            2804
+#define MNG_FN_WRITE_TRNS            2805
+#define MNG_FN_WRITE_GAMA            2806
+#define MNG_FN_WRITE_CHRM            2807
+#define MNG_FN_WRITE_SRGB            2808
+#define MNG_FN_WRITE_ICCP            2809
+#define MNG_FN_WRITE_TEXT            2810
+#define MNG_FN_WRITE_ZTXT            2811
+#define MNG_FN_WRITE_ITXT            2812
+#define MNG_FN_WRITE_BKGD            2813
+#define MNG_FN_WRITE_PHYS            2814
+#define MNG_FN_WRITE_SBIT            2815
+#define MNG_FN_WRITE_SPLT            2816
+#define MNG_FN_WRITE_HIST            2817
+#define MNG_FN_WRITE_TIME            2818
+#define MNG_FN_WRITE_MHDR            2819
+#define MNG_FN_WRITE_MEND            2820
+#define MNG_FN_WRITE_LOOP            2821
+#define MNG_FN_WRITE_ENDL            2822
+#define MNG_FN_WRITE_DEFI            2823
+#define MNG_FN_WRITE_BASI            2824
+#define MNG_FN_WRITE_CLON            2825
+#define MNG_FN_WRITE_PAST            2826
+#define MNG_FN_WRITE_DISC            2827
+#define MNG_FN_WRITE_BACK            2828
+#define MNG_FN_WRITE_FRAM            2829
+#define MNG_FN_WRITE_MOVE            2830
+#define MNG_FN_WRITE_CLIP            2831
+#define MNG_FN_WRITE_SHOW            2832
+#define MNG_FN_WRITE_TERM            2833
+#define MNG_FN_WRITE_SAVE            2834
+#define MNG_FN_WRITE_SEEK            2835
+#define MNG_FN_WRITE_EXPI            2836
+#define MNG_FN_WRITE_FPRI            2837
+#define MNG_FN_WRITE_NEED            2838
+#define MNG_FN_WRITE_PHYG            2839
+#define MNG_FN_WRITE_JHDR            2840
+#define MNG_FN_WRITE_JDAT            2841
+#define MNG_FN_WRITE_JSEP            2842
+#define MNG_FN_WRITE_DHDR            2843
+#define MNG_FN_WRITE_PROM            2844
+#define MNG_FN_WRITE_IPNG            2845
+#define MNG_FN_WRITE_PPLT            2846
+#define MNG_FN_WRITE_IJNG            2847
+#define MNG_FN_WRITE_DROP            2848
+#define MNG_FN_WRITE_DBYK            2849
+#define MNG_FN_WRITE_ORDR            2850
+#define MNG_FN_WRITE_UNKNOWN         2851
+#define MNG_FN_WRITE_MAGN            2852
+#define MNG_FN_WRITE_JDAA            2853
+#define MNG_FN_WRITE_EVNT            2854
+#define MNG_FN_WRITE_MPNG            2855
+
+/* ************************************************************************** */
+
+#define MNG_FN_ZLIB_INITIALIZE       3001
+#define MNG_FN_ZLIB_CLEANUP          3002
+#define MNG_FN_ZLIB_INFLATEINIT      3003
+#define MNG_FN_ZLIB_INFLATEROWS      3004
+#define MNG_FN_ZLIB_INFLATEDATA      3005
+#define MNG_FN_ZLIB_INFLATEFREE      3006
+#define MNG_FN_ZLIB_DEFLATEINIT      3007
+#define MNG_FN_ZLIB_DEFLATEROWS      3008
+#define MNG_FN_ZLIB_DEFLATEDATA      3009
+#define MNG_FN_ZLIB_DEFLATEFREE      3010
+
+/* ************************************************************************** */
+
+#define MNG_FN_PROCESS_DISPLAY_IHDR  3201
+#define MNG_FN_PROCESS_DISPLAY_PLTE  3202
+#define MNG_FN_PROCESS_DISPLAY_IDAT  3203
+#define MNG_FN_PROCESS_DISPLAY_IEND  3204
+#define MNG_FN_PROCESS_DISPLAY_TRNS  3205
+#define MNG_FN_PROCESS_DISPLAY_GAMA  3206
+#define MNG_FN_PROCESS_DISPLAY_CHRM  3207
+#define MNG_FN_PROCESS_DISPLAY_SRGB  3208
+#define MNG_FN_PROCESS_DISPLAY_ICCP  3209
+#define MNG_FN_PROCESS_DISPLAY_BKGD  3210
+#define MNG_FN_PROCESS_DISPLAY_PHYS  3211
+#define MNG_FN_PROCESS_DISPLAY_SBIT  3212
+#define MNG_FN_PROCESS_DISPLAY_SPLT  3213
+#define MNG_FN_PROCESS_DISPLAY_HIST  3214
+#define MNG_FN_PROCESS_DISPLAY_MHDR  3215
+#define MNG_FN_PROCESS_DISPLAY_MEND  3216
+#define MNG_FN_PROCESS_DISPLAY_LOOP  3217
+#define MNG_FN_PROCESS_DISPLAY_ENDL  3218
+#define MNG_FN_PROCESS_DISPLAY_DEFI  3219
+#define MNG_FN_PROCESS_DISPLAY_BASI  3220
+#define MNG_FN_PROCESS_DISPLAY_CLON  3221
+#define MNG_FN_PROCESS_DISPLAY_PAST  3222
+#define MNG_FN_PROCESS_DISPLAY_DISC  3223
+#define MNG_FN_PROCESS_DISPLAY_BACK  3224
+#define MNG_FN_PROCESS_DISPLAY_FRAM  3225
+#define MNG_FN_PROCESS_DISPLAY_MOVE  3226
+#define MNG_FN_PROCESS_DISPLAY_CLIP  3227
+#define MNG_FN_PROCESS_DISPLAY_SHOW  3228
+#define MNG_FN_PROCESS_DISPLAY_TERM  3229
+#define MNG_FN_PROCESS_DISPLAY_SAVE  3230
+#define MNG_FN_PROCESS_DISPLAY_SEEK  3231
+#define MNG_FN_PROCESS_DISPLAY_EXPI  3232
+#define MNG_FN_PROCESS_DISPLAY_FPRI  3233
+#define MNG_FN_PROCESS_DISPLAY_NEED  3234
+#define MNG_FN_PROCESS_DISPLAY_PHYG  3235
+#define MNG_FN_PROCESS_DISPLAY_JHDR  3236
+#define MNG_FN_PROCESS_DISPLAY_JDAT  3237
+#define MNG_FN_PROCESS_DISPLAY_JSEP  3238
+#define MNG_FN_PROCESS_DISPLAY_DHDR  3239
+#define MNG_FN_PROCESS_DISPLAY_PROM  3240
+#define MNG_FN_PROCESS_DISPLAY_IPNG  3241
+#define MNG_FN_PROCESS_DISPLAY_PPLT  3242
+#define MNG_FN_PROCESS_DISPLAY_IJNG  3243
+#define MNG_FN_PROCESS_DISPLAY_DROP  3244
+#define MNG_FN_PROCESS_DISPLAY_DBYK  3245
+#define MNG_FN_PROCESS_DISPLAY_ORDR  3246
+#define MNG_FN_PROCESS_DISPLAY_MAGN  3247
+#define MNG_FN_PROCESS_DISPLAY_JDAA  3248
+
+/* ************************************************************************** */
+
+#define MNG_FN_JPEG_INITIALIZE       3401
+#define MNG_FN_JPEG_CLEANUP          3402
+#define MNG_FN_JPEG_DECOMPRESSINIT   3403
+#define MNG_FN_JPEG_DECOMPRESSDATA   3404
+#define MNG_FN_JPEG_DECOMPRESSFREE   3405
+
+#define MNG_FN_STORE_JPEG_G8         3501
+#define MNG_FN_STORE_JPEG_RGB8       3502
+#define MNG_FN_STORE_JPEG_G12        3503
+#define MNG_FN_STORE_JPEG_RGB12      3504
+#define MNG_FN_STORE_JPEG_GA8        3505
+#define MNG_FN_STORE_JPEG_RGBA8      3506
+#define MNG_FN_STORE_JPEG_GA12       3507
+#define MNG_FN_STORE_JPEG_RGBA12     3508
+#define MNG_FN_STORE_JPEG_G8_ALPHA   3509
+#define MNG_FN_STORE_JPEG_RGB8_ALPHA 3510
+
+#define MNG_FN_INIT_JPEG_A1_NI       3511
+#define MNG_FN_INIT_JPEG_A2_NI       3512
+#define MNG_FN_INIT_JPEG_A4_NI       3513
+#define MNG_FN_INIT_JPEG_A8_NI       3514
+#define MNG_FN_INIT_JPEG_A16_NI      3515
+
+#define MNG_FN_STORE_JPEG_G8_A1      3521
+#define MNG_FN_STORE_JPEG_G8_A2      3522
+#define MNG_FN_STORE_JPEG_G8_A4      3523
+#define MNG_FN_STORE_JPEG_G8_A8      3524
+#define MNG_FN_STORE_JPEG_G8_A16     3525
+
+#define MNG_FN_STORE_JPEG_RGB8_A1    3531
+#define MNG_FN_STORE_JPEG_RGB8_A2    3532
+#define MNG_FN_STORE_JPEG_RGB8_A4    3533
+#define MNG_FN_STORE_JPEG_RGB8_A8    3534
+#define MNG_FN_STORE_JPEG_RGB8_A16   3535
+
+#define MNG_FN_STORE_JPEG_G12_A1     3541
+#define MNG_FN_STORE_JPEG_G12_A2     3542
+#define MNG_FN_STORE_JPEG_G12_A4     3543
+#define MNG_FN_STORE_JPEG_G12_A8     3544
+#define MNG_FN_STORE_JPEG_G12_A16    3545
+
+#define MNG_FN_STORE_JPEG_RGB12_A1   3551
+#define MNG_FN_STORE_JPEG_RGB12_A2   3552
+#define MNG_FN_STORE_JPEG_RGB12_A4   3553
+#define MNG_FN_STORE_JPEG_RGB12_A8   3554
+#define MNG_FN_STORE_JPEG_RGB12_A16  3555
+
+#define MNG_FN_NEXT_JPEG_ALPHAROW    3591
+#define MNG_FN_NEXT_JPEG_ROW         3592
+#define MNG_FN_DISPLAY_JPEG_ROWS     3593
+
+/* ************************************************************************** */
+
+#define MNG_FN_MAGNIFY_G8_X1         3701
+#define MNG_FN_MAGNIFY_G8_X2         3702
+#define MNG_FN_MAGNIFY_RGB8_X1       3703
+#define MNG_FN_MAGNIFY_RGB8_X2       3704
+#define MNG_FN_MAGNIFY_GA8_X1        3705
+#define MNG_FN_MAGNIFY_GA8_X2        3706
+#define MNG_FN_MAGNIFY_GA8_X3        3707
+#define MNG_FN_MAGNIFY_GA8_X4        3708
+#define MNG_FN_MAGNIFY_RGBA8_X1      3709
+#define MNG_FN_MAGNIFY_RGBA8_X2      3710
+#define MNG_FN_MAGNIFY_RGBA8_X3      3711
+#define MNG_FN_MAGNIFY_RGBA8_X4      3712
+#define MNG_FN_MAGNIFY_G8_X3         3713
+#define MNG_FN_MAGNIFY_RGB8_X3       3714
+#define MNG_FN_MAGNIFY_GA8_X5        3715
+#define MNG_FN_MAGNIFY_RGBA8_X5      3716
+
+#define MNG_FN_MAGNIFY_G16_X1        3725
+#define MNG_FN_MAGNIFY_G16_X2        3726
+#define MNG_FN_MAGNIFY_RGB16_X1      3727
+#define MNG_FN_MAGNIFY_RGB16_X2      3728
+#define MNG_FN_MAGNIFY_GA16_X1       3729
+#define MNG_FN_MAGNIFY_GA16_X2       3730
+#define MNG_FN_MAGNIFY_GA16_X3       3731
+#define MNG_FN_MAGNIFY_GA16_X4       3732
+#define MNG_FN_MAGNIFY_RGBA16_X1     3733
+#define MNG_FN_MAGNIFY_RGBA16_X2     3734
+#define MNG_FN_MAGNIFY_RGBA16_X3     3735
+#define MNG_FN_MAGNIFY_RGBA16_X4     3736
+#define MNG_FN_MAGNIFY_G16_X3        3737
+#define MNG_FN_MAGNIFY_RGB16_X3      3738
+#define MNG_FN_MAGNIFY_GA16_X5       3739
+#define MNG_FN_MAGNIFY_RGBA16_X5     3740
+
+#define MNG_FN_MAGNIFY_G8_Y1         3751
+#define MNG_FN_MAGNIFY_G8_Y2         3752
+#define MNG_FN_MAGNIFY_RGB8_Y1       3753
+#define MNG_FN_MAGNIFY_RGB8_Y2       3754
+#define MNG_FN_MAGNIFY_GA8_Y1        3755
+#define MNG_FN_MAGNIFY_GA8_Y2        3756
+#define MNG_FN_MAGNIFY_GA8_Y3        3757
+#define MNG_FN_MAGNIFY_GA8_Y4        3758
+#define MNG_FN_MAGNIFY_RGBA8_Y1      3759
+#define MNG_FN_MAGNIFY_RGBA8_Y2      3760
+#define MNG_FN_MAGNIFY_RGBA8_Y3      3761
+#define MNG_FN_MAGNIFY_RGBA8_Y4      3762
+#define MNG_FN_MAGNIFY_G8_Y3         3763
+#define MNG_FN_MAGNIFY_RGB8_Y3       3764
+#define MNG_FN_MAGNIFY_GA8_Y5        3765
+#define MNG_FN_MAGNIFY_RGBA8_Y5      3766
+
+#define MNG_FN_MAGNIFY_G16_Y1        3775
+#define MNG_FN_MAGNIFY_G16_Y2        3776
+#define MNG_FN_MAGNIFY_RGB16_Y1      3777
+#define MNG_FN_MAGNIFY_RGB16_Y2      3778
+#define MNG_FN_MAGNIFY_GA16_Y1       3779
+#define MNG_FN_MAGNIFY_GA16_Y2       3780
+#define MNG_FN_MAGNIFY_GA16_Y3       3781
+#define MNG_FN_MAGNIFY_GA16_Y4       3782
+#define MNG_FN_MAGNIFY_RGBA16_Y1     3783
+#define MNG_FN_MAGNIFY_RGBA16_Y2     3784
+#define MNG_FN_MAGNIFY_RGBA16_Y3     3785
+#define MNG_FN_MAGNIFY_RGBA16_Y4     3786
+#define MNG_FN_MAGNIFY_G16_Y3        3787
+#define MNG_FN_MAGNIFY_RGB16_Y3      3788
+#define MNG_FN_MAGNIFY_GA16_Y5       3789
+#define MNG_FN_MAGNIFY_RGBA16_Y5     3790
+
+/* ************************************************************************** */
+
+#define MNG_FN_DELTA_G1_G1           3801
+#define MNG_FN_DELTA_G2_G2           3802
+#define MNG_FN_DELTA_G4_G4           3803
+#define MNG_FN_DELTA_G8_G8           3804
+#define MNG_FN_DELTA_G16_G16         3805
+#define MNG_FN_DELTA_RGB8_RGB8       3806
+#define MNG_FN_DELTA_RGB16_RGB16     3807
+#define MNG_FN_DELTA_GA8_GA8         3808
+#define MNG_FN_DELTA_GA8_G8          3809
+#define MNG_FN_DELTA_GA8_A8          3810
+#define MNG_FN_DELTA_GA16_GA16       3811
+#define MNG_FN_DELTA_GA16_G16        3812
+#define MNG_FN_DELTA_GA16_A16        3813
+#define MNG_FN_DELTA_RGBA8_RGBA8     3814
+#define MNG_FN_DELTA_RGBA8_RGB8      3815
+#define MNG_FN_DELTA_RGBA8_A8        3816
+#define MNG_FN_DELTA_RGBA16_RGBA16   3817
+#define MNG_FN_DELTA_RGBA16_RGB16    3818
+#define MNG_FN_DELTA_RGBA16_A16      3819
+
+#define MNG_FN_PROMOTE_G8_G8         3901
+#define MNG_FN_PROMOTE_G8_G16        3902
+#define MNG_FN_PROMOTE_G16_G16       3903
+#define MNG_FN_PROMOTE_G8_GA8        3904
+#define MNG_FN_PROMOTE_G8_GA16       3905
+#define MNG_FN_PROMOTE_G16_GA16      3906
+#define MNG_FN_PROMOTE_G8_RGB8       3907
+#define MNG_FN_PROMOTE_G8_RGB16      3908
+#define MNG_FN_PROMOTE_G16_RGB16     3909
+#define MNG_FN_PROMOTE_G8_RGBA8      3910
+#define MNG_FN_PROMOTE_G8_RGBA16     3911
+#define MNG_FN_PROMOTE_G16_RGBA16    3912
+#define MNG_FN_PROMOTE_GA8_GA16      3913
+#define MNG_FN_PROMOTE_GA8_RGBA8     3914
+#define MNG_FN_PROMOTE_GA8_RGBA16    3915
+#define MNG_FN_PROMOTE_GA16_RGBA16   3916
+#define MNG_FN_PROMOTE_RGB8_RGB16    3917
+#define MNG_FN_PROMOTE_RGB8_RGBA8    3918
+#define MNG_FN_PROMOTE_RGB8_RGBA16   3919
+#define MNG_FN_PROMOTE_RGB16_RGBA16  3920
+#define MNG_FN_PROMOTE_RGBA8_RGBA16  3921
+#define MNG_FN_PROMOTE_IDX8_RGB8     3922
+#define MNG_FN_PROMOTE_IDX8_RGB16    3923
+#define MNG_FN_PROMOTE_IDX8_RGBA8    3924
+#define MNG_FN_PROMOTE_IDX8_RGBA16   3925
+
+#define MNG_FN_SCALE_G1_G2           4001
+#define MNG_FN_SCALE_G1_G4           4002
+#define MNG_FN_SCALE_G1_G8           4003
+#define MNG_FN_SCALE_G1_G16          4004
+#define MNG_FN_SCALE_G2_G4           4005
+#define MNG_FN_SCALE_G2_G8           4006
+#define MNG_FN_SCALE_G2_G16          4007
+#define MNG_FN_SCALE_G4_G8           4008
+#define MNG_FN_SCALE_G4_G16          4009
+#define MNG_FN_SCALE_G8_G16          4010
+#define MNG_FN_SCALE_GA8_GA16        4011
+#define MNG_FN_SCALE_RGB8_RGB16      4012
+#define MNG_FN_SCALE_RGBA8_RGBA16    4013
+
+#define MNG_FN_SCALE_G2_G1           4021
+#define MNG_FN_SCALE_G4_G1           4022
+#define MNG_FN_SCALE_G8_G1           4023
+#define MNG_FN_SCALE_G16_G1          4024
+#define MNG_FN_SCALE_G4_G2           4025
+#define MNG_FN_SCALE_G8_G2           4026
+#define MNG_FN_SCALE_G16_G2          4027
+#define MNG_FN_SCALE_G8_G4           4028
+#define MNG_FN_SCALE_G16_G4          4029
+#define MNG_FN_SCALE_G16_G8          4030
+#define MNG_FN_SCALE_GA16_GA8        4031
+#define MNG_FN_SCALE_RGB16_RGB8      4032
+#define MNG_FN_SCALE_RGBA16_RGBA8    4033
+
+#define MNG_FN_COMPOSEOVER_RGBA8     4501
+#define MNG_FN_COMPOSEOVER_RGBA16    4502
+#define MNG_FN_COMPOSEUNDER_RGBA8    4503
+#define MNG_FN_COMPOSEUNDER_RGBA16   4504
+
+#define MNG_FN_FLIP_RGBA8            4521
+#define MNG_FN_FLIP_RGBA16           4522
+#define MNG_FN_TILE_RGBA8            4523
+#define MNG_FN_TILE_RGBA16           4524
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Trace string-table entry                                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+typedef struct {
+                 mng_uint32 iFunction;
+                 mng_pchar  zTracetext;
+               } mng_trace_entry;
+typedef mng_trace_entry const * mng_trace_entryp;
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_TRACE_PROCS */
+
+/* ************************************************************************** */
+
+#endif /* _libmng_trace_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_types.h b/files/Source/LibMNG/libmng_types.h
new file mode 100644
index 0000000..95ee8f8
--- /dev/null
+++ b/files/Source/LibMNG/libmng_types.h
@@ -0,0 +1,574 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_types.h            copyright (c) 2000-2007 G.Juyn   * */
+/* * version   : 1.0.10                                                     * */
+/* *                                                                        * */
+/* * purpose   : type specifications                                        * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Specification of the types used by the library             * */
+/* *             Creates platform-independant structure                     * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/06/2000 - G.Juyn                                * */
+/* *             - added iteratechunk callback definition                   * */
+/* *             0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - improved definitions for DLL support                     * */
+/* *             - added 8-bit palette definition                           * */
+/* *             - added general array definitions                          * */
+/* *             - added MNG_NULL definition                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - changed most callback prototypes to allow the app        * */
+/* *               to report errors during callback processing              * */
+/* *             0.5.1 - 05/16/2000 - G.Juyn                                * */
+/* *             - moved standard header includes into this file            * */
+/* *               (stdlib/mem for mem-mngmt & math for fp gamma-calc)      * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/18/2000 - G.Juyn                                * */
+/* *             - B003 - fixed problem with <mem.h> being proprietary      * */
+/* *               to Borland platform                                      * */
+/* *             - added helper definitions for JNG (IJG-based)             * */
+/* *             - fixed support for IJGSRC6B                               * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - added default IJG compression parameters and such        * */
+/* *             0.5.2 - 05/31/2000 - G.Juyn                                * */
+/* *             - fixed inclusion for memcpy (contributed by Tim Rowley)   * */
+/* *             - added mng_int32p (contributed by Tim Rowley)             * */
+/* *             0.5.2 - 06/02/2000 - G.Juyn                                * */
+/* *             - removed SWAP_ENDIAN reference (contributed by Tim Rowley)* */
+/* *             - added getalphaline callback for RGB8_A8 canvasstyle      * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/21/2000 - G.Juyn                                * */
+/* *             - added speedtype to facilitate testing                    * */
+/* *             0.5.3 - 06/27/2000 - G.Juyn                                * */
+/* *             - added typedef for mng_size_t                             * */
+/* *             - changed size parameter for memory callbacks to           * */
+/* *               mng_size_t                                               * */
+/* *             0.5.3 - 06/28/2000 - G.Juyn                                * */
+/* *             - changed definition of 32-bit ints (64-bit platforms)     * */
+/* *             - changed definition of mng_handle (64-bit platforms)      * */
+/* *             0.5.3 - 06/29/2000 - G.Juyn                                * */
+/* *             - changed definition of mng_handle (again)                 * */
+/* *             - swapped refresh parameters                               * */
+/* *             - added inclusion of stdlib.h for abs()                    * */
+/* *                                                                        * */
+/* *             0.9.0 - 06/30/2000 - G.Juyn                                * */
+/* *             - changed refresh parameters to 'x,y,width,height'         * */
+/* *             0.9.1 - 07/10/2000 - G.Juyn                                * */
+/* *             - added suspendbuffer constants                            * */
+/* *             0.9.1 - 07/15/2000 - G.Juyn                                * */
+/* *             - added callbacks for SAVE/SEEK processing                 * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/07/2000 - G.Juyn                                * */
+/* *             - B111300 - fixup for improved portability                 * */
+/* *             0.9.3 - 08/12/2000 - G.Juyn                                * */
+/* *             - added workaround for faulty PhotoShop iCCP chunk         * */
+/* *             0.9.3 - 09/11/2000 - G.Juyn                                * */
+/* *             - added export of zlib functions from windows dll          * */
+/* *             - fixed inclusion parameters once again to make those      * */
+/* *               external libs work together                              * */
+/* *             - re-fixed fixed inclusion parameters                      * */
+/* *               (these freeking libraries make me mad)                   * */
+/* *             0.9.3 - 10/11/2000 - G.Juyn                                * */
+/* *             - added support for nEED                                   * */
+/* *             0.9.3 - 10/17/2000 - G.Juyn                                * */
+/* *             - added callback to process non-critical unknown chunks    * */
+/* *                                                                        * */
+/* *             0.9.4 - 11/20/2000 - R.Giles                               * */
+/* *             - fixed inclusion of lcms header for non-windows platforms * */
+/* *             0.9.4 - 12/12/2000 - G.Juyn                                * */
+/* *             - changed callback convention for MSVC (Thanks Chad)       * */
+/* *             0.9.4 - 12/16/2000 - G.Juyn                                * */
+/* *             - fixed mixup of data- & function-pointers (thanks Dimitri)* */
+/* *                                                                        * */
+/* *             1.0.1 - 02/08/2001 - G.Juyn                                * */
+/* *             - added MEND processing callback                           * */
+/* *                                                                        * */
+/* *             1.0.2 - 06/23/2001 - G.Juyn                                * */
+/* *             - added processterm callback                               * */
+/* *                                                                        * */
+/* *             1.0.3 - 08/06/2001 - G.Juyn                                * */
+/* *             - changed inclusion of lcms.h for Linux platforms          * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *                                                                        * */
+/* *             1.0.6 - 04/11/2003 - G.Juyn                                * */
+/* *             - B719420 - fixed several MNG_APP_CMS problems             * */
+/* *             1.0.6 - 06/15/2003 - R.Giles                               * */
+/* *             - lcms.h inclusion is generally no longer prefixed         * */
+/* *             1.0.6 - 07/07/2003 - G. R-P.                               * */
+/* *             - added png_imgtypes enumeration                           * */
+/* *                                                                        * */
+/* *             1.0.7 - 03/10/2004 - G.R-P                                 * */
+/* *             - added conditionals around openstream/closestream         * */
+/* *                                                                        * */
+/* *             1.0.8 - 04/11/2004 - G.Juyn                                * */
+/* *             - added data-push mechanisms for specialized decoders      * */
+/* *             1.0.8 - 08/01/2004 - G.Juyn                                * */
+/* *             - added support for 3+byte pixelsize for JPEG's            * */
+/* *                                                                        * */
+/* *             1.0.9 - 12/05/2004 - G.Juyn                                * */
+/* *             - inclusion of zlib/lcms/ijgsrc6b with <> instead of ""    * */
+/* *             1.0.9 - 12/06/2004 - G.Juyn                                * */
+/* *             - added conditional MNG_OPTIMIZE_CHUNKREADER               * */
+/* *                                                                        * */
+/* *             1.0.10 - 04/08/2007 - G.Juyn                               * */
+/* *             - added support for mPNG proposal                          * */
+/* *             1.0.10 - 04/12/2007 - G.Juyn                               * */
+/* *             - added support for ANG proposal                           * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifndef _libmng_types_h_
+#define _libmng_types_h_
+
+/* ************************************************************************** */
+
+#ifdef __BORLANDC__
+#pragma option -AT                     /* turn off strict ANSI-C for the moment */
+#endif
+
+#ifndef WIN32
+#if defined(_WIN32) || defined(__WIN32__) || defined(_Windows) || defined(_WINDOWS)
+#define WIN32                          /* gather them into a single define */
+#endif
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Here's where the external & standard libs are embedded                 * */
+/* *                                                                        * */
+/* * (it can be a bit of a pain in the lower-back to get them to work       * */
+/* *  together)                                                             * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#ifdef WIN32                           /* only include needed stuff */
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#endif
+
+#ifdef MNG_USE_DLL
+#ifdef MNG_SKIP_ZLIB
+#undef MNG_INCLUDE_ZLIB
+#endif
+#ifdef MNG_SKIP_LCMS
+#undef MNG_INCLUDE_LCMS
+#endif
+#ifdef MNG_SKIP_IJG6B
+#undef MNG_INCLUDE_IJG6B
+#endif
+#endif
+
+#ifdef MNG_INCLUDE_ZLIB                /* zlib by Mark Adler & Jean-loup Gailly */
+#include <zlib.h>
+#endif
+
+#ifdef MNG_INCLUDE_LCMS                /* little cms by Marti Maria Saguer */
+#ifndef ZLIB_DLL
+#undef FAR
+#endif
+#include <lcms.h>
+#endif /* MNG_INCLUDE_LCMS */
+
+#ifdef MNG_INCLUDE_IJG6B               /* IJG's jpgsrc6b */
+#include <stdio.h>
+#ifdef MNG_USE_SETJMP
+#include <setjmp.h>                    /* needed for error-recovery (blergh) */
+#else
+#ifdef WIN32
+#define USE_WINDOWS_MESSAGEBOX         /* display a messagebox under Windoze */
+#endif
+#endif /* MNG_USE_SETJMP */
+#ifdef FAR
+#undef FAR                             /* possibly defined by zlib or lcms */
+#endif
+#define JPEG_INTERNAL_OPTIONS          /* for RGB_PIXELSIZE */
+#include "third_party/jpeg/jpeglib.h"        /* all that for JPEG support  :-) */
+#endif /* MNG_INCLUDE_IJG6B */
+
+#if defined(MNG_INTERNAL_MEMMNGMT) || defined(MNG_INCLUDE_FILTERS)
+#include <stdlib.h>                    /* "calloc" & "free" & "abs" */
+#endif
+
+#include <limits.h>                    /* get proper integer widths */
+
+#ifdef WIN32
+#if defined __BORLANDC__
+#include <mem.h>                       /* defines "memcpy" for BCB */
+#else
+#include <memory.h>                    /* defines "memcpy" for other win32 platforms */
+#endif
+#include <string.h>                    /* "strncmp" + "strcmp" */
+#else /* WIN32 */
+#ifdef BSD
+#include <strings.h>                   /* defines "memcpy", etc for BSD (?) */
+#else
+#include <string.h>                    /* defines "memcpy", etc for all others (???) */
+#endif
+#endif /* WIN32 */
+
+#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS)
+#include <math.h>                      /* fp gamma-calculation */
+#endif
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Platform-dependant stuff                                               * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+/* TODO: this may require some elaboration for other platforms;
+   only works with BCB for now */
+
+#ifndef MNG_DLL
+#if defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL)
+#define MNG_DLL
+#endif
+#endif
+
+#define MNG_LOCAL static
+
+#if defined(MNG_DLL) && defined(WIN32) /* setup DLL calling conventions */ 
+#define MNG_DECL __stdcall
+#if defined(MNG_BUILD_DLL)
+#define MNG_EXT __declspec(dllexport)
+#elif defined(MNG_USE_DLL)
+#define MNG_EXT __declspec(dllimport)
+#else
+#define MNG_EXT
+#endif
+#ifdef MNG_STRICT_ANSI
+#undef MNG_STRICT_ANSI                 /* can't do strict-ANSI with this DLL-stuff */
+#endif
+#else
+#define MNG_DECL                       /* dummies for non-DLL */
+#define MNG_EXT
+#endif /* MNG_DLL && WIN32 */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* now force ANSI-C from here on */
+#endif
+
+/* ************************************************************************** */
+
+#if USHRT_MAX == 0xffffffffU                     /* get the proper 32-bit width !!! */
+typedef unsigned short   mng_uint32;
+typedef signed   short   mng_int32;
+#elif UINT_MAX == 0xffffffffU
+typedef unsigned int     mng_uint32;
+typedef signed   int     mng_int32;
+#elif ULONG_MAX == 0xffffffffU
+typedef unsigned long    mng_uint32;
+typedef signed   long    mng_int32;
+#else
+#error "Sorry, I can't find any 32-bit integers on this platform."
+#endif
+
+typedef signed   short   mng_int16;              /* other basic integers */
+typedef unsigned short   mng_uint16;
+typedef signed   char    mng_int8;
+typedef unsigned char    mng_uint8;
+
+typedef double           mng_float;              /* basic float */
+
+typedef size_t           mng_size_t;             /* size field for memory allocation */
+
+typedef char *           mng_pchar;              /* string */
+typedef void *           mng_ptr;                /* generic pointer */
+typedef void             (*mng_fptr) (void);     /* generic function pointer */
+
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * Platform-independant from here                                         * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+typedef mng_uint32 *     mng_uint32p;            /* pointer to unsigned longs */
+typedef mng_int32 *      mng_int32p;             /* pointer to longs */
+typedef mng_uint16 *     mng_uint16p;            /* pointer to unsigned words */
+typedef mng_uint8 *      mng_uint8p;             /* pointer to unsigned bytes */
+
+typedef mng_int8         mng_bool;               /* booleans */
+
+struct mng_data_struct;
+typedef struct mng_data_struct * mng_handle;     /* generic handle */
+
+typedef mng_int32        mng_retcode;            /* generic return code */
+typedef mng_int32        mng_chunkid;            /* 4-byte chunkname identifier */
+typedef mng_ptr          mng_chunkp;             /* pointer to a chunk-structure */
+typedef mng_ptr          mng_objectp;            /* pointer to an object-structure */
+
+typedef mng_chunkid *    mng_chunkidp;           /* pointer to chunkid */
+
+typedef struct {                                 /* 8-bit palette element */
+          mng_uint8 iRed;
+          mng_uint8 iGreen;
+          mng_uint8 iBlue;
+        } mng_palette8e;
+typedef mng_palette8e   mng_palette8[256];       /* 8-bit palette */
+typedef mng_palette8e * mng_palette8ep;
+
+typedef mng_uint8       mng_uint8arr[256];       /* generic arrays */
+typedef mng_uint8       mng_uint8arr4[4];
+typedef mng_uint16      mng_uint16arr[256];
+typedef mng_uint32      mng_uint32arr2[2];
+
+/* ************************************************************************** */
+
+#define MNG_FALSE 0
+#define MNG_TRUE  1
+#define MNG_NULL  0
+
+#define MNG_SUSPENDBUFFERSIZE  32768
+#define MNG_SUSPENDREQUESTSIZE  1024
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+
+/* size of temporary zlib buffer for deflate processing */
+#define MNG_ZLIB_MAXBUF     8192
+
+/* default zlib compression parameters for deflateinit2 */
+#define MNG_ZLIB_LEVEL      9                    /* level */
+#define MNG_ZLIB_METHOD     Z_DEFLATED           /* method */
+#define MNG_ZLIB_WINDOWBITS 15                   /* window size */
+#define MNG_ZLIB_MEMLEVEL   9                    /* memory level */
+#define MNG_ZLIB_STRATEGY   Z_DEFAULT_STRATEGY   /* strategy */
+
+#define MNG_MAX_IDAT_SIZE   4096                 /* maximum size of IDAT data */
+
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_JNG
+
+#ifdef MNG_INCLUDE_IJG6B                         /* IJG helper defs */
+typedef struct jpeg_compress_struct   mngjpeg_comp;
+typedef struct jpeg_decompress_struct mngjpeg_decomp;
+typedef struct jpeg_error_mgr         mngjpeg_error;
+typedef struct jpeg_source_mgr        mngjpeg_source;
+
+typedef mngjpeg_comp   * mngjpeg_compp;
+typedef mngjpeg_decomp * mngjpeg_decompp;
+typedef mngjpeg_error  * mngjpeg_errorp;
+typedef mngjpeg_source * mngjpeg_sourcep;
+
+typedef J_DCT_METHOD     mngjpeg_dctmethod;
+
+/* default IJG parameters for compression */
+#define MNG_JPEG_DCT         JDCT_DEFAULT        /* DCT algorithm (JDCT_ISLOW) */
+#define MNG_JPEG_QUALITY     100                 /* quality 0..100; 100=best */
+#define MNG_JPEG_SMOOTHING   0                   /* default no smoothing */
+#define MNG_JPEG_PROGRESSIVE MNG_FALSE           /* default is just baseline */
+#define MNG_JPEG_OPTIMIZED   MNG_FALSE           /* default is not optimized */
+#endif /* MNG_INCLUDE_IJG6B */
+
+#define MNG_JPEG_MAXBUF      65500               /* max size of temp JPEG buffer */
+#define MNG_MAX_JDAT_SIZE    4096                /* maximum size of JDAT data */
+
+#endif /* MNG_INCLUDE_JNG */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_LCMS
+typedef cmsHPROFILE         mng_cmsprof;         /* little CMS helper defs */
+typedef cmsHTRANSFORM       mng_cmstrans;
+typedef cmsCIExyY           mng_CIExyY;
+typedef cmsCIExyYTRIPLE     mng_CIExyYTRIPLE;
+typedef LPGAMMATABLE        mng_gammatabp;
+#endif /* MNG_INCLUDE_LCMS */
+
+/* ************************************************************************** */
+
+                                       /* enumeration of known graphics types */
+enum mng_imgtypes {mng_it_unknown, mng_it_png, mng_it_mng, mng_it_jng
+#ifdef MNG_INCLUDE_MPNG_PROPOSAL
+     ,mng_it_mpng
+#endif     
+#ifdef MNG_INCLUDE_ANG_PROPOSAL
+     ,mng_it_ang
+#endif
+     };
+typedef enum mng_imgtypes mng_imgtype;
+
+                                       /* enumeration of animation speed-types */
+enum mng_speedtypes {mng_st_normal, mng_st_fast, mng_st_slow, mng_st_slowest};
+typedef enum mng_speedtypes mng_speedtype;
+
+#ifdef MNG_OPTIMIZE_CHUNKREADER
+                                       /* enumeration object-creation indicators */
+enum mng_createobjtypes {mng_create_none, mng_create_always, mng_create_ifglobal};
+typedef enum mng_createobjtypes mng_createobjtype;
+#endif
+
+/* ************************************************************************** */
+
+/* enumeration of PNG image types */
+#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT
+enum png_imgtypes
+  {
+    png_g1,
+    png_g2,
+    png_g4,
+    png_g8,
+    png_rgb8,
+    png_idx1,
+    png_idx2,
+    png_idx4,
+    png_idx8,
+    png_ga8,
+    png_rgba8,
+#ifdef MNG_INCLUDE_JNG
+    png_jpeg_a1,
+    png_jpeg_a2,
+    png_jpeg_a4,
+    png_jpeg_a8,
+#endif
+#ifndef MNG_NO_16BIT_SUPPORT
+    png_g16,
+    png_ga16,
+    png_rgb16,
+    png_rgba16,
+#ifdef MNG_INCLUDE_JNG
+    png_jpeg_a16,
+#endif
+#endif
+    png_none
+  };
+    
+typedef enum png_imgtypes png_imgtype;
+#endif
+/* ************************************************************************** */
+
+                                       /* memory management callbacks */
+typedef mng_ptr    (MNG_DECL *mng_memalloc)      (mng_size_t  iLen);
+typedef void       (MNG_DECL *mng_memfree)       (mng_ptr     iPtr,
+                                                  mng_size_t  iLen);
+
+typedef void       (MNG_DECL *mng_releasedata)   (mng_ptr     pUserdata,
+                                                  mng_ptr     pData,
+                                                  mng_size_t  iLength);
+
+                                       /* I/O management callbacks */
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+typedef mng_bool   (MNG_DECL *mng_openstream)    (mng_handle  hHandle);
+typedef mng_bool   (MNG_DECL *mng_closestream)   (mng_handle  hHandle);
+#endif
+typedef mng_bool   (MNG_DECL *mng_readdata)      (mng_handle  hHandle,
+                                                  mng_ptr     pBuf,
+                                                  mng_uint32  iBuflen,
+                                                  mng_uint32p pRead);
+typedef mng_bool   (MNG_DECL *mng_writedata)     (mng_handle  hHandle,
+                                                  mng_ptr     pBuf,
+                                                  mng_uint32  iBuflen,
+                                                  mng_uint32p pWritten);
+
+                                       /* error & trace processing callbacks */
+typedef mng_bool   (MNG_DECL *mng_errorproc)     (mng_handle  hHandle,
+                                                  mng_int32   iErrorcode,
+                                                  mng_int8    iSeverity,
+                                                  mng_chunkid iChunkname,
+                                                  mng_uint32  iChunkseq,
+                                                  mng_int32   iExtra1,
+                                                  mng_int32   iExtra2,
+                                                  mng_pchar   zErrortext);
+typedef mng_bool   (MNG_DECL *mng_traceproc)     (mng_handle  hHandle,
+                                                  mng_int32   iFuncnr,
+                                                  mng_int32   iFuncseq,
+                                                  mng_pchar   zFuncname);
+
+                                       /* read processing callbacks */
+typedef mng_bool   (MNG_DECL *mng_processheader) (mng_handle  hHandle,
+                                                  mng_uint32  iWidth,
+                                                  mng_uint32  iHeight);
+typedef mng_bool   (MNG_DECL *mng_processtext)   (mng_handle  hHandle,
+                                                  mng_uint8   iType,
+                                                  mng_pchar   zKeyword,
+                                                  mng_pchar   zText,
+                                                  mng_pchar   zLanguage,
+                                                  mng_pchar   zTranslation);
+typedef mng_bool   (MNG_DECL *mng_processsave)   (mng_handle  hHandle);
+typedef mng_bool   (MNG_DECL *mng_processseek)   (mng_handle  hHandle,
+                                                  mng_pchar   zName);
+typedef mng_bool   (MNG_DECL *mng_processneed)   (mng_handle  hHandle,
+                                                  mng_pchar   zKeyword);
+typedef mng_bool   (MNG_DECL *mng_processmend)   (mng_handle  hHandle,
+                                                  mng_uint32  iIterationsdone,
+                                                  mng_uint32  iIterationsleft);
+typedef mng_bool   (MNG_DECL *mng_processunknown) (mng_handle  hHandle,
+                                                   mng_chunkid iChunkid,
+                                                   mng_uint32  iRawlen,
+                                                   mng_ptr     pRawdata);
+typedef mng_bool   (MNG_DECL *mng_processterm)   (mng_handle  hHandle,
+                                                  mng_uint8   iTermaction,
+                                                  mng_uint8   iIteraction,
+                                                  mng_uint32  iDelay,
+                                                  mng_uint32  iItermax);
+
+                                       /* display processing callbacks */
+typedef mng_ptr    (MNG_DECL *mng_getcanvasline) (mng_handle  hHandle,
+                                                  mng_uint32  iLinenr);
+typedef mng_ptr    (MNG_DECL *mng_getbkgdline)   (mng_handle  hHandle,
+                                                  mng_uint32  iLinenr);
+typedef mng_ptr    (MNG_DECL *mng_getalphaline)  (mng_handle  hHandle,
+                                                  mng_uint32  iLinenr);
+typedef mng_bool   (MNG_DECL *mng_refresh)       (mng_handle  hHandle,
+                                                  mng_uint32  iX,
+                                                  mng_uint32  iY,
+                                                  mng_uint32  iWidth,
+                                                  mng_uint32  iHeight);
+
+                                       /* timer management callbacks */
+typedef mng_uint32 (MNG_DECL *mng_gettickcount)  (mng_handle  hHandle);
+typedef mng_bool   (MNG_DECL *mng_settimer)      (mng_handle  hHandle,
+                                                  mng_uint32  iMsecs);
+
+                                       /* color management callbacks */
+typedef mng_bool   (MNG_DECL *mng_processgamma)  (mng_handle  hHandle,
+                                                  mng_uint32  iGamma);
+typedef mng_bool   (MNG_DECL *mng_processchroma) (mng_handle  hHandle,
+                                                  mng_uint32  iWhitepointx,
+                                                  mng_uint32  iWhitepointy,
+                                                  mng_uint32  iRedx,
+                                                  mng_uint32  iRedy,
+                                                  mng_uint32  iGreenx,
+                                                  mng_uint32  iGreeny,
+                                                  mng_uint32  iBluex,
+                                                  mng_uint32  iBluey);
+typedef mng_bool   (MNG_DECL *mng_processsrgb)   (mng_handle  hHandle,
+                                                  mng_uint8   iRenderingintent);
+typedef mng_bool   (MNG_DECL *mng_processiccp)   (mng_handle  hHandle,
+                                                  mng_uint32  iProfilesize,
+                                                  mng_ptr     pProfile);
+typedef mng_bool   (MNG_DECL *mng_processarow)   (mng_handle  hHandle,
+                                                  mng_uint32  iRowsamples,
+                                                  mng_bool    bIsRGBA16,
+                                                  mng_ptr     pRow);
+
+                                       /* chunk access callback(s) */
+typedef mng_bool   (MNG_DECL *mng_iteratechunk)  (mng_handle  hHandle,
+                                                  mng_handle  hChunk,
+                                                  mng_chunkid iChunkid,
+                                                  mng_uint32  iChunkseq);
+
+/* ************************************************************************** */
+
+#endif /* _libmng_types_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_write.c b/files/Source/LibMNG/libmng_write.c
new file mode 100644
index 0000000..79ff544
--- /dev/null
+++ b/files/Source/LibMNG/libmng_write.c
@@ -0,0 +1,198 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_write.c            copyright (c) 2000-2004 G.Juyn   * */
+/* * version   : 1.0.9                                                      * */
+/* *                                                                        * */
+/* * purpose   : Write management (implementation)                          * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the write management routines            * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *             0.5.1 - 05/16/2000 - G.Juyn                                * */
+/* *             - moved the actual write_graphic functionality from        * */
+/* *               mng_hlapi to its appropriate function here               * */
+/* *                                                                        * */
+/* *             0.9.1 - 07/19/2000 - G.Juyn                                * */
+/* *             - fixed writing of signature                               * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *                                                                        * */
+/* *             1.0.8 - 07/06/2004 - G.R-P                                 * */
+/* *             - added conditionals around openstream/closestream         * */
+/* *             - defend against using undefined Open/Closestream function * */
+/* *             1.0.8 - 08/02/2004 - G.Juyn                                * */
+/* *             - added conditional to allow easier writing of large MNG's * */
+/* *                                                                        * */
+/* *             1.0.9 - 09/25/2004 - G.Juyn                                * */
+/* *             - replaced MNG_TWEAK_LARGE_FILES with permanent solution   * */
+/* *             1.0.9 - 12/20/2004 - G.Juyn                                * */
+/* *             - cleaned up macro-invocations (thanks to D. Airlie)       * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_memory.h"
+#include "libmng_chunks.h"
+#include "libmng_chunk_io.h"
+#include "libmng_write.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE)
+mng_retcode mng_drop_chunks (mng_datap pData)
+{
+  mng_chunkp       pChunk;
+  mng_chunkp       pNext;
+  mng_cleanupchunk fCleanup;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DROP_CHUNKS, MNG_LC_START);
+#endif
+
+  pChunk = pData->pFirstchunk;         /* and get first stored chunk (if any) */
+
+  while (pChunk)                       /* more chunks to discard ? */
+  {
+    pNext = ((mng_chunk_headerp)pChunk)->pNext;
+                                       /* call appropriate cleanup */
+    fCleanup = ((mng_chunk_headerp)pChunk)->fCleanup;
+    fCleanup (pData, pChunk);
+
+    pChunk = pNext;                    /* neeeext */
+  }
+
+  pData->pFirstchunk = MNG_NULL;
+  pData->pLastchunk  = MNG_NULL;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_DROP_CHUNKS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_WRITE_PROCS
+
+/* ************************************************************************** */
+
+mng_retcode mng_write_graphic (mng_datap pData)
+{
+  mng_chunkp  pChunk;
+  mng_retcode iRetcode;
+  mng_uint32  iWritten;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_GRAPHIC, MNG_LC_START);
+#endif
+
+  pChunk = pData->pFirstchunk;         /* we'll start with the first, thank you */
+
+  if (pChunk)                          /* is there anything to write ? */
+  {                                    /* open the file */
+    if (!pData->bWriting)
+    {
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+      if (pData->fOpenstream && !pData->fOpenstream ((mng_handle)pData))
+        MNG_ERROR (pData, MNG_APPIOERROR);
+#endif
+      {
+        pData->bWriting      = MNG_TRUE; /* indicate writing */
+        pData->iWritebufsize = 32768;    /* get a temporary write buffer */
+                                       /* reserve 12 bytes for length, chunkname & crc */
+        MNG_ALLOC (pData, pData->pWritebuf, pData->iWritebufsize+12);
+
+                                       /* write the signature */
+        if (((mng_chunk_headerp)pChunk)->iChunkname == MNG_UINT_IHDR)
+          mng_put_uint32 (pData->pWritebuf, PNG_SIG);
+        else
+        if (((mng_chunk_headerp)pChunk)->iChunkname == MNG_UINT_JHDR)
+          mng_put_uint32 (pData->pWritebuf, JNG_SIG);
+        else
+          mng_put_uint32 (pData->pWritebuf, MNG_SIG);
+
+        mng_put_uint32 (pData->pWritebuf+4, POST_SIG);
+
+        if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf, 8, &iWritten))
+        {
+          MNG_FREE (pData, pData->pWritebuf, pData->iWritebufsize+12);
+          MNG_ERROR (pData, MNG_APPIOERROR);
+        }
+
+        if (iWritten != 8)             /* disk full ? */
+        {
+          MNG_FREE (pData, pData->pWritebuf, pData->iWritebufsize+12);
+          MNG_ERROR (pData, MNG_OUTPUTERROR);
+        }
+      }
+    }
+
+    while (pChunk)                     /* so long as there's something to write */
+    {                                  /* let's call its output routine */
+      iRetcode = ((mng_chunk_headerp)pChunk)->fWrite (pData, pChunk);
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+                                       /* neeeext */
+      pChunk = ((mng_chunk_headerp)pChunk)->pNext;
+    }
+
+    if (!pData->bCreating)
+    {                                  /* free the temporary buffer */
+      MNG_FREE (pData, pData->pWritebuf, pData->iWritebufsize+12);
+
+      pData->bWriting = MNG_FALSE;     /* done writing */
+                                       /* close the stream now */
+#ifndef MNG_NO_OPEN_CLOSE_STREAM
+      if (pData->fClosestream && !pData->fClosestream ((mng_handle)pData))
+        MNG_ERROR (pData, MNG_APPIOERROR);
+#endif
+
+    } else {
+                                       /* cleanup the written chunks */
+      iRetcode = mng_drop_chunks (pData);
+      if (iRetcode)                    /* on error bail out */
+        return iRetcode;
+    }
+  }
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_WRITE_GRAPHIC, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_WRITE_PROCS */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
+
diff --git a/files/Source/LibMNG/libmng_write.h b/files/Source/LibMNG/libmng_write.h
new file mode 100644
index 0000000..df058fb
--- /dev/null
+++ b/files/Source/LibMNG/libmng_write.h
@@ -0,0 +1,49 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_write.h            copyright (c) 2000-2004 G.Juyn   * */
+/* * version   : 1.0.9                                                      * */
+/* *                                                                        * */
+/* * purpose   : Write management (definition)                              * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the write management routines                * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *                                                                        * */
+/* *             1.0.9 - 09/25/2004 - G.Juyn                                * */
+/* *             - replaced MNG_TWEAK_LARGE_FILES with permanent solution   * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_write_h_
+#define _libmng_write_h_
+
+/* ************************************************************************** */
+
+mng_retcode mng_drop_chunks   (mng_datap pData);
+
+mng_retcode mng_write_graphic (mng_datap pData);
+
+/* ************************************************************************** */
+
+#endif /* _libmng_write_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/LibMNG/libmng_zlib.c b/files/Source/LibMNG/libmng_zlib.c
new file mode 100644
index 0000000..7d102e1
--- /dev/null
+++ b/files/Source/LibMNG/libmng_zlib.c
@@ -0,0 +1,607 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_zlib.c             copyright (c) 2000-2004 G.Juyn   * */
+/* * version   : 1.0.9                                                      * */
+/* *                                                                        * */
+/* * purpose   : ZLIB library interface (implementation)                    * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : implementation of the ZLIB library interface               * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *             0.5.1 - 05/11/2000 - G.Juyn                                * */
+/* *             - filled the deflatedata routine                           * */
+/* *             0.5.1 - 05/12/2000 - G.Juyn                                * */
+/* *             - changed trace to macro for callback error-reporting      * */
+/* *                                                                        * */
+/* *             0.5.2 - 05/20/2000 - G.Juyn                                * */
+/* *             - fixed for JNG alpha handling                             * */
+/* *             0.5.2 - 05/24/2000 - G.Juyn                                * */
+/* *             - moved init of default zlib parms from here to            * */
+/* *               "mng_hlapi.c"                                            * */
+/* *                                                                        * */
+/* *             0.5.3 - 06/16/2000 - G.Juyn                                * */
+/* *             - changed progressive-display processing                   * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* *             0.9.3 - 08/08/2000 - G.Juyn                                * */
+/* *             - fixed compiler-warnings from Mozilla                     * */
+/* *             0.9.3 - 09/07/2000 - G.Juyn                                * */
+/* *             - added support for new filter_types                       * */
+/* *                                                                        * */
+/* *             1.0.5 - 08/07/2002 - G.Juyn                                * */
+/* *             - added test-option for PNG filter method 193 (=no filter) * */
+/* *             1.0.5 - 08/19/2002 - G.Juyn                                * */
+/* *             - B597134 - libmng pollutes the linker namespace           * */
+/* *             1.0.5 - 09/19/2002 - G.Juyn                                * */
+/* *             - added warning for too much IDAT data                     * */
+/* *                                                                        * */
+/* *             1.0.6 - 07/07/2003 - G.R-P                                 * */
+/* *             - added MNG_NO_16BIT_SUPPORT support                       * */
+/* *                                                                        * */
+/* *             1.0.9 - 10/09/2004 - G.R-P                                 * */
+/* *             - added MNG_NO_1_2_4BIT_SUPPORT support                    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#include "libmng.h"
+#include "libmng_data.h"
+#include "libmng_error.h"
+#include "libmng_trace.h"
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+#include "libmng_memory.h"
+#include "libmng_pixels.h"
+#include "libmng_filter.h"
+#include "libmng_zlib.h"
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+/* ************************************************************************** */
+
+#ifdef MNG_INCLUDE_ZLIB
+
+/* ************************************************************************** */
+
+voidpf mngzlib_alloc (voidpf pData,
+                      uInt   iCount,
+                      uInt   iSize)
+{
+  voidpf pPtr;                         /* temporary space */
+
+#ifdef MNG_INTERNAL_MEMMNGMT
+  pPtr = calloc (iCount, iSize);       /* local allocation */
+#else
+  if (((mng_datap)pData)->fMemalloc)   /* callback function set ? */
+    pPtr = ((mng_datap)pData)->fMemalloc (iCount * iSize);
+  else
+    pPtr = Z_NULL;                     /* can't allocate! */
+#endif
+
+  return pPtr;                         /* return the result */
+}
+
+/* ************************************************************************** */
+
+void mngzlib_free (voidpf pData,
+                   voidpf pAddress)
+{
+#ifdef MNG_INTERNAL_MEMMNGMT
+  free (pAddress);                     /* free locally */
+#else
+  if (((mng_datap)pData)->fMemfree)    /* callback set? */
+    ((mng_datap)pData)->fMemfree (pAddress, 1);
+#endif
+}
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_initialize (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INITIALIZE, MNG_LC_START);
+#endif
+
+#ifdef MNG_INTERNAL_MEMMNGMT
+  pData->sZlib.zalloc = Z_NULL;        /* let zlib figure out memory management */
+  pData->sZlib.zfree  = Z_NULL;
+  pData->sZlib.opaque = Z_NULL;
+#else                                  /* use user-provided callbacks */
+  pData->sZlib.zalloc = mngzlib_alloc;
+  pData->sZlib.zfree  = mngzlib_free;
+  pData->sZlib.opaque = (voidpf)pData;
+#endif
+
+  pData->bInflating   = MNG_FALSE;     /* not performing any action yet */
+  pData->bDeflating   = MNG_FALSE;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INITIALIZE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_cleanup (mng_datap pData)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_CLEANUP, MNG_LC_START);
+#endif
+
+  if (pData->bInflating)               /* force zlib cleanup */
+    mngzlib_inflatefree (pData);
+  if (pData->bDeflating)
+    mngzlib_deflatefree (pData);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_CLEANUP, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_inflateinit (mng_datap pData)
+{
+  int iZrslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEINIT, MNG_LC_START);
+#endif
+                                       /* initialize zlib structures and such */
+  iZrslt = inflateInit (&pData->sZlib);
+
+  if (iZrslt != Z_OK)                  /* on error bail out */
+    MNG_ERRORZ (pData, (mng_uint32)iZrslt);
+
+  pData->bInflating      = MNG_TRUE;   /* really inflating something now */
+  pData->sZlib.next_out  = 0;          /* force JIT initialization */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEINIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+#ifdef MNG_SUPPORT_DISPLAY
+mng_retcode mngzlib_inflaterows (mng_datap  pData,
+                                 mng_uint32 iInlen,
+                                 mng_uint8p pIndata)
+{
+  int         iZrslt;
+  mng_retcode iRslt;
+  mng_ptr     pSwap;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEROWS, MNG_LC_START);
+#endif
+
+  pData->sZlib.next_in   = pIndata;    /* let zlib know where to get stuff */
+  pData->sZlib.avail_in  = (uInt)iInlen;
+
+  if (pData->sZlib.next_out == 0)      /* initialize output variables ? */
+  {                                    /* let zlib know where to store stuff */
+    pData->sZlib.next_out  = pData->pWorkrow;
+    pData->sZlib.avail_out = (uInt)(pData->iRowsize + pData->iPixelofs);
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+    if (pData->iPNGdepth < 8)
+       pData->sZlib.avail_out = (uInt)((pData->iPNGdepth*pData->iRowsize + 7)/8
+           + pData->iPixelofs);
+#endif
+#ifdef MNG_NO_16BIT_SUPPORT
+    if (pData->iPNGdepth > 8)
+       pData->sZlib.avail_out = (uInt)(2*pData->iRowsize + pData->iPixelofs);
+#endif
+  }
+
+  do
+  {                                    /* now inflate a row */
+    iZrslt = inflate (&pData->sZlib, Z_SYNC_FLUSH);
+                                       /* produced a full row ? */
+    if (((iZrslt == Z_OK) || (iZrslt == Z_STREAM_END)) &&
+        (pData->sZlib.avail_out == 0))
+    {                                  /* image not completed yet ? */
+      if (pData->iRow < (mng_int32)pData->iDataheight)
+      {
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+        if (pData->iPNGdepth == 1)
+        {
+          /* Inflate Workrow to 8-bit */
+          mng_int32  iX;
+          mng_uint8p pSrc = pData->pWorkrow+1;
+          mng_uint8p pDest = pSrc + pData->iRowsize - (pData->iRowsize+7)/8;
+
+          for (iX = ((pData->iRowsize+7)/8) ; iX > 0 ; iX--)
+             *pDest++ = *pSrc++;
+
+          pDest = pData->pWorkrow+1;
+          pSrc = pDest + pData->iRowsize - (pData->iRowsize+7)/8;
+          for (iX = pData->iRowsize; ;)
+          {
+            *pDest++ = (((*pSrc)>>7)&1);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)>>6)&1);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)>>5)&1);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)>>4)&1);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)>>3)&1);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)>>2)&1);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)>>1)&1);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)   )&1);
+            if (iX-- <= 0)
+              break;
+            pSrc++;
+          }
+        }
+        else if (pData->iPNGdepth == 2)
+        {
+          /* Inflate Workrow to 8-bit */
+          mng_int32  iX;
+          mng_uint8p pSrc = pData->pWorkrow+1;
+          mng_uint8p pDest = pSrc + pData->iRowsize - (2*pData->iRowsize+7)/8;
+
+          for (iX = ((2*pData->iRowsize+7)/8) ; iX > 0 ; iX--)
+             *pDest++ = *pSrc++;
+
+          pDest = pData->pWorkrow+1;
+          pSrc = pDest + pData->iRowsize - (2*pData->iRowsize+7)/8;
+          for (iX = pData->iRowsize; ;)
+          {
+            *pDest++ = (((*pSrc)>>6)&3);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)>>4)&3);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)>>2)&3);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)   )&3);
+            if (iX-- <= 0)
+              break;
+            pSrc++;
+          }
+        }
+        else if (pData->iPNGdepth == 4)
+        {
+          /* Inflate Workrow to 8-bit */
+          mng_int32  iX;
+          mng_uint8p pSrc = pData->pWorkrow+1;
+          mng_uint8p pDest = pSrc + pData->iRowsize - (4*pData->iRowsize+7)/8;
+
+          for (iX = ((4*pData->iRowsize+7)/8) ; iX > 0 ; iX--)
+             *pDest++ = *pSrc++;
+
+          pDest = pData->pWorkrow+1;
+          pSrc = pDest + pData->iRowsize - (4*pData->iRowsize+7)/8;
+          for (iX = pData->iRowsize; ;)
+          {
+            *pDest++ = (((*pSrc)>>4)&0x0f);
+            if (iX-- <= 0)
+              break;
+            *pDest++ = (((*pSrc)   )&0x0f);
+            if (iX-- <= 0)
+              break;
+            pSrc++;
+          }
+        }
+        if (pData->iPNGdepth < 8 && pData->iColortype == 0)
+        {
+          /* Expand samples to 8-bit by LBR */
+          mng_int32  iX;
+          mng_uint8p pSrc = pData->pWorkrow+1;
+          mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1};
+
+          for (iX = pData->iRowsize; iX > 0; iX--)
+              *pSrc++ *= multiplier[pData->iPNGdepth];
+        }
+#endif
+#ifdef MNG_NO_16BIT_SUPPORT
+        if (pData->iPNGdepth > 8)
+        {
+          /* Reduce Workrow to 8-bit */
+          mng_int32  iX;
+          mng_uint8p pSrc = pData->pWorkrow+1;
+          mng_uint8p pDest = pSrc;
+
+          for (iX = pData->iRowsize; iX > 0; iX--)
+          {
+            *pDest = *pSrc;
+            pDest++;
+            pSrc+=2;
+          }
+        }
+#endif
+
+#ifdef FILTER192                       /* has leveling info ? */
+        if (pData->iFilterofs == MNG_FILTER_DIFFERING)
+          iRslt = init_rowdiffering (pData);
+        else
+#endif
+          iRslt = MNG_NOERROR;
+                                       /* filter the row if necessary */
+        if ((!iRslt) && (pData->iFilterofs < pData->iPixelofs  ) &&
+                        (*(pData->pWorkrow + pData->iFilterofs))    )
+          iRslt = mng_filter_a_row (pData);
+        else
+          iRslt = MNG_NOERROR;
+                                       /* additional leveling/differing ? */
+        if ((!iRslt) && (pData->fDifferrow))
+        {
+          iRslt = ((mng_differrow)pData->fDifferrow) (pData);
+
+          pSwap           = pData->pWorkrow;
+          pData->pWorkrow = pData->pPrevrow;
+          pData->pPrevrow = pSwap;     /* make sure we're processing the right data */
+        }
+
+        if (!iRslt)
+        {
+#ifdef MNG_INCLUDE_JNG
+          if (pData->bHasJHDR)         /* is JNG alpha-channel ? */
+          {                            /* just store in object ? */
+            if ((!iRslt) && (pData->fStorerow))
+              iRslt = ((mng_storerow)pData->fStorerow)     (pData);
+          }
+          else
+#endif /* MNG_INCLUDE_JNG */
+          {                            /* process this row */
+            if ((!iRslt) && (pData->fProcessrow))
+              iRslt = ((mng_processrow)pData->fProcessrow) (pData);
+                                       /* store in object ? */
+            if ((!iRslt) && (pData->fStorerow))
+              iRslt = ((mng_storerow)pData->fStorerow)     (pData);
+                                       /* color correction ? */
+            if ((!iRslt) && (pData->fCorrectrow))
+              iRslt = ((mng_correctrow)pData->fCorrectrow) (pData);
+                                       /* slap onto canvas ? */
+            if ((!iRslt) && (pData->fDisplayrow))
+            {
+              iRslt = ((mng_displayrow)pData->fDisplayrow) (pData);
+
+              if (!iRslt)              /* check progressive display refresh */
+                iRslt = mng_display_progressive_check (pData);
+
+            }
+          }
+        }
+
+        if (iRslt)                     /* on error bail out */
+          MNG_ERROR (pData, iRslt);
+
+        if (!pData->fDifferrow)        /* swap row-pointers */
+        {
+          pSwap           = pData->pWorkrow;
+          pData->pWorkrow = pData->pPrevrow;
+          pData->pPrevrow = pSwap;     /* so prev points to the processed row! */
+        }
+
+        iRslt = mng_next_row (pData);  /* adjust variables for next row */
+
+        if (iRslt)                     /* on error bail out */
+          MNG_ERROR (pData, iRslt);
+      }
+                                       /* let zlib know where to store next output */
+      pData->sZlib.next_out  = pData->pWorkrow;
+      pData->sZlib.avail_out = (uInt)(pData->iRowsize + pData->iPixelofs);
+#ifdef MNG_NO_1_2_4BIT_SUPPORT
+    if (pData->iPNGdepth < 8)
+       pData->sZlib.avail_out = (uInt)((pData->iPNGdepth*pData->iRowsize + 7)/8
+           + pData->iPixelofs);
+#endif
+#ifdef MNG_NO_16BIT_SUPPORT
+      if (pData->iPNGdepth > 8)
+        pData->sZlib.avail_out = (uInt)(2*pData->iRowsize + pData->iPixelofs);
+#endif
+    }
+  }                                    /* until some error or EOI
+                                          or all pixels received */
+  while ( (iZrslt == Z_OK) && (pData->sZlib.avail_in > 0)      &&
+          ( (pData->iRow < (mng_int32)pData->iDataheight) ||
+            ( (pData->iPass >= 0) && (pData->iPass < 7) )    )    );
+                                       /* on error bail out */
+  if ((iZrslt != Z_OK) && (iZrslt != Z_STREAM_END))
+    MNG_ERRORZ (pData, (mng_uint32)iZrslt);
+                                       /* too much data ? */
+  if ((iZrslt == Z_OK) && (pData->sZlib.avail_in > 0))
+    MNG_WARNING (pData, MNG_TOOMUCHIDAT);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEROWS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+#endif /* MNG_SUPPORT_DISPLAY */
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_inflatedata (mng_datap  pData,
+                                 mng_uint32 iInlen,
+                                 mng_uint8p pIndata)
+{
+  int iZrslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEDATA, MNG_LC_START);
+#endif
+                                       /* let zlib know where to get stuff */
+  pData->sZlib.next_in   = pIndata;
+  pData->sZlib.avail_in  = (uInt)iInlen;
+                                       /* now inflate the data in one go! */
+  iZrslt = inflate (&pData->sZlib, Z_FINISH);
+                                       /* not enough room in output-buffer ? */
+  if ((iZrslt == Z_BUF_ERROR) || (pData->sZlib.avail_in > 0))
+    return MNG_BUFOVERFLOW;
+                                       /* on error bail out */
+  if ((iZrslt != Z_OK) && (iZrslt != Z_STREAM_END))
+    MNG_ERRORZ (pData, (mng_uint32)iZrslt);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_inflatefree (mng_datap pData)
+{
+  int iZrslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEFREE, MNG_LC_START);
+#endif
+
+  pData->bInflating = MNG_FALSE;       /* stopped it */
+
+  iZrslt = inflateEnd (&pData->sZlib); /* let zlib cleanup its own stuff */
+
+  if (iZrslt != Z_OK)                  /* on error bail out */
+    MNG_ERRORZ (pData, (mng_uint32)iZrslt);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEFREE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_deflateinit (mng_datap pData)
+{
+  int iZrslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEINIT, MNG_LC_START);
+#endif
+                                       /* initialize zlib structures and such */
+  iZrslt = deflateInit2 (&pData->sZlib, pData->iZlevel, pData->iZmethod,
+                         pData->iZwindowbits, pData->iZmemlevel,
+                         pData->iZstrategy);
+
+  if (iZrslt != Z_OK)                  /* on error bail out */
+    MNG_ERRORZ (pData, (mng_uint32)iZrslt);
+
+  pData->bDeflating = MNG_TRUE;        /* really deflating something now */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEINIT, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_deflaterows (mng_datap  pData,
+                                 mng_uint32 iInlen,
+                                 mng_uint8p pIndata)
+{
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEROWS, MNG_LC_START);
+#endif
+
+
+
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEROWS, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_deflatedata (mng_datap  pData,
+                                 mng_uint32 iInlen,
+                                 mng_uint8p pIndata)
+{
+  int iZrslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEDATA, MNG_LC_START);
+#endif
+
+  pData->sZlib.next_in  = pIndata;     /* let zlib know where to get stuff */
+  pData->sZlib.avail_in = (uInt)iInlen;
+                                       /* now deflate the data in one go! */
+  iZrslt = deflate (&pData->sZlib, Z_FINISH);
+                                       /* not enough room in output-buffer ? */
+  if ((iZrslt == Z_BUF_ERROR) || (pData->sZlib.avail_in > 0))
+    return MNG_BUFOVERFLOW;
+                                       /* on error bail out */
+  if ((iZrslt != Z_OK) && (iZrslt != Z_STREAM_END))
+    MNG_ERRORZ (pData, (mng_uint32)iZrslt);
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEDATA, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;
+}
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_deflatefree (mng_datap pData)
+{
+  int iZrslt;
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEFREE, MNG_LC_START);
+#endif
+
+  iZrslt = deflateEnd (&pData->sZlib); /* let zlib cleanup its own stuff */
+
+  if (iZrslt != Z_OK)                  /* on error bail out */
+    MNG_ERRORZ (pData, (mng_uint32)iZrslt);
+
+  pData->bDeflating = MNG_FALSE;       /* stopped it */
+
+#ifdef MNG_SUPPORT_TRACE
+  MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEFREE, MNG_LC_END);
+#endif
+
+  return MNG_NOERROR;                  /* done */
+}
+
+/* ************************************************************************** */
+
+#endif /* MNG_INCLUDE_ZLIB */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
+
diff --git a/files/Source/LibMNG/libmng_zlib.h b/files/Source/LibMNG/libmng_zlib.h
new file mode 100644
index 0000000..cfc3918
--- /dev/null
+++ b/files/Source/LibMNG/libmng_zlib.h
@@ -0,0 +1,60 @@
+/* ************************************************************************** */
+/* *             For conditions of distribution and use,                    * */
+/* *                see copyright notice in libmng.h                        * */
+/* ************************************************************************** */
+/* *                                                                        * */
+/* * project   : libmng                                                     * */
+/* * file      : libmng_zlib.h             copyright (c) 2000-2002 G.Juyn   * */
+/* * version   : 1.0.0                                                      * */
+/* *                                                                        * */
+/* * purpose   : ZLIB package interface (definition)                        * */
+/* *                                                                        * */
+/* * author    : G.Juyn                                                     * */
+/* *                                                                        * */
+/* * comment   : Definition of the ZLIB package interface                   * */
+/* *                                                                        * */
+/* * changes   : 0.5.1 - 05/08/2000 - G.Juyn                                * */
+/* *             - changed strict-ANSI stuff                                * */
+/* *                                                                        * */
+/* *             0.9.2 - 08/05/2000 - G.Juyn                                * */
+/* *             - changed file-prefixes                                    * */
+/* *                                                                        * */
+/* ************************************************************************** */
+
+#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI)
+#pragma option -A                      /* force ANSI-C */
+#endif
+
+#ifndef _libmng_zlib_h_
+#define _libmng_zlib_h_
+
+/* ************************************************************************** */
+
+mng_retcode mngzlib_initialize  (mng_datap pData);
+mng_retcode mngzlib_cleanup     (mng_datap pData);
+
+mng_retcode mngzlib_inflateinit (mng_datap pData);
+mng_retcode mngzlib_inflaterows (mng_datap  pData,
+                                 mng_uint32 iInlen,
+                                 mng_uint8p pIndata);
+mng_retcode mngzlib_inflatedata (mng_datap  pData,
+                                 mng_uint32 iInlen,
+                                 mng_uint8p pIndata);
+mng_retcode mngzlib_inflatefree (mng_datap pData);
+
+mng_retcode mngzlib_deflateinit (mng_datap pData);
+mng_retcode mngzlib_deflaterows (mng_datap  pData,
+                                 mng_uint32 iInlen,
+                                 mng_uint8p pIndata);
+mng_retcode mngzlib_deflatedata (mng_datap  pData,
+                                 mng_uint32 iInlen,
+                                 mng_uint8p pIndata);
+mng_retcode mngzlib_deflatefree (mng_datap pData);
+
+/* ************************************************************************** */
+
+#endif /* _libmng_zlib_h_ */
+
+/* ************************************************************************** */
+/* * end of file                                                            * */
+/* ************************************************************************** */
diff --git a/files/Source/MapIntrospector.h b/files/Source/MapIntrospector.h
new file mode 100644
index 0000000..091ba68
--- /dev/null
+++ b/files/Source/MapIntrospector.h
@@ -0,0 +1,212 @@
+// ==========================================================
+// STL MapIntrospector class
+//
+// 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!
+// ==========================================================
+
+#ifndef MAPINTROSPECTOR_H_
+#define MAPINTROSPECTOR_H_
+
+// we need at least one C++ header included, 
+// that defines the C++ Standard Library's version macro, 
+// that is used below to identify the library.
+#include <cstdlib>
+
+/**
+Class MapIntrospector - STL std::map Introspector
+
+The MapIntrospector is a helper class used to calculate or estimate part
+of the internal memory footprint of a std::map, that is the memory used
+by N entries, where N is provided as an argument. This class is used by
+function FreeImage_GetMemorySize, which aims to get the total memory
+usage for a FreeImage bitmap.
+
+The type argument _Maptype must take the type of the std::map to be
+introspected.
+
+This class accounts for 'internal' memory per entry only, that is, the
+size returned does neither include the actual size of the std::map class
+itself, nor does it include the size of referenced keys and values (also
+the actual bytes required for std::string type keys or values are not
+counted). For example, the total memory usage should be something like:
+
+typedef std::map<std::string, double> DBLMAP
+DBLMAP MyMap;
+
+int total_size = sizeof(DBLMAP) + MapIntrospector<DBLMAP>::GetNodesMemorySize(MyMap.size())
+for (DBLMAP::iterator i = MyMap->begin(); i != MyMap->end(); i++) {
+ std::string key = i->first;
+ total_size += key.capacity();
+}
+
+So, basically, this class' task is to get the (constant) number of bytes
+per entry, which is multiplied by N (parameter node_count) and returned
+in method GetNodesMemorySize. Since this heavily depends on the actually
+used C++ Standard Library, this class must be implemented specifically
+for each C++ Standard Library.
+
+At current, there is an implementation available for these C++ Standard
+Libraries:
+
+- Microsoft C++ Standard Library
+- GNU Standard C++ Library v3, libstdc++-v3
+- LLVM "libc++" C++ Standard Library (untested)
+- Unknown C++ Standard Library
+
+Volunteers for testing as well as for providing support for other/new
+libraries are welcome.
+
+The 'Unknown C++ Standard Library' case is used if no other known C++
+Standard Library was detected. It uses a typical _Node structure to
+declare an estimated memory consumption for a node.
+*/
+
+#if defined(_CPPLIB_VER)	// Microsoft C++ Standard Library
+/**
+ The Microsoft C++ Standard Library uses the protected structure _Node
+ of class std::_Tree_nod to represent a node. This class is used by
+ std::_Tree, the base class of std::map. So, since std::map is derived
+ from std::_Tree (and _Node is protected), we can get access to this
+ structure by deriving from std::map.
+
+ Additionally, the Microsoft C++ Standard Library uses a separately
+ allocated end node for its balanced red-black tree so, actually, there
+ are size() + 1 nodes present in memory.
+
+ With all that in place, the total memory for all nodes in std::map
+ is simply (node_count + 1) * sizeof(_Node).
+*/
+template<class _Maptype>
+class MapIntrospector: private _Maptype {
+public:
+	static size_t GetNodesMemorySize(size_t node_count) {
+		return (node_count + 1) * sizeof(_Node);
+	}
+};
+
+#elif defined(__GLIBCXX__)	// GNU Standard C++ Library v3, libstdc++-v3
+/**
+ The GNU Standard C++ Library v3 uses structure std::_Rb_tree_node<_Val>,
+ which is publicly declared in the standard namespace. Its value type
+ _Val is actually the map's value_type std::map::value_type.
+
+ So, the total memory for all nodes in std::map is simply
+ node_count * sizeof(std::_Rb_tree_node<_Val>), _Val being the map's
+ value_type.
+*/
+template<class _Maptype>
+class MapIntrospector {
+private:
+	typedef typename _Maptype::value_type _Val;
+
+public:
+	static size_t GetNodesMemorySize(size_t node_count) {
+		return node_count * sizeof(std::_Rb_tree_node<_Val>);
+	}
+};
+
+#elif defined(_LIBCPP_VERSION)	// "libc++" C++ Standard Library (LLVM)
+/*
+ The "libc++" C++ Standard Library uses structure
+ std::__tree_node<_Val, void*> for regular nodes and one instance of
+ structure std::__tree_end_node<void*> for end nodes, which both are
+ publicly declared in the standard namespace. Its value type _Val is
+ actually the map's value_type std::map::value_type.
+
+ So, the total memory for all nodes in std::map is simply
+ node_count * sizeof(std::__tree_node<_Val, void*>)
+ + sizeof(std::__tree_end_node<void*>).
+ 
+ REMARK: this implementation is not yet tested!
+*/
+template<class _Maptype>
+class MapIntrospector {
+private:
+	typedef typename _Maptype::value_type _Val;
+
+public:
+	static size_t GetNodesMemorySize(size_t node_count) {
+		return node_count * sizeof(std::__tree_node<_Val, void*>) + sizeof(std::__tree_end_node<void*>);
+	}
+};
+
+//#elif defined(_ADD_YOUR_CPP_STD_LIBRARY_HERE_)
+
+#else							// Unknown C++ Standard Library
+/**
+ If we do not know the actual C++ Standard Library and so, have no
+ access to any internal types, we can just make some assumptions about
+ the implementation and memory usage.
+
+ However, all implementations will probably be based on a balanced
+ red-black tree, will also store the map's value_type in each node and
+ need some extra information like the node's color. For a binary tree,
+ at least two pointers, one for left and one for right are required.
+ Since it is handy, many implementations also have a parent pointer.
+
+ We let the compiler calculate the size of the above mentioned items by
+ using a fake structure. By using a real structure (in contrast to just
+ adding numbers/bytes) we'll get correct pointer sizes as well as any
+ padding applied for free.
+*/
+template<class _Maptype>
+class MapIntrospector {
+private:
+	/* Define some handy typedefs to build up the structure. */
+
+	/**
+	 Each node will likely store the value_type of the mapping,
+	 that is a std::pair<_Key, _Value>.
+	*/
+	typedef typename _Maptype::value_type _Val;
+
+	/**
+	 We will need some pointers, since std::map is likely implemented
+	 as a balanced red-black tree.
+	*/
+	typedef void*                         _Ptr;
+
+	/**
+	 Space for some extra information (like the node's color).
+	 An int should be sufficient.
+	*/
+	typedef int                           _Ext;
+
+	/* The memory required for each node will likely look like this
+	 structure. We will just multiply sizeof(_Node) by the number
+	 of nodes to get the total memory of all nodes. By using the
+	 size of the structure, we will also take care of the compiler's
+	 default padding.
+	*/
+	typedef struct {
+		_Ptr _parent_node;
+		_Ptr _left_node;
+		_Ptr _right_node;
+		_Val _value;
+		_Ext _extra_info;
+	} _Node;
+
+public:
+	static size_t GetNodesMemorySize(size_t node_count) {
+		return node_count * sizeof(_Node);
+	}
+};
+
+#endif // Standard C++ Library
+
+#endif // MAPINTROSPECTOR_H_
diff --git a/files/Source/Metadata/Exif.cpp b/files/Source/Metadata/Exif.cpp
new file mode 100644
index 0000000..95c7fd4
--- /dev/null
+++ b/files/Source/Metadata/Exif.cpp
@@ -0,0 +1,1252 @@
+// ==========================================================
+// Metadata functions implementation
+// Exif metadata model
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// Based on the following implementations:
+// - metadata-extractor : http://www.drewnoakes.com/code/exif/
+// - jhead : http://www.sentex.net/~mwandel/jhead/
+// - ImageMagick : http://www.imagemagick.org/
+//
+// 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 "FreeImageTag.h"
+
+// ==========================================================
+// Exif JPEG routines
+// ==========================================================
+
+#define EXIF_NUM_FORMATS  12
+
+#define TAG_EXIF_OFFSET			0x8769	// Exif IFD Pointer
+#define TAG_GPS_OFFSET			0x8825	// GPS Info IFD Pointer
+#define TAG_INTEROP_OFFSET		0xA005	// Interoperability IFD Pointer
+#define TAG_MAKER_NOTE			0x927C	// Maker note
+
+// CANON cameras have some funny bespoke fields that need further processing...
+#define TAG_CANON_CAMERA_STATE_0x01	0x0001		// tags under tag 0x001 (CameraSettings)
+#define TAG_CANON_CAMERA_STATE_0x02	0x0002		// tags under tag 0x002 (FocalLength)
+#define TAG_CANON_CAMERA_STATE_0x04	0x0004		// tags under tag 0x004 (ShotInfo)
+#define TAG_CANON_CAMERA_STATE_0x12	0x0012		// tags under tag 0x012 (AFInfo)
+#define TAG_CANON_CAMERA_STATE_0xA0	0x00A0		// tags under tag 0x0A0 (ProcessingInfo)
+#define TAG_CANON_CAMERA_STATE_0xE0	0x00E0		// tags under tag 0x0E0 (SensorInfo)
+
+
+// =====================================================================
+// Reimplementation of strnicmp (it is not supported on some systems)
+// =====================================================================
+
+/**
+Compare characters of two strings without regard to case.
+@param s1 Null-terminated string to compare.
+@param s2 Null-terminated string to compare.
+@param len Number of characters to compare
+@return Returns 0 if s1 substring identical to s2 substring
+*/
+static int 
+FreeImage_strnicmp(const char *s1, const char *s2, size_t len) {
+	unsigned char c1, c2;
+
+	if(!s1 || !s2) return -1;
+
+	c1 = 0;	c2 = 0;
+	if(len) {
+		do {
+			c1 = *s1; c2 = *s2;
+			s1++; s2++;
+			if (!c1)
+				break;
+			if (!c2)
+				break;
+			if (c1 == c2)
+				continue;
+			c1 = (BYTE)tolower(c1);
+			c2 = (BYTE)tolower(c2);
+			if (c1 != c2)
+				break;
+		} while (--len);
+	}
+	return (int)c1 - (int)c2;
+}
+
+
+// ----------------------------------------------------------
+//   Little Endian / Big Endian io routines
+// ----------------------------------------------------------
+
+static short 
+ReadInt16(BOOL msb_order, const void *buffer) {
+	short value;
+
+	if(msb_order) {
+		value = (short)((((BYTE*) buffer)[0] << 8) | ((BYTE*) buffer)[1]);
+		return value;
+    }
+	value = (short)((((BYTE*) buffer)[1] << 8) | ((BYTE*) buffer)[0]);
+	return value;
+}
+
+static LONG 
+ReadInt32(BOOL msb_order, const void *buffer) {
+	LONG value;
+
+	if(msb_order) {
+		value = (LONG)((((BYTE*) buffer)[0] << 24) | (((BYTE*) buffer)[1] << 16) | (((BYTE*) buffer)[2] << 8) | (((BYTE*) buffer)[3]));
+		return value;
+    }
+	value = (LONG)((((BYTE*) buffer)[3] << 24) | (((BYTE*) buffer)[2] << 16) | (((BYTE*) buffer)[1] << 8 ) | (((BYTE*) buffer)[0]));
+	return value;
+}
+
+static WORD 
+ReadUint16(BOOL msb_order, const void *buffer) {
+	WORD value;
+	
+	if(msb_order) {
+		value = (WORD) ((((BYTE*) buffer)[0] << 8) | ((BYTE*) buffer)[1]);
+		return value;
+    }
+	value = (WORD) ((((BYTE*) buffer)[1] << 8) | ((BYTE*) buffer)[0]);
+	return value;
+}
+
+static DWORD 
+ReadUint32(BOOL msb_order, const void *buffer) {
+	return ((DWORD) ReadInt32(msb_order, buffer) & 0xFFFFFFFF);
+}
+
+// ----------------------------------------------------------
+//   Exif JPEG markers routines
+// ----------------------------------------------------------
+
+/**
+Process a IFD offset
+Returns the offset and the metadata model for this tag
+*/
+static void 
+processIFDOffset(FITAG *tag, const char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) {
+	// get the IFD offset
+	*subdirOffset = ReadUint32(msb_order, pval);
+
+	// select a tag info table
+	switch(FreeImage_GetTagID(tag)) {
+		case TAG_EXIF_OFFSET:
+			*md_model = TagLib::EXIF_EXIF;
+			break;
+		case TAG_GPS_OFFSET:
+			*md_model = TagLib::EXIF_GPS;
+			break;
+		case TAG_INTEROP_OFFSET:
+			*md_model = TagLib::EXIF_INTEROP;
+			break;
+	}
+}
+
+/**
+Process a maker note IFD offset
+Returns the offset and the metadata model for this tag
+*/
+static void 
+processMakerNote(FIBITMAP *dib, const char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) {
+	FITAG *tagMake = NULL;
+
+	*subdirOffset = 0;
+	*md_model = TagLib::UNKNOWN;
+
+	// Determine the camera model and makernote format
+	// WARNING: note that Maker may be NULL sometimes so check its value before using it
+	// (NULL pointer checking is done by FreeImage_strnicmp)
+	FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "Make", &tagMake);
+	const char *Maker = (char*)FreeImage_GetTagValue(tagMake);
+
+	if((memcmp("OLYMP\x00\x01", pval, 7) == 0) || (memcmp("OLYMP\x00\x02", pval, 7) == 0) || (memcmp("EPSON", pval, 5) == 0) || (memcmp("AGFA", pval, 4) == 0)) {
+		// Olympus Type 1 Makernote
+		// Epson and Agfa use Olympus maker note standard, 
+		// see: http://www.ozhiker.com/electronics/pjmt/jpeg_info/
+		*md_model = TagLib::EXIF_MAKERNOTE_OLYMPUSTYPE1;
+		*subdirOffset = 8;
+	} 
+	else if(memcmp("OLYMPUS\x00\x49\x49\x03\x00", pval, 12) == 0) {
+		// Olympus Type 2 Makernote
+		// !!! NOT YET SUPPORTED !!!
+		*subdirOffset = 0;
+		*md_model = TagLib::UNKNOWN;
+	}
+	else if(memcmp("Nikon", pval, 5) == 0) {
+		/* There are two scenarios here:
+		 * Type 1:
+		 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
+		 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
+		 * Type 3:
+		 * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*...
+		 * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200
+		 */
+		if (pval[6] == 1) {
+			// Nikon type 1 Makernote
+			*md_model = TagLib::EXIF_MAKERNOTE_NIKONTYPE1;
+			*subdirOffset = 8;
+        } else if (pval[6] == 2) {
+            // Nikon type 3 Makernote
+			*md_model = TagLib::EXIF_MAKERNOTE_NIKONTYPE3;
+			*subdirOffset = 18;
+        } else {
+			// Unsupported makernote data ignored
+			*subdirOffset = 0;
+			*md_model = TagLib::UNKNOWN;
+		}
+	} else if(Maker && (FreeImage_strnicmp("NIKON", Maker, 5) == 0)) {
+		// Nikon type 2 Makernote
+		*md_model = TagLib::EXIF_MAKERNOTE_NIKONTYPE2;
+		*subdirOffset = 0;
+    } else if(Maker && (FreeImage_strnicmp("Canon", Maker, 5) == 0)) {
+        // Canon Makernote
+		*md_model = TagLib::EXIF_MAKERNOTE_CANON;
+		*subdirOffset = 0;		
+    } else if(Maker && (FreeImage_strnicmp("Casio", Maker, 5) == 0)) {
+        // Casio Makernote
+		if(memcmp("QVC\x00\x00\x00", pval, 6) == 0) {
+			// Casio Type 2 Makernote
+			*md_model = TagLib::EXIF_MAKERNOTE_CASIOTYPE2;
+			*subdirOffset = 6;
+		} else {
+			// Casio Type 1 Makernote
+			*md_model = TagLib::EXIF_MAKERNOTE_CASIOTYPE1;
+			*subdirOffset = 0;
+		}
+	} else if ((memcmp("FUJIFILM", pval, 8) == 0) || (Maker && (FreeImage_strnicmp("Fujifilm", Maker, 8) == 0))) {
+        // Fujifile Makernote
+		// Fujifilm's Makernote always use little-endian order altough the Exif section maybe in little-endian order or in big-endian order. 
+		// If msb_order == TRUE, the Makernote won't be read: 
+		// the value of ifdStart will be 0x0c000000 instead of 0x0000000c and the MakerNote section will be discarded later
+		// in jpeg_read_exif_dir because the IFD is too high
+		*md_model = TagLib::EXIF_MAKERNOTE_FUJIFILM;
+        DWORD ifdStart = ReadUint32(msb_order, pval + 8);
+		*subdirOffset = ifdStart;
+    }
+	else if(memcmp("KYOCERA\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x00\x00\x00", pval, 22) == 0) {
+		*md_model = TagLib::EXIF_MAKERNOTE_KYOCERA;
+		*subdirOffset = 22;
+	}
+	else if(Maker && (FreeImage_strnicmp("Minolta", Maker, 7) == 0)) {
+		// Minolta maker note
+		*md_model = TagLib::EXIF_MAKERNOTE_MINOLTA;
+		*subdirOffset = 0;
+	}
+	else if(memcmp("Panasonic\x00\x00\x00", pval, 12) == 0) {
+		// Panasonic maker note
+		*md_model = TagLib::EXIF_MAKERNOTE_PANASONIC;
+		*subdirOffset = 12;
+	}
+	else if(Maker && (FreeImage_strnicmp("LEICA", Maker, 5) == 0)) {
+		// Leica maker note
+		if(memcmp("LEICA\x00\x00\x00", pval, 8) == 0) {
+			// not yet supported makernote data ignored
+			*subdirOffset = 0;
+			*md_model = TagLib::UNKNOWN;
+		}
+	}
+	else if(Maker && ((FreeImage_strnicmp("Pentax", Maker, 6) == 0) || (FreeImage_strnicmp("Asahi", Maker, 5) == 0))) {
+		// Pentax maker note
+		if(memcmp("AOC\x00", pval, 4) == 0) {
+			// Type 2 Pentax Makernote
+			*md_model = TagLib::EXIF_MAKERNOTE_PENTAX;
+			*subdirOffset = 6;
+		} else {
+			// Type 1 Pentax Makernote
+			*md_model = TagLib::EXIF_MAKERNOTE_ASAHI;
+			*subdirOffset = 0;
+		}
+	}	
+	else if((memcmp("SONY CAM\x20\x00\x00\x00", pval, 12) == 0) || (memcmp("SONY DSC\x20\x00\x00\x00", pval, 12) == 0)) {
+		*md_model = TagLib::EXIF_MAKERNOTE_SONY;
+		*subdirOffset = 12;
+	}
+	else if((memcmp("SIGMA\x00\x00\x00", pval, 8) == 0) || (memcmp("FOVEON\x00\x00", pval, 8) == 0)) {
+		FITAG *tagModel = NULL;
+		FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "Model", &tagModel);
+		const char *Model = (char*)FreeImage_GetTagValue(tagModel);
+		if(Model && (memcmp("SIGMA SD1\x00", Model, 10) == 0)) {
+			// Sigma SD1 maker note
+			*subdirOffset = 10;
+			*md_model = TagLib::EXIF_MAKERNOTE_SIGMA_SD1;
+		} else {
+			// Sigma / Foveon makernote
+			*subdirOffset = 10;
+			*md_model = TagLib::EXIF_MAKERNOTE_SIGMA_FOVEON;
+		}
+	}
+}
+
+/**
+Process a Canon maker note tag. 
+A single Canon tag may contain many other tags within.
+*/
+static BOOL 
+processCanonMakerNoteTag(FIBITMAP *dib, FITAG *tag) {
+	char defaultKey[16];
+	DWORD startIndex = 0;
+	TagLib& s = TagLib::instance();
+
+	WORD tag_id = FreeImage_GetTagID(tag);
+
+	int subTagTypeBase = 0;
+
+	switch(tag_id) {
+		case TAG_CANON_CAMERA_STATE_0x01:
+			subTagTypeBase = 0xC100;
+			startIndex = 1;
+			break;
+		case TAG_CANON_CAMERA_STATE_0x02:
+			subTagTypeBase = 0xC200;
+			startIndex = 0;
+			break;
+		case TAG_CANON_CAMERA_STATE_0x04:
+			subTagTypeBase = 0xC400;
+			startIndex = 1;
+			break;
+		case TAG_CANON_CAMERA_STATE_0x12:
+			subTagTypeBase = 0x1200;
+			startIndex = 0;
+			break;
+		case TAG_CANON_CAMERA_STATE_0xA0:
+			subTagTypeBase = 0xCA00;
+			startIndex = 1;
+			break;
+		case TAG_CANON_CAMERA_STATE_0xE0:
+			subTagTypeBase = 0xCE00;
+			startIndex = 1;
+			break;
+
+		default:
+		{
+			// process as a normal tag
+
+			// get the tag key and description
+			const char *key = s.getTagFieldName(TagLib::EXIF_MAKERNOTE_CANON, tag_id, defaultKey);
+			FreeImage_SetTagKey(tag, key);
+			const char *description = s.getTagDescription(TagLib::EXIF_MAKERNOTE_CANON, tag_id);
+			FreeImage_SetTagDescription(tag, description);
+
+			// store the tag
+			if(key) {
+				FreeImage_SetMetadata(FIMD_EXIF_MAKERNOTE, dib, key, tag);
+			}
+
+			return TRUE;
+		}
+		break;
+
+	}
+
+	WORD *pvalue = (WORD*)FreeImage_GetTagValue(tag);
+
+	// create a tag
+	FITAG *canonTag = FreeImage_CreateTag();
+	if(!canonTag) return FALSE;
+
+	// we intentionally skip the first array member (if needed)
+    for (DWORD i = startIndex; i < FreeImage_GetTagCount(tag); i++) {
+
+		tag_id = (WORD)(subTagTypeBase + i);
+
+		FreeImage_SetTagID(canonTag, tag_id);
+		FreeImage_SetTagType(canonTag, FIDT_SHORT);
+		FreeImage_SetTagCount(canonTag, 1);
+		FreeImage_SetTagLength(canonTag, 2);
+		FreeImage_SetTagValue(canonTag, &pvalue[i]);
+
+		// get the tag key and description
+		const char *key = s.getTagFieldName(TagLib::EXIF_MAKERNOTE_CANON, tag_id, defaultKey);
+		FreeImage_SetTagKey(canonTag, key);
+		const char *description = s.getTagDescription(TagLib::EXIF_MAKERNOTE_CANON, tag_id);
+		FreeImage_SetTagDescription(canonTag, description);
+
+		// store the tag
+		if(key) {
+			FreeImage_SetMetadata(FIMD_EXIF_MAKERNOTE, dib, key, canonTag);
+		}
+	}
+
+	// delete the tag
+	FreeImage_DeleteTag(canonTag);
+
+	return TRUE;
+}
+
+/**
+Process a standard Exif tag
+*/
+static void 
+processExifTag(FIBITMAP *dib, FITAG *tag, char *pval, BOOL msb_order, TagLib::MDMODEL md_model) {
+	char defaultKey[16];
+	int n;
+	DWORD i;
+
+	// allocate a buffer to store the tag value
+	BYTE *exif_value = (BYTE*)malloc(FreeImage_GetTagLength(tag) * sizeof(BYTE));
+	if(NULL == exif_value) {
+		// out of memory ...
+		return;
+	}
+	memset(exif_value, 0, FreeImage_GetTagLength(tag) * sizeof(BYTE));
+
+	// get the tag value
+	switch(FreeImage_GetTagType(tag)) {
+
+		case FIDT_SHORT:
+		{
+			WORD *value = (WORD*)&exif_value[0];
+			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
+				value[i] = ReadUint16(msb_order, pval + i * sizeof(WORD));
+			}
+			FreeImage_SetTagValue(tag, value);
+			break;
+		}
+		case FIDT_SSHORT:
+		{
+			short *value = (short*)&exif_value[0];
+			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
+				value[i] = ReadInt16(msb_order, pval + i * sizeof(short));
+			}
+			FreeImage_SetTagValue(tag, value);
+			break;
+		}
+		case FIDT_LONG:
+		{
+			DWORD *value = (DWORD*)&exif_value[0];
+			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
+				value[i] = ReadUint32(msb_order, pval + i * sizeof(DWORD));
+			}
+			FreeImage_SetTagValue(tag, value);
+			break;
+		}
+		case FIDT_SLONG:
+		{
+			LONG *value = (LONG*)&exif_value[0];
+			for(i = 0; i < FreeImage_GetTagCount(tag); i++) {
+				value[i] = ReadInt32(msb_order, pval + i * sizeof(LONG));
+			}
+			FreeImage_SetTagValue(tag, value);
+			break;
+		}
+		case FIDT_RATIONAL:
+		{
+			n = sizeof(DWORD);
+
+			DWORD *value = (DWORD*)&exif_value[0];						
+			for(i = 0; i < 2 * FreeImage_GetTagCount(tag); i++) {
+				// read a sequence of (numerator, denominator)
+				value[i] = ReadUint32(msb_order, n*i + (char*)pval);
+			}
+			FreeImage_SetTagValue(tag, value);
+			break;
+		}
+		case FIDT_SRATIONAL:
+		{
+			n = sizeof(LONG);
+
+			LONG *value = (LONG*)&exif_value[0];
+			for(i = 0; i < 2 * FreeImage_GetTagCount(tag); i++) {
+				// read a sequence of (numerator, denominator)
+				value[i] = ReadInt32(msb_order, n*i + (char*)pval);
+			}
+			FreeImage_SetTagValue(tag, value);
+			break;
+		}
+		case FIDT_BYTE:
+		case FIDT_ASCII:
+		case FIDT_SBYTE:
+		case FIDT_UNDEFINED:
+		case FIDT_FLOAT:
+		case FIDT_DOUBLE:
+		default:
+			FreeImage_SetTagValue(tag, pval);
+			break;
+	}
+
+	if(md_model == TagLib::EXIF_MAKERNOTE_CANON) {
+		// A single Canon tag can have multiple values within
+		processCanonMakerNoteTag(dib, tag);
+	}
+	else {
+		TagLib& s = TagLib::instance();
+
+		WORD tag_id = FreeImage_GetTagID(tag);
+
+		// get the tag key and description
+		const char *key = s.getTagFieldName(md_model, tag_id, defaultKey);
+		FreeImage_SetTagKey(tag, key);
+		const char *description = s.getTagDescription(md_model, tag_id);
+		FreeImage_SetTagDescription(tag, description);
+
+		// store the tag
+		if(key) {
+			FreeImage_SetMetadata(s.getFreeImageModel(md_model), dib, key, tag);
+		}
+	}
+	
+
+	// free the temporary buffer
+	free(exif_value);
+
+}
+
+/**
+Process Exif directory
+
+@param dib Input FIBITMAP
+@param tiffp Pointer to the TIFF header
+@param dwOffsetIfd0 Offset to the 0th IFD (first IFD)
+@param dwLength Length of the Exif file
+@param dwProfileOffset File offset to be used when reading 'offset/value' tags
+@param msb_order Endianness order of the Exif file (TRUE if big-endian, FALSE if little-endian)
+@param starting_md_model Metadata model of the IFD (should be TagLib::EXIF_MAIN for a jpeg)
+@return Returns TRUE if sucessful, returns FALSE otherwise
+*/
+static BOOL 
+jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, DWORD dwOffsetIfd0, DWORD dwLength, DWORD dwProfileOffset, BOOL msb_order, TagLib::MDMODEL starting_md_model) {
+	WORD de, nde;
+
+	std::stack<WORD>			destack;	// directory entries stack
+	std::stack<const BYTE*>		ifdstack;	// IFD stack
+	std::stack<TagLib::MDMODEL>	modelstack; // metadata model stack
+
+	// Keep a list of already visited IFD to avoid stack overflows 
+	// when recursive/cyclic directory structures exist. 
+	// This kind of recursive Exif file was encountered with Kodak images coming from 
+	// KODAK PROFESSIONAL DCS Photo Desk JPEG Export v3.2 W
+	std::map<DWORD, int> visitedIFD;
+
+	/*
+	"An Image File Directory (IFD) consists of a 2-byte count of the number of directory
+	entries (i.e. the number of fields), followed by a sequence of 12-byte field
+	entries, followed by a 4-byte offset of the next IFD (or 0 if none)."
+	The "next IFD" (1st IFD) is the thumbnail.
+	*/
+	#define DIR_ENTRY_ADDR(_start, _entry) (_start + 2 + (12 * _entry))
+
+	// set the metadata model to Exif
+
+	TagLib::MDMODEL md_model = starting_md_model;
+
+	// set the pointer to the first IFD (0th IFD) and follow it were it leads.
+
+	const BYTE *ifd0th = (BYTE*)tiffp + (size_t)dwOffsetIfd0;
+
+	const BYTE *ifdp = ifd0th;
+
+	de = 0;
+
+	do {
+		// if there is anything on the stack then pop it off
+		if(!destack.empty()) {
+			ifdp		= ifdstack.top();	ifdstack.pop();
+			de			= destack.top();	destack.pop();
+			md_model	= modelstack.top();	modelstack.pop();
+		}
+
+		// remember that we've visited this directory and entry so that we don't visit it again later
+		DWORD visited = (DWORD)( (((size_t)ifdp & 0xFFFF) << 16) | (size_t)de );
+		if(visitedIFD.find(visited) != visitedIFD.end()) {
+			continue;
+		} else {
+			visitedIFD[visited] = 1;	// processed
+		}
+
+		// determine how many entries there are in the current IFD
+		nde = ReadUint16(msb_order, ifdp);
+		if (((size_t)(ifdp - tiffp) + 12 * nde) > (size_t)dwLength) {
+			// suspicious IFD offset, ignore
+			continue;
+		}
+
+		for(; de < nde; de++) {
+			char *pde = NULL;	// pointer to the directory entry
+			char *pval = NULL;	// pointer to the tag value
+			
+			// create a tag
+			FITAG *tag = FreeImage_CreateTag();
+			if(!tag) return FALSE;
+
+			// point to the directory entry
+			pde = (char*) DIR_ENTRY_ADDR(ifdp, de);
+
+			// get the tag ID
+			WORD tag_id = ReadUint16(msb_order, pde);
+			FreeImage_SetTagID(tag, tag_id);
+
+			// get the tag type
+			WORD tag_type = (WORD)ReadUint16(msb_order, pde + 2);
+            if((tag_type - 1) >= EXIF_NUM_FORMATS) {
+                // a problem occured : delete the tag (not free'd after)
+			    FreeImage_DeleteTag(tag);
+				// break out of the for loop
+				break;
+            }
+			FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)tag_type);
+
+			// get number of components
+			DWORD tag_count = ReadUint32(msb_order, pde + 4);
+			FreeImage_SetTagCount(tag, tag_count);
+            // check that tag length (size of the tag value in bytes) will fit in a DWORD
+            unsigned tag_data_width = FreeImage_TagDataWidth(FreeImage_GetTagType(tag));
+            if (tag_data_width != 0 && FreeImage_GetTagCount(tag) > ~(DWORD)0 / tag_data_width) {
+                FreeImage_DeleteTag(tag);
+				        // don't risk processing this directory further
+				        return FALSE;
+            }
+			FreeImage_SetTagLength(tag, FreeImage_GetTagCount(tag) * tag_data_width);
+
+			if(FreeImage_GetTagLength(tag) <= 4) {
+				// 4 bytes or less and value is in the dir entry itself
+				pval = pde + 8;
+			} else {
+				// if its bigger than 4 bytes, the directory entry contains an offset				
+				DWORD offset_value = ReadUint32(msb_order, pde + 8);
+				// the offset can be relative to tiffp or to an external reference (see JPEG-XR)
+				if(dwProfileOffset) {
+					offset_value -= dwProfileOffset;
+				}
+				// first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data
+				if(offset_value > dwLength) {
+					// a problem occured : delete the tag (not free'd after)
+					FreeImage_DeleteTag(tag);
+					// jump to next entry
+					continue;
+				}
+				// now check that length does not exceed the buffer size
+				if(FreeImage_GetTagLength(tag) > dwLength - offset_value){
+					// a problem occured : delete the tag (not free'd after)
+					FreeImage_DeleteTag(tag);
+					// jump to next entry
+					continue;
+				}
+				pval = (char*)(tiffp + offset_value);
+			}
+
+			// check for a IFD offset
+			BOOL isIFDOffset = FALSE;
+			switch(FreeImage_GetTagID(tag)) {
+				case TAG_EXIF_OFFSET:
+				case TAG_GPS_OFFSET:
+				case TAG_INTEROP_OFFSET:
+				case TAG_MAKER_NOTE:
+					isIFDOffset = TRUE;
+					break;
+			}
+			if(isIFDOffset)	{
+				DWORD sub_offset = 0;
+				TagLib::MDMODEL next_mdmodel = md_model;
+				const BYTE *next_ifd = ifdp;
+				
+				// get offset and metadata model
+				if (FreeImage_GetTagID(tag) == TAG_MAKER_NOTE) {
+					processMakerNote(dib, pval, msb_order, &sub_offset, &next_mdmodel);
+					next_ifd = (BYTE*)pval + sub_offset;
+				} else {
+					processIFDOffset(tag, pval, msb_order, &sub_offset, &next_mdmodel);
+					next_ifd = (BYTE*)tiffp + sub_offset;
+				}
+
+				if((sub_offset < dwLength) && (next_mdmodel != TagLib::UNKNOWN)) {
+					// push our current directory state onto the stack
+					ifdstack.push(ifdp);
+					// jump to the next entry
+					de++;
+					destack.push(de);
+
+					// push our current metadata model
+					modelstack.push(md_model);
+
+					// push new state onto of stack to cause a jump
+					ifdstack.push(next_ifd);
+					destack.push(0);
+
+					// select a new metadata model
+					modelstack.push(next_mdmodel);
+					
+					// delete the tag as it won't be stored nor deleted in the for() loop
+					FreeImage_DeleteTag(tag);
+					
+					break; // break out of the for loop
+				}
+				else {
+					// unsupported camera model, canon maker tag or something unknown
+					// process as a standard tag
+					processExifTag(dib, tag, pval, msb_order, md_model);
+				}			
+
+			} else {
+				// process as a standard tag
+				processExifTag(dib, tag, pval, msb_order, md_model);
+			}
+			
+			// delete the tag
+			FreeImage_DeleteTag(tag);
+
+        } // for(nde)
+
+		// additional thumbnail data is skipped
+
+    } while (!destack.empty()); 
+
+	//
+	// --- handle thumbnail data ---
+	//
+
+	const WORD entriesCount0th = ReadUint16(msb_order, ifd0th);
+	
+	DWORD next_offset = ReadUint32(msb_order, DIR_ENTRY_ADDR(ifd0th, entriesCount0th));
+	if((next_offset == 0) || (next_offset >= dwLength)) {
+		return TRUE; //< no thumbnail
+	}
+	
+	const BYTE* const ifd1st = (BYTE*)tiffp + next_offset;
+	const WORD entriesCount1st = ReadUint16(msb_order, ifd1st);
+	
+	unsigned thCompression = 0;
+	unsigned thOffset = 0; 
+	unsigned thSize = 0; 
+	
+	for(int e = 0; e < entriesCount1st; e++) {
+
+		// point to the directory entry
+		const BYTE* base = DIR_ENTRY_ADDR(ifd1st, e);
+		
+		// check for buffer overflow
+		const size_t remaining = (size_t)base + 12 - (size_t)tiffp;
+		if(remaining >= dwLength) {
+			// bad IFD1 directory, ignore it
+			return FALSE;
+		}
+
+		// get the tag ID
+		WORD tag = ReadUint16(msb_order, base);
+		// get the tag type
+		/*WORD type = */ReadUint16(msb_order, base + sizeof(WORD));
+		// get number of components
+		/*DWORD count = */ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD));
+		// get the tag value
+		DWORD offset = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD) + sizeof(DWORD));
+
+		switch(tag) {
+			case TAG_COMPRESSION:
+				// Tiff Compression Tag (should be COMPRESSION_OJPEG (6), but is not always respected)
+				thCompression = offset;
+				break;
+			case TAG_JPEG_INTERCHANGE_FORMAT:
+				// Tiff JPEGInterchangeFormat Tag
+				thOffset = offset;
+				break;
+			case TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
+				// Tiff JPEGInterchangeFormatLength Tag
+				thSize = offset;
+				break;
+			// ### X and Y Resolution ignored, orientation ignored
+			case TAG_X_RESOLUTION:		// XResolution
+			case TAG_Y_RESOLUTION:		// YResolution
+			case TAG_RESOLUTION_UNIT:	// ResolutionUnit
+			case TAG_ORIENTATION:		// Orientation
+				break;
+			default:
+				break;
+		}
+	}
+	
+	if(/*thCompression != 6 ||*/ thOffset == 0 || thSize == 0) {
+		return TRUE;
+	}
+	
+	if(thOffset + thSize > dwLength) {
+		return TRUE;
+	}
+	
+	// load the thumbnail
+
+	const BYTE *thLocation = tiffp + thOffset;
+	
+	FIMEMORY* hmem = FreeImage_OpenMemory(const_cast<BYTE*>(thLocation), thSize);
+	FIBITMAP* thumbnail = FreeImage_LoadFromMemory(FIF_JPEG, hmem);
+	FreeImage_CloseMemory(hmem);
+	
+	// store the thumbnail
+	FreeImage_SetThumbnail(dib, thumbnail);
+	// then delete it
+	FreeImage_Unload(thumbnail);
+
+	return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Read and decode JPEG_APP1 marker (Exif profile)
+@param dib Input FIBITMAP
+@param data Pointer to the APP1 marker
+@param length APP1 marker length
+@return Returns TRUE if successful, FALSE otherwise
+*/
+BOOL  
+jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *data, unsigned length) {
+    // marker identifying string for Exif = "Exif\0\0"
+    BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
+	BYTE lsb_first[4] = { 0x49, 0x49, 0x2A, 0x00 };		// Classic TIFF signature - little-endian order
+	BYTE msb_first[4] = { 0x4D, 0x4D, 0x00, 0x2A };		// Classic TIFF signature - big-endian order
+
+	// profile size is up to 32-bit
+	DWORD dwProfileLength = (DWORD)length;
+	BYTE *pbProfile = (BYTE*)data;
+
+	// verify the identifying string
+	if(memcmp(exif_signature, pbProfile, sizeof(exif_signature)) == 0) {
+		// This is an Exif profile
+		// should contain a TIFF header with up to 2 IFDs (IFD stands for 'Image File Directory')
+		// 0th IFD : the image attributes, 1st IFD : may be used for thumbnail
+
+		pbProfile += sizeof(exif_signature);
+		dwProfileLength -= sizeof(exif_signature);
+
+		// read the TIFF header (8 bytes)
+
+		// check the endianess order
+		
+		BOOL bBigEndian = TRUE;
+
+		if(memcmp(pbProfile, lsb_first, sizeof(lsb_first)) == 0) {
+			// Exif section is in little-endian order
+			bBigEndian = FALSE;
+		} else {
+			if(memcmp(pbProfile, msb_first, sizeof(msb_first)) == 0) {
+				// Exif section is in big-endian order
+				bBigEndian = TRUE;
+			} else {
+				// Invalid Exif alignment marker
+				return FALSE;
+			}
+		}
+
+		// this is the offset to the first IFD (Image File Directory)
+		DWORD dwFirstOffset = ReadUint32(bBigEndian, pbProfile + 4);
+		if (dwFirstOffset > dwProfileLength) {
+			// bad Exif data
+			return FALSE;
+		}
+
+		/*
+		Note: as FreeImage 3.14.0, this test is no longer needed for images with similar suspicious offset
+		=> tested with Pentax Optio 230, FujiFilm SP-2500 and Canon EOS 300D
+		if (dwFirstOffset < 8 || dwFirstOffset > 16) {
+			// This is usually set to 8
+			// but PENTAX Optio 230 has it set differently, and uses it as offset. 
+			FreeImage_OutputMessageProc(FIF_JPEG, "Exif: Suspicious offset of first IFD value");
+			return FALSE;
+		}
+		*/
+
+		// process Exif directories, starting with Exif-TIFF IFD
+		return jpeg_read_exif_dir(dib, pbProfile, dwFirstOffset, dwProfileLength, 0, bBigEndian, TagLib::EXIF_MAIN);
+	}
+
+	return FALSE;
+}
+
+// ==========================================================
+// Exif JPEG helper routines
+// ==========================================================
+
+/**
+Read JPEG_APP1 marker (Exif profile)
+@param dib Input FIBITMAP
+@param dataptr Pointer to the APP1 marker
+@param datalen APP1 marker length
+@return Returns TRUE if successful, FALSE otherwise
+*/
+BOOL  
+jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned length) {
+    // marker identifying string for Exif = "Exif\0\0"
+    BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
+
+	// verify the identifying string
+	if(memcmp(exif_signature, profile, sizeof(exif_signature)) != 0) {
+		// not an Exif profile
+		return FALSE;
+	}
+
+	// create a tag
+	FITAG *tag = FreeImage_CreateTag();
+	if(tag) {
+		FreeImage_SetTagKey(tag, g_TagLib_ExifRawFieldName);
+		FreeImage_SetTagLength(tag, (DWORD)length);
+		FreeImage_SetTagCount(tag, (DWORD)length);
+		FreeImage_SetTagType(tag, FIDT_BYTE);
+		FreeImage_SetTagValue(tag, profile);
+
+		// store the tag
+		FreeImage_SetMetadata(FIMD_EXIF_RAW, dib, FreeImage_GetTagKey(tag), tag);
+
+		// destroy the tag
+		FreeImage_DeleteTag(tag);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+// ==========================================================
+// Exif JPEG-XR helper routines
+// ==========================================================
+
+/**
+Read and decode JPEG-XR Exif IFD
+@param dib Input FIBITMAP
+@param profile Pointer to the Exif marker
+@param length Exif marker length
+@param file_offset Reference offset in the original file of each tag value whose length is > 4
+@return Returns TRUE if successful, FALSE otherwise
+*/
+BOOL  
+jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset) {
+	// assume Little Endian order
+	BOOL bBigEndian = FALSE;
+	
+	// process Exif specific IFD
+	return jpeg_read_exif_dir(dib, profile, 0, length, file_offset, bBigEndian, TagLib::EXIF_EXIF);
+}
+
+/**
+Read and decode JPEG-XR Exif-GPS IFD
+@param dib Input FIBITMAP
+@param profile Pointer to the Exif-GPS profile
+@param length Exif-GPS profile length
+@param file_offset Reference offset in the original file of each tag value whose length is > 4
+@return Returns TRUE if successful, FALSE otherwise
+*/
+BOOL  
+jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset) {
+	// assume Little Endian order
+	BOOL bBigEndian = FALSE;
+	
+	// process Exif GPS IFD
+	return jpeg_read_exif_dir(dib, profile, 0, length, file_offset, bBigEndian, TagLib::EXIF_GPS);
+}
+
+// ==========================================================
+// Exif common helper routines
+// ==========================================================
+
+/**
+Rotate a dib according to Exif info
+@param dib Input / Output dib to rotate
+@see PluginJPEG.cpp
+*/
+void 
+RotateExif(FIBITMAP **dib) {
+	// check for Exif rotation
+	if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, *dib)) {
+		FIBITMAP *rotated = NULL;
+		// process Exif rotation
+		FITAG *tag = NULL;
+		FreeImage_GetMetadata(FIMD_EXIF_MAIN, *dib, "Orientation", &tag);
+		if((tag != NULL) && (FreeImage_GetTagID(tag) == TAG_ORIENTATION)) {
+			const WORD orientation = *((WORD *)FreeImage_GetTagValue(tag));
+			switch (orientation) {
+				case 1:		// "top, left side" => 0°
+					break;
+				case 2:		// "top, right side" => flip left-right
+					FreeImage_FlipHorizontal(*dib);
+					break;
+				case 3:		// "bottom, right side" => -180°
+					rotated = FreeImage_Rotate(*dib, 180);
+					FreeImage_Unload(*dib);
+					*dib = rotated;
+					break;
+				case 4:		// "bottom, left side" => flip up-down
+					FreeImage_FlipVertical(*dib);
+					break;
+				case 5:		// "left side, top" => +90° + flip up-down
+					rotated = FreeImage_Rotate(*dib, 90);
+					FreeImage_Unload(*dib);
+					*dib = rotated;
+					FreeImage_FlipVertical(*dib);
+					break;
+				case 6:		// "right side, top" => -90°
+					rotated = FreeImage_Rotate(*dib, -90);
+					FreeImage_Unload(*dib);
+					*dib = rotated;
+					break;
+				case 7:		// "right side, bottom" => -90° + flip up-down
+					rotated = FreeImage_Rotate(*dib, -90);
+					FreeImage_Unload(*dib);
+					*dib = rotated;
+					FreeImage_FlipVertical(*dib);
+					break;
+				case 8:		// "left side, bottom" => +90°
+					rotated = FreeImage_Rotate(*dib, 90);
+					FreeImage_Unload(*dib);
+					*dib = rotated;
+					break;
+				default:
+					break;
+			}
+		}
+	}
+}
+
+// ==========================================================
+// Exif TIFF JPEG-XR helper routines
+// ==========================================================
+
+class PredicateTagIDCompare {
+public:
+	bool operator()(FITAG *a, FITAG *b) {
+		WORD tag_id_a = FreeImage_GetTagID(a);
+		WORD tag_id_b = FreeImage_GetTagID(b);
+		return (tag_id_a < tag_id_b);
+	}
+};
+
+/**
+Write a metadata model as a TIF IFD to a FIMEMORY handle.
+The entries in the TIF IFD are sorted in ascending order by tag id.	
+The last entry is written as 0 (4 bytes) which means no more IFD to follow. 
+Supported metadata models are
+<ul>
+<li>FIMD_EXIF_MAIN
+<li>FIMD_EXIF_EXIF
+<li>FIMD_EXIF_GPS
+<li>FIMD_EXIF_INTEROP
+</ul>
+The end of the buffer is filled with 4 bytes equal to 0 (end of IFD offset)
+
+@param dib Input FIBITMAP
+@param md_model Metadata model to write
+@param hmem Memory handle
+@return Returns TRUE if successful, FALSE otherwise
+@see tiff_get_ifd_profile
+*/
+static BOOL
+tiff_write_ifd(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, FIMEMORY *hmem) {
+	FITAG *tag = NULL;
+	FIMETADATA *mdhandle = NULL;
+	std::vector<FITAG*> vTagList;
+	TagLib::MDMODEL internal_md_model;
+
+	DWORD ifd_offset = 0;	// WORD-aligned IFD value offset
+
+	const BYTE empty_byte = 0;
+
+	// start of the file
+	const long start_of_file = FreeImage_TellMemory(hmem);
+
+	// get the metadata count
+	unsigned metadata_count = FreeImage_GetMetadataCount(md_model, dib);
+	if(metadata_count == 0) {
+		return FALSE;
+	}
+
+	TagLib& s = TagLib::instance();
+
+	// check for supported metadata models
+	switch(md_model) {
+		case FIMD_EXIF_MAIN:
+			internal_md_model = TagLib::EXIF_MAIN;
+			break;
+		case FIMD_EXIF_EXIF:
+			internal_md_model = TagLib::EXIF_EXIF;
+			break;
+		case FIMD_EXIF_GPS:
+			internal_md_model = TagLib::EXIF_GPS;
+			break;
+		case FIMD_EXIF_INTEROP:
+			internal_md_model = TagLib::EXIF_INTEROP;
+			break;
+		default:
+			return FALSE;
+	}
+
+	try {
+		// 1) according to the TIFF specifications, 
+		// the entries in a TIF IFD must be sorted in ascending order by tag id
+
+		// store the tags into a vector
+		vTagList.reserve(metadata_count);
+		mdhandle = FreeImage_FindFirstMetadata(md_model, dib, &tag);
+		if(mdhandle) {
+			// parse the tags and store them inside vTagList
+			do {
+				// rewrite the tag id using FreeImage internal database
+				// (in case the tag id is wrong or missing)
+				const char *key = FreeImage_GetTagKey(tag);
+				int tag_id = s.getTagID(internal_md_model, key);
+				if(tag_id != -1) {
+					// this is a known tag, set the tag ID
+					FreeImage_SetTagID(tag, (WORD)tag_id);
+					// record the tag
+					vTagList.push_back(tag);
+				}
+				// else ignore this tag
+			} while(FreeImage_FindNextMetadata(mdhandle, &tag));
+
+			FreeImage_FindCloseMetadata(mdhandle);
+
+			// sort the vector by tag id
+			std::sort(vTagList.begin(), vTagList.end(), PredicateTagIDCompare());
+
+			// update the metadata_count
+			metadata_count = (unsigned)vTagList.size();
+
+		} else {
+			throw(1);
+		}
+
+		// 2) prepare the place for each IFD entries.
+
+		/*
+		An Image File Directory (IFD) consists of a 2-byte count of the number of directory entries (i.e., the number of fields), 
+		followed by a sequence of 12-byte field entries, 
+		followed by a 4-byte offset of the next IFD (or 0 if none). Do not forget to write the 4 bytes of 0 after the last IFD.
+		*/
+
+		{		
+			// prepare place for 2 bytes for number of entries + 12 bytes for each entry
+			unsigned ifd_size = 2 + 12 * metadata_count;
+			FreeImage_WriteMemory(&empty_byte, 1, ifd_size, hmem);
+			// record the offset used to write values > 4-bytes
+			ifd_offset = FreeImage_TellMemory(hmem);
+			// rewind
+			FreeImage_SeekMemory(hmem, start_of_file, SEEK_SET);
+		}
+
+		// 3) write each IFD entry in tag id ascending order
+
+		// number of directory entries
+		WORD nde = (WORD)metadata_count;
+		FreeImage_WriteMemory(&nde, 1, 2, hmem);
+
+		// for each entry ...
+		for(unsigned i = 0; i < metadata_count; i++) {
+			FITAG *tag = vTagList[i];
+			// tag id
+			WORD tag_id = FreeImage_GetTagID(tag);
+			FreeImage_WriteMemory(&tag_id, 1, 2, hmem);
+			// tag type (compliant with TIFF specification)
+			WORD tag_type = (WORD)FreeImage_GetTagType(tag);
+			FreeImage_WriteMemory(&tag_type, 1, 2, hmem);
+			// tag count
+			DWORD tag_count = FreeImage_GetTagCount(tag);
+			FreeImage_WriteMemory(&tag_count, 1, 4, hmem);
+			// tag value or offset (results are in BYTE's units)
+			unsigned tag_length = FreeImage_GetTagLength(tag);
+			if(tag_length <= 4) {
+				// 4 bytes or less, write the value (left justified)
+				const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag);
+				FreeImage_WriteMemory(tag_value, 1, tag_length, hmem);
+				for(unsigned k = tag_length; k < 4; k++) {
+					FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
+				}
+			} else {
+				// write an offset
+				FreeImage_WriteMemory(&ifd_offset, 1, 4, hmem);
+				// write the value
+				long current_position = FreeImage_TellMemory(hmem);
+				FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
+				FreeImage_WriteMemory(FreeImage_GetTagValue(tag), 1, tag_length, hmem);
+				if(tag_length & 1) {
+					// align to the next WORD boundary
+					FreeImage_WriteMemory(&empty_byte, 1, 1, hmem);
+				}
+				// next offset to use
+				ifd_offset = FreeImage_TellMemory(hmem);
+				// rewind
+				FreeImage_SeekMemory(hmem, current_position, SEEK_SET);
+			}
+		}
+
+		// end-of-IFD or next IFD (0 == none)
+		FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET);
+		FreeImage_WriteMemory(&empty_byte, 1, 4, hmem);
+
+		return TRUE;
+	}
+	catch(int) {
+		return FALSE;
+	}
+}
+
+/**
+Write a metadata model as a TIF IFD, returns the IFD as a buffer.
+The buffer is allocated by the function and must be freed by the caller, using 'free'.
+@param dib Input FIBITMAP
+@param md_model Metadata model to write
+@param ppbProfile Returned buffer
+@param uProfileLength Returned buffer size
+@return Returns TRUE if successful, FALSE otherwise
+@see tiff_write_ifd
+*/
+BOOL
+tiff_get_ifd_profile(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, BYTE **ppbProfile, unsigned *uProfileLength) {
+	FIMEMORY *hmem = NULL;
+
+	try {
+		// open a memory stream
+		hmem = FreeImage_OpenMemory(NULL, 0);
+		if(!hmem) {
+			throw(1);
+		}
+
+		// write the metadata model as a TIF IFD
+		BOOL bResult = tiff_write_ifd(dib, md_model, hmem);
+
+		if(bResult) {
+			BYTE *data = NULL;
+			DWORD size_in_bytes = 0;
+
+			// get a pointer to the stream buffer
+			FreeImage_AcquireMemory(hmem, &data, &size_in_bytes);
+			
+			// (re-)allocate output buffer
+			BYTE *pbProfile = *ppbProfile;
+			pbProfile = (BYTE*)realloc(pbProfile, size_in_bytes);
+			if(!pbProfile) {
+				throw(1);
+			} else {
+				// copy IFD
+				memcpy(pbProfile, data, size_in_bytes);
+				*ppbProfile = pbProfile;
+				*uProfileLength = size_in_bytes;
+			}
+		}
+
+		// free the memory stream
+		FreeImage_CloseMemory(hmem);
+
+		return bResult;
+
+	} catch(int) {
+		FreeImage_CloseMemory(hmem);
+		return FALSE;
+	}
+}
diff --git a/files/Source/Metadata/FIRational.cpp b/files/Source/Metadata/FIRational.cpp
new file mode 100644
index 0000000..65651f1
--- /dev/null
+++ b/files/Source/Metadata/FIRational.cpp
@@ -0,0 +1,178 @@
+// ==========================================================
+// Helper class for rational numbers
+//
+// 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 "FIRational.h"
+
+#include <cmath>
+
+/// Initialize and normalize a rational number
+void FIRational::initialize(LONG n, LONG d) {
+	if(d) {
+		_numerator = n;
+		_denominator = d;
+		// normalize rational
+		normalize();
+	} else {
+		_numerator = 0;
+		_denominator = 0;
+	}
+}
+
+/// Default constructor
+FIRational::FIRational() {
+	_numerator = 0;
+	_denominator = 0;
+}
+
+/// Constructor with longs
+FIRational::FIRational(LONG n, LONG d) {
+	initialize(n, d);
+}
+
+/// Constructor with FITAG
+FIRational::FIRational(const FITAG *tag) {
+	switch(FreeImage_GetTagType((FITAG*)tag)) {
+		case FIDT_RATIONAL:		// 64-bit unsigned fraction 
+		{
+			DWORD *pvalue = (DWORD*)FreeImage_GetTagValue((FITAG*)tag);
+			initialize((LONG)pvalue[0], (LONG)pvalue[1]);
+			break;
+		}
+
+		case FIDT_SRATIONAL:	// 64-bit signed fraction 
+		{
+			LONG *pvalue = (LONG*)FreeImage_GetTagValue((FITAG*)tag);
+			initialize((LONG)pvalue[0], (LONG)pvalue[1]);
+			break;
+		}
+	}
+}
+
+FIRational::FIRational(float value) {
+	if (value == (float)((LONG)value)) {
+	   _numerator = (LONG)value;
+	   _denominator = 1L;
+	} else {
+		int k, count;
+		LONG n[4];
+
+		float x = fabs(value);
+		int sign = (value > 0) ? 1 : -1;
+
+		// make a continued-fraction expansion of x
+		count = -1;
+		for(k = 0; k < 4; k++) {
+                  n[k] = (LONG)std::floor(x);
+                  count++;
+                  x -= (float)n[k];
+                  if (x == 0) break;
+                  x = 1 / x;
+                }
+                // compute the rational
+		_numerator = 1;
+		_denominator = n[count];
+
+		for(int i = count - 1; i >= 0; i--) {
+			if(n[i] == 0) break;
+			LONG _num = (n[i] * _numerator + _denominator);
+			LONG _den = _numerator;
+			_numerator = _num;
+			_denominator = _den;
+		}
+		_numerator *= sign;
+	}
+}
+
+/// Copy constructor
+FIRational::FIRational (const FIRational& r) {
+	initialize(r._numerator, r._denominator);
+}
+
+/// Destructor
+FIRational::~FIRational() {
+}
+
+/// Assignement operator
+FIRational& FIRational::operator=(FIRational& r) {
+	if(this != &r) {
+		initialize(r._numerator, r._denominator);
+	}
+	return *this;
+}
+
+/// Get the numerator
+LONG FIRational::getNumerator() {
+	return _numerator;
+}
+
+/// Get the denominator
+LONG FIRational::getDenominator() {
+	return _denominator;
+}
+
+/// Calculate GCD
+LONG FIRational::gcd(LONG a, LONG b) {
+	LONG temp;
+	while (b) {		// While non-zero value
+		temp = b;	// Save current value
+		b = a % b;	// Assign remainder of division
+		a = temp;	// Copy old value
+	}
+	return a;		// Return GCD of numbers
+}
+
+/// Normalize numerator / denominator 
+void FIRational::normalize() {
+	if (_numerator != 1 && _denominator != 1) {	// Is there something to do?
+		 // Calculate GCD
+		LONG common = gcd(_numerator, _denominator);
+		if (common != 1) { // If GCD is not one			
+			_numerator /= common;	// Calculate new numerator
+			_denominator /= common;	// Calculate new denominator
+		}
+	}
+	if(_denominator < 0) {	// If sign is in denominator
+		_numerator *= -1;	// Multiply num and den by -1
+		_denominator *= -1;	// To keep sign in numerator
+	}
+}
+
+/// Checks if this rational number is an Integer, either positive or negative
+BOOL FIRational::isInteger() {
+	if(_denominator == 1 || (_denominator != 0 && (_numerator % _denominator == 0)) || (_denominator == 0 && _numerator == 0))
+		return TRUE;
+	return FALSE;
+}
+
+/// Convert as "numerator/denominator"
+std::string FIRational::toString() {
+	std::ostringstream s;
+	if(isInteger()) {
+		s << intValue();
+	} else {
+		s << _numerator << "/" << _denominator;
+	}
+	return s.str();
+}
+
+
diff --git a/files/Source/Metadata/FIRational.h b/files/Source/Metadata/FIRational.h
new file mode 100644
index 0000000..bd3ee13
--- /dev/null
+++ b/files/Source/Metadata/FIRational.h
@@ -0,0 +1,108 @@
+// ==========================================================
+// Helper class for rational numbers
+//
+// 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!
+// ==========================================================
+
+#ifndef FIRATIONAL_H
+#define FIRATIONAL_H
+
+/**
+Helper class to deal with rational numbers. 
+NB: LONG data type is assumed to be a signed 32-bit number. 
+*/
+class FIRational {
+private:
+	/// numerator
+	LONG _numerator;
+	/// denominator
+	LONG _denominator;
+
+public:
+	/// Default constructor
+	FIRational();
+
+	/// Constructor with longs
+	FIRational(LONG n, LONG d = 1);
+
+	/// Constructor with FITAG
+	FIRational(const FITAG *tag);
+
+	/// Constructor with a float
+	FIRational(float value);
+
+	/// Copy constructor
+	FIRational (const FIRational& r);
+
+	/// Destructor
+	~FIRational();
+
+	/// Assignement operator
+	FIRational& operator=(FIRational& r);
+
+	/// Get the numerator
+	LONG getNumerator();
+
+	/// Get the denominator
+	LONG getDenominator();
+
+	/// Converts rational value by truncating towards zero
+	LONG truncate() {
+		// Return truncated rational
+		return _denominator ? (LONG) (_numerator / _denominator) : 0;
+	}
+
+	/**@name Implicit conversions */
+	//@{	
+	short shortValue() {
+		return (short)truncate();
+	}
+	int intValue() {
+		return (int)truncate();
+	}
+	LONG longValue() {
+		return (LONG)truncate();
+	}
+	float floatValue() {
+		return _denominator ? ((float)_numerator)/((float)_denominator) : 0;
+	}
+	double doubleValue() {
+		return _denominator ? ((double)_numerator)/((double)_denominator) : 0;
+	}
+	//@}
+
+	/// Checks if this rational number is an integer, either positive or negative
+	BOOL isInteger();
+
+	/// Convert as "numerator/denominator"
+	std::string toString();
+
+private:
+	/// Initialize and normalize a rational number
+	void initialize(LONG n, LONG d);
+
+	/// Calculate GCD
+	LONG gcd(LONG a, LONG b);
+	
+	/// Normalize numerator / denominator 
+	void normalize();
+
+};
+
+#endif // FIRATIONAL_H
+
diff --git a/files/Source/Metadata/FreeImageTag.cpp b/files/Source/Metadata/FreeImageTag.cpp
new file mode 100644
index 0000000..cc12a5d
--- /dev/null
+++ b/files/Source/Metadata/FreeImageTag.cpp
@@ -0,0 +1,353 @@
+// ==========================================================
+// Tag manipulation functions
+//
+// 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!
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageTag.h"
+
+// --------------------------------------------------------------------------
+// FITAG header definition
+// --------------------------------------------------------------------------
+
+FI_STRUCT (FITAGHEADER) { 
+	char *key;			// tag field name
+	char *description;	// tag description
+	WORD id;			// tag ID
+	WORD type;			// tag data type (see FREE_IMAGE_MDTYPE)
+	DWORD count;		// number of components (in 'tag data types' units)
+	DWORD length;		// value length in bytes
+	void *value;		// tag value
+};
+
+// --------------------------------------------------------------------------
+// FITAG creation / destruction
+// --------------------------------------------------------------------------
+
+FITAG * DLL_CALLCONV 
+FreeImage_CreateTag() {
+	FITAG *tag = (FITAG *)malloc(sizeof(FITAG));
+
+	if (tag != NULL) {
+		unsigned tag_size = sizeof(FITAGHEADER); 
+		tag->data = (BYTE *)malloc(tag_size * sizeof(BYTE));
+		if (tag->data != NULL) {
+			memset(tag->data, 0, tag_size);
+			return tag;
+		}
+		free(tag);
+	}
+
+	return NULL;
+}
+
+void DLL_CALLCONV 
+FreeImage_DeleteTag(FITAG *tag) {
+	if (NULL != tag) {	
+		if (NULL != tag->data) {
+			FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+			// delete tag members
+			free(tag_header->key); 
+			free(tag_header->description); 
+			free(tag_header->value);
+			// delete the tag
+			free(tag->data);
+		}
+		// and the wrapper
+		free(tag);
+	}
+}
+
+FITAG * DLL_CALLCONV 
+FreeImage_CloneTag(FITAG *tag) {
+	if(!tag) return NULL;
+
+	// allocate a new tag
+	FITAG *clone = FreeImage_CreateTag();
+	if(!clone) return NULL;
+
+	try {
+		// copy the tag
+		FITAGHEADER *src_tag = (FITAGHEADER *)tag->data;
+		FITAGHEADER *dst_tag = (FITAGHEADER *)clone->data;
+
+		// tag ID
+		dst_tag->id = src_tag->id;
+		// tag key
+		if(src_tag->key) {
+			dst_tag->key = (char*)malloc((strlen(src_tag->key) + 1) * sizeof(char));
+			if(!dst_tag->key) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+			strcpy(dst_tag->key, src_tag->key);
+		}
+		// tag description
+		if(src_tag->description) {
+			dst_tag->description = (char*)malloc((strlen(src_tag->description) + 1) * sizeof(char));
+			if(!dst_tag->description) {
+				throw FI_MSG_ERROR_MEMORY;
+			}
+			strcpy(dst_tag->description, src_tag->description);
+		}
+		// tag data type
+		dst_tag->type = src_tag->type;
+		// tag count
+		dst_tag->count = src_tag->count;
+		// tag length
+		dst_tag->length = src_tag->length;
+		// tag value
+		switch(dst_tag->type) {
+			case FIDT_ASCII:
+				dst_tag->value = (BYTE*)malloc((src_tag->length + 1) * sizeof(BYTE));
+				if(!dst_tag->value) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+				memcpy(dst_tag->value, src_tag->value, src_tag->length);
+				((BYTE*)dst_tag->value)[src_tag->length] = 0;
+				break;
+			default:
+				dst_tag->value = (BYTE*)malloc(src_tag->length * sizeof(BYTE));
+				if(!dst_tag->value) {
+					throw FI_MSG_ERROR_MEMORY;
+				}
+				memcpy(dst_tag->value, src_tag->value, src_tag->length);
+				break;
+		}
+
+		return clone;
+
+	} catch(const char *message) {
+		FreeImage_DeleteTag(clone);
+		FreeImage_OutputMessageProc(FIF_UNKNOWN, message);
+		return NULL;
+	}
+}
+
+// --------------------------------------------------------------------------
+// FITAG getters / setters
+// --------------------------------------------------------------------------
+
+const char * DLL_CALLCONV 
+FreeImage_GetTagKey(FITAG *tag) {
+	return tag ? ((FITAGHEADER *)tag->data)->key : 0;
+}
+
+const char * DLL_CALLCONV 
+FreeImage_GetTagDescription(FITAG *tag) {
+	return tag ? ((FITAGHEADER *)tag->data)->description : 0;
+}
+
+WORD DLL_CALLCONV 
+FreeImage_GetTagID(FITAG *tag) {
+	return tag ? ((FITAGHEADER *)tag->data)->id : 0;
+}
+
+FREE_IMAGE_MDTYPE DLL_CALLCONV 
+FreeImage_GetTagType(FITAG *tag) {
+	return tag ? (FREE_IMAGE_MDTYPE)(((FITAGHEADER *)tag->data)->type) : FIDT_NOTYPE;
+}
+
+DWORD DLL_CALLCONV 
+FreeImage_GetTagCount(FITAG *tag) {
+	return tag ? ((FITAGHEADER *)tag->data)->count : 0;
+}
+
+DWORD DLL_CALLCONV 
+FreeImage_GetTagLength(FITAG *tag) {
+	return tag ? ((FITAGHEADER *)tag->data)->length : 0;
+}
+
+const void *DLL_CALLCONV 
+FreeImage_GetTagValue(FITAG *tag) {
+	return tag ? ((FITAGHEADER *)tag->data)->value : 0;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_SetTagKey(FITAG *tag, const char *key) {
+	if(tag && key) {
+		FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+		if(tag_header->key) free(tag_header->key);
+		tag_header->key = (char*)malloc(strlen(key) + 1);
+		strcpy(tag_header->key, key);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_SetTagDescription(FITAG *tag, const char *description) {
+	if(tag && description) {
+		FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+		if(tag_header->description) free(tag_header->description);
+		tag_header->description = (char*)malloc(strlen(description) + 1);
+		strcpy(tag_header->description, description);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_SetTagID(FITAG *tag, WORD id) {
+	if(tag) {
+		FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+		tag_header->id = id;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_SetTagType(FITAG *tag, FREE_IMAGE_MDTYPE type) {
+	if(tag) {
+		FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+		tag_header->type = (WORD)type;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_SetTagCount(FITAG *tag, DWORD count) {
+	if(tag) {
+		FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+		tag_header->count = count;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_SetTagLength(FITAG *tag, DWORD length) {
+	if(tag) {
+		FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+		tag_header->length = length;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_SetTagValue(FITAG *tag, const void *value) {
+	if(tag && value) {
+		FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+		// first, check the tag
+		if(tag_header->count * FreeImage_TagDataWidth((FREE_IMAGE_MDTYPE)tag_header->type) != tag_header->length) {
+			// invalid data count ?
+			return FALSE;
+		}
+
+		if(tag_header->value) {
+			free(tag_header->value);
+		}
+
+		switch(tag_header->type) {
+			case FIDT_ASCII:
+			{
+				tag_header->value = (char*)malloc((tag_header->length + 1) * sizeof(char));
+				if(!tag_header->value) {
+					return FALSE;
+				}
+				char *src_data = (char*)value;
+				char *dst_data = (char*)tag_header->value;
+				for(DWORD i = 0; i < tag_header->length; i++) {
+					dst_data[i] = src_data[i];
+				}
+				dst_data[tag_header->length] = '\0';
+			}
+			break;
+
+			default:
+				tag_header->value = malloc(tag_header->length * sizeof(BYTE));
+				if(!tag_header->value) {
+					return FALSE;
+				}
+				memcpy(tag_header->value, value, tag_header->length);
+				break;
+		}
+		return TRUE;
+	}
+	return FALSE;
+}
+
+
+// --------------------------------------------------------------------------
+// FITAG internal helper functions
+// --------------------------------------------------------------------------
+
+unsigned 
+FreeImage_TagDataWidth(FREE_IMAGE_MDTYPE type) {
+	static const unsigned format_bytes[] = { 
+		0, // FIDT_NOTYPE	= 0,	// placeholder 
+		1, // FIDT_BYTE		= 1,	// 8-bit unsigned integer 
+		1, // FIDT_ASCII	= 2,	// 8-bit bytes w/ last byte null 
+		2, // FIDT_SHORT	= 3,	// 16-bit unsigned integer 
+		4, // FIDT_LONG		= 4,	// 32-bit unsigned integer 
+		8, // FIDT_RATIONAL	= 5,	// 64-bit unsigned fraction 
+		1, // FIDT_SBYTE	= 6,	// 8-bit signed integer 
+		1, // FIDT_UNDEFINED= 7,	// 8-bit untyped data 
+		2, // FIDT_SSHORT	= 8,	// 16-bit signed integer 
+		4, // FIDT_SLONG	= 9,	// 32-bit signed integer 
+		8, // FIDT_SRATIONAL= 10,	// 64-bit signed fraction 
+		4, // FIDT_FLOAT	= 11,	// 32-bit IEEE floating point 
+		8, // FIDT_DOUBLE	= 12,	// 64-bit IEEE floating point 
+		4, // FIDT_IFD		= 13,	// 32-bit unsigned integer (offset) 
+		4, // FIDT_PALETTE	= 14	// 32-bit RGBQUAD 
+		0, // placeholder (15)
+		8, // FIDT_LONG8	= 16,	// 64-bit unsigned integer 
+		8, // FIDT_SLONG8	= 17,	// 64-bit signed integer
+		8  // FIDT_IFD8		= 18	// 64-bit unsigned integer (offset)
+	};
+
+	  return (type < (sizeof(format_bytes)/sizeof(format_bytes[0]))) ?
+		  format_bytes[type] : 0;
+}
+
+size_t 
+FreeImage_GetTagMemorySize(FITAG *tag) {
+	size_t size = 0;
+	if (tag) {
+		FITAGHEADER *tag_header = (FITAGHEADER *)tag->data;
+		size += sizeof(FITAG);
+		size += sizeof(FITAGHEADER);
+		if (tag_header->key) {
+			size += strlen(tag_header->key) + 1;
+		}
+		if (tag_header->description) {
+			size += strlen(tag_header->description) + 1;
+		}
+		if (tag_header->value) {
+			switch (tag_header->type) {
+				case FIDT_ASCII:
+					// for ASCII strings, the value of the count part of an ASCII tag entry includes the NULL.
+					// however, FreeImage adds another '\0' to be sure that this last character is present.
+					size += tag_header->length + 1;
+					break;
+				default:
+					size += tag_header->length;
+					break;
+			}
+		}
+	}
+	return size;
+}
diff --git a/files/Source/Metadata/FreeImageTag.h b/files/Source/Metadata/FreeImageTag.h
new file mode 100644
index 0000000..beb8bc8
--- /dev/null
+++ b/files/Source/Metadata/FreeImageTag.h
@@ -0,0 +1,500 @@
+// ==========================================================
+// Tag manipulation functions
+//
+// 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!
+// ==========================================================
+
+#ifndef FREEIMAGETAG_H
+#define FREEIMAGETAG_H
+
+// ==========================================================
+// Exif JPEG tags
+// ==========================================================
+
+// ----------------------------------------------------------
+// TIFF Rev. 6.0 Attribute Information Used in Exif
+// ----------------------------------------------------------
+
+// Tags relating to image data structure
+
+#define TAG_IMAGE_WIDTH					0x0100
+#define TAG_IMAGE_HEIGHT				0x0101
+#define TAG_BITS_PER_SAMPLE				0x0102
+#define TAG_COMPRESSION					0x0103
+#define TAG_PHOTOMETRIC_INTERPRETATION	0x0106
+#define TAG_ORIENTATION					0x0112
+#define TAG_SAMPLES_PER_PIXEL			0x0115
+#define TAG_PLANAR_CONFIGURATION		0x011C
+#define TAG_YCBCR_SUBSAMPLING			0x0212
+#define TAG_YCBCR_POSITIONING			0x0213
+#define TAG_X_RESOLUTION				0x011A
+#define TAG_Y_RESOLUTION				0x011B
+#define TAG_RESOLUTION_UNIT				0x0128
+
+// LibTIF compression modes
+
+#define	    TAG_COMPRESSION_NONE		1	/* dump mode */
+#define	    TAG_COMPRESSION_CCITTRLE	2	/* CCITT modified Huffman RLE */
+#define	    TAG_COMPRESSION_CCITTFAX3	3	/* CCITT Group 3 fax encoding */
+#define     TAG_COMPRESSION_CCITT_T4        3       /* CCITT T.4 (TIFF 6 name) */
+#define	    TAG_COMPRESSION_CCITTFAX4	4	/* CCITT Group 4 fax encoding */
+#define     TAG_COMPRESSION_CCITT_T6        4       /* CCITT T.6 (TIFF 6 name) */
+#define	    TAG_COMPRESSION_LZW		5       /* Lempel-Ziv  & Welch */
+#define	    TAG_COMPRESSION_OJPEG		6	/* !6.0 JPEG */
+#define	    TAG_COMPRESSION_JPEG		7	/* %JPEG DCT compression */
+#define	    TAG_COMPRESSION_NEXT		32766	/* NeXT 2-bit RLE */
+#define	    TAG_COMPRESSION_CCITTRLEW	32771	/* #1 w/ word alignment */
+#define	    TAG_COMPRESSION_PACKBITS	32773	/* Macintosh RLE */
+#define	    TAG_COMPRESSION_THUNDERSCAN	32809	/* ThunderScan RLE */
+/* codes 32895-32898 are reserved for ANSI IT8 TIFF/IT <dkelly@apago.com) */
+#define	    TAG_COMPRESSION_IT8CTPAD	32895   /* IT8 CT w/padding */
+#define	    TAG_COMPRESSION_IT8LW		32896   /* IT8 Linework RLE */
+#define	    TAG_COMPRESSION_IT8MP		32897   /* IT8 Monochrome picture */
+#define	    TAG_COMPRESSION_IT8BL		32898   /* IT8 Binary line art */
+/* compression codes 32908-32911 are reserved for Pixar */
+#define     TAG_COMPRESSION_PIXARFILM	32908   /* Pixar companded 10bit LZW */
+#define	    TAG_COMPRESSION_PIXARLOG	32909   /* Pixar companded 11bit ZIP */
+#define	    TAG_COMPRESSION_DEFLATE		32946	/* Deflate compression */
+#define     TAG_COMPRESSION_ADOBE_DEFLATE   8       /* Deflate compression,
+						   as recognized by Adobe */
+/* compression code 32947 is reserved for Oceana Matrix <dev@oceana.com> */
+#define     TAG_COMPRESSION_DCS             32947   /* Kodak DCS encoding */
+#define	    TAG_COMPRESSION_JBIG		34661	/* ISO JBIG */
+#define     TAG_COMPRESSION_SGILOG		34676	/* SGI Log Luminance RLE */
+#define     TAG_COMPRESSION_SGILOG24	34677	/* SGI Log 24-bit packed */
+#define     TAG_COMPRESSION_JP2000          34712   /* Leadtools JPEG2000 */
+#define	    TAG_COMPRESSION_LZMA		34925	/* LZMA2 */
+
+// Tags relating to recording offset
+
+#define TAG_STRIP_OFFSETS					0x0111
+#define TAG_ROWS_PER_STRIP					0x0116
+#define TAG_STRIP_BYTE_COUNTS				0x0117
+#define TAG_JPEG_INTERCHANGE_FORMAT			0x0201
+#define TAG_JPEG_INTERCHANGE_FORMAT_LENGTH	0x0202
+
+// Tags relating to image data characteristics
+
+#define TAG_TRANSFER_FUNCTION		0x012D
+#define TAG_WHITE_POINT				0x013E
+#define TAG_PRIMARY_CHROMATICITIES	0x013F
+#define TAG_YCBCR_COEFFICIENTS		0x0211
+#define TAG_REFERENCE_BLACK_WHITE	0x0214
+
+// Other tags
+
+#define TAG_DATETIME 				0x0132
+#define TAG_IMAGE_DESCRIPTION 		0x010E
+#define TAG_MAKE 					0x010F
+#define TAG_MODEL 					0x0110
+#define TAG_SOFTWARE 				0x0131
+#define TAG_ARTIST 					0x013B
+#define TAG_COPYRIGHT 				0x8298
+
+// ----------------------------------------------------------
+// Exif IFD Attribute Information
+// ----------------------------------------------------------
+
+// Tags relating to version
+
+#define TAG_EXIF_VERSION 			0x9000
+#define TAG_FLASHPIX_VERSION 		0xA000
+
+// Tag relating to image data characteristics
+
+#define TAG_COLOR_SPACE 			0xA001
+
+// Tags relating to image configuration
+
+#define TAG_COMPONENTS_CONFIGURATION	0x9101
+#define TAG_COMPRESSED_BITS_PER_PIXEL	0x9102
+#define TAG_PIXEL_X_DIMENSION			0xA002
+#define TAG_PIXEL_Y_DIMENSION			0xA003
+
+// Tags relating to user information
+
+#define TAG_MARKER_NOTE		0x927C
+#define TAG_USER_COMMENT	0x9286
+    
+// Tag relating to related file information
+
+#define TAG_RELATED_SOUND_FILE			0xA004
+
+// Tags relating to date and time
+
+#define TAG_DATETIME_ORIGINAL			0x9003
+#define TAG_DATETIME_DIGITIZED			0x9004
+#define TAG_SUBSECOND_TIME				0x9290
+#define TAG_SUBSECOND_TIME_ORIGINAL		0x9291
+#define TAG_SUBSECOND_TIME_DIGITIZED	0x9292
+
+// Tags relating to picture-taking conditions
+
+#define TAG_EXPOSURE_TIME				0x829A
+#define TAG_FNUMBER						0x829D
+#define TAG_EXPOSURE_PROGRAM			0x8822
+#define TAG_SPECTRAL_SENSITIVITY		0x8824
+#define TAG_ISO_SPEED_RATINGS 			0x8827
+#define TAG_OECF						0x8828
+#define TAG_SHUTTER_SPEED_VALUE 		0x9201
+#define TAG_APERTURE_VALUE 				0x9202
+#define TAG_BRIGHTNESS_VALUE			0x9203
+#define TAG_EXPOSURE_BIAS_VALUE 		0x9204
+#define TAG_MAX_APERTURE_VALUE 			0x9205
+#define TAG_SUBJECT_DISTANCE			0x9206
+#define TAG_METERING_MODE				0x9207
+#define TAG_LIGHT_SOURCE				0x9208
+#define TAG_FLASH						0x9209
+#define TAG_FOCAL_LENGTH				0x920A
+#define TAG_SUBJECT_AREA				0x9214
+#define TAG_FLASH_ENERGY				0xA20B
+#define TAG_SPATIAL_FREQ_RESPONSE 		0xA20C
+#define TAG_FOCAL_PLANE_X_RES			0xA20E
+#define TAG_FOCAL_PLANE_Y_RES			0xA20F
+#define TAG_FOCAL_PLANE_UNIT			0xA210
+#define TAG_SUBJECT_LOCATION 			0xA214
+#define TAG_EXPOSURE_INDEX				0xA215
+#define TAG_SENSING_METHOD				0xA217
+#define TAG_FILE_SOURCE					0xA300
+#define TAG_SCENE_TYPE					0xA301
+#define TAG_CFA_PATTERN					0xA302
+#define TAG_CUSTOM_RENDERED				0xA401
+#define TAG_EXPOSURE_MODE				0xA402
+#define TAG_WHITE_BALANCE				0xA403
+#define TAG_DIGITAL_ZOOM_RATIO			0xA404
+#define TAG_FOCAL_LENGTH_IN_35MM_FILM	0xA405
+#define TAG_SCENE_CAPTURE_TYPE			0xA406
+#define TAG_GAIN_CONTROL				0xA407
+#define TAG_CONTRAST					0xA408
+#define TAG_SATURATION					0xA409
+#define TAG_SHARPNESS					0xA40A
+#define TAG_DEVICE_SETTING_DESCRIPTION	0xA40B
+#define TAG_SUBJECT_DISTANCE_RANGE		0xA40C
+
+// Other tags
+
+#define TAG_IMAGE_UNIQUE_ID				0xA420
+
+// ----------------------------------------------------------
+// GPS Attribute Information
+// ----------------------------------------------------------
+
+#define TAG_GPS_VERSION_ID				0x0000
+#define TAG_GPS_LATITUDE_REF			0x0001
+#define TAG_GPS_LATITUDE				0x0002
+#define TAG_GPS_LONGITUDE_REF			0x0003
+#define TAG_GPS_LONGITUDE				0x0004
+#define TAG_GPS_ALTITUDE_REF			0x0005
+#define TAG_GPS_ALTITUDE				0x0006
+#define TAG_GPS_TIME_STAMP				0x0007
+#define TAG_GPS_SATELLITES				0x0008
+#define TAG_GPS_STATUS					0x0009
+#define TAG_GPS_MEASURE_MODE			0x000A
+#define TAG_GPS_DOP						0x000B
+#define TAG_GPS_SPEED_REF				0x000C
+#define TAG_GPS_SPEED					0x000D
+#define TAG_GPS_TRACK_REF				0x000E
+#define TAG_GPS_TRACK					0x000F
+#define TAG_GPS_IMG_DIRECTION_REF		0x0010
+#define TAG_GPS_IMG_DIRECTION			0x0011
+#define TAG_GPS_MAP_DATUM				0x0012
+#define TAG_GPS_DEST_LATITUDE_REF		0x0013
+#define TAG_GPS_DEST_LATITUDE			0x0014
+#define TAG_GPS_DEST_LONGITUDE_REF		0x0015
+#define TAG_GPS_DEST_LONGITUDE			0x0016
+#define TAG_GPS_DEST_BEARING_REF		0x0017
+#define TAG_GPS_DEST_BEARING			0x0018
+#define TAG_GPS_DEST_DISTANCE_REF		0x0019
+#define TAG_GPS_DEST_DISTANCE			0x001A
+#define TAG_GPS_PROCESSING_METHOD		0x001B
+#define TAG_GPS_AREA_INFORMATION		0x001C
+#define TAG_GPS_DATE_STAMP				0x001D
+#define TAG_GPS_DIFFERENTIAL			0x001E
+
+// ==========================================================
+// IPTC/NAA tags
+// ==========================================================
+
+#define TAG_RECORD_VERSION					0x0200
+#define TAG_CAPTION							0x0278
+#define TAG_WRITER							0x027A
+#define TAG_HEADLINE						0x0269
+#define TAG_SPECIAL_INSTRUCTIONS			0x0228
+#define TAG_BY_LINE							0x0250
+#define TAG_BY_LINE_TITLE					0x0255
+#define TAG_CREDIT							0x026E
+#define TAG_SOURCE							0x0273
+#define TAG_OBJECT_NAME						0x0205
+#define TAG_DATE_CREATED					0x0237
+#define TAG_CITY							0x025A
+#define TAG_PROVINCE_OR_STATE				0x025F
+#define TAG_COUNTRY_OR_PRIMARY_LOCATION		0x0265
+#define TAG_ORIGINAL_TRANSMISSION_REFERENCE 0x0267
+#define TAG_CATEGORY						0x020F
+#define TAG_SUPPLEMENTAL_CATEGORIES			0x0214
+#define TAG_URGENCY							0x020A
+#define TAG_KEYWORDS						0x0219
+#define TAG_COPYRIGHT_NOTICE				0x0274
+#define TAG_RELEASE_DATE					0x021E
+#define TAG_RELEASE_TIME					0x0223
+#define TAG_TIME_CREATED					0x023C
+#define TAG_ORIGINATING_PROGRAM				0x0241
+
+// ==========================================================
+// GeoTIFF tags
+// ==========================================================
+
+// tags 33550 is a private tag registered to SoftDesk, Inc
+#define TIFFTAG_GEOPIXELSCALE		33550
+// tags 33920-33921 are private tags registered to Intergraph, Inc
+#define TIFFTAG_INTERGRAPH_MATRIX	33920
+#define TIFFTAG_GEOTIEPOINTS		33922
+// tags 34263-34264 are private tags registered to NASA-JPL Carto Group
+#define TIFFTAG_JPL_CARTO_IFD		34263
+#define TIFFTAG_GEOTRANSMATRIX		34264    /* New Matrix Tag replaces 33920 */
+// tags 34735-3438 are private tags registered to SPOT Image, Inc
+#define TIFFTAG_GEOKEYDIRECTORY		34735
+#define TIFFTAG_GEODOUBLEPARAMS		34736
+#define TIFFTAG_GEOASCIIPARAMS		34737
+
+// ==========================================================
+// FreeImage Animation tags
+// ==========================================================
+
+#define ANIMTAG_LOGICALWIDTH	0x0001
+#define ANIMTAG_LOGICALHEIGHT	0x0002
+#define ANIMTAG_GLOBALPALETTE	0x0003
+#define ANIMTAG_LOOP			0x0004
+#define ANIMTAG_FRAMELEFT		0x1001
+#define ANIMTAG_FRAMETOP		0x1002
+#define ANIMTAG_NOLOCALPALETTE	0x1003
+#define ANIMTAG_INTERLACED		0x1004
+#define ANIMTAG_FRAMETIME		0x1005
+#define ANIMTAG_DISPOSALMETHOD	0x1006
+
+// --------------------------------------------------------------------------
+// Helper functions to deal with the FITAG structure
+// --------------------------------------------------------------------------
+
+/**
+Describes the tag format descriptor
+Given a FREE_IMAGE_MDTYPE, calculate the size of this type in bytes unit
+@param type Tag data type
+@return Returns the size of the data type, in bytes
+@see FREE_IMAGE_MDTYPE
+*/
+unsigned FreeImage_TagDataWidth(FREE_IMAGE_MDTYPE type);
+
+/**
+Calculate the memory size required by a tag, including the size of the structure
+@param tag The tag to examine
+@return Retuns the memory size used by a tag
+*/
+size_t FreeImage_GetTagMemorySize(FITAG *tag);
+
+// --------------------------------------------------------------------------
+
+/**
+	Structure to hold a tag information
+*/
+typedef struct tagTagInfo {
+	WORD tag;			// Tag ID (required)
+	char *fieldname;	// Field name (required)
+	char *description;	// Field description (may be NULL)
+} TagInfo;
+
+
+/**
+Class to hold tag information (based on MeyersÂ’ Singleton).<br>
+
+Sample usage :<br>
+<code>
+TagLib& s = TagLib::instance();
+TagInfo *tag_info = s.getTagInfo(EXIF_MAIN, 0x0100);
+</code>
+
+Note on multi-threaded applications : 
+
+The singleton pattern must be carefully constructed in multi-threaded applications. 
+If two threads are to execute the creation method at the same time when a singleton 
+does not yet exist, they both must check for an instance of the singleton and then 
+only one should create the new one.
+The classic solution to this problem is to use mutual exclusion on the class that 
+indicates that the object is being instantiated.
+The FreeImage solution is to instantiate the singleton before any other thread is launched, 
+i.e. inside the FreeImage_Initialise function (see Plugin.cpp). 
+*/
+
+class TagLib {
+public:
+
+	/**
+	internal tag info tables registered in TagLib
+	*/
+	enum MDMODEL {
+		UNKNOWN,
+		EXIF_MAIN, 
+		EXIF_EXIF, 
+		EXIF_GPS, 
+		EXIF_INTEROP,
+		EXIF_MAKERNOTE_CANON,
+		EXIF_MAKERNOTE_CASIOTYPE1,
+		EXIF_MAKERNOTE_CASIOTYPE2,
+		EXIF_MAKERNOTE_FUJIFILM,
+		EXIF_MAKERNOTE_KYOCERA,
+		EXIF_MAKERNOTE_MINOLTA,
+		EXIF_MAKERNOTE_NIKONTYPE1,
+		EXIF_MAKERNOTE_NIKONTYPE2,
+		EXIF_MAKERNOTE_NIKONTYPE3,
+		EXIF_MAKERNOTE_OLYMPUSTYPE1,
+		EXIF_MAKERNOTE_PANASONIC,
+		EXIF_MAKERNOTE_ASAHI,
+		EXIF_MAKERNOTE_PENTAX,
+		EXIF_MAKERNOTE_SONY,
+		EXIF_MAKERNOTE_SIGMA_SD1,
+		EXIF_MAKERNOTE_SIGMA_FOVEON,
+		IPTC,
+		GEOTIFF,
+		ANIMATION
+	};
+
+private:
+
+	typedef std::map<WORD, TagInfo*> TAGINFO;
+	typedef std::map<int, TAGINFO*>  TABLEMAP;
+
+	/// store hash tables for all known tag info tables
+	TABLEMAP _table_map;
+
+private:
+	/**
+	Constructor (private)<br>
+	This is where the tag info tables are initialized.
+	@see addMetadataModel
+	*/
+	TagLib();
+
+	/// Assignement operator (disabled)
+	void operator=(TagLib&);
+
+	/// Copy constructor (disabled)
+	TagLib(const TagLib&);
+	
+	/** 
+	Used in the constructor to initialize the tag tables
+	@param md_model Internal metadata model
+	@param tag_table Tag info table
+	@return Returns TRUE if successful, returns FALSE otherwise
+	*/
+	BOOL addMetadataModel(MDMODEL md_model, TagInfo *tag_table);
+
+public:
+	/// Destructor
+	~TagLib();
+
+	/**
+	@return Returns a reference to the TagLib instance
+	*/
+	static TagLib& instance();
+
+	/**
+	Given a tag ID, returns its TagInfo descriptor
+	@param md_model Internal metadata model
+	@param tagID tag ID
+	@return Returns the TagInfo descriptor if successful, returns NULL otherwise
+	*/
+	const TagInfo* getTagInfo(MDMODEL md_model, WORD tagID);
+
+	/**
+	Given a tag ID, returns its tag field name. 
+	When the tag is unknown and defaultKey is not NULL, a string such as "Tag 0x1234" is returned. 
+	This string is contained in the provided defaultKey buffer (assumed to be an array of at least 16 chars). 
+	@param md_model Internal metadata model
+	@param tagID tag ID
+	@param defaultKey Assumed to be an array of 16 chars. If not NULL, build a key for unknown tags
+	@return Returns the tag field name if successful, returns an 'unknown tag' string contained in defaultKey otherwise
+	*/
+	const char* getTagFieldName(MDMODEL md_model, WORD tagID, char *defaultKey);
+
+	/**
+	Given a tag ID, returns its description. 
+	When the tag has no description, a NULL value is returned.
+	@param md_model Internal metadata model
+	@param tagID tag ID
+	@return Returns the tag description if successful, returns NULL otherwise
+	*/
+	const char* getTagDescription(MDMODEL md_model, WORD tagID);
+
+	/**
+	Given a tag field name, returns its tag ID. 
+	When the tag doesn't exists, a value '-1' is returned.
+	@param md_model Internal metadata model
+	@param key tag field name
+	@return Returns the tag ID if successful, returns -1 otherwise
+	*/
+	int getTagID(MDMODEL md_model, const char *key);
+
+	/**
+	Perform a conversion between internal metadata models and FreeImage public metadata models
+	@param md_model Internal metadata model
+	*/
+	FREE_IMAGE_MDMODEL getFreeImageModel(MDMODEL model);
+
+};
+
+// --------------------------------------------------------------------------
+// Constant strings
+// --------------------------------------------------------------------------
+
+/// Name of the XMP field
+static const char *g_TagLib_XMPFieldName = "XMLPacket";
+
+/// Name of the Exif raw field
+static const char *g_TagLib_ExifRawFieldName = "ExifRaw";
+
+// --------------------------------------------------------------------------
+// Metadata routines
+// --------------------------------------------------------------------------
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// JPEG / JPEG-XR Exif profile (see Exif.cpp)
+// --------------------------------------------------------------------------
+BOOL jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen);
+BOOL jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned length);
+BOOL jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset);
+BOOL jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset);
+
+BOOL tiff_get_ifd_profile(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, BYTE **ppbProfile, unsigned *uProfileLength);
+
+
+// JPEG / TIFF IPTC profile (see IPTC.cpp)
+// --------------------------------------------------------------------------
+BOOL read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen);
+BOOL write_iptc_profile(FIBITMAP *dib, BYTE **profile, unsigned *profile_size);
+
+#if defined(__cplusplus)
+}
+#endif
+
+
+#endif // FREEIMAGETAG_H
+
+
diff --git a/files/Source/Metadata/IPTC.cpp b/files/Source/Metadata/IPTC.cpp
new file mode 100644
index 0000000..bde718c
--- /dev/null
+++ b/files/Source/Metadata/IPTC.cpp
@@ -0,0 +1,342 @@
+// ==========================================================
+// Metadata functions implementation
+//
+// 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!
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageTag.h"
+
+// ----------------------------------------------------------
+//   IPTC JPEG / TIFF markers routines
+// ----------------------------------------------------------
+
+static const char* IPTC_DELIMITER = ";";	// keywords/supplemental category delimiter
+/**
+	Read and decode IPTC binary data
+*/
+BOOL 
+read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+	char defaultKey[16];
+	size_t length = datalen;
+	BYTE *profile = (BYTE*)dataptr;
+
+	const char *JPEG_AdobeCM_Tag = "Adobe_CM";
+
+	std::string Keywords;
+	std::string SupplementalCategory;
+
+	WORD tag_id;
+
+	if(!dataptr || (datalen == 0)) {
+		return FALSE;
+	}
+
+	if(datalen > 8) {
+		if(memcmp(JPEG_AdobeCM_Tag, dataptr, 8) == 0) {
+			// the "Adobe_CM" APP13 segment presumably contains color management information, 
+			// but the meaning of the data is currently unknown. 
+			// If anyone has an idea about what this means, please let me know.
+			return FALSE;
+		}
+	}
+
+
+	// create a tag
+
+	FITAG *tag = FreeImage_CreateTag();
+
+	TagLib& tag_lib = TagLib::instance();
+
+    // find start of the BIM portion of the binary data
+    size_t offset = 0;
+	while(offset < length - 1) {
+		if((profile[offset] == 0x1C) && (profile[offset+1] == 0x02))
+			break;
+		offset++;
+	}
+
+    // for each tag
+    while (offset < length) {
+
+        // identifies start of a tag
+        if (profile[offset] != 0x1c) {
+            break;
+        }
+        // we need at least five bytes left to read a tag
+        if ((offset + 5) >= length) {
+            break;
+        }
+
+        offset++;
+
+		int directoryType	= profile[offset++];
+        int tagType			= profile[offset++];;
+        int tagByteCount	= ((profile[offset] & 0xFF) << 8) | (profile[offset + 1] & 0xFF);
+        offset += 2;
+
+        if ((offset + tagByteCount) > length) {
+            // data for tag extends beyond end of iptc segment
+            break;
+        }
+
+		if(tagByteCount == 0) {
+			// go to next tag
+			continue;
+		}
+
+		// process the tag
+
+		tag_id = (WORD)(tagType | (directoryType << 8));
+
+		FreeImage_SetTagID(tag, tag_id);
+		FreeImage_SetTagLength(tag, tagByteCount);
+
+		// allocate a buffer to store the tag value
+		BYTE *iptc_value = (BYTE*)malloc((tagByteCount + 1) * sizeof(BYTE));
+		memset(iptc_value, 0, (tagByteCount + 1) * sizeof(BYTE));
+
+		// get the tag value
+
+		switch (tag_id) {
+			case TAG_RECORD_VERSION:
+			{
+				// short
+				FreeImage_SetTagType(tag, FIDT_SSHORT);
+				FreeImage_SetTagCount(tag, 1);
+				short *pvalue = (short*)&iptc_value[0];
+				*pvalue = (short)((profile[offset] << 8) | profile[offset + 1]);
+				FreeImage_SetTagValue(tag, pvalue);
+				break;
+			}
+
+			case TAG_RELEASE_DATE:
+			case TAG_DATE_CREATED:
+				// Date object
+			case TAG_RELEASE_TIME:
+			case TAG_TIME_CREATED:
+				// time
+			default:
+			{
+				// string
+				FreeImage_SetTagType(tag, FIDT_ASCII);
+				FreeImage_SetTagCount(tag, tagByteCount);
+				for(int i = 0; i < tagByteCount; i++) {
+					iptc_value[i] = profile[offset + i];
+				}
+				iptc_value[tagByteCount] = '\0';
+				FreeImage_SetTagValue(tag, (char*)&iptc_value[0]);
+				break;
+			}
+		}
+
+		if(tag_id == TAG_SUPPLEMENTAL_CATEGORIES) {
+			// concatenate the categories
+			if(SupplementalCategory.length() == 0) {
+				SupplementalCategory.append((char*)iptc_value);
+			} else {
+				SupplementalCategory.append(IPTC_DELIMITER);
+				SupplementalCategory.append((char*)iptc_value);
+			}
+		}
+		else if(tag_id == TAG_KEYWORDS) {
+			// concatenate the keywords
+			if(Keywords.length() == 0) {
+				Keywords.append((char*)iptc_value);
+			} else {
+				Keywords.append(IPTC_DELIMITER);
+				Keywords.append((char*)iptc_value);
+			}
+		}
+		else {
+			// get the tag key and description
+			const char *key = tag_lib.getTagFieldName(TagLib::IPTC, tag_id, defaultKey);
+			FreeImage_SetTagKey(tag, key);
+			const char *description = tag_lib.getTagDescription(TagLib::IPTC, tag_id);
+			FreeImage_SetTagDescription(tag, description);
+
+			// store the tag
+			if(key) {
+				FreeImage_SetMetadata(FIMD_IPTC, dib, key, tag);
+			}
+		}
+
+		free(iptc_value);
+
+        // next tag
+		offset += tagByteCount;
+
+    }
+
+	// store the 'keywords' tag
+	if(Keywords.length()) {
+		FreeImage_SetTagType(tag, FIDT_ASCII);
+		FreeImage_SetTagID(tag, TAG_KEYWORDS);
+		FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_KEYWORDS, defaultKey));
+		FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_KEYWORDS));
+		FreeImage_SetTagLength(tag, (DWORD)Keywords.length());
+		FreeImage_SetTagCount(tag, (DWORD)Keywords.length());
+		FreeImage_SetTagValue(tag, (char*)Keywords.c_str());
+		FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag);
+	}
+
+	// store the 'supplemental category' tag
+	if(SupplementalCategory.length()) {
+		FreeImage_SetTagType(tag, FIDT_ASCII);
+		FreeImage_SetTagID(tag, TAG_SUPPLEMENTAL_CATEGORIES);
+		FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES, defaultKey));
+		FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::IPTC, TAG_SUPPLEMENTAL_CATEGORIES));
+		FreeImage_SetTagLength(tag, (DWORD)SupplementalCategory.length());
+		FreeImage_SetTagCount(tag, (DWORD)SupplementalCategory.length());
+		FreeImage_SetTagValue(tag, (char*)SupplementalCategory.c_str());
+		FreeImage_SetMetadata(FIMD_IPTC, dib, FreeImage_GetTagKey(tag), tag);
+	}
+
+	// delete the tag
+
+	FreeImage_DeleteTag(tag);
+
+	return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+static BYTE* 
+append_iptc_tag(BYTE *profile, unsigned *profile_size, WORD id, DWORD length, const void *value) {
+	BYTE *buffer = NULL;
+
+	// calculate the new buffer size
+	size_t buffer_size = (5 + *profile_size + length) * sizeof(BYTE);
+	buffer = (BYTE*)malloc(buffer_size);
+	if(!buffer)
+		return NULL;
+
+	// add the header
+	buffer[0] = 0x1C;
+	buffer[1] = 0x02;
+	// add the tag type
+	buffer[2] = (BYTE)(id & 0x00FF);
+	// add the tag length
+	buffer[3] = (BYTE)(length >> 8);
+	buffer[4] = (BYTE)(length & 0xFF);
+	// add the tag value
+	memcpy(buffer + 5, (BYTE*)value, length);
+	// append the previous profile
+	if(NULL == profile)	{
+		*profile_size = (5 + length);
+	}
+	else {
+		memcpy(buffer + 5 + length, profile, *profile_size);
+		*profile_size += (5 + length);
+		free(profile);
+	}
+	
+	return buffer;
+}
+
+/**
+Encode IPTC metadata into a binary buffer. 
+The buffer is allocated by the function and must be freed by the caller. 
+*/
+BOOL 
+write_iptc_profile(FIBITMAP *dib, BYTE **profile, unsigned *profile_size) {
+	FITAG *tag = NULL;
+	FIMETADATA *mdhandle = NULL;
+
+	BYTE *buffer = NULL;
+	unsigned buffer_size = 0;
+
+	// parse all IPTC tags and rebuild a IPTC profile
+	mdhandle = FreeImage_FindFirstMetadata(FIMD_IPTC, dib, &tag);
+
+	if(mdhandle) {
+		do {
+			WORD tag_id	= FreeImage_GetTagID(tag);
+
+			// append the tag to the profile
+
+			switch(tag_id) {
+				case TAG_RECORD_VERSION:
+					// ignore (already handled)
+					break;
+
+				case TAG_SUPPLEMENTAL_CATEGORIES:
+				case TAG_KEYWORDS:
+					if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
+						std::string value = (const char*)FreeImage_GetTagValue(tag);
+
+						// split the tag value
+						std::vector<std::string> output;
+						std::string delimiter = IPTC_DELIMITER;		
+						
+						size_t offset = 0;
+						size_t delimiterIndex = 0;
+
+						delimiterIndex = value.find(delimiter, offset);
+						while (delimiterIndex != std::string::npos) {
+							output.push_back(value.substr(offset, delimiterIndex - offset));
+							offset += delimiterIndex - offset + delimiter.length();
+							delimiterIndex = value.find(delimiter, offset);
+						}
+						output.push_back(value.substr(offset));
+
+						// add as many tags as there are comma separated strings
+						for(int i = 0; i < (int)output.size(); i++) {
+							std::string& tag_value = output[i];
+							buffer = append_iptc_tag(buffer, &buffer_size, tag_id, (DWORD)tag_value.length(), tag_value.c_str());
+						}
+
+					}
+					break;
+
+				case TAG_URGENCY:
+					if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
+						DWORD length = 1;	// keep the first octet only
+						buffer = append_iptc_tag(buffer, &buffer_size, tag_id, length, FreeImage_GetTagValue(tag));
+					}
+					break;
+
+				default:
+					if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
+						DWORD length = FreeImage_GetTagLength(tag);	
+						buffer = append_iptc_tag(buffer, &buffer_size, tag_id, length, FreeImage_GetTagValue(tag));
+					}					
+					break;
+			}
+
+		} while(FreeImage_FindNextMetadata(mdhandle, &tag));
+		
+		FreeImage_FindCloseMetadata(mdhandle);
+
+		// add the DirectoryVersion tag
+		const short version = 0x0200;
+		buffer = append_iptc_tag(buffer, &buffer_size, TAG_RECORD_VERSION, sizeof(version), &version);
+		
+		*profile = buffer;
+		*profile_size = buffer_size;
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
diff --git a/files/Source/Metadata/TagConversion.cpp b/files/Source/Metadata/TagConversion.cpp
new file mode 100644
index 0000000..f058186
--- /dev/null
+++ b/files/Source/Metadata/TagConversion.cpp
@@ -0,0 +1,1094 @@
+// ==========================================================
+// Tag to string conversion functions
+//
+// 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!
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageTag.h"
+#include "FIRational.h"
+
+#define MAX_TEXT_EXTENT	512
+
+/**
+Convert a tag to a C string
+*/
+static const char* 
+ConvertAnyTag(FITAG *tag) {
+	char format[MAX_TEXT_EXTENT];
+	static std::string buffer;
+	DWORD i;
+
+	if(!tag)
+		return NULL;
+
+	buffer.erase();
+	
+	// convert the tag value to a string buffer
+
+	FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag);
+	DWORD tag_count = FreeImage_GetTagCount(tag);
+
+	switch(tag_type) {
+		case FIDT_BYTE:		// N x 8-bit unsigned integer 
+		{
+			BYTE *pvalue = (BYTE*)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%ld",	(LONG) pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " %ld",	(LONG) pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_SHORT:	// N x 16-bit unsigned integer 
+		{
+			unsigned short *pvalue = (unsigned short *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%hu", pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " %hu",	pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_LONG:		// N x 32-bit unsigned integer 
+		{
+			DWORD *pvalue = (DWORD *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%lu", pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " %lu",	pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_RATIONAL: // N x 64-bit unsigned fraction 
+		{
+			DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_SBYTE:	// N x 8-bit signed integer 
+		{
+			char *pvalue = (char*)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%ld",	(LONG) pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " %ld",	(LONG) pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_SSHORT:	// N x 16-bit signed integer 
+		{
+			short *pvalue = (short *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%hd", pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " %hd",	pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_SLONG:	// N x 32-bit signed integer 
+		{
+			LONG *pvalue = (LONG *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%ld", pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " %ld",	pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_SRATIONAL:// N x 64-bit signed fraction 
+		{
+			LONG *pvalue = (LONG*)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%ld/%ld", pvalue[0], pvalue[1]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " %ld/%ld", pvalue[2*i], pvalue[2*i+1]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_FLOAT:	// N x 32-bit IEEE floating point 
+		{
+			float *pvalue = (float *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%f", (double) pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, "%f", (double) pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_DOUBLE:	// N x 64-bit IEEE floating point 
+		{
+			double *pvalue = (double *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%f", pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, "%f", pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_IFD:		// N x 32-bit unsigned integer (offset) 
+		{
+			DWORD *pvalue = (DWORD *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%X", pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " %X",	pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+		case FIDT_PALETTE:	// N x 32-bit RGBQUAD 
+		{
+			RGBQUAD *pvalue = (RGBQUAD *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "(%d,%d,%d,%d)", pvalue[0].rgbRed, pvalue[0].rgbGreen, pvalue[0].rgbBlue, pvalue[0].rgbReserved);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, " (%d,%d,%d,%d)", pvalue[i].rgbRed, pvalue[i].rgbGreen, pvalue[i].rgbBlue, pvalue[i].rgbReserved);
+				buffer += format;
+			}
+			break;
+		}
+		
+		case FIDT_LONG8:	// N x 64-bit unsigned integer 
+		{
+			UINT64 *pvalue = (UINT64 *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%ld", pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, "%ld", pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+
+		case FIDT_IFD8:		// N x 64-bit unsigned integer (offset)
+		{
+			UINT64 *pvalue = (UINT64 *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%X", pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, "%X", pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+
+		case FIDT_SLONG8:	// N x 64-bit signed integer
+		{
+			INT64 *pvalue = (INT64 *)FreeImage_GetTagValue(tag);
+
+			sprintf(format, "%ld", pvalue[0]);
+			buffer += format;
+			for(i = 1; i < tag_count; i++) {
+				sprintf(format, "%ld", pvalue[i]);
+				buffer += format;
+			}
+			break;
+		}
+
+		case FIDT_ASCII:	// 8-bit bytes w/ last byte null 
+		case FIDT_UNDEFINED:// 8-bit untyped data 
+		default:
+		{
+			int max_size = MIN((int)FreeImage_GetTagLength(tag), (int)MAX_TEXT_EXTENT);
+			if(max_size == MAX_TEXT_EXTENT)
+				max_size--;
+			memcpy(format, (char*)FreeImage_GetTagValue(tag), max_size);
+			format[max_size] = '\0';
+			buffer += format;
+			break;
+		}
+	}
+
+	return buffer.c_str();
+}
+
+/**
+Convert a Exif tag to a C string
+*/
+static const char* 
+ConvertExifTag(FITAG *tag) {
+	char format[MAX_TEXT_EXTENT];
+	static std::string buffer;
+
+	if(!tag)
+		return NULL;
+
+	buffer.erase();
+
+	// convert the tag value to a string buffer
+
+	switch(FreeImage_GetTagID(tag)) {
+		case TAG_ORIENTATION:
+		{
+			unsigned short orientation = *((unsigned short *)FreeImage_GetTagValue(tag));
+			switch (orientation) {
+				case 1:
+					return "top, left side";
+				case 2:
+					return "top, right side";
+				case 3:
+					return "bottom, right side";
+				case 4:
+					return "bottom, left side";
+				case 5:
+					return "left side, top";
+				case 6:
+					return "right side, top";
+				case 7:
+					return "right side, bottom";
+				case 8:
+					return "left side, bottom";
+				default:
+					break;
+			}
+		}
+		break;
+
+		case TAG_REFERENCE_BLACK_WHITE:
+		{
+			DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag);
+			if(FreeImage_GetTagLength(tag) == 48) {
+				// reference black point value and reference white point value (ReferenceBlackWhite)
+				int blackR = 0, whiteR = 0, blackG = 0, whiteG = 0, blackB = 0, whiteB = 0;
+				if(pvalue[1])
+					blackR = (int)(pvalue[0] / pvalue[1]);
+				if(pvalue[3])
+					whiteR = (int)(pvalue[2] / pvalue[3]);
+				if(pvalue[5])
+					blackG = (int)(pvalue[4] / pvalue[5]);
+				if(pvalue[7])
+					whiteG = (int)(pvalue[6] / pvalue[7]);
+				if(pvalue[9])
+					blackB = (int)(pvalue[8] / pvalue[9]);
+				if(pvalue[11])
+					whiteB = (int)(pvalue[10] / pvalue[11]);
+
+				sprintf(format, "[%d,%d,%d] [%d,%d,%d]", blackR, blackG, blackB, whiteR, whiteG, whiteB);
+				buffer += format;
+				return buffer.c_str();
+			}
+
+		}
+		break;
+
+		case TAG_COLOR_SPACE:
+		{
+			unsigned short colorSpace = *((unsigned short *)FreeImage_GetTagValue(tag));
+			if (colorSpace == 1) {
+				return "sRGB";
+			} else if (colorSpace == 65535) {
+				return "Undefined";
+			} else {
+				return "Unknown";
+			}
+		}
+		break;
+
+		case TAG_COMPONENTS_CONFIGURATION:
+		{
+			const char *componentStrings[7] = {"", "Y", "Cb", "Cr", "R", "G", "B"};
+			BYTE *pvalue = (BYTE*)FreeImage_GetTagValue(tag);
+			for(DWORD i = 0; i < MIN((DWORD)4, FreeImage_GetTagCount(tag)); i++) {
+				int j = pvalue[i];
+				if(j > 0 && j < 7)
+					buffer += componentStrings[j];
+			}
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_COMPRESSED_BITS_PER_PIXEL:
+		{
+			FIRational r(tag);
+			buffer = r.toString();
+			if(buffer == "1")
+				buffer += " bit/pixel";
+			else 
+				buffer += " bits/pixel";
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_X_RESOLUTION:
+		case TAG_Y_RESOLUTION:
+		case TAG_FOCAL_PLANE_X_RES:
+		case TAG_FOCAL_PLANE_Y_RES:
+		case TAG_BRIGHTNESS_VALUE:
+		case TAG_EXPOSURE_BIAS_VALUE:
+		{
+			FIRational r(tag);
+			buffer = r.toString();
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_RESOLUTION_UNIT:
+		case TAG_FOCAL_PLANE_UNIT:
+		{
+			unsigned short resolutionUnit = *((unsigned short *)FreeImage_GetTagValue(tag));
+			switch (resolutionUnit) {
+				case 1:
+					return "(No unit)";
+				case 2:
+					return "inches";
+				case 3:
+					return "cm";
+				default:
+					break;
+			}
+		}
+		break;
+
+		case TAG_YCBCR_POSITIONING:
+		{
+			unsigned short yCbCrPosition = *((unsigned short *)FreeImage_GetTagValue(tag));
+			switch (yCbCrPosition) {
+				case 1:
+					return "Center of pixel array";
+				case 2:
+					return "Datum point";
+				default:
+					break;
+			}
+		} 
+		break;
+
+		case TAG_EXPOSURE_TIME:
+		{
+			FIRational r(tag);
+			buffer = r.toString();
+			buffer += " sec";
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_SHUTTER_SPEED_VALUE:
+		{
+			FIRational r(tag);
+			LONG apexValue = r.longValue();
+			LONG apexPower = 1 << apexValue;
+			sprintf(format, "1/%d sec", (int)apexPower);
+			buffer += format;
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_APERTURE_VALUE:
+		case TAG_MAX_APERTURE_VALUE:
+		{
+			FIRational r(tag);
+			double apertureApex = r.doubleValue();
+	        double rootTwo = sqrt((double)2);
+			double fStop = pow(rootTwo, apertureApex);
+			sprintf(format, "F%.1f", fStop);
+			buffer += format;
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_FNUMBER:
+		{
+			FIRational r(tag);
+			double fnumber = r.doubleValue();
+			sprintf(format, "F%.1f", fnumber);
+			buffer += format;
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_FOCAL_LENGTH:
+		{
+			FIRational r(tag);
+			double focalLength = r.doubleValue();
+			sprintf(format, "%.1f mm", focalLength);
+			buffer += format;
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_FOCAL_LENGTH_IN_35MM_FILM:
+		{
+			unsigned short focalLength = *((unsigned short *)FreeImage_GetTagValue(tag));
+			sprintf(format, "%hu mm", focalLength);
+			buffer += format;
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_FLASH:
+		{
+			unsigned short flash = *((unsigned short *)FreeImage_GetTagValue(tag));
+			switch(flash) {
+				case 0x0000:
+					return "Flash did not fire";
+				case 0x0001:
+					return "Flash fired";
+				case 0x0005:
+					return "Strobe return light not detected";
+				case 0x0007:
+					return "Strobe return light detected";
+				case 0x0009:
+					return "Flash fired, compulsory flash mode";
+				case 0x000D:
+					return "Flash fired, compulsory flash mode, return light not detected";
+				case 0x000F:
+					return "Flash fired, compulsory flash mode, return light detected";
+				case 0x0010:
+					return "Flash did not fire, compulsory flash mode";
+				case 0x0018:
+					return "Flash did not fire, auto mode";
+				case 0x0019:
+					return "Flash fired, auto mode";
+				case 0x001D:
+					return "Flash fired, auto mode, return light not detected";
+				case 0x001F:
+					return "Flash fired, auto mode, return light detected";
+				case 0x0020:
+					return "No flash function";
+				case 0x0041:
+					return "Flash fired, red-eye reduction mode";
+				case 0x0045:
+					return "Flash fired, red-eye reduction mode, return light not detected";
+				case 0x0047:
+					return "Flash fired, red-eye reduction mode, return light detected";
+				case 0x0049:
+					return "Flash fired, compulsory flash mode, red-eye reduction mode";
+				case 0x004D:
+					return "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected";
+				case 0x004F:
+					return "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected";
+				case 0x0059:
+					return "Flash fired, auto mode, red-eye reduction mode";
+				case 0x005D:
+					return "Flash fired, auto mode, return light not detected, red-eye reduction mode";
+				case 0x005F:
+					return "Flash fired, auto mode, return light detected, red-eye reduction mode";
+				default:
+					sprintf(format, "Unknown (%d)", flash);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_SCENE_TYPE:
+		{
+			BYTE sceneType = *((BYTE*)FreeImage_GetTagValue(tag));
+			if (sceneType == 1) {
+				return "Directly photographed image";
+			} else {
+				sprintf(format, "Unknown (%d)", sceneType);
+				buffer += format;
+				return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_SUBJECT_DISTANCE:
+		{
+			FIRational r(tag);
+			if(r.getNumerator() == 0xFFFFFFFF) {
+				return "Infinity";
+			} else if(r.getNumerator() == 0) {
+				return "Distance unknown";
+			} else {
+				double distance = r.doubleValue();
+				sprintf(format, "%.3f meters", distance);
+				buffer += format;
+				return buffer.c_str();
+			}
+		}
+		break;
+			
+		case TAG_METERING_MODE:
+		{
+			unsigned short meteringMode = *((unsigned short *)FreeImage_GetTagValue(tag));
+			switch (meteringMode) {
+				case 0:
+					return "Unknown";
+				case 1:
+					return "Average";
+				case 2:
+					return "Center weighted average";
+				case 3:
+					return "Spot";
+				case 4:
+					return "Multi-spot";
+				case 5:
+					return "Multi-segment";
+				case 6:
+					return "Partial";
+				case 255:
+					return "(Other)";
+				default:
+					return "";
+			}
+		}
+		break;
+
+		case TAG_LIGHT_SOURCE:
+		{
+			unsigned short lightSource = *((unsigned short *)FreeImage_GetTagValue(tag));
+			switch (lightSource) {
+				case 0:
+					return "Unknown";
+				case 1:
+					return "Daylight";
+				case 2:
+					return "Fluorescent";
+				case 3:
+					return "Tungsten (incandescent light)";
+				case 4:
+					return "Flash";
+				case 9:
+					return "Fine weather";
+				case 10:
+					return "Cloudy weather";
+				case 11:
+					return "Shade";
+				case 12:
+					return "Daylight fluorescent (D 5700 - 7100K)";
+				case 13:
+					return "Day white fluorescent (N 4600 - 5400K)";
+				case 14:
+					return "Cool white fluorescent (W 3900 - 4500K)";
+				case 15:
+					return "White fluorescent (WW 3200 - 3700K)";
+				case 17:
+					return "Standard light A";
+				case 18:
+					return "Standard light B";
+				case 19:
+					return "Standard light C";
+				case 20:
+					return "D55";
+				case 21:
+					return "D65";
+				case 22:
+					return "D75";
+				case 23:
+					return "D50";
+				case 24:
+					return "ISO studio tungsten";
+				case 255:
+					return "(Other)";
+				default:
+					return "";
+			}
+		}
+		break;
+
+		case TAG_SENSING_METHOD:
+		{
+			unsigned short sensingMethod = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (sensingMethod) {
+				case 1:
+					return "(Not defined)";
+				case 2:
+					return "One-chip color area sensor";
+				case 3:
+					return "Two-chip color area sensor";
+				case 4:
+					return "Three-chip color area sensor";
+				case 5:
+					return "Color sequential area sensor";
+				case 7:
+					return "Trilinear sensor";
+				case 8:
+					return "Color sequential linear sensor";
+				default:
+					return "";
+			}
+		}
+		break;
+
+		case TAG_FILE_SOURCE:
+		{
+			BYTE fileSource = *((BYTE*)FreeImage_GetTagValue(tag));
+			if (fileSource == 3) {
+				return "Digital Still Camera (DSC)";
+			} else {
+				sprintf(format, "Unknown (%d)", fileSource);
+				buffer += format;
+				return buffer.c_str();
+			}
+        }
+		break;
+
+		case TAG_EXPOSURE_PROGRAM:
+		{
+			unsigned short exposureProgram = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (exposureProgram) {
+				case 1:
+					return "Manual control";
+				case 2:
+					return "Program normal";
+				case 3:
+					return "Aperture priority";
+				case 4:
+					return "Shutter priority";
+				case 5:
+					return "Program creative (slow program)";
+				case 6:
+					return "Program action (high-speed program)";
+				case 7:
+					return "Portrait mode";
+				case 8:
+					return "Landscape mode";
+				default:
+					sprintf(format, "Unknown program (%d)", exposureProgram);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_CUSTOM_RENDERED:
+		{
+			unsigned short customRendered = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (customRendered) {
+				case 0:
+					return "Normal process";
+				case 1:
+					return "Custom process";
+				default:
+					sprintf(format, "Unknown rendering (%d)", customRendered);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_EXPOSURE_MODE:
+		{
+			unsigned short exposureMode = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (exposureMode) {
+				case 0:
+					return "Auto exposure";
+				case 1:
+					return "Manual exposure";
+				case 2:
+					return "Auto bracket";
+				default:
+					sprintf(format, "Unknown mode (%d)", exposureMode);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_WHITE_BALANCE:
+		{
+			unsigned short whiteBalance = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (whiteBalance) {
+				case 0:
+					return "Auto white balance";
+				case 1:
+					return "Manual white balance";
+				default:
+					sprintf(format, "Unknown (%d)", whiteBalance);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_SCENE_CAPTURE_TYPE:
+		{
+			unsigned short sceneType = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (sceneType) {
+				case 0:
+					return "Standard";
+				case 1:
+					return "Landscape";
+				case 2:
+					return "Portrait";
+				case 3:
+					return "Night scene";
+				default:
+					sprintf(format, "Unknown (%d)", sceneType);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_GAIN_CONTROL:
+		{
+			unsigned short gainControl = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (gainControl) {
+				case 0:
+					return "None";
+				case 1:
+					return "Low gain up";
+				case 2:
+					return "High gain up";
+				case 3:
+					return "Low gain down";
+				case 4:
+					return "High gain down";
+				default:
+					sprintf(format, "Unknown (%d)", gainControl);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_CONTRAST:
+		{
+			unsigned short contrast = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (contrast) {
+				case 0:
+					return "Normal";
+				case 1:
+					return "Soft";
+				case 2:
+					return "Hard";
+				default:
+					sprintf(format, "Unknown (%d)", contrast);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_SATURATION:
+		{
+			unsigned short saturation = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (saturation) {
+				case 0:
+					return "Normal";
+				case 1:
+					return "Low saturation";
+				case 2:
+					return "High saturation";
+				default:
+					sprintf(format, "Unknown (%d)", saturation);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_SHARPNESS:
+		{
+			unsigned short sharpness = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (sharpness) {
+				case 0:
+					return "Normal";
+				case 1:
+					return "Soft";
+				case 2:
+					return "Hard";
+				default:
+					sprintf(format, "Unknown (%d)", sharpness);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_SUBJECT_DISTANCE_RANGE:
+		{
+			unsigned short distanceRange = *((unsigned short *)FreeImage_GetTagValue(tag));
+
+			switch (distanceRange) {
+				case 0:
+					return "unknown";
+				case 1:
+					return "Macro";
+				case 2:
+					return "Close view";
+				case 3:
+					return "Distant view";
+				default:
+					sprintf(format, "Unknown (%d)", distanceRange);
+					buffer += format;
+					return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_ISO_SPEED_RATINGS:
+		{
+			unsigned short isoEquiv = *((unsigned short *)FreeImage_GetTagValue(tag));
+			if (isoEquiv < 50) {
+				isoEquiv *= 200;
+			}
+			sprintf(format, "%d", isoEquiv);
+			buffer += format;
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_USER_COMMENT:
+		{
+			// first 8 bytes are used to define an ID code
+			// we assume this is an ASCII string
+			const BYTE *userComment = (BYTE*)FreeImage_GetTagValue(tag);
+			for(DWORD i = 8; i < FreeImage_GetTagLength(tag); i++) {
+				buffer += userComment[i];
+			}
+			buffer += '\0';
+			return buffer.c_str();
+		}
+		break;
+
+		case TAG_COMPRESSION:
+		{
+			WORD compression = *((WORD*)FreeImage_GetTagValue(tag));
+			switch(compression) {
+				case TAG_COMPRESSION_NONE:
+					sprintf(format, "dump mode (%d)", compression);
+					break;
+				case TAG_COMPRESSION_CCITTRLE:
+					sprintf(format, "CCITT modified Huffman RLE (%d)", compression);
+					break;
+				case TAG_COMPRESSION_CCITTFAX3:
+					sprintf(format, "CCITT Group 3 fax encoding (%d)", compression);
+					break;
+				/*
+				case TAG_COMPRESSION_CCITT_T4:
+					sprintf(format, "CCITT T.4 (TIFF 6 name) (%d)", compression);
+					break;
+				*/
+				case TAG_COMPRESSION_CCITTFAX4:
+					sprintf(format, "CCITT Group 4 fax encoding (%d)", compression);
+					break;
+				/*
+				case TAG_COMPRESSION_CCITT_T6:
+					sprintf(format, "CCITT T.6 (TIFF 6 name) (%d)", compression);
+					break;
+				*/
+				case TAG_COMPRESSION_LZW:
+					sprintf(format, "LZW (%d)", compression);
+					break;
+				case TAG_COMPRESSION_OJPEG:
+					sprintf(format, "!6.0 JPEG (%d)", compression);
+					break;
+				case TAG_COMPRESSION_JPEG:
+					sprintf(format, "JPEG (%d)", compression);
+					break;
+				case TAG_COMPRESSION_NEXT:
+					sprintf(format, "NeXT 2-bit RLE (%d)", compression);
+					break;
+				case TAG_COMPRESSION_CCITTRLEW:
+					sprintf(format, "CCITTRLEW (%d)", compression);
+					break;
+				case TAG_COMPRESSION_PACKBITS:
+					sprintf(format, "PackBits Macintosh RLE (%d)", compression);
+					break;
+				case TAG_COMPRESSION_THUNDERSCAN:
+					sprintf(format, "ThunderScan RLE (%d)", compression);
+					break;
+				case TAG_COMPRESSION_PIXARFILM:
+					sprintf(format, "Pixar companded 10bit LZW (%d)", compression);
+					break;
+				case TAG_COMPRESSION_PIXARLOG:
+					sprintf(format, "Pixar companded 11bit ZIP (%d)", compression);
+					break;
+				case TAG_COMPRESSION_DEFLATE:
+					sprintf(format, "Deflate compression (%d)", compression);
+					break;
+				case TAG_COMPRESSION_ADOBE_DEFLATE:
+					sprintf(format, "Adobe Deflate compression (%d)", compression);
+					break;
+				case TAG_COMPRESSION_DCS:
+					sprintf(format, "Kodak DCS encoding (%d)", compression);
+					break;
+				case TAG_COMPRESSION_JBIG:
+					sprintf(format, "ISO JBIG (%d)", compression);
+					break;
+				case TAG_COMPRESSION_SGILOG:
+					sprintf(format, "SGI Log Luminance RLE (%d)", compression);
+					break;
+				case TAG_COMPRESSION_SGILOG24:
+					sprintf(format, "SGI Log 24-bit packed (%d)", compression);
+					break;
+				case TAG_COMPRESSION_JP2000:
+					sprintf(format, "Leadtools JPEG2000 (%d)", compression);
+					break;
+				case TAG_COMPRESSION_LZMA:
+					sprintf(format, "LZMA2 (%d)", compression);
+					break;
+				default:
+					sprintf(format, "Unknown type (%d)", compression);
+					break;
+			}
+
+			buffer += format;
+			return buffer.c_str();
+		}
+		break;
+	}
+
+	return ConvertAnyTag(tag);
+}
+
+/**
+Convert a Exif GPS tag to a C string
+*/
+static const char* 
+ConvertExifGPSTag(FITAG *tag) {
+	char format[MAX_TEXT_EXTENT];
+	static std::string buffer;
+
+	if(!tag)
+		return NULL;
+
+	buffer.erase();
+
+	// convert the tag value to a string buffer
+
+	switch(FreeImage_GetTagID(tag)) {
+		case TAG_GPS_LATITUDE:
+		case TAG_GPS_LONGITUDE:
+		case TAG_GPS_TIME_STAMP:
+		{
+			DWORD *pvalue = (DWORD*)FreeImage_GetTagValue(tag);
+			if(FreeImage_GetTagLength(tag) == 24) {
+				// dd:mm:ss or hh:mm:ss
+				int dd = 0, mm = 0;
+				double ss = 0;
+
+				// convert to seconds
+				if(pvalue[1])
+					ss += ((double)pvalue[0] / (double)pvalue[1]) * 3600;
+				if(pvalue[3])
+					ss += ((double)pvalue[2] / (double)pvalue[3]) * 60;
+				if(pvalue[5])
+					ss += ((double)pvalue[4] / (double)pvalue[5]);
+				
+				// convert to dd:mm:ss.ss
+				dd = (int)(ss / 3600);
+				mm = (int)(ss / 60) - dd * 60;
+				ss = ss - dd * 3600 - mm * 60;
+
+				sprintf(format, "%d:%d:%.2f", dd, mm, ss);
+				buffer += format;
+				return buffer.c_str();
+			}
+		}
+		break;
+
+		case TAG_GPS_VERSION_ID:
+		case TAG_GPS_LATITUDE_REF:
+		case TAG_GPS_LONGITUDE_REF:
+		case TAG_GPS_ALTITUDE_REF:
+		case TAG_GPS_ALTITUDE:
+		case TAG_GPS_SATELLITES:
+		case TAG_GPS_STATUS:
+		case TAG_GPS_MEASURE_MODE:
+		case TAG_GPS_DOP:
+		case TAG_GPS_SPEED_REF:
+		case TAG_GPS_SPEED:
+		case TAG_GPS_TRACK_REF:
+		case TAG_GPS_TRACK:
+		case TAG_GPS_IMG_DIRECTION_REF:
+		case TAG_GPS_IMG_DIRECTION:
+		case TAG_GPS_MAP_DATUM:
+		case TAG_GPS_DEST_LATITUDE_REF:
+		case TAG_GPS_DEST_LATITUDE:
+		case TAG_GPS_DEST_LONGITUDE_REF:
+		case TAG_GPS_DEST_LONGITUDE:
+		case TAG_GPS_DEST_BEARING_REF:
+		case TAG_GPS_DEST_BEARING:
+		case TAG_GPS_DEST_DISTANCE_REF:
+		case TAG_GPS_DEST_DISTANCE:
+		case TAG_GPS_PROCESSING_METHOD:
+		case TAG_GPS_AREA_INFORMATION:
+		case TAG_GPS_DATE_STAMP:
+		case TAG_GPS_DIFFERENTIAL:
+			break;
+	}
+
+	return ConvertAnyTag(tag);
+}
+
+// ==========================================================
+// Tag to string conversion function
+//
+
+const char* DLL_CALLCONV 
+FreeImage_TagToString(FREE_IMAGE_MDMODEL model, FITAG *tag, char *Make) {
+	switch(model) {
+		case FIMD_EXIF_MAIN:
+		case FIMD_EXIF_EXIF:
+			return ConvertExifTag(tag);
+
+		case FIMD_EXIF_GPS:
+			return ConvertExifGPSTag(tag);
+
+		case FIMD_EXIF_MAKERNOTE:
+			// We should use the Make string to select an appropriate conversion function
+			// TO DO ...
+			break;
+
+		case FIMD_EXIF_INTEROP:
+		default:
+			break;
+	}
+
+	return ConvertAnyTag(tag);
+}
+
diff --git a/files/Source/Metadata/TagLib.cpp b/files/Source/Metadata/TagLib.cpp
new file mode 100644
index 0000000..580ad6f
--- /dev/null
+++ b/files/Source/Metadata/TagLib.cpp
@@ -0,0 +1,1618 @@
+// ==========================================================
+// Tag library
+//
+// 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!
+// ==========================================================
+
+// ==========================================================
+// Implementation notes : 
+// ----------------------
+// The tag info tables declared in this file should probably 
+// be loaded from an XML file. 
+// This would allow internationalization features and also 
+// more extensibility. 
+// Maybe in a future release ? 
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageTag.h"
+
+/**
+ HOW-TO : add a new TagInfo table
+ --------------------------------------------------------------------------
+ 1) add a table identifier in the TagLib class definition (see enum MDMODEL)
+ 2) declare the tag table as static and use a 0/NULL value as last entry
+ 3) initialize the table in TagLib::TagLib
+ 4) provide a conversion in TagLib::getFreeImageModel
+*/
+
+// --------------------------------------------------------------------------
+// EXIF standard tags definition
+// --------------------------------------------------------------------------
+
+static TagInfo
+  exif_exif_tag_table[] =
+  {
+    {  0x0100, (char *) "ImageWidth", (char *) "Image width"},
+    {  0x0101, (char *) "ImageLength", (char *) "Image height"},
+    {  0x0102, (char *) "BitsPerSample", (char *) "Number of bits per component"},
+    {  0x0103, (char *) "Compression", (char *) "Compression scheme"},
+    {  0x0106, (char *) "PhotometricInterpretation", (char *) "Pixel composition"},
+    {  0x010A, (char *) "FillOrder", (char*) NULL},
+    {  0x010D, (char *) "DocumentName", (char *) NULL},
+    {  0x010E, (char *) "ImageDescription", (char *) "Image title"},
+    {  0x010F, (char *) "Make", (char *) "Image input equipment manufacturer"},
+    {  0x0110, (char *) "Model", (char *) "Image input equipment model"},
+    {  0x0111, (char *) "StripOffsets", (char *) "Image data location"},
+    {  0x0112, (char *) "Orientation", (char *) "Orientation of image"},
+    {  0x0115, (char *) "SamplesPerPixel", (char *) "Number of components"},
+    {  0x0116, (char *) "RowsPerStrip", (char *) "Number of rows per strip"},
+    {  0x0117, (char *) "StripByteCounts", (char *) "Bytes per compressed strip"},
+    {  0x011A, (char *) "XResolution", (char *) "Image resolution in width direction"},
+    {  0x011B, (char *) "YResolution", (char *) "Image resolution in height direction"},
+    {  0x011C, (char *) "PlanarConfiguration", (char *) "Image data arrangement"},
+	{  0x011D, (char *) "PageName", (char *) "Name of the page"},
+	{  0x011E, (char *) "XPosition", (char *) "X position of the image"},
+	{  0x011F, (char *) "YPosition", (char *) "Y position of the image"},
+    {  0x0128, (char *) "ResolutionUnit", (char *) "Unit of X and Y resolution"},
+	{  0x0129, (char *) "PageNumber", (char *) "Page number"},
+    {  0x012D, (char *) "TransferFunction", (char *) "Transfer function"},
+    {  0x0131, (char *) "Software", (char *) "Software used"},
+    {  0x0132, (char *) "DateTime", (char *) "File change date and time"},
+    {  0x013B, (char *) "Artist", (char *) "Person who created the image"},
+	{  0x013C, (char *) "HostComputer", (char *) "Host computer used to generate the image"},
+    {  0x013E, (char *) "WhitePoint", (char *) "White point chromaticity"},
+    {  0x013F, (char *) "PrimaryChromaticities", (char *) "Chromaticities of primaries"},
+    {  0x0156, (char *) "TransferRange", (char *) NULL},
+    {  0x0200, (char *) "JPEGProc", (char *) NULL},
+    {  0x0201, (char *) "JPEGInterchangeFormat", (char *) "Offset to JPEG SOI"},
+    {  0x0202, (char *) "JPEGInterchangeFormatLength", (char *) "Bytes of JPEG data"},
+    {  0x0211, (char *) "YCbCrCoefficients", (char *) "Color space transformation matrix coefficients"},
+    {  0x0212, (char *) "YCbCrSubSampling", (char *) "Subsampling ratio of Y to C"},
+    {  0x0213, (char *) "YCbCrPositioning", (char *) "Y and C positioning"},
+    {  0x0214, (char *) "ReferenceBlackWhite", (char *) "Pair of black and white reference values"},
+    {  0x828D, (char *) "CFARepeatPatternDim", (char *) NULL},
+    {  0x828E, (char *) "CFAPattern", (char *) NULL},
+    {  0x828F, (char *) "BatteryLevel", (char *) NULL},
+    {  0x8298, (char *) "Copyright", (char *) "Copyright holder"},
+    {  0x829A, (char *) "ExposureTime", (char *) "Exposure time"},
+    {  0x829D, (char *) "FNumber", (char *) "F number"},
+    {  0x83BB, (char *) "IPTC/NAA", (char *) NULL},
+    {  0x8773, (char *) "InterColorProfile", (char *) NULL},
+    {  0x8822, (char *) "ExposureProgram", (char *) "Exposure program"},
+    {  0x8824, (char *) "SpectralSensitivity", (char *) "Spectral sensitivity"},
+    {  0x8825, (char *) "GPSInfo", (char *) NULL},
+    {  0x8827, (char *) "ISOSpeedRatings", (char *) "ISO speed rating"},
+    {  0x8828, (char *) "OECF", (char *) "Optoelectric conversion factor"},
+    {  0x9000, (char *) "ExifVersion", (char *) "Exif version"},
+    {  0x9003, (char *) "DateTimeOriginal", (char *) "Date and time of original data generation"},
+    {  0x9004, (char *) "DateTimeDigitized", (char *) "Date and time of digital data generation"},
+    {  0x9101, (char *) "ComponentsConfiguration", (char *) "Meaning of each component"},
+    {  0x9102, (char *) "CompressedBitsPerPixel", (char *) "Image compression mode"},
+    {  0x9201, (char *) "ShutterSpeedValue", (char *) "Shutter speed"},
+    {  0x9202, (char *) "ApertureValue", (char *) "Aperture"},
+    {  0x9203, (char *) "BrightnessValue", (char *) "Brightness"},
+    {  0x9204, (char *) "ExposureBiasValue", (char *) "Exposure bias"},
+    {  0x9205, (char *) "MaxApertureValue", (char *) "Maximum lens aperture"},
+    {  0x9206, (char *) "SubjectDistance", (char *) "Subject distance"},
+    {  0x9207, (char *) "MeteringMode", (char *) "Metering mode"},
+    {  0x9208, (char *) "LightSource", (char *) "Light source"},
+    {  0x9209, (char *) "Flash", (char *) "Flash"},
+    {  0x920A, (char *) "FocalLength", (char *) "Lens focal length"},
+	{  0x9214, (char *) "SubjectArea", (char *) "Subject area"},
+    {  0x927C, (char *) "MakerNote", (char *) "Manufacturer notes"},
+    {  0x9286, (char *) "UserComment", (char *) "User comments"},
+    {  0x9290, (char *) "SubSecTime", (char *) "DateTime subseconds"},
+    {  0x9291, (char *) "SubSecTimeOriginal", (char *) "DateTimeOriginal subseconds"},
+    {  0x9292, (char *) "SubSecTimeDigitized", (char *) "DateTimeDigitized subseconds"},
+    {  0xA000, (char *) "FlashPixVersion", (char *) "Supported Flashpix version"},
+    {  0xA001, (char *) "ColorSpace", (char *) "Color space information"},
+    {  0xA002, (char *) "PixelXDimension", (char *) "Valid image width"},
+    {  0xA003, (char *) "PixelYDimension", (char *) "Valid image height"},
+    {  0xA004, (char *) "RelatedSoundFile", (char *) "Related audio file"},
+    {  0xA005, (char *) "InteroperabilityOffset", (char *) NULL},
+    {  0xA20B, (char *) "FlashEnergy", (char *) "Flash energy"},
+    {  0xA20C, (char *) "SpatialFrequencyResponse", (char *) "Spatial frequency response"},
+    {  0xA20E, (char *) "FocalPlaneXResolution", (char *) "Focal plane X resolution"},
+    {  0xA20F, (char *) "FocalPlaneYResolution", (char *) "Focal plane Y resolution"},
+    {  0xA210, (char *) "FocalPlaneResolutionUnit", (char *) "Focal plane resolution unit"},
+    {  0xA214, (char *) "SubjectLocation", (char *) "Subject location"},
+    {  0xA215, (char *) "ExposureIndex", (char *) "Exposure index"},
+    {  0xA217, (char *) "SensingMethod", (char *) "Sensing method"},
+    {  0xA300, (char *) "FileSrc", (char *) "File source"},
+    {  0xA301, (char *) "SceneType", (char *) "Scene type"},
+    {  0xA302, (char *) "CFAPattern", (char *) "CFA pattern"},
+    {  0xA401, (char *) "CustomRendered", (char *) "Custom image processing"},
+    {  0xA402, (char *) "ExposureMode", (char *) "Exposure mode"},
+    {  0xA403, (char *) "WhiteBalance", (char *) "White balance"},
+    {  0xA404, (char *) "DigitalZoomRatio", (char *) "Digital zoom ratio"},
+    {  0xA405, (char *) "FocalLengthIn35mmFilm", (char *) "Focal length in 35 mm film"},
+    {  0xA406, (char *) "SceneCaptureType", (char *) "Scene capture type"},
+    {  0xA407, (char *) "GainControl", (char *) "Gain control"},
+    {  0xA408, (char *) "Contrast", (char *) "Contrast"},
+    {  0xA409, (char *) "Saturation", (char *) "Saturation"},
+    {  0xA40A, (char *) "Sharpness", (char *) "Sharpness"},
+    {  0xA40B, (char *) "DeviceSettingDescription", (char *) "Device settings description"},
+    {  0xA40C, (char *) "SubjectDistanceRange", (char *) "Subject distance range"},
+    {  0xA420, (char *) "ImageUniqueID", (char *) "Unique image ID"},
+    {  0xA430, (char *) "CameraOwnerName", (char *) "Camera owner name"},
+    {  0xA431, (char *) "BodySerialNumber", (char *) "Body serial number"},
+    {  0xA432, (char *) "LensSpecification", (char *) "Lens specification"},
+    {  0xA433, (char *) "LensMake", (char *) "Lens make"},
+    {  0xA434, (char *) "LensModel", (char *) "Lens model"},
+    {  0xA435, (char *) "LensSerialNumber", (char *) "Lens serial number"},
+
+	// These tags are not part of the Exiv v2.3 specifications but are often loaded by applications as Exif data
+	{  0x4746, (char *) "Rating", (char *) "Rating tag used by Windows"},
+	{  0x4749, (char *) "RatingPercent", (char *) "Rating tag used by Windows, value in percent"},
+	{  0x9C9B, (char *) "XPTitle", (char *) "Title tag used by Windows, encoded in UCS2"},
+	{  0x9C9C, (char *) "XPComment", (char *) "Comment tag used by Windows, encoded in UCS2"},
+	{  0x9C9D, (char *) "XPAuthor", (char *) "Author tag used by Windows, encoded in UCS2"},
+	{  0x9C9E, (char *) "XPKeywords", (char *) "Keywords tag used by Windows, encoded in UCS2"},
+	{  0x9C9F, (char *) "XPSubject", (char *) "Subject tag used by Windows, encoded in UCS2"},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+// --------------------------------------------------------------------------
+// EXIF GPS tags definition
+// --------------------------------------------------------------------------
+
+static TagInfo
+  exif_gps_tag_table[] =
+  {
+    {  0x0000, (char *) "GPSVersionID", (char *) "GPS tag version"},
+	{  0x0001, (char *) "GPSLatitudeRef", (char *) "North or South Latitude"},
+    {  0x0002, (char *) "GPSLatitude", (char *) "Latitude"},
+    {  0x0003, (char *) "GPSLongitudeRef", (char *) "East or West Longitude"},
+    {  0x0004, (char *) "GPSLongitude", (char *) "Longitude"},
+    {  0x0005, (char *) "GPSAltitudeRef", (char *) "Altitude reference"},
+    {  0x0006, (char *) "GPSAltitude", (char *) "Altitude"},
+    {  0x0007, (char *) "GPSTimeStamp", (char *) "GPS time (atomic clock)"},
+    {  0x0008, (char *) "GPSSatellites", (char *) "GPS satellites used for measurement"},
+    {  0x0009, (char *) "GPSStatus", (char *) "GPS receiver status"},
+    {  0x000A, (char *) "GPSMeasureMode", (char *) "GPS measurement mode"},
+    {  0x000B, (char *) "GPSDOP", (char *) "Measurement precision"},
+    {  0x000C, (char *) "GPSSpeedRef", (char *) "Speed unit"},
+    {  0x000D, (char *) "GPSSpeed", (char *) "Speed of GPS receiver"},
+    {  0x000E, (char *) "GPSTrackRef", (char *) "Reference for direction of movement"},
+    {  0x000F, (char *) "GPSTrack", (char *) "Direction of movement"},
+    {  0x0010, (char *) "GPSImgDirectionRef", (char *) "Reference for direction of image"},
+    {  0x0011, (char *) "GPSImgDirection", (char *) "Direction of image"},
+    {  0x0012, (char *) "GPSMapDatum", (char *) "Geodetic survey data used"},
+    {  0x0013, (char *) "GPSDestLatitudeRef", (char *) "Reference for latitude of destination"},
+    {  0x0014, (char *) "GPSDestLatitude", (char *) "Latitude of destination"},
+    {  0x0015, (char *) "GPSDestLongitudeRef", (char *) "Reference for longitude of destination"},
+    {  0x0016, (char *) "GPSDestLongitude", (char *) "Longitude of destination"},
+    {  0x0017, (char *) "GPSDestBearingRef", (char *) "Reference for bearing of destination"},
+    {  0x0018, (char *) "GPSDestBearing", (char *) "Bearing of destination"},
+    {  0x0019, (char *) "GPSDestDistanceRef", (char *) "Reference for distance to destination"},
+    {  0x001A, (char *) "GPSDestDistance", (char *) "Distance to destination"},
+    {  0x001B, (char *) "GPSProcessingMethod", (char *) "Name of GPS processing method"},
+    {  0x001C, (char *) "GPSAreaInformation", (char *) "Name of GPS area"},
+    {  0x001D, (char *) "GPSDateStamp", (char *) "GPS date"},
+    {  0x001E, (char *) "GPSDifferential", (char *) "GPS differential correction"},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+// --------------------------------------------------------------------------
+// EXIF interoperability tags definition
+// --------------------------------------------------------------------------
+
+static TagInfo
+  exif_interop_tag_table[] =
+  {
+    {  0x0001, (char *) "InteroperabilityIndex", (char *) "Interoperability Identification"},
+    {  0x0002, (char *) "InteroperabilityVersion", (char *) "Interoperability version"},
+    {  0x1000, (char *) "RelatedImageFileFormat", (char *) "File format of image file"},
+    {  0x1001, (char *) "RelatedImageWidth", (char *) "Image width"},
+    {  0x1002, (char *) "RelatedImageLength", (char *) "Image height"},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+// --------------------------------------------------------------------------
+// EXIF maker note tags definition
+// --------------------------------------------------------------------------
+
+/**
+Canon maker note
+*/
+static TagInfo
+  exif_canon_tag_table[] =
+  {
+    {  0x0001, (char *) "CanonCameraSettings", (char *) "Canon CameraSettings Tags"},
+    {  0x0002, (char *) "CanonFocalLength", (char *) "Canon FocalLength Tags"},
+    {  0x0003, (char *) "CanonFlashInfo?", (char *) NULL},
+    {  0x0004, (char *) "CanonShotInfo", (char *) "Canon ShotInfo Tags"},
+    {  0x0005, (char *) "CanonPanorama", (char *) "Canon Panorama Tags"},
+    {  0x0006, (char *) "CanonImageType", (char *) NULL},
+    {  0x0007, (char *) "CanonFirmwareVersion", (char *) NULL},
+    {  0x0008, (char *) "FileNumber", (char *) NULL},
+    {  0x0009, (char *) "OwnerName", (char *) NULL},
+    {  0x000A, (char *) "UnknownD30", (char *) "Canon UnknownD30 Tags"},
+    {  0x000C, (char *) "SerialNumber", (char *) NULL},
+    {  0x000D, (char *) "CanonCameraInfo", (char *) "Canon CameraInfo Tags"},
+    {  0x000E, (char *) "CanonFileLength", (char *) NULL},
+    {  0x000F, (char *) "CanonCustomFunctions", (char *) "Custom Functions"},
+    {  0x0010, (char *) "CanonModelID", (char *) NULL},
+    {  0x0012, (char *) "CanonAFInfo", (char *) "Canon AFInfo Tags"},
+    {  0x0013, (char *) "ThumbnailImageValidArea", (char *) NULL},
+    {  0x0015, (char *) "SerialNumberFormat", (char *) NULL},
+    {  0x001A, (char *) "SuperMacro", (char *) NULL},
+    {  0x001C, (char *) "DateStampMode", (char *) NULL},
+    {  0x001D, (char *) "MyColors", (char *) NULL},
+    {  0x001E, (char *) "FirmwareRevision", (char *) NULL},
+    {  0x0023, (char *) "Categories", (char *) NULL},
+    {  0x0024, (char *) "FaceDetect1", (char *) NULL},
+    {  0x0025, (char *) "FaceDetect2", (char *) NULL},
+    {  0x0026, (char *) "CanonAFInfo2", (char *) "Canon AFInfo2 Tags"},
+    {  0x0028, (char *) "ImageUniqueID", (char *) NULL},
+    {  0x0081, (char *) "RawDataOffset", (char *) NULL},
+    {  0x0083, (char *) "OriginalDecisionDataOffset", (char *) NULL},
+    {  0x0090, (char *) "CustomFunctions1D", (char *) "CanonCustom Functions1D Tags"},
+    {  0x0091, (char *) "PersonalFunctions", (char *) "CanonCustom PersonalFuncs Tags"},
+    {  0x0092, (char *) "PersonalFunctionValues", (char *) "CanonCustom PersonalFuncValues Tags"},
+    {  0x0093, (char *) "CanonFileInfo", (char *) "Canon FileInfo Tags"},
+    {  0x0094, (char *) "AFPointsInFocus1D", (char *) NULL},
+    {  0x0095, (char *) "LensModel", (char *) NULL},
+    {  0x0096, (char *) "SerialInfo", (char *) NULL},
+    {  0x0097, (char *) "DustRemovalData", (char *) NULL},
+	{  0x0098, (char *) "CropInfo", (char *) NULL},
+    {  0x0099, (char *) "CustomFunctions2", (char *) NULL},
+	{  0x009A, (char *) "AspectInfo", (char *) NULL},
+    {  0x00A0, (char *) "ProcessingInfo", (char *) NULL},
+    {  0x00A1, (char *) "ToneCurveTable", (char *) NULL},
+    {  0x00A2, (char *) "SharpnessTable", (char *) NULL},
+    {  0x00A3, (char *) "SharpnessFreqTable", (char *) NULL},
+    {  0x00A4, (char *) "WhiteBalanceTable", (char *) NULL},
+    {  0x00A9, (char *) "ColorBalance", (char *) NULL},
+    {  0x00AA, (char *) "MeasuredColor", (char *) NULL},
+    {  0x00AE, (char *) "ColorTemperature", (char *) NULL},
+    {  0x00B0, (char *) "CanonFlags", (char *) NULL},
+    {  0x00B1, (char *) "ModifiedInfo", (char *) NULL},
+    {  0x00B2, (char *) "ToneCurveMatching", (char *) NULL},
+    {  0x00B3, (char *) "WhiteBalanceMatching", (char *) NULL},
+    {  0x00B4, (char *) "ColorSpace", (char *) NULL},
+    {  0x00B6, (char *) "PreviewImageInfo", (char *) NULL},
+    {  0x00D0, (char *) "VRDOffset", (char *) "Offset of VRD 'recipe data' if it exists"},
+    {  0x00E0, (char *) "SensorInfo", (char *) NULL},
+    {  0x4001, (char *) "ColorData", (char *) "Canon ColorData Tags"},
+    {  0x4002, (char *) "CRWParam?", (char *) NULL},
+    {  0x4003, (char *) "ColorInfo", (char *) NULL},
+    {  0x4005, (char *) "Flavor?", (char *) NULL},
+    {  0x4008, (char *) "BlackLevel?", (char *) NULL},
+	{  0x4010, (char *) "CustomPictureStyleFileName", (char *) NULL},
+    {  0x4013, (char *) "AFMicroAdj", (char *) NULL},
+    {  0x4015, (char *) "VignettingCorr", (char *) NULL},
+    {  0x4016, (char *) "VignettingCorr2", (char *) NULL},
+    {  0x4018, (char *) "LightingOpt", (char *) NULL},
+	{  0x4019, (char *) "LensInfo", (char *) NULL},
+	{  0x4020, (char *) "AmbienceInfo", (char *) NULL},
+	{  0x4024, (char *) "FilterInfo", (char *) NULL},
+
+	// These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment
+
+	// Fields under tag 0x0001 (we add 0xC100 to make unique tag id)
+    {  0xC100 + 1, (char *) "CameraSettings:MacroMode", (char *) NULL},
+    {  0xC100 + 2, (char *) "CameraSettings:SelfTimer", (char *) NULL},
+    {  0xC100 + 3, (char *) "CameraSettings:Quality", (char *) NULL},
+    {  0xC100 + 4, (char *) "CameraSettings:CanonFlashMode", (char *) NULL},
+    {  0xC100 + 5, (char *) "CameraSettings:ContinuousDrive", (char *) NULL},
+	{  0xC100 + 6, (char *) "CameraSettings:0x0006", (char *) NULL},
+    {  0xC100 + 7, (char *) "CameraSettings:FocusMode", (char *) NULL},
+	{  0xC100 + 8, (char *) "CameraSettings:0x0008", (char *) NULL},
+    {  0xC100 + 9, (char *) "CameraSettings:RecordMode", (char *) NULL},
+    {  0xC100 + 10, (char *) "CameraSettings:CanonImageSize", (char *) NULL},
+    {  0xC100 + 11, (char *) "CameraSettings:EasyMode", (char *) NULL},
+    {  0xC100 + 12, (char *) "CameraSettings:DigitalZoom", (char *) NULL},
+    {  0xC100 + 13, (char *) "CameraSettings:Contrast", (char *) NULL},
+    {  0xC100 + 14, (char *) "CameraSettings:Saturation", (char *) NULL},
+    {  0xC100 + 15, (char *) "CameraSettings:Sharpness", (char *) NULL},
+    {  0xC100 + 16, (char *) "CameraSettings:CameraISO", (char *) NULL},
+    {  0xC100 + 17, (char *) "CameraSettings:MeteringMode", (char *) NULL},
+    {  0xC100 + 18, (char *) "CameraSettings:FocusRange", (char *) NULL},
+    {  0xC100 + 19, (char *) "CameraSettings:AFPoint", (char *) NULL},
+    {  0xC100 + 20, (char *) "CameraSettings:CanonExposureMode", (char *) NULL},
+	{  0xC100 + 21, (char *) "CameraSettings:0x0015", (char *) NULL},
+    {  0xC100 + 22, (char *) "CameraSettings:LensType", (char *) NULL},
+    {  0xC100 + 23, (char *) "CameraSettings:LongFocal", (char *) NULL},
+    {  0xC100 + 24, (char *) "CameraSettings:ShortFocal", (char *) NULL},
+    {  0xC100 + 25, (char *) "CameraSettings:FocalUnits", (char *) "Focal Units per mm"},
+    {  0xC100 + 26, (char *) "CameraSettings:MaxAperture", (char *) NULL},
+    {  0xC100 + 27, (char *) "CameraSettings:MinAperture", (char *) NULL},
+    {  0xC100 + 28, (char *) "CameraSettings:FlashActivity", (char *) NULL},
+    {  0xC100 + 29, (char *) "CameraSettings:FlashBits", (char *) NULL},
+	{  0xC100 + 30, (char *) "CameraSettings:0x001E", (char *) NULL},
+	{  0xC100 + 31, (char *) "CameraSettings:0x001F", (char *) NULL},
+    {  0xC100 + 32, (char *) "CameraSettings:FocusContinuous", (char *) NULL},
+    {  0xC100 + 33, (char *) "CameraSettings:AESetting", (char *) NULL},
+    {  0xC100 + 34, (char *) "CameraSettings:ImageStabilization", (char *) NULL},
+    {  0xC100 + 35, (char *) "CameraSettings:DisplayAperture", (char *) NULL},
+    {  0xC100 + 36, (char *) "CameraSettings:ZoomSourceWidth", (char *) NULL},
+    {  0xC100 + 37, (char *) "CameraSettings:ZoomTargetWidth", (char *) NULL},
+	{  0xC100 + 38, (char *) "CameraSettings:0x0026", (char *) NULL},
+    {  0xC100 + 39, (char *) "CameraSettings:SpotMeteringMode", (char *) NULL},
+    {  0xC100 + 40, (char *) "CameraSettings:PhotoEffect", (char *) NULL},
+    {  0xC100 + 41, (char *) "CameraSettings:ManualFlashOutput", (char *) NULL},
+    {  0xC100 + 42, (char *) "CameraSettings:ColorTone", (char *) NULL},
+	{  0xC100 + 43, (char *) "CameraSettings:0x002B", (char *) NULL},
+	{  0xC100 + 44, (char *) "CameraSettings:0x002C", (char *) NULL},
+	{  0xC100 + 45, (char *) "CameraSettings:0x002D", (char *) NULL},
+    {  0xC100 + 46, (char *) "CameraSettings:SRAWQuality", (char *) NULL},
+	{  0xC100 + 47, (char *) "CameraSettings:0x002F", (char *) NULL},
+	{  0xC100 + 48, (char *) "CameraSettings:0x0030", (char *) NULL},
+
+	// Fields under tag 0x0002 (we add 0xC200 to make unique tag id)
+    {  0xC200 + 0, (char *) "FocalLength:FocalType", (char *) NULL},
+    {  0xC200 + 1, (char *) "FocalLength:FocalLength", (char *) NULL},
+    {  0xC200 + 2, (char *) "FocalLength:FocalPlaneXSize", (char *) NULL},
+    {  0xC200 + 3, (char *) "FocalLength:FocalPlaneYSize", (char *) NULL},
+	
+	// Fields under tag 0x0004 (we add 0xC400 to make unique tag id)
+    {  0xC400 + 1, (char *) "ShotInfo:AutoISO", (char *) NULL},
+	{  0xC400 + 2, (char *) "ShotInfo:BaseISO", (char *) NULL},
+	{  0xC400 + 3, (char *) "ShotInfo:MeasuredEV", (char *) NULL},
+    {  0xC400 + 4, (char *) "ShotInfo:TargetAperture", (char *) NULL},
+    {  0xC400 + 5, (char *) "ShotInfo:TargetExposureTime", (char *) NULL},
+    {  0xC400 + 6, (char *) "ShotInfo:ExposureCompensation", (char *) NULL},
+    {  0xC400 + 7, (char *) "ShotInfo:WhiteBalance", (char *) NULL},
+    {  0xC400 + 8, (char *) "ShotInfo:SlowShutter", (char *) NULL},
+    {  0xC400 + 9, (char *) "ShotInfo:SequenceNumber", (char *) NULL},
+    {  0xC400 + 10, (char *) "ShotInfo:OpticalZoomCode", (char *) NULL},
+	{  0xC400 + 11, (char *) "ShotInfo:0x000B", (char *) NULL},
+	{  0xC400 + 12, (char *) "ShotInfo:CameraTemperature", (char *) NULL},
+    {  0xC400 + 13, (char *) "ShotInfo:FlashGuideNumber", (char *) NULL},
+    {  0xC400 + 14, (char *) "ShotInfo:AFPointsInFocus", (char *) NULL},
+    {  0xC400 + 15, (char *) "ShotInfo:FlashExposureComp", (char *) NULL},
+    {  0xC400 + 16, (char *) "ShotInfo:AutoExposureBracketing", (char *) NULL},
+    {  0xC400 + 17, (char *) "ShotInfo:AEBBracketValue", (char *) NULL},
+    {  0xC400 + 18, (char *) "ShotInfo:ControlMode", (char *) NULL},
+    {  0xC400 + 19, (char *) "ShotInfo:FocusDistanceUpper", (char *) NULL},
+    {  0xC400 + 20, (char *) "ShotInfo:FocusDistanceLower", (char *) NULL},
+    {  0xC400 + 21, (char *) "ShotInfo:FNumber", (char *) NULL},
+    {  0xC400 + 22, (char *) "ShotInfo:ExposureTime", (char *) NULL},
+    {  0xC400 + 23, (char *) "ShotInfo:MeasuredEV2", (char *) NULL},
+    {  0xC400 + 24, (char *) "ShotInfo:BulbDuration", (char *) NULL},
+	{  0xC400 + 25, (char *) "ShotInfo:0x0019", (char *) NULL},
+    {  0xC400 + 26, (char *) "ShotInfo:CameraType", (char *) NULL},
+    {  0xC400 + 27, (char *) "ShotInfo:AutoRotate", (char *) NULL},
+    {  0xC400 + 28, (char *) "ShotInfo:NDFilter", (char *) NULL},
+    {  0xC400 + 29, (char *) "ShotInfo:SelfTimer2", (char *) NULL},
+	{  0xC400 + 30, (char *) "ShotInfo:0x001E", (char *) NULL},
+	{  0xC400 + 31, (char *) "ShotInfo:0x001F", (char *) NULL},
+	{  0xC400 + 32, (char *) "ShotInfo:0x0020", (char *) NULL},
+    {  0xC400 + 33, (char *) "ShotInfo:FlashOutput", (char *) NULL},
+
+	// Fields under tag 0x0012 (we add 0x1200 to make unique tag id)
+    {  0x1200 + 0, (char *) "AFInfo:NumAFPoints", (char *) NULL},
+    {  0x1200 + 1, (char *) "AFInfo:ValidAFPoints", (char *) NULL},
+    {  0x1200 + 2, (char *) "AFInfo:CanonImageWidth", (char *) NULL},
+    {  0x1200 + 3, (char *) "AFInfo:CanonImageHeight", (char *) NULL},
+    {  0x1200 + 4, (char *) "AFInfo:AFImageWidth", (char *) NULL},
+    {  0x1200 + 5, (char *) "AFInfo:AFImageHeight", (char *) NULL},
+    {  0x1200 + 6, (char *) "AFInfo:AFAreaWidth", (char *) NULL},
+    {  0x1200 + 7, (char *) "AFInfo:AFAreaHeight", (char *) NULL},
+    {  0x1200 + 8, (char *) "AFInfo:AFAreaXPositions", (char *) NULL},
+    {  0x1200 + 9, (char *) "AFInfo:AFAreaYPositions", (char *) NULL},
+    {  0x1200 + 10, (char *) "AFInfo:AFPointsInFocus", (char *) NULL},
+    {  0x1200 + 11, (char *) "AFInfo:PrimaryAFPoint?", (char *) NULL},
+    {  0x1200 + 12, (char *) "AFInfo:PrimaryAFPoint", (char *) NULL},
+	{  0x1200 + 13, (char *) "AFInfo:0x000D", (char *) NULL},
+	{  0x1200 + 14, (char *) "AFInfo:0x000E", (char *) NULL},
+	{  0x1200 + 15, (char *) "AFInfo:0x000F", (char *) NULL},
+	{  0x1200 + 16, (char *) "AFInfo:0x0010", (char *) NULL},
+	{  0x1200 + 17, (char *) "AFInfo:0x0011", (char *) NULL},
+	{  0x1200 + 18, (char *) "AFInfo:0x0012", (char *) NULL},
+	{  0x1200 + 19, (char *) "AFInfo:0x0013", (char *) NULL},
+	{  0x1200 + 20, (char *) "AFInfo:0x0014", (char *) NULL},
+	{  0x1200 + 21, (char *) "AFInfo:0x0015", (char *) NULL},
+	{  0x1200 + 22, (char *) "AFInfo:0x0016", (char *) NULL},
+	{  0x1200 + 23, (char *) "AFInfo:0x0017", (char *) NULL},
+	{  0x1200 + 24, (char *) "AFInfo:0x0018", (char *) NULL},
+	{  0x1200 + 25, (char *) "AFInfo:0x0019", (char *) NULL},
+	{  0x1200 + 26, (char *) "AFInfo:0x001A", (char *) NULL},
+	{  0x1200 + 27, (char *) "AFInfo:0x001B", (char *) NULL},
+
+	// Fields under tag 0x00A0 (we add 0xCA00 to make unique tag id)
+    {  0xCA00 + 1, (char *) "ProcessingInfo:ToneCurve", (char *) NULL},
+    {  0xCA00 + 2, (char *) "ProcessingInfo:Sharpness", (char *) NULL},
+    {  0xCA00 + 3, (char *) "ProcessingInfo:SharpnessFrequency", (char *) NULL},
+    {  0xCA00 + 4, (char *) "ProcessingInfo:SensorRedLevel", (char *) NULL},
+    {  0xCA00 + 5, (char *) "ProcessingInfo:SensorBlueLevel", (char *) NULL},
+    {  0xCA00 + 6, (char *) "ProcessingInfo:WhiteBalanceRed", (char *) NULL},
+    {  0xCA00 + 7, (char *) "ProcessingInfo:WhiteBalanceBlue", (char *) NULL},
+    {  0xCA00 + 8, (char *) "ProcessingInfo:WhiteBalance", (char *) NULL},
+    {  0xCA00 + 9, (char *) "ProcessingInfo:ColorTemperature", (char *) NULL},
+    {  0xCA00 + 10, (char *) "ProcessingInfo:PictureStyle", (char *) NULL},
+    {  0xCA00 + 11, (char *) "ProcessingInfo:DigitalGain", (char *) NULL},
+    {  0xCA00 + 12, (char *) "ProcessingInfo:WBShiftAB", (char *) NULL},
+    {  0xCA00 + 13, (char *) "ProcessingInfo:WBShiftGM", (char *) NULL},
+
+	// Fields under tag 0x00E0 (we add 0xCE00 to make unique tag id)
+    {  0xCE00 + 1, (char *) "SensorInfo:SensorWidth", (char *) NULL},
+    {  0xCE00 + 2, (char *) "SensorInfo:SensorHeight", (char *) NULL},
+	{  0xCE00 + 3, (char *) "SensorInfo:0x0003", (char *) NULL},
+	{  0xCE00 + 4, (char *) "SensorInfo:0x0004", (char *) NULL},
+    {  0xCE00 + 5, (char *) "SensorInfo:SensorLeftBorder", (char *) NULL},
+    {  0xCE00 + 6, (char *) "SensorInfo:SensorTopBorder", (char *) NULL},
+    {  0xCE00 + 7, (char *) "SensorInfo:SensorRightBorder", (char *) NULL},
+    {  0xCE00 + 8, (char *) "SensorInfo:SensorBottomBorder", (char *) NULL},
+    {  0xCE00 + 9, (char *) "SensorInfo:BlackMaskLeftBorder", (char *) NULL},
+    {  0xCE00 + 10, (char *) "SensorInfo:BlackMaskTopBorder", (char *) NULL},
+    {  0xCE00 + 11, (char *) "SensorInfo:BlackMaskRightBorder", (char *) NULL},
+    {  0xCE00 + 12, (char *) "SensorInfo:BlackMaskBottomBorder", (char *) NULL},
+	{  0xCE00 + 13, (char *) "SensorInfo:0x000D", (char *) NULL},
+	{  0xCE00 + 14, (char *) "SensorInfo:0x000E", (char *) NULL},
+	{  0xCE00 + 15, (char *) "SensorInfo:0x000F", (char *) NULL},
+	{  0xCE00 + 16, (char *) "SensorInfo:0x0010", (char *) NULL},
+
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Casio type 1 maker note
+*/
+static TagInfo
+  exif_casio_type1_tag_table[] =
+  {
+    {  0x0001, (char *) "RecordingMode", (char *) NULL},
+    {  0x0002, (char *) "Quality", (char *) NULL},
+    {  0x0003, (char *) "FocusMode", (char *) NULL},
+    {  0x0004, (char *) "FlashMode", (char *) NULL},
+    {  0x0005, (char *) "FlashIntensity", (char *) NULL},
+    {  0x0006, (char *) "ObjectDistance", (char *) NULL},
+    {  0x0007, (char *) "WhiteBalance", (char *) NULL},
+    {  0x000A, (char *) "DigitalZoom", (char *) NULL},
+    {  0x000B, (char *) "Sharpness", (char *) NULL},
+    {  0x000C, (char *) "Contrast", (char *) NULL},
+    {  0x000D, (char *) "Saturation", (char *) NULL},
+    {  0x0014, (char *) "ISO", (char *) NULL},
+	{  0x0015, (char *) "FirmwareDate", (char *) NULL},
+    {  0x0016, (char *) "Enhancement", (char *) NULL},
+    {  0x0017, (char *) "ColorFilter", (char *) NULL},
+    {  0x0018, (char *) "AFPoint", (char *) NULL},
+    {  0x0019, (char *) "FlashIntensity", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Casio type 2 maker note
+*/
+static TagInfo
+  exif_casio_type2_tag_table[] =
+  {
+    {  0x0002, (char *) "PreviewImageSize", (char *) NULL},
+    {  0x0003, (char *) "PreviewImageLength", (char *) NULL},
+    {  0x0004, (char *) "PreviewImageStart", (char *) NULL},
+    {  0x0008, (char *) "QualityMode", (char *) NULL},
+    {  0x0009, (char *) "CasioImageSize", (char *) NULL},
+    {  0x000D, (char *) "FocusMode", (char *) NULL},
+    {  0x0014, (char *) "ISO", (char *) NULL},
+    {  0x0019, (char *) "WhiteBalance", (char *) NULL},
+    {  0x001D, (char *) "FocalLength", (char *) NULL},
+    {  0x001F, (char *) "Saturation", (char *) NULL},
+    {  0x0020, (char *) "Contrast", (char *) NULL},
+    {  0x0021, (char *) "Sharpness", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) NULL},
+    {  0x2000, (char *) "PreviewImage", (char *) NULL},
+    {  0x2001, (char *) "FirmwareDate", (char *) NULL},
+    {  0x2011, (char *) "WhiteBalanceBias", (char *) NULL},
+    {  0x2012, (char *) "WhiteBalance", (char *) NULL},
+    {  0x2021, (char *) "AFPointPosition", (char *) NULL},
+    {  0x2022, (char *) "ObjectDistance", (char *) NULL},
+    {  0x2034, (char *) "FlashDistance", (char *) NULL},
+	{  0x2076, (char *) "SpecialEffectMode", (char *) NULL},
+    {  0x3000, (char *) "RecordMode", (char *) NULL},
+    {  0x3001, (char *) "ReleaseMode", (char *) NULL},
+    {  0x3002, (char *) "Quality", (char *) NULL},
+    {  0x3003, (char *) "FocusMode", (char *) NULL},
+    {  0x3006, (char *) "HometownCity", (char *) NULL},
+    {  0x3007, (char *) "BestShotMode", (char *) NULL},
+    {  0x3008, (char *) "AutoISO", (char *) NULL},
+    {  0x3009, (char *) "AFMode", (char *) NULL},
+    {  0x3011, (char *) "Sharpness", (char *) NULL},
+    {  0x3012, (char *) "Contrast", (char *) NULL},
+    {  0x3013, (char *) "Saturation", (char *) NULL},
+    {  0x3014, (char *) "ISO", (char *) NULL},
+    {  0x3015, (char *) "ColorMode", (char *) NULL},
+    {  0x3016, (char *) "Enhancement", (char *) NULL},
+    {  0x3017, (char *) "ColorFilter", (char *) NULL},
+	{  0x301B, (char *) "ArtMode", (char *) NULL},
+    {  0x301C, (char *) "SequenceNumber", (char *) NULL},
+    {  0x301D, (char *) "BracketSequence", (char *) NULL},
+    {  0x3020, (char *) "ImageStabilization", (char *) NULL},
+    {  0x302A, (char *) "LightingMode", (char *) NULL},
+    {  0x302B, (char *) "PortraitRefiner", (char *) NULL},
+    {  0x3030, (char *) "SpecialEffectLevel", (char *) NULL},
+    {  0x3031, (char *) "SpecialEffectSetting", (char *) NULL},
+	{  0x3103, (char *) "DriveMode", (char *) NULL},
+	{  0x4001, (char *) "CaptureFrameRate", (char *) NULL},
+	{  0x4003, (char *) "VideoQuality", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+FujiFilm maker note
+*/
+static TagInfo
+  exif_fujifilm_tag_table[] =
+  {
+    {  0x0000, (char *) "Version", (char *) NULL},
+    {  0x0010, (char *) "InternalSerialNumber", (char *) NULL},
+    {  0x1000, (char *) "Quality", (char *) NULL},
+    {  0x1001, (char *) "Sharpness", (char *) NULL},
+    {  0x1002, (char *) "WhiteBalance", (char *) NULL},
+    {  0x1003, (char *) "Saturation", (char *) NULL},
+    {  0x1004, (char *) "Contrast", (char *) NULL},
+	{  0x1005, (char *) "ColorTemperature", (char *) NULL},
+	{  0x100A, (char *) "WhiteBalanceFineTune", (char *) NULL},
+	{  0x100B, (char *) "NoiseReduction", (char *) NULL},
+    {  0x1010, (char *) "FujiFlashMode", (char *) NULL},
+    {  0x1011, (char *) "FlashExposureComp", (char *) NULL},
+    {  0x1020, (char *) "Macro", (char *) NULL},
+    {  0x1021, (char *) "FocusMode", (char *) NULL},
+    {  0x1023, (char *) "FocusPixel", (char *) NULL},
+    {  0x1030, (char *) "SlowSync", (char *) NULL},
+    {  0x1031, (char *) "PictureMode", (char *) NULL},
+	{  0x1033, (char *) "EXRAuto", (char *) NULL},
+	{  0x1034, (char *) "EXRMode", (char *) NULL},
+    {  0x1100, (char *) "AutoBracketting", (char *) NULL},
+    {  0x1101, (char *) "SequenceNumber", (char *) NULL},
+    {  0x1210, (char *) "ColorMode", (char *) NULL},
+    {  0x1300, (char *) "BlurWarning", (char *) NULL},
+    {  0x1301, (char *) "FocusWarning", (char *) NULL},
+    {  0x1302, (char *) "ExposureWarning", (char *) NULL},
+    {  0x1400, (char *) "DynamicRange", (char *) NULL},
+	{  0x1401, (char *) "FilmMode", (char *) NULL},
+	{  0x1402, (char *) "DynamicRangeSetting", (char *) NULL},
+	{  0x1403, (char *) "DevelopmentDynamicRange", (char *) NULL},
+	{  0x1404, (char *) "MinFocalLength", (char *) NULL},
+	{  0x1405, (char *) "MaxFocalLength", (char *) NULL},
+	{  0x1406, (char *) "MaxApertureAtMinFocal", (char *) NULL},
+	{  0x1407, (char *) "MaxApertureAtMaxFocal", (char *) NULL},
+	{  0x4100, (char *) "FacesDetected", (char *) NULL},
+	{  0x4103, (char *) "FacePositions", (char *) NULL},
+	{  0x8000, (char *) "FileSource", (char *) NULL},
+	{  0x8002, (char *) "OrderNumber", (char *) NULL},
+	{  0x8003, (char *) "FrameNumber", (char *) NULL},
+	{  0xB211, (char *) "Parallax", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Kyocera maker note
+*/
+static TagInfo
+  exif_kyocera_tag_table[] =
+  {
+    {  0x0001, (char *) "ThumbnailImage", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) "Print Image Matching Info"},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Olympus Type 1 / Epson / Agfa maker note
+*/
+static TagInfo
+  exif_olympus_type1_tag_table[] =
+  {
+    {  0x0000, (char *) "MakerNoteVersion", (char *) NULL},
+    {  0x0001, (char *) "MinoltaCameraSettingsOld", (char *) NULL},
+    {  0x0003, (char *) "MinoltaCameraSettings", (char *) NULL},
+    {  0x0040, (char *) "CompressedImageSize", (char *) NULL},
+    {  0x0081, (char *) "PreviewImageData", (char *) NULL},
+    {  0x0088, (char *) "PreviewImageStart", (char *) NULL},
+    {  0x0089, (char *) "PreviewImageLength", (char *) NULL},
+    {  0x0100, (char *) "ThumbnailImage", (char *) NULL},
+    {  0x0104, (char *) "BodyFirmwareVersion", (char *) NULL},
+    {  0x0200, (char *) "SpecialMode", (char *) NULL},
+    {  0x0201, (char *) "Quality", (char *) NULL},
+    {  0x0202, (char *) "Macro", (char *) NULL},
+    {  0x0203, (char *) "BWMode", (char *) NULL},
+    {  0x0204, (char *) "DigitalZoom", (char *) NULL},
+    {  0x0205, (char *) "FocalPlaneDiagonal", (char *) NULL},
+	{  0x0206, (char *) "LensDistortionParams", (char *) NULL},
+    {  0x0207, (char *) "CameraType", (char *) NULL},
+    {  0x0208, (char *) "TextInfo", (char *) "Olympus TextInfo Tags"},
+    {  0x0209, (char *) "CameraID", (char *) NULL},
+    {  0x020B, (char *) "EpsonImageWidth", (char *) NULL},
+    {  0x020C, (char *) "EpsonImageHeight", (char *) NULL},
+    {  0x020D, (char *) "EpsonSoftware", (char *) NULL},
+    {  0x0280, (char *) "PreviewImage", (char *) NULL},
+    {  0x0300, (char *) "PreCaptureFrames", (char *) NULL},
+	{  0x0301, (char *) "WhiteBoard", (char *) NULL},
+    {  0x0302, (char *) "OneTouchWB", (char *) NULL}, 
+	{  0x0303, (char *) "WhiteBalanceBracket", (char *) NULL}, 
+	{  0x0304, (char *) "WhiteBalanceBias", (char *) NULL}, 
+	{  0x0403, (char *) "SceneMode", (char *) NULL}, 
+    {  0x0404, (char *) "SerialNumber", (char *) NULL}, 
+	{  0x0405, (char *) "Firmware", (char *) NULL}, 
+    {  0x0E00, (char *) "PrintIM", (char *) "PrintIM Tags"}, 
+	{  0x0F00, (char *) "DataDump", (char *) NULL},
+	{  0x0F01, (char *) "DataDump2", (char *) NULL},
+	{  0x1000, (char *) "ShutterSpeedValue", (char *) NULL},
+	{  0x1001, (char *) "ISOValue", (char *) NULL},
+	{  0x1002, (char *) "ApertureValue", (char *) NULL},
+	{  0x1003, (char *) "BrightnessValue", (char *) NULL},
+	{  0x1004, (char *) "FlashMode", (char *) NULL},
+	{  0x1005, (char *) "FlashDevice", (char *) NULL},
+	{  0x1006, (char *) "ExposureCompensation", (char *) NULL},
+	{  0x1007, (char *) "SensorTemperature", (char *) NULL},
+	{  0x1008, (char *) "LensTemperature", (char *) NULL},
+	{  0x1009, (char *) "LightCondition", (char *) NULL},
+	{  0x100A, (char *) "FocusRange", (char *) NULL},
+	{  0x100B, (char *) "FocusMode", (char *) NULL},
+	{  0x100C, (char *) "ManualFocusDistance", (char *) NULL},
+	{  0x100D, (char *) "ZoomStepCount", (char *) NULL},
+	{  0x100E, (char *) "FocusStepCount", (char *) NULL},
+	{  0x100F, (char *) "Sharpness", (char *) NULL},
+	{  0x1010, (char *) "FlashChargeLevel", (char *) NULL},
+	{  0x1011, (char *) "ColorMatrix", (char *) NULL},
+	{  0x1012, (char *) "BlackLevel", (char *) NULL},
+	{  0x1015, (char *) "WBMode", (char *) NULL},
+	{  0x1017, (char *) "RedBalance", (char *) NULL},
+	{  0x1018, (char *) "BlueBalance", (char *) NULL},
+	{  0x1019, (char *) "ColorMatrixNumber", (char *) NULL},
+	{  0x101A, (char *) "SerialNumber", (char *) NULL},
+	{  0x1023, (char *) "FlashExposureComp", (char *) NULL},
+	{  0x1024, (char *) "InternalFlashTable", (char *) NULL},
+	{  0x1025, (char *) "ExternalFlashGValue", (char *) NULL},
+	{  0x1026, (char *) "ExternalFlashBounce", (char *) NULL},
+	{  0x1027, (char *) "ExternalFlashZoom", (char *) NULL},
+	{  0x1028, (char *) "ExternalFlashMode", (char *) NULL},
+	{  0x1029, (char *) "Contrast", (char *) NULL},
+	{  0x102A, (char *) "SharpnessFactor", (char *) NULL},
+	{  0x102B, (char *) "ColorControl", (char *) NULL},
+	{  0x102C, (char *) "ValidBits", (char *) NULL},
+	{  0x102D, (char *) "CoringFilter", (char *) NULL},
+	{  0x102E, (char *) "OlympusImageWidth", (char *) NULL},
+	{  0x102F, (char *) "OlympusImageHeight", (char *) NULL},
+	{  0x1030, (char *) "SceneDetect", (char *) NULL},
+	{  0x1031, (char *) "SceneArea?", (char *) NULL},
+	{  0x1033, (char *) "SceneDetectData?", (char *) NULL},
+	{  0x1034, (char *) "CompressionRatio", (char *) NULL},
+	{  0x1035, (char *) "PreviewImageValid", (char *) NULL},
+	{  0x1036, (char *) "PreviewImageStart", (char *) NULL},
+	{  0x1037, (char *) "PreviewImageLength", (char *) NULL},
+	{  0x1038, (char *) "AFResult", (char *) NULL},
+	{  0x1039, (char *) "CCDScanMode", (char *) NULL},
+	{  0x103A, (char *) "NoiseReduction", (char *) NULL},
+	{  0x103B, (char *) "InfinityLensStep", (char *) NULL},
+	{  0x103C, (char *) "NearLensStep", (char *) NULL},
+	{  0x103D, (char *) "LightValueCenter", (char *) NULL},
+	{  0x103E, (char *) "LightValuePeriphery", (char *) NULL},
+	{  0x2010, (char *) "Equipment", (char *) "Olympus Equipment Tags"},
+	{  0x2020, (char *) "CameraSettings", (char *) "Olympus CameraSettings Tags"},
+	{  0x2030, (char *) "RawDevelopment", (char *) "Olympus RawDevelopment Tags"},
+	{  0x2040, (char *) "ImageProcessing", (char *) "Olympus ImageProcessing Tags"},
+	{  0x2050, (char *) "FocusInfo", (char *) "Olympus FocusInfo Tags"},
+	{  0x2100, (char *) "Olympus2100", (char *) "Olympus FE Tags"},
+	{  0x2200, (char *) "Olympus2200", (char *) "Olympus FE Tags"},
+	{  0x2300, (char *) "Olympus2300", (char *) "Olympus FE Tags"},
+	{  0x2400, (char *) "Olympus2400", (char *) "Olympus FE Tags"},
+	{  0x2500, (char *) "Olympus2500", (char *) "Olympus FE Tags"},
+	{  0x2600, (char *) "Olympus2600", (char *) "Olympus FE Tags"},
+	{  0x2700, (char *) "Olympus2700", (char *) "Olympus FE Tags"},
+	{  0x2800, (char *) "Olympus2800", (char *) "Olympus FE Tags"},
+	{  0x2900, (char *) "Olympus2900", (char *) "Olympus FE Tags"},
+	{  0x3000, (char *) "RawInfo", (char *) "Olympus RawInfo Tags"},
+	{  0x4000, (char *) "MainInfo", (char *) "Olympus MainInfo Tags"},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Minolta maker note
+*/
+static TagInfo
+  exif_minolta_tag_table[] =
+  {
+    {  0x0000, (char *) "MakerNoteVersion", (char *) NULL},
+    {  0x0001, (char *) "MinoltaCameraSettingsOld", (char *) NULL},
+    {  0x0003, (char *) "MinoltaCameraSettings", (char *) NULL},
+    {  0x0004, (char *) "MinoltaCameraSettings7D", (char *) NULL},
+    {  0x0018, (char *) "ImageStabilization", (char *) NULL},
+	{  0x0040, (char *) "CompressedImageSize", (char *) NULL},
+    {  0x0081, (char *) "PreviewImage", (char *) NULL},
+    {  0x0088, (char *) "PreviewImageStart", (char *) NULL},
+    {  0x0089, (char *) "PreviewImageLength", (char *) NULL},
+    {  0x0100, (char *) "SceneMode", (char *) NULL},
+    {  0x0101, (char *) "ColorMode", (char *) NULL},
+    {  0x0102, (char *) "MinoltaQuality", (char *) NULL},
+    {  0x0103, (char *) "MinoltaImageSize", (char *) NULL},
+    {  0x0104, (char *) "FlashExposureComp", (char *) NULL},
+    {  0x0105, (char *) "Teleconverter", (char *) NULL},
+    {  0x0107, (char *) "ImageStabilization", (char *) NULL},
+    {  0x0109, (char *) "RawAndJpgRecording", (char *) NULL},
+    {  0x010A, (char *) "ZoneMatching", (char *) NULL},
+    {  0x010B, (char *) "ColorTemperature", (char *) NULL},
+    {  0x010C, (char *) "LensType", (char *) NULL},
+    {  0x0111, (char *) "ColorCompensationFilter", (char *) NULL},
+    {  0x0112, (char *) "WhiteBalanceFineTune", (char *) NULL},
+    {  0x0113, (char *) "ImageStabilization", (char *) NULL},
+    {  0x0114, (char *) "MinoltaCameraSettings5D", (char *) NULL},
+    {  0x0115, (char *) "WhiteBalance", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) NULL},
+    {  0x0F00, (char *) "MinoltaCameraSettings2", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+There are 3 formats of Nikon's MakerNote. MakerNote of E700/E800/E900/E900S/E910/E950
+starts from ASCII string "Nikon". Data format is the same as IFD, but it starts from
+offset 0x08. This is the same as Olympus except start string. 
+*/
+
+/**
+TYPE 1 is for E-Series cameras prior to (not including) E990
+*/
+static TagInfo
+  exif_nikon_type1_tag_table[] =
+  {
+    {  0x0002, (char *) "FamilyID", (char *) NULL},
+    {  0x0003, (char *) "Quality", (char *) NULL},
+    {  0x0004, (char *) "ColorMode", (char *) NULL},
+    {  0x0005, (char *) "ImageAdjustment", (char *) NULL},
+    {  0x0006, (char *) "CCDSensitivity", (char *) NULL},
+    {  0x0007, (char *) "WhiteBalance", (char *) NULL},
+    {  0x0008, (char *) "Focus", (char *) NULL},
+    {  0x000A, (char *) "DigitalZoom", (char *) NULL},
+    {  0x000B, (char *) "FisheyeConverter", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Nikon type 2 maker note
+*/
+static TagInfo
+  exif_nikon_type2_tag_table[] =
+  {
+    {  0x0001, (char *) "MakerNoteVersion", (char *) NULL},
+    {  0x0002, (char *) "ISO", (char *) NULL},
+    {  0x0003, (char *) "ColorMode", (char *) NULL},
+    {  0x0004, (char *) "Quality", (char *) NULL},
+    {  0x0005, (char *) "WhiteBalance", (char *) NULL},
+    {  0x0006, (char *) "Sharpness", (char *) NULL},
+    {  0x0007, (char *) "FocusMode", (char *) NULL},
+    {  0x0008, (char *) "FlashSetting", (char *) NULL},
+    {  0x0009, (char *) "FlashType", (char *) NULL},
+    {  0x000B, (char *) "WhiteBalanceFineTune", (char *) NULL},
+    {  0x000F, (char *) "ISOSelection", (char *) NULL},
+    {  0x0010, (char *) "DataDump", (char *) NULL},
+    {  0x0080, (char *) "ImageAdjustment", (char *) NULL},
+    {  0x0082, (char *) "AuxiliaryLens", (char *) NULL},
+    {  0x0085, (char *) "ManualFocusDistance", (char *) NULL},
+    {  0x0086, (char *) "DigitalZoom", (char *) NULL},
+    {  0x0088, (char *) "AFInfo", (char *) NULL},
+    {  0x0089, (char *) "ShootingMode", (char *) NULL},
+    {  0x008D, (char *) "ColorMode", (char *) NULL},
+    {  0x008F, (char *) "SceneMode", (char *) NULL},
+    {  0x0092, (char *) "HueAdjustment", (char *) NULL},
+    {  0x0094, (char *) "Saturation", (char *) NULL},
+    {  0x0095, (char *) "NoiseReduction", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+The type-3 directory is for D-Series cameras such as the D1 and D100.
+see http://www.timelesswanderings.net/equipment/D100/NEF.html
+*/
+static TagInfo
+  exif_nikon_type3_tag_table[] =
+  {
+    {  0x0001, (char *) "MakerNoteVersion", (char *) NULL},
+    {  0x0002, (char *) "ISO", (char *) NULL},
+    {  0x0003, (char *) "ColorMode", (char *) NULL},
+    {  0x0004, (char *) "Quality", (char *) NULL},
+    {  0x0005, (char *) "WhiteBalance", (char *) NULL},
+    {  0x0006, (char *) "Sharpness", (char *) NULL},
+    {  0x0007, (char *) "FocusMode", (char *) NULL},
+    {  0x0008, (char *) "FlashSetting", (char *) NULL},
+    {  0x0009, (char *) "FlashType", (char *) NULL},
+    {  0x000B, (char *) "WhiteBalanceFineTune", (char *) NULL},
+    {  0x000C, (char *) "WB_RBLevels", (char *) NULL},
+    {  0x000D, (char *) "ProgramShift", (char *) NULL},
+    {  0x000E, (char *) "ExposureDifference", (char *) NULL},
+    {  0x000F, (char *) "ISOSelection", (char *) NULL},
+	{  0x0010, (char *) "DataDump", (char *) NULL},
+    {  0x0011, (char *) "PreviewIFD", (char *) NULL},
+    {  0x0012, (char *) "FlashExposureComp", (char *) NULL},
+    {  0x0013, (char *) "ISOSetting", (char *) NULL},
+	{  0x0014, (char *) "ColorBalanceA", (char *) NULL},
+    {  0x0016, (char *) "ImageBoundary", (char *) NULL},
+	{  0x0017, (char *) "FlashExposureComp", (char *) NULL},
+    {  0x0018, (char *) "FlashExposureBracketValue", (char *) NULL},
+    {  0x0019, (char *) "ExposureBracketValue", (char *) NULL},
+    {  0x001A, (char *) "ImageProcessing", (char *) NULL},
+    {  0x001B, (char *) "CropHiSpeed", (char *) NULL},
+	{  0x001C, (char *) "ExposureTuning", (char *) NULL},
+    {  0x001D, (char *) "SerialNumber", (char *) NULL},
+    {  0x001E, (char *) "ColorSpace", (char *) NULL},
+	{  0x001F, (char *) "VRInfo", (char *) NULL},
+	{  0x0020, (char *) "ImageAuthentication", (char *) NULL},
+	{  0x0022, (char *) "ActiveD-Lighting", (char *) NULL},
+	{  0x0023, (char *) "PictureControl", (char *) NULL},
+	{  0x0024, (char *) "WorldTime", (char *) NULL},
+	{  0x0025, (char *) "ISOInfo", (char *) NULL},
+	{  0x002A, (char *) "VignetteControl", (char *) NULL},
+	{  0x002B, (char *) "DistortInfo", (char *) NULL},
+	{  0x0080, (char *) "ImageAdjustment", (char *) NULL},
+	{  0x0081, (char *) "ToneComp", (char *) NULL},
+	{  0x0082, (char *) "AuxiliaryLens", (char *) NULL},
+	{  0x0083, (char *) "LensType", (char *) NULL},
+    {  0x0084, (char *) "Lens", (char *) NULL},
+    {  0x0085, (char *) "ManualFocusDistance", (char *) NULL},
+    {  0x0086, (char *) "DigitalZoom", (char *) NULL},
+    {  0x0087, (char *) "FlashMode", (char *) NULL},
+    {  0x0088, (char *) "AFInfo", (char *) NULL},
+    {  0x0089, (char *) "ShootingMode", (char *) NULL},
+    {  0x008B, (char *) "LensFStops", (char *) NULL},
+    {  0x008C, (char *) "ContrastCurve", (char *) NULL},
+    {  0x008D, (char *) "ColorHue", (char *) NULL},
+    {  0x008F, (char *) "SceneMode", (char *) NULL},
+    {  0x0090, (char *) "LightSource", (char *) NULL},
+	{  0x0091, (char *) "ShotInfo", (char *) NULL},
+    {  0x0092, (char *) "HueAdjustment", (char *) NULL},
+	{  0x0093, (char *) "NEFCompression", (char *) NULL},
+    {  0x0094, (char *) "Saturation", (char *) NULL},
+    {  0x0095, (char *) "NoiseReduction", (char *) NULL},
+    {  0x0096, (char *) "LinearizationTable", (char *) NULL},
+	{  0x0097, (char *) "ColorBalance", (char *) NULL},
+	{  0x0098, (char *) "LensData", (char *) NULL},
+    {  0x0099, (char *) "RawImageCenter", (char *) NULL},
+    {  0x009A, (char *) "SensorPixelSize", (char *) NULL},
+    {  0x009C, (char *) "SceneAssist", (char *) NULL},
+    {  0x009E, (char *) "RetouchHistory", (char *) NULL},
+    {  0x00A0, (char *) "SerialNumber", (char *) NULL},
+    {  0x00A2, (char *) "ImageDataSize", (char *) NULL},
+    {  0x00A5, (char *) "ImageCount", (char *) NULL},
+    {  0x00A6, (char *) "DeletedImageCount", (char *) NULL},
+    {  0x00A7, (char *) "ShutterCount", (char *) NULL},
+	{  0x00A8, (char *) "FlashInfo", (char *) NULL},
+    {  0x00A9, (char *) "ImageOptimization", (char *) NULL},
+    {  0x00AA, (char *) "Saturation", (char *) NULL},
+    {  0x00AB, (char *) "VariProgram", (char *) NULL},
+    {  0x00AC, (char *) "ImageStabilization", (char *) NULL},
+    {  0x00AD, (char *) "AFResponse", (char *) NULL},
+    {  0x00B0, (char *) "MultiExposure", (char *) NULL},
+    {  0x00B1, (char *) "HighISONoiseReduction", (char *) NULL},
+    {  0x00B3, (char *) "ToningEffect", (char *) NULL},
+    {  0x00B6, (char *) "PowerUpTime", (char *) NULL},
+    {  0x00B7, (char *) "AFInfo2", (char *) NULL},
+    {  0x00B8, (char *) "FileInfo", (char *) NULL},
+    {  0x00B9, (char *) "AFTune", (char *) NULL},
+    {  0x00BD, (char *) "PictureControl", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) NULL},
+    {  0x0E01, (char *) "NikonCaptureData", (char *) NULL},
+    {  0x0E09, (char *) "NikonCaptureVersion", (char *) NULL},
+    {  0x0E0E, (char *) "NikonCaptureOffsets", (char *) NULL},
+    {  0x0E10, (char *) "NikonScanIFD", (char *) NULL},
+    {  0x0E1D, (char *) "NikonICCProfile", (char *) NULL},
+    {  0x0E1E, (char *) "NikonCaptureOutput", (char *) NULL},
+	{  0x0E22, (char *) "NEFBitDepth", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Panasonic / Leica maker note
+*/
+static TagInfo
+  exif_panasonic_tag_table[] =
+  {
+    {  0x0001, (char *) "ImageQuality", (char *) NULL},
+    {  0x0002, (char *) "FirmwareVersion", (char *) NULL},
+    {  0x0003, (char *) "WhiteBalance", (char *) NULL},
+    {  0x0007, (char *) "FocusMode", (char *) NULL},
+    {  0x000F, (char *) "AFAreaMode", (char *) NULL},
+    {  0x001A, (char *) "ImageStabilization", (char *) NULL},
+    {  0x001C, (char *) "MacroMode", (char *) NULL},
+    {  0x001F, (char *) "ShootingMode", (char *) NULL},
+    {  0x0020, (char *) "Audio", (char *) NULL},
+    {  0x0021, (char *) "DataDump", (char *) NULL},
+	{  0x0022, (char *) "EasyMode", (char *) NULL},
+    {  0x0023, (char *) "WhiteBalanceBias", (char *) NULL},
+    {  0x0024, (char *) "FlashBias", (char *) NULL},
+    {  0x0025, (char *) "InternalSerialNumber", (char *) NULL},
+	{  0x0026, (char *) "PanasonicExifVersion", (char *) NULL},
+    {  0x0028, (char *) "ColorEffect", (char *) NULL},
+	{  0x0029, (char *) "TimeSincePowerOn", (char *) NULL},
+    {  0x002A, (char *) "BurstMode", (char *) NULL},
+    {  0x002B, (char *) "SequenceNumber", (char *) NULL},
+    {  0x002C, (char *) "ContrastMode", (char *) NULL},
+    {  0x002D, (char *) "NoiseReduction", (char *) NULL},
+    {  0x002E, (char *) "SelfTimer", (char *) NULL},
+    {  0x0030, (char *) "Rotation", (char *) NULL},
+	{  0x0031, (char *) "AFAssistLamp", (char *) NULL},
+    {  0x0032, (char *) "ColorMode", (char *) NULL},
+	{  0x0033, (char *) "BabyAge_0x0033", (char *) NULL},
+	{  0x0034, (char *) "OpticalZoomMode", (char *) NULL},
+	{  0x0035, (char *) "ConversionLens", (char *) NULL},
+	{  0x0036, (char *) "TravelDay", (char *) NULL},
+	{  0x0039, (char *) "Contrast", (char *) NULL},
+	{  0x003A, (char *) "WorldTimeLocation", (char *) NULL},
+	{  0x003B, (char *) "TextStamp_0x003B", (char *) NULL},
+	{  0x003C, (char *) "ProgramISO", (char *) NULL},
+	{  0x003D, (char *) "AdvancedSceneMode", (char *) NULL},
+	{  0x003E, (char *) "TextStamp_0x003E", (char *) NULL},
+	{  0x003F, (char *) "FacesDetected", (char *) NULL},
+	{  0x0040, (char *) "Saturation", (char *) NULL},
+	{  0x0041, (char *) "Sharpness", (char *) NULL},
+	{  0x0042, (char *) "FilmMode", (char *) NULL},
+	{  0x0046, (char *) "WBAdjustAB", (char *) NULL},
+	{  0x0047, (char *) "WBAdjustGM", (char *) NULL},
+	{  0x004B, (char *) "PanasonicImageWidth", (char *) NULL},
+	{  0x004C, (char *) "PanasonicImageHeight", (char *) NULL},
+	{  0x004D, (char *) "AFPointPosition", (char *) NULL},
+	{  0x004E, (char *) "FaceDetInfo", (char *) "Panasonic FaceDetInfo Tags"},
+	{  0x0051, (char *) "LensType", (char *) NULL},
+	{  0x0052, (char *) "LensSerialNumber", (char *) NULL},
+	{  0x0053, (char *) "AccessoryType", (char *) NULL},
+	{  0x0059, (char *) "Transform", (char *) NULL},
+	{  0x005D, (char *) "IntelligentExposure", (char *) NULL},
+	{  0x0061, (char *) "FaceRecInfo", (char *) "Panasonic FaceRecInfo Tags"},
+	{  0x0062, (char *) "FlashWarning", (char *) NULL},
+	{  0x0063, (char *) "RecognizedFaceFlags?", (char *) NULL},
+	{  0x0065, (char *) "Title", (char *) NULL},
+	{  0x0066, (char *) "BabyName", (char *) NULL},
+	{  0x0067, (char *) "Location", (char *) NULL},
+	{  0x0069, (char *) "Country", (char *) NULL},
+	{  0x006B, (char *) "State", (char *) NULL},
+	{  0x006D, (char *) "City", (char *) NULL},
+	{  0x006F, (char *) "Landmark", (char *) NULL},
+	{  0x0070, (char *) "IntelligentResolution", (char *) NULL},
+	{  0x0079, (char *) "IntelligentD-Range", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) NULL},
+	{  0x8000, (char *) "MakerNoteVersion", (char *) NULL},
+	{  0x8001, (char *) "SceneMode", (char *) NULL},
+	{  0x8004, (char *) "WBRedLevel", (char *) NULL},
+	{  0x8005, (char *) "WBGreenLevel", (char *) NULL},
+	{  0x8006, (char *) "WBBlueLevel", (char *) NULL},
+	{  0x8007, (char *) "FlashFired", (char *) NULL},
+	{  0x8008, (char *) "TextStamp_0x8008", (char *) NULL},
+	{  0x8009, (char *) "TextStamp_0x8009", (char *) NULL},
+	{  0x8010, (char *) "BabyAge_0x8010", (char *) NULL},
+	{  0x8012, (char *) "Transform", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Pentax (Asahi) maker note type 1
+*/
+static TagInfo
+  exif_asahi_tag_table[] =
+  {
+    {  0x0001, (char *) "Capture Mode", (char *) NULL},
+    {  0x0002, (char *) "Quality Level", (char *) NULL},
+    {  0x0003, (char *) "Focus Mode", (char *) NULL},
+    {  0x0004, (char *) "Flash Mode", (char *) NULL},
+    {  0x0007, (char *) "White Balance", (char *) NULL},
+    {  0x000A, (char *) "Digital Zoom", (char *) NULL},
+    {  0x000B, (char *) "Sharpness", (char *) NULL},
+    {  0x000C, (char *) "Contrast", (char *) NULL},
+    {  0x000D, (char *) "Saturation", (char *) NULL},
+    {  0x0014, (char *) "ISO Speed", (char *) NULL},
+    {  0x0017, (char *) "Color", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) NULL},
+    {  0x1000, (char *) "Time Zone", (char *) NULL},
+    {  0x1001, (char *) "Daylight Savings", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Pentax maker note type 2
+*/
+static TagInfo
+  exif_pentax_tag_table[] =
+  {
+    {  0x0000, (char *) "PentaxVersion", (char *) NULL},
+    {  0x0001, (char *) "PentaxMode", (char *) NULL},
+    {  0x0002, (char *) "PreviewImageSize", (char *) NULL},
+    {  0x0003, (char *) "PreviewImageLength", (char *) NULL},
+    {  0x0004, (char *) "PreviewImageStart", (char *) NULL},
+    {  0x0005, (char *) "PentaxModelID", (char *) "Pentax PentaxModelID Values"},
+    {  0x0006, (char *) "Date", (char *) NULL},
+    {  0x0007, (char *) "Time", (char *) NULL},
+    {  0x0008, (char *) "Quality", (char *) NULL},
+    {  0x0009, (char *) "PentaxImageSize", (char *) NULL},
+    {  0x000B, (char *) "PictureMode", (char *) NULL},
+    {  0x000C, (char *) "FlashMode", (char *) NULL},
+    {  0x000D, (char *) "FocusMode", (char *) NULL},
+    {  0x000E, (char *) "AFPointSelected", (char *) NULL},
+    {  0x000F, (char *) "AFPointsInFocus", (char *) NULL},
+    {  0x0010, (char *) "FocusPosition", (char *) NULL},
+    {  0x0012, (char *) "ExposureTime", (char *) NULL},
+    {  0x0013, (char *) "FNumber", (char *) NULL},
+    {  0x0014, (char *) "ISO", (char *) NULL},
+	{  0x0015, (char *) "LightReading", (char *) NULL},
+    {  0x0016, (char *) "ExposureCompensation", (char *) NULL},
+    {  0x0017, (char *) "MeteringMode", (char *) NULL},
+    {  0x0018, (char *) "AutoBracketing", (char *) NULL},
+    {  0x0019, (char *) "WhiteBalance", (char *) NULL},
+    {  0x001A, (char *) "WhiteBalanceMode", (char *) NULL},
+    {  0x001B, (char *) "BlueBalance", (char *) NULL},
+    {  0x001C, (char *) "RedBalance", (char *) NULL},
+    {  0x001D, (char *) "FocalLength", (char *) NULL},
+    {  0x001E, (char *) "DigitalZoom", (char *) NULL},
+    {  0x001F, (char *) "Saturation", (char *) NULL},
+    {  0x0020, (char *) "Contrast", (char *) NULL},
+    {  0x0021, (char *) "Sharpness", (char *) NULL},
+    {  0x0022, (char *) "WorldTimeLocation", (char *) NULL},
+    {  0x0023, (char *) "HometownCity", (char *) "Pentax City Values"},
+    {  0x0024, (char *) "DestinationCity", (char *) "Pentax City Values"},
+    {  0x0025, (char *) "HometownDST", (char *) NULL},
+    {  0x0026, (char *) "DestinationDST", (char *) NULL},
+    {  0x0027, (char *) "DSPFirmwareVersion", (char *) NULL},
+    {  0x0028, (char *) "CPUFirmwareVersion", (char *) NULL},
+    {  0x0029, (char *) "FrameNumber", (char *) NULL},
+    {  0x002D, (char *) "EffectiveLV", (char *) NULL},
+    {  0x0032, (char *) "ImageProcessing", (char *) NULL},
+    {  0x0033, (char *) "PictureMode", (char *) NULL},
+    {  0x0034, (char *) "DriveMode", (char *) NULL},
+	{  0x0035, (char *) "SensorSize", (char *) NULL},
+    {  0x0037, (char *) "ColorSpace", (char *) NULL},
+    {  0x0039, (char *) "RawImageSize", (char *) NULL},
+	{  0x003C, (char *) "AFPointsInFocus", (char *) NULL},
+    {  0x003E, (char *) "PreviewImageBorders", (char *) NULL},
+    {  0x003F, (char *) "LensType", (char *) "Pentax LensType Values"},
+    {  0x0040, (char *) "SensitivityAdjust", (char *) NULL},
+    {  0x0041, (char *) "ImageProcessingCount", (char *) NULL},
+    {  0x0047, (char *) "CameraTemperature", (char *) NULL},
+    {  0x0048, (char *) "AELock", (char *) NULL},
+    {  0x0049, (char *) "NoiseReduction", (char *) NULL},
+    {  0x004D, (char *) "FlashExposureComp", (char *) NULL},
+    {  0x004F, (char *) "ImageTone", (char *) NULL},
+	{  0x0050, (char *) "ColorTemperature", (char *) NULL},
+    {  0x005C, (char *) "ShakeReductionInfo", (char *) "Pentax SRInfo Tags"},
+    {  0x005D, (char *) "ShutterCount", (char *) NULL},
+	{  0x0060, (char *) "FaceInfo", (char *) "Pentax FaceInfo Tags"},
+	{  0x0067, (char *) "Hue", (char *) NULL},
+	{  0x0068, (char *) "AWBInfo", (char *) "Pentax AWBInfo Tags"},
+    {  0x0069, (char *) "DynamicRangeExpansion", (char *) NULL},
+	{  0x006B, (char *) "TimeInfo", (char *) "Pentax TimeInfo Tags"},
+	{  0x006C, (char *) "HighLowKeyAdj", (char *) NULL},
+	{  0x006D, (char *) "ContrastHighlight", (char *) NULL},
+	{  0x006E, (char *) "ContrastShadow", (char *) NULL},
+	{  0x006F, (char *) "ContrastHighlightShadowAdj", (char *) NULL},
+	{  0x0070, (char *) "FineSharpness", (char *) NULL},
+    {  0x0071, (char *) "HighISONoiseReduction", (char *) NULL},
+    {  0x0072, (char *) "AFAdjustment", (char *) NULL},
+	{  0x0073, (char *) "MonochromeFilterEffect", (char *) NULL},
+	{  0x0074, (char *) "MonochromeToning", (char *) NULL},
+	{  0x0076, (char *) "FaceDetect", (char *) NULL},
+	{  0x0077, (char *) "FaceDetectFrameSize", (char *) NULL},
+	{  0x0079, (char *) "ShadowCompensation", (char *) NULL},
+	{  0x007A, (char *) "ISOAutoParameters", (char *) NULL},
+	{  0x007B, (char *) "CrossProcess", (char *) NULL},
+	{  0x007D, (char *) "LensCorr", (char *) "Pentax LensCorr Tags"},
+	{  0x007F, (char *) "BleachBypassToning", (char *) NULL},
+    {  0x0200, (char *) "BlackPoint", (char *) NULL},
+    {  0x0201, (char *) "WhitePoint", (char *) NULL},
+    {  0x0203, (char *) "ColorMatrixA", (char *) NULL},
+    {  0x0204, (char *) "ColorMatrixB", (char *) NULL},
+    {  0x0205, (char *) "CameraSettings", (char *) "Pentax CameraSettings Tags"},
+	{  0x0206, (char *) "AEInfo", (char *) "Pentax AEInfo Tags"},
+    {  0x0207, (char *) "LensInfo", (char *) "Pentax LensInfo Tags"},
+    {  0x0208, (char *) "FlashInfo", (char *) "Pentax FlashInfo Tags"},
+    {  0x0209, (char *) "AEMeteringSegments", (char *) NULL},
+    {  0x020A, (char *) "FlashMeteringSegments", (char *) NULL},
+    {  0x020B, (char *) "SlaveFlashMeteringSegments", (char *) NULL},
+    {  0x020D, (char *) "WB_RGGBLevelsDaylight", (char *) NULL},
+    {  0x020E, (char *) "WB_RGGBLevelsShade", (char *) NULL},
+    {  0x020F, (char *) "WB_RGGBLevelsCloudy", (char *) NULL},
+    {  0x0210, (char *) "WB_RGGBLevelsTungsten", (char *) NULL},
+    {  0x0211, (char *) "WB_RGGBLevelsFluorescentD", (char *) NULL},
+    {  0x0212, (char *) "WB_RGGBLevelsFluorescentN", (char *) NULL},
+    {  0x0213, (char *) "WB_RGGBLevelsFluorescentW", (char *) NULL},
+    {  0x0214, (char *) "WB_RGGBLevelsFlash", (char *) NULL},
+    {  0x0215, (char *) "CameraInfo", (char *) "Pentax CameraInfo Tags"},
+    {  0x0216, (char *) "BatteryInfo", (char *) "Pentax BatteryInfo Tags"},
+    {  0x021B, (char *) "SaturationInfo", (char *) NULL},
+    {  0x021F, (char *) "AFInfo", (char *) "Pentax AFInfo Tags"},
+    {  0x0222, (char *) "ColorInfo", (char *) "Pentax ColorInfo Tags"},
+    {  0x0224, (char *) "EVStepInfo", (char *) "Pentax EVStepInfo Tags"},
+	{  0x0226, (char *) "ShotInfo", (char *) "Pentax ShotInfo Tags"},
+	{  0x0227, (char *) "FacePos", (char *) "Pentax FacePos Tags"},
+	{  0x0228, (char *) "FaceSize", (char *) "Pentax FaceSize Tags"},
+	{  0x0229, (char *) "SerialNumber", (char *) NULL},
+	{  0x022A, (char *) "FilterInfo", (char *) "Pentax FilterInfo Tags"},
+	{  0x022B, (char *) "LevelInfo", (char *) "Pentax LevelInfo Tags"},
+	{  0x022E, (char *) "Artist", (char *) NULL},
+	{  0x022F, (char *) "Copyright", (char *) NULL},
+	{  0x0230, (char *) "FirmwareVersion", (char *) NULL},
+	{  0x0231, (char *) "ContrastDetectAFArea", (char *) NULL},
+	{  0x0235, (char *) "CrossProcessParams", (char *) NULL},
+	{  0x03FE, (char *) "DataDump", (char *) NULL},
+    {  0x0402, (char *) "ToneCurve", (char *) NULL},
+    {  0x0403, (char *) "ToneCurves", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) NULL},
+    {  0x1000, (char *) "HometownCityCode", (char *) NULL},
+    {  0x1001, (char *) "DestinationCityCode", (char *) NULL},
+    {  0x2000, (char *) "PreviewImageData", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Sony maker note
+*/
+static TagInfo
+  exif_sony_tag_table[] =
+  {
+    {  0x0102, (char *) "Quality", (char *) NULL},
+    {  0x0104, (char *) "FlashExposureComp", (char *) NULL},
+    {  0x0105, (char *) "Teleconverter", (char *) NULL},
+    {  0x0112, (char *) "WhiteBalanceFineTune", (char *) NULL},
+    {  0x0114, (char *) "CameraSettings", (char *) NULL},
+    {  0x0115, (char *) "WhiteBalance", (char *) NULL},
+    {  0x0E00, (char *) "PrintIM", (char *) NULL},
+    {  0x1000, (char *) "MultiBurstMode", (char *) NULL},
+    {  0x1001, (char *) "MultiBurstImageWidth", (char *) NULL},
+    {  0x1002, (char *) "MultiBurstImageHeight", (char *) NULL},
+    {  0x1003, (char *) "Panorama", (char *) NULL},
+    {  0x2001, (char *) "PreviewImage", (char *) NULL},
+    {  0x2004, (char *) "Contrast", (char *) NULL},
+    {  0x2005, (char *) "Saturation", (char *) NULL},
+    {  0x2006, (char *) "Sharpness", (char *) NULL},
+    {  0x2007, (char *) "Brightness", (char *) NULL},
+    {  0x2008, (char *) "LongExposureNoiseReduction", (char *) NULL},
+    {  0x2009, (char *) "HighISONoiseReduction", (char *) NULL},
+    {  0x200A, (char *) "HDR", (char *) NULL},
+    {  0x200B, (char *) "MultiFrameNoiseReduction", (char *) NULL},
+    {  0x3000, (char *) "ShotInfo", (char *) NULL},
+    {  0xB000, (char *) "FileFormat", (char *) NULL},
+    {  0xB001, (char *) "SonyModelID", (char *) NULL},
+    {  0xB020, (char *) "ColorReproduction", (char *) NULL},
+    {  0xB021, (char *) "ColorTemperature", (char *) NULL},
+    {  0xB022, (char *) "ColorCompensationFilter", (char *) NULL},
+    {  0xB023, (char *) "SceneMode", (char *) NULL},
+    {  0xB024, (char *) "ZoneMatching", (char *) NULL},
+    {  0xB025, (char *) "DynamicRangeOptimizer", (char *) NULL},
+    {  0xB026, (char *) "ImageStabilization", (char *) NULL},
+    {  0xB027, (char *) "LensType", (char *) NULL},
+    {  0xB028, (char *) "MinoltaMakerNote", (char *) NULL},
+    {  0xB029, (char *) "ColorMode", (char *) NULL},
+    {  0xB02B, (char *) "FullImageSize", (char *) NULL},
+    {  0xB02C, (char *) "PreviewImageSize", (char *) NULL},
+    {  0xB040, (char *) "Macro", (char *) NULL},
+    {  0xB041, (char *) "ExposureMode", (char *) NULL},
+    {  0xB042, (char *) "FocusMode", (char *) NULL},
+    {  0xB043, (char *) "AFMode", (char *) NULL},
+    {  0xB044, (char *) "AFIlluminator", (char *) NULL},
+    {  0xB047, (char *) "Quality2", (char *) NULL},
+    {  0xB048, (char *) "FlashLevel", (char *) NULL},
+    {  0xB049, (char *) "ReleaseMode", (char *) NULL},
+    {  0xB04A, (char *) "SequenceNumber", (char *) NULL},
+    {  0xB04B, (char *) "Anti-Blur", (char *) NULL},
+    {  0xB04E, (char *) "LongExposureNoiseReduction", (char *) NULL},
+    {  0xB04F, (char *) "DynamicRangeOptimizer", (char *) NULL},
+    {  0xB052, (char *) "IntelligentAuto", (char *) NULL},
+    {  0xB054, (char *) "WhiteBalance2", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Sigma SD1 maker note
+*/
+static TagInfo
+  exif_sigma_sd1_tag_table[] =
+  {
+    {  0x0002, (char *) "SerialNumber", (char *) NULL},
+    {  0x0003, (char *) "DriveMode", (char *) NULL},
+    {  0x0004, (char *) "ResolutionMode", (char *) NULL},
+    {  0x0005, (char *) "AFMode", (char *) NULL},
+    {  0x0006, (char *) "FocusSetting", (char *) NULL},
+    {  0x0007, (char *) "WhiteBalance", (char *) NULL},
+    {  0x0008, (char *) "ExposureMode", (char *) NULL},
+    {  0x0009, (char *) "MeteringMode", (char *) NULL},
+    {  0x000A, (char *) "LensFocalRange", (char *) NULL},
+    {  0x000B, (char *) "ColorSpace", (char *) NULL},
+    {  0x000C, (char *) "ExposureCompensation", (char *) NULL},
+    {  0x000D, (char *) "Contrast", (char *) NULL},
+    {  0x000E, (char *) "Shadow", (char *) NULL},
+    {  0x000F, (char *) "Highlight", (char *) NULL},
+    {  0x0010, (char *) "Saturation", (char *) NULL},
+    {  0x0011, (char *) "Sharpness", (char *) NULL},
+    {  0x0012, (char *) "X3FillLight", (char *) NULL},
+    {  0x0014, (char *) "ColorAdjustment", (char *) NULL},
+    {  0x0015, (char *) "AdjustmentMode", (char *) NULL},
+    {  0x0016, (char *) "Quality", (char *) NULL},
+    {  0x0017, (char *) "Firmware", (char *) NULL},
+    {  0x0018, (char *) "Software", (char *) NULL},
+    {  0x0019, (char *) "AutoBracket", (char *) NULL},
+    {  0x001A, (char *) "ChrominanceNoiseReduction", (char *) NULL},
+    {  0x001B, (char *) "LuminanceNoiseReduction", (char *) NULL},
+    {  0x001C, (char *) "PreviewImageStart", (char *) NULL},
+    {  0x001D, (char *) "PreviewImageLength", (char *) NULL},
+    {  0x001F, (char *) "MakerNoteVersion", (char *) NULL},
+    {  0x0026, (char *) "FileFormat", (char *) NULL},
+    {  0x002C, (char *) "ColorMode", (char *) NULL},
+    {  0x0030, (char *) "Calibration", (char *) NULL},
+    {  0x0048, (char *) "LensApertureRange", (char *) NULL},
+    {  0x0049, (char *) "FNumber", (char *) NULL},
+    {  0x004A, (char *) "ExposureTime", (char *) NULL},
+    {  0x004B, (char *) "ExposureTime2", (char *) NULL},
+    {  0x004D, (char *) "ExposureCompensation_SD1", (char *) NULL},
+    {  0x0055, (char *) "SensorTemperature", (char *) NULL},
+    {  0x0056, (char *) "FlashExposureComp", (char *) NULL},
+    {  0x0057, (char *) "Firmware_SD1", (char *) NULL},
+    {  0x0058, (char *) "WhiteBalance", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+/**
+Sigma / Foveon maker note (others than SD1 models)
+NB: many tags are not consistent between different models
+*/
+static TagInfo
+  exif_sigma_foveon_tag_table[] =
+  {
+    {  0x0002, (char *) "SerialNumber", (char *) NULL},
+    {  0x0003, (char *) "DriveMode", (char *) NULL},
+    {  0x0004, (char *) "ResolutionMode", (char *) NULL},
+    {  0x0005, (char *) "AFMode", (char *) NULL},
+    {  0x0006, (char *) "FocusSetting", (char *) NULL},
+    {  0x0007, (char *) "WhiteBalance", (char *) NULL},
+    {  0x0008, (char *) "ExposureMode", (char *) NULL},
+    {  0x0009, (char *) "MeteringMode", (char *) NULL},
+    {  0x000A, (char *) "LensFocalRange", (char *) NULL},
+    {  0x000B, (char *) "ColorSpace", (char *) NULL},
+    {  0x000C, (char *) "ExposureCompensation", (char *) NULL},
+    {  0x000D, (char *) "Contrast", (char *) NULL},
+    {  0x000E, (char *) "Shadow", (char *) NULL},
+    {  0x000F, (char *) "Highlight", (char *) NULL},
+    {  0x0010, (char *) "Saturation", (char *) NULL},
+    {  0x0011, (char *) "Sharpness", (char *) NULL},
+    {  0x0012, (char *) "X3FillLight", (char *) NULL},
+    {  0x0014, (char *) "ColorAdjustment", (char *) NULL},
+    {  0x0015, (char *) "AdjustmentMode", (char *) NULL},
+    {  0x0016, (char *) "Quality", (char *) NULL},
+    {  0x0017, (char *) "Firmware", (char *) NULL},
+    {  0x0018, (char *) "Software", (char *) NULL},
+    {  0x0019, (char *) "AutoBracket", (char *) NULL},
+    {  0x001A, (char *) "PreviewImageStart", (char *) NULL},
+    {  0x001B, (char *) "PreviewImageLength", (char *) NULL},
+    {  0x001C, (char *) "PreviewImageSize", (char *) NULL},
+    {  0x001D, (char *) "MakerNoteVersion", (char *) NULL},
+    {  0x001F, (char *) "AFPoint", (char *) NULL},
+    {  0x0022, (char *) "FileFormat", (char *) NULL},
+    {  0x0024, (char *) "Calibration", (char *) NULL},
+    {  0x002C, (char *) "ColorMode", (char *) NULL},
+    {  0x0030, (char *) "LensApertureRange", (char *) NULL},
+    {  0x0031, (char *) "FNumber", (char *) NULL},
+    {  0x0032, (char *) "ExposureTime", (char *) NULL},
+    {  0x0033, (char *) "ExposureTime2", (char *) NULL},
+    {  0x0034, (char *) "BurstShot", (char *) NULL},
+    {  0x0035, (char *) "ExposureCompensation", (char *) NULL},
+    {  0x0039, (char *) "SensorTemperature", (char *) NULL},
+    {  0x003A, (char *) "FlashExposureComp", (char *) NULL},
+    {  0x003B, (char *) "Firmware", (char *) NULL},
+    {  0x003C, (char *) "WhiteBalance", (char *) NULL},
+    {  0x003D, (char *) "PictureMode", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+// --------------------------------------------------------------------------
+// IPTC tags definition
+// --------------------------------------------------------------------------
+
+static TagInfo
+  iptc_tag_table[] =
+  {
+	  // IPTC-NAA IIM version 4
+    {  0x0200 +   0, (char *) "ApplicationRecordVersion", (char *) "Application Record Version"},
+    {  0x0200 +   3, (char *) "ObjectTypeReference", (char *) "Object Type Reference"},
+    {  0x0200 +   4, (char *) "ObjectAttributeReference", (char *) "Object Attribute Reference"},
+    {  0x0200 +   5, (char *) "ObjectName", (char *) "Title"},
+    {  0x0200 +   7, (char *) "EditStatus", (char *) "Edit Status"},
+    {  0x0200 +   8, (char *) "EditorialUpdate", (char *) "Editorial Update"},
+    {  0x0200 +  10, (char *) "Urgency", (char *) "Urgency"},
+    {  0x0200 +  12, (char *) "SubjectReference", (char *) "Subject Reference"},
+    {  0x0200 +  15, (char *) "Category", (char *) "Category"},
+    {  0x0200 +  20, (char *) "SupplementalCategories", (char *) "Supplemental Categories"},
+    {  0x0200 +  22, (char *) "FixtureIdentifier", (char *) "Fixture Identifier"},
+    {  0x0200 +  25, (char *) "Keywords", (char *) "Keywords"},
+    {  0x0200 +  26, (char *) "ContentLocationCode", (char *) "Content Location Code"},
+    {  0x0200 +  27, (char *) "ContentLocationName", (char *) "Content Location Name"},
+    {  0x0200 +  30, (char *) "ReleaseDate", (char *) "Release Date"},
+    {  0x0200 +  35, (char *) "ReleaseTime", (char *) "Release Time"},
+    {  0x0200 +  37, (char *) "ExpirationDate", (char *) "Expiration Date"},
+    {  0x0200 +  38, (char *) "ExpirationTime", (char *) "Expiration Time"},
+    {  0x0200 +  40, (char *) "SpecialInstructions", (char *) "Instructions"},
+    {  0x0200 +  42, (char *) "ActionAdvised", (char *) "Action Advised"},
+    {  0x0200 +  45, (char *) "ReferenceService", (char *) "Reference Service"},
+    {  0x0200 +  47, (char *) "ReferenceDate", (char *) "Reference Date"},
+    {  0x0200 +  50, (char *) "ReferenceNumber", (char *) "Reference Number"},
+    {  0x0200 +  55, (char *) "DateCreated", (char *) "Date Created"},
+    {  0x0200 +  60, (char *) "TimeCreated", (char *) "Time Created"},
+    {  0x0200 +  62, (char *) "DigitalCreationDate", (char *) "Digital Creation Date"},
+    {  0x0200 +  63, (char *) "DigitalCreationTime", (char *) "Digital Creation Time"},
+    {  0x0200 +  65, (char *) "OriginatingProgram", (char *) "Originating Program"},
+    {  0x0200 +  70, (char *) "ProgramVersion", (char *) "Program Version"},
+    {  0x0200 +  75, (char *) "ObjectCycle", (char *) "Object Cycle"},
+    {  0x0200 +  80, (char *) "By-line", (char *) "Author"},
+    {  0x0200 +  85, (char *) "By-lineTitle", (char *) "Author's Position"},
+    {  0x0200 +  90, (char *) "City", (char *) "City"},
+    {  0x0200 +  92, (char *) "SubLocation", (char *) "Sub-Location"},
+    {  0x0200 +  95, (char *) "Province-State", (char *) "State/Province"},
+    {  0x0200 + 100, (char *) "Country-PrimaryLocationCode", (char *) "Country Code"},
+    {  0x0200 + 101, (char *) "Country-PrimaryLocationName", (char *) "Country Name"},
+    {  0x0200 + 103, (char *) "OriginalTransmissionReference", (char *) "Transmission Reference"},
+    {  0x0200 + 105, (char *) "Headline", (char *) "Headline"},
+    {  0x0200 + 110, (char *) "Credit", (char *) "Credit"},
+    {  0x0200 + 115, (char *) "Source", (char *) "Source"},
+    {  0x0200 + 116, (char *) "CopyrightNotice", (char *) "Copyright Notice"},
+    {  0x0200 + 118, (char *) "Contact", (char *) "Contact"},
+    {  0x0200 + 120, (char *) "Caption-Abstract", (char *) "Caption"},
+    {  0x0200 + 122, (char *) "Writer-Editor", (char *) "Caption Writer"},
+    {  0x0200 + 125, (char *) "RasterizedCaption", (char *) "Rasterized Caption"},
+    {  0x0200 + 130, (char *) "ImageType", (char *) "Image Type"},
+    {  0x0200 + 131, (char *) "ImageOrientation", (char *) "Image Orientation"},
+    {  0x0200 + 135, (char *) "LanguageIdentifier", (char *) "Language Identifier"},
+    {  0x0200 + 150, (char *) "AudioType", (char *) "Audio Type"},
+    {  0x0200 + 151, (char *) "AudioSamplingRate", (char *) "Audio Sampling Rate"},
+    {  0x0200 + 152, (char *) "AudioSamplingResolution", (char *) "Audio Sampling Resolution"},
+    {  0x0200 + 153, (char *) "AudioDuration", (char *) "Audio Duration"},
+    {  0x0200 + 154, (char *) "AudioOutcue", (char *) "Audio Outcue"},
+		// Metadata seen in other softwares (see also http://owl.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html#ApplicationRecord)
+    {  0x0200 + 184, (char *) "JobID", (char *) "Job ID"},
+    {  0x0200 + 185, (char *) "MasterDocumentID", (char *) "Master Document ID"},
+    {  0x0200 + 186, (char *) "ShortDocumentID", (char *) "Short Document ID"},
+    {  0x0200 + 187, (char *) "UniqueDocumentID", (char *) "Unique Document ID"},
+    {  0x0200 + 188, (char *) "OwnerID", (char *) "Owner ID"},
+		// IPTC-NAA IIM version 4
+    {  0x0200 + 200, (char *) "ObjectPreviewFileFormat", (char *) "Object Preview File Format"},
+    {  0x0200 + 201, (char *) "ObjectPreviewFileVersion", (char *) "Object Preview File Version"},
+    {  0x0200 + 202, (char *) "ObjectPreviewData", (char *) "Audio Outcue"},
+		// Metadata seen in other softwares (see also http://owl.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html#ApplicationRecord)
+    {  0x0200 + 221, (char *) "Prefs", (char *) "PhotoMechanic preferences"},
+    {  0x0200 + 225, (char *) "ClassifyState", (char *) "Classify State"},
+    {  0x0200 + 228, (char *) "SimilarityIndex", (char *) "Similarity Index"},
+    {  0x0200 + 230, (char *) "DocumentNotes", (char *) "Document Notes"},
+    {  0x0200 + 231, (char *) "DocumentHistory", (char *) "Document History"},
+    {  0x0200 + 232, (char *) "ExifCameraInfo", (char *) "Exif Camera Info"},
+
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+// --------------------------------------------------------------------------
+// GeoTIFF tags definition
+// --------------------------------------------------------------------------
+
+static TagInfo
+  geotiff_tag_table[] =
+  {
+    {  0x830E, (char *) "GeoPixelScale", (char *) NULL},
+    {  0x8480, (char *) "Intergraph TransformationMatrix", (char *) NULL},
+    {  0x8482, (char *) "GeoTiePoints", (char *) NULL},
+    {  0x85D7, (char *) "JPL Carto IFD offset", (char *) NULL},
+    {  0x85D8, (char *) "GeoTransformationMatrix", (char *) NULL},
+    {  0x87AF, (char *) "GeoKeyDirectory", (char *) NULL},
+    {  0x87B0, (char *) "GeoDoubleParams", (char *) NULL},
+    {  0x87B1, (char *) "GeoASCIIParams", (char *) NULL},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+// --------------------------------------------------------------------------
+// Animation tags definition
+// --------------------------------------------------------------------------
+
+static TagInfo
+  animation_tag_table[] =
+  {
+    {  0x0001, (char *) "LogicalWidth", (char *) "Logical width"},
+    {  0x0002, (char *) "LogicalHeight", (char *) "Logical height"},
+    {  0x0003, (char *) "GlobalPalette", (char *) "Global Palette"},
+    {  0x0004, (char *) "Loop", (char *) "loop"},
+    {  0x1001, (char *) "FrameLeft", (char *) "Frame left"},
+    {  0x1002, (char *) "FrameTop", (char *) "Frame top"},
+    {  0x1003, (char *) "NoLocalPalette", (char *) "No Local Palette"},
+    {  0x1004, (char *) "Interlaced", (char *) "Interlaced"},
+    {  0x1005, (char *) "FrameTime", (char *) "Frame display time"},
+    {  0x1006, (char *) "DisposalMethod", (char *) "Frame disposal method"},
+    {  0x0000, (char *) NULL, (char *) NULL}
+  };
+
+// --------------------------------------------------------------------------
+// TagLib class definition
+// --------------------------------------------------------------------------
+
+
+/**
+This is where the tag info tables are initialized
+*/
+TagLib::TagLib() {
+	// initialize all known metadata models
+	// ====================================
+
+	// Exif
+	addMetadataModel(TagLib::EXIF_MAIN, exif_exif_tag_table);
+	addMetadataModel(TagLib::EXIF_EXIF, exif_exif_tag_table);
+	addMetadataModel(TagLib::EXIF_GPS, exif_gps_tag_table);
+	addMetadataModel(TagLib::EXIF_INTEROP, exif_interop_tag_table);
+
+	// Exif maker note
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_CANON, exif_canon_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_CASIOTYPE1, exif_casio_type1_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_CASIOTYPE2, exif_casio_type2_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_FUJIFILM, exif_fujifilm_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_KYOCERA, exif_kyocera_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_MINOLTA, exif_minolta_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_NIKONTYPE1, exif_nikon_type1_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_NIKONTYPE2, exif_nikon_type2_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_NIKONTYPE3, exif_nikon_type3_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_OLYMPUSTYPE1, exif_olympus_type1_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_PANASONIC, exif_panasonic_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_ASAHI, exif_asahi_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_PENTAX, exif_pentax_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_SONY, exif_sony_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_SIGMA_SD1, exif_sigma_sd1_tag_table);
+	addMetadataModel(TagLib::EXIF_MAKERNOTE_SIGMA_FOVEON, exif_sigma_foveon_tag_table);
+
+	// IPTC/NAA
+	addMetadataModel(TagLib::IPTC, iptc_tag_table);
+
+	// GeoTIFF
+	addMetadataModel(TagLib::GEOTIFF, geotiff_tag_table);
+
+	// Animation
+	addMetadataModel(TagLib::ANIMATION, animation_tag_table);
+}
+
+BOOL TagLib::addMetadataModel(MDMODEL md_model, TagInfo *tag_table) {
+	// check that the model doesn't already exist
+	if((_table_map.find(md_model) == _table_map.end()) && (tag_table != NULL)) {
+
+		// add the tag description table
+		TAGINFO *info_map = new(std::nothrow) TAGINFO();
+		if(!info_map) return FALSE;
+
+		for(int i = 0; ; i++) {
+			if((tag_table[i].tag == 0) && (tag_table[i].fieldname == NULL))
+				break;
+			(*info_map)[tag_table[i].tag] = &tag_table[i];
+		}
+
+		// add the metadata model
+		_table_map[md_model] = info_map;
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+TagLib::~TagLib() {
+	// delete metadata models
+	for(TABLEMAP::iterator i = _table_map.begin(); i != _table_map.end(); i++) {
+		TAGINFO *info_map = (*i).second;
+		delete info_map;
+	}
+}
+
+
+TagLib& 
+TagLib::instance() {
+	static TagLib s;
+	return s;
+}
+
+const TagInfo* 
+TagLib::getTagInfo(MDMODEL md_model, WORD tagID) {
+
+	if(_table_map.find(md_model) != _table_map.end()) {
+
+		TAGINFO *info_map = (TAGINFO*)_table_map[md_model];
+		if(info_map->find(tagID) != info_map->end()) {
+			return (*info_map)[tagID];
+		}
+	}
+	return NULL;
+}
+
+const char* 
+TagLib::getTagFieldName(MDMODEL md_model, WORD tagID, char *defaultKey) {
+
+	const TagInfo *info = getTagInfo(md_model, tagID);
+	if(NULL == info) {
+		if(defaultKey != NULL) {
+			sprintf(defaultKey, "Tag 0x%04X", tagID);
+			return &defaultKey[0];
+		} else {
+			return NULL;
+		}
+	}
+
+	return info->fieldname;
+}
+
+const char* 
+TagLib::getTagDescription(MDMODEL md_model, WORD tagID) {
+
+	const TagInfo *info = getTagInfo(md_model, tagID);
+	if(info) {
+		return info->description;
+	}
+
+	return NULL;
+}
+
+int TagLib::getTagID(MDMODEL md_model, const char *key) {
+
+	if(_table_map.find(md_model) != _table_map.end()) {
+
+		TAGINFO *info_map = (TAGINFO*)_table_map[md_model];
+		for(TAGINFO::iterator i = info_map->begin(); i != info_map->end(); i++) {
+			const TagInfo *info = (*i).second;
+			if(info && (strcmp(info->fieldname, key) == 0)) {
+				return (int)info->tag;
+			}
+		}
+	}
+	return -1;
+}
+
+FREE_IMAGE_MDMODEL 
+TagLib::getFreeImageModel(MDMODEL model) {
+	switch(model) {
+		case EXIF_MAIN:
+			return FIMD_EXIF_MAIN;
+
+		case EXIF_EXIF:
+			return FIMD_EXIF_EXIF;
+
+		case EXIF_GPS: 
+			return FIMD_EXIF_GPS;
+
+		case EXIF_INTEROP:
+			return FIMD_EXIF_INTEROP;
+
+		case EXIF_MAKERNOTE_CANON:
+		case EXIF_MAKERNOTE_CASIOTYPE1:
+		case EXIF_MAKERNOTE_CASIOTYPE2:
+		case EXIF_MAKERNOTE_FUJIFILM:
+		case EXIF_MAKERNOTE_KYOCERA:
+		case EXIF_MAKERNOTE_MINOLTA:
+		case EXIF_MAKERNOTE_NIKONTYPE1:
+		case EXIF_MAKERNOTE_NIKONTYPE2:
+		case EXIF_MAKERNOTE_NIKONTYPE3:
+		case EXIF_MAKERNOTE_OLYMPUSTYPE1:
+		case EXIF_MAKERNOTE_PANASONIC:
+		case EXIF_MAKERNOTE_ASAHI:
+		case EXIF_MAKERNOTE_PENTAX:
+		case EXIF_MAKERNOTE_SONY:
+		case EXIF_MAKERNOTE_SIGMA_SD1:
+		case EXIF_MAKERNOTE_SIGMA_FOVEON:
+			return FIMD_EXIF_MAKERNOTE;
+
+		case IPTC:
+			return FIMD_IPTC;
+
+		case GEOTIFF:
+			return FIMD_GEOTIFF;
+
+		case ANIMATION:
+			return FIMD_ANIMATION;
+	}
+
+	return FIMD_NODATA;
+}
+
diff --git a/files/Source/Metadata/XTIFF.cpp b/files/Source/Metadata/XTIFF.cpp
new file mode 100644
index 0000000..d677492
--- /dev/null
+++ b/files/Source/Metadata/XTIFF.cpp
@@ -0,0 +1,766 @@
+// ==========================================================
+// Metadata functions implementation
+// Extended TIFF Directory GEO Tag Support
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Thorsten Radde (support@IdealSoftware.com)
+// - Berend Engelbrecht (softwarecave@users.sourceforge.net)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// Based on the LibTIFF xtiffio sample and on LibGeoTIFF
+//
+// 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 "third_party/tiff/libtiff/tiffiop.h"
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageTag.h"
+#include "FIRational.h"
+
+// ----------------------------------------------------------
+//   Extended TIFF Directory GEO Tag Support
+// ----------------------------------------------------------
+
+/**
+  Tiff info structure.
+  Entry format:
+  { TAGNUMBER, ReadCount, WriteCount, DataType, FIELDNUM, OkToChange, PassDirCountOnSet, AsciiName }
+
+  For ReadCount, WriteCount, -1 = unknown.
+*/
+static const TIFFFieldInfo xtiffFieldInfo[] = {
+  { TIFFTAG_GEOPIXELSCALE, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoPixelScale" },
+  { TIFFTAG_INTERGRAPH_MATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"Intergraph TransformationMatrix" },
+  { TIFFTAG_GEOTRANSMATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTransformationMatrix" },
+  { TIFFTAG_GEOTIEPOINTS,	-1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTiePoints" },
+  { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoKeyDirectory" },
+  { TIFFTAG_GEODOUBLEPARAMS, -1, -1, TIFF_DOUBLE,	FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoDoubleParams" },
+  { TIFFTAG_GEOASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, (char*) "GeoASCIIParams" },
+  { TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, TRUE, TRUE, (char*)"JPL Carto IFD offset" }  /** Don't use this! **/
+};
+
+static void
+_XTIFFLocalDefaultDirectory(TIFF *tif) {
+	int tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
+	// Install the extended Tag field info
+	TIFFMergeFieldInfo(tif, xtiffFieldInfo, tag_size);
+}
+
+static TIFFExtendProc _ParentExtender;
+
+/**
+This is the callback procedure, and is
+called by the DefaultDirectory method
+every time a new TIFF directory is opened.
+*/
+static void
+_XTIFFDefaultDirectory(TIFF *tif) {
+	// set up our own defaults
+	_XTIFFLocalDefaultDirectory(tif);
+
+	/*
+	Since an XTIFF client module may have overridden
+	the default directory method, we call it now to
+	allow it to set up the rest of its own methods.
+	*/
+	if (_ParentExtender) {
+		(*_ParentExtender)(tif);
+	}
+}
+
+/**
+XTIFF Initializer -- sets up the callback procedure for the TIFF module.
+@see PluginTIFF::InitTIFF
+*/
+void
+XTIFFInitialize(void) {
+	static int first_time = 1;
+
+	if (! first_time) {
+		return; /* Been there. Done that. */
+	}
+	first_time = 0;
+
+	// Grab the inherited method and install
+	_ParentExtender = TIFFSetTagExtender(_XTIFFDefaultDirectory);
+}
+
+// ----------------------------------------------------------
+//   GeoTIFF tag reading / writing
+// ----------------------------------------------------------
+
+BOOL
+tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
+	char defaultKey[16];
+
+	// first check for a mandatory tag
+	{
+		short tag_count = 0;
+		void* data = NULL;
+		
+		if(!TIFFGetField(tif, TIFFTAG_GEOKEYDIRECTORY, &tag_count, &data)) {
+			// no GeoTIFF tag here
+			return TRUE;
+		}
+	}
+
+	// next, read GeoTIFF tags
+
+	const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
+
+	TagLib& tag_lib = TagLib::instance();
+
+	for(size_t i = 0; i < tag_size; i++) {
+
+		const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i];
+
+		if(fieldInfo->field_type == TIFF_ASCII) {
+			char *params = NULL;
+
+			if(TIFFGetField(tif, fieldInfo->field_tag, &params)) {
+				// create a tag
+				FITAG *tag = FreeImage_CreateTag();
+				if(!tag) {
+					return FALSE;
+				}
+
+				WORD tag_id = (WORD)fieldInfo->field_tag;
+
+				FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)fieldInfo->field_type);
+				FreeImage_SetTagID(tag, tag_id);
+				FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::GEOTIFF, tag_id, defaultKey));
+				FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::GEOTIFF, tag_id));
+				FreeImage_SetTagLength(tag, (DWORD)strlen(params) + 1);
+				FreeImage_SetTagCount(tag, FreeImage_GetTagLength(tag));
+				FreeImage_SetTagValue(tag, params);
+				FreeImage_SetMetadata(FIMD_GEOTIFF, dib, FreeImage_GetTagKey(tag), tag);
+
+				// delete the tag
+				FreeImage_DeleteTag(tag);
+			}
+		} else {
+			short tag_count = 0;
+			void* data = NULL;
+
+			if(TIFFGetField(tif, fieldInfo->field_tag, &tag_count, &data)) {
+				// create a tag
+				FITAG *tag = FreeImage_CreateTag();
+				if(!tag) {
+					return FALSE;
+				}
+
+				WORD tag_id = (WORD)fieldInfo->field_tag;
+				FREE_IMAGE_MDTYPE tag_type = (FREE_IMAGE_MDTYPE)fieldInfo->field_type;
+
+				FreeImage_SetTagType(tag, tag_type);
+				FreeImage_SetTagID(tag, tag_id);
+				FreeImage_SetTagKey(tag, tag_lib.getTagFieldName(TagLib::GEOTIFF, tag_id, defaultKey));
+				FreeImage_SetTagDescription(tag, tag_lib.getTagDescription(TagLib::GEOTIFF, tag_id));
+				FreeImage_SetTagLength(tag, FreeImage_TagDataWidth(tag_type) * tag_count);
+				FreeImage_SetTagCount(tag, tag_count);
+				FreeImage_SetTagValue(tag, data);
+				FreeImage_SetMetadata(FIMD_GEOTIFF, dib, FreeImage_GetTagKey(tag), tag);
+
+				// delete the tag
+				FreeImage_DeleteTag(tag);
+			}
+		}
+	} // for(tag_size)
+
+	return TRUE;
+}
+
+BOOL
+tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) {
+	char defaultKey[16];
+
+	if(FreeImage_GetMetadataCount(FIMD_GEOTIFF, dib) == 0) {
+		// no GeoTIFF tag here
+		return TRUE;
+	}
+
+	const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]);
+
+	TagLib& tag_lib = TagLib::instance();
+
+	for(size_t i = 0; i < tag_size; i++) {
+		const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i];
+
+		FITAG *tag = NULL;
+		const char *key = tag_lib.getTagFieldName(TagLib::GEOTIFF, (WORD)fieldInfo->field_tag, defaultKey);
+
+		if(FreeImage_GetMetadata(FIMD_GEOTIFF, dib, key, &tag)) {
+			if(FreeImage_GetTagType(tag) == FIDT_ASCII) {
+				TIFFSetField(tif, fieldInfo->field_tag, FreeImage_GetTagValue(tag));
+			} else {
+				TIFFSetField(tif, fieldInfo->field_tag, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag));
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+//   TIFF EXIF tag reading & writing
+// ----------------------------------------------------------
+
+/**
+Read a single Exif tag
+
+@param tif TIFF handle
+@param tag_id TIFF Tag ID
+@param dib Image being read
+@param md_model Metadata model where to store the tag
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+tiff_read_exif_tag(TIFF *tif, uint32 tag_id, FIBITMAP *dib, TagLib::MDMODEL md_model) {
+	uint32 value_count = 0;
+	int mem_alloc = 0;
+	void *raw_data = NULL;
+
+	if(tag_id == TIFFTAG_EXIFIFD) {
+		// Exif IFD offset - skip this tag
+		// md_model should be EXIF_MAIN, the Exif IFD is processed later using the EXIF_EXIF metadata model
+		return TRUE;
+	}
+	if((tag_id == TIFFTAG_GPSIFD) && (md_model == TagLib::EXIF_MAIN)) {
+		// Exif GPS IFD offset - skip this tag
+		// should be processed in another way ...
+		return TRUE;
+	}
+	
+	TagLib& tagLib = TagLib::instance();
+
+	// get the tag key - use NULL to avoid reading GeoTIFF tags
+	const char *key = tagLib.getTagFieldName(md_model, (WORD)tag_id, NULL);
+	if(key == NULL) {
+		return TRUE;
+	}
+
+	const TIFFField *fip = TIFFFieldWithTag(tif, tag_id);
+	if(fip == NULL) {
+		return TRUE;
+	}
+
+	if(TIFFFieldPassCount(fip)) { 
+		// a count value is required for 'TIFFGetField'
+
+		if (TIFFFieldReadCount(fip) != TIFF_VARIABLE2) {
+			// a count is required, it will be of type uint16
+			uint16 value_count16 = 0;
+			if(TIFFGetField(tif, tag_id, &value_count16, &raw_data) != 1) {
+				// stop, ignore error
+				return TRUE;
+			}
+			value_count = value_count16;
+		} else {
+			// a count is required, it will be of type uint32
+			uint32 value_count32 = 0;
+			if(TIFFGetField(tif, tag_id, &value_count32, &raw_data) != 1) {
+				// stop, ignore error
+				return TRUE;
+			}
+			value_count = value_count32;
+		}
+
+	} else {
+		// determine count
+
+		if (TIFFFieldReadCount(fip) == TIFF_VARIABLE || TIFFFieldReadCount(fip) == TIFF_VARIABLE2) {
+			value_count = 1;
+		} else if (TIFFFieldReadCount(fip) == TIFF_SPP) {
+			uint16 spp;
+			TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
+			value_count = spp;
+		} else {
+			value_count = TIFFFieldReadCount(fip);
+		}
+
+		// access fields as pointers to data
+		// (### determining this is NOT robust... and hardly can be. It is implemented looking the _TIFFVGetField code)
+
+		if(TIFFFieldTag(fip) == TIFFTAG_TRANSFERFUNCTION) {
+			// reading this tag cause a bug probably located somewhere inside libtiff
+			return TRUE;
+		}
+
+		if ((TIFFFieldDataType(fip) == TIFF_ASCII
+		     || TIFFFieldReadCount(fip) == TIFF_VARIABLE
+		     || TIFFFieldReadCount(fip) == TIFF_VARIABLE2
+		     || TIFFFieldReadCount(fip) == TIFF_SPP
+			 || value_count > 1)
+			 
+			 && TIFFFieldTag(fip) != TIFFTAG_PAGENUMBER
+			 && TIFFFieldTag(fip) != TIFFTAG_HALFTONEHINTS
+			 && TIFFFieldTag(fip) != TIFFTAG_YCBCRSUBSAMPLING
+			 && TIFFFieldTag(fip) != TIFFTAG_DOTRANGE
+
+			 && TIFFFieldTag(fip) != TIFFTAG_BITSPERSAMPLE	//<- these two are tricky - 
+			 && TIFFFieldTag(fip) != TIFFTAG_COMPRESSION	//<- they are defined as TIFF_VARIABLE but in reality return a single value
+			 ) {
+				 if(TIFFGetField(tif, tag_id, &raw_data) != 1) {
+					 // stop, ignore error
+					 return TRUE;
+				 }
+		} else {
+			int value_size = 0;
+
+			// access fields as values
+
+			// Note: 
+			// For TIFF_RATIONAL values, TIFFDataWidth() returns 8, but LibTIFF use internaly 4-byte float to represent rationals.
+			{
+				TIFFDataType tag_type = TIFFFieldDataType(fip);
+				switch(tag_type) {
+					case TIFF_RATIONAL:
+					case TIFF_SRATIONAL:
+						value_size = 4;
+						break;
+					default:
+						value_size = TIFFDataWidth(tag_type);
+						break;
+				}
+			}
+
+			raw_data = _TIFFmalloc(value_size * value_count);
+			mem_alloc = 1;
+			int ok = FALSE;
+			
+			// ### if value_count > 1, tag is PAGENUMBER or HALFTONEHINTS or YCBCRSUBSAMPLING or DOTRANGE, 
+			// all off which are value_count == 2 (see tif_dirinfo.c)
+			switch(value_count)
+			{
+				case 1:
+					ok = TIFFGetField(tif, tag_id, raw_data);
+					break;
+				case 2:
+					ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1);
+					break;
+/* # we might need more in the future:
+				case 3:
+					ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1, (BYTE*)(raw_data) + value_size*2);
+					break;
+*/
+				default:
+					FreeImage_OutputMessageProc(FIF_TIFF, "Unimplemented variable number of parameters for Tiff Tag %s", TIFFFieldName(fip));
+					break;
+			}
+			if(ok != 1) {
+				_TIFFfree(raw_data);
+				return TRUE;
+			}
+		}
+	}
+
+	// build FreeImage tag from Tiff Tag data we collected
+
+	FITAG *fitag = FreeImage_CreateTag();
+	if(!fitag) {
+		if(mem_alloc) {
+			_TIFFfree(raw_data);
+		}
+		return FALSE;
+	}
+
+	FreeImage_SetTagID(fitag, (WORD)tag_id);
+	FreeImage_SetTagKey(fitag, key);
+
+	switch(TIFFFieldDataType(fip)) {
+		case TIFF_BYTE:
+			FreeImage_SetTagType(fitag, FIDT_BYTE);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_UNDEFINED:
+			FreeImage_SetTagType(fitag, FIDT_UNDEFINED);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_SBYTE:
+			FreeImage_SetTagType(fitag, FIDT_SBYTE);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_SHORT:
+			FreeImage_SetTagType(fitag, FIDT_SHORT);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_SSHORT:
+			FreeImage_SetTagType(fitag, FIDT_SSHORT);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_LONG:
+			FreeImage_SetTagType(fitag, FIDT_LONG);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_IFD:
+			FreeImage_SetTagType(fitag, FIDT_IFD);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_SLONG:
+			FreeImage_SetTagType(fitag, FIDT_SLONG);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_RATIONAL: {
+			// LibTIFF converts rational to floats : reconvert floats to rationals
+			DWORD *rvalue = (DWORD*)malloc(2 * value_count * sizeof(DWORD));
+			for(uint32 i = 0; i < value_count; i++) {
+				float *fv = (float*)raw_data;
+				FIRational rational(fv[i]);
+				rvalue[2*i] = rational.getNumerator();
+				rvalue[2*i+1] = rational.getDenominator();
+			}
+			FreeImage_SetTagType(fitag, FIDT_RATIONAL);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, rvalue);
+			free(rvalue);
+		}
+		break;
+
+		case TIFF_SRATIONAL: {
+			// LibTIFF converts rational to floats : reconvert floats to rationals
+			LONG *rvalue = (LONG*)malloc(2 * value_count * sizeof(LONG));
+			for(uint32 i = 0; i < value_count; i++) {
+				float *fv = (float*)raw_data;
+				FIRational rational(fv[i]);
+				rvalue[2*i] = rational.getNumerator();
+				rvalue[2*i+1] = rational.getDenominator();
+			}
+			FreeImage_SetTagType(fitag, FIDT_RATIONAL);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, rvalue);
+			free(rvalue);
+		}
+		break;
+
+		case TIFF_FLOAT:
+			FreeImage_SetTagType(fitag, FIDT_FLOAT);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_DOUBLE:
+			FreeImage_SetTagType(fitag, FIDT_DOUBLE);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_LONG8:	// BigTIFF 64-bit unsigned integer 
+			FreeImage_SetTagType(fitag, FIDT_LONG8);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_IFD8:		// BigTIFF 64-bit unsigned integer (offset) 
+			FreeImage_SetTagType(fitag, FIDT_IFD8);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_SLONG8:		// BigTIFF 64-bit signed integer 
+			FreeImage_SetTagType(fitag, FIDT_SLONG8);
+			FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count);
+			FreeImage_SetTagCount(fitag, value_count);
+			FreeImage_SetTagValue(fitag, raw_data);
+			break;
+
+		case TIFF_ASCII:
+		default: {
+			size_t length = 0;
+			if(!mem_alloc && (TIFFFieldDataType(fip) == TIFF_ASCII) && (TIFFFieldReadCount(fip) == TIFF_VARIABLE)) {
+				// when metadata tag is of type ASCII and it's value is of variable size (TIFF_VARIABLE),
+				// tiff_read_exif_tag function gives length of 1 so all strings are truncated ...
+				// ... try to avoid this by using an explicit calculation for 'length'
+				length = strlen((char*)raw_data) + 1;
+			}
+			else {
+				// remember that raw_data = _TIFFmalloc(value_size * value_count);
+				const int value_size = TIFFDataWidth( TIFFFieldDataType(fip) );
+				length = value_size * value_count;
+			}
+			FreeImage_SetTagType(fitag, FIDT_ASCII);
+			FreeImage_SetTagLength(fitag, (DWORD)length);
+			FreeImage_SetTagCount(fitag, (DWORD)length);
+			FreeImage_SetTagValue(fitag, raw_data);
+		}
+		break;
+	}
+
+	const char *description = tagLib.getTagDescription(md_model, (WORD)tag_id);
+	if(description) {
+		FreeImage_SetTagDescription(fitag, description);
+	}
+	// store the tag
+	FreeImage_SetMetadata(tagLib.getFreeImageModel(md_model), dib, FreeImage_GetTagKey(fitag), fitag);
+
+	// destroy the tag
+	FreeImage_DeleteTag(fitag);
+
+	if(mem_alloc) {
+		_TIFFfree(raw_data);
+	}
+	return TRUE;
+}
+
+/**
+Read all known exif tags
+
+@param tif TIFF handle
+@param md_model Metadata model where to store the tags
+@param dib Image being read
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL 
+tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
+
+	TagLib& tagLib = TagLib::instance();
+
+	const int count = TIFFGetTagListCount(tif);
+	for(int i = 0; i < count; i++) {
+		uint32 tag_id = TIFFGetTagListEntry(tif, i);
+		// read the tag
+		if (!tiff_read_exif_tag(tif, tag_id, dib, md_model))
+			return FALSE;
+	}
+
+	// we want to know values of standard tags too!!
+
+	// loop over all Core Directory Tags
+	// ### uses private data, but there is no other way
+	if(md_model == TagLib::EXIF_MAIN) {
+		const TIFFDirectory *td = &tif->tif_dir;
+
+		uint32 lastTag = 0;	//<- used to prevent reading some tags twice (as stored in tif_fieldinfo)
+
+		for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) {
+			const TIFFField *fld = tif->tif_fields[fi];
+
+			const uint32 tag_id = TIFFFieldTag(fld);
+
+			if(tag_id == lastTag) {
+				continue;
+			}
+
+			// test if tag value is set
+			// (lifted directly from LibTiff _TIFFWriteDirectory)
+
+			if( fld->field_bit == FIELD_CUSTOM ) {
+				int is_set = FALSE;
+
+				for(int ci = 0; ci < td->td_customValueCount; ci++ ) {
+					is_set |= (td->td_customValues[ci].info == fld);
+				}
+
+				if( !is_set ) {
+					continue;
+				}
+
+			} else if(!TIFFFieldSet(tif, fld->field_bit)) {
+				continue;
+			}
+
+			// process *all* other tags (some will be ignored)
+
+			tiff_read_exif_tag(tif, tag_id, dib, md_model);
+
+			lastTag = tag_id;
+		}
+
+	}
+
+	return TRUE;
+}
+
+
+/**
+Skip tags that are already handled by the LibTIFF writing process
+*/
+static BOOL 
+skip_write_field(TIFF* tif, uint32 tag) {
+	switch (tag) {
+		case TIFFTAG_SUBFILETYPE:
+		case TIFFTAG_OSUBFILETYPE:
+		case TIFFTAG_IMAGEWIDTH:
+		case TIFFTAG_IMAGELENGTH:
+		case TIFFTAG_BITSPERSAMPLE:
+		case TIFFTAG_COMPRESSION:
+		case TIFFTAG_PHOTOMETRIC:
+		case TIFFTAG_THRESHHOLDING:
+		case TIFFTAG_CELLWIDTH:
+		case TIFFTAG_CELLLENGTH:
+		case TIFFTAG_FILLORDER:
+		case TIFFTAG_STRIPOFFSETS:
+		case TIFFTAG_ORIENTATION:
+		case TIFFTAG_SAMPLESPERPIXEL:
+		case TIFFTAG_ROWSPERSTRIP:
+		case TIFFTAG_STRIPBYTECOUNTS:
+		case TIFFTAG_MINSAMPLEVALUE:
+		case TIFFTAG_MAXSAMPLEVALUE:
+		case TIFFTAG_XRESOLUTION:
+		case TIFFTAG_YRESOLUTION:
+		case TIFFTAG_PLANARCONFIG:
+		case TIFFTAG_FREEOFFSETS:
+		case TIFFTAG_FREEBYTECOUNTS:
+		case TIFFTAG_GRAYRESPONSEUNIT:
+		case TIFFTAG_GRAYRESPONSECURVE:
+		case TIFFTAG_GROUP3OPTIONS:
+		case TIFFTAG_GROUP4OPTIONS:
+		case TIFFTAG_RESOLUTIONUNIT:
+		case TIFFTAG_PAGENUMBER:
+		case TIFFTAG_COLORRESPONSEUNIT:
+		case TIFFTAG_PREDICTOR:
+		case TIFFTAG_COLORMAP:
+		case TIFFTAG_HALFTONEHINTS:
+		case TIFFTAG_TILEWIDTH:
+		case TIFFTAG_TILELENGTH:
+		case TIFFTAG_TILEOFFSETS:
+		case TIFFTAG_TILEBYTECOUNTS:
+		case TIFFTAG_EXTRASAMPLES:
+		case TIFFTAG_SAMPLEFORMAT:
+		case TIFFTAG_SMINSAMPLEVALUE:
+		case TIFFTAG_SMAXSAMPLEVALUE:
+			// skip always, values have been set in SaveOneTIFF()
+			return TRUE;
+			break;
+		
+		case TIFFTAG_RICHTIFFIPTC:
+			// skip always, IPTC metadata model is set in tiff_write_iptc_profile()
+			return TRUE;
+			break;
+
+		case TIFFTAG_YCBCRCOEFFICIENTS:
+		case TIFFTAG_REFERENCEBLACKWHITE:
+		case TIFFTAG_YCBCRSUBSAMPLING:
+			// skip as they cannot be filled yet
+			return TRUE;
+			break;
+			
+		case TIFFTAG_PAGENAME:
+		{
+			char *value = NULL;
+			TIFFGetField(tif, TIFFTAG_PAGENAME, &value);
+			// only skip if no value has been set
+			if(value == NULL) {
+				return FALSE;
+			} else {
+				return TRUE;
+			}
+		}
+		default:
+			return FALSE;
+			break;
+	}
+}
+
+/**
+Write all known exif tags
+
+@param tif TIFF handle
+@param md_model Metadata model from where to load the tags
+@param dib Image being written
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL 
+tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) {
+	char defaultKey[16];
+	
+	// only EXIF_MAIN so far
+	if(md_model != TagLib::EXIF_MAIN) {
+		return FALSE;
+	}
+	
+	if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, dib) == 0) {
+		return FALSE;
+	}
+	
+	TagLib& tag_lib = TagLib::instance();
+	
+	for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) {
+		const TIFFField *fld = tif->tif_fields[fi];
+		
+		const uint32 tag_id = TIFFFieldTag(fld);
+
+		if(skip_write_field(tif, tag_id)) {
+			// skip tags that are already handled by the LibTIFF writing process
+			continue;
+		}
+
+		FITAG *tag = NULL;
+		// get the tag key
+		const char *key = tag_lib.getTagFieldName(TagLib::EXIF_MAIN, (WORD)tag_id, defaultKey);
+
+		if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) {
+			FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag);
+			TIFFDataType tif_tag_type = TIFFFieldDataType(fld);
+			
+			// check for identical formats
+
+			// (enum value are the sames between FREE_IMAGE_MDTYPE and TIFFDataType types)
+			if((int)tif_tag_type != (int)tag_type) {
+				// skip tag or _TIFFmemcpy will fail
+				continue;
+			}
+			// type of storage may differ (e.g. rationnal array vs float array type)
+			if((unsigned)_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) {
+				// skip tag or _TIFFmemcpy will fail
+				continue;
+			}
+
+			if(tag_type == FIDT_ASCII) {
+				TIFFSetField(tif, tag_id, FreeImage_GetTagValue(tag));
+			} else {
+				TIFFSetField(tif, tag_id, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag));
+			}
+		}
+	}
+
+	return TRUE;
+}
diff --git a/files/Source/Plugin.h b/files/Source/Plugin.h
new file mode 100644
index 0000000..a668bec
--- /dev/null
+++ b/files/Source/Plugin.h
@@ -0,0 +1,143 @@
+// ==========================================================
+// FreeImage Plugin Interface
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Rui Lopes (ruiglopes@yahoo.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 
+
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+
+struct Plugin;
+
+// =====================================================================
+//  Plugin Node
+// =====================================================================
+
+FI_STRUCT (PluginNode) {
+	/** FREE_IMAGE_FORMAT attached to this plugin */
+	int m_id;
+	/** Handle to a user plugin DLL (NULL for standard plugins) */
+	void *m_instance;
+	/** The actual plugin, holding the function pointers */
+	Plugin *m_plugin;
+	/** Enable/Disable switch */
+	BOOL m_enabled;
+
+	/** Unique format string for the plugin */
+	const char *m_format;
+	/** Description string for the plugin */
+	const char *m_description;
+	/** Comma separated list of file extensions indicating what files this plugin can open */
+	const char *m_extension;
+	/** optional regular expression to help	software identifying a bitmap type */
+	const char *m_regexpr;
+};
+
+// =====================================================================
+//  Internal Plugin List
+// =====================================================================
+
+class PluginList {
+public :
+	PluginList();
+	~PluginList();
+
+	FREE_IMAGE_FORMAT AddNode(FI_InitProc proc, void *instance = NULL, const char *format = 0, const char *description = 0, const char *extension = 0, const char *regexpr = 0);
+	PluginNode *FindNodeFromFormat(const char *format);
+	PluginNode *FindNodeFromMime(const char *mime);
+	PluginNode *FindNodeFromFIF(int node_id);
+
+	int Size() const;
+	BOOL IsEmpty() const;
+
+private :
+	std::map<int, PluginNode *> m_plugin_map;
+	int m_node_count;
+};
+
+// ==========================================================
+//   Plugin Initialisation Callback
+// ==========================================================
+
+void DLL_CALLCONV FreeImage_OutputMessage(int fif, const char *message, ...);
+
+// =====================================================================
+// Reimplementation of stricmp (it is not supported on some systems)
+// =====================================================================
+
+int FreeImage_stricmp(const char *s1, const char *s2);
+
+// ==========================================================
+//   Internal functions
+// ==========================================================
+
+extern "C" {
+	BOOL DLL_CALLCONV FreeImage_Validate(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle);
+    void * DLL_CALLCONV FreeImage_Open(PluginNode *node, FreeImageIO *io, fi_handle handle, BOOL open_for_reading);
+    void DLL_CALLCONV FreeImage_Close(PluginNode *node, FreeImageIO *io, fi_handle handle, void *data); // plugin.cpp
+    PluginList * DLL_CALLCONV FreeImage_GetPluginList(); // plugin.cpp
+}
+
+// ==========================================================
+//   Internal plugins
+// ==========================================================
+
+void DLL_CALLCONV InitBMP(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitCUT(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitICO(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitIFF(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitJPEG(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitKOALA(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitLBM(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitMNG(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitPCD(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitPCX(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitPNG(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitPNM(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitPSD(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitRAS(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitTARGA(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitTIFF(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitWBMP(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitXBM(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitXPM(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitDDS(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitGIF(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitHDR(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitG3(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitSGI(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitEXR(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitJ2K(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitJP2(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitPFM(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitPICT(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitRAW(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitJNG(Plugin *plugin, int format_id);
+void DLL_CALLCONV InitWEBP(Plugin *plugin, int format_id);
+
+#endif //!PLUGIN_H
diff --git a/files/Source/Quantizers.h b/files/Source/Quantizers.h
new file mode 100644
index 0000000..3db12d3
--- /dev/null
+++ b/files/Source/Quantizers.h
@@ -0,0 +1,354 @@
+// =============================================================
+// Quantizer objects and functions
+//
+// Design and implementation by:
+// - Hervé Drolon <drolon@infonie.fr>
+// - 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"
+
+////////////////////////////////////////////////////////////////
+
+/**
+  Xiaolin Wu color quantization algorithm
+*/
+class WuQuantizer
+{
+public:
+
+typedef struct tagBox {
+    int r0;			 // min value, exclusive
+    int r1;			 // max value, inclusive
+    int g0;  
+    int g1;  
+    int b0;  
+    int b1;
+    int vol;
+} Box;
+
+protected:
+    float *gm2;
+	LONG *wt, *mr, *mg, *mb;
+	WORD *Qadd;
+
+	// DIB data
+	unsigned width, height;
+	unsigned pitch;
+	FIBITMAP *m_dib;
+
+protected:
+    void Hist3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2, int ReserveSize, RGBQUAD *ReservePalette);
+	void M3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2);
+	LONG Vol(Box *cube, LONG *mmt);
+	LONG Bottom(Box *cube, BYTE dir, LONG *mmt);
+	LONG Top(Box *cube, BYTE dir, int pos, LONG *mmt);
+	float Var(Box *cube);
+	float Maximize(Box *cube, BYTE dir, int first, int last , int *cut,
+				   LONG whole_r, LONG whole_g, LONG whole_b, LONG whole_w);
+	bool Cut(Box *set1, Box *set2);
+	void Mark(Box *cube, int label, BYTE *tag);
+
+public:
+	// Constructor - Input parameter: DIB 24-bit to be quantized
+    WuQuantizer(FIBITMAP *dib);
+	// Destructor
+	~WuQuantizer();
+	// Quantizer - Return value: quantized 8-bit (color palette) DIB
+	FIBITMAP* Quantize(int PaletteSize, int ReserveSize, RGBQUAD *ReservePalette);
+};
+
+
+/**
+  NEUQUANT Neural-Net quantization algorithm by Anthony Dekker
+*/
+
+// ----------------------------------------------------------------
+// Constant definitions
+// ----------------------------------------------------------------
+
+/** number of colours used: 
+	for 256 colours, fixed arrays need 8kb, plus space for the image
+*/
+//static const int netsize = 256;
+
+/**@name network definitions */
+//@{
+//static const int maxnetpos = (netsize - 1);
+/// bias for colour values
+static const int netbiasshift = 4;
+/// no. of learning cycles
+static const int ncycles = 100;
+//@}
+
+/**@name defs for freq and bias */
+//@{
+/// bias for fractions
+static const int intbiasshift = 16;
+static const int intbias = (((int)1) << intbiasshift);
+/// gamma = 1024
+static const int gammashift = 10;
+// static const int gamma = (((int)1) << gammashift);
+/// beta = 1 / 1024
+static const int betashift = 10;
+static const int beta = (intbias >> betashift);
+static const int betagamma = (intbias << (gammashift-betashift));
+//@}
+
+/**@name defs for decreasing radius factor */
+//@{
+/// for 256 cols, radius starts
+//static const int initrad = (netsize >> 3);
+/// at 32.0 biased by 6 bits
+static const int radiusbiasshift = 6;
+static const int radiusbias = (((int)1) << radiusbiasshift);
+/// and decreases by a 
+//static const int initradius	= (initrad * radiusbias);
+// factor of 1/30 each cycle
+static const int radiusdec = 30;
+//@}
+
+/**@name defs for decreasing alpha factor */
+//@{
+/// alpha starts at 1.0
+static const int alphabiasshift = 10;
+static const int initalpha = (((int)1) << alphabiasshift);
+//@}
+
+/**@name radbias and alpharadbias used for radpower calculation */
+//@{
+static const int radbiasshift = 8;
+static const int radbias = (((int)1) << radbiasshift);
+static const int alpharadbshift = (alphabiasshift+radbiasshift);
+static const int alpharadbias = (((int)1) << alpharadbshift);	
+//@}
+
+class NNQuantizer
+{
+protected:
+	/**@name image parameters */
+	//@{
+	/// pointer to input dib
+	FIBITMAP *dib_ptr;
+	/// image width
+	int img_width;
+	/// image height
+	int img_height;
+	/// image line length
+	int img_line;
+	//@}
+
+	/**@name network parameters */
+	//@{
+
+	int netsize, maxnetpos, initrad, initradius;
+
+	/// BGRc
+	typedef int pixel[4];
+	/// the network itself
+	pixel *network;
+
+	/// for network lookup - really 256
+	int netindex[256];
+
+	/// bias array for learning
+	int *bias;
+	/// freq array for learning
+	int *freq;
+	/// radpower for precomputation
+	int *radpower;
+	//@}
+
+protected:
+	/// Initialise network in range (0,0,0) to (255,255,255) and set parameters
+	void initnet();	
+
+	/// Unbias network to give byte values 0..255 and record position i to prepare for sort
+	void unbiasnet();
+
+	/// Insertion sort of network and building of netindex[0..255] (to do after unbias)
+	void inxbuild();
+
+	/// Search for BGR values 0..255 (after net is unbiased) and return colour index
+	int inxsearch(int b, int g, int r);
+
+	/// Search for biased BGR values
+	int contest(int b, int g, int r);
+	
+	/// Move neuron i towards biased (b,g,r) by factor alpha
+	void altersingle(int alpha, int i, int b, int g, int r);
+
+	/// Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|]
+	void alterneigh(int rad, int i, int b, int g, int r);
+
+	/** Main Learning Loop
+	@param sampling_factor sampling factor in [1..30]
+	*/
+	void learn(int sampling_factor);
+
+	/// Get a pixel sample at position pos. Handle 4-byte boundary alignment.
+	void getSample(long pos, int *b, int *g, int *r);
+
+
+public:
+	/// Constructor
+	NNQuantizer(int PaletteSize);
+
+	/// Destructor
+	~NNQuantizer();
+
+	/** Quantizer
+	@param dib input 24-bit dib to be quantized
+	@param sampling a sampling factor in range 1..30. 
+	1 => slower (but better), 30 => faster. Default value is 1
+	@return returns the quantized 8-bit (color palette) DIB
+	*/
+	FIBITMAP* Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette, int sampling = 1);
+
+};
+
+/**
+ * LFPQUANT - Lossless Fast Pseudo-Quantization Algorithm
+ *
+ * The Lossless Fast Pseudo-Quantization algorithm is no real quantization
+ * algorithm, since it makes no attempt to create a palette, that is suitable
+ * for all colors of the 24-bit source image. However, it provides very fast
+ * conversions from 24-bit to 8-bit images, if the number of distinct colors
+ * in the source image is not greater than the desired palette size. If the
+ * number of colors in the source image is exceeded, the Quantize method of
+ * this implementation stops the process and returns NULL.
+ *
+ * This implementation uses a very fast hash map implementation to collect
+ * the source image's colors. It turned out that a customized implementation
+ * of a hash table with open addressing (using linear probing) provides the
+ * best performance. The hash table has 512 entries, which prevents the load
+ * factor to exceed 0.5 as we have 256 entries at most. Each entry consumes
+ * 64 bits, so the whole hash table takes 4KB of memory.
+ *
+ * For large images, the LFPQuantizer is typically up to three times faster
+ * than the WuQuantizer.
+ */
+class LFPQuantizer {
+public:
+	/** Constructor */
+	LFPQuantizer(unsigned PaletteSize);
+
+	/** Destructor */
+	~LFPQuantizer();
+
+	/**
+	 * Quantizer
+	 * @param dib input 24-bit or 32-bit bitmap to be quantized
+	 * @return returns the pseudo-quantized 8-bit bitmap
+	 */
+	FIBITMAP* Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette);
+
+protected:
+	/** The maximum size of a palette. */
+	static const unsigned MAX_SIZE = 256;
+
+	/**
+	 * The size of the hash table. Must be a power of 2. By sizing it
+	 * MAX_SIZE * 2, we ensure the load factor not to exceed 0.5 at any
+	 * time, since we will have MAX_SIZE entries at most.
+	 */
+	static const unsigned MAP_SIZE = MAX_SIZE * 2;
+
+	/**
+	 * With open addressing we need a special value for empty buckets.
+	 * Both entry.color and entry.index are 0xFFFFFFFF for an empty
+	 * entry.
+	 */
+	static const unsigned EMPTY_BUCKET = 0xFFFFFFFF;
+
+	/**
+	 * This structure defines a single entry in the hash table. We use
+	 * color as the entry's key.
+	 */
+	typedef struct MapEntry {
+		unsigned color;
+		unsigned index;
+	} MapEntry;
+
+	/** The hash table. */
+	MapEntry *m_map;
+
+	/**
+	 * The current size of the newly created palette. Since the provided
+	 * reserve palette could contain duplicates, this is not necessarily
+	 * the number of entries in the hash table. Initialized to zero.
+	 */
+	unsigned m_size;
+
+	/**
+	 * The desired maximum number of entries in the newly created palette.
+	 * If m_size exceeds this value, the palette is full and the
+	 * quantization process is stopped. Initialized to the desired
+	 * palette size.
+	 */
+	unsigned m_limit;
+
+	/**
+	 *  The palette index used for the next color added. Initialized to
+	 *  zero (the reserve palette is put to the end of the palette).
+	 */
+	unsigned m_index;
+
+	/**
+	 * Ensures that hash codes that differ only by constant multiples
+	 * at each bit position have a bounded number of collisions.
+	 * @param h the initial (aka raw) hash code
+	 * @return the modified hash code
+	 */
+	static inline unsigned hash(unsigned h) {
+		h ^= (h >> 20) ^ (h >> 12);
+		return h ^ (h >> 7) ^ (h >> 4);
+	}
+
+	/**
+	 * 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
+	 */
+	int GetIndexForColor(unsigned color);
+
+	/**
+	 * 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 AddReservePalette(const void *palette, unsigned size);
+
+	/**
+	 * Copies the newly created palette into the specified destination
+	 * palettte. 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 WritePalette(void *palette);
+
+};
diff --git a/files/Source/ToneMapping.h b/files/Source/ToneMapping.h
new file mode 100644
index 0000000..0519933
--- /dev/null
+++ b/files/Source/ToneMapping.h
@@ -0,0 +1,44 @@
+// ==========================================================
+// High Dynamic Range 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!
+// ==========================================================
+
+#ifndef TONE_MAPPING_H
+#define TONE_MAPPING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+BOOL ConvertInPlaceRGBFToYxy(FIBITMAP *dib);
+BOOL ConvertInPlaceYxyToRGBF(FIBITMAP *dib);
+FIBITMAP* ConvertRGBFToY(FIBITMAP *src);
+
+BOOL LuminanceFromYxy(FIBITMAP *dib, float *maxLum, float *minLum, float *worldLum);
+BOOL LuminanceFromY(FIBITMAP *dib, float *maxLum, float *minLum, float *Lav, float *Llav);
+
+void NormalizeY(FIBITMAP *Y, float minPrct, float maxPrct);
+
+FIBITMAP* ClampConvertRGBFTo24(FIBITMAP *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TONE_MAPPING_H
diff --git a/files/Source/Utilities.h b/files/Source/Utilities.h
new file mode 100644
index 0000000..7c972b5
--- /dev/null
+++ b/files/Source/Utilities.h
@@ -0,0 +1,518 @@
+// ==========================================================
+// Utility functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon <drolon@infonie.fr>
+// - Ryan Rubley (ryan@lostreality.org)
+// - 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!
+// ==========================================================
+
+#ifndef UTILITIES_H
+#define UTILITIES_H
+
+// ==========================================================
+//   Standard includes used by the library
+// ==========================================================
+
+#include <math.h>
+#include <stdlib.h>
+#if !defined(__native_client__)
+#include <memory.h>
+#endif  // !defined(__native_client__)
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <assert.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+
+#include <string>
+#include <list>
+#include <map>
+#include <set>
+#include <vector>
+#include <stack>
+#include <sstream>
+#include <algorithm>
+#include <limits>
+#include <memory>
+
+// ==========================================================
+//   Bitmap palette and pixels alignment
+// ==========================================================
+
+#define FIBITMAP_ALIGNMENT	16	// We will use a 16 bytes alignment boundary
+
+// Memory allocation on a specified alignment boundary
+// defined in BitmapAccess.cpp
+
+void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment);
+void FreeImage_Aligned_Free(void* mem);
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**
+Allocate a FIBITMAP with possibly no pixel data 
+(i.e. only header data and some or all metadata)
+@param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP
+@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
+@see FreeImage_AllocateT
+*/
+DLL_API FIBITMAP * DLL_CALLCONV FreeImage_AllocateHeaderT(BOOL header_only, 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));
+
+/**
+Allocate a FIBITMAP of type FIT_BITMAP, with possibly no pixel data 
+(i.e. only header data and some or all metadata)
+@param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP
+@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
+@see FreeImage_Allocate
+*/
+DLL_API FIBITMAP * DLL_CALLCONV FreeImage_AllocateHeader(BOOL header_only, int width, int height, int bpp, unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+
+/**
+Allocate a FIBITMAP with no pixel data and wrap a user provided pixel buffer
+@param ext_bits Pointer to external user's pixel buffer
+@param ext_pitch Pointer to external user's pixel buffer pitch
+@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
+@see FreeImage_ConvertFromRawBitsEx
+*/
+DLL_API 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);
+
+/**
+Helper for 16-bit FIT_BITMAP
+@see FreeImage_GetRGBMasks
+*/
+DLL_API BOOL DLL_CALLCONV FreeImage_HasRGBMasks(FIBITMAP *dib);
+
+#if defined(__cplusplus)
+}
+#endif
+
+
+// ==========================================================
+//   File I/O structs
+// ==========================================================
+
+// these structs are for file I/O and should not be confused with similar
+// structs in FreeImage.h which are for in-memory bitmap handling
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // _WIN32
+
+typedef struct tagFILE_RGBA {
+  unsigned char r,g,b,a;
+} FILE_RGBA;
+
+typedef struct tagFILE_BGRA {
+  unsigned char b,g,r,a;
+} FILE_BGRA;
+
+typedef struct tagFILE_RGB {
+  unsigned char r,g,b;
+} FILE_RGB;
+
+typedef struct tagFILE_BGR {
+  unsigned char b,g,r;
+} FILE_BGR;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // _WIN32
+
+// ==========================================================
+//   Template utility functions
+// ==========================================================
+
+/// Max function
+template <class T> T MAX(const T &a, const T &b) {
+	return (a > b) ? a: b;
+}
+
+/// Min function
+template <class T> T MIN(const T &a, const T &b) {
+	return (a < b) ? a: b;
+}
+
+/// INPLACESWAP adopted from codeguru.com 
+template <class T> void INPLACESWAP(T& a, T& b) {
+	a ^= b; b ^= a; a ^= b;
+}
+
+/// Clamp function
+template <class T> T CLAMP(const T &value, const T &min_value, const T &max_value) {
+	return ((value < min_value) ? min_value : (value > max_value) ? max_value : value);
+}
+
+/** This procedure computes minimum min and maximum max
+ of n numbers using only (3n/2) - 2 comparisons.
+ min = L[i1] and max = L[i2].
+ ref: Aho A.V., Hopcroft J.E., Ullman J.D., 
+ The design and analysis of computer algorithms, 
+ Addison-Wesley, Reading, 1974.
+*/
+template <class T> void 
+MAXMIN(const T* L, long n, T& max, T& min) {
+	long i1, i2, i, j;
+	T x1, x2;
+	long k1, k2;
+
+	i1 = 0; i2 = 0; min = L[0]; max = L[0]; j = 0;
+	if((n % 2) != 0)  j = 1;
+	for(i = j; i < n; i+= 2) {
+		k1 = i; k2 = i+1;
+		x1 = L[k1]; x2 = L[k2];
+		if(x1 > x2)	{
+			k1 = k2;  k2 = i;
+			x1 = x2;  x2 = L[k2];
+		}
+		if(x1 < min) {
+			min = x1;  i1 = k1;
+		}
+		if(x2 > max) {
+			max = x2;  i2 = k2;
+		}
+	}
+}
+
+// ==========================================================
+//   Utility functions
+// ==========================================================
+
+#ifndef _WIN32
+inline char*
+i2a(unsigned i, char *a, unsigned r) {
+	if (i/r > 0) a = i2a(i/r,a,r);
+	*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
+	return a+1;
+}
+
+/** 
+ Transforms integer i into an ascii string and stores the result in a; 
+ string is encoded in the base indicated by r.
+ @param i Number to be converted
+ @param a String result
+ @param r Base of value; must be in the range 2 - 36
+ @return Returns a
+*/
+inline char *
+_itoa(int i, char *a, int r) {
+	r = ((r < 2) || (r > 36)) ? 10 : r;
+	if(i < 0) {
+		*a = '-';
+		*i2a(-i, a+1, r) = 0;
+	}
+	else *i2a(i, a, r) = 0;
+	return a;
+}
+
+#endif // !_WIN32
+
+inline unsigned char
+HINIBBLE (unsigned char byte) {
+	return byte & 0xF0;
+}
+
+inline unsigned char
+LOWNIBBLE (unsigned char byte) {
+	return byte & 0x0F;
+}
+
+inline int
+CalculateUsedBits(int bits) {
+	int bit_count = 0;
+	unsigned bit = 1;
+
+	for (unsigned i = 0; i < 32; i++) {
+		if ((bits & bit) == bit) {
+			bit_count++;
+		}
+
+		bit <<= 1;
+	}
+
+	return bit_count;
+}
+
+inline unsigned
+CalculateLine(unsigned width, unsigned bitdepth) {
+	return (unsigned)( ((unsigned long long)width * bitdepth + 7) / 8 );
+}
+
+inline unsigned
+CalculatePitch(unsigned line) {
+	return line + 3 & ~3;
+}
+
+inline unsigned
+CalculateUsedPaletteEntries(unsigned bit_count) {
+	if ((bit_count >= 1) && (bit_count <= 8))
+		return 1 << bit_count;
+
+	return 0;
+}
+
+inline unsigned char *
+CalculateScanLine(unsigned char *bits, unsigned pitch, int scanline) {
+	return bits ? (bits + ((size_t)pitch * scanline)) : NULL;
+}
+
+// ----------------------------------------------------------
+
+/**
+Fast generic assign (faster than for loop)
+@param dst Destination pixel
+@param src Source pixel
+@param bytesperpixel # of bytes per pixel
+*/
+inline void 
+AssignPixel(BYTE* dst, const BYTE* src, unsigned bytesperpixel) {
+	switch (bytesperpixel) {
+		case 1:	// FIT_BITMAP (8-bit)
+			*dst = *src;
+			break;
+
+		case 2: // FIT_UINT16 / FIT_INT16 / 16-bit
+			*(reinterpret_cast<WORD*>(dst)) = *(reinterpret_cast<const WORD*> (src));
+			break;
+
+		case 3: // FIT_BITMAP (24-bit)
+			*(reinterpret_cast<WORD*>(dst)) = *(reinterpret_cast<const WORD*> (src));
+			dst[2] = src[2];
+			break;
+
+		case 4: // FIT_BITMAP (32-bit) / FIT_UINT32 / FIT_INT32 / FIT_FLOAT
+			*(reinterpret_cast<DWORD*>(dst)) = *(reinterpret_cast<const DWORD*> (src));
+			break;
+
+		case 6: // FIT_RGB16 (3 x 16-bit)
+			*(reinterpret_cast<DWORD*>(dst)) = *(reinterpret_cast<const DWORD*> (src));
+			*(reinterpret_cast<WORD*>(dst + 4)) = *(reinterpret_cast<const WORD*> (src + 4));	
+			break;
+
+		// the rest can be speeded up with int64
+			
+		case 8: // FIT_RGBA16 (4 x 16-bit)
+			*(reinterpret_cast<DWORD*>(dst)) = *(reinterpret_cast<const DWORD*> (src));
+			*(reinterpret_cast<DWORD*>(dst + 4)) = *(reinterpret_cast<const DWORD*> (src + 4));	
+			break;
+		
+		case 12: // FIT_RGBF (3 x 32-bit IEEE floating point)
+			*(reinterpret_cast<float*>(dst)) = *(reinterpret_cast<const float*> (src));
+			*(reinterpret_cast<float*>(dst + 4)) = *(reinterpret_cast<const float*> (src + 4));
+			*(reinterpret_cast<float*>(dst + 8)) = *(reinterpret_cast<const float*> (src + 8));
+			break;
+		
+		case 16: // FIT_RGBAF (4 x 32-bit IEEE floating point)
+			*(reinterpret_cast<float*>(dst)) = *(reinterpret_cast<const float*> (src));
+			*(reinterpret_cast<float*>(dst + 4)) = *(reinterpret_cast<const float*> (src + 4));
+			*(reinterpret_cast<float*>(dst + 8)) = *(reinterpret_cast<const float*> (src + 8));
+			*(reinterpret_cast<float*>(dst + 12)) = *(reinterpret_cast<const float*> (src + 12));
+			break;
+			
+		default:
+			assert(FALSE);
+	}
+}
+
+/**
+Swap red and blue channels in a 24- or 32-bit dib. 
+@return Returns TRUE if successful, returns FALSE otherwise
+@see See definition in Conversion.cpp
+*/
+BOOL SwapRedBlue32(FIBITMAP* dib);
+
+/**
+Inplace convert CMYK to RGBA.(8- and 16-bit). 
+Alpha is filled with the first extra channel if any or white otherwise.
+@return Returns TRUE if successful, returns FALSE otherwise
+@see See definition in Conversion.cpp
+*/
+BOOL ConvertCMYKtoRGBA(FIBITMAP* dib);
+
+/**
+Inplace convert CIELab to RGBA (8- and 16-bit).
+@return Returns TRUE if successful, returns FALSE otherwise
+@see See definition in Conversion.cpp
+*/
+BOOL ConvertLABtoRGB(FIBITMAP* dib);
+
+/**
+RGBA to RGB conversion
+@see See definition in Conversion.cpp
+*/
+FIBITMAP* RemoveAlphaChannel(FIBITMAP* dib);
+
+/**
+Rotate a dib according to Exif info
+@param dib Input / Output dib to rotate
+@see Exif.cpp, PluginJPEG.cpp
+*/
+void RotateExif(FIBITMAP **dib);
+
+
+// ==========================================================
+//   Big Endian / Little Endian utility functions
+// ==========================================================
+
+inline WORD 
+__SwapUInt16(WORD arg) { 
+#if defined(_MSC_VER) && _MSC_VER >= 1310 
+	return _byteswap_ushort(arg); 
+#elif defined(__i386__) && defined(__GNUC__) 
+	__asm__("xchgb %b0, %h0" : "+q" (arg)); 
+	return arg; 
+#elif defined(__ppc__) && defined(__GNUC__) 
+	WORD result; 
+	__asm__("lhbrx %0,0,%1" : "=r" (result) : "r" (&arg), "m" (arg)); 
+	return result; 
+#else 
+	// swap bytes 
+	WORD result;
+	result = ((arg << 8) & 0xFF00) | ((arg >> 8) & 0x00FF); 
+	return result; 
+#endif 
+} 
+ 
+inline DWORD 
+__SwapUInt32(DWORD arg) { 
+#if defined(_MSC_VER) && _MSC_VER >= 1310 
+	return _byteswap_ulong(arg); 
+#elif defined(__i386__) && defined(__GNUC__) 
+	__asm__("bswap %0" : "+r" (arg)); 
+	return arg; 
+#elif defined(__ppc__) && defined(__GNUC__) 
+	DWORD result; 
+	__asm__("lwbrx %0,0,%1" : "=r" (result) : "r" (&arg), "m" (arg)); 
+	return result; 
+#else 
+	// swap words then bytes
+	DWORD result; 
+	result = ((arg & 0x000000FF) << 24) | ((arg & 0x0000FF00) << 8) | ((arg >> 8) & 0x0000FF00) | ((arg >> 24) & 0x000000FF); 
+	return result; 
+#endif 
+} 
+ 
+/**
+for later use ...
+inline uint64_t 
+SwapInt64(uint64_t arg) { 
+#if defined(_MSC_VER) && _MSC_VER >= 1310 
+	return _byteswap_uint64(arg); 
+#else 
+	union Swap { 
+		uint64_t sv; 
+		uint32_t ul[2]; 
+	} tmp, result; 
+	tmp.sv = arg; 
+	result.ul[0] = SwapInt32(tmp.ul[1]);  
+	result.ul[1] = SwapInt32(tmp.ul[0]); 
+	return result.sv; 
+#endif 
+} 
+*/
+
+inline void
+SwapShort(WORD *sp) {
+	*sp = __SwapUInt16(*sp);
+}
+
+inline void
+SwapLong(DWORD *lp) {
+	*lp = __SwapUInt32(*lp);
+}
+
+// ==========================================================
+//   Greyscale and color conversion
+// ==========================================================
+
+/**
+Extract the luminance channel L from a RGBF image. 
+Luminance is calculated from the sRGB model using a D65 white point, using the Rec.709 formula : 
+L = ( 0.2126 * r ) + ( 0.7152 * g ) + ( 0.0722 * b )
+Reference : 
+A Standard Default Color Space for the Internet - sRGB. 
+[online] http://www.w3.org/Graphics/Color/sRGB
+*/
+#define LUMA_REC709(r, g, b)	(0.2126F * r + 0.7152F * g + 0.0722F * b)
+
+#define GREY(r, g, b) (BYTE)(LUMA_REC709(r, g, b) + 0.5F)
+/*
+#define GREY(r, g, b) (BYTE)(((WORD)r * 77 + (WORD)g * 150 + (WORD)b * 29) >> 8)	// .299R + .587G + .114B
+*/
+/*
+#define GREY(r, g, b) (BYTE)(((WORD)r * 169 + (WORD)g * 256 + (WORD)b * 87) >> 9)	// .33R + 0.5G + .17B
+*/
+
+#define RGB565(b, g, r) ((((b) >> 3) << FI16_565_BLUE_SHIFT) | (((g) >> 2) << FI16_565_GREEN_SHIFT) | (((r) >> 3) << FI16_565_RED_SHIFT))
+#define RGB555(b, g, r) ((((b) >> 3) << FI16_555_BLUE_SHIFT) | (((g) >> 3) << FI16_555_GREEN_SHIFT) | (((r) >> 3) << FI16_555_RED_SHIFT))
+
+#define IS_FORMAT_RGB565(dib) ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK))
+#define RGBQUAD_TO_WORD(dib, color) (IS_FORMAT_RGB565(dib) ? RGB565((color)->rgbBlue, (color)->rgbGreen, (color)->rgbRed) : RGB555((color)->rgbBlue, (color)->rgbGreen, (color)->rgbRed))
+
+#define CREATE_GREYSCALE_PALETTE(palette, entries) \
+	for (unsigned i = 0, v = 0; i < entries; i++, v += 0x00FFFFFF / (entries - 1)) { \
+		((unsigned *)palette)[i] = v; \
+	}
+
+#define CREATE_GREYSCALE_PALETTE_REVERSE(palette, entries) \
+	for (unsigned i = 0, v = 0x00FFFFFF; i < entries; i++, v -= (0x00FFFFFF / (entries - 1))) { \
+		((unsigned *)palette)[i] = v; \
+	}
+
+// ==========================================================
+//   Generic error messages
+// ==========================================================
+
+static const char *FI_MSG_ERROR_MEMORY = "Memory allocation failed";
+static const char *FI_MSG_ERROR_DIB_MEMORY = "DIB allocation failed, maybe caused by an invalid image size or by a lack of memory";
+static const char *FI_MSG_ERROR_PARSING = "Parsing error";
+static const char *FI_MSG_ERROR_MAGIC_NUMBER = "Invalid magic number";
+static const char *FI_MSG_ERROR_UNSUPPORTED_FORMAT = "Unsupported format";
+static const char *FI_MSG_ERROR_UNSUPPORTED_COMPRESSION = "Unsupported compression type";
+static const char *FI_MSG_WARNING_INVALID_THUMBNAIL = "Warning: attached thumbnail cannot be written to output file (invalid format) - Thumbnail saving aborted";
+
+#endif // UTILITIES_H
diff --git a/files/Wrapper/FreeImagePlus/FreeImagePlus.h b/files/Wrapper/FreeImagePlus/FreeImagePlus.h
new file mode 100644
index 0000000..571010f
--- /dev/null
+++ b/files/Wrapper/FreeImagePlus/FreeImagePlus.h
@@ -0,0 +1,1713 @@
+// ==========================================================
+// FreeImagePlus 3
+//
+// 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!
+// ==========================================================
+
+#ifndef FREEIMAGEPLUS_H
+#define FREEIMAGEPLUS_H
+
+#ifdef _WIN32
+#include <windows.h>
+#endif // _WIN32
+#include "FreeImage.h"
+
+
+// Compiler options ---------------------------------------------------------
+
+#if defined(FREEIMAGE_LIB)
+	#define FIP_API
+	#define FIP_CALLCONV
+#else
+	#if defined(_WIN32) || defined(__WIN32__)
+		#define WIN32_LEAN_AND_MEAN
+		#define FIP_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 FIP_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 
+		// FIP_API functions as being imported from a DLL, wheras this DLL sees symbols
+		// defined with this macro as being exported.
+		#ifdef FIP_EXPORTS
+			#define FIP_API __declspec(dllexport)
+		#else
+			#define FIP_API __declspec(dllimport)
+		#endif // FIP_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	
+		#define FIP_CALLCONV
+		#if defined(GCC_HASCLASSVISIBILITY)
+			#define FIP_API __attribute__ ((visibility("default")))
+		#else
+			#define FIP_API
+		#endif
+	#endif // WIN32 / !WIN32
+#endif // FREEIMAGE_LIB
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+// ----------------------------------------------------------
+
+/** Abstract base class for all objects used by the library.
+	@version FreeImage 3
+	@author Hervé Drolon
+*/
+
+class FIP_API fipObject
+{
+public:
+	/// Destructor
+	virtual ~fipObject(){};
+	
+	/**@name Information functions */
+	//@{
+	/// Returns TRUE if the object is allocated, FALSE otherwise
+	virtual BOOL isValid() const = 0;
+	//@}
+};
+
+// ----------------------------------------------------------
+
+class fipMemoryIO;
+class fipMultiPage;
+class fipTag;
+
+/** A class used to manage all photo related images and all image types used by the library.
+
+	fipImage encapsulates the FIBITMAP format. It relies on the FreeImage library, especially for 
+	loading / saving images and for bit depth conversion.
+	@version FreeImage 3
+	@author Hervé Drolon
+*/
+
+class FIP_API fipImage : public fipObject
+{
+protected:
+	/// DIB data
+	FIBITMAP *_dib;
+	/// Original (or last saved) fif format if available, FIF_UNKNOWN otherwise
+	FREE_IMAGE_FORMAT _fif;
+	/// TRUE whenever the display need to be refreshed
+	mutable BOOL _bHasChanged;
+
+public:
+	friend class fipMultiPage;
+
+public:
+
+	/**@name Creation & Destruction */
+	//@{	
+	/**
+	Constructor
+	@see FreeImage_AllocateT
+	*/
+	fipImage(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0);
+	/// Destructor
+	virtual ~fipImage();
+	/**
+	Image allocator
+	@see FreeImage_AllocateT
+	*/
+	BOOL setSize(FREE_IMAGE_TYPE image_type, unsigned width, unsigned height, unsigned bpp, unsigned red_mask = 0, unsigned green_mask = 0, unsigned blue_mask = 0);
+	/// Destroy image data
+	virtual void clear();
+	//@}
+
+	/**@name Copying */
+	//@{	
+	/**
+	Copy constructor
+	@see FreeImage_Clone
+	*/
+	fipImage(const fipImage& src);
+	/**
+	Copy constructor
+	@see FreeImage_Clone
+	*/
+	fipImage& operator=(const fipImage& src);
+	/**
+	<b>Assignement operator</b><br>
+	Copy the input pointer and manage its destruction
+	@see operator FIBITMAP*()
+	*/
+	fipImage& operator=(FIBITMAP *dib);
+
+
+	/**
+	@brief Copy a sub part of the current image and returns it as a fipImage object.
+	
+	This method works with any bitmap type.
+	@param dst Output subimage
+	@param left Specifies the left position of the cropped rectangle. 
+	@param top Specifies the top position of the cropped rectangle. 
+	@param right Specifies the right position of the cropped rectangle. 
+	@param bottom Specifies the bottom position of the cropped rectangle. 
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_Copy
+	*/
+	BOOL copySubImage(fipImage& dst, int left, int top, int right, int bottom) const;
+
+	/**
+	@brief Alpha blend or combine a sub part image with the current image.
+
+    The bit depth of dst bitmap must be greater than or equal to the bit depth of src. 
+	Upper promotion of src is done internally. Supported bit depth equals to 4, 8, 16, 24 or 32.
+	@param src Source subimage
+	@param left Specifies the left position of the sub image. 
+	@param top Specifies the top position of the sub image. 
+	@param alpha Alpha blend factor. The source and destination images are alpha blended if 
+	alpha = 0..255. If alpha > 255, then the source image is combined to the destination image.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_Paste
+	*/
+	BOOL pasteSubImage(fipImage& src, int left, int top, int alpha = 256);
+
+	/**
+	@brief Crop a sub part of the current image and update it accordingly.
+	
+	This method works with any bitmap type.
+	@param left Specifies the left position of the cropped rectangle. 
+	@param top Specifies the top position of the cropped rectangle. 
+	@param right Specifies the right position of the cropped rectangle. 
+	@param bottom Specifies the bottom position of the cropped rectangle. 
+	@return Returns TRUE if successful, FALSE otherwise.
+	*/
+	BOOL crop(int left, int top, int right, int bottom);
+
+	//@}
+
+	/** @name File type identification
+	 */
+	//@{	
+	/**
+	@brief Identifies an image from disk, given its file name
+	@param lpszPathName Path and file name of the image to identify.
+	@return Returns the found FreeImage format if successful, returns FIF_UNKNOWN otherwise.
+	@see FreeImage_GetFileType, FreeImage_GetFIFFromFilename, FreeImage documentation
+	*/
+	static FREE_IMAGE_FORMAT identifyFIF(const char* lpszPathName);
+
+	/**
+	UNICODE version of identifyFIF (this function only works under WIN32 and does nothing on other OS)
+	@see FreeImage_GetFileTypeU, FreeImage_GetFIFFromFilenameU, FreeImage documentation
+	*/
+	static FREE_IMAGE_FORMAT identifyFIFU(const wchar_t* lpszPathName);
+
+	/**
+	@brief Identifies an image using the specified FreeImageIO struct and fi_handle.
+	@param io FreeImageIO structure
+	@param handle FreeImage fi_handle
+	@return Returns the found FreeImage format if successful, returns FIF_UNKNOWN otherwise.
+	@see FreeImage_GetFileTypeFromHandle, FreeImage documentation
+	*/
+	static FREE_IMAGE_FORMAT identifyFIFFromHandle(FreeImageIO *io, fi_handle handle);
+
+	/**
+	@brief Identifies an image using the specified memory stream.
+	@param hmem FreeImage memory stream
+	@return Returns the found FreeImage format if successful, returns FIF_UNKNOWN otherwise.
+	@see FreeImage_GetFileTypeFromMemory, FreeImage documentation
+	*/
+	static FREE_IMAGE_FORMAT identifyFIFFromMemory(FIMEMORY *hmem);
+
+	//@}
+
+
+	/** @name Loading & Saving
+	 * Loading and saving is handled by the FreeImage library.
+	 */
+	//@{	
+	/**
+	@brief Loads an image from disk, given its file name and an optional flag.
+	@param lpszPathName Path and file name of the image to load.
+	@param flag The signification of this flag depends on the image to be read.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_Load, FreeImage documentation
+	*/
+	BOOL load(const char* lpszPathName, int flag = 0);
+
+	/**
+	UNICODE version of load (this function only works under WIN32 and does nothing on other OS)
+	@see load
+	*/
+	BOOL loadU(const wchar_t* lpszPathName, int flag = 0);
+
+	/**
+	@brief Loads an image using the specified FreeImageIO struct and fi_handle, and an optional flag.
+	@param io FreeImageIO structure
+	@param handle FreeImage fi_handle
+	@param flag The signification of this flag depends on the image to be read.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_LoadFromHandle, FreeImage documentation
+	*/
+	BOOL loadFromHandle(FreeImageIO *io, fi_handle handle, int flag = 0);
+
+	/**
+	@brief Loads an image using the specified memory stream and an optional flag.
+	@param memIO FreeImage memory stream
+	@param flag The signification of this flag depends on the image to be read.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_LoadFromMemory, FreeImage documentation
+	*/
+	BOOL loadFromMemory(fipMemoryIO& memIO, int flag = 0);
+
+	/**
+	@brief Saves an image to disk, given its file name and an optional flag.
+	@param lpszPathName Path and file name of the image to save.
+	@param flag The signification of this flag depends on the image to be saved.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_Save, FreeImage documentation
+	*/
+	BOOL save(const char* lpszPathName, int flag = 0) const;
+
+	/**
+	UNICODE version of save (this function only works under WIN32 and does nothing on other OS)
+	@see save
+	*/
+	BOOL saveU(const wchar_t* lpszPathName, int flag = 0) const;
+
+	/**
+	@brief Saves an image using the specified FreeImageIO struct and fi_handle, and an optional flag.
+	@param fif Format identifier (FreeImage format)
+	@param io FreeImageIO structure
+	@param handle FreeImage fi_handle
+	@param flag The signification of this flag depends on the image to be saved.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_SaveToHandle, FreeImage documentation
+	*/
+	BOOL saveToHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flag = 0) const;
+
+	/**
+	@brief Saves an image using the specified memory stream and an optional flag.
+	@param fif Format identifier (FreeImage format)
+	@param memIO FreeImage memory stream
+	@param flag The signification of this flag depends on the image to be saved.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_SaveToMemory, FreeImage documentation
+	*/
+	BOOL saveToMemory(FREE_IMAGE_FORMAT fif, fipMemoryIO& memIO, int flag = 0) const;
+
+	//@}
+
+	/**	@name Information functions
+	 *  Accessors to the DIB BITMAPINFO structure.
+	 */
+	//@{	
+
+	/**
+	Returns the data type of the image
+	@see FreeImage_GetImageType
+	*/
+	FREE_IMAGE_TYPE getImageType() const;
+
+	/**
+	Returns the image width in pixels
+	@see FreeImage_GetWidth
+	*/
+	unsigned getWidth() const;
+	
+	/**
+	Returns the image height in pixels
+	@see FreeImage_GetHeight
+	*/
+	unsigned getHeight() const;
+	
+	/**
+	Returns the width of the bitmap in bytes rounded to the nearest DWORD.
+	@see FreeImage_GetPitch
+	*/
+	unsigned getScanWidth() const;
+
+	/**
+	Returns a pointer to the FIBITMAP data. Used for direct access from FREEIMAGE functions 
+	or from your own low level C functions.<br>
+	<b>Sample use</b> : <br>
+	<pre>
+	fipImage src, dst;
+	src.load("test.png");
+	dst = FreeImage_ConvertTo8Bits(src);
+	FreeImage_Save(FIF_TIFF, dst, "test.tif", 0);
+	</pre>
+	@see operator=(FIBITMAP *dib)
+	*/
+	operator FIBITMAP*() { 
+		return _dib; 
+	}
+
+	/// Returns TRUE if the image is allocated, FALSE otherwise
+	BOOL isValid() const;
+
+	/**
+	Returns a pointer to the bitmap's BITMAPINFO header. 
+	@see FreeImage_GetInfo
+	*/
+	BITMAPINFO* getInfo() const;
+
+	/**
+	Returns a pointer to the bitmap's BITMAPINFOHEADER. 
+	@see FreeImage_GetInfoHeader
+	*/
+    BITMAPINFOHEADER* getInfoHeader() const;
+
+	/**
+	Returns the size of the bitmap in bytes. 
+	The size of the bitmap is the BITMAPINFOHEADER + the size of the palette + the size of the bitmap data. 
+	@see FreeImage_GetDIBSize
+	*/
+	unsigned getImageSize() const;
+
+	/**
+	Returns the memory footprint of a bitmap, in bytes. 
+	@see FreeImage_GetMemorySize
+	*/
+	unsigned getImageMemorySize() const;
+	
+	/**
+	Returns the bitdepth of the bitmap. <br>
+	When the image type is FIT_BITMAP, valid bitdepth can be 1, 4, 8, 16, 24 or 32.
+	@see FreeImage_GetBPP, getImageType
+	*/
+	unsigned getBitsPerPixel() const;
+
+	/**
+	Returns the width of the bitmap in bytes.<br>
+	<b>This is not the size of the scanline</b>.
+	@see FreeImage_GetLine, getScanWidth
+	*/
+	unsigned getLine() const;
+
+	/**
+	Returns the bitmap resolution along the X axis, in pixels / cm
+	@see FreeImage_GetDotsPerMeterX
+	*/
+	double getHorizontalResolution() const;
+	
+	/**
+	Returns the bitmap resolution along the Y axis, in pixels / cm
+	@see FreeImage_GetDotsPerMeterY
+	*/
+	double getVerticalResolution() const;
+
+	/**
+	set the bitmap resolution along the X axis, in pixels / cm
+	@see FreeImage_GetInfoHeader
+	*/
+	void setHorizontalResolution(double value);
+	
+	/**
+	set the bitmap resolution along the Y axis, in pixels / cm
+	@see FreeImage_GetInfoHeader
+	*/
+	void setVerticalResolution(double value);
+
+	//@}
+
+	/**@name Palette operations */
+	//@{
+	/**
+	Returns a pointer to the bitmap's palette. If the bitmap doesn't have a palette, getPalette returns NULL. 
+	@see FreeImage_GetPalette
+	*/
+	RGBQUAD* getPalette() const;
+	
+	/**
+	Returns the palette size in <b>bytes</b>.
+	@see FreeImage_GetColorsUsed
+	*/
+	unsigned getPaletteSize() const;
+
+	/**
+	Retrieves the number of colours used in the bitmap. If the bitmap is non-palletised, 0 is returned. 
+	@see FreeImage_GetColorsUsed
+	*/
+	unsigned getColorsUsed() const;
+
+	/** 
+	Investigates the colour type of the bitmap.
+	@see FreeImage_GetColorType, FREE_IMAGE_COLOR_TYPE
+	*/
+	FREE_IMAGE_COLOR_TYPE getColorType() const;
+
+	/**
+	Returns TRUE if the bitmap is a 8-bit bitmap with a greyscale palette, FALSE otherwise
+	@see FreeImage_GetBPP, FreeImage_GetColorType
+	*/
+	BOOL isGrayscale() const;
+	//@}
+
+	/**@name Thumbnail access */
+	//@{
+
+	/**
+	Retrieves a copy the thumbnail possibly attached to the bitmap
+	@return Returns TRUE if the thumbnail is present in the bitmap and successfuly retrieved, returns FALSE otherwise
+	@see FreeImage_GetThumbnail
+	*/
+	BOOL getThumbnail(fipImage& image) const;
+
+	/**
+	Attach a thumbnail to the bitmap
+	@return Returns TRUE if the thumbnail was successfuly set, returns FALSE otherwise
+	@see FreeImage_SetThumbnail
+	*/
+	BOOL setThumbnail(const fipImage& image);
+
+	/**
+	Check if the image has an embedded thumbnail
+	@return Returns TRUE if a thumbnail is present in the bitmap, returns FALSE otherwise
+	@see FreeImage_GetThumbnail
+	*/
+	BOOL hasThumbnail() const;
+
+	/**
+	Clear the thumbnail possibly attached to the bitmap
+	@return Returns TRUE if successful, returns FALSe otherwise
+	@see FreeImage_SetThumbnail
+	*/
+	BOOL clearThumbnail();
+
+	//@}
+
+	/**@name Pixel access */
+	//@{	
+
+	/** @brief Returns a pointer to the bitmap bits.
+
+	It is up to you to interpret these bytes correctly, 
+	according to the results of FreeImage_GetBPP and 
+	GetRedMask, FreeImage_GetGreenMask and FreeImage_GetBlueMask.<br>
+	Use this function with getScanWidth to iterates through the pixels. 
+	@see FreeImage_GetBits
+	*/
+	BYTE* accessPixels() const;
+
+	/** @brief Returns a pointer to the start of the given scanline in the bitmapÂ’s data-bits.
+		This pointer can be cast according to the result returned by getImageType.<br>
+		Use this function with getScanWidth to iterates through the pixels. 
+		@see FreeImage_GetScanLine, FreeImage documentation
+	*/
+	BYTE* getScanLine(unsigned scanline) const;
+
+	/** 
+	Get the pixel index of a 1-, 4- or 8-bit palettized image at position (x, y), including range check (slow access). 
+	@param x Pixel position in horizontal direction
+	@param y Pixel position in vertical direction
+	@param value Pixel index (returned value)
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_GetPixelIndex
+	*/
+	BOOL getPixelIndex(unsigned x, unsigned y, BYTE *value) const;
+
+	/** 
+	Get the pixel color of a 16-, 24- or 32-bit image at position (x, y), including range check (slow access). 
+	@param x Pixel position in horizontal direction
+	@param y Pixel position in vertical direction
+	@param value Pixel color (returned value)
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_GetPixelColor
+	*/
+	BOOL getPixelColor(unsigned x, unsigned y, RGBQUAD *value) const;
+
+	/** 
+	Set the pixel index of a 1-, 4- or 8-bit palettized image at position (x, y), including range check (slow access). 
+	@param x Pixel position in horizontal direction
+	@param y Pixel position in vertical direction
+	@param value Pixel index
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_SetPixelIndex
+	*/
+	BOOL setPixelIndex(unsigned x, unsigned y, BYTE *value);
+
+	/** 
+	Set the pixel color of a 16-, 24- or 32-bit image at position (x, y), including range check (slow access). 
+	@param x Pixel position in horizontal direction
+	@param y Pixel position in vertical direction
+	@param value Pixel color
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_SetPixelColor
+	*/
+	BOOL setPixelColor(unsigned x, unsigned y, RGBQUAD *value);
+
+	//@}
+
+	/**	@name Conversion routines
+	 *  Bitmaps are always loaded in their default bit depth. If you want the bitmap to be stored in another bit depth, the class provides several conversion functions.
+	 */
+	//@{	
+	/** 
+	Converts an image to a type supported by FreeImage.
+	@param image_type New image type
+	@param scale_linear TRUE if image pixels must be scaled linearly when converting to a standard bitmap
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertToType, FreeImage_ConvertToStandardType
+	*/
+	BOOL convertToType(FREE_IMAGE_TYPE image_type, BOOL scale_linear = TRUE);
+
+	/** 
+	Converts the bitmap to 1 bit using a threshold T.
+	@param T Threshold value in [0..255]
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_Threshold
+	*/
+	BOOL threshold(BYTE T);
+	
+	/** 
+	Converts a 8-bit image to a monochrome 1-bit image using a dithering algorithm.
+	@param algorithm Dithering algorithm to use.
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_Dither, FREE_IMAGE_DITHER
+	*/
+	BOOL dither(FREE_IMAGE_DITHER algorithm);
+
+	/** 
+	Converts the bitmap to 4 bits. Unless the bitmap is a 1-bit palettized bitmap, colour values are converted to greyscale.
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertTo4Bits
+	*/
+	BOOL convertTo4Bits();
+
+	/** 
+	Converts the bitmap to 8 bits. If the bitmap is 24 or 32-bit RGB, the colour values are converted to greyscale.
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertTo8Bits
+	*/
+	BOOL convertTo8Bits();
+
+	/** 
+	Converts the bitmap to 8 bits.<br> 
+	For palletized bitmaps, the color map is converted to a greyscale ramp.
+	@see FreeImage_ConvertToGreyscale
+	@return Returns TRUE if successful, FALSE otherwise. 
+	*/
+	BOOL convertToGrayscale();
+	
+	/** 
+	Quantizes a full colour 24-bit bitmap to a palletised 8-bit bitmap.<br>
+	The quantize parameter specifies which colour reduction algorithm should be used.
+	@param algorithm Color quantization algorithm to use.
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ColorQuantize, FREE_IMAGE_QUANTIZE
+	*/
+	BOOL colorQuantize(FREE_IMAGE_QUANTIZE algorithm);
+
+	/** 
+	Converts the bitmap to 16 bits. The resulting bitmap has a layout of 5 bits red, 5 bits green, 5 bits blue and 1 unused bit. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertTo16Bits555
+	*/
+	BOOL convertTo16Bits555();
+	
+	/** 
+	Converts the bitmap to 16 bits. The resulting bitmap has a layout of 5 bits red, 6 bits green and 5 bits blue. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertTo16Bits565
+	*/
+	BOOL convertTo16Bits565();
+	
+	/** 
+	Converts the bitmap to 24 bits. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertTo24Bits
+	*/
+	BOOL convertTo24Bits();
+	
+	/** 
+	Converts the bitmap to 32 bits. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertTo32Bits
+	*/
+	BOOL convertTo32Bits();
+
+	/** 
+	Converts the bitmap to a 32-bit float image. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertToFloat
+	*/
+	BOOL convertToFloat();
+
+	/** 
+	Converts the bitmap to a 96-bit RGBF image. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertToRGBF
+	*/
+	BOOL convertToRGBF();
+
+	/** 
+	Converts the bitmap to a 128-bit RGBAF image. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertToRGBAF
+	*/
+	BOOL convertToRGBAF();
+
+	/** 
+	Converts the bitmap to a 16-bit unsigned short image. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertToUINT16
+	*/
+	BOOL convertToUINT16();
+
+	/** 
+	Converts the bitmap to a 48-bit RGB16 image. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertToRGB16
+	*/
+	BOOL convertToRGB16();
+
+	/** 
+	Converts the bitmap to a 64-bit RGBA16 image. 
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ConvertToRGBA16
+	*/
+	BOOL convertToRGBA16();
+
+	/**
+	Converts a High Dynamic Range image (48-bit RGB or 96-bit RGB Float) to a 24-bit RGB image. 
+	@param tmo Tone mapping operator
+	@param first_param First tone mapping algorithm parameter (algorithm dependant)
+	@param second_param Second tone mapping algorithm parameter (algorithm dependant)
+	@param third_param Third tone mapping algorithm parameter (algorithm dependant)
+	@param fourth_param Fourth tone mapping algorithm parameter (algorithm dependant)
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_ToneMapping, FreeImage_TmoReinhard05Ex
+	*/
+	BOOL toneMapping(FREE_IMAGE_TMO tmo, double first_param = 0, double second_param = 0, double third_param = 1, double fourth_param = 0);
+
+	//@}
+
+	/**	@name Transparency support: background colour and alpha channel */
+	//@{
+
+	/**
+	Returns TRUE if the image is transparent, returns FALSE otherwise
+	@see FreeImage_IsTransparent
+	*/
+	BOOL isTransparent() const;
+
+	/**
+	8-bit transparency : get the number of transparent colors.
+	@return Returns the number of transparent colors in a palletised bitmap.
+	@see FreeImage_GetTransparencyCount
+	*/
+	unsigned getTransparencyCount() const;
+
+	/**
+	8-bit transparency : get the bitmapÂ’s transparency table.
+	@return Returns a pointer to the bitmapÂ’s transparency table.
+	@see FreeImage_GetTransparencyTable
+	*/
+	BYTE* getTransparencyTable() const;
+
+	/** 
+	8-bit transparency : set the bitmapÂ’s transparency table.
+	@see FreeImage_SetTransparencyTable
+	*/
+	void setTransparencyTable(BYTE *table, int count);
+
+	/**
+	Returns TRUE when the image has a file background color, FALSE otherwise.
+	@see FreeImage_HasBackgroundColor
+	*/
+	BOOL hasFileBkColor() const;
+
+	/**
+	@brief Retrieves the file background color of an image. 
+	
+	For 8-bit images, the color index 
+	in the palette is returned in the rgbReserved member of the bkcolor parameter.
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_GetBackgroundColor
+	*/
+	BOOL getFileBkColor(RGBQUAD *bkcolor) const;
+
+	/**
+	@brief Set the file background color of an image. 
+	
+	When saving an image to PNG, this background color is transparently saved to the PNG file. 
+	When the bkcolor parameter is NULL, the background color is removed from the image.
+	@return Returns TRUE if successful, FALSE otherwise. 
+	@see FreeImage_SetBackgroundColor
+	*/
+	BOOL setFileBkColor(RGBQUAD *bkcolor);
+	//@}
+
+	/**@name Channel processing support */
+	//@{	
+	/** @brief Retrieves the red, green, blue or alpha channel of a 24- or 32-bit BGR[A] image. 
+	@param image Output image to be extracted
+	@param channel Color channel to extract
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_GetChannel, FREE_IMAGE_COLOR_CHANNEL
+	*/
+	BOOL getChannel(fipImage& image, FREE_IMAGE_COLOR_CHANNEL channel) const;
+
+	/**
+	@brief Insert a 8-bit dib into a 24- or 32-bit image. 
+	@param image Input 8-bit image to insert
+	@param channel Color channel to replace
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_SetChannel, FREE_IMAGE_COLOR_CHANNEL
+	*/
+	BOOL setChannel(fipImage& image, FREE_IMAGE_COLOR_CHANNEL channel);
+
+	/** @brief Split a 24-bit RGB image into 3 greyscale images corresponding to the red, green and blue channels.
+	@param RedChannel Output red channel.
+	@param GreenChannel Output green channel.
+	@param BlueChannel Output blue channel.
+	@return Returns FALSE if the dib isn't a valid image, if it's not a 24-bit image or if 
+	one of the output channel can't be allocated. Returns TRUE otherwise.
+	@see FreeImage_GetChannel
+	*/
+	BOOL splitChannels(fipImage& RedChannel, fipImage& GreenChannel, fipImage& BlueChannel);
+
+	/** @brief Builds a 24-bit RGB image given its red, green and blue channel.
+	@param red Input red channel.
+	@param green Input green channel.
+	@param blue Input blue channel.
+	@return Returns FALSE if the dib can't be allocated, if the input channels are not 8-bit images. Returns TRUE otherwise.
+	@see FreeImage_SetChannel
+	*/
+	BOOL combineChannels(fipImage& red, fipImage& green, fipImage& blue);
+	//@}
+
+	/**@name Rotation and flipping */
+	//@{	
+	/** 
+	Image translation and rotation using B-Splines.
+	@param angle Image rotation angle, in degree
+	@param x_shift Image horizontal shift
+	@param y_shift Image vertical shift
+	@param x_origin Origin of the x-axis
+	@param y_origin Origin of the y-axis
+	@param use_mask Whether or not to mask the image. Image mirroring is applied when use_mask is set to FALSE
+	@return Returns the translated & rotated dib if successful, returns NULL otherwise
+	@see FreeImage_RotateEx
+	*/
+	BOOL rotateEx(double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask);
+
+	/** 
+	Image rotation by means of three shears.
+	@param angle Image rotation angle, in degree
+	@param bkcolor Background color (image type dependent), default to black background
+	@return Returns rotated dib if successful, returns NULL otherwise
+	@see FreeImage_Rotate
+	*/
+	BOOL rotate(double angle, const void *bkcolor = NULL);
+
+	/**
+	Flip the image horizontally along the vertical axis
+	@see FreeImage_FlipHorizontal
+	*/
+	BOOL flipHorizontal();
+
+	/**
+	Flip the image vertically along the horizontal axis
+	@see FreeImage_FlipVertical
+	*/
+	BOOL flipVertical();
+	//@}
+
+	/**@name Color manipulation routines */
+	//@{	
+	/** 
+	Inverts each pixel data.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_Invert
+	*/
+	BOOL invert();
+	
+	/** @brief Perfoms an histogram transformation on a 8, 24 or 32-bit image 
+	according to the values of a lookup table (LUT).
+
+	The transformation is done as follows.<br>
+	Image 8-bit : if the image has a color palette, the LUT is applied to this palette, 
+	otherwise, it is applied to the grey values.<br>
+	Image 24-bit & 32-bit : if channel == IPL_CC_RGB, the same LUT is applied to each color
+	plane (R,G, and B). Otherwise, the LUT is applied to the specified channel only.
+	@param LUT Lookup table. <b>The size of 'LUT' is assumed to be 256.</b>
+	@param channel The color channel to be processed (only used with 24 & 32-bit DIB).
+	@return Returns TRUE if the operation was successful, FALSE otherwise
+	@see FreeImage_AdjustCurve, FREE_IMAGE_COLOR_CHANNEL
+	*/
+	BOOL adjustCurve(BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel);
+
+	/** @brief Performs gamma correction on a 8, 24 or 32-bit image.
+	@param gamma Gamma value to use. A value of 1.0 leaves the image alone, 
+	less than one darkens it, and greater than one lightens it.
+	@return Returns TRUE if the operation was successful, FALSE otherwise
+	@see FreeImage_AdjustGamma, adjustCurve
+	*/
+	BOOL adjustGamma(double gamma);
+
+	/** @brief Adjusts the brightness of a 8, 24 or 32-bit image by a certain amount.
+	@param percentage Where -100 <= percentage <= 100<br>
+	A value 0 means no change, less than 0 will make the image darker 
+	and greater than 0 will make the image brighter.
+	@return Returns TRUE if the operation was succesful, FALSE otherwise
+	@see FreeImage_AdjustBrightness, adjustCurve
+	*/
+	BOOL adjustBrightness(double percentage);
+
+	/** @brief Adjusts the contrast of a 8, 24 or 32-bit image by a certain amount.
+	@param percentage Where -100 <= percentage <= 100<br>
+	A value 0 means no change, less than 0 will decrease the contrast 
+	and greater than 0 will increase the contrast of the image.
+	@return Returns TRUE if the operation was succesfull, FALSE otherwise
+	@see FreeImage_AdjustContrast, adjustCurve
+	*/
+	BOOL adjustContrast(double percentage);
+
+	/**
+	Adjusts an image's brightness, contrast and gamma within a single operation. 
+	If more than one of these image display properties need to be adjusted, 
+	using this function should be preferred over calling each adjustment function separately. 
+	That's particularly true for huge images or if performance is an issue. 
+	@see adjustBrightness
+	@see adjustContrast
+	@see adjustGamma
+	@see FreeImage_AdjustColors
+	*/
+	BOOL adjustBrightnessContrastGamma(double brightness, double contrast, double gamma);
+
+	/** @brief Computes image histogram
+	
+	For 24-bit and 32-bit images, histogram can be computed from red, green, blue and 
+	black channels. For 8-bit images, histogram is computed from the black channel. Other 
+	bit depth is not supported.
+	@param histo pointer to an histogram array. <b>Size of this array is assumed to be 256</b>.
+	@param channel Color channel to use
+	@return Returns TRUE if the operation was succesfull, FALSE otherwise
+	@see FreeImage_GetHistogram
+	*/
+	BOOL getHistogram(DWORD *histo, FREE_IMAGE_COLOR_CHANNEL channel = FICC_BLACK) const;
+	//@}
+
+	/**@name Upsampling / downsampling */
+	//@{	
+
+	/** @brief Rescale the image to a new width / height.
+
+	@param new_width New image width
+	@param new_height New image height
+	@param filter The filter parameter specifies which resampling filter should be used.
+	@return Returns TRUE if the operation was successful, FALSE otherwise
+	@see FreeImage_Rescale, FREE_IMAGE_FILTER
+	*/
+	BOOL rescale(unsigned new_width, unsigned new_height, FREE_IMAGE_FILTER filter);
+
+	/** @brief Creates a thumbnail image keeping aspect ratio
+
+	@param max_size Maximum width or height in pixel units
+	@param convert When set to TRUE, converts the image to a standard type
+	@return Returns TRUE if the operation was successful, FALSE otherwise
+	@see FreeImage_MakeThumbnail
+	*/
+	BOOL makeThumbnail(unsigned max_size, BOOL convert = TRUE);
+	//@}
+
+	/**@name Image status */
+	//@{	
+	/**
+	Set the image status as 'modified'.<br>
+	When using the fipWinImage class, the image status is used to refresh the display. 
+	It is changed to FALSE whenever the display has just been refreshed. 
+	@param bStatus TRUE if the image should be marked as modified, FALSE otherwise
+	@see isModified
+	*/
+	void setModified(BOOL bStatus = TRUE) {
+		_bHasChanged = bStatus;
+	}
+
+	/**
+	Get the image status
+	@return Returns TRUE if the image is marked as modified, FALSE otherwise
+	@see setModified
+	*/
+	BOOL isModified() {
+		return _bHasChanged;
+	}
+	//@}
+
+	/**@name Metadata */
+	//@{	
+	/**
+	Returns the number of tags contained in the <i>model</i> metadata model 
+	attached to the dib
+	@param model Metadata model to look for
+	*/
+	unsigned getMetadataCount(FREE_IMAGE_MDMODEL model) const;
+	/**
+	Retrieve a metadata attached to the dib
+	@param model Metadata model to look for
+	@param key Metadata field name 
+	@param tag Returned tag
+	@return Returns TRUE if the operation was succesfull, FALSE otherwise
+	@see FreeImage_GetMetadata
+	*/
+	BOOL getMetadata(FREE_IMAGE_MDMODEL model, const char *key, fipTag& tag) const;
+	/**
+	Attach a new FreeImage tag to the dib.<br>
+	<b>Sample use</b> : <br>
+	<pre>
+	fipImage image;
+	// ...
+	fipTag tag;
+	tag.setKeyValue("Caption/Abstract", "my caption");
+	image.setMetadata(FIMD_IPTC, tag.getKey(), tag);
+	tag.setKeyValue("Keywords", "FreeImage;Library;Images;Compression");
+	image.setMetadata(FIMD_IPTC, tag.getKey(), tag);
+	</pre>
+
+	@param model Metadata model used to store the tag
+	@param key Tag field name 
+	@param tag Tag to be attached
+	@return Returns TRUE if the operation was succesfull, FALSE otherwise
+	@see FreeImage_SetMetadata
+	*/
+	BOOL setMetadata(FREE_IMAGE_MDMODEL model, const char *key, fipTag& tag);
+	//@}
+
+
+  protected:
+	/**@name Internal use */
+	//@{
+	  BOOL replace(FIBITMAP *new_dib);
+	//@}
+
+};
+
+// ----------------------------------------------------------
+
+/** A class designed for MS Windows (TM) platforms.
+
+    fipWinImage provides methods used to :
+	<ul>
+	<li>Display a DIB on the screen
+	<li>Copy / Paste a DIB to/from Windows devices (HANDLE, HBITMAP, Clipboard)
+	<li>Capture a window (HWND) and convert it to an image
+	</ul>
+	@version FreeImage 3
+	@author Hervé Drolon
+*/
+#ifdef _WIN32
+
+class FIP_API fipWinImage : public fipImage
+{
+public:
+	/**@name Creation & Destruction */
+	//@{	
+	/// Constructor
+	fipWinImage(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0);
+
+	/// Destructor
+	virtual ~fipWinImage();
+
+	/// Destroy image data
+	virtual void clear();
+
+	/// Returns TRUE if the image is allocated, FALSE otherwise
+	BOOL isValid() const;
+	//@}
+
+	/**@name Copying */
+	//@{	
+
+	/**
+	Copy constructor. 
+	Delete internal _display_dib data and copy the base class image data. 
+	Tone mapping parameters are left unchanged. 
+	@see FreeImage_Clone
+	*/
+	fipWinImage& operator=(const fipImage& src);
+
+	/**
+	Copy constructor
+	Delete internal _display_dib data and copy tone mapping parameters. 
+	Copy also the base class image data. 
+	@see FreeImage_Clone
+	*/
+	fipWinImage& operator=(const fipWinImage& src);
+
+	/** Clone function used for clipboard copy.<br>
+	Convert the FIBITMAP image to a DIB, 
+	and transfer the DIB in a global bitmap handle.<br>
+	For non standard bitmaps, the BITMAPINFOHEADER->biCompression field is set to 0xFF + FreeImage_GetImageType(_dib), 
+	in order to recognize the bitmap as non standard. 
+	*/
+	HANDLE copyToHandle() const;
+
+	/** Copy constructor used for clipboard paste.<br>
+	Converts a global object to a FIBITMAP. The clipboard format must be CF_DIB.<br>
+	When the BITMAPINFOHEADER->biCompression field is set to 0xFF + [one of the predefined FREE_IMAGE_TYPE], 
+	the bitmap is recognized as non standard and correctly copied. 
+	@return Returns TRUE if successful, returns FALSE otherwise
+	*/
+	BOOL copyFromHandle(HANDLE hMem);
+
+	/** Copy constructor.<br>
+	Converts a HBITMAP object to a FIBITMAP.
+	@return Returns TRUE if successful, returns FALSE otherwise
+	*/
+	BOOL copyFromBitmap(HBITMAP hbmp);
+	//@}
+
+	/**@name Clipboard operations */
+	//@{	
+	/**
+	Clipboard copy.
+	@param hWndNewOwner Handle to the window to be associated with the open clipboard. 
+	In MFC, you can use AfxGetApp()->m_pMainWnd->GetSafeHwnd().
+	@return Returns TRUE if successful, returns FALSE otherwise
+	*/
+	BOOL copyToClipboard(HWND hWndNewOwner) const;
+
+	/**
+	Retrieves data from the clipboard. The clipboard format must be CF_DIB.
+	@return Returns TRUE if successful, returns FALSE otherwise
+	*/
+	BOOL pasteFromClipboard();
+	//@}
+
+	/**@name Screen capture */
+	//@{	
+	/** Capture a window and convert it to an image
+	@param hWndApplicationWindow Handle to the application main window
+	@param hWndSelectedWindow Handle to the window to be captured
+	@return Returns TRUE if successful, returns FALSE otherwise
+	*/
+	BOOL captureWindow(HWND hWndApplicationWindow, HWND hWndSelectedWindow);
+	//@}
+
+
+	/**@name Painting operations */
+	//@{	
+
+	/** @brief Draw (stretch) the image on a HDC, using StretchDIBits.
+
+    When the image is transparent or has a file background, this function composite 
+	the foreground image against a checkerboard background image.
+	@param hDC Handle to the device context
+	@param rcDest Destination rectangle
+	@see FreeImage_Composite
+	*/
+	void draw(HDC hDC, RECT& rcDest) const {
+		drawEx(hDC, rcDest, FALSE, NULL, NULL);
+	}
+
+	/** @brief Draw (stretch) the image on a HDC, using StretchDIBits.
+
+    When the image is transparent or has a file background, this function can composite 
+	the foreground image against a checkerboard background image, against a single background color or 
+	against a user background image.<br>
+	When the image is a High Dynamic Range image (48-bit or RGB float), this function will apply a 
+	tone mapping operator before drawing the image.<br>
+	The original image (located in the fipImage class) will not be affected by any of the operations 
+	that could be done in order to display it. 
+	@param hDC Handle to the device context
+	@param rcDest Destination rectangle
+	@param useFileBkg When set to TRUE, the function uses the file color background if there is one
+	@param appBkColor When a color is given, the function uses it as the background color
+	@param bg When a FIBITMAP is given, the function uses it as the background image
+	@see FreeImage_Composite
+	@see setToneMappingOperator
+	*/
+	void drawEx(HDC hDC, RECT& rcDest, BOOL useFileBkg = FALSE, RGBQUAD *appBkColor = NULL, FIBITMAP *bg = NULL) const;
+
+	/**
+	Select a tone mapping algorithm used for drawing and set the image as modified 
+	so that the display will be refreshed.
+	@param tmo Tone mapping operator
+	@param first_param First tone mapping algorithm parameter
+	@param second_param Second tone mapping algorithm parameter
+	@param third_param Third tone mapping algorithm parameter
+	@param fourth_param Fourth tone mapping algorithm parameter
+	@see FreeImage_ToneMapping
+	*/
+	void setToneMappingOperator(FREE_IMAGE_TMO tmo, double first_param = 0, double second_param = 0, double third_param = 1, double fourth_param = 0);
+
+	/**
+	Get the tone mapping algorithm used for drawing, with its parameters.
+	@param tmo Tone mapping operator
+	@param first_param First tone mapping algorithm parameter
+	@param second_param Second tone mapping algorithm parameter
+	@param third_param Third tone mapping algorithm parameter
+	@param fourth_param Fourth tone mapping algorithm parameter
+	@see FreeImage_ToneMapping
+	*/
+	void getToneMappingOperator(FREE_IMAGE_TMO *tmo, double *first_param, double *second_param, double *third_param, double *fourth_param) const;
+
+	//@}
+
+protected:
+	/// DIB used for display (this allow to display non-standard bitmaps)
+	mutable FIBITMAP *_display_dib;
+	/// remember to delete _display_dib
+	mutable BOOL _bDeleteMe;
+	/// tone mapping operator
+	FREE_IMAGE_TMO _tmo;
+	/// first tone mapping algorithm parameter
+	double _tmo_param_1;
+	/// second tone mapping algorithm parameter
+	double _tmo_param_2;
+	/// third tone mapping algorithm parameter
+	double _tmo_param_3;
+	/// fourth tone mapping algorithm parameter
+	double _tmo_param_4;
+};
+
+#endif // _WIN32
+
+// ----------------------------------------------------------
+
+/** Memory handle
+	
+	fipMemoryIO is a class that allows you to load / save images from / to a memory stream.
+	@version FreeImage 3
+	@author Hervé Drolon
+*/
+class FIP_API fipMemoryIO : public fipObject
+{
+protected:
+	/// Pointer to a memory stream
+	FIMEMORY *_hmem;
+
+public :
+	/** Constructor.
+	Wrap a memory buffer containing image data.<br>
+	The memory buffer is read only and has to be freed by the user 
+	when no longer in use.<br>
+	When default arguments are used, open a memory file as read/write. 
+	@param data Pointer to the memory buffer
+	@param size_in_bytes Buffer size in bytes
+	@see FreeImage_OpenMemory
+	*/
+    fipMemoryIO(BYTE *data = NULL, DWORD size_in_bytes = 0);
+
+	/** Destructor.
+	Free any allocated memory
+	@see FreeImage_CloseMemory
+	*/
+	virtual ~fipMemoryIO();
+
+	/** Destructor.
+	Free any allocated memory and invalidate the stream
+	@see FreeImage_CloseMemory
+	*/
+	void close();
+
+	/** Returns TRUE if the internal memory buffer is a valid buffer, returns FALSE otherwise
+	*/
+	BOOL isValid() const;
+
+	/** Returns the buffer image format
+	@see FreeImage_GetFileTypeFromMemory
+	*/
+	FREE_IMAGE_FORMAT getFileType() const;
+
+	/**
+	Returns a pointer to the FIMEMORY data. Used for direct access from FREEIMAGE functions 
+	or from your own low level C functions.
+	*/
+	operator FIMEMORY*() { 
+		return _hmem; 
+	}
+
+	/**@name Memory IO routines */
+	//@{	
+	/**
+	Loads a dib from a memory stream
+	@param fif Format identifier (FreeImage format)
+	@param flags The signification of this flag depends on the image to be loaded.
+	@return Returns the loaded dib if successful, returns NULL otherwise
+	@see FreeImage_LoadFromMemory
+	*/
+	FIBITMAP* load(FREE_IMAGE_FORMAT fif, int flags = 0) const;
+	/**
+	Loads a multi-page bitmap from a memory stream
+	@param fif Format identifier (FreeImage format)
+	@param flags The signification of this flag depends on the multi-page to be loaded.
+	@return Returns the loaded multi-page if successful, returns NULL otherwise
+	@see FreeImage_LoadMultiBitmapFromMemory
+	*/
+	FIMULTIBITMAP* loadMultiPage(FREE_IMAGE_FORMAT fif, int flags = 0) const;
+	/**
+	Saves a dib to a memory stream
+	@param fif Format identifier (FreeImage format)
+	@param dib Image to be saved
+	@param flags The signification of this flag depends on the image to be saved.
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_SaveToMemory
+	*/
+	BOOL save(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, int flags = 0);
+	/**
+	Saves a multi-page bitmap to a memory stream
+	@param fif Format identifier (FreeImage format)
+	@param bitmap Multi-page image to be saved
+	@param flags The signification of this flag depends on the image to be saved.
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_SaveMultiBitmapToMemory
+	*/
+	BOOL saveMultiPage(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, int flags = 0);
+	/**
+	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
+	@return Returns the number of full items actually read, which may be less than count if an error occurs
+	@see FreeImage_ReadMemory
+	*/
+	unsigned read(void *buffer, unsigned size, unsigned count) const;
+	/**
+	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
+	@return Returns the number of full items actually written, which may be less than count if an error occurs
+	@see FreeImage_WriteMemory
+	*/
+	unsigned write(const void *buffer, unsigned size, unsigned count);
+	/**
+	Gets the current position of a memory pointer
+	@see FreeImage_TellMemory
+	*/
+	long tell() const;
+	/**
+	Moves the memory pointer to a specified location
+	@see FreeImage_SeekMemory
+	*/
+	BOOL seek(long offset, int origin);
+	/**
+	Provides a direct buffer access to a memory stream
+	@param data Pointer to the memory buffer (returned value)
+	@param size_in_bytes Buffer size in bytes (returned value)
+	@see FreeImage_AcquireMemory
+	*/
+	BOOL acquire(BYTE **data, DWORD *size_in_bytes);
+	//@}
+
+private:
+	/// Disable copy
+	fipMemoryIO(const fipMemoryIO& src);
+	/// Disable copy
+	fipMemoryIO& operator=(const fipMemoryIO& src);
+
+};
+
+// ----------------------------------------------------------
+
+/** Multi-page file stream
+
+	fipMultiPage encapsulates the multi-page API. It supports reading/writing 
+	multi-page TIFF, ICO and GIF files. 
+*/
+class FIP_API fipMultiPage : public fipObject 
+{
+protected:
+	/// Pointer to a multi-page file stream
+	FIMULTIBITMAP *_mpage;
+	/// TRUE when using a memory cache, FALSE otherwise
+	BOOL _bMemoryCache;
+
+public:
+	/**
+	Constructor
+	@param keep_cache_in_memory When it is TRUE, all gathered bitmap data in the page manipulation process is kept in memory, otherwise it is lazily flushed to a temporary file on the hard disk in 64 Kb blocks.
+	*/
+	fipMultiPage(BOOL keep_cache_in_memory = FALSE);
+
+	/**
+	Destructor
+	Close the file stream if not already done. 
+	*/
+	virtual ~fipMultiPage();
+
+	/// Returns TRUE if the multi-page stream is opened
+	BOOL isValid() const;
+
+	/**
+	Returns a pointer to the FIMULTIBITMAP data. Used for direct access from FREEIMAGE functions 
+	or from your own low level C functions.
+	*/
+	operator FIMULTIBITMAP*() { 
+		return _mpage; 
+	}
+
+	/**
+	Open a multi-page file stream
+	@param lpszPathName Name of the multi-page bitmap file
+	@param create_new When TRUE, it means that a new bitmap will be created rather than an existing one being opened
+	@param read_only When TRUE the bitmap is opened read-only
+	@param flags Load flags. The signification of this flag depends on the image to be loaded.
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_OpenMultiBitmap
+	*/
+	BOOL open(const char* lpszPathName, BOOL create_new, BOOL read_only, int flags = 0);
+
+	/**
+	Open a multi-page memory stream as read/write. 
+	@param memIO Memory stream. The memory stream MUST BE a wrapped user buffer. 
+	@param flags Load flags. The signification of this flag depends on the image to be loaded.
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_LoadMultiBitmapFromMemory
+	*/
+	BOOL open(fipMemoryIO& memIO, int flags = 0);
+
+	/**
+	Open a multi-page image as read/write, using the specified FreeImageIO struct and fi_handle, and an optional flag.
+	@param io FreeImageIO structure
+	@param handle FreeImage fi_handle
+	@param flag The signification of this flag depends on the image to be read.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_OpenMultiBitmapFromHandle
+	*/
+	BOOL open(FreeImageIO *io, fi_handle handle, int flags = 0);
+
+	/**
+	Close a file stream
+	@param flags Save flags. The signification of this flag depends on the image to be saved.
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_CloseMultiBitmap
+	*/
+	BOOL close(int flags = 0);
+
+	/**
+	Saves a multi-page image using the specified FreeImageIO struct and fi_handle, and an optional flag.
+	@param fif Format identifier (FreeImage format)
+	@param io FreeImageIO structure
+	@param handle FreeImage fi_handle
+	@param flag The signification of this flag depends on the multi-page image to be saved.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_SaveMultiBitmapToHandle, FreeImage documentation
+	*/
+	BOOL saveToHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags = 0) const;
+
+	/**
+	Saves a multi-page image using the specified memory stream and an optional flag.
+	@param fif Format identifier (FreeImage format)
+	@param memIO FreeImage memory stream
+	@param flag The signification of this flag depends on the image to be saved.
+	@return Returns TRUE if successful, FALSE otherwise.
+	@see FreeImage_SaveMultiBitmapToMemory, FreeImage documentation
+	*/
+	BOOL saveToMemory(FREE_IMAGE_FORMAT fif, fipMemoryIO& memIO, int flags = 0) const;
+
+	/**
+	Returns the number of pages currently available in the multi-paged bitmap
+	@see FreeImage_GetPageCount
+	*/
+	int getPageCount() const;
+
+	/**
+	Appends a new page to the end of the bitmap
+	@param image Image to append
+	@see FreeImage_AppendPage
+	*/
+	void appendPage(fipImage& image);
+
+	/**
+	Inserts a new page before the given position in the bitmap
+	@param page Page number. Page has to be a number smaller than the current number of pages available in the bitmap.
+	@param image Image to insert
+	@see FreeImage_InsertPage
+	*/
+	void insertPage(int page, fipImage& image);
+
+	/**
+	Deletes the page on the given position
+	@param page Page number
+	@see FreeImage_DeletePage
+	*/
+	void deletePage(int page);
+
+	/**
+	Moves the source page to the position of the target page. 
+	@param target Target page position
+	@param source Source page position
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_MovePage
+	*/
+	BOOL movePage(int target, int source);
+
+	/**
+	Locks a page in memory for editing. You must call unlockPage to free the page<br>
+	<b>Usage : </b><br>
+	<pre>
+	fipMultiPage mpage;
+	// ...
+	fipImage image;		// You must declare this before
+	image = mpage.lockPage(2);
+	if(image.isValid()) {
+	  // ...
+	  mpage.unlockPage(image, TRUE);
+	}
+	</pre>
+	@param page Page number
+	@return Returns the page if successful, returns NULL otherwise
+	@see FreeImage_LockPage
+	*/
+	FIBITMAP* lockPage(int page);
+
+	/**
+	Unlocks a previously locked page and gives it back to the multi-page engine
+	@param image Page to unlock
+	@param changed When TRUE, the page is marked changed and the new page data is applied in the multi-page bitmap.
+	@see FreeImage_UnlockPage
+	*/
+	void unlockPage(fipImage& image, BOOL changed);
+
+	/**
+	Returns an array of page-numbers that are currently locked in memory. 
+	When the pages parameter is NULL, the size of the array is returned in the count variable. 
+	You can then allocate the array of the desired size and call 
+	getLockedPageNumbers again to populate the array.
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_GetLockedPageNumbers
+	*/
+	BOOL getLockedPageNumbers(int *pages, int *count) const;
+};
+
+// ----------------------------------------------------------
+
+/**
+FreeImage Tag
+
+FreeImage uses this structure to store metadata information. 
+*/
+class FIP_API fipTag : public fipObject
+{
+protected:
+	/// Pointer to a FreeImage tag
+	FITAG *_tag;
+
+public:
+	/**@name Creation & Destruction */
+	//@{	
+	/**
+	Constructor
+	@see FreeImage_CreateTag
+	*/
+	fipTag();
+	/** 
+	Destructor
+	@see FreeImage_DeleteTag
+	*/
+	virtual ~fipTag();
+	/**
+	Construct a FIDT_ASCII tag (ASCII string).<br>
+	This method is useful to store comments or IPTC tags. 
+	@param name Field name
+	@param value Field value
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_CreateTag
+	*/
+	BOOL setKeyValue(const char *key, const char *value);
+
+	//@}
+
+	/**@name Copying */
+	//@{	
+	/**
+	Copy constructor
+	@see FreeImage_CloneTag
+	*/
+	fipTag(const fipTag& tag);
+	/**
+	Copy constructor
+	@see FreeImage_CloneTag
+	*/
+	fipTag& operator=(const fipTag& tag);
+	/**
+	<b>Assignement operator</b><br>
+	Copy the input pointer and manage its destruction
+	@see operator FITAG*()
+	*/
+	fipTag& operator=(FITAG *tag);
+	//@}
+
+	/**
+	Returns a pointer to the FITAG data. Used for direct access from FREEIMAGE functions 
+	or from your own low level C functions.
+	@see operator=(FITAG *tag)
+	*/
+	operator FITAG*() { 
+		return _tag; 
+	}
+
+	/// Returns TRUE if the tag is allocated, FALSE otherwise
+	BOOL isValid() const;
+
+	/**@name Tag accessors */
+	//@{	
+	/**
+	Returns the tag field name (unique inside a metadata model).
+	@see FreeImage_GetTagKey
+	*/
+	const char *getKey() const;
+	/**
+	Returns the tag description if available, returns NULL otherwise
+	@see FreeImage_GetTagDescription
+	*/
+	const char *getDescription() const;
+	/**
+	Returns the tag ID if available, returns 0 otherwise
+	@see FreeImage_GetTagID
+	*/
+	WORD getID() const;
+	/**
+	Returns the tag data type 
+	@see FreeImage_GetTagType
+	*/
+	FREE_IMAGE_MDTYPE getType() const;
+	/**
+	Returns the number of components in the tag (in tag type units)
+	@see FreeImage_GetTagCount
+	*/
+	DWORD getCount() const;
+	/**
+	Returns the length of the tag value in bytes
+	@see FreeImage_GetTagLength
+	*/
+	DWORD getLength() const;
+	/**
+	Returns the tag value
+	@see FreeImage_GetTagValue
+	*/
+	const void *getValue() const;
+	/**
+	Set the tag field name 
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_SetTagKey
+	*/
+	BOOL setKey(const char *key);
+	/**
+	Set the (usually optional) tag description
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_SetTagDescription
+	*/
+	BOOL setDescription(const char *description);
+	/**
+	Set the (usually optional) tad ID
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_SetTagID
+	*/
+	BOOL setID(WORD id);
+	/**
+	Set the tag data type 
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_SetTagType
+	*/
+	BOOL setType(FREE_IMAGE_MDTYPE type);
+	/**
+	Set the number of data in the tag 
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_SetTagCount
+	*/
+	BOOL setCount(DWORD count);
+	/**
+	Set the length of the tag value, in bytes 
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_SetTagLength
+	*/
+	BOOL setLength(DWORD length);
+	/**
+	Set the tag value 
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_SetTagValue
+	*/
+	BOOL setValue(const void *value);
+
+	//@}
+
+	/**
+	Converts a FreeImage tag structure to a string that represents the interpreted tag value
+	@param model Metadata model specification (metadata model from which the tag was extracted)
+	@param Make Camera model (not used yet)
+	*/
+	const char* toString(FREE_IMAGE_MDMODEL model, char *Make = NULL) const;
+
+};
+
+/**
+Metadata iterator
+
+<b>Usage : </b><br>
+<pre>
+fipImage image;
+// ...
+fipTag tag;
+fipMetadataFind finder;
+if( finder.findFirstMetadata(FIMD_EXIF_MAIN, image, tag) ) {
+  do {
+    // process the tag
+	cout << tag.getKey() << "\n";
+
+  } while( finder.findNextMetadata(tag) );
+}
+// the class can be called again with another metadata model
+if( finder.findFirstMetadata(FIMD_EXIF_EXIF, image, tag) ) {
+  do {
+    // process the tag
+	cout << tag.getKey() << "\n";
+
+  } while( finder.findNextMetadata(tag) );
+}
+</pre>
+*/
+class FIP_API fipMetadataFind : public fipObject
+{
+protected:
+	/// Pointer to a search handle
+	FIMETADATA *_mdhandle;
+
+public:
+	/// Returns TRUE if the search handle is allocated, FALSE otherwise
+	BOOL isValid() const;
+
+	/// Constructor
+	fipMetadataFind();
+	/**
+	Destructor
+	@see FreeImage_FindCloseMetadata
+	*/
+	virtual ~fipMetadataFind();
+	/**
+	Provides information about the first instance of a tag that matches 
+	the metadata model specified in the <i>model</i> argument. 
+	@param model Metadata model
+	@param image Input image
+	@param tag Returned tag
+	@return Returns TRUE if successful, returns FALSE otherwise
+	@see FreeImage_FindFirstMetadata
+	*/
+	BOOL findFirstMetadata(FREE_IMAGE_MDMODEL model, fipImage& image, fipTag& tag);
+	/**
+	Find the next tag, if any, that matches the metadata model argument 
+	in a previous call to findFirstMetadata
+	@param tag Returned tag
+	@return Returns TRUE if successful, returns FALSE otherwise, indicating that no more matching tags could be found
+	@see FreeImage_FindNextMetadata
+	*/
+	BOOL findNextMetadata(fipTag& tag);
+
+};
+
+#endif	// FREEIMAGEPLUS_H
diff --git a/files/Wrapper/FreeImagePlus/src/FreeImagePlus.cpp b/files/Wrapper/FreeImagePlus/src/FreeImagePlus.cpp
new file mode 100644
index 0000000..efb8fe9
--- /dev/null
+++ b/files/Wrapper/FreeImagePlus/src/FreeImagePlus.cpp
@@ -0,0 +1,46 @@
+// ==========================================================
+// FreeImagePlus.cpp : Defines the entry point for the DLL application.
+//
+// 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!
+// ==========================================================
+
+#ifdef _WIN32
+#include <windows.h>
+#endif // _WIN32
+
+#include "FreeImagePlus.h"
+
+//----------------------------------------------------------------------
+
+#ifdef _WIN32
+
+BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
+{
+    switch (ul_reason_for_call)
+	{
+		case DLL_PROCESS_ATTACH:
+		case DLL_THREAD_ATTACH:
+		case DLL_THREAD_DETACH:
+		case DLL_PROCESS_DETACH:
+			break;
+    }
+    return TRUE;
+}
+
+
+#endif // _WIN32
diff --git a/files/Wrapper/FreeImagePlus/src/fipImage.cpp b/files/Wrapper/FreeImagePlus/src/fipImage.cpp
new file mode 100644
index 0000000..0ce5688
--- /dev/null
+++ b/files/Wrapper/FreeImagePlus/src/fipImage.cpp
@@ -0,0 +1,974 @@
+// ==========================================================
+// fipImage class implementation
+//
+// 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 "FreeImagePlus.h"
+
+///////////////////////////////////////////////////////////////////   
+// Protected functions
+
+BOOL fipImage::replace(FIBITMAP *new_dib) {
+	if(new_dib == NULL) 
+		return FALSE;
+	if(_dib)
+		FreeImage_Unload(_dib);
+	_dib = new_dib;
+	_bHasChanged = TRUE;
+	return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////
+// Creation & Destruction
+
+fipImage::fipImage(FREE_IMAGE_TYPE image_type, unsigned width, unsigned height, unsigned bpp) {
+	_dib = NULL;
+	_bHasChanged = FALSE;
+	if(width && height && bpp)
+		setSize(image_type, width, height, bpp);
+}
+
+fipImage::~fipImage() {
+	if(_dib) {
+		FreeImage_Unload(_dib);
+		_dib = NULL;
+	}
+}
+
+BOOL fipImage::setSize(FREE_IMAGE_TYPE image_type, unsigned width, unsigned height, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+	if(_dib) {
+		FreeImage_Unload(_dib);
+	}
+	if((_dib = FreeImage_AllocateT(image_type, width, height, bpp, red_mask, green_mask, blue_mask)) == NULL)
+		return FALSE;
+
+	if(image_type == FIT_BITMAP) {
+		// Create palette if needed
+		switch(bpp)	{
+			case 1:
+			case 4:
+			case 8:
+				RGBQUAD *pal = FreeImage_GetPalette(_dib);
+				for(unsigned i = 0; i < FreeImage_GetColorsUsed(_dib); i++) {
+					pal[i].rgbRed = i;
+					pal[i].rgbGreen = i;
+					pal[i].rgbBlue = i;
+				}
+				break;
+		}
+	}
+
+	_bHasChanged = TRUE;
+
+	return TRUE;
+}
+
+void fipImage::clear() {
+	if(_dib) {
+		FreeImage_Unload(_dib);
+		_dib = NULL;
+	}
+	_bHasChanged = TRUE;
+}
+
+///////////////////////////////////////////////////////////////////
+// Copying
+
+fipImage::fipImage(const fipImage& Image) {
+	_dib = NULL;
+	_fif = FIF_UNKNOWN;
+	FIBITMAP *clone = FreeImage_Clone((FIBITMAP*)Image._dib);
+	replace(clone);
+}
+
+fipImage& fipImage::operator=(const fipImage& Image) {
+	if(this != &Image) {
+		FIBITMAP *clone = FreeImage_Clone((FIBITMAP*)Image._dib);
+		replace(clone);
+	}
+	return *this;
+}
+
+fipImage& fipImage::operator=(FIBITMAP *dib) {
+	if(_dib != dib) {
+		replace(dib);
+	}
+	return *this;
+}
+
+BOOL fipImage::copySubImage(fipImage& dst, int left, int top, int right, int bottom) const {
+	if(_dib) {
+		dst = FreeImage_Copy(_dib, left, top, right, bottom);
+		return dst.isValid();
+	}
+	return FALSE;
+}
+
+BOOL fipImage::pasteSubImage(fipImage& src, int left, int top, int alpha) {
+	if(_dib) {
+		BOOL bResult = FreeImage_Paste(_dib, src._dib, left, top, alpha);
+		_bHasChanged = TRUE;
+		return bResult;
+	}
+	return FALSE;
+}
+
+BOOL fipImage::crop(int left, int top, int right, int bottom) {
+	if(_dib) {
+		FIBITMAP *dst = FreeImage_Copy(_dib, left, top, right, bottom);
+		return replace(dst);
+	}
+	return FALSE;
+}
+
+
+///////////////////////////////////////////////////////////////////
+// Information functions
+
+FREE_IMAGE_TYPE fipImage::getImageType() const {
+	return FreeImage_GetImageType(_dib);
+}
+
+unsigned fipImage::getWidth() const {
+	return FreeImage_GetWidth(_dib); 
+}
+
+unsigned fipImage::getHeight() const {
+	return FreeImage_GetHeight(_dib); 
+}
+
+unsigned fipImage::getScanWidth() const {
+	return FreeImage_GetPitch(_dib);
+}
+
+BOOL fipImage::isValid() const {
+	return (_dib != NULL) ? TRUE:FALSE;
+}
+
+BITMAPINFO* fipImage::getInfo() const {
+	return FreeImage_GetInfo(_dib);
+}
+
+BITMAPINFOHEADER* fipImage::getInfoHeader() const {
+	return FreeImage_GetInfoHeader(_dib);
+}
+
+unsigned fipImage::getImageSize() const {
+	return FreeImage_GetDIBSize(_dib);
+}
+
+unsigned fipImage::getImageMemorySize() const {
+	return FreeImage_GetMemorySize(_dib);
+}
+
+unsigned fipImage::getBitsPerPixel() const {
+	return FreeImage_GetBPP(_dib);
+}
+
+unsigned fipImage::getLine() const {
+	return FreeImage_GetLine(_dib);
+}
+
+double fipImage::getHorizontalResolution() const {
+	return (FreeImage_GetDotsPerMeterX(_dib) / (double)100); 
+}
+
+double fipImage::getVerticalResolution() const {
+	return (FreeImage_GetDotsPerMeterY(_dib) / (double)100);
+}
+
+void fipImage::setHorizontalResolution(double value) {
+	FreeImage_SetDotsPerMeterX(_dib, (unsigned)(value * 100 + 0.5));
+}
+
+void fipImage::setVerticalResolution(double value) {
+	FreeImage_SetDotsPerMeterY(_dib, (unsigned)(value * 100 + 0.5));
+}
+
+
+///////////////////////////////////////////////////////////////////
+// Palette operations
+
+RGBQUAD* fipImage::getPalette() const {
+	return FreeImage_GetPalette(_dib);
+}
+
+unsigned fipImage::getPaletteSize() const {
+	return FreeImage_GetColorsUsed(_dib) * sizeof(RGBQUAD);
+}
+
+unsigned fipImage::getColorsUsed() const {
+	return FreeImage_GetColorsUsed(_dib);
+}
+
+FREE_IMAGE_COLOR_TYPE fipImage::getColorType() const { 
+	return FreeImage_GetColorType(_dib);
+}
+
+BOOL fipImage::isGrayscale() const {
+	return ((FreeImage_GetBPP(_dib) == 8) && (FreeImage_GetColorType(_dib) != FIC_PALETTE)); 
+}
+
+///////////////////////////////////////////////////////////////////
+// Thumbnail access
+
+BOOL fipImage::getThumbnail(fipImage& image) const {
+	image = FreeImage_Clone( FreeImage_GetThumbnail(_dib) );
+	return image.isValid();
+}
+
+BOOL fipImage::setThumbnail(const fipImage& image) {
+	return FreeImage_SetThumbnail(_dib, (FIBITMAP*)image._dib);
+}
+
+BOOL fipImage::hasThumbnail() const {
+	return (FreeImage_GetThumbnail(_dib) != NULL);
+}
+
+BOOL fipImage::clearThumbnail() {
+	return FreeImage_SetThumbnail(_dib, NULL);
+}
+
+
+///////////////////////////////////////////////////////////////////
+// Pixel access
+
+BYTE* fipImage::accessPixels() const {
+	return FreeImage_GetBits(_dib); 
+}
+
+BYTE* fipImage::getScanLine(unsigned scanline) const {
+	if(scanline < FreeImage_GetHeight(_dib)) {
+		return FreeImage_GetScanLine(_dib, scanline);
+	}
+	return NULL;
+}
+
+BOOL fipImage::getPixelIndex(unsigned x, unsigned y, BYTE *value) const {
+	return FreeImage_GetPixelIndex(_dib, x, y, value);
+}
+
+BOOL fipImage::getPixelColor(unsigned x, unsigned y, RGBQUAD *value) const {
+	return FreeImage_GetPixelColor(_dib, x, y, value);
+}
+
+BOOL fipImage::setPixelIndex(unsigned x, unsigned y, BYTE *value) {
+	_bHasChanged = TRUE;
+	return FreeImage_SetPixelIndex(_dib, x, y, value);
+}
+
+BOOL fipImage::setPixelColor(unsigned x, unsigned y, RGBQUAD *value) {
+	_bHasChanged = TRUE;
+	return FreeImage_SetPixelColor(_dib, x, y, value);
+}
+
+///////////////////////////////////////////////////////////////////
+// File type identification
+
+FREE_IMAGE_FORMAT fipImage::identifyFIF(const char* lpszPathName) {
+	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
+
+	// check the file signature and get its format
+	// (the second argument is currently not used by FreeImage)
+	fif = FreeImage_GetFileType(lpszPathName, 0);
+	if(fif == FIF_UNKNOWN) {
+		// no signature ?
+		// try to guess the file format from the file extension
+		fif = FreeImage_GetFIFFromFilename(lpszPathName);
+	}
+
+	return fif;
+}
+
+FREE_IMAGE_FORMAT fipImage::identifyFIFU(const wchar_t* lpszPathName) {
+	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
+
+	// check the file signature and get its format
+	// (the second argument is currently not used by FreeImage)
+	fif = FreeImage_GetFileTypeU(lpszPathName, 0);
+	if(fif == FIF_UNKNOWN) {
+		// no signature ?
+		// try to guess the file format from the file extension
+		fif = FreeImage_GetFIFFromFilenameU(lpszPathName);
+	}
+
+	return fif;
+}
+
+FREE_IMAGE_FORMAT fipImage::identifyFIFFromHandle(FreeImageIO *io, fi_handle handle) {
+	if(io && handle) {
+		// check the file signature and get its format
+		return FreeImage_GetFileTypeFromHandle(io, handle, 16);
+	}
+	return FIF_UNKNOWN;
+}
+
+FREE_IMAGE_FORMAT fipImage::identifyFIFFromMemory(FIMEMORY *hmem) {
+	if(hmem != NULL) {
+		return FreeImage_GetFileTypeFromMemory(hmem, 0);
+	}
+	return FIF_UNKNOWN;
+}
+
+
+///////////////////////////////////////////////////////////////////
+// Loading & Saving
+
+BOOL fipImage::load(const char* lpszPathName, int flag) {
+	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
+
+	// check the file signature and get its format
+	// (the second argument is currently not used by FreeImage)
+	fif = FreeImage_GetFileType(lpszPathName, 0);
+	if(fif == FIF_UNKNOWN) {
+		// no signature ?
+		// try to guess the file format from the file extension
+		fif = FreeImage_GetFIFFromFilename(lpszPathName);
+	}
+	// check that the plugin has reading capabilities ...
+	if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) {
+		// Free the previous dib
+		if(_dib) {
+			FreeImage_Unload(_dib);			
+		}
+		// Load the file
+		_dib = FreeImage_Load(fif, lpszPathName, flag);
+		_bHasChanged = TRUE;
+		if(_dib == NULL)
+			return FALSE;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL fipImage::loadU(const wchar_t* lpszPathName, int flag) {
+	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
+
+	// check the file signature and get its format
+	// (the second argument is currently not used by FreeImage)
+	fif = FreeImage_GetFileTypeU(lpszPathName, 0);
+	if(fif == FIF_UNKNOWN) {
+		// no signature ?
+		// try to guess the file format from the file extension
+		fif = FreeImage_GetFIFFromFilenameU(lpszPathName);
+	}
+	// check that the plugin has reading capabilities ...
+	if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) {
+		// Free the previous dib
+		if(_dib) {
+			FreeImage_Unload(_dib);			
+		}
+		// Load the file
+		_dib = FreeImage_LoadU(fif, lpszPathName, flag);
+		_bHasChanged = TRUE;
+		if(_dib == NULL)
+			return FALSE;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL fipImage::loadFromHandle(FreeImageIO *io, fi_handle handle, int flag) {
+	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
+
+	// check the file signature and get its format
+	fif = FreeImage_GetFileTypeFromHandle(io, handle, 16);
+	if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) {
+		// Free the previous dib
+		if(_dib) {
+			FreeImage_Unload(_dib);			
+		}
+		// Load the file
+		_dib = FreeImage_LoadFromHandle(fif, io, handle, flag);
+		_bHasChanged = TRUE;
+		if(_dib == NULL)
+			return FALSE;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL fipImage::loadFromMemory(fipMemoryIO& memIO, int flag) {
+	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
+
+	// check the file signature and get its format
+	fif = memIO.getFileType();
+	if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) {
+		// Free the previous dib
+		if(_dib) {
+			FreeImage_Unload(_dib);			
+		}
+		// Load the file
+		_dib = memIO.load(fif, flag);
+		_bHasChanged = TRUE;
+		if(_dib == NULL)
+			return FALSE;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+BOOL fipImage::save(const char* lpszPathName, int flag) const {
+	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
+	BOOL bSuccess = FALSE;
+
+	// Try to guess the file format from the file extension
+	fif = FreeImage_GetFIFFromFilename(lpszPathName);
+	if(fif != FIF_UNKNOWN ) {
+		// Check that the dib can be saved in this format
+		BOOL bCanSave;
+
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(_dib);
+		if(image_type == FIT_BITMAP) {
+			// standard bitmap type
+			WORD bpp = FreeImage_GetBPP(_dib);
+			bCanSave = (FreeImage_FIFSupportsWriting(fif) && FreeImage_FIFSupportsExportBPP(fif, bpp));
+		} else {
+			// special bitmap type
+			bCanSave = FreeImage_FIFSupportsExportType(fif, image_type);
+		}
+
+		if(bCanSave) {
+			bSuccess = FreeImage_Save(fif, _dib, lpszPathName, flag);
+			return bSuccess;
+		}
+	}
+	return bSuccess;
+}
+
+BOOL fipImage::saveU(const wchar_t* lpszPathName, int flag) const {
+	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
+	BOOL bSuccess = FALSE;
+
+	// Try to guess the file format from the file extension
+	fif = FreeImage_GetFIFFromFilenameU(lpszPathName);
+	if(fif != FIF_UNKNOWN ) {
+		// Check that the dib can be saved in this format
+		BOOL bCanSave;
+
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(_dib);
+		if(image_type == FIT_BITMAP) {
+			// standard bitmap type
+			WORD bpp = FreeImage_GetBPP(_dib);
+			bCanSave = (FreeImage_FIFSupportsWriting(fif) && FreeImage_FIFSupportsExportBPP(fif, bpp));
+		} else {
+			// special bitmap type
+			bCanSave = FreeImage_FIFSupportsExportType(fif, image_type);
+		}
+
+		if(bCanSave) {
+			bSuccess = FreeImage_SaveU(fif, _dib, lpszPathName, flag);
+			return bSuccess;
+		}
+	}
+	return bSuccess;
+}
+
+BOOL fipImage::saveToHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flag) const {
+	BOOL bSuccess = FALSE;
+
+	if(fif != FIF_UNKNOWN ) {
+		// Check that the dib can be saved in this format
+		BOOL bCanSave;
+
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(_dib);
+		if(image_type == FIT_BITMAP) {
+			// standard bitmap type
+			WORD bpp = FreeImage_GetBPP(_dib);
+			bCanSave = (FreeImage_FIFSupportsWriting(fif) && FreeImage_FIFSupportsExportBPP(fif, bpp));
+		} else {
+			// special bitmap type
+			bCanSave = FreeImage_FIFSupportsExportType(fif, image_type);
+		}
+
+		if(bCanSave) {
+			bSuccess = FreeImage_SaveToHandle(fif, _dib, io, handle, flag);
+			return bSuccess;
+		}
+	}
+	return bSuccess;
+}
+
+BOOL fipImage::saveToMemory(FREE_IMAGE_FORMAT fif, fipMemoryIO& memIO, int flag) const {
+	BOOL bSuccess = FALSE;
+
+	if(fif != FIF_UNKNOWN ) {
+		// Check that the dib can be saved in this format
+		BOOL bCanSave;
+
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(_dib);
+		if(image_type == FIT_BITMAP) {
+			// standard bitmap type
+			WORD bpp = FreeImage_GetBPP(_dib);
+			bCanSave = (FreeImage_FIFSupportsWriting(fif) && FreeImage_FIFSupportsExportBPP(fif, bpp));
+		} else {
+			// special bitmap type
+			bCanSave = FreeImage_FIFSupportsExportType(fif, image_type);
+		}
+
+		if(bCanSave) {
+			bSuccess = memIO.save(fif, _dib, flag);
+			return bSuccess;
+		}
+	}
+	return bSuccess;
+}
+
+///////////////////////////////////////////////////////////////////   
+// Conversion routines
+
+BOOL fipImage::convertToType(FREE_IMAGE_TYPE image_type, BOOL scale_linear) {
+	if(_dib) {
+		FIBITMAP *dib = FreeImage_ConvertToType(_dib, image_type, scale_linear);
+		return replace(dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::threshold(BYTE T) {
+	if(_dib) {
+		FIBITMAP *dib1 = FreeImage_Threshold(_dib, T);
+		return replace(dib1);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertTo4Bits() {
+	if(_dib) {
+		FIBITMAP *dib4 = FreeImage_ConvertTo4Bits(_dib);
+		return replace(dib4);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertTo8Bits() {
+	if(_dib) {
+		FIBITMAP *dib8 = FreeImage_ConvertTo8Bits(_dib);
+		return replace(dib8);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertTo16Bits555() {
+	if(_dib) {
+		FIBITMAP *dib16_555 = FreeImage_ConvertTo16Bits555(_dib);
+		return replace(dib16_555);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertTo16Bits565() {
+	if(_dib) {
+		FIBITMAP *dib16_565 = FreeImage_ConvertTo16Bits565(_dib);
+		return replace(dib16_565);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertTo24Bits() {
+	if(_dib) {
+		FIBITMAP *dibRGB = FreeImage_ConvertTo24Bits(_dib);
+		return replace(dibRGB);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertTo32Bits() {
+	if(_dib) {
+		FIBITMAP *dib32 = FreeImage_ConvertTo32Bits(_dib);
+		return replace(dib32);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertToGrayscale() {
+	if(_dib) {
+		FIBITMAP *dib8 = FreeImage_ConvertToGreyscale(_dib);
+		return replace(dib8);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::colorQuantize(FREE_IMAGE_QUANTIZE algorithm) {
+	if(_dib) {
+		FIBITMAP *dib8 = FreeImage_ColorQuantize(_dib, algorithm);
+		return replace(dib8);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::dither(FREE_IMAGE_DITHER algorithm) {
+	if(_dib) {
+		FIBITMAP *dib = FreeImage_Dither(_dib, algorithm);
+		return replace(dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertToFloat() {
+	if(_dib) {
+		FIBITMAP *dib = FreeImage_ConvertToFloat(_dib);
+		return replace(dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertToRGBF() {
+	if(_dib) {
+		FIBITMAP *dib = FreeImage_ConvertToRGBF(_dib);
+		return replace(dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertToRGBAF() {
+	if(_dib) {
+		FIBITMAP *dib = FreeImage_ConvertToRGBAF(_dib);
+		return replace(dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertToUINT16() {
+	if(_dib) {
+		FIBITMAP *dib = FreeImage_ConvertToUINT16(_dib);
+		return replace(dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertToRGB16() {
+	if(_dib) {
+		FIBITMAP *dib = FreeImage_ConvertToRGB16(_dib);
+		return replace(dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::convertToRGBA16() {
+	if(_dib) {
+		FIBITMAP *dib = FreeImage_ConvertToRGBA16(_dib);
+		return replace(dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::toneMapping(FREE_IMAGE_TMO tmo, double first_param, double second_param, double third_param, double fourth_param) {
+	if(_dib) {
+		FIBITMAP *dst = NULL;
+		// Apply a tone mapping algorithm and convert to 24-bit 
+		switch(tmo) {
+			case FITMO_REINHARD05:
+				dst = FreeImage_TmoReinhard05Ex(_dib, first_param, second_param, third_param, fourth_param);
+				break;
+			default:
+				dst = FreeImage_ToneMapping(_dib, tmo, first_param, second_param);
+				break;
+		}
+
+		return replace(dst);
+	}
+	return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////   
+// Transparency support: background colour and alpha channel
+
+BOOL fipImage::isTransparent() const {
+	return FreeImage_IsTransparent(_dib);
+}
+
+unsigned fipImage::getTransparencyCount() const {
+	return FreeImage_GetTransparencyCount(_dib);
+}
+
+BYTE* fipImage::getTransparencyTable() const {
+	return FreeImage_GetTransparencyTable(_dib);
+}
+
+void fipImage::setTransparencyTable(BYTE *table, int count) {
+	FreeImage_SetTransparencyTable(_dib, table, count);
+	_bHasChanged = TRUE;
+}
+
+BOOL fipImage::hasFileBkColor() const {
+	return FreeImage_HasBackgroundColor(_dib);
+}
+
+BOOL fipImage::getFileBkColor(RGBQUAD *bkcolor) const {
+	return FreeImage_GetBackgroundColor(_dib, bkcolor);
+}
+
+BOOL fipImage::setFileBkColor(RGBQUAD *bkcolor) {
+	_bHasChanged = TRUE;
+	return FreeImage_SetBackgroundColor(_dib, bkcolor);
+}
+
+///////////////////////////////////////////////////////////////////   
+// Channel processing support
+
+BOOL fipImage::getChannel(fipImage& image, FREE_IMAGE_COLOR_CHANNEL channel) const {
+	if(_dib) {
+		image = FreeImage_GetChannel(_dib, channel);
+		return image.isValid();
+	}
+	return FALSE;
+}
+
+BOOL fipImage::setChannel(fipImage& image, FREE_IMAGE_COLOR_CHANNEL channel) {
+	if(_dib) {
+		_bHasChanged = TRUE;
+		return FreeImage_SetChannel(_dib, image._dib, channel);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::splitChannels(fipImage& RedChannel, fipImage& GreenChannel, fipImage& BlueChannel) {
+	if(_dib) {
+		RedChannel = FreeImage_GetChannel(_dib, FICC_RED);
+		GreenChannel = FreeImage_GetChannel(_dib, FICC_GREEN);
+		BlueChannel = FreeImage_GetChannel(_dib, FICC_BLUE);
+
+		return (RedChannel.isValid() && GreenChannel.isValid() && BlueChannel.isValid());
+	}
+	return FALSE;
+}
+
+BOOL fipImage::combineChannels(fipImage& red, fipImage& green, fipImage& blue) {
+	if(!_dib) {
+		int width = red.getWidth();
+		int height = red.getHeight();
+		_dib = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+	}
+
+	if(_dib) {
+		BOOL bResult = TRUE;
+		bResult &= FreeImage_SetChannel(_dib, red._dib, FICC_RED);
+		bResult &= FreeImage_SetChannel(_dib, green._dib, FICC_GREEN);
+		bResult &= FreeImage_SetChannel(_dib, blue._dib, FICC_BLUE);
+
+		_bHasChanged = TRUE;
+
+		return bResult;
+	}
+	return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////   
+// Rotation and flipping
+
+BOOL fipImage::rotateEx(double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask) {
+	if(_dib) {
+		if(FreeImage_GetBPP(_dib) >= 8) {
+			FIBITMAP *rotated = FreeImage_RotateEx(_dib, angle, x_shift, y_shift, x_origin, y_origin, use_mask);
+			return replace(rotated);
+		}
+	}
+	return FALSE;
+}
+
+BOOL fipImage::rotate(double angle, const void *bkcolor) {
+	if(_dib) {
+		switch(FreeImage_GetImageType(_dib)) {
+			case FIT_BITMAP:
+				switch(FreeImage_GetBPP(_dib)) {
+					case 1:
+					case 8:
+					case 24:
+					case 32:
+						break;
+					default:
+						return FALSE;
+				}
+				break;
+
+			case FIT_UINT16:
+			case FIT_RGB16:
+			case FIT_RGBA16:
+			case FIT_FLOAT:
+			case FIT_RGBF:
+			case FIT_RGBAF:
+				break;
+			default:
+				return FALSE;
+				break;
+		}
+
+		FIBITMAP *rotated = FreeImage_Rotate(_dib, angle, bkcolor);
+		return replace(rotated);
+
+	}
+	return FALSE;
+}
+
+BOOL fipImage::flipVertical() {
+	if(_dib) {
+		_bHasChanged = TRUE;
+
+		return FreeImage_FlipVertical(_dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::flipHorizontal() {
+	if(_dib) {
+		_bHasChanged = TRUE;
+
+		return FreeImage_FlipHorizontal(_dib);
+	}
+	return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////   
+// Color manipulation routines
+
+BOOL fipImage::invert() {
+	if(_dib) {
+		_bHasChanged = TRUE;
+
+		return FreeImage_Invert(_dib);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::adjustCurve(BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel) {
+	if(_dib) {
+		_bHasChanged = TRUE;
+
+		return FreeImage_AdjustCurve(_dib, LUT, channel);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::adjustGamma(double gamma) {
+	if(_dib) {
+		_bHasChanged = TRUE;
+
+		return FreeImage_AdjustGamma(_dib, gamma);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::adjustBrightness(double percentage) {
+	if(_dib) {
+		_bHasChanged = TRUE;
+
+		return FreeImage_AdjustBrightness(_dib, percentage);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::adjustContrast(double percentage) {
+	if(_dib) {
+		_bHasChanged = TRUE;
+
+		return FreeImage_AdjustContrast(_dib, percentage);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::adjustBrightnessContrastGamma(double brightness, double contrast, double gamma) {
+	if(_dib) {
+		_bHasChanged = TRUE;
+
+		return FreeImage_AdjustColors(_dib, brightness, contrast, gamma, FALSE);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::getHistogram(DWORD *histo, FREE_IMAGE_COLOR_CHANNEL channel) const {
+	if(_dib) {
+		return FreeImage_GetHistogram(_dib, histo, channel);
+	}
+	return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////
+// Upsampling / downsampling routine
+
+BOOL fipImage::rescale(unsigned new_width, unsigned new_height, FREE_IMAGE_FILTER filter) {
+	if(_dib) {
+		switch(FreeImage_GetImageType(_dib)) {
+			case FIT_BITMAP:
+			case FIT_UINT16:
+			case FIT_RGB16:
+			case FIT_RGBA16:
+			case FIT_FLOAT:
+			case FIT_RGBF:
+			case FIT_RGBAF:
+				break;
+			default:
+				return FALSE;
+				break;
+		}
+
+		// Perform upsampling / downsampling
+		FIBITMAP *dst = FreeImage_Rescale(_dib, new_width, new_height, filter);
+		return replace(dst);
+	}
+	return FALSE;
+}
+
+BOOL fipImage::makeThumbnail(unsigned max_size, BOOL convert) {
+	if(_dib) {
+		switch(FreeImage_GetImageType(_dib)) {
+			case FIT_BITMAP:
+			case FIT_UINT16:
+			case FIT_RGB16:
+			case FIT_RGBA16:
+			case FIT_FLOAT:
+			case FIT_RGBF:
+			case FIT_RGBAF:
+				break;
+			default:
+				return FALSE;
+				break;
+		}
+
+		// Perform downsampling
+		FIBITMAP *dst = FreeImage_MakeThumbnail(_dib, max_size, convert);
+		return replace(dst);
+	}
+	return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////
+// Metadata
+
+unsigned fipImage::getMetadataCount(FREE_IMAGE_MDMODEL model) const {
+	return FreeImage_GetMetadataCount(model, _dib);
+}
+
+BOOL fipImage::getMetadata(FREE_IMAGE_MDMODEL model, const char *key, fipTag& tag) const {
+	FITAG *searchedTag = NULL;
+	FreeImage_GetMetadata(model, _dib, key, &searchedTag);
+	if(searchedTag != NULL) {
+		tag = FreeImage_CloneTag(searchedTag);
+		return TRUE;
+	} else {
+		// clear the tag
+		tag = (FITAG*)NULL;
+	}
+	return FALSE;
+}
+
+BOOL fipImage::setMetadata(FREE_IMAGE_MDMODEL model, const char *key, fipTag& tag) {
+	return FreeImage_SetMetadata(model, _dib, key, tag);
+}
+
diff --git a/files/Wrapper/FreeImagePlus/src/fipMemoryIO.cpp b/files/Wrapper/FreeImagePlus/src/fipMemoryIO.cpp
new file mode 100644
index 0000000..4a8fe80
--- /dev/null
+++ b/files/Wrapper/FreeImagePlus/src/fipMemoryIO.cpp
@@ -0,0 +1,95 @@
+// ==========================================================
+// fipMemoryIO class implementation
+//
+// 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 "FreeImagePlus.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+fipMemoryIO::fipMemoryIO(BYTE *data, DWORD size_in_bytes) {
+	_hmem = FreeImage_OpenMemory(data, size_in_bytes);
+}
+
+fipMemoryIO::~fipMemoryIO() { 
+	if(_hmem != NULL) {
+		FreeImage_CloseMemory(_hmem);
+	}
+}
+
+void fipMemoryIO::close() { 
+	if(_hmem != NULL) {
+		FreeImage_CloseMemory(_hmem);
+		_hmem = NULL;
+	}
+}
+
+BOOL fipMemoryIO::isValid() const {
+	return (_hmem != NULL);
+}
+
+FREE_IMAGE_FORMAT fipMemoryIO::getFileType() const {
+	if(_hmem != NULL) {
+		return FreeImage_GetFileTypeFromMemory(_hmem, 0);
+	}
+
+	return FIF_UNKNOWN;
+}
+
+FIBITMAP* fipMemoryIO::load(FREE_IMAGE_FORMAT fif, int flags) const {
+	return FreeImage_LoadFromMemory(fif, _hmem, flags);
+}
+
+FIMULTIBITMAP* fipMemoryIO::loadMultiPage(FREE_IMAGE_FORMAT fif, int flags) const {
+	return FreeImage_LoadMultiBitmapFromMemory(fif, _hmem, flags);
+}
+
+BOOL fipMemoryIO::save(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, int flags) {
+	return FreeImage_SaveToMemory(fif, dib, _hmem, flags);
+}
+
+BOOL fipMemoryIO::saveMultiPage(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, int flags) {
+	return FreeImage_SaveMultiBitmapToMemory(fif, bitmap, _hmem, flags);
+}
+
+unsigned fipMemoryIO::read(void *buffer, unsigned size, unsigned count) const {
+	return FreeImage_ReadMemory(buffer, size, count, _hmem);
+}
+
+unsigned fipMemoryIO::write(const void *buffer, unsigned size, unsigned count) {
+	return FreeImage_WriteMemory(buffer, size, count, _hmem);
+}
+
+long fipMemoryIO::tell() const {
+	return FreeImage_TellMemory(_hmem);
+}
+
+BOOL fipMemoryIO::seek(long offset, int origin) {
+	return FreeImage_SeekMemory(_hmem, offset, origin);
+}
+
+BOOL fipMemoryIO::acquire(BYTE **data, DWORD *size_in_bytes) {
+	return FreeImage_AcquireMemory(_hmem, data, size_in_bytes);
+}
+
+
diff --git a/files/Wrapper/FreeImagePlus/src/fipMetadataFind.cpp b/files/Wrapper/FreeImagePlus/src/fipMetadataFind.cpp
new file mode 100644
index 0000000..57d01f6
--- /dev/null
+++ b/files/Wrapper/FreeImagePlus/src/fipMetadataFind.cpp
@@ -0,0 +1,54 @@
+// ==========================================================
+// fipMetadataFind class implementation
+//
+// 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 "FreeImagePlus.h"
+
+BOOL fipMetadataFind::isValid() const {
+	return (_mdhandle != NULL) ? TRUE : FALSE;
+}
+
+fipMetadataFind::fipMetadataFind() : _mdhandle(NULL) {
+}
+
+fipMetadataFind::~fipMetadataFind() {
+	FreeImage_FindCloseMetadata(_mdhandle);
+}
+
+BOOL fipMetadataFind::findFirstMetadata(FREE_IMAGE_MDMODEL model, fipImage& image, fipTag& tag) {
+	FITAG *firstTag = NULL;
+	if(_mdhandle) FreeImage_FindCloseMetadata(_mdhandle);
+	_mdhandle = FreeImage_FindFirstMetadata(model, image, &firstTag);
+	if(_mdhandle) {
+		tag = FreeImage_CloneTag(firstTag);
+		return TRUE;
+	}
+	return FALSE;
+} 
+
+BOOL fipMetadataFind::findNextMetadata(fipTag& tag) {
+	FITAG *nextTag = NULL;
+	if( FreeImage_FindNextMetadata(_mdhandle, &nextTag) ) {
+		tag = FreeImage_CloneTag(nextTag);
+		return TRUE;
+	}
+	return FALSE;
+}
+
diff --git a/files/Wrapper/FreeImagePlus/src/fipMultiPage.cpp b/files/Wrapper/FreeImagePlus/src/fipMultiPage.cpp
new file mode 100644
index 0000000..cc33196
--- /dev/null
+++ b/files/Wrapper/FreeImagePlus/src/fipMultiPage.cpp
@@ -0,0 +1,140 @@
+// ==========================================================
+// fipMultiPage class implementation
+//
+// 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 "FreeImagePlus.h"
+
+fipMultiPage::fipMultiPage(BOOL keep_cache_in_memory) : _mpage(NULL), _bMemoryCache(keep_cache_in_memory) {
+}
+
+fipMultiPage::~fipMultiPage() {
+	if(_mpage) {
+		// close the stream
+		close(0);
+	}
+}
+
+BOOL fipMultiPage::isValid() const {
+	return (NULL != _mpage) ? TRUE : FALSE;
+}
+
+BOOL fipMultiPage::open(const char* lpszPathName, BOOL create_new, BOOL read_only, int flags) {
+	// try to guess the file format from the filename
+	FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(lpszPathName);
+
+	// open the stream
+	_mpage = FreeImage_OpenMultiBitmap(fif, lpszPathName, create_new, read_only, _bMemoryCache, flags);
+
+	return (NULL != _mpage ) ? TRUE : FALSE;
+}
+
+BOOL fipMultiPage::open(fipMemoryIO& memIO, int flags) {
+	// try to guess the file format from the memory handle
+	FREE_IMAGE_FORMAT fif = memIO.getFileType();
+
+	// open the stream
+	_mpage = memIO.loadMultiPage(fif, flags);
+
+	return (NULL != _mpage ) ? TRUE : FALSE;
+}
+
+BOOL fipMultiPage::open(FreeImageIO *io, fi_handle handle, int flags) {
+	// try to guess the file format from the handle
+	FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromHandle(io, handle, 0);
+
+	// open the stream
+	_mpage = FreeImage_OpenMultiBitmapFromHandle(fif, io, handle, flags);
+
+	return (NULL != _mpage ) ? TRUE : FALSE;
+}
+
+BOOL fipMultiPage::close(int flags) {
+	BOOL bSuccess = FALSE;
+	if(_mpage) {
+		// close the stream
+		bSuccess = FreeImage_CloseMultiBitmap(_mpage, flags);
+		_mpage = NULL;
+	}
+
+	return bSuccess;
+}
+
+BOOL fipMultiPage::saveToHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) const {
+	BOOL bSuccess = FALSE;
+	if(_mpage) {
+		bSuccess = FreeImage_SaveMultiBitmapToHandle(fif, _mpage, io, handle, flags);
+	}
+
+	return bSuccess;
+}
+
+BOOL fipMultiPage::saveToMemory(FREE_IMAGE_FORMAT fif, fipMemoryIO& memIO, int flags) const {
+	BOOL bSuccess = FALSE;
+	if(_mpage) {
+		bSuccess = memIO.saveMultiPage(fif, _mpage, flags);
+	}
+
+	return bSuccess;
+}
+
+int fipMultiPage::getPageCount() const {
+	return _mpage ? FreeImage_GetPageCount(_mpage) : 0;
+}
+
+void fipMultiPage::appendPage(fipImage& image) {
+	if(_mpage) {
+		FreeImage_AppendPage(_mpage, image);
+	}
+}
+
+void fipMultiPage::insertPage(int page, fipImage& image) {
+	if(_mpage) {
+		FreeImage_InsertPage(_mpage, page, image);
+	}
+}
+
+void fipMultiPage::deletePage(int page) {
+	if(_mpage) {
+		FreeImage_DeletePage(_mpage, page);
+	}
+}
+
+BOOL fipMultiPage::movePage(int target, int source) {
+	return _mpage ? FreeImage_MovePage(_mpage, target, source) : FALSE;
+}
+
+FIBITMAP* fipMultiPage::lockPage(int page) {
+	return _mpage ? FreeImage_LockPage(_mpage, page) : NULL;
+}
+
+void fipMultiPage::unlockPage(fipImage& image, BOOL changed) {
+	if(_mpage) {
+		FreeImage_UnlockPage(_mpage, image, changed);
+		// clear the image so that it becomes invalid.
+		// this is possible because of the friend declaration
+		image._dib = NULL;
+		image._bHasChanged = FALSE;
+	}
+}
+
+BOOL fipMultiPage::getLockedPageNumbers(int *pages, int *count) const {
+	return _mpage ? FreeImage_GetLockedPageNumbers(_mpage, pages, count) : FALSE;
+}
+
diff --git a/files/Wrapper/FreeImagePlus/src/fipTag.cpp b/files/Wrapper/FreeImagePlus/src/fipTag.cpp
new file mode 100644
index 0000000..b00a095
--- /dev/null
+++ b/files/Wrapper/FreeImagePlus/src/fipTag.cpp
@@ -0,0 +1,134 @@
+// ==========================================================
+// fipTag class implementation
+//
+// 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 <string.h>
+#include "FreeImagePlus.h"
+
+fipTag::fipTag() {
+	_tag = FreeImage_CreateTag();
+}
+
+fipTag::~fipTag() {
+	FreeImage_DeleteTag(_tag);
+}
+
+BOOL fipTag::setKeyValue(const char *key, const char *value) {
+	if(_tag) {
+		FreeImage_DeleteTag(_tag);
+		_tag = NULL;
+	}
+	// create a tag
+	_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);
+		return bSuccess;
+	}
+	return FALSE;
+}
+
+fipTag::fipTag(const fipTag& tag) {
+	_tag = FreeImage_CloneTag(tag._tag);
+}
+
+fipTag& fipTag::operator=(const fipTag& tag) {
+	if(this != &tag) {
+		if(_tag) FreeImage_DeleteTag(_tag);
+		_tag = FreeImage_CloneTag(tag._tag);
+	}
+	return *this;
+}
+
+fipTag& fipTag::operator=(FITAG *tag) {
+	if(_tag) FreeImage_DeleteTag(_tag);
+	_tag = tag;
+	return *this;
+}
+
+BOOL fipTag::isValid() const {
+	return (_tag != NULL) ? TRUE : FALSE;
+}
+
+const char* fipTag::getKey() const {
+	return FreeImage_GetTagKey(_tag);
+}
+
+const char* fipTag::getDescription() const {
+	return FreeImage_GetTagDescription(_tag);
+}
+
+WORD fipTag::getID() const {
+	return FreeImage_GetTagID(_tag);
+}
+
+FREE_IMAGE_MDTYPE fipTag::getType() const {
+	return FreeImage_GetTagType(_tag);
+}
+
+DWORD fipTag::getCount() const {
+	return FreeImage_GetTagCount(_tag);
+}
+
+DWORD fipTag::getLength() const {
+	return FreeImage_GetTagLength(_tag);
+}
+
+const void* fipTag::getValue() const {
+	return FreeImage_GetTagValue(_tag);
+}
+
+BOOL fipTag::setKey(const char *key) {
+	return FreeImage_SetTagKey(_tag, key);
+}
+
+BOOL fipTag::setDescription(const char *description) {
+	return FreeImage_SetTagDescription(_tag, description);
+}
+
+BOOL fipTag::setID(WORD id) {
+	return FreeImage_SetTagID(_tag, id);
+}
+
+BOOL fipTag::setType(FREE_IMAGE_MDTYPE type) {
+	return FreeImage_SetTagType(_tag, type);
+}
+
+BOOL fipTag::setCount(DWORD count) {
+	return FreeImage_SetTagCount(_tag, count);
+}
+
+BOOL fipTag::setLength(DWORD length) {
+	return FreeImage_SetTagLength(_tag, length);
+}
+
+BOOL fipTag::setValue(const void *value) {
+	return FreeImage_SetTagValue(_tag, value);
+}
+
+const char* fipTag::toString(FREE_IMAGE_MDMODEL model, char *Make) const {
+	return FreeImage_TagToString(model, _tag, Make);
+}
diff --git a/files/Wrapper/FreeImagePlus/src/fipWinImage.cpp b/files/Wrapper/FreeImagePlus/src/fipWinImage.cpp
new file mode 100644
index 0000000..092b655
--- /dev/null
+++ b/files/Wrapper/FreeImagePlus/src/fipWinImage.cpp
@@ -0,0 +1,488 @@
+// ==========================================================
+// fipWinImage class implementation
+//
+// 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 "FreeImagePlus.h"
+
+#ifdef _WIN32
+
+// marker used for clipboard copy / paste
+
+static inline void 
+SET_FREEIMAGE_MARKER(BITMAPINFOHEADER *bmih, FIBITMAP *dib) {
+	// Windows constants goes from 0L to 5L
+	// Add 0xFF to avoid conflicts
+	bmih->biCompression = 0xFF + FreeImage_GetImageType(dib);
+}
+
+static inline FREE_IMAGE_TYPE 
+GET_FREEIMAGE_MARKER(BITMAPINFOHEADER *bmih) {
+	return (FREE_IMAGE_TYPE)(bmih->biCompression - 0xFF);
+}
+
+///////////////////////////////////////////////////////////////////
+// Construction / Destruction
+
+fipWinImage::fipWinImage(FREE_IMAGE_TYPE image_type, unsigned width, unsigned height, unsigned bpp) : fipImage(image_type, width, height, bpp) {
+	_display_dib = NULL;
+	_bDeleteMe = FALSE;
+	// default tone mapping operator
+	_tmo = FITMO_DRAGO03;
+	_tmo_param_1 = 0;
+	_tmo_param_2 = 0;
+	_tmo_param_3 = 1;
+	_tmo_param_4 = 0;
+}
+
+fipWinImage::~fipWinImage() { 
+	if(_bDeleteMe) {
+		FreeImage_Unload(_display_dib);
+	}
+}
+
+void fipWinImage::clear() {
+	// delete _display_dib
+	if(_bDeleteMe) {
+		FreeImage_Unload(_display_dib);
+	}
+	_display_dib = NULL;
+	_bDeleteMe = FALSE;
+	// delete base class data
+	fipImage::clear();
+}
+
+BOOL fipWinImage::isValid() const {
+	return fipImage::isValid();
+}
+
+///////////////////////////////////////////////////////////////////
+// Copying
+
+fipWinImage& fipWinImage::operator=(const fipImage& Image) {
+	// delete _display_dib
+	if(_bDeleteMe) {
+		FreeImage_Unload(_display_dib);
+	}
+	_display_dib = NULL;
+	_bDeleteMe = FALSE;
+	// clone the base class
+	fipImage::operator=(Image);
+
+	return *this;
+}
+
+fipWinImage& fipWinImage::operator=(const fipWinImage& Image) {
+	if(this != &Image) {
+		// delete _display_dib
+		if(_bDeleteMe) {
+			FreeImage_Unload(_display_dib);
+		}
+		_display_dib = NULL;
+		_bDeleteMe = FALSE;
+		// copy tmo data
+		_tmo = Image._tmo;
+		_tmo_param_1 = Image._tmo_param_1;
+		_tmo_param_2 = Image._tmo_param_2;
+		_tmo_param_3 = Image._tmo_param_3;
+		_tmo_param_4 = Image._tmo_param_4;
+
+		// clone the base class
+		fipImage::operator=(Image);
+	}
+	return *this;
+}
+
+HANDLE fipWinImage::copyToHandle() const {
+	HANDLE hMem = NULL;
+
+	if(_dib) {
+
+		// Get equivalent DIB size
+		long dib_size = sizeof(BITMAPINFOHEADER);
+		dib_size += FreeImage_GetColorsUsed(_dib) * sizeof(RGBQUAD);
+		dib_size += FreeImage_GetPitch(_dib) * FreeImage_GetHeight(_dib);
+
+		// Allocate a DIB
+		hMem = GlobalAlloc(GHND, dib_size);
+		BYTE *dib = (BYTE*)GlobalLock(hMem);
+
+		memset(dib, 0, dib_size);
+
+		BYTE *p_dib = (BYTE*)dib;
+
+		// Copy the BITMAPINFOHEADER
+
+		BITMAPINFOHEADER *bih = FreeImage_GetInfoHeader(_dib);
+		memcpy(p_dib, bih, sizeof(BITMAPINFOHEADER));
+		if(FreeImage_GetImageType(_dib) != FIT_BITMAP) {
+			// this hack is used to store the bitmap type in the biCompression member of the BITMAPINFOHEADER
+			SET_FREEIMAGE_MARKER((BITMAPINFOHEADER*)p_dib, _dib);
+		}
+		p_dib += sizeof(BITMAPINFOHEADER);
+
+		// Copy the palette
+
+		RGBQUAD *pal = FreeImage_GetPalette(_dib);
+		memcpy(p_dib, pal, FreeImage_GetColorsUsed(_dib) * sizeof(RGBQUAD));
+		p_dib += FreeImage_GetColorsUsed(_dib) * sizeof(RGBQUAD);
+
+		// Copy the bitmap
+
+		BYTE *bits = FreeImage_GetBits(_dib);
+		memcpy(p_dib, bits, FreeImage_GetPitch(_dib) * FreeImage_GetHeight(_dib));
+
+		GlobalUnlock(hMem);
+	}
+
+	return hMem;
+}
+
+BOOL fipWinImage::copyFromHandle(HANDLE hMem) {
+	BYTE *lpVoid = NULL;
+	BITMAPINFOHEADER *pHead = NULL;
+	RGBQUAD *pPalette = NULL;
+	BYTE *bits = NULL;
+	DWORD bitfields[3] = {0, 0, 0};
+
+	// Get a pointer to the bitmap
+	lpVoid = (BYTE *)GlobalLock(hMem);
+
+	// Get a pointer to the bitmap header
+	pHead = (BITMAPINFOHEADER *)lpVoid;
+
+	// Get a pointer to the palette
+	if(pHead->biBitCount < 16)
+		pPalette = (RGBQUAD *)(((BYTE *)pHead) + sizeof(BITMAPINFOHEADER));
+
+	// Get a pointer to the pixels
+	bits = ((BYTE*)pHead + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * pHead->biClrUsed);
+
+	if(pHead->biCompression == BI_BITFIELDS) {
+		// Take into account the color masks that specify the red, green, and blue components (16- and 32-bit)
+		unsigned mask_size = 3 * sizeof(DWORD);
+		memcpy(&bitfields[0], bits, mask_size);
+		bits += mask_size;
+	} 
+
+	if(lpVoid) {
+
+		// Allocate a new FIBITMAP
+
+		FREE_IMAGE_TYPE image_type = FIT_BITMAP;
+		// Use a hack to decide if the clipboard contains non standard bitmaps ...
+		switch(GET_FREEIMAGE_MARKER(pHead)) {
+			case FIT_UINT16:
+			case FIT_INT16:
+			case FIT_UINT32:
+			case FIT_INT32:
+			case FIT_FLOAT:
+			case FIT_DOUBLE:
+			case FIT_COMPLEX:
+			case FIT_RGB16:
+			case FIT_RGBA16:
+			case FIT_RGBF:
+			case FIT_RGBAF:
+				image_type = GET_FREEIMAGE_MARKER(pHead);
+				break;
+		}
+		if(!setSize(image_type, (WORD)pHead->biWidth, (WORD)pHead->biHeight, pHead->biBitCount, bitfields[2], bitfields[1], bitfields[0])) {
+			GlobalUnlock(lpVoid);
+			return FALSE;
+		}
+
+		// Copy the bitmap header
+		memcpy(FreeImage_GetInfoHeader(_dib), pHead, sizeof(BITMAPINFOHEADER));
+
+
+		// Copy the palette
+		memcpy(FreeImage_GetPalette(_dib), pPalette, pHead->biClrUsed * sizeof(RGBQUAD));
+
+		// Copy the bitmap
+		memcpy(FreeImage_GetBits(_dib), bits, FreeImage_GetPitch(_dib) * FreeImage_GetHeight(_dib));
+
+		GlobalUnlock(lpVoid);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL fipWinImage::copyFromBitmap(HBITMAP hbmp) {
+	if(hbmp) { 
+		int Success;
+        BITMAP bm;
+		// Get informations about the bitmap
+        GetObject(hbmp, sizeof(BITMAP), (LPSTR) &bm);
+		// Create the image
+        setSize(FIT_BITMAP, (WORD)bm.bmWidth, (WORD)bm.bmHeight, (WORD)bm.bmBitsPixel);
+
+		// The GetDIBits function clears the biClrUsed and biClrImportant BITMAPINFO members (dont't know why) 
+		// So we save these infos below. This is needed for palettized images only. 
+		int nColors = FreeImage_GetColorsUsed(_dib);
+
+		// Create a device context for the bitmap
+        HDC dc = GetDC(NULL);
+		// Copy the pixels
+		Success = GetDIBits(dc,								// handle to DC
+								hbmp,						// handle to bitmap
+								0,							// first scan line to set
+								FreeImage_GetHeight(_dib),	// number of scan lines to copy
+								FreeImage_GetBits(_dib),	// array for bitmap bits
+								FreeImage_GetInfo(_dib),	// bitmap data buffer
+								DIB_RGB_COLORS				// RGB 
+								);
+		if(Success == 0) {
+			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Error : GetDIBits failed");
+			ReleaseDC(NULL, dc);
+			return FALSE;
+        }
+        ReleaseDC(NULL, dc);
+
+		// restore BITMAPINFO members
+		
+		FreeImage_GetInfoHeader(_dib)->biClrUsed = nColors;
+		FreeImage_GetInfoHeader(_dib)->biClrImportant = nColors;
+
+		return TRUE;
+    }
+
+	return FALSE;
+}
+
+BOOL fipWinImage::copyToClipboard(HWND hWndNewOwner) const {
+	HANDLE hDIB = copyToHandle();
+
+	if(OpenClipboard(hWndNewOwner)) {
+		if(EmptyClipboard()) {
+			if(SetClipboardData(CF_DIB, hDIB) == NULL) {
+				MessageBox(hWndNewOwner, "Unable to set Clipboard data", "FreeImage", MB_ICONERROR);
+				CloseClipboard();
+				return FALSE;
+			}
+		}
+	}
+	CloseClipboard();
+
+	return TRUE;
+}
+
+BOOL fipWinImage::pasteFromClipboard() {
+	if(!IsClipboardFormatAvailable(CF_DIB))
+		return FALSE;
+
+	if(OpenClipboard(NULL)) {
+		HANDLE hDIB = GetClipboardData(CF_DIB);
+		copyFromHandle(hDIB);
+		CloseClipboard();
+		return TRUE;
+	}
+	CloseClipboard();
+
+	return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////
+// Screen capture
+
+BOOL fipWinImage::captureWindow(HWND hWndApplicationWindow, HWND hWndSelectedWindow) {
+	int xScreen, yScreen, xshift, yshift;
+	RECT r;
+
+	// Get window size
+	GetWindowRect(hWndSelectedWindow, &r);
+
+	// Check if the window is out of the screen or maximixed
+	xshift = 0;
+	yshift = 0;
+	xScreen = GetSystemMetrics(SM_CXSCREEN);
+	yScreen = GetSystemMetrics(SM_CYSCREEN);
+	if(r.right > xScreen)
+		   r.right = xScreen;
+	if(r.bottom > yScreen)
+		   r.bottom = yScreen;
+	if(r.left < 0) {
+		   xshift = -r.left;
+		   r.left = 0;
+	}
+	if(r.top < 0){
+		   yshift = -r.top;
+		   r.top = 0;
+	}
+	
+	int width  = r.right  - r.left;
+	int height = r.bottom - r.top;
+
+	if(width <= 0 || height <= 0)
+		return FALSE;
+
+	// Hide the application window. 
+	ShowWindow(hWndApplicationWindow, SW_HIDE); 
+	// Bring the window at the top most level
+	SetWindowPos(hWndSelectedWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+	// Give enough time to refresh the window
+	Sleep(500);
+
+	// Prepare the DCs
+	HDC dstDC = GetDC(NULL);
+    HDC srcDC = GetWindowDC(hWndSelectedWindow); // full window (GetDC(hWndSelectedWindow) = clientarea)
+	HDC memDC = CreateCompatibleDC(dstDC);
+	
+	// Copy the screen to the bitmap
+	HBITMAP bm = CreateCompatibleBitmap(dstDC, width, height);
+	HBITMAP oldbm = (HBITMAP)SelectObject(memDC, bm);
+	BitBlt(memDC, 0, 0, width, height, srcDC, xshift, yshift, SRCCOPY);
+
+	// Redraw the application window. 
+	ShowWindow(hWndApplicationWindow, SW_SHOW); 
+
+	// Restore the position
+	SetWindowPos(hWndSelectedWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+	SetWindowPos(hWndApplicationWindow, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+	
+	// Convert the HBITMAP to a FIBITMAP
+	copyFromBitmap(bm);
+
+	// Free objects
+	DeleteObject(SelectObject(memDC, oldbm));
+	DeleteDC(memDC);
+
+	// Convert 32-bit images to 24-bit
+	if(getBitsPerPixel() == 32) {
+		convertTo24Bits();
+	}
+
+	return TRUE;
+}
+
+
+///////////////////////////////////////////////////////////////////
+// Painting operations
+
+void fipWinImage::drawEx(HDC hDC, RECT& rcDest, BOOL useFileBkg, RGBQUAD *appBkColor, FIBITMAP *bg) const {
+	// Convert to a standard bitmap if needed
+	if(_bHasChanged) {
+		if(_bDeleteMe) {
+			FreeImage_Unload(_display_dib);
+			_display_dib = NULL;
+			_bDeleteMe = FALSE;
+		}
+
+		FREE_IMAGE_TYPE image_type = getImageType();
+		if(image_type == FIT_BITMAP) {
+			BOOL bHasBackground = FreeImage_HasBackgroundColor(_dib);
+			BOOL bIsTransparent = FreeImage_IsTransparent(_dib);
+
+			if(!bIsTransparent && (!bHasBackground || !useFileBkg)) {
+				// Copy pointer
+				_display_dib = _dib;
+			}
+			else {
+				// Create the transparent / alpha blended image
+				_display_dib = FreeImage_Composite(_dib, useFileBkg, appBkColor, bg);
+				if(_display_dib) {
+					// Remember to delete _display_dib
+					_bDeleteMe = TRUE;
+				} else {
+					// Something failed: copy pointers
+					_display_dib = _dib;
+				}
+			}
+		} else {
+			// Convert to a standard dib for display
+
+			if(image_type == FIT_COMPLEX) {
+				// Convert to type FIT_DOUBLE
+				FIBITMAP *dib_double = FreeImage_GetComplexChannel(_dib, FICC_MAG);
+				// Convert to a standard bitmap (linear scaling)
+				_display_dib = FreeImage_ConvertToStandardType(dib_double, TRUE);
+				// Free image of type FIT_DOUBLE
+				FreeImage_Unload(dib_double);
+			} else if((image_type == FIT_RGBF) || (image_type == FIT_RGBAF) || (image_type == FIT_RGB16)) {
+				// Apply a tone mapping algorithm and convert to 24-bit 
+				switch(_tmo) {
+					case FITMO_REINHARD05:
+						_display_dib = FreeImage_TmoReinhard05Ex(_dib, _tmo_param_1, _tmo_param_2, _tmo_param_3, _tmo_param_4);
+						break;
+					default:
+						_display_dib = FreeImage_ToneMapping(_dib, _tmo, _tmo_param_1, _tmo_param_2);
+						break;
+				}
+			} else if(image_type == FIT_RGBA16) {
+				// Convert to 32-bit
+				FIBITMAP *dib32 = FreeImage_ConvertTo32Bits(_dib);
+				if(dib32) {
+					// Create the transparent / alpha blended image
+					_display_dib = FreeImage_Composite(dib32, useFileBkg, appBkColor, bg);
+					FreeImage_Unload(dib32);
+				}
+			} else {
+				// Other cases: convert to a standard bitmap (linear scaling)
+				_display_dib = FreeImage_ConvertToStandardType(_dib, TRUE);
+			}
+			// Remember to delete _display_dib
+			_bDeleteMe = TRUE;
+		}
+
+		_bHasChanged = FALSE;
+	}
+
+	// Draw the dib
+	SetStretchBltMode(hDC, COLORONCOLOR);	
+	StretchDIBits(hDC, rcDest.left, rcDest.top, 
+		rcDest.right-rcDest.left, rcDest.bottom-rcDest.top, 
+		0, 0, FreeImage_GetWidth(_display_dib), FreeImage_GetHeight(_display_dib),
+		FreeImage_GetBits(_display_dib), FreeImage_GetInfo(_display_dib), DIB_RGB_COLORS, SRCCOPY);
+
+}
+
+void fipWinImage::setToneMappingOperator(FREE_IMAGE_TMO tmo, double first_param, double second_param, double third_param, double fourth_param) {
+	// avoid costly operations if possible ...
+	if((_tmo != tmo) || (_tmo_param_1 != first_param) || (_tmo_param_2 != second_param) || (_tmo_param_3 != third_param) || (_tmo_param_4 != fourth_param)) {
+		_tmo = tmo;
+		_tmo_param_1 = first_param;
+		_tmo_param_2 = second_param;
+		_tmo_param_3 = third_param;
+		_tmo_param_4 = fourth_param;
+
+		FREE_IMAGE_TYPE image_type = getImageType();
+		switch(image_type) {
+			case FIT_RGBF:
+			case FIT_RGBAF:
+			case FIT_RGB16:
+			case FIT_RGBA16:
+				_bHasChanged = TRUE;
+				break;
+			default:
+				break;
+		}
+	}
+}
+
+void fipWinImage::getToneMappingOperator(FREE_IMAGE_TMO *tmo, double *first_param, double *second_param, double *third_param, double *fourth_param) const {
+	*tmo = _tmo;
+	*first_param = _tmo_param_1;
+	*second_param = _tmo_param_2;
+	*third_param = _tmo_param_3;
+	*fourth_param = _tmo_param_4;
+}
+
+
+#endif // _WIN32