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
10 #if CXIMAGE_SUPPORT_MNG
\r
12 ////////////////////////////////////////////////////////////////////////////////
\r
13 // callbacks for the mng decoder:
\r
14 ////////////////////////////////////////////////////////////////////////////////
\r
16 ////////////////////////////////////////////////////////////////////////////////
\r
17 // memory allocation; data must be zeroed
\r
19 mymngalloc( mng_uint32 size )
\r
21 return (mng_ptr)calloc(1, size);
\r
24 ////////////////////////////////////////////////////////////////////////////////
\r
25 // memory deallocation
\r
26 static void mymngfree(mng_ptr p, mng_uint32 size)
\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
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
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
49 ////////////////////////////////////////////////////////////////////////////////
\r
50 static mng_bool mymngwritestream (mng_handle mng, mng_ptr pBuf, mng_uint32 iSize, mng_uint32 *iWritten)
\r
52 mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
\r
54 *iWritten = mymng->file->Write (pBuf, 1, iSize);
\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
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
66 mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
\r
68 mymng->width = width;
\r
69 mymng->height = height;
\r
71 mymng->effwdt = ((((width * mymng->bpp) + 31) >> 5) << 2);
\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
80 mymng->image = (BYTE*)malloc(height * mymng->effwdt);
\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
87 mng_set_canvasstyle( mng, MNG_CANVAS_BGR8);
\r
88 mymng->alpha = NULL;
\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
97 mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
\r
98 return (mng_ptr)(mymng->image + (mymng->effwdt * (mymng->height - 1 - line)));
\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
104 mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
\r
105 return (mng_ptr)(mymng->alpha + (mymng->width * (mymng->height - 1 - line)));
\r
108 ////////////////////////////////////////////////////////////////////////////////
\r
110 static mng_uint32 mymnggetticks(mng_handle mng)
\r
113 return (mng_uint32)GetTickCount();
\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
123 // mngstuff *mymng = (mngstuff *)mng_get_userdata(mng);
\r
127 ////////////////////////////////////////////////////////////////////////////////
\r
128 // interframe delay callback
\r
129 static mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)
\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
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
139 return mng_cleanup(&mng); //<Arkadiy Olovyannikov>
\r
142 ////////////////////////////////////////////////////////////////////////////////
\r
144 ////////////////////////////////////////////////////////////////////////////////
\r
145 CxImageMNG::CxImageMNG(): CxImage(CXIMAGE_FORMAT_MNG)
\r
148 memset(&mnginfo,0,sizeof(mngstuff));
\r
149 mnginfo.nBkgndIndex = -1;
\r
150 mnginfo.speed = 1.0f;
\r
152 ////////////////////////////////////////////////////////////////////////////////
\r
153 CxImageMNG::~CxImageMNG()
\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
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
167 ////////////////////////////////////////////////////////////////////////////////
\r
168 void CxImageMNG::SetCallbacks(mng_handle mng)
\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
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
188 if ((hFile=_tfopen(imageFileName,_T("rb")))==NULL) return false; // For UNICODE support
\r
190 if ((hFile=fopen(imageFileName,"rb"))==NULL) return false;
\r
192 bool bOK = Decode(hFile);
\r
196 ////////////////////////////////////////////////////////////////////////////////
\r
197 #if CXIMAGE_SUPPORT_DECODE
\r
198 ////////////////////////////////////////////////////////////////////////////////
\r
199 bool CxImageMNG::Decode(CxFile *hFile)
\r
201 if (hFile == NULL) return false;
\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
209 // set the file we want to play
\r
210 mnginfo.file = hFile;
\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
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
226 SetCallbacks(hmng);
\r
228 mng_datap pData = (mng_datap)hmng;
\r
230 // read in the image
\r
232 int retval=MNG_NOERROR;
\r
234 retval = mng_readdisplay(hmng);
\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
241 cx_throw("Error in MNG file");
\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
254 while(pData->bReading){
\r
255 retval = mng_display_resume(hmng);
\r
259 // single frame check:
\r
260 if (retval != MNG_NEEDTIMERWAIT){
\r
263 mnginfo.animation=1;
\r
266 if (info.nNumFrames<=0) info.nNumFrames=1;
\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
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
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
288 if(AlphaIsValid() && mnginfo.alpha){
\r
289 memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);
\r
292 } else cx_throw("CxImageMNG::Decode cannot create image");
\r
296 if (strcmp(message,"")) strncpy(info.szLastError,message,255);
\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
308 if (EncodeSafeCheck(hFile)) return false;
\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
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
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
328 mng_setcb_openstream(hmng, mymngopenstreamwrite );
\r
329 mng_setcb_closestream(hmng, mymngclosestream);
\r
330 mng_setcb_writedata(hmng, mymngwritestream);
\r
334 // Just a single Frame (save a normal PNG):
\r
335 WritePNG(hmng, 0, 1 );
\r
340 if (strcmp(message,"")) strncpy(info.szLastError,message,255);
\r
345 ////////////////////////////////////////////////////////////////////////////////
\r
346 // Writes a single PNG datastream
\r
347 void CxImageMNG::WritePNG( mng_handle hMNG, int Frame, int FrameCount )
\r
349 mngstuff *mymng = (mngstuff *)mng_get_userdata(hMNG);
\r
351 int OffsetX=0,OffsetY=0,OffsetW=mymng->width,OffsetH=mymng->height;
\r
353 BYTE *tmpbuffer = new BYTE[ (mymng->effwdt+1) * mymng->height];
\r
354 if( tmpbuffer == 0 ) return;
\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
364 MNG_COLORTYPE_RGB,
\r
365 MNG_COMPRESSION_DEFLATE,
\r
366 MNG_FILTER_ADAPTIVE,
\r
367 MNG_INTERLACE_NONE
\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
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
387 if(Z_OK != compress2((Bytef *)dstbuffer,(ULONG *)&dstbufferSize,(const Bytef*)tmpbuffer,
\r
388 (ULONG) (mymng->effwdt+1)*OffsetH,9 )) return;
\r
390 // Write Data into MNG File:
\r
391 mng_putchunk_idat( hMNG, dstbufferSize, (mng_ptr*)dstbuffer);
\r
392 mng_putchunk_iend(hMNG);
\r
395 delete [] tmpbuffer;
\r
396 delete [] dstbuffer;
\r
398 ////////////////////////////////////////////////////////////////////////////////
\r
399 long CxImageMNG::Resume()
\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
406 memcpy(GetBits(), mnginfo.image, info.dwEffWidth * head.biHeight);
\r
407 #if CXIMAGE_SUPPORT_ALPHA
\r
410 if(AlphaIsValid() && mnginfo.alpha){
\r
411 memcpy(AlphaGetPointer(),mnginfo.alpha,mnginfo.width * mnginfo.height);
\r
416 mnginfo.animation_enabled = 0;
\r
418 return mnginfo.animation_enabled;
\r
420 ////////////////////////////////////////////////////////////////////////////////
\r
421 void CxImageMNG::SetSpeed(float speed)
\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
427 ////////////////////////////////////////////////////////////////////////////////
\r
428 #endif //CXIMAGE_SUPPORT_ENCODE
\r
429 ////////////////////////////////////////////////////////////////////////////////
\r
430 #endif // CXIMAGE_SUPPORT_MNG
\r