8 #include "ygdkmngloader.h" 15 #include <gdk-pixbuf/gdk-pixbuf.h> 18 #define MNG_UINT_MHDR 0x4d484452L 19 #define MNG_UINT_BACK 0x4241434bL 20 #define MNG_UINT_PLTE 0x504c5445L 21 #define MNG_UINT_tRNS 0x74524e53L 22 #define MNG_UINT_IHDR 0x49484452L 23 #define MNG_UINT_IDAT 0x49444154L 24 #define MNG_UINT_IEND 0x49454e44L 25 #define MNG_UINT_MEND 0x4d454e44L 26 #define MNG_UINT_FRAM 0x4652414dL 27 #define MNG_UINT_LOOP 0x4c4f4f50L 28 #define MNG_UINT_ENDL 0x454e444cL 29 #define MNG_UINT_TERM 0x5445524dL 38 static DataStream data_stream_constructor (
const guint8 *raw_data,
long size)
44 static gboolean read_signature (
DataStream *data)
46 if (data->offset+8 > data->size)
49 return memcmp (data->data + data->offset-8,
"\212MNG\r\n\032\n", 8) == 0;
52 static gboolean read_uint8 (
DataStream *data, guint8 *value)
54 if (data->offset+1 > data->size)
56 *value = data->data [data->offset++];
60 static gboolean read_uint32 (
DataStream *data, guint32 *value)
62 if (data->offset+4 > data->size)
64 *value = data->data[data->offset+0] << 24;
65 *value |= data->data[data->offset+1] << 16;
66 *value |= data->data[data->offset+2] << 8;
67 *value |= data->data[data->offset+3];
72 static gboolean read_data (
DataStream *data, guint32 size, GdkPixbufLoader *loader,
75 if (data->offset+size > data->size)
77 g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
78 "Unexpected end of file when reading PNG chunk");
82 ret = gdk_pixbuf_loader_write (loader, data->data + data->offset, size, error);
87 static void image_prepared_cb (GdkPixbufLoader *loader,
YGdkMngPixbuf *mng_pixbuf)
89 GdkPixbuf *pix = gdk_pixbuf_loader_get_pixbuf (loader);
90 mng_pixbuf->frames = g_list_append (mng_pixbuf->frames, pix);
95 G_DEFINE_TYPE (
YGdkMngPixbuf, ygdk_mng_pixbuf, GDK_TYPE_PIXBUF_ANIMATION)
101 gboolean ygdk_mng_pixbuf_is_file_mng (
const gchar *filename)
103 FILE *file = fopen (filename,
"rb");
105 goto is_file_mng_failed;
108 if (fread (raw_data, 1, 8, file) < 8)
109 goto is_file_mng_failed;
111 gboolean ret = ygdk_mng_pixbuf_is_data_mng (raw_data, 8);
121 gboolean ygdk_mng_pixbuf_is_data_mng (
const guint8 *raw_data,
long size)
123 DataStream data = data_stream_constructor (raw_data, size);
124 return read_signature (&data);
128 #define SET_ERROR(msg) { error = TRUE; (void) error; \ 129 g_set_error (error_msg, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, msg); } 131 GdkPixbufAnimation *ygdk_mng_pixbuf_new_from_file (
const gchar *filename,
134 gboolean error = FALSE;
135 FILE *file = fopen (filename,
"rb");
138 SET_ERROR (
"Could not open specified file")
142 fseek (file, 0, SEEK_END);
143 long file_size = ftell (file);
144 fseek (file, 0, SEEK_SET);
146 GdkPixbufAnimation *mng_pixbuf = 0;
147 guchar *data = mmap (NULL, file_size, PROT_READ, MAP_PRIVATE,
149 if (data == MAP_FAILED)
150 SET_ERROR ("Could not map file")
152 mng_pixbuf = ygdk_mng_pixbuf_new_from_data (data, file_size, error_msg);
153 munmap (data, file_size);
159 GdkPixbufAnimation *ygdk_mng_pixbuf_new_from_data (
const guint8 *raw_data,
long size,
162 DataStream data = data_stream_constructor (raw_data, size);
164 gboolean error = FALSE;
165 if (!read_signature (&data)) {
166 SET_ERROR (
"Not a MNG file")
170 YGdkMngPixbuf *mng_pixbuf = g_object_new (YGDK_TYPE_MNG_PIXBUF, NULL);
171 mng_pixbuf->iteration_max = 0x7fffffff;
173 guint32 chunk_size, chunk_id;
175 GdkPixbufLoader *loader = NULL;
176 gboolean first_read = TRUE;
179 error = !read_uint32 (&data, &chunk_size);
180 error = error || !read_uint32 (&data, &chunk_id);
182 SET_ERROR (
"Unexpected end of file on new chunk")
185 chunk_offset = data.offset + chunk_size + 4;
187 if (first_read && chunk_id != MNG_UINT_MHDR)
189 SET_ERROR (
"MHDR chunk must come first")
201 SET_ERROR (
"Only one MHDR chunk allowed")
205 if (chunk_size == 7*4)
208 error = !read_uint32 (&data, &mng_pixbuf->frame_width);
209 error = error || !read_uint32 (&data, &mng_pixbuf->frame_height);
210 error = error || !read_uint32 (&data, &mng_pixbuf->ticks_per_second);
212 SET_ERROR (
"Unexpected end of file on MHDR chunk")
215 else if (mng_pixbuf->frame_width <= 0 ||
216 mng_pixbuf->frame_height <= 0 ||
217 mng_pixbuf->ticks_per_second < 0)
218 SET_ERROR ("Invalid MHDR parameter")
222 SET_ERROR ("MHDR chunk must be 28 bytes
long")
225 loader = gdk_pixbuf_loader_new_with_type ("png", NULL);
226 g_signal_connect (G_OBJECT (loader), "area-prepared",
227 G_CALLBACK (image_prepared_cb), mng_pixbuf);
228 gdk_pixbuf_loader_set_size (loader, mng_pixbuf->frame_width,
229 mng_pixbuf->frame_height);
232 const guchar sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
233 if(!gdk_pixbuf_loader_write (loader, sig, 8, error_msg))
245 error = !read_uint8 (&data, &t);
246 if (t == 3 && chunk_size == 2+8)
248 error = error || !read_uint8 (&data, &t);
249 error = error || !read_uint32 (&data, &mng_pixbuf->last_frame_delay);
250 error = error || !read_uint32 (&data, &mng_pixbuf->iteration_max);
252 SET_ERROR (
"Unexpected end of file on TERM chunk")
255 SET_ERROR (
"TERM chunk must be 10 bytes when term action is 3")
258 SET_ERROR (
"TERM chunk must have at least 1 byte")
266 SET_ERROR ("Corrupted PNG chunk closures")
281 if (!read_data (&data, chunk_size+8+4, loader, error_msg))
283 else if (chunk_id == MNG_UINT_IEND)
285 if (!gdk_pixbuf_loader_close (loader, error_msg))
294 data.offset = chunk_offset;
296 }
while (chunk_id != MNG_UINT_MEND && !error);
300 g_object_unref (G_OBJECT (mng_pixbuf));
303 return GDK_PIXBUF_ANIMATION (mng_pixbuf);
308 static gboolean ygdk_mng_pixbuf_is_static_image (GdkPixbufAnimation *anim)
311 return g_list_length (mng_anim->frames) == 1;
314 static GdkPixbuf *ygdk_mng_pixbuf_get_static_image (GdkPixbufAnimation *anim)
317 return g_list_nth_data (mng_anim->frames, 0);
320 static void ygdk_mng_pixbuf_get_size (GdkPixbufAnimation *anim,
int *width,
int *height)
323 if (width) *width = mng_anim->frame_width;
324 if (height) *height = mng_anim->frame_height;
327 static GdkPixbufAnimationIter *ygdk_mng_pixbuf_get_iter (GdkPixbufAnimation *anim,
328 const GTimeVal *start_time)
331 iter->mng_pixbuf = YGDK_MNG_PIXBUF( anim );
333 return GDK_PIXBUF_ANIMATION_ITER( iter );
338 ygdk_mng_pixbuf_parent_class = g_type_class_peek_parent (klass);
340 GdkPixbufAnimationClass *pixbuf_class = GDK_PIXBUF_ANIMATION_CLASS (klass);
341 pixbuf_class->is_static_image = ygdk_mng_pixbuf_is_static_image;
342 pixbuf_class->get_static_image = ygdk_mng_pixbuf_get_static_image;
343 pixbuf_class->get_size = ygdk_mng_pixbuf_get_size;
344 pixbuf_class->get_iter = ygdk_mng_pixbuf_get_iter;
349 G_DEFINE_TYPE (
YGdkMngPixbufIter, ygdk_mng_pixbuf_iter, GDK_TYPE_PIXBUF_ANIMATION_ITER)
355 static GdkPixbuf *ygdk_mng_pixbuf_iter_get_pixbuf (GdkPixbufAnimationIter *iter)
358 return g_list_nth_data (mng_iter->mng_pixbuf->frames, mng_iter->cur_frame);
361 static gboolean ygdk_mng_pixbuf_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter)
366 static int ygdk_mng_pixbuf_iter_get_delay_time (GdkPixbufAnimationIter *iter)
369 int delay = 1000.0 / mng_iter->mng_pixbuf->ticks_per_second;
370 if (mng_iter->cur_frame == g_list_length (mng_iter->mng_pixbuf->frames)-1)
371 delay += mng_iter->mng_pixbuf->last_frame_delay;
375 static gboolean ygdk_mng_pixbuf_iter_advance (GdkPixbufAnimationIter *iter,
376 const GTimeVal *current_time)
380 if (!mng_pixbuf->frames)
383 gboolean can_advance = TRUE;
384 int frames_len = g_list_length (mng_pixbuf->frames);
385 if (mng_iter->cur_frame+1 == frames_len)
387 if (mng_pixbuf->iteration_max == 0x7fffffff ||
388 mng_iter->cur_iteration < mng_pixbuf->iteration_max)
389 mng_iter->cur_iteration++;
395 mng_iter->cur_frame = (mng_iter->cur_frame+1) % frames_len;
401 ygdk_mng_pixbuf_iter_parent_class = g_type_class_peek_parent (klass);
403 GdkPixbufAnimationIterClass *iter_class = GDK_PIXBUF_ANIMATION_ITER_CLASS (klass);
404 iter_class->get_delay_time = ygdk_mng_pixbuf_iter_get_delay_time;
405 iter_class->get_pixbuf = ygdk_mng_pixbuf_iter_get_pixbuf;
406 iter_class->on_currently_loading_frame = ygdk_mng_pixbuf_iter_on_currently_loading_frame;
407 iter_class->advance = ygdk_mng_pixbuf_iter_advance;