1 //---------------------------------------------------------------------------------------------
2 //---------------------------------------------------------------------------------------------
3 //
4 // MNGView Sample Application for VC6:
5 // Loads all MNG/JNG/PNG Files LibMNG can do
6 // Can save a single Frame to PNG Format.
7 //
8 // This code is public domain.
9 // Created by Nikolaus Brennig, November 14th, 2000.
10 // virtualnik@nol.at
11 // http://cust.nol.at/ppee
12 //
13 // Tab: 4
14 //
15 //---------------------------------------------------------------------------------------------
16 //---------------------------------------------------------------------------------------------
17 #define WIN32_LEAN_AND_MEAN
18 #include "Main.h"
19 #include <libmng.h>
20
21
22 //---------------------------------------------------------------------------------------------
23 // VARS:
24 //---------------------------------------------------------------------------------------------
25 typedef struct
26 {
27 FILE *file;
28 LPSTR filename;
29 mng_uint32 delay;
30 } mngstuff;
31
32 int lineWidth;
33 BYTE *mngdestbuffer;
34 mngstuff *mymngstuff;
35 mng_handle Curmng;
36
37
38
39 ///////////////////////////////////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////////////////////////////////
41 //---------------------------------------------------------------------------------------------
42 // callbacks for the mng decoder:
43 //---------------------------------------------------------------------------------------------
44 ///////////////////////////////////////////////////////////////////////////////////////////////
45 ///////////////////////////////////////////////////////////////////////////////////////////////
46
47 //---------------------------------------------------------------------------------------------
48 // memory allocation; data must be zeroed
49 //---------------------------------------------------------------------------------------------
mymngalloc(mng_uint32 size)50 mng_ptr mymngalloc( mng_uint32 size )
51 {
52 return (mng_ptr)calloc(1, size);
53 }
54
55
56 //---------------------------------------------------------------------------------------------
57 // memory deallocation
58 //---------------------------------------------------------------------------------------------
mymngfree(mng_ptr p,mng_uint32 size)59 void mymngfree(mng_ptr p, mng_uint32 size)
60 {
61 free(p);
62 }
63
64
65 //---------------------------------------------------------------------------------------------
66 // Stream open:
67 //---------------------------------------------------------------------------------------------
mymngopenstream(mng_handle mng)68 mng_bool mymngopenstream(mng_handle mng)
69 {
70 mngstuff *mymng;
71
72 // look up our stream struct
73 mymng = (mngstuff*)mng_get_userdata(mng);
74
75 // open the file
76 mymng->file = fopen( mymng->filename, "rb" );
77 if( mymng->file == NULL )
78 {
79 char temp[100];
80 sprintf( temp, "Unable to open File: %s", mymng->filename );
81 Warning( temp );
82 return MNG_FALSE;
83 }
84
85 return MNG_TRUE;
86 }
87
88
89 //---------------------------------------------------------------------------------------------
90 // Stream open for Writing:
91 //---------------------------------------------------------------------------------------------
mymngopenstreamwrite(mng_handle mng)92 mng_bool mymngopenstreamwrite(mng_handle mng)
93 {
94 mngstuff *mymng;
95
96 // look up our stream struct
97 mymng = (mngstuff*)mng_get_userdata(mng);
98
99 // open the file
100 mymng->file = fopen( mymng->filename, "wb" );
101 if( mymng->file == NULL )
102 {
103 Warning( "unable to open file!" );
104 return MNG_FALSE;
105 }
106
107 return MNG_TRUE;
108 }
109
110
111 //---------------------------------------------------------------------------------------------
112 // Stream close:
113 //---------------------------------------------------------------------------------------------
mymngclosestream(mng_handle mng)114 mng_bool mymngclosestream(mng_handle mng)
115 {
116 return MNG_TRUE; // We close the file ourself, mng_cleanup doesnt seem to do it...
117 }
118
119
120 //---------------------------------------------------------------------------------------------
121 // feed data to the decoder
122 //---------------------------------------------------------------------------------------------
mymngreadstream(mng_handle mng,mng_ptr buffer,mng_uint32 size,mng_uint32 * bytesread)123 mng_bool mymngreadstream( mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 *bytesread )
124 {
125 mngstuff *mymng;
126
127 // look up our stream struct
128 mymng = (mngstuff*)mng_get_userdata(mng);
129
130 // read the requested amount of data from the file
131 *bytesread = fread( buffer, sizeof(BYTE), size, mymng->file );
132
133 return MNG_TRUE;
134 }
135
136
137 //---------------------------------------------------------------------------------------------
138 // the header's been read. set up the display stuff
139 //---------------------------------------------------------------------------------------------
mymngprocessheader(mng_handle mng,mng_uint32 width,mng_uint32 height)140 mng_bool mymngprocessheader( mng_handle mng, mng_uint32 width, mng_uint32 height )
141 {
142 // Store values:
143 W = width; H = height;
144 Bits = 24;
145 lineWidth = ((((W * Bits) + 31) >> 5) << 2);
146
147 // Create decoderbuffer:
148 mngdestbuffer = new BYTE[lineWidth*H];
149 if( mngdestbuffer == 0 ) Warning( "Out of Memory!" );
150
151 // Create the MemoryBitmap now, we store there the image...
152 if( DefaultMemImage ) SelectObject( MemDC, DefaultMemImage );
153 if( MemDC ) DeleteDC( MemDC );
154 if( MemImage ) DeleteObject( MemImage );
155 hdc = GetDC( hPicWin );
156 MemDC = CreateCompatibleDC( 0 );
157 MemImage = CreateCompatibleBitmap( hdc, W, H );
158 DefaultMemImage = (HBITMAP)SelectObject( MemDC, MemImage );
159 ReleaseDC( hPicWin, hdc );
160
161 // Set output style:
162 mng_set_canvasstyle( mng, MNG_CANVAS_BGR8 );
163
164 return MNG_TRUE;
165 }
166
167
168 //---------------------------------------------------------------------------------------------
169 // return a row pointer for the decoder to fill
170 //---------------------------------------------------------------------------------------------
mymnggetcanvasline(mng_handle mng,mng_uint32 line)171 mng_ptr mymnggetcanvasline( mng_handle mng, mng_uint32 line )
172 {
173 return (mng_ptr)(mngdestbuffer + (lineWidth*(H-1-line)));
174 }
175
176
177 //---------------------------------------------------------------------------------------------
178 // timer
179 //---------------------------------------------------------------------------------------------
mymnggetticks(mng_handle mng)180 mng_uint32 mymnggetticks(mng_handle mng)
181 {
182 return (mng_uint32)GetTickCount();
183 }
184
185
186 //---------------------------------------------------------------------------------------------
187 // Refresh:
188 //---------------------------------------------------------------------------------------------
mymngrefresh(mng_handle mng,mng_uint32 x,mng_uint32 y,mng_uint32 w,mng_uint32 h)189 mng_bool mymngrefresh( mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h )
190 {
191 PBITMAPINFO bmpi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
192 bmpi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
193 bmpi->bmiHeader.biWidth = W;
194 bmpi->bmiHeader.biHeight = H;
195 bmpi->bmiHeader.biPlanes = 1;
196 bmpi->bmiHeader.biCompression = BI_RGB;
197 bmpi->bmiHeader.biBitCount = Bits;
198 bmpi->bmiHeader.biSizeImage = 0;
199 bmpi->bmiHeader.biClrUsed = 0;
200 bmpi->bmiHeader.biClrImportant = 0;
201
202 // Now blt the Image onto our MemDC...
203 StretchDIBits( MemDC, 0, 0, W, H, 0, 0, W, H, mngdestbuffer, bmpi, 0, SRCCOPY );
204 LocalFree((PBITMAPINFO)bmpi);
205
206 return MNG_TRUE;
207 }
208
209
210 //---------------------------------------------------------------------------------------------
211 // interframe delay callback
212 //---------------------------------------------------------------------------------------------
mymngsettimer(mng_handle mng,mng_uint32 msecs)213 mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)
214 {
215 mngstuff *mymng;
216
217 // look up our stream struct
218 mymng = (mngstuff*)mng_get_userdata(mng);
219
220 // set the timer for when the decoder wants to be woken
221 mymng->delay = msecs;
222
223 return MNG_TRUE;
224 }
225
226
227 //---------------------------------------------------------------------------------------------
228 // Error Callback;
229 //---------------------------------------------------------------------------------------------
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)230 mng_bool mymngerror(
231 mng_handle mng, mng_int32 code, mng_int8 severity,
232 mng_chunkid chunktype, mng_uint32 chunkseq,
233 mng_int32 extra1, mng_int32 extra2, mng_pchar text
234 )
235 {
236 char chunk[5];
237
238 // pull out the chuck type as a string
239 // FIXME: does this assume unsigned char?
240 chunk[0] = (char)((chunktype >> 24) & 0xFF);
241 chunk[1] = (char)((chunktype >> 16) & 0xFF);
242 chunk[2] = (char)((chunktype >> 8) & 0xFF);
243 chunk[3] = (char)((chunktype ) & 0xFF);
244 chunk[4] = '\0';
245
246 // output the error:
247 char temp[1000];
248 sprintf( temp, "error playing chunk %s (%d)", chunk, chunkseq );
249 Warning( temp );
250
251 // No need for anymore decoding:
252 KillTimer( hPicWin, 2 );
253
254 // error occured;
255 return MNG_FALSE;
256 }
257
258
259 //---------------------------------------------------------------------------------------------
260 // Load a MNG/JNG/PNG file:
261 //---------------------------------------------------------------------------------------------
LoadMNG(LPSTR Filename,HWND hwnd,HDC hdc)262 VOID LoadMNG( LPSTR Filename, HWND hwnd, HDC hdc )
263 {
264 // allocate our stream data structure
265 mymngstuff = (mngstuff*)calloc(1, sizeof(*mymngstuff));
266 if( mymngstuff == NULL )
267 {
268 Warning( "Unable to allocate MNG struct!" );
269 return;
270 }
271
272 // pass the name of the file we want to play
273 mymngstuff->filename = Filename;
274
275 // set up the mng decoder for our stream
276 Curmng = mng_initialize(mymngstuff, mymngalloc, mymngfree, MNG_NULL);
277 if(Curmng == MNG_NULL)
278 {
279 free(mymngstuff);
280 Warning( "MNG Init Error!" );
281 return;
282 }
283
284 // No need to store chunks:
285 mng_set_storechunks(Curmng, MNG_FALSE);
286
287 // Set the colorprofile, lcms uses this:
288 mng_set_srgb( Curmng, MNG_TRUE );
289 char DestDir[2048];
290 SearchPath( NULL, "MNGVIEW.EXE", NULL, sizeof(DestDir), DestDir, NULL );
291 lstrcpyn( DestDir, DestDir, lstrlen(DestDir)-lstrlen("MNGVIEW.EXE") );
292 catpath( DestDir, "sRGB.icm" );
293 FILE *RGBfile = fopen( DestDir, "rb" );
294 if( RGBfile == 0 )
295 {
296 mng_cleanup(&Curmng);
297 free(mymngstuff);
298 Warning( "Need file \"sRGB.icm\" !" );
299 return;
300 }
301 fclose(RGBfile);
302 mng_set_outputprofile(Curmng, DestDir);
303
304 // Set white as background color:
305 WORD Red = (255 << 8) + 255;
306 WORD Green = (255 << 8) + 255;
307 WORD Blue = (255 << 8) + 255;
308 mng_set_bgcolor( Curmng, Red, Green, Blue );
309
310 // If PNG Background is available, use it:
311 mng_set_usebkgd( Curmng, MNG_TRUE );
312
313 // set the callbacks
314 mng_setcb_errorproc(Curmng, mymngerror);
315 mng_setcb_openstream(Curmng, mymngopenstream);
316 mng_setcb_closestream(Curmng, mymngclosestream);
317 mng_setcb_readdata(Curmng, mymngreadstream);
318 mng_setcb_gettickcount(Curmng, mymnggetticks);
319 mng_setcb_settimer(Curmng, mymngsettimer);
320 mng_setcb_processheader(Curmng, mymngprocessheader);
321 mng_setcb_getcanvasline(Curmng, mymnggetcanvasline);
322 mng_setcb_refresh(Curmng, mymngrefresh);
323
324 // Read the stuff:
325 mng_readdisplay(Curmng);
326
327 AnimFile.CurFrame = mng_get_layercount( Curmng );
328 AnimFile.MaxFrame = mng_get_framecount( Curmng );
329 AnimFile.isAnimation = 1;
330 AnimFile.Delay = mymngstuff->delay;
331
332 // Start the whole thing:
333 SetTimer( hPicWin, 2, mymngstuff->delay, 0 );
334 }
335
336
337 //---------------------------------------------------------------------------------------------
338 // Called when loading a new file or Appquit:
339 //---------------------------------------------------------------------------------------------
CleanUpMNG()340 void CleanUpMNG()
341 {
342 KillTimer( hPicWin, 2 );
343 mng_cleanup(&Curmng);
344 fclose( mymngstuff->file );
345 free(mymngstuff);
346 delete [] mngdestbuffer;
347 }
348
349
350 //---------------------------------------------------------------------------------------------
351 // Called when timer says next frame/layer/update is needed:
352 //---------------------------------------------------------------------------------------------
UpdateMNG()353 void UpdateMNG()
354 {
355 mymngstuff->delay = 0;
356 if( MNG_NEEDTIMERWAIT == mng_display_resume(Curmng) )
357 {
358 KillTimer( hPicWin, 2 );
359 SetTimer( hPicWin, 2, mymngstuff->delay, 0 );
360 }
361 else
362 {
363 CleanUpMNG();
364 AnimFile.CurFrame = -1;
365 AnimFile.MaxFrame = -1;
366 AnimFile.isAnimation = -1;
367 AnimFile.Delay = -1;
368 }
369 }
370
371
372
373 ///////////////////////////////////////////////////////////////////////////////////////////////
374 ///////////////////////////////////////////////////////////////////////////////////////////////
375 //---------------------------------------------------------------------------------------------
376 // MNG WRITING STUFF:
377 //---------------------------------------------------------------------------------------------
378 ///////////////////////////////////////////////////////////////////////////////////////////////
379 ///////////////////////////////////////////////////////////////////////////////////////////////
380 int OffsetX=0,OffsetY=0,OffsetW=0,OffsetH=0;
381 BYTE *srcbuffer=0, *tmpbuffer;
382
383
384 //---------------------------------------------------------------------------------------------
385 // Callback for writing data:
386 //---------------------------------------------------------------------------------------------
mymngwritedata(mng_handle hMNG,mng_ptr pBuf,mng_uint32 iSize,mng_uint32 * iWritten)387 mng_bool mymngwritedata( mng_handle hMNG, mng_ptr pBuf, mng_uint32 iSize, mng_uint32 *iWritten )
388 {
389 mngstuff *pMydata = (mngstuff*)mng_get_userdata(hMNG);
390
391 *iWritten = fwrite( pBuf, sizeof(BYTE), iSize, pMydata->file );
392
393 if( *iWritten < iSize )
394 {
395 Warning( "write error" );
396 return MNG_FALSE;
397 }
398
399 return MNG_TRUE;
400 }
401
402
403 //---------------------------------------------------------------------------------------------
404 // swap Rs and Bs...
405 //---------------------------------------------------------------------------------------------
RGBFromBGR(BYTE * buf,UINT widthPix,UINT height)406 BOOL RGBFromBGR( BYTE *buf, UINT widthPix, UINT height )
407 {
408 UINT col, row;
409 LPBYTE pRed, pBlu;
410 BYTE tmp;
411
412 if (buf==NULL)return FALSE;
413
414 INT TmpRow = 0;
415 INT WidthBytes = widthPix*3;
416 if(WidthBytes & 0x003) WidthBytes = (WidthBytes | 3) + 1;
417 INT OurCol = 0;
418 for( row=0; row<height; row++ )
419 {
420 for( col=0; col<widthPix; col++ )
421 {
422 pRed = buf + TmpRow + OurCol;
423 pBlu = pRed + 2;
424
425 // swap red and blue
426 tmp = *pBlu;
427 *pBlu = *pRed;
428 *pRed = tmp;
429
430 OurCol += 3;
431 }
432 TmpRow += WidthBytes;
433 OurCol = 0;
434 }
435
436 return TRUE;
437 }
438
439
440 //---------------------------------------------------------------------------------------------
441 // Creates the srcuffer filled with data for saving:
442 //---------------------------------------------------------------------------------------------
CreateSrcBuffer(int Frame,int FrameCount,HBITMAP hBmp2)443 VOID CreateSrcBuffer( int Frame, int FrameCount, HBITMAP hBmp2 )
444 {
445 PBITMAPINFO pbmi;
446 BITMAP bmp;
447
448 srcbuffer=0;
449
450 // Get WidthBytes...
451 INT WidthBytes = W*3;
452
453 INT LineWidth = W*3;
454 if(LineWidth & 0x003) LineWidth = (LineWidth | 3) + 1;
455
456 // Bitmapstruct init...
457 GetObject( hBmp2, sizeof(BITMAP), (LPSTR)&bmp );
458 pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
459
460 // Initialize the fields in the BITMAPINFO structure.
461 pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
462 pbmi->bmiHeader.biWidth = bmp.bmWidth;
463 pbmi->bmiHeader.biHeight = -bmp.bmHeight;
464 pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
465 pbmi->bmiHeader.biBitCount = 24;
466 pbmi->bmiHeader.biCompression = BI_RGB;
467 pbmi->bmiHeader.biSizeImage = LineWidth * H * FrameCount;
468 pbmi->bmiHeader.biClrImportant = 0;
469
470 // Alloc Memory...
471 srcbuffer = 0;
472 srcbuffer = new BYTE[LineWidth*H*FrameCount];
473 if( srcbuffer == 0 )
474 Warning( "srcbuffer == 0!" );
475
476 // get the buffer and modify the format:
477 if( 0 == GetDIBits( MemDC, hBmp2, 0, (WORD) (H*FrameCount), srcbuffer, pbmi, 0 ) )
478 Warning( "no GetDIBits!!!" );
479 RGBFromBGR( srcbuffer, W, H*FrameCount );
480 if( srcbuffer == 0 )
481 Warning( "srcbuffer == 0!" );
482
483 // Freee.
484 LocalFree((PBITMAPINFO)pbmi);
485 }
486
487
488 //---------------------------------------------------------------------------------------------
489 // Writes a single PNG datastream
490 //---------------------------------------------------------------------------------------------
WritePNG(mng_handle hMNG,int Frame,int FrameCount)491 VOID WritePNG( mng_handle hMNG, int Frame, int FrameCount )
492 {
493 BYTE *dstbuffer;
494 INT LineWidth;
495 INT WidthBytes;
496
497 OffsetX=0; OffsetY=0; OffsetW=W; OffsetH=H;
498
499 // Get WidthBytes...
500 WidthBytes = W*3;
501 LineWidth = W*3;
502 if(LineWidth & 0x003) LineWidth = (LineWidth | 3) + 1;
503
504 tmpbuffer = new BYTE[(WidthBytes+1)*OffsetH];
505 if( tmpbuffer == 0 ) Warning( "Out of Memory!" );
506
507 // Write DEFI chunk.
508 mng_putchunk_defi( hMNG, 0, 0, 0, MNG_TRUE, OffsetX, OffsetY, MNG_FALSE, 0, 0, 0, 0 );
509
510 // Write Header:
511 mng_putchunk_ihdr(
512 hMNG,
513 OffsetW, OffsetH,
514 MNG_BITDEPTH_8/*iBitdepth*/,
515 MNG_COLORTYPE_RGB/*iColortype*/,
516 MNG_COMPRESSION_DEFLATE/*iCompression*/,
517 MNG_FILTER_ADAPTIVE/*iFilter*/,
518 MNG_INTERLACE_NONE /*iInterlace*/
519 );
520
521 // transfer data, add Filterbyte:
522 for( int Row=0; Row<OffsetH; Row++ )
523 {
524 // First Byte in each Scanline is Filterbyte: Currently 0 -> No Filter.
525 tmpbuffer[Row*(WidthBytes+1)]=0;
526
527 // Copy the scanline:
528 memcpy(
529 tmpbuffer+Row*(WidthBytes+1)+1,
530 srcbuffer+((OffsetY+Row)*(LineWidth))+OffsetX,
531 WidthBytes
532 );
533 }
534
535 // Free srcbuffer if not animated GIF:
536 delete [] srcbuffer;
537
538 // Compress data with ZLib (Deflate):
539 dstbuffer = new BYTE[(WidthBytes+1)*OffsetH];
540 if( dstbuffer == 0 ) Warning( "Out of Memory!" );
541 DWORD dstbufferSize=(WidthBytes+1)*OffsetH;
542
543 // Compress data:
544 if( Z_OK != compress2(
545 (Bytef *)dstbuffer, (ULONG *)&dstbufferSize,
546 (const Bytef*)tmpbuffer, (ULONG) (WidthBytes+1)*OffsetH,
547 9
548 )) Warning( "Unable to compress imagedata!" );
549
550 // Write Data into MNG File:
551 mng_putchunk_idat( hMNG, dstbufferSize, (mng_ptr*)dstbuffer);
552 mng_putchunk_iend(hMNG);
553
554 // Free the stuff:
555 delete [] tmpbuffer;
556 delete [] dstbuffer;
557 }
558
559
560 //---------------------------------------------------------------------------------------------
561 // Writes a MNG (24bit)
562 //---------------------------------------------------------------------------------------------
SaveMNG(LPSTR Filename,HDC hdc,HBITMAP hBmp)563 VOID SaveMNG( LPSTR Filename, HDC hdc, HBITMAP hBmp )
564 {
565 mng_handle hMNG;
566
567 // check if currently a MNG file is loaded:
568 if( AnimFile.isAnimation == 1 )
569 CleanUpMNG();
570
571 // Creates the srcbuffer for imagedata:
572 CreateSrcBuffer( 0, 1, hBmp );
573
574 // allocate our stream data structure
575 mymngstuff = (mngstuff*)calloc(1, sizeof(*mymngstuff));
576 if( mymngstuff == NULL )
577 {
578 Warning( "Cannot allocate data buffer." );
579 return;
580 }
581
582 // pass the name of the file we want to play
583 mymngstuff->filename = Filename;
584
585 // init the lib:
586 hMNG = mng_initialize((mng_ptr)mymngstuff, mymngalloc, mymngfree, MNG_NULL);
587 if( !hMNG )
588 {
589 Warning( "Cannot initialize libmng." );
590 return;
591 }
592 else
593 {
594 mng_setcb_openstream(hMNG, mymngopenstreamwrite );
595 mng_setcb_closestream(hMNG, mymngclosestream);
596 mng_setcb_writedata(hMNG, mymngwritedata);
597
598 // Write File:
599 mng_create(hMNG);
600
601 // Just a single Frame (save a normal PNG):
602 WritePNG( hMNG, 0, 1 );
603
604 // Now write file:
605 mng_write(hMNG);
606
607 // Free the stuff:
608 fclose( mymngstuff->file );
609 mng_cleanup(&hMNG);
610 }
611
612 free( mymngstuff );
613 }
614
615
616