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
10 #if CXIMAGE_SUPPORT_MNG
12 ////////////////////////////////////////////////////////////////////////////////
13 // callbacks for the mng decoder:
14 ////////////////////////////////////////////////////////////////////////////////
16 ////////////////////////////////////////////////////////////////////////////////
17 // memory allocation; data must be zeroed
19 mymngalloc( mng_uint32 size )
21 return (mng_ptr)calloc(1, size);
24 ////////////////////////////////////////////////////////////////////////////////
25 // memory deallocation
26 static void mymngfree(mng_ptr p, mng_uint32 size)
31 ////////////////////////////////////////////////////////////////////////////////
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; }
39 ////////////////////////////////////////////////////////////////////////////////
40 // feed data to the decoder
41 static mng_bool mymngreadstream(mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 *bytesread)
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);
49 ////////////////////////////////////////////////////////////////////////////////
50 static mng_bool mymngwritestream (mng_handle mng, mng_ptr pBuf, mng_uint32 iSize, mng_uint32 *iWritten)
52 mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
54 *iWritten = mymng->file->Write (pBuf, 1, iSize);
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 )
62 // normally the image buffer is allocated here,
63 // but in this module we don't know nothing about
64 // the final environment.
66 mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
69 mymng->height = height;
71 mymng->effwdt = ((((width * mymng->bpp) + 31) >> 5) << 2);
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;
80 mymng->image = (BYTE*)malloc(height * mymng->effwdt);
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);
87 mng_set_canvasstyle( mng, MNG_CANVAS_BGR8);
93 ////////////////////////////////////////////////////////////////////////////////
94 // return a row pointer for the decoder to fill
95 static mng_ptr mymnggetcanvasline( mng_handle mng, mng_uint32 line )
97 mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
98 return (mng_ptr)(mymng->image + (mymng->effwdt * (mymng->height - 1 - line)));
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 )
104 mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
105 return (mng_ptr)(mymng->alpha + (mymng->width * (mymng->height - 1 - line)));
108 ////////////////////////////////////////////////////////////////////////////////
110 static mng_uint32 mymnggetticks(mng_handle mng)
113 return (mng_uint32)GetTickCount();
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)
123 // mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
127 ////////////////////////////////////////////////////////////////////////////////
128 // interframe delay callback
129 static mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)
131 mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
132 mymng->delay = msecs; // set the timer for when the decoder wants to be woken
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)
139 return mng_cleanup(&mng); //<Arkadiy Olovyannikov>
142 ////////////////////////////////////////////////////////////////////////////////
144 ////////////////////////////////////////////////////////////////////////////////
145 CxImageMNG::CxImageMNG(): CxImage(CXIMAGE_FORMAT_MNG)
148 memset(&mnginfo,0,sizeof(mngstuff));
149 mnginfo.nBkgndIndex = -1;
150 mnginfo.speed = 1.0f;
152 ////////////////////////////////////////////////////////////////////////////////
153 CxImageMNG::~CxImageMNG()
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);
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 ?)
167 ////////////////////////////////////////////////////////////////////////////////
168 void CxImageMNG::SetCallbacks(mng_handle mng)
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);
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
188 if ((hFile=_tfopen(imageFileName,_T("rb")))==NULL) return false; // For UNICODE support
190 if ((hFile=fopen(imageFileName,"rb"))==NULL) return false;
192 bool bOK = Decode(hFile);
196 ////////////////////////////////////////////////////////////////////////////////
197 #if CXIMAGE_SUPPORT_DECODE
198 ////////////////////////////////////////////////////////////////////////////////
199 bool CxImageMNG::Decode(CxFile *hFile)
201 if (hFile == NULL) return false;
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");
209 // set the file we want to play
210 mnginfo.file = hFile;
212 // Set the colorprofile, lcms uses this:
213 mng_set_srgb(hmng, MNG_TRUE );
214 // Set white as background color:
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 );
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);
228 mng_datap pData = (mng_datap)hmng;
232 int retval=MNG_NOERROR;
234 retval = mng_readdisplay(hmng);
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);
241 cx_throw("Error in MNG file");
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;
254 while(pData->bReading){
255 retval = mng_display_resume(hmng);
259 // single frame check:
260 if (retval != MNG_NEEDTIMERWAIT){
266 if (info.nNumFrames<=0) info.nNumFrames=1;
268 if (mnginfo.animation_enabled==0){
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");
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;
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
288 if(AlphaIsValid() && mnginfo.alpha){
289 memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);
292 } else cx_throw("CxImageMNG::Decode cannot create image");
296 if (strcmp(message,"")) strncpy(info.szLastError,message,255);
301 ////////////////////////////////////////////////////////////////////////////////
302 #endif //CXIMAGE_SUPPORT_DECODE
303 ////////////////////////////////////////////////////////////////////////////////
304 #if CXIMAGE_SUPPORT_ENCODE
305 ////////////////////////////////////////////////////////////////////////////////
306 bool CxImageMNG::Encode(CxFile *hFile)
308 if (EncodeSafeCheck(hFile)) return false;
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;
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);
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");
328 mng_setcb_openstream(hmng, mymngopenstreamwrite );
329 mng_setcb_closestream(hmng, mymngclosestream);
330 mng_setcb_writedata(hmng, mymngwritestream);
334 // Just a single Frame (save a normal PNG):
335 WritePNG(hmng, 0, 1 );
340 if (strcmp(message,"")) strncpy(info.szLastError,message,255);
345 ////////////////////////////////////////////////////////////////////////////////
346 // Writes a single PNG datastream
347 void CxImageMNG::WritePNG( mng_handle hMNG, int Frame, int FrameCount )
349 mngstuff *mymng = (mngstuff *)mng_get_userdata(hMNG);
351 int OffsetX=0,OffsetY=0,OffsetW=mymng->width,OffsetH=mymng->height;
353 BYTE *tmpbuffer = new BYTE[ (mymng->effwdt+1) * mymng->height];
354 if( tmpbuffer == 0 ) return;
357 mng_putchunk_defi( hMNG, 0, 0, 0, MNG_TRUE, OffsetX, OffsetY, MNG_FALSE, 0, 0, 0, 0 );
365 MNG_COMPRESSION_DEFLATE,
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);
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;
387 if(Z_OK != compress2((Bytef *)dstbuffer,(ULONG *)&dstbufferSize,(const Bytef*)tmpbuffer,
388 (ULONG) (mymng->effwdt+1)*OffsetH,9 )) return;
390 // Write Data into MNG File:
391 mng_putchunk_idat( hMNG, dstbufferSize, (mng_ptr*)dstbuffer);
392 mng_putchunk_iend(hMNG);
398 ////////////////////////////////////////////////////////////////////////////////
399 long CxImageMNG::Resume()
401 if (MNG_NEEDTIMERWAIT == mng_display_resume(hmng)){
402 if (info.pImage==NULL){
403 Create(mnginfo.width,mnginfo.height,mnginfo.bpp, CXIMAGE_FORMAT_MNG);
406 memcpy(GetBits(), mnginfo.image, info.dwEffWidth * head.biHeight);
407 #if CXIMAGE_SUPPORT_ALPHA
410 if(AlphaIsValid() && mnginfo.alpha){
411 memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);
416 mnginfo.animation_enabled = 0;
418 return mnginfo.animation_enabled;
420 ////////////////////////////////////////////////////////////////////////////////
421 void CxImageMNG::SetSpeed(float speed)
423 if (speed>10.0) mnginfo.speed = 10.0f;
424 else if (speed<0.1) mnginfo.speed = 0.1f;
425 else mnginfo.speed=speed;
427 ////////////////////////////////////////////////////////////////////////////////
428 #endif //CXIMAGE_SUPPORT_ENCODE
429 ////////////////////////////////////////////////////////////////////////////////
430 #endif // CXIMAGE_SUPPORT_MNG