4844320ecdab6209698e079d4752bed329d176c3
[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_gif_private.h"
28 #include "mm_util_private.h"
29
30 #include <limits.h>
31 #define GIF_TMP_FILE    "/tmp/.libmm_gif.gif"
32
33 #define COLORMAP_FREE(map)                      { if (map != NULL) { GifFreeMapObject(map); map = NULL; } }
34 #define RGB_ALLOC(r, g, b, number, size)        { r = calloc(1, number * size); g = calloc(1, number * size); b = calloc(1, number * size); }
35 #define RGB_FREE(r, g, b)                                       { MMUTIL_SAFE_FREE(r); MMUTIL_SAFE_FREE(g); MMUTIL_SAFE_FREE(b); }
36
37 #define GRAPHIC_EXT_BLOCK_SIZE  4
38
39 static int __convert_gif_to_rgba(void **data, ColorMapObject *color_map, GifRowType *screen_buffer, unsigned long width, unsigned long height)
40 {
41         unsigned long i, j;
42         GifRowType gif_row;
43         GifColorType *color_map_entry;
44         GifByteType *buffer;
45
46         mm_util_fenter();
47
48         if ((*data = (void *)calloc(1, width * height * 4)) == NULL) {
49                 mm_util_error("Failed to allocate memory required, aborted.");
50                 return MM_UTIL_ERROR_OUT_OF_MEMORY;
51         }
52
53         buffer = (GifByteType *) *data;
54         for (i = 0; i < height; i++) {
55                 gif_row = screen_buffer[i];
56                 for (j = 0; j < width; j++) {
57                         color_map_entry = &color_map->Colors[gif_row[j]];
58                         *buffer++ = color_map_entry->Red;
59                         *buffer++ = color_map_entry->Green;
60                         *buffer++ = color_map_entry->Blue;
61                         *buffer++ = 255;
62                 }
63         }
64         return MM_UTIL_ERROR_NONE;
65 }
66
67 static int __read_function(GifFileType *gft, GifByteType *data, int size)
68 {
69         gif_mem_s *read_data_ptr = (gif_mem_s *) gft->UserData;
70
71         mm_util_retvm_if(size <= 0, 0, "Failed to read memory due to size(%d).", size);
72         mm_util_retvm_if(read_data_ptr == NULL, 0, "Failed to read memory due to invalid input data.");
73         mm_util_retvm_if(read_data_ptr->mem == NULL, 0, "Failed to read memory due to invalid input buffer.");
74         mm_util_retvm_if(read_data_ptr->size + size > read_data_ptr->mem_size, 0, "Failed to read memory due to buffer overflow(%zu/%d/%zu).", read_data_ptr->size, size, read_data_ptr->mem_size);
75
76         memcpy(data, read_data_ptr->mem + read_data_ptr->size, size);
77         read_data_ptr->size += size;
78
79         return size;
80 }
81
82 static int __read_gif(mm_image_info_s *decoded, const char *filename, void *memory, const size_t src_size)
83 {
84         int ret = MM_UTIL_ERROR_NONE;
85
86         int ExtCode, i, j, Row, Col, Width, Height;
87         size_t Size = 0;
88         GifRecordType record_type;
89         GifRowType *screen_buffer = NULL;
90         GifFileType *GifFile;
91         unsigned int image_num = 0;
92         ColorMapObject *ColorMap;
93         gif_mem_s read_data_ptr;
94
95         mm_util_fenter();
96
97         if (filename) {
98                 if ((GifFile = DGifOpenFileName(filename, NULL)) == NULL) {
99                         mm_util_error("could not open Gif File");
100                         return MM_UTIL_ERROR_INVALID_OPERATION;
101                 }
102         } else if (memory) {
103                 read_data_ptr.mem = memory;
104                 read_data_ptr.mem_size = src_size;
105                 read_data_ptr.size = 0;
106                 if ((GifFile = DGifOpen(&read_data_ptr, __read_function, NULL)) == NULL) {
107                         mm_util_error("could not open Gif File");
108                         return MM_UTIL_ERROR_INVALID_OPERATION;
109                 }
110         } else {
111                 mm_util_error("Gif File wrong decode parameters");
112                 return MM_UTIL_ERROR_INVALID_OPERATION;
113         }
114         if (GifFile->SWidth <= 0 || GifFile->SHeight <= 0) {
115                 mm_util_error("Gif File wrong decode width & height");
116                 return MM_UTIL_ERROR_INVALID_OPERATION;
117         }
118         decoded->width = GifFile->SWidth;
119         decoded->height = GifFile->SHeight;
120         decoded->size = (unsigned long long)GifFile->SWidth * (unsigned long long)GifFile->SHeight * 4;
121
122         if ((screen_buffer = (GifRowType *)
123                  calloc(1, GifFile->SHeight * sizeof(GifRowType))) == NULL) {
124                 mm_util_error("Failed to allocate memory required, aborted.");
125                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
126                 goto error;
127         }
128
129         Size = GifFile->SWidth * sizeof(GifPixelType);  /* Size in bytes one row. */
130         if ((screen_buffer[0] = (GifRowType) calloc(1, Size)) == NULL) {        /* First row. */
131                 mm_util_error("Failed to allocate memory required, aborted.");
132                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
133                 goto error;
134         }
135
136         for (i = 0; i < (int)(GifFile->SWidth); i++)    /* Set its color to BackGround. */
137                 screen_buffer[0][i] = GifFile->SBackGroundColor;
138         for (i = 1; i < (int)(GifFile->SHeight); i++) {
139                 /* Allocate the other rows, and set their color to background too: */
140                 if ((screen_buffer[i] = (GifRowType) calloc(1, Size)) == NULL) {
141                         mm_util_error("Failed to allocate memory required, aborted.");
142                         ret = MM_UTIL_ERROR_INVALID_OPERATION;
143                         goto error;
144                 }
145
146                 memcpy(screen_buffer[i], screen_buffer[0], Size);
147         }
148
149         /* Scan the content of the GIF file and load the image(s) in: */
150         do {
151                 if (DGifGetRecordType(GifFile, &record_type) == GIF_ERROR) {
152                         mm_util_error("could not get record type");
153                         ret = MM_UTIL_ERROR_INVALID_OPERATION;
154                         goto error;
155                 }
156                 switch (record_type) {
157                 case IMAGE_DESC_RECORD_TYPE:
158                         if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
159                                 mm_util_error("could not get image description");
160                                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
161                                 goto error;
162                         }
163                         if (GifFile->Image.Top < 0 || GifFile->Image.Left < 0 || GifFile->Image.Width <= 0 || GifFile->Image.Height <= 0) {
164                                 mm_util_error("Gif File wrong decode width & height");
165                                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
166                                 goto error;
167                         }
168                         Row = GifFile->Image.Top;       /* Image Position relative to Screen. */
169                         Col = GifFile->Image.Left;
170                         Width = GifFile->Image.Width;
171                         Height = GifFile->Image.Height;
172                         mm_util_debug("Image %d at (%d, %d) [%dx%d]", image_num + 1, Col, Row, Width, Height);
173                         if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth || GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
174                                 mm_util_debug("Image %d is not confined to screen dimension, aborted.", image_num + 1);
175                                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
176                                 goto error;
177                         }
178                         image_num++;
179                         if (GifFile->Image.Interlace) {
180                                 int interlaced_offset[] = { 0, 4, 2, 1 }, interlaced_jumps[] = {
181                                 8, 8, 4, 2};
182                                 /* Need to perform 4 passes on the images: */
183                                 for (i = 0; i < 4; i++)
184                                         for (j = Row + interlaced_offset[i]; j < Row + Height; j += interlaced_jumps[i]) {
185                                                 if (DGifGetLine(GifFile, &screen_buffer[j][Col], Width) == GIF_ERROR) {
186                                                         mm_util_error("could not get line");
187                                                         ret = MM_UTIL_ERROR_INVALID_OPERATION;
188                                                         goto error;
189                                                 }
190                                         }
191                         } else {
192                                 for (i = 0; i < Height; i++) {
193                                         if (DGifGetLine(GifFile, &screen_buffer[Row++][Col], Width) == GIF_ERROR) {
194                                                 mm_util_error("could not get line");
195                                                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
196                                                 goto error;
197                                         }
198                                 }
199                         }
200                         break;
201                 case EXTENSION_RECORD_TYPE:
202                         {
203                                 GifByteType *extension;
204                                 /* Skip any extension blocks in file: */
205                                 if (DGifGetExtension(GifFile, &ExtCode, &extension) == GIF_ERROR) {
206                                         mm_util_error("could not get extension");
207                                         ret = MM_UTIL_ERROR_INVALID_OPERATION;
208                                         goto error;
209                                 }
210                                 while (extension != NULL) {
211                                         if (DGifGetExtensionNext(GifFile, &extension) == GIF_ERROR) {
212                                                 mm_util_error("could not get next extension");
213                                                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
214                                                 goto error;
215                                         }
216                                 }
217                         }
218                         break;
219                 case TERMINATE_RECORD_TYPE:
220                         break;
221                 default:                                /* Should be trapped by DGifGetRecordType. */
222                         break;
223                 }
224                 if (image_num > 0)
225                         break;
226         } while (record_type != TERMINATE_RECORD_TYPE);
227
228         ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
229         if (ColorMap == NULL) {
230                 mm_util_error("Gif Image does not have a colormap");
231                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
232                 goto error;
233         }
234
235         ret = __convert_gif_to_rgba(&decoded->data, ColorMap, screen_buffer, GifFile->SWidth, GifFile->SHeight);
236         if (ret != MM_UTIL_ERROR_NONE) {
237                 mm_util_error("could not convert gif to rgba");
238                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
239                 goto error;
240         }
241
242         ret = MM_UTIL_ERROR_NONE;
243 error:
244         if (screen_buffer) {
245                 if (screen_buffer[0])
246                         (void)free(screen_buffer[0]);
247                 for (i = 1; i < (int)(GifFile->SHeight); i++) {
248                         if (screen_buffer[i])
249                                 (void)free(screen_buffer[i]);
250                 }
251                 (void)free(screen_buffer);
252         }
253         if (DGifCloseFile(GifFile, NULL) == GIF_ERROR) {
254                 mm_util_error("could not close file");
255                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
256         }
257
258         mm_util_fleave();
259         return ret;
260 }
261
262 int mm_util_decode_from_gif_file(const char *fpath, mm_image_info_s *decoded)
263 {
264         mm_util_fenter();
265
266         return __read_gif(decoded, fpath, NULL, 0);
267 }
268
269 int mm_util_decode_from_gif_memory(void *memory, const size_t src_size, mm_image_info_s *decoded)
270 {
271         mm_util_fenter();
272
273         return __read_gif(decoded, NULL, memory, src_size);
274 }
275
276 static int __write_function(GifFileType *gft, const GifByteType *data, int size)
277 {
278         gif_mem_s *write_data_ptr = (gif_mem_s *) gft->UserData;
279
280         mm_util_retvm_if(size <= 0, 0, "Failed to write memory due to size(%d).", size);
281         mm_util_retvm_if(write_data_ptr == NULL, 0, "Failed to write memory due to invalid output data.");
282
283         write_data_ptr->mem = (void *)realloc(write_data_ptr->mem, (write_data_ptr->size + size));
284         mm_util_retvm_if(write_data_ptr->mem == NULL, 0, "Failed to write memory due to allocation failure.");
285
286         memcpy(write_data_ptr->mem + write_data_ptr->size, data, size);
287         write_data_ptr->size += size;
288
289         return size;
290 }
291
292 static const int DEFAULT_COLORMAP_SIZE = (1 << 8);
293
294 static int __gif_extract_rgb(mm_image_info_s *gif_image, unsigned long num_of_pixels, GifByteType **red, GifByteType **green, GifByteType **blue)
295 {
296         GifWord i, j;
297         GifByteType *redP, *greenP, *blueP;
298         GifByteType *buffer = (GifByteType*)gif_image->data;
299
300         mm_util_fenter();
301
302         RGB_ALLOC(redP, greenP, blueP, num_of_pixels, sizeof(GifByteType));
303
304         *red = redP;
305         *green = greenP;
306         *blue = blueP;
307         mm_util_retvm_if((redP == NULL || greenP == NULL || blueP == NULL), MM_UTIL_ERROR_OUT_OF_MEMORY, "Memory allocation failed");
308
309         for (i = 0; i < gif_image->height; i++) {
310                 for (j = 0; j < gif_image->width; j++) {
311                         *redP++ = *buffer++;
312                         *greenP++ = *buffer++;
313                         *blueP++ = *buffer++;
314                         buffer++;
315                 }
316         }
317
318         return MM_UTIL_ERROR_NONE;
319 }
320
321 int __gif_make_color_map(mm_image_info_s *gif_image, ColorMapObject **color_map, GifByteType **intermediate_image, unsigned long *intermediate_image_size)
322 {
323         int ret = MM_UTIL_ERROR_NONE;
324         int colormap_size = DEFAULT_COLORMAP_SIZE;
325         GifByteType *red = NULL, *green = NULL, *blue = NULL;
326
327         mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
328
329         unsigned long num_of_pixels = gif_image->width * gif_image->height;
330
331         /* make colormap and quantization for the color table of gif */
332         ret = __gif_extract_rgb(gif_image, num_of_pixels, &red, &green, &blue);
333         if (ret != MM_UTIL_ERROR_NONE) {
334                 mm_util_error("To extract rgb from image is  failed");
335                 goto FAIL;
336         }
337
338         if ((*color_map = GifMakeMapObject(colormap_size, NULL)) == NULL) {
339                 mm_util_error("To make color map is  failed");
340                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
341                 goto FAIL;
342         }
343
344         /* allocate output buffer */
345         *intermediate_image = (GifByteType *) calloc(1, num_of_pixels * sizeof(GifByteType));
346         if (*intermediate_image == NULL) {
347                 mm_util_error("Memory allocation failed");
348                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
349                 goto FAIL;
350         }
351         *intermediate_image_size = num_of_pixels * sizeof(GifByteType);
352
353         if (GifQuantizeBuffer(gif_image->width, gif_image->height, &colormap_size, red, green, blue, *intermediate_image, (*color_map)->Colors) == GIF_ERROR) {
354                 mm_util_error("could not quantize buffer");
355                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
356                 goto FAIL;
357         }
358         goto SUCCESS;
359
360 FAIL:
361         if (*intermediate_image)
362                 MMUTIL_SAFE_FREE(*intermediate_image);
363
364         *intermediate_image_size = 0;
365         COLORMAP_FREE(*color_map);
366 SUCCESS:
367         RGB_FREE(red, green, blue);
368
369         return ret;
370 }
371
372 static int _gif_encode_open_file(GifFileType **gft)
373 {
374         mm_util_fenter();
375         mm_util_retvm_if(gft == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
376
377         if ((*gft = EGifOpenFileName(GIF_TMP_FILE, 0, NULL)) == NULL) {
378                 mm_util_error("could not open file");
379                 return MM_UTIL_ERROR_INVALID_OPERATION;
380         }
381
382         return MM_UTIL_ERROR_NONE;
383 }
384
385 int _gif_encode_open_mem(gif_file_s *gif_file)
386 {
387         mm_util_fenter();
388         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
389
390         gif_file->write_data_ptr.mem = NULL;
391         gif_file->write_data_ptr.size = 0;
392
393         if ((gif_file->GifFile = EGifOpen(&(gif_file->write_data_ptr), __write_function, NULL)) == NULL) {
394                 mm_util_error("could not open File");
395                 return MM_UTIL_ERROR_INVALID_OPERATION;
396         }
397
398         return MM_UTIL_ERROR_NONE;
399 }
400
401 static int _gif_encode_close_file(GifFileType *gft)
402 {
403         mm_util_fenter();
404         mm_util_retvm_if(gft == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
405
406         if (EGifCloseFile(gft, NULL) == GIF_ERROR) {
407                 mm_util_error("could not close file");
408                 return MM_UTIL_ERROR_INVALID_OPERATION;
409         }
410         gft = NULL;
411
412         return MM_UTIL_ERROR_NONE;
413 }
414
415 int _gif_encode_move_to_file(const char *dst)
416 {
417         int ret = MM_UTIL_ERROR_NONE;
418         GFile *source = g_file_new_for_path(GIF_TMP_FILE);
419         GFile *destination =  g_file_new_for_path(dst);
420         GError *error = NULL;
421
422         if (source == NULL || destination == NULL) {
423                 mm_util_error("could not open file");
424                 if (source)
425                         g_object_unref(source);
426                 if (destination)
427                         g_object_unref(destination);
428                 return MM_UTIL_ERROR_INVALID_OPERATION;
429         }
430
431         gboolean result = g_file_copy(source, destination, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error);
432         if (!result) {
433                 mm_util_error("g_file_copy failed: %s", error ? error->message : "none");
434                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
435         }
436
437         if (error)
438                 g_error_free(error);
439         g_object_unref(source);
440         g_object_unref(destination);
441
442         return ret;
443 }
444
445 int _gif_encode_move_to_mem(const unsigned char *src, size_t src_size, void **dst, size_t *dst_size)
446 {
447         unsigned char *buffer = NULL;
448         mm_util_retvm_if(src == NULL || dst == NULL || dst_size == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
449         mm_util_retvm_if(src_size == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
450
451         mm_util_debug("src_size: %u", src_size);
452
453         buffer = (unsigned char *)calloc(1, src_size);
454         mm_util_retvm_if(buffer == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "memory allocation failed");
455
456         memcpy(buffer, src, src_size);
457
458         *dst = buffer;
459         *dst_size = src_size;
460
461         return MM_UTIL_ERROR_NONE;
462 }
463
464 int _gif_image_write_image(gif_file_s *gif_file, mm_image_info_s *gif_image)
465 {
466         int ret = MM_UTIL_ERROR_NONE;
467         ColorMapObject *color_map = NULL;
468         GifByteType *intermediate_image = NULL;
469         unsigned long intermediate_image_size = 0;
470         unsigned long idx = 0;
471
472         mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
473         mm_util_retvm_if(gif_image->data == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
474
475         /* Make local color map */
476         ret = __gif_make_color_map(gif_image, &color_map, &intermediate_image, &intermediate_image_size);
477         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_make_color_map failed");
478
479         if (EGifPutImageDesc(gif_file->GifFile, 0, 0, gif_image->width, gif_image->height,
480                 FALSE, color_map) == GIF_ERROR) {
481                 mm_util_error("EGifPutImageDesc failed due to %s", GifErrorString(gif_file->GifFile->Error));
482                 COLORMAP_FREE(color_map);
483                 MMUTIL_SAFE_FREE(intermediate_image);
484                 return MM_UTIL_ERROR_INVALID_OPERATION;
485         }
486         /* release color map */
487         COLORMAP_FREE(color_map);
488
489         mm_util_debug("put pixel count: %lu", (intermediate_image_size / sizeof(GifPixelType)));
490
491         for (idx = 0; idx < (intermediate_image_size / sizeof(GifPixelType)); idx++) {
492                 GifPixelType pixel = (GifPixelType)intermediate_image[idx];
493                 if (EGifPutPixel(gif_file->GifFile, pixel) == GIF_ERROR) {
494                         mm_util_error("could not put pixel");
495                         return MM_UTIL_ERROR_INVALID_OPERATION;
496                 }
497         }
498
499         /* release intermediate_image */
500         MMUTIL_SAFE_FREE(intermediate_image);
501
502         return MM_UTIL_ERROR_NONE;
503 }
504
505 int _gif_image_create_ext_block(int function, int byte_count, ExtensionBlock **ext_block)
506 {
507         ExtensionBlock *_ext_block = NULL;
508
509         mm_util_retvm_if(byte_count == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
510         mm_util_retvm_if(ext_block == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
511
512         /* get allocated extention block */
513         _ext_block = (ExtensionBlock *)calloc(1, sizeof(ExtensionBlock));
514         if (_ext_block == NULL) {
515                 mm_util_error("Memory allocation failed");
516                 *ext_block = NULL;
517                 return MM_UTIL_ERROR_OUT_OF_MEMORY;
518         }
519
520         _ext_block->Function = function;
521         _ext_block->ByteCount = byte_count;
522         _ext_block->Bytes = (GifByteType *)calloc(1, (sizeof(GifByteType) * byte_count));
523         if (_ext_block->Bytes == NULL) {
524                 mm_util_error("Memory allocation failed");
525                 MMUTIL_SAFE_FREE(_ext_block);
526                 return MM_UTIL_ERROR_OUT_OF_MEMORY;
527         }
528
529         *ext_block = _ext_block;
530         return MM_UTIL_ERROR_NONE;
531 }
532
533 int _gif_image_write_ext_block(gif_file_s *gif_file, unsigned int delay_time)
534 {
535         int ret = MM_UTIL_ERROR_NONE;
536         ExtensionBlock *_ext_block = NULL;
537         GraphicsControlBlock graphic_control_block;
538
539         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
540
541         ret = _gif_image_create_ext_block(GRAPHICS_EXT_FUNC_CODE, GRAPHIC_EXT_BLOCK_SIZE, &_ext_block);
542         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_alloc_ext_block failed");
543         mm_util_retvm_if(_ext_block->Bytes == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "_gif_image_alloc_ext_block failed");
544
545         /* use fixed graphics control */
546         graphic_control_block.DisposalMode = MM_UTIL_GIF_DISPOSAL_UNSPECIFIED;
547         graphic_control_block.UserInputFlag = FALSE;
548         graphic_control_block.TransparentColor = NO_TRANSPARENT_COLOR;
549         graphic_control_block.DelayTime = delay_time;
550
551         EGifGCBToExtension(&graphic_control_block, _ext_block->Bytes);
552
553         if (EGifPutExtension(gif_file->GifFile, _ext_block->Function, _ext_block->ByteCount, _ext_block->Bytes) == GIF_ERROR) {
554                 mm_util_error("EGifPutExtension failed due to %s", GifErrorString(gif_file->GifFile->Error));
555                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
556         }
557
558         /* release extension blocks */
559         if (_ext_block != NULL) {
560                 MMUTIL_SAFE_FREE(_ext_block->Bytes);
561                 MMUTIL_SAFE_FREE(_ext_block);
562         }
563
564         return MM_UTIL_ERROR_NONE;
565 }
566
567 int mm_util_gif_encode_create(mm_gif_file_h *gif_file_h)
568 {
569         gif_file_s *gif_file = NULL;
570
571         mm_util_retvm_if(gif_file_h == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
572
573         gif_file = (gif_file_s *)calloc(1, sizeof(gif_file_s));
574         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "Memory allocation failed");
575
576         /* initialize data before set */
577         *gif_file_h = (mm_gif_file_h)gif_file;
578
579         return MM_UTIL_ERROR_NONE;
580 }
581
582 int mm_util_gif_encode_set_file(mm_gif_file_h gif_file_h, const char *file_name)
583 {
584         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
585
586         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
587         mm_util_retvm_if(file_name == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
588         mm_util_retvm_if(strlen(file_name) == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
589         mm_util_retvm_if(gif_file->GifFile != NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started");
590
591         gif_file->filename = g_strdup(file_name);
592         mm_util_retvm_if(gif_file->filename == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "Memory allocation failed");
593
594         return MM_UTIL_ERROR_NONE;
595 }
596
597 int mm_util_gif_encode_set_mem(mm_gif_file_h gif_file_h, void **data, size_t *data_size)
598 {
599         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
600
601         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
602         mm_util_retvm_if(data == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
603         mm_util_retvm_if(data_size == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
604         mm_util_retvm_if(gif_file->GifFile != NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started");
605
606         gif_file->enc_buffer = data;
607         gif_file->enc_buffer_size = data_size;
608
609         return MM_UTIL_ERROR_NONE;
610 }
611
612 static int _mm_util_gif_encode_start(mm_gif_file_h gif_file_h, unsigned long width, unsigned long height)
613 {
614         int ret = MM_UTIL_ERROR_NONE;
615         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
616
617         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
618         mm_util_retvm_if(gif_file->GifFile != NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started");
619
620         mm_util_fenter();
621
622         if (gif_file->filename != NULL) {
623                 ret = _gif_encode_open_file(&gif_file->GifFile);
624                 mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_encode_open_file failed");
625         } else if (gif_file->enc_buffer != NULL && gif_file->enc_buffer_size != NULL) {
626                 ret = _gif_encode_open_mem(gif_file);
627                 mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_encode_open_mem failed");
628         } else {
629                 mm_util_error("could not find output");
630                 return MM_UTIL_ERROR_INVALID_OPERATION;
631         }
632
633         /* To initialize data after GifFile opened */
634         EGifSetGifVersion(gif_file->GifFile, TRUE);
635
636         /* Write screen description */
637         if (EGifPutScreenDesc(gif_file->GifFile, width, height, 8 /* color_res */, 0 /* background_color */, NULL) == GIF_ERROR) {
638                 mm_util_error("could not put screen description");
639                 _gif_encode_close_file(gif_file->GifFile);
640                 return MM_UTIL_ERROR_INVALID_OPERATION;
641         }
642
643         mm_util_fleave();
644         return MM_UTIL_ERROR_NONE;
645 }
646
647 int mm_util_gif_encode_add_image(mm_gif_file_h gif_file_h, mm_image_info_s *gif_image_h)
648 {
649         int ret = MM_UTIL_ERROR_NONE;
650         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
651         mm_image_info_s *gif_image = (mm_image_info_s *)gif_image_h;
652
653         mm_util_fenter();
654         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
655         mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
656         mm_util_retvm_if((gif_image->width == 0) || (gif_image->height == 0), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
657         mm_util_retvm_if(gif_image->data == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
658
659         if (gif_file->GifFile == NULL) {
660                 mm_util_warn("first added image, _mm_util_gif_encode_start is needed");
661                 ret = _mm_util_gif_encode_start(gif_file_h, gif_image->width, gif_image->height);
662                 mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_mm_util_gif_encode_start failed");
663         }
664
665         /* Write graphic control block */
666         ret = _gif_image_write_ext_block(gif_file, gif_image->delay_time);
667         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_write_ext_block failed");
668
669         /* Write image description & data */
670         ret = _gif_image_write_image(gif_file, gif_image);
671         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_write_image failed");
672
673         gif_file->GifFile->ImageCount++;
674
675         return MM_UTIL_ERROR_NONE;
676 }
677
678 int mm_util_gif_encode_save(mm_gif_file_h gif_file_h)
679 {
680         int ret = MM_UTIL_ERROR_NONE;
681         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
682
683         mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
684         mm_util_retvm_if(gif_file->GifFile == NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has not started");
685         mm_util_retvm_if(gif_file->GifFile->ImageCount <= 0, MM_UTIL_ERROR_INVALID_OPERATION, "No frame has encoded");
686
687         mm_util_fenter();
688
689         ret = _gif_encode_close_file(gif_file->GifFile);
690         gif_file->GifFile = NULL;
691         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_encode_close_file failed");
692
693         if (gif_file->filename != NULL) {
694                 ret = _gif_encode_move_to_file(gif_file->filename);
695                 mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__move_tmp_to_origin failed");
696         } else if (gif_file->write_data_ptr.mem != NULL) {
697                 ret = _gif_encode_move_to_mem(gif_file->write_data_ptr.mem, gif_file->write_data_ptr.size,
698                         gif_file->enc_buffer, gif_file->enc_buffer_size);
699                 MMUTIL_SAFE_FREE(gif_file->write_data_ptr.mem);
700                 gif_file->write_data_ptr.size = 0;
701                 mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_encode_move_to_origin_mem failed");
702         }
703
704         return MM_UTIL_ERROR_NONE;
705 }
706
707 int mm_util_encode_to_gif_file(mm_image_info_s **images, const unsigned int image_count, const char *path)
708 {
709         int ret = MM_UTIL_ERROR_NONE;
710         int i = 0;
711         mm_gif_file_h gif_file_h = NULL;
712
713         mm_util_fenter();
714
715         mm_util_retvm_if(images == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
716         mm_util_retvm_if(image_count == 0, MM_UTIL_ERROR_INVALID_OPERATION, "Invalid parameter");
717         mm_util_retvm_if(path == NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Invalid parameter");
718
719         ret = mm_util_gif_encode_create(&gif_file_h);
720         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_create failed %d", ret);
721
722         ret = mm_util_gif_encode_set_file(gif_file_h, path);
723         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_set_file failed %d", ret);
724
725         ret = _mm_util_gif_encode_start(gif_file_h, images[0]->width, images[0]->height);
726         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_mm_util_gif_encode_start failed");
727
728         for (i = 0; i < image_count; i++) {
729                 ret = mm_util_gif_encode_add_image(gif_file_h, images[i]);
730                 mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_add_image failed");
731         }
732
733         ret = mm_util_gif_encode_save(gif_file_h);
734         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_save failed");
735
736         mm_util_fleave();
737
738         return MM_UTIL_ERROR_NONE;
739 }
740
741 int mm_util_encode_to_gif_memory(mm_image_info_s **images, const unsigned int image_count, void **buffer, size_t *size)
742 {
743         int ret = MM_UTIL_ERROR_NONE;
744         int i = 0;
745         mm_gif_file_h gif_file_h = NULL;
746
747         mm_util_fenter();
748
749         mm_util_retvm_if(images == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
750         mm_util_retvm_if(image_count == 0, MM_UTIL_ERROR_INVALID_OPERATION, "Invalid parameter");
751         mm_util_retvm_if(buffer == NULL || size == NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Invalid parameter");
752
753         ret = mm_util_gif_encode_create(&gif_file_h);
754         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_create failed %d", ret);
755
756         ret = mm_util_gif_encode_set_mem(gif_file_h, buffer, size);
757         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_set_mem failed %d", ret);
758
759         ret = _mm_util_gif_encode_start(gif_file_h, images[0]->width, images[0]->height);
760         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_mm_util_gif_encode_start failed");
761
762         for (i = 0; i < image_count; i++) {
763                 ret = mm_util_gif_encode_add_image(gif_file_h, images[i]);
764                 mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_add_image failed");
765         }
766
767         ret = mm_util_gif_encode_save(gif_file_h);
768         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_save failed");
769
770         mm_util_fleave();
771
772         return MM_UTIL_ERROR_NONE;
773 }
774
775 void mm_util_gif_encode_destroy(mm_gif_file_h gif_file_h)
776 {
777         int ret = MM_UTIL_ERROR_NONE;
778         gif_file_s *gif_file = (gif_file_s *)gif_file_h;
779
780         mm_util_retm_if(gif_file == NULL, "Invalid parameter");
781
782         if (gif_file->GifFile != NULL) {
783                 ret = _gif_encode_close_file(gif_file->GifFile);
784                 mm_util_retm_if(ret != MM_UTIL_ERROR_NONE, "_gif_encode_close_file failed");
785         }
786
787         MMUTIL_SAFE_G_FREE(gif_file->filename);
788         MMUTIL_SAFE_FREE(gif_file->write_data_ptr.mem);
789 }