1 /*
2 mngplay
3
4 $Date: 2003/12/07 09:45:16 $
5
6 Ralph Giles <giles :at: ashlu.bc.ca>
7
8 This program my be redistributed under the terms of the
9 GNU General Public Licence, version 2, or at your preference,
10 any later version.
11
12 (this assuming there's no problem with libmng not being GPL...)
13
14
15 this is an SDL based mng player. the code is very rough;
16 patches welcome.
17
18
19 GRR 20010708: added SDL/libmng/zlib/libjpeg version info, mouse-click
20 handling (alternate quit mode); improved automake setup
21
22 Raphael Assenat <raph :at: raphnet.net>
23 2003/11/26: added command line options to run in alternate color depths.
24
25 */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #include <SDL/SDL.h>
31 #include <libmng.h>
32
33 #include <libgen.h> // basename
34
35
36 #define DEFAULT_SDL_VIDEO_DEPTH 32
37
38 /* structure for keeping track of our mng stream inside the callbacks */
39 typedef struct {
40 FILE *file; /* pointer to the file we're decoding */
41 char *filename; /* pointer to the file's path/name */
42 SDL_Surface *surface; /* SDL display */
43 mng_uint32 delay; /* ticks to wait before resuming decode */
44 int sdl_video_depth; /* The depth for SDL_SetVideoMode */
45 } mngstuff;
46
47 /* callbacks for the mng decoder */
48
49 /* memory allocation; data must be zeroed */
mymngalloc(mng_uint32 size)50 mng_ptr mymngalloc(mng_uint32 size)
51 {
52 return (mng_ptr)calloc(1, size);
53 }
54
55 /* memory deallocation */
mymngfree(mng_ptr p,mng_uint32 size)56 void mymngfree(mng_ptr p, mng_uint32 size)
57 {
58 free(p);
59 return;
60 }
61
mymngopenstream(mng_handle mng)62 mng_bool mymngopenstream(mng_handle mng)
63 {
64 mngstuff *mymng;
65
66 /* look up our stream struct */
67 mymng = (mngstuff*)mng_get_userdata(mng);
68
69 /* open the file */
70 mymng->file = fopen(mymng->filename, "rb");
71 if (mymng->file == NULL) {
72 fprintf(stderr, "unable to open '%s'\n", mymng->filename);
73 return MNG_FALSE;
74 }
75
76 return MNG_TRUE;
77 }
78
mymngclosestream(mng_handle mng)79 mng_bool mymngclosestream(mng_handle mng)
80 {
81 mngstuff *mymng;
82
83 /* look up our stream struct */
84 mymng = (mngstuff*)mng_get_userdata(mng);
85
86 /* close the file */
87 fclose(mymng->file);
88 mymng->file = NULL; /* for safety */
89
90 return MNG_TRUE;
91 }
92
93 /* feed data to the decoder */
mymngreadstream(mng_handle mng,mng_ptr buffer,mng_uint32 size,mng_uint32 * bytesread)94 mng_bool mymngreadstream(mng_handle mng, mng_ptr buffer,
95 mng_uint32 size, mng_uint32 *bytesread)
96 {
97 mngstuff *mymng;
98
99 /* look up our stream struct */
100 mymng = (mngstuff*)mng_get_userdata(mng);
101
102 /* read the requested amount of data from the file */
103 *bytesread = fread(buffer, 1, size, mymng->file);
104
105 return MNG_TRUE;
106 }
107
108 /* the header's been read. set up the display stuff */
mymngprocessheader(mng_handle mng,mng_uint32 width,mng_uint32 height)109 mng_bool mymngprocessheader(mng_handle mng,
110 mng_uint32 width, mng_uint32 height)
111 {
112 mngstuff *mymng;
113 SDL_Surface *screen;
114 char title[256];
115
116 // fprintf(stderr, "our mng is %dx%d\n", width,height);
117
118 /* retreive our user data */
119 mymng = (mngstuff*)mng_get_userdata(mng);
120
121 screen = SDL_SetVideoMode(width,height, mymng->sdl_video_depth, SDL_SWSURFACE);
122 if (screen == NULL) {
123 fprintf(stderr, "unable to allocate %dx%d video memory: %s\n",
124 width, height, SDL_GetError());
125 return MNG_FALSE;
126 }
127
128 printf("SDL Video Mode: %dx%d bpp=%d\n", width, height, mymng->sdl_video_depth);
129
130 /* save the surface pointer */
131 mymng->surface = screen;
132
133 /* set a descriptive window title */
134 snprintf(title, 256, "mngplay: %s", mymng->filename);
135 SDL_WM_SetCaption(title, "mngplay");
136
137 /* in necessary, lock the drawing surface to the decoder
138 can safely fill it. We'll unlock elsewhere before display */
139 if (SDL_MUSTLOCK(mymng->surface)) {
140 if ( SDL_LockSurface(mymng->surface) < 0 ) {
141 fprintf(stderr, "could not lock display surface\n");
142 exit(1);
143 }
144 }
145
146 /*
147 printf("RGBA Masks: %08X %08X %08X %08X\n",
148 mymng->surface->format->Rmask,
149 mymng->surface->format->Gmask,
150 mymng->surface->format->Bmask,
151 mymng->surface->format->Amask);
152 printf("RGBA Shifts: %08X %08X %08X %08X\n",
153 mymng->surface->format->Rshift,
154 mymng->surface->format->Gshift,
155 mymng->surface->format->Bshift,
156 mymng->surface->format->Ashift);
157 */
158 /* Choose a canvas style which matches the SDL_Surface pixel format */
159 switch(mymng->surface->format->BitsPerPixel)
160 {
161 case 32:
162 if (mymng->surface->format->Amask==0) {
163 /* No alpha (padding byte) */
164 if (mymng->surface->format->Bshift==0) {
165 /* Blue first */
166 mng_set_canvasstyle(mng, MNG_CANVAS_BGRX8);
167 } else {
168 /* Red first */
169 fprintf(stderr, "No matching mng canvas for sdl pixel format. Colors may be wrong.\n");
170 mng_set_canvasstyle(mng, MNG_CANVAS_BGRX8);
171 }
172 }
173 else {
174 /* Real alpha */
175 if (mymng->surface->format->Bshift==0) {
176 /* Blue first */
177 mng_set_canvasstyle(mng, MNG_CANVAS_BGRA8);
178 } else {
179 /* Red first */
180 mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8);
181 }
182 }
183 break;
184 case 24:
185 if (mymng->surface->format->Amask==0) {
186 /* No alpha here should mean true rgb24bit */
187 if (mymng->surface->format->Bshift==0) {
188 /* Blue first */
189 mng_set_canvasstyle(mng, MNG_CANVAS_BGR8);
190 } else {
191 /* Red first */
192 mng_set_canvasstyle(mng, MNG_CANVAS_RGB8);
193 }
194 }
195 else {
196 /* If there is an alpha and we are in 24 bpp, this must
197 * mean rgb5658 */
198 if (mymng->surface->format->Bshift==0) {
199 /* Blue first */
200 mng_set_canvasstyle(mng, MNG_CANVAS_BGRA565);
201 } else {
202 /* Red first */
203 mng_set_canvasstyle(mng, MNG_CANVAS_RGBA565);
204 }
205 }
206 break;
207 case 16:
208 if (mymng->surface->format->Bshift==0) {
209 /* Blue first */
210 mng_set_canvasstyle(mng, MNG_CANVAS_BGR565);
211 } else {
212 /* Red first */
213 mng_set_canvasstyle(mng, MNG_CANVAS_RGB565);
214 }
215 break;
216 default:
217 return MNG_FALSE;
218 }
219
220 return MNG_TRUE;
221 }
222
223 /* return a row pointer for the decoder to fill */
mymnggetcanvasline(mng_handle mng,mng_uint32 line)224 mng_ptr mymnggetcanvasline(mng_handle mng, mng_uint32 line)
225 {
226 mngstuff *mymng;
227 SDL_Surface *surface;
228 mng_ptr row;
229
230 /* dereference our structure */
231 mymng = (mngstuff*)mng_get_userdata(mng);
232
233 /* we assume any necessary locking has happened
234 outside, in the frame level code */
235 row = mymng->surface->pixels + mymng->surface->pitch*line;
236
237 // fprintf(stderr, " returning pointer to line %d (%p)\n", line, row);
238
239 return (row);
240 }
241
242 /* timer */
mymnggetticks(mng_handle mng)243 mng_uint32 mymnggetticks(mng_handle mng)
244 {
245 mng_uint32 ticks;
246
247 ticks = (mng_uint32)SDL_GetTicks();
248 // fprintf(stderr, " %d\t(returning tick count)\n",ticks);
249
250 return(ticks);
251 }
252
mymngrefresh(mng_handle mng,mng_uint32 x,mng_uint32 y,mng_uint32 w,mng_uint32 h)253 mng_bool mymngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y,
254 mng_uint32 w, mng_uint32 h)
255 {
256 mngstuff *mymng;
257 SDL_Rect frame;
258
259 frame.x = x;
260 frame.y = y;
261 frame.w = w;
262 frame.h = h;
263
264 /* dereference our structure */
265 mymng = (mngstuff*)mng_get_userdata(mng);
266
267 /* if necessary, unlock the display */
268 if (SDL_MUSTLOCK(mymng->surface)) {
269 SDL_UnlockSurface(mymng->surface);
270 }
271
272 /* refresh the screen with the new frame */
273 SDL_UpdateRects(mymng->surface, 1, &frame);
274
275 /* in necessary, relock the drawing surface */
276 if (SDL_MUSTLOCK(mymng->surface)) {
277 if ( SDL_LockSurface(mymng->surface) < 0 ) {
278 fprintf(stderr, "could not lock display surface\n");
279 return MNG_FALSE;
280 }
281 }
282
283
284 return MNG_TRUE;
285 }
286
287 /* interframe delay callback */
mymngsettimer(mng_handle mng,mng_uint32 msecs)288 mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)
289 {
290 mngstuff *mymng;
291
292 // fprintf(stderr," pausing for %d ms\n", msecs);
293
294 /* look up our stream struct */
295 mymng = (mngstuff*)mng_get_userdata(mng);
296
297 /* set the timer for when the decoder wants to be woken */
298 mymng->delay = msecs;
299
300 return MNG_TRUE;
301
302 }
303
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)304 mng_bool mymngerror(mng_handle mng, mng_int32 code, mng_int8 severity,
305 mng_chunkid chunktype, mng_uint32 chunkseq,
306 mng_int32 extra1, mng_int32 extra2, mng_pchar text)
307 {
308 mngstuff *mymng;
309 char chunk[5];
310
311 /* dereference our data so we can get the filename */
312 mymng = (mngstuff*)mng_get_userdata(mng);
313
314 /* pull out the chuck type as a string */
315 // FIXME: does this assume unsigned char?
316 chunk[0] = (char)((chunktype >> 24) & 0xFF);
317 chunk[1] = (char)((chunktype >> 16) & 0xFF);
318 chunk[2] = (char)((chunktype >> 8) & 0xFF);
319 chunk[3] = (char)((chunktype ) & 0xFF);
320 chunk[4] = '\0';
321
322 /* output the error */
323 fprintf(stderr, "error playing '%s' chunk %s (%d):\n",
324 mymng->filename, chunk, chunkseq);
325 fprintf(stderr, "%s\n", text);
326
327 return (0);
328 }
329
mymngquit(mng_handle mng)330 int mymngquit(mng_handle mng)
331 {
332 mngstuff *mymng;
333
334 /* dereference our data so we can free it */
335 mymng = (mngstuff*)mng_get_userdata(mng);
336
337 /* cleanup. this will call mymngclosestream */
338 mng_cleanup(&mng);
339
340 /* free our data */
341 free(mymng);
342
343 /* quit */
344 exit(0);
345 }
346
checkevents(mng_handle mng)347 int checkevents(mng_handle mng)
348 {
349 SDL_Event event;
350
351 /* check if there's an event pending */
352 if (!SDL_PollEvent(&event)) {
353 return 0; /* no events pending */
354 }
355
356 /* we have an event; process it */
357 switch (event.type) {
358 case SDL_QUIT:
359 mymngquit(mng); /* quit */
360 break;
361 case SDL_MOUSEBUTTONDOWN:
362 case SDL_MOUSEBUTTONUP:
363 mymngquit(mng);
364 break;
365 case SDL_KEYUP:
366 switch (event.key.keysym.sym) {
367 case SDLK_ESCAPE:
368 case SDLK_q:
369 mymngquit(mng);
370 break;
371 }
372 /* FALL THROUGH */
373 default:
374 return 1;
375 }
376
377 return 0; /* GRR ADDED: non-void function */
378 }
379
main(int argc,char * argv[])380 int main(int argc, char *argv[])
381 {
382 mngstuff *mymng;
383 mng_handle mng;
384 SDL_Rect updaterect;
385
386 if (argc < 2) {
387 const SDL_version *pSDLver = SDL_Linked_Version();
388
389 fprintf(stderr, "Usage: %s mngfile [depth]\n\n", basename(argv[0]));
390 fprintf(stderr, " where 'depth' is 15,16,24 or 32\n");
391 fprintf(stderr,
392 " Compiled with SDL %d.%d.%d; using SDL %d.%d.%d.\n",
393 SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
394 pSDLver->major, pSDLver->minor, pSDLver->patch);
395 fprintf(stderr, " Compiled with libmng %s; using libmng %s.\n",
396 MNG_VERSION_TEXT, mng_version_text());
397 fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n",
398 ZLIB_VERSION, zlib_version);
399 #ifdef JPEG_LIB_VERSION
400 {
401 int major = JPEG_LIB_VERSION / 10;
402 int minor = JPEG_LIB_VERSION % 10;
403 char minoralpha[2];
404
405 if (minor) {
406 minoralpha[0] = (char)(minor - 1 + 'a');
407 minoralpha[1] = '\0';
408 } else
409 minoralpha[0] = '\0';
410 fprintf(stderr, " Compiled with libjpeg %d%s.\n",
411 major, minoralpha);
412 }
413 #endif
414 fprintf(stderr,
415 "\nPress Esc or Q, or click mouse button, to quit.\n");
416 exit(1);
417 }
418
419 /* allocate our stream data structure */
420 mymng = (mngstuff*)calloc(1, sizeof(*mymng));
421 if (mymng == NULL) {
422 fprintf(stderr, "could not allocate stream structure.\n");
423 exit(0);
424 }
425
426 /* pass the name of the file we want to play */
427 mymng->filename = argv[1];
428
429 /* pass the color depth we wish to use */
430 if (argc>=3) {
431 mymng->sdl_video_depth = atoi(argv[2]);
432 switch(mymng->sdl_video_depth) {
433 case 15:
434 case 16:
435 case 24:
436 case 32:
437 break;
438 default:
439 fprintf(stderr, "Unsupported color depth. Choices are: 15, 16, 24 and 32\n");
440 exit(1);
441 }
442 }
443 else {
444 mymng->sdl_video_depth = DEFAULT_SDL_VIDEO_DEPTH;
445 }
446
447 /* set up the mng decoder for our stream */
448 mng = mng_initialize(mymng, mymngalloc, mymngfree, MNG_NULL);
449 if (mng == MNG_NULL) {
450 fprintf(stderr, "could not initialize libmng.\n");
451 exit(1);
452 }
453
454 /* set the callbacks */
455 mng_setcb_errorproc(mng, mymngerror);
456 mng_setcb_openstream(mng, mymngopenstream);
457 mng_setcb_closestream(mng, mymngclosestream);
458 mng_setcb_readdata(mng, mymngreadstream);
459 mng_setcb_gettickcount(mng, mymnggetticks);
460 mng_setcb_settimer(mng, mymngsettimer);
461 mng_setcb_processheader(mng, mymngprocessheader);
462 mng_setcb_getcanvasline(mng, mymnggetcanvasline);
463 mng_setcb_refresh(mng, mymngrefresh);
464 /* FIXME: should check for errors here */
465
466 /* initialize SDL */
467 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
468 fprintf(stderr, "%s: Unable to initialize SDL (%s)\n",
469 argv[0], SDL_GetError());
470 exit(1);
471 }
472 /* arrange to call the shutdown routine before we exit */
473 atexit(SDL_Quit);
474
475 /* restrict event handling to the relevant bits */
476 SDL_EventState(SDL_KEYDOWN, SDL_IGNORE); /* keyup only */
477 SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
478 // SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
479 // SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
480
481 // fprintf(stderr, "playing mng...maybe.\n");
482
483 mng_readdisplay(mng);
484
485 /* loop though the frames */
486 while (mymng->delay) {
487 // fprintf(stderr, " waiting for %d ms\n", mymng->delay);
488 SDL_Delay(mymng->delay);
489
490 /* reset the delay in case the decoder
491 doesn't update it again */
492 mymng->delay = 0;
493
494 mng_display_resume(mng);
495
496 /* check for user input (just quit at this point) */
497 checkevents(mng);
498 }
499
500 /* �hay alguno? pause before quitting */
501 fprintf(stderr, "pausing before shutdown...\n");
502 SDL_Delay(1000);
503
504 /* cleanup and quit */
505 mymngquit(mng);
506 }
507
508