]> Creatis software - clitk.git/blob - utilities/CxImage/ximamng.cpp
Merge branch 'master' of git.creatis.insa-lyon.fr:clitk
[clitk.git] / utilities / CxImage / ximamng.cpp
1 /*\r
2  * File:        ximamng.cpp\r
3  * Purpose:     Platform Independent MNG Image Class Loader and Writer\r
4  * Author:      07/Aug/2001 Davide Pizzolato - www.xdp.it\r
5  * CxImage version 6.0.0 02/Feb/2008\r
6  */\r
7 \r
8 #include "ximamng.h"\r
9 \r
10 #if CXIMAGE_SUPPORT_MNG\r
11 \r
12 ////////////////////////////////////////////////////////////////////////////////\r
13 // callbacks for the mng decoder:\r
14 ////////////////////////////////////////////////////////////////////////////////\r
15 \r
16 ////////////////////////////////////////////////////////////////////////////////\r
17 // memory allocation; data must be zeroed\r
18 static mng_ptr\r
19 mymngalloc( mng_uint32 size )\r
20 {\r
21         return (mng_ptr)calloc(1, size);\r
22 }\r
23 \r
24 ////////////////////////////////////////////////////////////////////////////////\r
25 // memory deallocation\r
26 static void mymngfree(mng_ptr p, mng_uint32 size)\r
27 {\r
28         free(p);\r
29 }\r
30 \r
31 ////////////////////////////////////////////////////////////////////////////////\r
32 // Stream open/close:\r
33 // since the user is responsible for opening and closing the file,\r
34 // we leave the default implementation open\r
35 static mng_bool mymngopenstream(mng_handle mng)      { return MNG_TRUE; }\r
36 static mng_bool mymngopenstreamwrite(mng_handle mng) { return MNG_TRUE; }\r
37 static mng_bool mymngclosestream(mng_handle mng)     { return MNG_TRUE; }\r
38 \r
39 ////////////////////////////////////////////////////////////////////////////////\r
40 // feed data to the decoder\r
41 static mng_bool mymngreadstream(mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 *bytesread)\r
42 {\r
43         mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
44         // read the requested amount of data from the file\r
45         *bytesread = mymng->file->Read( buffer, sizeof(BYTE), size);\r
46         return MNG_TRUE;\r
47 }\r
48 \r
49 ////////////////////////////////////////////////////////////////////////////////\r
50 static mng_bool mymngwritestream (mng_handle mng, mng_ptr pBuf, mng_uint32 iSize, mng_uint32 *iWritten)\r
51 {\r
52         mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
53         // write it\r
54         *iWritten = mymng->file->Write (pBuf, 1, iSize);\r
55         return MNG_TRUE;\r
56 }\r
57 \r
58 ////////////////////////////////////////////////////////////////////////////////\r
59 // the header's been read. set up the display stuff\r
60 static mng_bool mymngprocessheader( mng_handle mng, mng_uint32 width, mng_uint32 height )\r
61 {\r
62         // normally the image buffer is allocated here,\r
63         // but in this module we don't know nothing about\r
64         // the final environment.\r
65 \r
66         mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
67         \r
68         mymng->width  = width;\r
69         mymng->height = height;\r
70         mymng->bpp    = 24;\r
71         mymng->effwdt = ((((width * mymng->bpp) + 31) >> 5) << 2);\r
72 \r
73         if (mng->bUseBKGD){\r
74                 mymng->nBkgndIndex = 0;\r
75                 mymng->nBkgndColor.rgbRed  = mng->iBGred >> 8;\r
76                 mymng->nBkgndColor.rgbGreen =mng->iBGgreen >> 8;\r
77                 mymng->nBkgndColor.rgbBlue = mng->iBGblue >> 8;\r
78         }\r
79 \r
80         mymng->image = (BYTE*)malloc(height * mymng->effwdt);\r
81 \r
82         // tell the mng decoder about our bit-depth choice\r
83 #if CXIMAGE_SUPPORT_ALPHA\r
84         mng_set_canvasstyle( mng, MNG_CANVAS_RGB8_A8 );\r
85         mymng->alpha = (BYTE*)malloc(height * width);\r
86 #else\r
87         mng_set_canvasstyle( mng, MNG_CANVAS_BGR8);\r
88         mymng->alpha = NULL;\r
89 #endif\r
90         return MNG_TRUE;\r
91 }\r
92 \r
93 ////////////////////////////////////////////////////////////////////////////////\r
94 // return a row pointer for the decoder to fill\r
95 static mng_ptr mymnggetcanvasline( mng_handle mng, mng_uint32 line )\r
96 {\r
97         mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
98         return (mng_ptr)(mymng->image + (mymng->effwdt * (mymng->height - 1 - line)));\r
99 }\r
100 ////////////////////////////////////////////////////////////////////////////////\r
101 // return a row pointer for the decoder to fill for alpha channel\r
102 static mng_ptr mymnggetalphaline( mng_handle mng, mng_uint32 line )\r
103 {\r
104         mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
105         return (mng_ptr)(mymng->alpha + (mymng->width * (mymng->height - 1 - line)));\r
106 }\r
107 \r
108 ////////////////////////////////////////////////////////////////////////////////\r
109 // timer\r
110 static mng_uint32 mymnggetticks(mng_handle mng)\r
111 {\r
112 #ifdef WIN32\r
113         return (mng_uint32)GetTickCount();\r
114 #else\r
115   return 0;\r
116 #endif\r
117 }\r
118 \r
119 ////////////////////////////////////////////////////////////////////////////////\r
120 // Refresh: actual frame need to be updated (Invalidate)\r
121 static mng_bool mymngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h)\r
122 {\r
123 //      mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
124         return MNG_TRUE;\r
125 }\r
126 \r
127 ////////////////////////////////////////////////////////////////////////////////\r
128 // interframe delay callback\r
129 static mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)\r
130 {\r
131         mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);\r
132         mymng->delay = msecs;   // set the timer for when the decoder wants to be woken\r
133         return MNG_TRUE;\r
134 }\r
135 \r
136 ////////////////////////////////////////////////////////////////////////////////\r
137 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
138 {\r
139         return mng_cleanup(&mng); //<Arkadiy Olovyannikov>\r
140 }\r
141 \r
142 ////////////////////////////////////////////////////////////////////////////////\r
143 // CxImage members\r
144 ////////////////////////////////////////////////////////////////////////////////\r
145 CxImageMNG::CxImageMNG(): CxImage(CXIMAGE_FORMAT_MNG)\r
146 {\r
147         hmng = NULL;\r
148         memset(&mnginfo,0,sizeof(mngstuff));\r
149         mnginfo.nBkgndIndex = -1;\r
150         mnginfo.speed = 1.0f;\r
151 }\r
152 ////////////////////////////////////////////////////////////////////////////////\r
153 CxImageMNG::~CxImageMNG()\r
154 {\r
155         // cleanup and return\r
156         if (mnginfo.thread){ //close the animation thread\r
157                 mnginfo.animation_enabled=0;\r
158                 ResumeThread(mnginfo.thread);\r
159                 WaitForSingleObject(mnginfo.thread,500);\r
160                 CloseHandle(mnginfo.thread);\r
161         }\r
162         // free objects\r
163         if (mnginfo.image) free(mnginfo.image);\r
164         if (mnginfo.alpha) free(mnginfo.alpha);\r
165         if (hmng) mng_cleanup(&hmng); //be sure it's not needed any more. (active timers ?)\r
166 }\r
167 ////////////////////////////////////////////////////////////////////////////////\r
168 void CxImageMNG::SetCallbacks(mng_handle mng)\r
169 {\r
170         // set the callbacks\r
171         mng_setcb_errorproc(mng, mymngerror);\r
172         mng_setcb_openstream(mng, mymngopenstream);\r
173         mng_setcb_closestream(mng, mymngclosestream);\r
174         mng_setcb_readdata(mng, mymngreadstream);\r
175         mng_setcb_processheader(mng, mymngprocessheader);\r
176         mng_setcb_getcanvasline(mng, mymnggetcanvasline);\r
177         mng_setcb_refresh(mng, mymngrefresh);\r
178         mng_setcb_gettickcount(mng, mymnggetticks);\r
179         mng_setcb_settimer(mng, mymngsettimer);\r
180         mng_setcb_refresh(mng, mymngrefresh);\r
181         mng_setcb_getalphaline(mng, mymnggetalphaline);\r
182 }\r
183 ////////////////////////////////////////////////////////////////////////////////\r
184 // can't use the CxImage implementation because it looses mnginfo\r
185 bool CxImageMNG::Load(const TCHAR * imageFileName){\r
186         FILE* hFile;    //file handle to read the image\r
187 #ifdef WIN32\r
188         if ((hFile=_tfopen(imageFileName,_T("rb")))==NULL)  return false;       // For UNICODE support\r
189 #else\r
190         if ((hFile=fopen(imageFileName,"rb"))==NULL)  return false;\r
191 #endif\r
192         bool bOK = Decode(hFile);\r
193         fclose(hFile);\r
194         return bOK;\r
195 }\r
196 ////////////////////////////////////////////////////////////////////////////////\r
197 #if CXIMAGE_SUPPORT_DECODE\r
198 ////////////////////////////////////////////////////////////////////////////////\r
199 bool CxImageMNG::Decode(CxFile *hFile)\r
200 {\r
201         if (hFile == NULL) return false;\r
202 \r
203         cx_try\r
204         {\r
205                 // set up the mng decoder for our stream\r
206                 hmng = mng_initialize(&mnginfo, mymngalloc, mymngfree, MNG_NULL);\r
207                 if (hmng == NULL) cx_throw("could not initialize libmng");                      \r
208 \r
209                 // set the file we want to play\r
210                 mnginfo.file = hFile;\r
211 \r
212                 // Set the colorprofile, lcms uses this:\r
213                 mng_set_srgb(hmng, MNG_TRUE );\r
214                 // Set white as background color:\r
215                 WORD Red,Green,Blue;\r
216                 Red = Green = Blue = (255 << 8) + 255;\r
217                 mng_set_bgcolor(hmng, Red, Green, Blue );\r
218                 // If PNG Background is available, use it:\r
219                 mng_set_usebkgd(hmng, MNG_TRUE );\r
220 \r
221                 // No need to store chunks:\r
222                 mng_set_storechunks(hmng, MNG_FALSE);\r
223                 // No need to wait: straight reading\r
224                 mng_set_suspensionmode(hmng, MNG_FALSE);\r
225 \r
226                 SetCallbacks(hmng);\r
227 \r
228                 mng_datap pData = (mng_datap)hmng;\r
229 \r
230                 // read in the image\r
231                 info.nNumFrames=0;\r
232                 int retval=MNG_NOERROR;\r
233 \r
234                 retval = mng_readdisplay(hmng);\r
235 \r
236                 if (retval != MNG_NOERROR && retval != MNG_NEEDTIMERWAIT){\r
237                         mng_store_error(hmng,retval,0,0);\r
238                         if (hmng->zErrortext){\r
239                                 cx_throw(hmng->zErrortext);\r
240                         } else {\r
241                                 cx_throw("Error in MNG file");\r
242                         }\r
243                 }\r
244 \r
245                 if (info.nEscape == -1) {\r
246                         // Return output dimensions only\r
247                         head.biWidth = hmng->iWidth;\r
248                         head.biHeight = hmng->iHeight;\r
249                         info.dwType = CXIMAGE_FORMAT_MNG;\r
250                         return true;\r
251                 }\r
252 \r
253                 // read all\r
254                 while(pData->bReading){\r
255                         retval = mng_display_resume(hmng);\r
256                         info.nNumFrames++;\r
257                 }\r
258 \r
259                 // single frame check:\r
260                 if (retval != MNG_NEEDTIMERWAIT){\r
261                         info.nNumFrames--;\r
262                 } else {\r
263                         mnginfo.animation=1;\r
264                 }\r
265 \r
266                 if (info.nNumFrames<=0) info.nNumFrames=1;\r
267 \r
268                 if (mnginfo.animation_enabled==0){\r
269                         // select the frame\r
270                         if (info.nFrame>=0 && info.nFrame<info.nNumFrames){\r
271                                 for (int n=0;n<info.nFrame;n++) mng_display_resume(hmng);\r
272                         } else cx_throw("Error: frame not present in MNG file");\r
273                 }\r
274 \r
275                 if (mnginfo.nBkgndIndex >= 0){\r
276                         info.nBkgndIndex = mnginfo.nBkgndIndex;\r
277                         info.nBkgndColor.rgbRed = mnginfo.nBkgndColor.rgbRed;\r
278                         info.nBkgndColor.rgbGreen = mnginfo.nBkgndColor.rgbGreen;\r
279                         info.nBkgndColor.rgbBlue = mnginfo.nBkgndColor.rgbBlue;\r
280                 }\r
281 \r
282                 //store the newly created image\r
283                 if (Create(mnginfo.width,mnginfo.height,mnginfo.bpp, CXIMAGE_FORMAT_MNG)){\r
284                         memcpy(GetBits(), mnginfo.image, info.dwEffWidth * head.biHeight);\r
285 #if CXIMAGE_SUPPORT_ALPHA\r
286                         SwapRGB2BGR();\r
287                         AlphaCreate();\r
288                         if(AlphaIsValid() && mnginfo.alpha){\r
289                                 memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);\r
290                         }\r
291 #endif\r
292                 } else cx_throw("CxImageMNG::Decode cannot create image");\r
293 \r
294 \r
295         } cx_catch {\r
296                 if (strcmp(message,"")) strncpy(info.szLastError,message,255);\r
297                 return false;\r
298         }\r
299         return true;\r
300 }\r
301 ////////////////////////////////////////////////////////////////////////////////\r
302 #endif //CXIMAGE_SUPPORT_DECODE\r
303 ////////////////////////////////////////////////////////////////////////////////\r
304 #if CXIMAGE_SUPPORT_ENCODE\r
305 ////////////////////////////////////////////////////////////////////////////////\r
306 bool CxImageMNG::Encode(CxFile *hFile)\r
307 {\r
308         if (EncodeSafeCheck(hFile)) return false;\r
309 \r
310         cx_try\r
311         {\r
312                 if (head.biClrUsed != 0) cx_throw("MNG encoder can save only RGB images");\r
313                 // set the file we want to play\r
314                 mnginfo.file = hFile;\r
315                 mnginfo.bpp = head.biBitCount;\r
316                 mnginfo.effwdt = info.dwEffWidth;\r
317                 mnginfo.height = head.biHeight;\r
318                 mnginfo.width =  head.biWidth;\r
319 \r
320                 mnginfo.image = (BYTE*)malloc(head.biSizeImage);\r
321                 if (mnginfo.image == NULL) cx_throw("could not allocate memory for MNG");\r
322                 memcpy(mnginfo.image,info.pImage, head.biSizeImage);\r
323 \r
324                 // set up the mng decoder for our stream\r
325                 hmng = mng_initialize(&mnginfo, mymngalloc, mymngfree, MNG_NULL);\r
326                 if (hmng == NULL) cx_throw("could not initialize libmng");                      \r
327 \r
328                 mng_setcb_openstream(hmng, mymngopenstreamwrite );\r
329                 mng_setcb_closestream(hmng, mymngclosestream);\r
330                 mng_setcb_writedata(hmng, mymngwritestream);\r
331 \r
332                 // Write File:\r
333                 mng_create(hmng);\r
334                 // Just a single Frame (save a normal PNG):\r
335                 WritePNG(hmng, 0, 1 );\r
336                 // Now write file:\r
337                 mng_write(hmng);\r
338 \r
339         } cx_catch {\r
340                 if (strcmp(message,"")) strncpy(info.szLastError,message,255);\r
341                 return false;\r
342         }\r
343         return true;\r
344 }\r
345 ////////////////////////////////////////////////////////////////////////////////\r
346 // Writes a single PNG datastream\r
347 void CxImageMNG::WritePNG( mng_handle hMNG, int Frame, int FrameCount )\r
348 {\r
349         mngstuff *mymng = (mngstuff *)mng_get_userdata(hMNG);\r
350         \r
351         int OffsetX=0,OffsetY=0,OffsetW=mymng->width,OffsetH=mymng->height;\r
352 \r
353         BYTE *tmpbuffer = new BYTE[ (mymng->effwdt+1) * mymng->height];\r
354         if( tmpbuffer == 0 ) return;\r
355 \r
356         // Write DEFI chunk.\r
357         mng_putchunk_defi( hMNG, 0, 0, 0, MNG_TRUE, OffsetX, OffsetY, MNG_FALSE, 0, 0, 0, 0 );\r
358                  \r
359         // Write Header:\r
360         mng_putchunk_ihdr(\r
361                 hMNG, \r
362                 OffsetW, OffsetH, \r
363                 MNG_BITDEPTH_8, \r
364                 MNG_COLORTYPE_RGB, \r
365                 MNG_COMPRESSION_DEFLATE, \r
366                 MNG_FILTER_ADAPTIVE, \r
367                 MNG_INTERLACE_NONE \r
368         );\r
369 \r
370         // transfer data, add Filterbyte:\r
371         for( int Row=0; Row<OffsetH; Row++ ){\r
372                 // First Byte in each Scanline is Filterbyte: Currently 0 -> No Filter.\r
373                 tmpbuffer[Row*(mymng->effwdt+1)]=0; \r
374                 // Copy the scanline: (reverse order)\r
375                 memcpy(tmpbuffer+Row*(mymng->effwdt+1)+1, \r
376                         mymng->image+((OffsetH-1-(OffsetY+Row))*(mymng->effwdt))+OffsetX,mymng->effwdt);\r
377                 // swap red and blue components\r
378                 RGBtoBGR(tmpbuffer+Row*(mymng->effwdt+1)+1,mymng->effwdt);\r
379         } \r
380 \r
381         // Compress data with ZLib (Deflate):\r
382         BYTE *dstbuffer = new BYTE[(mymng->effwdt+1)*OffsetH];\r
383         if( dstbuffer == 0 ) return;\r
384         DWORD dstbufferSize=(mymng->effwdt+1)*OffsetH;\r
385 \r
386         // Compress data:\r
387         if(Z_OK != compress2((Bytef *)dstbuffer,(ULONG *)&dstbufferSize,(const Bytef*)tmpbuffer,\r
388                                                 (ULONG) (mymng->effwdt+1)*OffsetH,9 )) return;\r
389 \r
390         // Write Data into MNG File:\r
391         mng_putchunk_idat( hMNG, dstbufferSize, (mng_ptr*)dstbuffer);\r
392         mng_putchunk_iend(hMNG);\r
393 \r
394         // Free the stuff:\r
395         delete [] tmpbuffer;\r
396         delete [] dstbuffer;\r
397 }\r
398 ////////////////////////////////////////////////////////////////////////////////\r
399 long CxImageMNG::Resume()\r
400 {\r
401         if (MNG_NEEDTIMERWAIT == mng_display_resume(hmng)){\r
402                 if (info.pImage==NULL){\r
403                         Create(mnginfo.width,mnginfo.height,mnginfo.bpp, CXIMAGE_FORMAT_MNG);\r
404                 }\r
405                 if (IsValid()){\r
406                         memcpy(GetBits(), mnginfo.image, info.dwEffWidth * head.biHeight);\r
407 #if CXIMAGE_SUPPORT_ALPHA\r
408                         SwapRGB2BGR();\r
409                         AlphaCreate();\r
410                         if(AlphaIsValid() && mnginfo.alpha){\r
411                                 memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);\r
412                         }\r
413 #endif\r
414                 }\r
415         } else {\r
416                 mnginfo.animation_enabled = 0;\r
417         }\r
418         return mnginfo.animation_enabled;\r
419 }\r
420 ////////////////////////////////////////////////////////////////////////////////\r
421 void CxImageMNG::SetSpeed(float speed)\r
422 {\r
423         if (speed>10.0) mnginfo.speed = 10.0f;\r
424         else if (speed<0.1) mnginfo.speed = 0.1f;\r
425         else mnginfo.speed=speed;\r
426 }\r
427 ////////////////////////////////////////////////////////////////////////////////\r
428 #endif //CXIMAGE_SUPPORT_ENCODE\r
429 ////////////////////////////////////////////////////////////////////////////////\r
430 #endif // CXIMAGE_SUPPORT_MNG\r