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                                  &microseconds);
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