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