replace g_memdup() to g_memdup2()
[platform/core/multimedia/libmm-utility.git] / gif / mm_util_gif.c
1 /*
2  * libmm-utility
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Vineeth T M <vineeth.tm@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include <string.h>
23 #include <gio/gio.h>
24 #include <gif_lib.h>
25
26 #include "mm_util_gif.h"
27 #include "mm_util_private.h"
28
29 #include <limits.h>
30
31 #define COLORMAP_FREE(map)                      { if (map != NULL) { GifFreeMapObject(map); map = NULL; } }
32 #define GRAPHIC_EXT_BLOCK_SIZE  4
33
34 typedef struct {
35         void *buf;
36         size_t buf_size;
37         size_t pos;
38 } gif_io_buf_s;
39
40 typedef struct {
41         GifFileType *GifFile;                     /**< GifFile opened */
42         char *filename;
43         unsigned int loop_count;
44         void **enc_buffer;                        /**< Encoded output data attached to callback */
45         size_t *enc_buffer_size;
46         gif_io_buf_s io_buf;
47 } gif_file_s;
48
49 static int __read_function(GifFileType *gft, GifByteType *data, int size)
50 {
51         gif_io_buf_s *io_buf = (gif_io_buf_s *) gft->UserData;
52
53         mm_util_retvm_if(!io_buf || !io_buf->buf || io_buf->buf_size == 0, 0, "Invalid io_buf");
54         mm_util_retvm_if(!data, 0, "Invalid data");
55         mm_util_retvm_if(size <= 0, 0, "Invalid size [%d]", size);
56         mm_util_retvm_if(io_buf->pos + size > io_buf->buf_size, 0, "Invalid read due to buffer overflow [%zu/%d/%zu].", io_buf->pos, size, io_buf->buf_size);
57
58         memcpy(data, io_buf->buf + io_buf->pos, size);
59         io_buf->pos += size;
60
61         return size;
62 }
63
64 static int __gif_open(const char *file_path, void *memory, const size_t src_size, gif_io_buf_s *io_buf, GifFileType **gif_image)
65 {
66         mm_util_retvm_if(!MMUTIL_STRING_VALID(file_path) && (memory == NULL), MM_UTIL_ERROR_INVALID_PARAMETER, "invalid gif image");
67
68         if (MMUTIL_STRING_VALID(file_path)) {
69                 mm_util_sec_debug("read from file [%s]", file_path);
70                 if ((*gif_image = DGifOpenFileName(file_path, NULL)) == NULL) {
71                         mm_util_error("could not open Gif File");
72                         return MM_UTIL_ERROR_INVALID_OPERATION;
73                 }
74         } else {
75                 mm_util_debug("read from memory");
76                 if ((*gif_image = DGifOpen(io_buf, __read_function, NULL)) == NULL) {
77                         mm_util_error("could not open Gif File");
78                         return MM_UTIL_ERROR_INVALID_OPERATION;
79                 }
80         }
81
82         return MM_UTIL_ERROR_NONE;
83 }
84
85 static void __gif_free_frame_buffer(GifRowType *frame_buffer, unsigned int size)
86 {
87         int idx = 0;
88
89         if (!frame_buffer)
90                 return;
91
92         for (idx = 0; idx < size; idx++)
93                 g_free(frame_buffer[idx]);
94
95         g_free(frame_buffer);
96 }
97
98 static int __gif_generate_frame_buffer(GifFileType *gif_image, GifRowType **screen_buf)
99 {
100         int idx = 0;
101         size_t row_size = 0;
102         GifRowType* _screen_buf = NULL;
103
104         _screen_buf = g_new0(GifRowType, gif_image->SHeight);
105
106         row_size = gif_image->SWidth * sizeof(GifPixelType);    /* Size in bytes one row. */
107         _screen_buf[0] = g_malloc0(row_size);
108
109         for (idx = 0; idx < (int)(gif_image->SWidth); idx++)    /* Set its color to BackGround. */
110                 _screen_buf[0][idx] = gif_image->SBackGroundColor;
111
112         for (idx = 1; idx < (int)(gif_image->SHeight); idx++) {
113                 /* Allocate the other rows, and set their color to background too: */
114                 _screen_buf[idx] = g_memdup2(_screen_buf[0], row_size);
115         }
116
117         *screen_buf = _screen_buf;
118
119         return MM_UTIL_ERROR_NONE;
120 }
121
122 static gboolean __gif_is_good_frame(GifFileType *gif_image)
123 {
124         mm_util_debug("Frame pos: %d, %d size: %dx%d", gif_image->Image.Left, gif_image->Image.Top, gif_image->Image.Width, gif_image->Image.Height);
125
126         if (gif_image->Image.Top < 0 || gif_image->Image.Left < 0 || gif_image->Image.Width <= 0 || gif_image->Image.Height <= 0) {
127                 mm_util_error("Frame has wrong dimensions");
128                 return FALSE;
129         }
130
131         if (gif_image->Image.Left + gif_image->Image.Width > gif_image->SWidth || gif_image->Image.Top + gif_image->Image.Height > gif_image->SHeight) {
132                 mm_util_debug("Frame is not full screen frame");
133                 return FALSE;
134         }
135
136         return TRUE;
137 }
138
139 static int __gif_fill_frame_buffer(GifFileType *gif_image, GifRowType *frame_buffer)
140 {
141         unsigned int i, j;
142
143         unsigned int Row = (unsigned int)gif_image->Image.Top;  /* Image Position relative to Screen. */
144         unsigned int Col = (unsigned int)gif_image->Image.Left;
145         unsigned int Width = (unsigned int)gif_image->Image.Width;
146         unsigned int Height = (unsigned int)gif_image->Image.Height;
147         unsigned int interlaced_offset[] = { 0, 4, 2, 1 }, interlaced_jumps[] = {
148         8, 8, 4, 2};
149
150         if (gif_image->Image.Interlace) {
151                 /* Need to perform 4 passes on the images: */
152                 for (i = 0; i < 4; i++)
153                         for (j = Row + interlaced_offset[i]; j < Row + Height; j += interlaced_jumps[i]) {
154                                 if (DGifGetLine(gif_image, &frame_buffer[j][Col], Width) == GIF_ERROR) {
155                                         mm_util_error("could not get line (%u)", gif_image->Error);
156                                         return MM_UTIL_ERROR_INVALID_OPERATION;
157                                 }
158                         }
159         } else {
160                 for (i = 0; i < Height; i++) {
161                         if (DGifGetLine(gif_image, &frame_buffer[Row++][Col], Width) == GIF_ERROR) {
162                                 mm_util_error("could not get line (%u)", gif_image->Error);
163                                 return MM_UTIL_ERROR_INVALID_OPERATION;
164                         }
165                 }
166         }
167
168         return MM_UTIL_ERROR_NONE;
169 }
170
171 static int __gif_get_extension(GifFileType *gif_image)
172 {
173         int ExtCode = 0;
174         GifByteType *extension = NULL;
175
176         /* for skip any extension blocks in file */
177         if (DGifGetExtension(gif_image, &ExtCode, &extension) == GIF_ERROR) {
178                 mm_util_error("could not get extension (%u)", gif_image->Error);
179                 return MM_UTIL_ERROR_INVALID_OPERATION;
180         }
181         while (extension != NULL) {
182                 if (DGifGetExtensionNext(gif_image, &extension) == GIF_ERROR) {
183                         mm_util_error("could not get next extension (%u)", gif_image->Error);
184                         return MM_UTIL_ERROR_INVALID_OPERATION;
185                 }
186         }
187
188         return MM_UTIL_ERROR_NONE;
189 }
190
191 static int __gif_convert_to_rgba(void **data, ColorMapObject *color_map, GifRowType *frame_buffer, unsigned int width, unsigned int height)
192 {
193         unsigned int i, j;
194         GifRowType gif_row;
195         GifColorType *color_map_entry;
196         GifByteType *buffer;
197
198         mm_util_fenter();
199
200         *data = g_malloc0(width * height * 4);
201
202         buffer = (GifByteType *) *data;
203         for (i = 0; i < height; i++) {
204                 gif_row = frame_buffer[i];
205                 for (j = 0; j < width; j++) {
206                         color_map_entry = &color_map->Colors[gif_row[j]];
207                         *buffer++ = color_map_entry->Red;
208                         *buffer++ = color_map_entry->Green;
209                         *buffer++ = color_map_entry->Blue;
210                         *buffer++ = 255;
211                 }
212         }
213
214         return MM_UTIL_ERROR_NONE;
215 }
216
217 static int __read_gif(const char *file_path, void *memory, const size_t src_size, mm_util_image_h *decoded)
218 {
219         int ret = MM_UTIL_ERROR_NONE;
220
221         GifRecordType record_type = UNDEFINED_RECORD_TYPE;
222         GifRowType *frame_buffer = NULL;
223         GifFileType *GifFile;
224         gboolean is_found = FALSE;
225         ColorMapObject *ColorMap = NULL;
226         gif_io_buf_s io_buf = { memory, src_size, 0 };
227         void *image_buffer = NULL;
228
229         mm_util_retvm_if(!decoded, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid image handle");
230
231         mm_util_fenter();
232
233         ret = __gif_open(file_path, memory, src_size, &io_buf, &GifFile);
234         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_open failed");
235
236         mm_util_retvm_if(GifFile->SWidth <= 0 || GifFile->SHeight <= 0, MM_UTIL_ERROR_INVALID_OPERATION, "Gif File wrong decode width & height");
237
238         ret = __gif_generate_frame_buffer(GifFile, &frame_buffer);
239         if (ret != MM_UTIL_ERROR_NONE) {
240                 mm_util_debug("__gif_generate_frame_buffer failed");
241                 goto error;
242         }
243
244         /* Scan the content of the GIF file and load the image(s) in: */
245         do {
246                 if (DGifGetRecordType(GifFile, &record_type) == GIF_ERROR) {
247                         mm_util_error("could not get record type (%u)", GifFile->Error);
248                         ret = MM_UTIL_ERROR_INVALID_OPERATION;
249                         goto error;
250                 }
251                 switch (record_type) {
252                 case IMAGE_DESC_RECORD_TYPE:
253                         /* get frame data */
254                         if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
255                                 mm_util_error("could not get image description");
256                                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
257                                 goto error;
258                         }
259
260                         if (!__gif_is_good_frame(GifFile)) {
261                                 mm_util_debug("Image is not confined to screen dimension, aborted.");
262                                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
263                                 goto error;
264                         }
265
266                         is_found = TRUE;
267
268                         ret = __gif_fill_frame_buffer(GifFile, frame_buffer);
269                         if (ret != MM_UTIL_ERROR_NONE) {
270                                 mm_util_error("__gif_generate_frame_buffer failed");
271                                 goto error;
272                         }
273                         break;
274                 case EXTENSION_RECORD_TYPE:
275                         /* currently, we don't use extension value, it needs to play animation */
276                         ret = __gif_get_extension(GifFile);
277                         if (ret != MM_UTIL_ERROR_NONE)
278                                 goto error;
279                         break;
280                 case TERMINATE_RECORD_TYPE:
281                         break;
282                 default:
283                         /* should be trapped by DGifGetRecordType. */
284                         break;
285                 }
286         } while (record_type != TERMINATE_RECORD_TYPE && !is_found);
287
288         ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
289         if (ColorMap == NULL) {
290                 mm_util_error("Gif Image does not have a colormap");
291                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
292                 goto error;
293         }
294
295         /* decompress image with colormap(256) */
296         ret = __gif_convert_to_rgba(&image_buffer, ColorMap, frame_buffer, GifFile->SWidth, GifFile->SHeight);
297         if (ret != MM_UTIL_ERROR_NONE) {
298                 mm_util_error("could not convert gif to rgba");
299                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
300                 goto error;
301         }
302
303         ret = mm_image_create_image(GifFile->SWidth, GifFile->SHeight, MM_UTIL_COLOR_RGBA, image_buffer, GifFile->SWidth * GifFile->SHeight * 4, decoded);
304         g_free(image_buffer);
305
306 error:
307         __gif_free_frame_buffer(frame_buffer, GifFile->SHeight);
308
309         if (DGifCloseFile(GifFile, NULL) == GIF_ERROR) {
310                 mm_util_error("could not close file");
311                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
312         }
313
314         mm_util_fleave();
315
316         return ret;
317 }
318
319 int mm_util_decode_from_gif_file(const char *file_path, mm_util_image_h *decoded)
320 {
321         mm_util_retvm_if(!MMUTIL_STRING_VALID(file_path), MM_UTIL_ERROR_INVALID_PARAMETER, "invalid file_path");
322
323         return __read_gif(file_path, NULL, 0, decoded);
324 }
325
326 int mm_util_decode_from_gif_memory(void *memory, const size_t src_size, mm_util_image_h *decoded)
327 {
328         mm_util_retvm_if(!memory, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid memory");
329         mm_util_retvm_if(!src_size, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid src_size");
330
331         return __read_gif(NULL, memory, src_size, decoded);
332 }
333
334 static int __write_function(GifFileType *gft, const GifByteType *data, int size)
335 {
336         gif_io_buf_s *io_buf = (gif_io_buf_s *) gft->UserData;
337
338         mm_util_retvm_if(!io_buf, 0, "Invalid io_buf");
339         mm_util_retvm_if(!data, 0, "Invalid data");
340         mm_util_retvm_if(size <= 0, 0, "Invalid size");
341
342         io_buf->buf = (void *)realloc(io_buf->buf, sizeof(GifByteType) * (io_buf->buf_size + size));
343         mm_util_retvm_if(!io_buf->buf, 0, "Memory allocation failed");
344
345         memcpy(io_buf->buf + io_buf->buf_size, data, size);
346         io_buf->buf_size += size;
347
348         return size;
349 }
350
351 static const int DEFAULT_COLORMAP_SIZE = (1 << 8);
352
353 static void __gif_extract_rgb(mm_image_info_s *gif_image, unsigned long num_of_pixels, GifByteType **red, GifByteType **green, GifByteType **blue)
354 {
355         GifWord i, j;
356         GifByteType *redP = NULL, *greenP = NULL, *blueP = NULL;
357         GifByteType *buffer = (GifByteType*)gif_image->data;
358
359         mm_util_fenter();
360
361         redP = g_new0(GifByteType, num_of_pixels);
362         greenP = g_new0(GifByteType, num_of_pixels);
363         blueP = g_new0(GifByteType, num_of_pixels);
364
365         *red = redP;
366         *green = greenP;
367         *blue = blueP;
368
369         for (i = 0; i < gif_image->height; i++) {
370                 for (j = 0; j < gif_image->width; j++) {
371                         *redP++ = *buffer++;
372                         *greenP++ = *buffer++;
373                         *blueP++ = *buffer++;
374                         buffer++;
375                 }
376         }
377 }
378
379 static int __gif_make_color_map(mm_image_info_s *gif_image, ColorMapObject **color_map, GifByteType **intermediate_image, unsigned long *intermediate_image_size)
380 {
381         int ret = GIF_OK;
382         int colormap_size = DEFAULT_COLORMAP_SIZE;
383         GifByteType *red = NULL, *green = NULL, *blue = NULL;
384         unsigned long num_of_pixels = 0;
385         ColorMapObject *gif_color_map = NULL;
386         GifByteType *out_buffer = NULL;
387
388         mm_util_retvm_if(!gif_image, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_image");
389         mm_util_retvm_if(!color_map, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid color_map");
390         mm_util_retvm_if(!intermediate_image, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid intermediate_image");
391         mm_util_retvm_if(!intermediate_image_size, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid intermediate_image_size");
392
393         num_of_pixels = gif_image->width * gif_image->height;
394
395         if ((gif_color_map = GifMakeMapObject(colormap_size, NULL)) == NULL) {
396                 mm_util_error("failed to make color map");
397                 return MM_UTIL_ERROR_INVALID_OPERATION;
398         }
399
400         __gif_extract_rgb(gif_image, num_of_pixels, &red, &green, &blue);
401
402         /* allocate output buffer */
403         out_buffer = g_new0(GifByteType, num_of_pixels);
404
405         ret = GifQuantizeBuffer(gif_image->width, gif_image->height, &colormap_size, red, green, blue, out_buffer, gif_color_map->Colors);
406         g_free(red);
407         g_free(green);
408         g_free(blue);
409
410         if (ret == GIF_ERROR) {
411                 mm_util_error("failed to quantize buffer");
412                 COLORMAP_FREE(gif_color_map);
413                 g_free(out_buffer);
414                 return MM_UTIL_ERROR_INVALID_OPERATION;
415         }
416
417         *color_map = gif_color_map;
418         *intermediate_image = out_buffer;
419         *intermediate_image_size = num_of_pixels * sizeof(GifByteType);
420
421         return MM_UTIL_ERROR_NONE;
422 }
423
424 static int __gif_encode_open_mem(gif_file_s *gif_file)
425 {
426         mm_util_fenter();
427         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
428
429         gif_file->io_buf.buf = NULL;
430         gif_file->io_buf.buf_size = 0;
431
432         if ((gif_file->GifFile = EGifOpen(&(gif_file->io_buf), __write_function, NULL)) == NULL) {
433                 mm_util_error("could not open File");
434                 return MM_UTIL_ERROR_INVALID_OPERATION;
435         }
436
437         return MM_UTIL_ERROR_NONE;
438 }
439
440 static int __gif_encode_close_file(GifFileType *gft)
441 {
442         int gif_error = E_GIF_SUCCEEDED;
443
444         mm_util_retvm_if(!gft, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
445
446         if (EGifCloseFile(gft, &gif_error) == GIF_ERROR) {
447                 mm_util_error("could not close file (%d) ", gif_error);
448                 return MM_UTIL_ERROR_INVALID_OPERATION;
449         }
450
451         return MM_UTIL_ERROR_NONE;
452 }
453
454 static int __write_gif_to_file(gif_file_s *handle, const char *path)
455 {
456         int ret = MM_UTIL_ERROR_NONE;
457         FILE *fp = NULL;
458
459         mm_util_retvm_if(!handle, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid handle");
460         mm_util_retvm_if((!handle->io_buf.buf || handle->io_buf.buf_size == 0), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid io_buf");
461         mm_util_retvm_if(!MMUTIL_STRING_VALID(path), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid path");
462
463         ret = mm_util_safe_fopen(path, "wb", &fp);
464         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_safe_fopen fail (%d)", ret);
465
466         ret = mm_util_safe_fwrite(fp, handle->io_buf.buf, handle->io_buf.buf_size);
467         if (ret != MM_UTIL_ERROR_NONE)
468                 mm_util_error("mm_util_safe_fwrite fail (%d)", ret);
469
470         mm_util_safe_fclose(fp);
471
472         return ret;
473 }
474
475 static int __write_gif_to_buffer(gif_file_s *handle, void **buffer, size_t *buffer_size)
476 {
477         mm_util_retvm_if(!handle, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid handle");
478         mm_util_retvm_if(!handle->io_buf.buf || handle->io_buf.buf_size == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid io_buf");
479         mm_util_retvm_if(!buffer || !buffer_size, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid buffer");
480
481         *buffer = g_memdup2(handle->io_buf.buf, handle->io_buf.buf_size);
482         *buffer_size = handle->io_buf.buf_size;
483
484         return MM_UTIL_ERROR_NONE;
485 }
486
487 static int __gif_image_write_image(gif_file_s *gif_file, mm_image_info_s *gif_image)
488 {
489         int ret = MM_UTIL_ERROR_NONE;
490         ColorMapObject *color_map = NULL;
491         GifByteType *intermediate_image = NULL;
492         unsigned long intermediate_image_size = 0;
493         unsigned long idx = 0;
494
495         mm_util_retvm_if(!gif_file, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file");
496         mm_util_retvm_if(!gif_image, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_image");
497         mm_util_retvm_if(!gif_image->data, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_image->data");
498
499         /* Make local color map */
500         ret = __gif_make_color_map(gif_image, &color_map, &intermediate_image, &intermediate_image_size);
501         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_make_color_map failed");
502
503         if (EGifPutImageDesc(gif_file->GifFile, 0, 0, gif_image->width, gif_image->height,
504                 FALSE, color_map) == GIF_ERROR) {
505                 mm_util_error("EGifPutImageDesc failed due to %s", GifErrorString(gif_file->GifFile->Error));
506                 COLORMAP_FREE(color_map);
507                 g_free(intermediate_image);
508                 return MM_UTIL_ERROR_INVALID_OPERATION;
509         }
510
511         COLORMAP_FREE(color_map);
512         mm_util_debug("pixel count: %lu", (intermediate_image_size / sizeof(GifPixelType)));
513
514         for (idx = 0; idx < (intermediate_image_size / sizeof(GifPixelType)); idx++) {
515                 GifPixelType pixel = (GifPixelType)intermediate_image[idx];
516                 if (EGifPutPixel(gif_file->GifFile, pixel) == GIF_ERROR) {
517                         mm_util_error("EGifPutPixel failed due to %s", GifErrorString(gif_file->GifFile->Error));
518                         ret = MM_UTIL_ERROR_INVALID_OPERATION;
519                         break;
520                 }
521         }
522
523         /* release intermediate_image */
524         g_free(intermediate_image);
525
526         return ret;
527 }
528
529 static int __gif_image_create_ext_block(int function, int byte_count, ExtensionBlock **ext_block)
530 {
531         ExtensionBlock *_ext_block = NULL;
532
533         mm_util_retvm_if(byte_count == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
534         mm_util_retvm_if(ext_block == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
535
536         /* get allocated extension block */
537         _ext_block = g_new0(ExtensionBlock, 1);
538
539         _ext_block->Function = function;
540         _ext_block->ByteCount = byte_count;
541         _ext_block->Bytes = g_new0(GifByteType, byte_count);
542
543         *ext_block = _ext_block;
544
545         return MM_UTIL_ERROR_NONE;
546 }
547
548 static void __gif_image_destroy_ext_block(ExtensionBlock *ext_block)
549 {
550         if (!ext_block)
551                 return;
552
553         g_free(ext_block->Bytes);
554         g_free(ext_block);
555 }
556
557 static int __gif_image_write_ext_block(gif_file_s *gif_file, unsigned int delay_time)
558 {
559         int ret = MM_UTIL_ERROR_NONE;
560         ExtensionBlock *_ext_block = NULL;
561         GraphicsControlBlock graphic_control_block;
562
563         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
564
565         ret = __gif_image_create_ext_block(GRAPHICS_EXT_FUNC_CODE, GRAPHIC_EXT_BLOCK_SIZE, &_ext_block);
566         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_image_create_ext_block failed");
567
568         /* use fixed graphics control */
569         graphic_control_block.DisposalMode = DISPOSAL_UNSPECIFIED;
570         graphic_control_block.UserInputFlag = FALSE;
571         graphic_control_block.TransparentColor = NO_TRANSPARENT_COLOR;
572         graphic_control_block.DelayTime = delay_time;
573
574         EGifGCBToExtension(&graphic_control_block, _ext_block->Bytes);
575
576         if (EGifPutExtension(gif_file->GifFile, _ext_block->Function, _ext_block->ByteCount, _ext_block->Bytes) == GIF_ERROR) {
577                 mm_util_error("EGifPutExtension failed due to %s", GifErrorString(gif_file->GifFile->Error));
578                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
579         }
580
581         /* release extension blocks */
582         __gif_image_destroy_ext_block(_ext_block);
583
584         return MM_UTIL_ERROR_NONE;
585 }
586
587 static int __gif_image_write_loop_count(gif_file_s *gif_file, unsigned int loop_count)
588 {
589 #define EXT_BLCOK_COUNT         2
590 #define APP_EXT_BLOCK_DATA      "NETSCAPE2.0"
591 #define APP_EXT_BLOCK_SIZE      strlen(APP_EXT_BLOCK_DATA)
592 #define SUB_BLOCK_SIZE  3
593         int ret = MM_UTIL_ERROR_NONE;
594         ExtensionBlock *app_ext_block = NULL;
595         ExtensionBlock *app_sub_block = NULL;
596
597         ret = __gif_image_create_ext_block(APPLICATION_EXT_FUNC_CODE, APP_EXT_BLOCK_SIZE, &app_ext_block);
598         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_image_create_ext_block failed");
599
600         memcpy(app_ext_block->Bytes, APP_EXT_BLOCK_DATA, APP_EXT_BLOCK_SIZE);
601
602         ret = __gif_image_create_ext_block(CONTINUE_EXT_FUNC_CODE, SUB_BLOCK_SIZE, &app_sub_block);
603         if (ret != MM_UTIL_ERROR_NONE) {
604                 mm_util_error("__gif_image_create_ext_block failed");
605                 goto END;
606         }
607
608         app_sub_block->Bytes[0] = 1;  /* index of sub block for netscape */
609         app_sub_block->Bytes[1] = loop_count & 0xff;
610         app_sub_block->Bytes[2] = (loop_count >> 8) & 0xff;
611
612         if (EGifPutExtensionLeader(gif_file->GifFile, app_ext_block->Function) == GIF_ERROR) {
613                 mm_util_error("EGifPutExtensionLeader failed");
614                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
615                 goto END;
616         }
617
618         if (EGifPutExtensionBlock(gif_file->GifFile, app_ext_block->ByteCount, app_ext_block->Bytes) == GIF_ERROR) {
619                 mm_util_error("EGifPutExtensionBlock(app_ext_block) failed");
620                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
621                 goto END;
622         }
623
624         if (EGifPutExtensionBlock(gif_file->GifFile, app_sub_block->ByteCount, app_sub_block->Bytes) == GIF_ERROR) {
625                 mm_util_error("EGifPutExtensionBlock(app_sub_block) failed");
626                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
627                 goto END;
628         }
629
630         if (EGifPutExtensionTrailer(gif_file->GifFile) == GIF_ERROR) {
631                 mm_util_error("EGifPutExtensionTrailer failed");
632                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
633                 goto END;
634         }
635
636 END:
637         __gif_image_destroy_ext_block(app_ext_block);
638         __gif_image_destroy_ext_block(app_sub_block);
639
640         return ret;
641 }
642
643 int mm_util_gif_encode_create(mm_gif_file_h *gif_file_h)
644 {
645         gif_file_s *gif_file = NULL;
646
647         mm_util_retvm_if(!gif_file_h, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
648
649         gif_file = g_new0(gif_file_s, 1);
650
651         gif_file->loop_count = 1;
652
653         *gif_file_h = (mm_gif_file_h)gif_file;
654
655         return MM_UTIL_ERROR_NONE;
656 }
657
658 int mm_util_gif_encode_set_loop_count(mm_gif_file_h gif_file_h, unsigned int loop_count)
659 {
660         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
661
662         mm_util_retvm_if(!gif_file, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
663
664         gif_file->loop_count = loop_count;
665
666         return MM_UTIL_ERROR_NONE;
667 }
668
669 static int __mm_util_gif_encode_start(mm_gif_file_h gif_file_h, unsigned int width, unsigned int height)
670 {
671         int ret = MM_UTIL_ERROR_NONE;
672         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
673
674         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
675         mm_util_retvm_if(gif_file->GifFile != NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started");
676
677         mm_util_fenter();
678
679         ret = __gif_encode_open_mem(gif_file);
680         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_encode_open_mem failed");
681
682         /* To initialize data after GifFile opened */
683         EGifSetGifVersion(gif_file->GifFile, TRUE);
684
685         /* Write screen description */
686         if (EGifPutScreenDesc(gif_file->GifFile, width, height, 8 /* color_res */, 0 /* background_color */, NULL) == GIF_ERROR) {
687                 mm_util_error("could not put screen description");
688                 __gif_encode_close_file(gif_file->GifFile);
689                 return MM_UTIL_ERROR_INVALID_OPERATION;
690         }
691
692         /* Write extension block for loop count */
693         ret = __gif_image_write_loop_count(gif_file, gif_file->loop_count);
694         if (ret != MM_UTIL_ERROR_NONE) {
695                 __gif_encode_close_file(gif_file->GifFile);
696                 mm_util_error("__gif_image_write_loop_count failed");
697         }
698
699         mm_util_fleave();
700         return ret;
701 }
702
703 int mm_util_gif_encode_add_image(mm_gif_file_h gif_file_h, mm_image_info_s *gif_image_h)
704 {
705         int ret = MM_UTIL_ERROR_NONE;
706         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
707         mm_image_info_s *gif_image = (mm_image_info_s *)gif_image_h;
708
709         mm_util_fenter();
710         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
711         mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
712         mm_util_retvm_if((gif_image->width == 0) || (gif_image->height == 0), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
713         mm_util_retvm_if(gif_image->data == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
714         mm_util_retvm_if(gif_image->color != MM_UTIL_COLOR_RGBA, MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT, "not supported color [%d]", gif_image->color);
715
716         if (gif_file->GifFile == NULL) {
717                 mm_util_warn("first added image, __mm_util_gif_encode_start is needed");
718                 ret = __mm_util_gif_encode_start(gif_file_h, gif_image->width, gif_image->height);
719                 mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__mm_util_gif_encode_start failed");
720         }
721
722         /* Write graphic control block */
723         ret = __gif_image_write_ext_block(gif_file, gif_image->delay_time / 10);
724         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_image_write_ext_block failed");
725
726         /* Write image description & data */
727         ret = __gif_image_write_image(gif_file, gif_image);
728         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_image_write_image failed");
729
730         gif_file->GifFile->ImageCount++;
731
732         return MM_UTIL_ERROR_NONE;
733 }
734
735 int mm_util_gif_encode_save_to_file(mm_gif_file_h gif_file_h, const char *path)
736 {
737         int ret = MM_UTIL_ERROR_NONE;
738         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
739
740         mm_util_retvm_if(!gif_file_h, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
741         mm_util_retvm_if(!MMUTIL_STRING_VALID(path), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid path");
742         mm_util_retvm_if(!gif_file->GifFile, MM_UTIL_ERROR_INVALID_PARAMETER, "Encoding has not started");
743         mm_util_retvm_if(gif_file->GifFile->ImageCount <= 0, MM_UTIL_ERROR_INVALID_PARAMETER, "No frame has encoded");
744
745         mm_util_fenter();
746
747         ret = __gif_encode_close_file(gif_file->GifFile);
748         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_encode_close_file failed");
749         gif_file->GifFile = NULL;
750
751         ret = __write_gif_to_file(gif_file, path);
752         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__write_gif_to_file failed (%d)", ret);
753
754         MMUTIL_SAFE_FREE(gif_file->io_buf.buf);
755         gif_file->io_buf.buf_size = 0;
756
757         return ret;
758 }
759
760 int mm_util_gif_encode_save_to_buffer(mm_gif_file_h gif_file_h, void **buffer, size_t *buffer_size)
761 {
762         int ret = MM_UTIL_ERROR_NONE;
763         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
764
765         mm_util_retvm_if(!gif_file_h, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
766         mm_util_retvm_if(!buffer || !buffer_size, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid buffer");
767         mm_util_retvm_if(!gif_file->GifFile, MM_UTIL_ERROR_INVALID_PARAMETER, "Encoding has not started");
768         mm_util_retvm_if(gif_file->GifFile->ImageCount <= 0, MM_UTIL_ERROR_INVALID_PARAMETER, "No frame has encoded");
769
770         mm_util_fenter();
771
772         ret = __gif_encode_close_file(gif_file->GifFile);
773         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_encode_close_file failed");
774         gif_file->GifFile = NULL;
775
776         ret = __write_gif_to_buffer(gif_file, buffer, buffer_size);
777         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__write_gif_to_buffer failed (%d)", ret);
778
779         MMUTIL_SAFE_FREE(gif_file->io_buf.buf);
780         gif_file->io_buf.buf_size = 0;
781
782         return ret;
783 }
784
785 int mm_util_encode_to_gif_file(mm_util_image_h *images, const unsigned int image_count, const char *file_path)
786 {
787         int ret = MM_UTIL_ERROR_NONE;
788         mm_image_info_s **_images = (mm_image_info_s **)images;
789         int i = 0;
790         mm_gif_file_h gif_file_h = NULL;
791
792         mm_util_fenter();
793
794         mm_util_retvm_if(images == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid images");
795         mm_util_retvm_if(image_count == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid image_count");
796         mm_util_retvm_if(!MMUTIL_STRING_VALID(file_path), MM_UTIL_ERROR_INVALID_PARAMETER, "invalid file_path");
797
798         ret = mm_util_gif_encode_create(&gif_file_h);
799         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_create failed %d", ret);
800
801         for (i = 0; i < image_count; i++) {
802                 ret = mm_util_gif_encode_add_image(gif_file_h, _images[i]);
803                 if (ret != MM_UTIL_ERROR_NONE) {
804                         mm_util_error("mm_util_gif_encode_add_image failed");
805                         mm_util_gif_encode_destroy(gif_file_h);
806                         return ret;
807                 }
808         }
809
810         ret = mm_util_gif_encode_save_to_file(gif_file_h, file_path);
811         if (ret != MM_UTIL_ERROR_NONE)
812                 mm_util_error("mm_util_gif_encode_save_to_file failed (%d)", ret);
813
814         mm_util_gif_encode_destroy(gif_file_h);
815
816         mm_util_fleave();
817
818         return ret;
819 }
820
821 int mm_util_encode_to_gif_memory(mm_util_image_h *images, const unsigned int image_count, void **buffer, size_t *size)
822 {
823         int ret = MM_UTIL_ERROR_NONE;
824         mm_image_info_s **_images = (mm_image_info_s **)images;
825         int i = 0;
826         mm_gif_file_h gif_file_h = NULL;
827
828         mm_util_fenter();
829
830         mm_util_retvm_if(images == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid images");
831         mm_util_retvm_if(image_count == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid image_count");
832         mm_util_retvm_if(buffer == NULL || size == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
833
834         ret = mm_util_gif_encode_create(&gif_file_h);
835         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_create failed %d", ret);
836
837         for (i = 0; i < image_count; i++) {
838                 ret = mm_util_gif_encode_add_image(gif_file_h, _images[i]);
839                 if (ret != MM_UTIL_ERROR_NONE) {
840                         mm_util_error("mm_util_gif_encode_add_image failed");
841                         mm_util_gif_encode_destroy(gif_file_h);
842                         return ret;
843                 }
844         }
845
846         ret = mm_util_gif_encode_save_to_buffer(gif_file_h, buffer, size);
847         if (ret != MM_UTIL_ERROR_NONE)
848                 mm_util_error("mm_util_gif_encode_save_to_buffer failed (%d)", ret);
849
850         mm_util_gif_encode_destroy(gif_file_h);
851
852         mm_util_fleave();
853
854         return ret;
855 }
856
857 void mm_util_gif_encode_destroy(mm_gif_file_h gif_file_h)
858 {
859         int ret = MM_UTIL_ERROR_NONE;
860         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
861
862         mm_util_retm_if(gif_file == NULL, "Invalid parameter");
863
864         if (gif_file->GifFile) {
865                 ret = __gif_encode_close_file(gif_file->GifFile);
866                 mm_util_retm_if(ret != MM_UTIL_ERROR_NONE, "__gif_encode_close_file failed");
867         }
868
869         g_free(gif_file->filename);
870         MMUTIL_SAFE_FREE(gif_file->io_buf.buf);
871         g_free(gif_file);
872 }
873
874 anim_enc_module_t *anim_enc_module_register(void)
875 {
876         anim_enc_module_t *module = NULL;
877
878         mm_util_fenter();
879
880         module = g_new0(anim_enc_module_t, 1);
881
882         module->create = (ANIM_ENC_CREATE)mm_util_gif_encode_create;
883         module->set_bgcolor = NULL;
884         module->set_loop_count = (ANIM_ENC_SET_LOOP_COUNT)mm_util_gif_encode_set_loop_count;
885         module->set_lossless = NULL;
886         module->add_image = (ANIM_ENC_ADD_IMAGE)mm_util_gif_encode_add_image;
887         module->save_to_file = (ANIM_ENC_SAVE_TO_FILE)mm_util_gif_encode_save_to_file;
888         module->save_to_buffer = (ANIM_ENC_SAVE_TO_BUFFER)mm_util_gif_encode_save_to_buffer;
889         module->destroy = (ANIM_ENC_DESTROY)mm_util_gif_encode_destroy;
890
891         mm_util_fleave();
892
893         return module;
894 }