1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2
3 /*
4 * GImageView
5 * Copyright (C) 2001 Takuro Ashie
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * $Id: mng.c,v 1.12 2004/09/21 08:44:27 makeinu Exp $
22 */
23
24 #include "mng.h"
25
26 #ifdef ENABLE_MNG
27
28 #include <libmng.h>
29
30 /* avoid libjpeg's bug */
31 #undef HAVE_STDDEF_H
32 #undef HAVE_STDLIB_H
33 #include "gimv_plugin.h"
34
35 typedef struct MNGAnim_Tag
36 {
37 GimvImage *image;
38 GimvIO *file;
39 gchar *filename;
40 mng_handle MNG_handle;
41 mng_ptr canvas;
42 mng_uint32 width;
43 mng_uint32 height;
44 gint bytes_per_pixel;
45 mng_uint32 delay;
46 GTimer *timer;
47 } MNGAnim;
48
49
50 /* GimvAnim methos */
51 static gint vfmng_iterate (GimvAnim *anim);
52 static gint vfmng_get_interval (GimvAnim *anim);
53 static void vfmng_delete (GimvAnim *anim);
54
55 /* libmng callback */
56 static mng_bool mymng_error (mng_handle mng,
57 mng_int32 code,
58 mng_int8 severity,
59 mng_chunkid chunktype,
60 mng_uint32 chunkseq,
61 mng_int32 extra1,
62 mng_int32 extra2,
63 mng_pchar text);
64 static mng_ptr mymng_malloc_callback (mng_size_t how_many);
65 static void mymng_free_callback (mng_ptr pointer,
66 mng_size_t number);
67 static mng_bool mymng_open_stream (mng_handle mng);
68 static mng_bool mymng_close_stream (mng_handle mng);
69 static mng_bool mymng_read_stream (mng_handle mng,
70 mng_ptr buffer,
71 mng_uint32 size,
72 mng_uint32 *bytesread);
73 static mng_uint32 mymng_get_ticks (mng_handle mng);
74 static mng_bool mymng_set_timer (mng_handle mng,
75 mng_uint32 msecs);
76 static mng_bool mymng_process_header (mng_handle mng,
77 mng_uint32 width,
78 mng_uint32 height);
79 static mng_ptr mymng_get_canvas_line (mng_handle mng,
80 mng_uint32 line);
81 static mng_bool mymng_refresh (mng_handle mng,
82 mng_uint32 x,
83 mng_uint32 y,
84 mng_uint32 w,
85 mng_uint32 h);
86
87
88 static GimvImageLoaderPlugin gimv_mng_loader[] =
89 {
90 {
91 if_version: GIMV_IMAGE_LOADER_IF_VERSION,
92 id: "MNG",
93 priority_hint: GIMV_IMAGE_LOADER_PRIORITY_CAN_CANCEL,
94 check_type: NULL,
95 get_info: NULL,
96 loader: mng_load,
97 }
98 };
99
100 static const gchar *jng_extensions[] =
101 {
102 "jng",
103 };
104
105 static const gchar *mng_extensions[] =
106 {
107 "mng",
108 };
109
110 static GimvMimeTypeEntry mng_mime_types[] =
111 {
112 {
113 mime_type: "image/x-jng",
114 description: "JNG Image",
115 extensions: jng_extensions,
116 extensions_len: sizeof (jng_extensions) / sizeof (gchar *),
117 icon: NULL,
118 },
119 {
120 mime_type: "video/x-mng",
121 description: "MNG Image",
122 extensions: mng_extensions,
123 extensions_len: sizeof (mng_extensions) / sizeof (gchar *),
124 icon: NULL,
125 },
126 };
127
128 GIMV_PLUGIN_GET_IMPL(gimv_mng_loader, GIMV_PLUGIN_IMAGE_LOADER)
129 GIMV_PLUGIN_GET_MIME_TYPE(mng_mime_types)
130
131 GimvPluginInfo gimv_plugin_info =
132 {
133 if_version: GIMV_PLUGIN_IF_VERSION,
134 name: N_("MNG Image Loader"),
135 version: "0.1.1",
136 author: N_("Takuro Ashie"),
137 description: NULL,
138 get_implement: gimv_plugin_get_impl,
139 get_mime_type: gimv_plugin_get_mime_type,
140 get_prefs_ui: NULL,
141 };
142
143
144 static GimvAnimFuncTable mng_vf_table = {
145 get_length : NULL,
146 get_idx : NULL,
147 get_interval : vfmng_get_interval,
148 iterate : vfmng_iterate,
149 seek : NULL,
150 delete : vfmng_delete,
151 };
152
153
154 static gboolean
mng_check_type(const gchar * filename)155 mng_check_type (const gchar *filename)
156 {
157 GimvIO *gio;
158 guchar buf[256];
159 GimvIOStatus status;
160 guint bytes;
161
162 gio = gimv_io_new (filename, "rb");
163 if (!gio) return FALSE;
164
165 status = gimv_io_read (gio, buf, 8, &bytes);
166 if (bytes != 8)
167 goto ERROR;
168
169 if (!(buf[0] == 0x8a && buf[1] == 'M' && buf[2] == 'N' && buf[3] == 'G') &&
170 !(buf[0] == 0x8b && buf[1] == 'J' && buf[2] == 'N' && buf[3] == 'G'))
171 {
172 goto ERROR;
173 }
174
175 if (buf[4] != 0x0d ||
176 buf[5] != 0x0a ||
177 buf[6] != 0x1a ||
178 buf[7] != 0x0a)
179 {
180 /* g_warning ("not mng format"); */
181 goto ERROR;
182 }
183
184 gimv_io_close (gio);
185 return TRUE;
186
187 ERROR:
188 gimv_io_close (gio);
189 return FALSE;
190 }
191
192
193 static MNGAnim *
mng_anim_new(const gchar * filename,GimvImage * image)194 mng_anim_new (const gchar *filename, GimvImage *image)
195 {
196 MNGAnim *anim = g_new0 (MNGAnim, 1);
197 mng_retcode retval;
198
199 g_return_val_if_fail (filename && *filename, NULL);
200 g_return_val_if_fail (image, NULL);
201
202 anim->image = image;
203 anim->file = NULL;
204 anim->filename = g_strdup (filename);
205 anim->MNG_handle = mng_initialize (image,
206 mymng_malloc_callback,
207 mymng_free_callback,
208 MNG_NULL);
209 anim->canvas = NULL;
210 anim->delay = 0;
211 anim->width = 0;
212 anim->height = 0;
213 anim->bytes_per_pixel = 3;
214
215 retval = mng_setcb_errorproc (anim->MNG_handle, mymng_error);
216 if (retval != MNG_NOERROR) goto ERROR;
217
218 retval = mng_setcb_openstream (anim->MNG_handle, mymng_open_stream);
219 if (retval != MNG_NOERROR) goto ERROR;
220
221 retval = mng_setcb_closestream (anim->MNG_handle, mymng_close_stream);
222 if (retval != MNG_NOERROR) goto ERROR;
223
224 retval = mng_setcb_readdata (anim->MNG_handle, mymng_read_stream);
225 if (retval != MNG_NOERROR) goto ERROR;
226
227 retval = mng_setcb_gettickcount (anim->MNG_handle, mymng_get_ticks);
228 if (retval != MNG_NOERROR) goto ERROR;
229
230 retval = mng_setcb_settimer (anim->MNG_handle, mymng_set_timer);
231 if (retval != MNG_NOERROR) goto ERROR;
232
233 retval = mng_setcb_processheader (anim->MNG_handle, mymng_process_header);
234 if (retval != MNG_NOERROR) goto ERROR;
235
236 retval = mng_setcb_getcanvasline (anim->MNG_handle, mymng_get_canvas_line);
237 if (retval != MNG_NOERROR) goto ERROR;
238
239 retval = mng_setcb_refresh (anim->MNG_handle, mymng_refresh);
240 if (retval != MNG_NOERROR) goto ERROR;
241
242 return anim;
243
244 ERROR:
245 g_free (anim);
246 return NULL;
247 }
248
249
250 static void
mng_anim_delete(MNGAnim * anim)251 mng_anim_delete (MNGAnim *anim)
252 {
253 g_return_if_fail (anim);
254
255 if (anim->file)
256 gimv_io_close (anim->file);
257 anim->file = NULL;
258
259 if (anim->filename)
260 g_free (anim->filename);
261 anim->filename = NULL;
262
263 if (anim->canvas)
264 g_free (anim->canvas);
265 anim->canvas = NULL;
266
267 mng_cleanup (&anim->MNG_handle);
268
269 if (anim->timer)
270 g_timer_destroy (anim->timer);
271 anim->timer = NULL;
272
273 g_free (anim);
274 }
275
276
277 /* FIXME */
278 GimvImage *
mng_load(GimvImageLoader * loader,gpointer data)279 mng_load (GimvImageLoader *loader, gpointer data)
280 {
281 const gchar *filename;
282 GimvAnim *anim;
283 GimvImage *image;
284 MNGAnim *mng_anim;
285
286 g_return_val_if_fail (loader, NULL);
287
288 filename = gimv_image_loader_get_path (loader);
289 if (!filename || !*filename) return NULL;
290
291 if (!mng_check_type (filename)) return NULL;
292
293 anim = gimv_anim_new ();
294 image = (GimvImage *) anim;
295
296 mng_anim = mng_anim_new (filename, image);
297 if (!mng_anim) {
298 gimv_image_unref (image);
299 return NULL;
300 }
301
302 image = (GimvImage *) anim;
303 anim->anim = mng_anim;
304 anim->table = &mng_vf_table;
305
306 mng_readdisplay (mng_anim->MNG_handle);
307
308 if (!anim->anim || !image->image) {
309 gimv_image_unref (image);
310 return NULL;
311 }
312
313 anim->current_frame_idx++;
314
315 return image;
316 }
317
318
319
320 /****************************************************************************
321 *
322 * GimvAnim methods
323 *
324 ****************************************************************************/
325 static gint
vfmng_iterate(GimvAnim * anim)326 vfmng_iterate (GimvAnim *anim)
327 {
328 MNGAnim *mng_anim;
329 gint ret;
330
331 g_return_val_if_fail (anim, -1);
332
333 mng_anim = anim->anim;
334
335 mng_anim->delay = 0;
336 ret = mng_display_resume (mng_anim->MNG_handle);
337
338 if (!ret) {
339 return anim->current_frame_idx;
340 }
341
342 return ++anim->current_frame_idx;
343 }
344
345
346 static gint
vfmng_get_interval(GimvAnim * anim)347 vfmng_get_interval (GimvAnim *anim)
348 {
349 MNGAnim *mng_anim;
350
351 g_return_val_if_fail (anim, -1);
352
353 mng_anim = anim->anim;
354
355 return mng_anim->delay;
356 }
357
358
359 static void
vfmng_delete(GimvAnim * anim)360 vfmng_delete (GimvAnim *anim)
361 {
362 MNGAnim *mng_anim;
363
364 g_return_if_fail (anim);
365
366 mng_anim = anim->anim;
367
368 mng_anim_delete (mng_anim);
369 anim->anim = NULL;
370 }
371
372
373
374 /****************************************************************************
375 *
376 * libmng callbacks
377 *
378 ****************************************************************************/
379 static mng_bool
mymng_error(mng_handle mng,mng_int32 code,mng_int8 severity,mng_chunkid chunktype,mng_uint32 chunkseq,mng_int32 extra1,mng_int32 extra2,mng_pchar text)380 mymng_error (mng_handle mng,
381 mng_int32 code,
382 mng_int8 severity,
383 mng_chunkid chunktype,
384 mng_uint32 chunkseq,
385 mng_int32 extra1,
386 mng_int32 extra2,
387 mng_pchar text)
388 {
389 GimvAnim *anim;
390 MNGAnim *mng_anim;
391 char chunk[5];
392
393 anim = mng_get_userdata (mng);
394 mng_anim = anim->anim;
395
396 anim = mng_get_userdata (mng);
397
398 /* pull out the chuck type as a string */
399 chunk[0] = (char)((chunktype >> 24) & 0xFF);
400 chunk[1] = (char)((chunktype >> 16) & 0xFF);
401 chunk[2] = (char)((chunktype >> 8) & 0xFF);
402 chunk[3] = (char)((chunktype ) & 0xFF);
403 chunk[4] = '\0';
404
405 /* output the error */
406 fprintf (stderr, "error playing '%s' chunk %s (%d):\n",
407 mng_anim->filename, chunk, chunkseq);
408 fprintf (stderr, "%s\n", text);
409
410 return 0;
411 }
412
413
414 static mng_ptr
mymng_malloc_callback(mng_size_t how_many)415 mymng_malloc_callback (mng_size_t how_many)
416 {
417 return (mng_ptr) g_new0 (guchar, how_many);
418 }
419
420
421 static void
mymng_free_callback(mng_ptr pointer,mng_size_t number)422 mymng_free_callback (mng_ptr pointer, mng_size_t number)
423 {
424 g_free (pointer);
425 }
426
427
428 static mng_bool
mymng_open_stream(mng_handle mng)429 mymng_open_stream(mng_handle mng)
430 {
431 GimvAnim *anim;
432 MNGAnim *mng_anim;
433
434 anim = mng_get_userdata (mng);
435 mng_anim = anim->anim;
436
437 /* open the file */
438 mng_anim->file = gimv_io_new (mng_anim->filename, "rb");
439 if (!mng_anim->file) {
440 g_print ("unable to open '%s'\n", mng_anim->filename);
441 return MNG_FALSE;
442 }
443
444 return MNG_TRUE;
445 }
446
447
448 static mng_bool
mymng_close_stream(mng_handle mng)449 mymng_close_stream (mng_handle mng)
450 {
451 GimvAnim *anim;
452 MNGAnim *mng_anim;
453
454 anim = mng_get_userdata (mng);
455 mng_anim = anim->anim;
456
457 /* close the file */
458 if (mng_anim->file)
459 gimv_io_close (mng_anim->file);
460 mng_anim->file = NULL;
461
462 return MNG_TRUE;
463 }
464
465
466 static mng_bool
mymng_read_stream(mng_handle mng,mng_ptr buffer,mng_uint32 size,mng_uint32 * bytesread)467 mymng_read_stream(mng_handle mng,
468 mng_ptr buffer,
469 mng_uint32 size,
470 mng_uint32 *bytesread)
471 {
472 GimvAnim *anim;
473 MNGAnim *mng_anim;
474
475 anim = mng_get_userdata (mng);
476 mng_anim = anim->anim;
477
478 /* read the requested amount of data from the file */
479 gimv_io_read (mng_anim->file, buffer, size, bytesread);
480
481 return MNG_TRUE;
482 }
483
484
485 static mng_uint32
mymng_get_ticks(mng_handle mng)486 mymng_get_ticks (mng_handle mng)
487 {
488 gdouble seconds;
489 gulong microseconds;
490 GimvAnim *anim;
491 MNGAnim *mng_anim;
492
493 anim = mng_get_userdata (mng);
494 mng_anim = anim->anim;
495
496 if (mng_anim->timer)
497 seconds = g_timer_elapsed (mng_anim->timer,
498 µseconds);
499 else
500 return 0;
501
502 return ((mng_uint32) (seconds * 1000.0 + ((gdouble) microseconds) / 1000.0));
503 }
504
505 static mng_bool
mymng_set_timer(mng_handle mng,mng_uint32 msecs)506 mymng_set_timer (mng_handle mng, mng_uint32 msecs)
507 {
508 GimvAnim *anim;
509 MNGAnim *mng_anim;
510
511 anim = mng_get_userdata (mng);
512 mng_anim = anim->anim;
513
514 mng_anim->delay = msecs;
515
516 return MNG_TRUE;
517 }
518
519
520 static mng_bool
mymng_process_header(mng_handle mng,mng_uint32 width,mng_uint32 height)521 mymng_process_header (mng_handle mng,
522 mng_uint32 width,
523 mng_uint32 height)
524 {
525 GimvAnim *anim;
526 MNGAnim *mng_anim;
527
528 anim = mng_get_userdata (mng);
529 mng_anim = anim->anim;
530
531 if (mng_anim->canvas)
532 g_free (mng_anim->canvas);
533
534 mng_anim->canvas
535 = g_new0 (guchar, width * height * mng_anim->bytes_per_pixel);
536
537 mng_anim->width = width;
538 mng_anim->height = height;
539 mng_set_canvasstyle (mng, MNG_CANVAS_RGB8); /* FIXME */
540 mng_set_bgcolor (mng, 65535, 65535, 65535);
541
542 if (mng_anim->timer)
543 g_timer_destroy (mng_anim->timer);
544 mng_anim->timer = g_timer_new ();
545 g_timer_start (mng_anim->timer);
546
547 return MNG_TRUE;
548 }
549
550
551 static mng_ptr
mymng_get_canvas_line(mng_handle mng,mng_uint32 line)552 mymng_get_canvas_line (mng_handle mng, mng_uint32 line)
553 {
554 GimvAnim *anim;
555 MNGAnim *mng_anim;
556 mng_ptr row;
557
558 anim = mng_get_userdata (mng);
559 mng_anim = anim->anim;
560
561 if (!mng_anim->canvas)
562 return NULL;
563
564 row = ((char *)mng_anim->canvas)
565 + mng_anim->bytes_per_pixel * mng_anim->width * line;
566
567 return row;
568 }
569
570
571 mng_bool
mymng_refresh(mng_handle mng,mng_uint32 x,mng_uint32 y,mng_uint32 w,mng_uint32 h)572 mymng_refresh (mng_handle mng,
573 mng_uint32 x, mng_uint32 y,
574 mng_uint32 w, mng_uint32 h)
575 {
576 GimvAnim *anim;
577 GimvImage *image;
578 MNGAnim *mng_anim;
579 gint bytes;
580 GimvImageAngle angle;
581
582 anim = mng_get_userdata (mng);
583 image = (GimvImage *) anim;
584 mng_anim = anim->anim;
585
586 if (!mng_anim->canvas)
587 return MNG_FALSE;
588
589 bytes = sizeof (guchar)
590 * mng_anim->width * mng_anim->height
591 * mng_anim->bytes_per_pixel;
592 gimv_anim_update_frame (anim, g_memdup (mng_anim->canvas, bytes),
593 mng_anim->width, mng_anim->height, FALSE);
594
595 /* restore angle */
596 angle = image->angle;
597 image->angle = 0;
598 gimv_image_rotate (image, angle);
599
600 return MNG_TRUE;
601 }
602
603
604 #endif /* ENABLE_MNG */
605