]> Creatis software - clitk.git/blobdiff - utilities/CxImage/ximamng.cpp
cosmetic
[clitk.git] / utilities / CxImage / ximamng.cpp
index cc86f4e600e1df09faa8fe7881a34c3108647841..865c49003c317035a30b31910504d187c5031531 100644 (file)
-/*\r
- * File:       ximamng.cpp\r
- * Purpose:    Platform Independent MNG Image Class Loader and Writer\r
- * Author:     07/Aug/2001 Davide Pizzolato - www.xdp.it\r
- * CxImage version 6.0.0 02/Feb/2008\r
- */\r
-\r
-#include "ximamng.h"\r
-\r
-#if CXIMAGE_SUPPORT_MNG\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// callbacks for the mng decoder:\r
-////////////////////////////////////////////////////////////////////////////////\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// memory allocation; data must be zeroed\r
-static mng_ptr\r
-mymngalloc( mng_uint32 size )\r
-{\r
-       return (mng_ptr)calloc(1, size);\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// memory deallocation\r
-static void mymngfree(mng_ptr p, mng_uint32 size)\r
-{\r
-       free(p);\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// Stream open/close:\r
-// since the user is responsible for opening and closing the file,\r
-// we leave the default implementation open\r
-static mng_bool mymngopenstream(mng_handle mng)      { return MNG_TRUE; }\r
-static mng_bool mymngopenstreamwrite(mng_handle mng) { return MNG_TRUE; }\r
-static mng_bool mymngclosestream(mng_handle mng)     { return MNG_TRUE; }\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// feed data to the decoder\r
-static mng_bool mymngreadstream(mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 *bytesread)\r
-{\r
-       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
-       // read the requested amount of data from the file\r
-       *bytesread = mymng->file->Read( buffer, sizeof(BYTE), size);\r
-       return MNG_TRUE;\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-static mng_bool mymngwritestream (mng_handle mng, mng_ptr pBuf, mng_uint32 iSize, mng_uint32 *iWritten)\r
-{\r
-       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
-       // write it\r
-       *iWritten = mymng->file->Write (pBuf, 1, iSize);\r
-       return MNG_TRUE;\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// the header's been read. set up the display stuff\r
-static mng_bool mymngprocessheader( mng_handle mng, mng_uint32 width, mng_uint32 height )\r
-{\r
-       // normally the image buffer is allocated here,\r
-       // but in this module we don't know nothing about\r
-       // the final environment.\r
-\r
-       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
-       \r
-       mymng->width  = width;\r
-       mymng->height = height;\r
-       mymng->bpp    = 24;\r
-       mymng->effwdt = ((((width * mymng->bpp) + 31) >> 5) << 2);\r
-\r
-       if (mng->bUseBKGD){\r
-               mymng->nBkgndIndex = 0;\r
-               mymng->nBkgndColor.rgbRed  = mng->iBGred >> 8;\r
-               mymng->nBkgndColor.rgbGreen =mng->iBGgreen >> 8;\r
-               mymng->nBkgndColor.rgbBlue = mng->iBGblue >> 8;\r
-       }\r
-\r
-       mymng->image = (BYTE*)malloc(height * mymng->effwdt);\r
-\r
-       // tell the mng decoder about our bit-depth choice\r
-#if CXIMAGE_SUPPORT_ALPHA\r
-       mng_set_canvasstyle( mng, MNG_CANVAS_RGB8_A8 );\r
-       mymng->alpha = (BYTE*)malloc(height * width);\r
-#else\r
-       mng_set_canvasstyle( mng, MNG_CANVAS_BGR8);\r
-       mymng->alpha = NULL;\r
-#endif\r
-       return MNG_TRUE;\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// return a row pointer for the decoder to fill\r
-static mng_ptr mymnggetcanvasline( mng_handle mng, mng_uint32 line )\r
-{\r
-       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
-       return (mng_ptr)(mymng->image + (mymng->effwdt * (mymng->height - 1 - line)));\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-// return a row pointer for the decoder to fill for alpha channel\r
-static mng_ptr mymnggetalphaline( mng_handle mng, mng_uint32 line )\r
-{\r
-       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
-       return (mng_ptr)(mymng->alpha + (mymng->width * (mymng->height - 1 - line)));\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// timer\r
-static mng_uint32 mymnggetticks(mng_handle mng)\r
-{\r
-#ifdef WIN32\r
-       return (mng_uint32)GetTickCount();\r
-#else\r
-  return 0;\r
-#endif\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// Refresh: actual frame need to be updated (Invalidate)\r
-static mng_bool mymngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h)\r
-{\r
-//     mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
-       return MNG_TRUE;\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// interframe delay callback\r
-static mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)\r
-{\r
-       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
-       mymng->delay = msecs;   // set the timer for when the decoder wants to be woken\r
-       return MNG_TRUE;\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-static mng_bool mymngerror(mng_handle mng, mng_int32 code, mng_int8 severity, mng_chunkid chunktype, mng_uint32 chunkseq, mng_int32 extra1, mng_int32 extra2, mng_pchar text)\r
-{\r
-       return mng_cleanup(&mng); //<Arkadiy Olovyannikov>\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////////////\r
-// CxImage members\r
-////////////////////////////////////////////////////////////////////////////////\r
-CxImageMNG::CxImageMNG(): CxImage(CXIMAGE_FORMAT_MNG)\r
-{\r
-       hmng = NULL;\r
-       memset(&mnginfo,0,sizeof(mngstuff));\r
-       mnginfo.nBkgndIndex = -1;\r
-       mnginfo.speed = 1.0f;\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-CxImageMNG::~CxImageMNG()\r
-{\r
-       // cleanup and return\r
-       if (mnginfo.thread){ //close the animation thread\r
-               mnginfo.animation_enabled=0;\r
-               ResumeThread(mnginfo.thread);\r
-               WaitForSingleObject(mnginfo.thread,500);\r
-               CloseHandle(mnginfo.thread);\r
-       }\r
-       // free objects\r
-       if (mnginfo.image) free(mnginfo.image);\r
-       if (mnginfo.alpha) free(mnginfo.alpha);\r
-       if (hmng) mng_cleanup(&hmng); //be sure it's not needed any more. (active timers ?)\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-void CxImageMNG::SetCallbacks(mng_handle mng)\r
-{\r
-       // set the callbacks\r
-       mng_setcb_errorproc(mng, mymngerror);\r
-       mng_setcb_openstream(mng, mymngopenstream);\r
-       mng_setcb_closestream(mng, mymngclosestream);\r
-       mng_setcb_readdata(mng, mymngreadstream);\r
-       mng_setcb_processheader(mng, mymngprocessheader);\r
-       mng_setcb_getcanvasline(mng, mymnggetcanvasline);\r
-       mng_setcb_refresh(mng, mymngrefresh);\r
-       mng_setcb_gettickcount(mng, mymnggetticks);\r
-       mng_setcb_settimer(mng, mymngsettimer);\r
-       mng_setcb_refresh(mng, mymngrefresh);\r
-       mng_setcb_getalphaline(mng, mymnggetalphaline);\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-// can't use the CxImage implementation because it looses mnginfo\r
-bool CxImageMNG::Load(const TCHAR * imageFileName){\r
-       FILE* hFile;    //file handle to read the image\r
-#ifdef WIN32\r
-       if ((hFile=_tfopen(imageFileName,_T("rb")))==NULL)  return false;       // For UNICODE support\r
-#else\r
-       if ((hFile=fopen(imageFileName,"rb"))==NULL)  return false;\r
-#endif\r
-       bool bOK = Decode(hFile);\r
-       fclose(hFile);\r
-       return bOK;\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-#if CXIMAGE_SUPPORT_DECODE\r
-////////////////////////////////////////////////////////////////////////////////\r
-bool CxImageMNG::Decode(CxFile *hFile)\r
-{\r
-       if (hFile == NULL) return false;\r
-\r
-       cx_try\r
-       {\r
-               // set up the mng decoder for our stream\r
-               hmng = mng_initialize(&mnginfo, mymngalloc, mymngfree, MNG_NULL);\r
-               if (hmng == NULL) cx_throw("could not initialize libmng");                      \r
-\r
-               // set the file we want to play\r
-               mnginfo.file = hFile;\r
-\r
-               // Set the colorprofile, lcms uses this:\r
-               mng_set_srgb(hmng, MNG_TRUE );\r
-               // Set white as background color:\r
-               WORD Red,Green,Blue;\r
-               Red = Green = Blue = (255 << 8) + 255;\r
-               mng_set_bgcolor(hmng, Red, Green, Blue );\r
-               // If PNG Background is available, use it:\r
-               mng_set_usebkgd(hmng, MNG_TRUE );\r
-\r
-               // No need to store chunks:\r
-               mng_set_storechunks(hmng, MNG_FALSE);\r
-               // No need to wait: straight reading\r
-               mng_set_suspensionmode(hmng, MNG_FALSE);\r
-\r
-               SetCallbacks(hmng);\r
-\r
-               mng_datap pData = (mng_datap)hmng;\r
-\r
-               // read in the image\r
-               info.nNumFrames=0;\r
-               int retval=MNG_NOERROR;\r
-\r
-               retval = mng_readdisplay(hmng);\r
-\r
-               if (retval != MNG_NOERROR && retval != MNG_NEEDTIMERWAIT){\r
-                       mng_store_error(hmng,retval,0,0);\r
-                       if (hmng->zErrortext){\r
-                               cx_throw(hmng->zErrortext);\r
-                       } else {\r
-                               cx_throw("Error in MNG file");\r
-                       }\r
-               }\r
-\r
-               if (info.nEscape == -1) {\r
-                       // Return output dimensions only\r
-                       head.biWidth = hmng->iWidth;\r
-                       head.biHeight = hmng->iHeight;\r
-                       info.dwType = CXIMAGE_FORMAT_MNG;\r
-                       return true;\r
-               }\r
-\r
-               // read all\r
-               while(pData->bReading){\r
-                       retval = mng_display_resume(hmng);\r
-                       info.nNumFrames++;\r
-               }\r
-\r
-               // single frame check:\r
-               if (retval != MNG_NEEDTIMERWAIT){\r
-                       info.nNumFrames--;\r
-               } else {\r
-                       mnginfo.animation=1;\r
-               }\r
-\r
-               if (info.nNumFrames<=0) info.nNumFrames=1;\r
-\r
-               if (mnginfo.animation_enabled==0){\r
-                       // select the frame\r
-                       if (info.nFrame>=0 && info.nFrame<info.nNumFrames){\r
-                               for (int n=0;n<info.nFrame;n++) mng_display_resume(hmng);\r
-                       } else cx_throw("Error: frame not present in MNG file");\r
-               }\r
-\r
-               if (mnginfo.nBkgndIndex >= 0){\r
-                       info.nBkgndIndex = mnginfo.nBkgndIndex;\r
-                       info.nBkgndColor.rgbRed = mnginfo.nBkgndColor.rgbRed;\r
-                       info.nBkgndColor.rgbGreen = mnginfo.nBkgndColor.rgbGreen;\r
-                       info.nBkgndColor.rgbBlue = mnginfo.nBkgndColor.rgbBlue;\r
-               }\r
-\r
-               //store the newly created image\r
-               if (Create(mnginfo.width,mnginfo.height,mnginfo.bpp, CXIMAGE_FORMAT_MNG)){\r
-                       memcpy(GetBits(), mnginfo.image, info.dwEffWidth * head.biHeight);\r
-#if CXIMAGE_SUPPORT_ALPHA\r
-                       SwapRGB2BGR();\r
-                       AlphaCreate();\r
-                       if(AlphaIsValid() && mnginfo.alpha){\r
-                               memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);\r
-                       }\r
-#endif\r
-               } else cx_throw("CxImageMNG::Decode cannot create image");\r
-\r
-\r
-       } cx_catch {\r
-               if (strcmp(message,"")) strncpy(info.szLastError,message,255);\r
-               return false;\r
-       }\r
-       return true;\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-#endif //CXIMAGE_SUPPORT_DECODE\r
-////////////////////////////////////////////////////////////////////////////////\r
-#if CXIMAGE_SUPPORT_ENCODE\r
-////////////////////////////////////////////////////////////////////////////////\r
-bool CxImageMNG::Encode(CxFile *hFile)\r
-{\r
-       if (EncodeSafeCheck(hFile)) return false;\r
-\r
-       cx_try\r
-       {\r
-               if (head.biClrUsed != 0) cx_throw("MNG encoder can save only RGB images");\r
-               // set the file we want to play\r
-               mnginfo.file = hFile;\r
-               mnginfo.bpp = head.biBitCount;\r
-               mnginfo.effwdt = info.dwEffWidth;\r
-               mnginfo.height = head.biHeight;\r
-               mnginfo.width =  head.biWidth;\r
-\r
-               mnginfo.image = (BYTE*)malloc(head.biSizeImage);\r
-               if (mnginfo.image == NULL) cx_throw("could not allocate memory for MNG");\r
-               memcpy(mnginfo.image,info.pImage, head.biSizeImage);\r
-\r
-               // set up the mng decoder for our stream\r
-               hmng = mng_initialize(&mnginfo, mymngalloc, mymngfree, MNG_NULL);\r
-               if (hmng == NULL) cx_throw("could not initialize libmng");                      \r
-\r
-               mng_setcb_openstream(hmng, mymngopenstreamwrite );\r
-               mng_setcb_closestream(hmng, mymngclosestream);\r
-               mng_setcb_writedata(hmng, mymngwritestream);\r
-\r
-               // Write File:\r
-               mng_create(hmng);\r
-               // Just a single Frame (save a normal PNG):\r
-               WritePNG(hmng, 0, 1 );\r
-               // Now write file:\r
-               mng_write(hmng);\r
-\r
-       } cx_catch {\r
-               if (strcmp(message,"")) strncpy(info.szLastError,message,255);\r
-               return false;\r
-       }\r
-       return true;\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-// Writes a single PNG datastream\r
-void CxImageMNG::WritePNG( mng_handle hMNG, int Frame, int FrameCount )\r
-{\r
-       mngstuff *mymng = (mngstuff *)mng_get_userdata(hMNG);\r
-       \r
-       int OffsetX=0,OffsetY=0,OffsetW=mymng->width,OffsetH=mymng->height;\r
-\r
-       BYTE *tmpbuffer = new BYTE[ (mymng->effwdt+1) * mymng->height];\r
-       if( tmpbuffer == 0 ) return;\r
-\r
-       // Write DEFI chunk.\r
-       mng_putchunk_defi( hMNG, 0, 0, 0, MNG_TRUE, OffsetX, OffsetY, MNG_FALSE, 0, 0, 0, 0 );\r
-                \r
-       // Write Header:\r
-       mng_putchunk_ihdr(\r
-               hMNG, \r
-               OffsetW, OffsetH, \r
-               MNG_BITDEPTH_8, \r
-               MNG_COLORTYPE_RGB, \r
-               MNG_COMPRESSION_DEFLATE, \r
-               MNG_FILTER_ADAPTIVE, \r
-               MNG_INTERLACE_NONE \r
-       );\r
-\r
-       // transfer data, add Filterbyte:\r
-       for( int Row=0; Row<OffsetH; Row++ ){\r
-               // First Byte in each Scanline is Filterbyte: Currently 0 -> No Filter.\r
-               tmpbuffer[Row*(mymng->effwdt+1)]=0; \r
-               // Copy the scanline: (reverse order)\r
-               memcpy(tmpbuffer+Row*(mymng->effwdt+1)+1, \r
-                       mymng->image+((OffsetH-1-(OffsetY+Row))*(mymng->effwdt))+OffsetX,mymng->effwdt);\r
-               // swap red and blue components\r
-               RGBtoBGR(tmpbuffer+Row*(mymng->effwdt+1)+1,mymng->effwdt);\r
-       } \r
-\r
-       // Compress data with ZLib (Deflate):\r
-       BYTE *dstbuffer = new BYTE[(mymng->effwdt+1)*OffsetH];\r
-       if( dstbuffer == 0 ) return;\r
-       DWORD dstbufferSize=(mymng->effwdt+1)*OffsetH;\r
-\r
-       // Compress data:\r
-       if(Z_OK != compress2((Bytef *)dstbuffer,(ULONG *)&dstbufferSize,(const Bytef*)tmpbuffer,\r
-                                               (ULONG) (mymng->effwdt+1)*OffsetH,9 )) return;\r
-\r
-       // Write Data into MNG File:\r
-       mng_putchunk_idat( hMNG, dstbufferSize, (mng_ptr*)dstbuffer);\r
-       mng_putchunk_iend(hMNG);\r
-\r
-       // Free the stuff:\r
-       delete [] tmpbuffer;\r
-       delete [] dstbuffer;\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-long CxImageMNG::Resume()\r
-{\r
-       if (MNG_NEEDTIMERWAIT == mng_display_resume(hmng)){\r
-               if (info.pImage==NULL){\r
-                       Create(mnginfo.width,mnginfo.height,mnginfo.bpp, CXIMAGE_FORMAT_MNG);\r
-               }\r
-               if (IsValid()){\r
-                       memcpy(GetBits(), mnginfo.image, info.dwEffWidth * head.biHeight);\r
-#if CXIMAGE_SUPPORT_ALPHA\r
-                       SwapRGB2BGR();\r
-                       AlphaCreate();\r
-                       if(AlphaIsValid() && mnginfo.alpha){\r
-                               memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);\r
-                       }\r
-#endif\r
-               }\r
-       } else {\r
-               mnginfo.animation_enabled = 0;\r
-       }\r
-       return mnginfo.animation_enabled;\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-void CxImageMNG::SetSpeed(float speed)\r
-{\r
-       if (speed>10.0) mnginfo.speed = 10.0f;\r
-       else if (speed<0.1) mnginfo.speed = 0.1f;\r
-       else mnginfo.speed=speed;\r
-}\r
-////////////////////////////////////////////////////////////////////////////////\r
-#endif //CXIMAGE_SUPPORT_ENCODE\r
-////////////////////////////////////////////////////////////////////////////////\r
-#endif // CXIMAGE_SUPPORT_MNG\r
+/*
+ * File:       ximamng.cpp
+ * Purpose:    Platform Independent MNG Image Class Loader and Writer
+ * Author:     07/Aug/2001 Davide Pizzolato - www.xdp.it
+ * CxImage version 6.0.0 02/Feb/2008
+ */
+
+#include "ximamng.h"
+
+#if CXIMAGE_SUPPORT_MNG
+
+////////////////////////////////////////////////////////////////////////////////
+// callbacks for the mng decoder:
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// memory allocation; data must be zeroed
+static mng_ptr
+mymngalloc( mng_uint32 size )
+{
+       return (mng_ptr)calloc(1, size);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// memory deallocation
+static void mymngfree(mng_ptr p, mng_uint32 size)
+{
+       free(p);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Stream open/close:
+// since the user is responsible for opening and closing the file,
+// we leave the default implementation open
+static mng_bool mymngopenstream(mng_handle mng)      { return MNG_TRUE; }
+static mng_bool mymngopenstreamwrite(mng_handle mng) { return MNG_TRUE; }
+static mng_bool mymngclosestream(mng_handle mng)     { return MNG_TRUE; }
+
+////////////////////////////////////////////////////////////////////////////////
+// feed data to the decoder
+static mng_bool mymngreadstream(mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 *bytesread)
+{
+       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
+       // read the requested amount of data from the file
+       *bytesread = mymng->file->Read( buffer, sizeof(BYTE), size);
+       return MNG_TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+static mng_bool mymngwritestream (mng_handle mng, mng_ptr pBuf, mng_uint32 iSize, mng_uint32 *iWritten)
+{
+       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
+       // write it
+       *iWritten = mymng->file->Write (pBuf, 1, iSize);
+       return MNG_TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// the header's been read. set up the display stuff
+static mng_bool mymngprocessheader( mng_handle mng, mng_uint32 width, mng_uint32 height )
+{
+       // normally the image buffer is allocated here,
+       // but in this module we don't know nothing about
+       // the final environment.
+
+       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
+       
+       mymng->width  = width;
+       mymng->height = height;
+       mymng->bpp    = 24;
+       mymng->effwdt = ((((width * mymng->bpp) + 31) >> 5) << 2);
+
+       if (mng->bUseBKGD){
+               mymng->nBkgndIndex = 0;
+               mymng->nBkgndColor.rgbRed  = mng->iBGred >> 8;
+               mymng->nBkgndColor.rgbGreen =mng->iBGgreen >> 8;
+               mymng->nBkgndColor.rgbBlue = mng->iBGblue >> 8;
+       }
+
+       mymng->image = (BYTE*)malloc(height * mymng->effwdt);
+
+       // tell the mng decoder about our bit-depth choice
+#if CXIMAGE_SUPPORT_ALPHA
+       mng_set_canvasstyle( mng, MNG_CANVAS_RGB8_A8 );
+       mymng->alpha = (BYTE*)malloc(height * width);
+#else
+       mng_set_canvasstyle( mng, MNG_CANVAS_BGR8);
+       mymng->alpha = NULL;
+#endif
+       return MNG_TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// return a row pointer for the decoder to fill
+static mng_ptr mymnggetcanvasline( mng_handle mng, mng_uint32 line )
+{
+       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
+       return (mng_ptr)(mymng->image + (mymng->effwdt * (mymng->height - 1 - line)));
+}
+////////////////////////////////////////////////////////////////////////////////
+// return a row pointer for the decoder to fill for alpha channel
+static mng_ptr mymnggetalphaline( mng_handle mng, mng_uint32 line )
+{
+       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
+       return (mng_ptr)(mymng->alpha + (mymng->width * (mymng->height - 1 - line)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// timer
+static mng_uint32 mymnggetticks(mng_handle mng)
+{
+#ifdef WIN32
+       return (mng_uint32)GetTickCount();
+#else
+  return 0;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Refresh: actual frame need to be updated (Invalidate)
+static mng_bool mymngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h)
+{
+//     mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
+       return MNG_TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// interframe delay callback
+static mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)
+{
+       mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
+       mymng->delay = msecs;   // set the timer for when the decoder wants to be woken
+       return MNG_TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+static mng_bool mymngerror(mng_handle mng, mng_int32 code, mng_int8 severity, mng_chunkid chunktype, mng_uint32 chunkseq, mng_int32 extra1, mng_int32 extra2, mng_pchar text)
+{
+       return mng_cleanup(&mng); //<Arkadiy Olovyannikov>
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CxImage members
+////////////////////////////////////////////////////////////////////////////////
+CxImageMNG::CxImageMNG(): CxImage(CXIMAGE_FORMAT_MNG)
+{
+       hmng = NULL;
+       memset(&mnginfo,0,sizeof(mngstuff));
+       mnginfo.nBkgndIndex = -1;
+       mnginfo.speed = 1.0f;
+}
+////////////////////////////////////////////////////////////////////////////////
+CxImageMNG::~CxImageMNG()
+{
+       // cleanup and return
+       if (mnginfo.thread){ //close the animation thread
+               mnginfo.animation_enabled=0;
+               ResumeThread(mnginfo.thread);
+               WaitForSingleObject(mnginfo.thread,500);
+               CloseHandle(mnginfo.thread);
+       }
+       // free objects
+       if (mnginfo.image) free(mnginfo.image);
+       if (mnginfo.alpha) free(mnginfo.alpha);
+       if (hmng) mng_cleanup(&hmng); //be sure it's not needed any more. (active timers ?)
+}
+////////////////////////////////////////////////////////////////////////////////
+void CxImageMNG::SetCallbacks(mng_handle mng)
+{
+       // set the callbacks
+       mng_setcb_errorproc(mng, mymngerror);
+       mng_setcb_openstream(mng, mymngopenstream);
+       mng_setcb_closestream(mng, mymngclosestream);
+       mng_setcb_readdata(mng, mymngreadstream);
+       mng_setcb_processheader(mng, mymngprocessheader);
+       mng_setcb_getcanvasline(mng, mymnggetcanvasline);
+       mng_setcb_refresh(mng, mymngrefresh);
+       mng_setcb_gettickcount(mng, mymnggetticks);
+       mng_setcb_settimer(mng, mymngsettimer);
+       mng_setcb_refresh(mng, mymngrefresh);
+       mng_setcb_getalphaline(mng, mymnggetalphaline);
+}
+////////////////////////////////////////////////////////////////////////////////
+// can't use the CxImage implementation because it looses mnginfo
+bool CxImageMNG::Load(const TCHAR * imageFileName){
+       FILE* hFile;    //file handle to read the image
+#ifdef WIN32
+       if ((hFile=_tfopen(imageFileName,_T("rb")))==NULL)  return false;       // For UNICODE support
+#else
+       if ((hFile=fopen(imageFileName,"rb"))==NULL)  return false;
+#endif
+       bool bOK = Decode(hFile);
+       fclose(hFile);
+       return bOK;
+}
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_DECODE
+////////////////////////////////////////////////////////////////////////////////
+bool CxImageMNG::Decode(CxFile *hFile)
+{
+       if (hFile == NULL) return false;
+
+       cx_try
+       {
+               // set up the mng decoder for our stream
+               hmng = mng_initialize(&mnginfo, mymngalloc, mymngfree, MNG_NULL);
+               if (hmng == NULL) cx_throw("could not initialize libmng");                      
+
+               // set the file we want to play
+               mnginfo.file = hFile;
+
+               // Set the colorprofile, lcms uses this:
+               mng_set_srgb(hmng, MNG_TRUE );
+               // Set white as background color:
+               WORD Red,Green,Blue;
+               Red = Green = Blue = (255 << 8) + 255;
+               mng_set_bgcolor(hmng, Red, Green, Blue );
+               // If PNG Background is available, use it:
+               mng_set_usebkgd(hmng, MNG_TRUE );
+
+               // No need to store chunks:
+               mng_set_storechunks(hmng, MNG_FALSE);
+               // No need to wait: straight reading
+               mng_set_suspensionmode(hmng, MNG_FALSE);
+
+               SetCallbacks(hmng);
+
+               mng_datap pData = (mng_datap)hmng;
+
+               // read in the image
+               info.nNumFrames=0;
+               int retval=MNG_NOERROR;
+
+               retval = mng_readdisplay(hmng);
+
+               if (retval != MNG_NOERROR && retval != MNG_NEEDTIMERWAIT){
+                       mng_store_error(hmng,retval,0,0);
+                       if (hmng->zErrortext){
+                               cx_throw(hmng->zErrortext);
+                       } else {
+                               cx_throw("Error in MNG file");
+                       }
+               }
+
+               if (info.nEscape == -1) {
+                       // Return output dimensions only
+                       head.biWidth = hmng->iWidth;
+                       head.biHeight = hmng->iHeight;
+                       info.dwType = CXIMAGE_FORMAT_MNG;
+                       return true;
+               }
+
+               // read all
+               while(pData->bReading){
+                       retval = mng_display_resume(hmng);
+                       info.nNumFrames++;
+               }
+
+               // single frame check:
+               if (retval != MNG_NEEDTIMERWAIT){
+                       info.nNumFrames--;
+               } else {
+                       mnginfo.animation=1;
+               }
+
+               if (info.nNumFrames<=0) info.nNumFrames=1;
+
+               if (mnginfo.animation_enabled==0){
+                       // select the frame
+                       if (info.nFrame>=0 && info.nFrame<info.nNumFrames){
+                               for (int n=0;n<info.nFrame;n++) mng_display_resume(hmng);
+                       } else cx_throw("Error: frame not present in MNG file");
+               }
+
+               if (mnginfo.nBkgndIndex >= 0){
+                       info.nBkgndIndex = mnginfo.nBkgndIndex;
+                       info.nBkgndColor.rgbRed = mnginfo.nBkgndColor.rgbRed;
+                       info.nBkgndColor.rgbGreen = mnginfo.nBkgndColor.rgbGreen;
+                       info.nBkgndColor.rgbBlue = mnginfo.nBkgndColor.rgbBlue;
+               }
+
+               //store the newly created image
+               if (Create(mnginfo.width,mnginfo.height,mnginfo.bpp, CXIMAGE_FORMAT_MNG)){
+                       memcpy(GetBits(), mnginfo.image, info.dwEffWidth * head.biHeight);
+#if CXIMAGE_SUPPORT_ALPHA
+                       SwapRGB2BGR();
+                       AlphaCreate();
+                       if(AlphaIsValid() && mnginfo.alpha){
+                               memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);
+                       }
+#endif
+               } else cx_throw("CxImageMNG::Decode cannot create image");
+
+
+       } cx_catch {
+               if (strcmp(message,"")) strncpy(info.szLastError,message,255);
+               return false;
+       }
+       return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+#endif //CXIMAGE_SUPPORT_DECODE
+////////////////////////////////////////////////////////////////////////////////
+#if CXIMAGE_SUPPORT_ENCODE
+////////////////////////////////////////////////////////////////////////////////
+bool CxImageMNG::Encode(CxFile *hFile)
+{
+       if (EncodeSafeCheck(hFile)) return false;
+
+       cx_try
+       {
+               if (head.biClrUsed != 0) cx_throw("MNG encoder can save only RGB images");
+               // set the file we want to play
+               mnginfo.file = hFile;
+               mnginfo.bpp = head.biBitCount;
+               mnginfo.effwdt = info.dwEffWidth;
+               mnginfo.height = head.biHeight;
+               mnginfo.width =  head.biWidth;
+
+               mnginfo.image = (BYTE*)malloc(head.biSizeImage);
+               if (mnginfo.image == NULL) cx_throw("could not allocate memory for MNG");
+               memcpy(mnginfo.image,info.pImage, head.biSizeImage);
+
+               // set up the mng decoder for our stream
+               hmng = mng_initialize(&mnginfo, mymngalloc, mymngfree, MNG_NULL);
+               if (hmng == NULL) cx_throw("could not initialize libmng");                      
+
+               mng_setcb_openstream(hmng, mymngopenstreamwrite );
+               mng_setcb_closestream(hmng, mymngclosestream);
+               mng_setcb_writedata(hmng, mymngwritestream);
+
+               // Write File:
+               mng_create(hmng);
+               // Just a single Frame (save a normal PNG):
+               WritePNG(hmng, 0, 1 );
+               // Now write file:
+               mng_write(hmng);
+
+       } cx_catch {
+               if (strcmp(message,"")) strncpy(info.szLastError,message,255);
+               return false;
+       }
+       return true;
+}
+////////////////////////////////////////////////////////////////////////////////
+// Writes a single PNG datastream
+void CxImageMNG::WritePNG( mng_handle hMNG, int Frame, int FrameCount )
+{
+       mngstuff *mymng = (mngstuff *)mng_get_userdata(hMNG);
+       
+       int OffsetX=0,OffsetY=0,OffsetW=mymng->width,OffsetH=mymng->height;
+
+       BYTE *tmpbuffer = new BYTE[ (mymng->effwdt+1) * mymng->height];
+       if( tmpbuffer == 0 ) return;
+
+       // Write DEFI chunk.
+       mng_putchunk_defi( hMNG, 0, 0, 0, MNG_TRUE, OffsetX, OffsetY, MNG_FALSE, 0, 0, 0, 0 );
+                
+       // Write Header:
+       mng_putchunk_ihdr(
+               hMNG, 
+               OffsetW, OffsetH, 
+               MNG_BITDEPTH_8, 
+               MNG_COLORTYPE_RGB, 
+               MNG_COMPRESSION_DEFLATE, 
+               MNG_FILTER_ADAPTIVE, 
+               MNG_INTERLACE_NONE 
+       );
+
+       // transfer data, add Filterbyte:
+       for( int Row=0; Row<OffsetH; Row++ ){
+               // First Byte in each Scanline is Filterbyte: Currently 0 -> No Filter.
+               tmpbuffer[Row*(mymng->effwdt+1)]=0; 
+               // Copy the scanline: (reverse order)
+               memcpy(tmpbuffer+Row*(mymng->effwdt+1)+1, 
+                       mymng->image+((OffsetH-1-(OffsetY+Row))*(mymng->effwdt))+OffsetX,mymng->effwdt);
+               // swap red and blue components
+               RGBtoBGR(tmpbuffer+Row*(mymng->effwdt+1)+1,mymng->effwdt);
+       } 
+
+       // Compress data with ZLib (Deflate):
+       BYTE *dstbuffer = new BYTE[(mymng->effwdt+1)*OffsetH];
+       if( dstbuffer == 0 ) return;
+       DWORD dstbufferSize=(mymng->effwdt+1)*OffsetH;
+
+       // Compress data:
+       if(Z_OK != compress2((Bytef *)dstbuffer,(ULONG *)&dstbufferSize,(const Bytef*)tmpbuffer,
+                                               (ULONG) (mymng->effwdt+1)*OffsetH,9 )) return;
+
+       // Write Data into MNG File:
+       mng_putchunk_idat( hMNG, dstbufferSize, (mng_ptr*)dstbuffer);
+       mng_putchunk_iend(hMNG);
+
+       // Free the stuff:
+       delete [] tmpbuffer;
+       delete [] dstbuffer;
+}
+////////////////////////////////////////////////////////////////////////////////
+long CxImageMNG::Resume()
+{
+       if (MNG_NEEDTIMERWAIT == mng_display_resume(hmng)){
+               if (info.pImage==NULL){
+                       Create(mnginfo.width,mnginfo.height,mnginfo.bpp, CXIMAGE_FORMAT_MNG);
+               }
+               if (IsValid()){
+                       memcpy(GetBits(), mnginfo.image, info.dwEffWidth * head.biHeight);
+#if CXIMAGE_SUPPORT_ALPHA
+                       SwapRGB2BGR();
+                       AlphaCreate();
+                       if(AlphaIsValid() && mnginfo.alpha){
+                               memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);
+                       }
+#endif
+               }
+       } else {
+               mnginfo.animation_enabled = 0;
+       }
+       return mnginfo.animation_enabled;
+}
+////////////////////////////////////////////////////////////////////////////////
+void CxImageMNG::SetSpeed(float speed)
+{
+       if (speed>10.0) mnginfo.speed = 10.0f;
+       else if (speed<0.1) mnginfo.speed = 0.1f;
+       else mnginfo.speed=speed;
+}
+////////////////////////////////////////////////////////////////////////////////
+#endif //CXIMAGE_SUPPORT_ENCODE
+////////////////////////////////////////////////////////////////////////////////
+#endif // CXIMAGE_SUPPORT_MNG