From: Jiyong Min Date: Fri, 28 Jul 2017 00:01:29 +0000 (+0900) Subject: Add new version gif encoder(V2) X-Git-Tag: submit/tizen/20170804.071846~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5d7498ff54cd5d5deee071b69d750aa528355d35;p=platform%2Fcore%2Fmultimedia%2Flibmm-utility.git Add new version gif encoder(V2) - Add new version gif encode api It reduce memory usage by encoding gif frame by frame. (After fixing capi, old version will be removed.) - At new version it support customized color map, disposal mode and tp color. Change-Id: Ia7a64d77e321c1f89b69525a9e74f92a3f5ee368 Signed-off-by: Jiyong Min --- diff --git a/gif/include/mm_util_gif.h b/gif/include/mm_util_gif.h index 6bb47ce..cb313b6 100755 --- a/gif/include/mm_util_gif.h +++ b/gif/include/mm_util_gif.h @@ -25,8 +25,10 @@ #ifdef __cplusplus extern "C" { #endif -#include "mm_util_imgp.h" + +#include #include "gif_lib.h" +#include "mm_util_imgp.h" /** @addtogroup UTILITY @@ -48,7 +50,7 @@ typedef struct { * Write data attached to encoding callback */ typedef struct { - unsigned long long size; + unsigned long size; void **mem; } write_data; @@ -83,6 +85,24 @@ typedef struct { void *data; /**< Data */ } mm_util_gif_frame_data; +#ifdef GIF_ENCODER_V2 +#define GRAPHIC_EXT_BLOCK_SIZE 4 +#define APP_EXT_BLOCK_SIZE 11 +#define APP_EXT_BLOCK_DATA "NETSCAPE2.0" +#define SUB_BLOCK_SIZE 3 +#define NETSCAPE_SUB_BLOCK_INDEX 1 + +typedef struct { + unsigned char red; + unsigned char green; + unsigned char blue; +} mm_util_gif_color_s; + +typedef struct gif_image_s* mm_gif_image_h; + +typedef struct gif_file_s* mm_gif_file_h; +#endif + typedef struct { unsigned long width; /**< Width */ unsigned long height; /**< Height */ @@ -121,44 +141,6 @@ int mm_util_decode_from_gif_file(mm_util_gif_data * decoded, const char *filenam */ int mm_util_decode_from_gif_memory(mm_util_gif_data * decoded, void **memory); -#if 0 -/** - * This function gets the width of the decoded image. - * This should be called after the actual decoding. - * - * @param data [in] pointer of mm_util_gif_data. - * @return Width of the decoded image. - * @remark - * @see mm_util_gif_data - * @since R1, 1.0 - */ -unsigned long mm_util_gif_decode_get_width(mm_util_gif_data * data); - -/** - * This function gets the height of the decoded image. - * This should be called after the actual decoding. - * - * @param data [in] pointer of mm_util_gif_data. - * @return Height of the decoded image. - * @remark - * @see mm_util_gif_data - * @since R1, 1.0 - */ -unsigned long mm_util_gif_decode_get_height(mm_util_gif_data * data); - -/** - * This function gets the size of the decoded image. - * This should be called after the actual decoding. - * - * @param data [in] pointer of mm_util_gif_data. - * @return Size of the decoded image. - * @remark - * @see mm_util_gif_data - * @since R1, 1.0 - */ -unsigned long long mm_util_gif_decode_get_size(mm_util_gif_data * data); -#endif - /** * This function creates gif file for output file * @@ -210,90 +192,247 @@ int mm_util_encode_gif(mm_util_gif_data *encoded); */ int mm_util_encode_close_gif(mm_util_gif_data *encoded); +#ifdef GIF_ENCODER_V2 +/** + * This function creates the handle of the single image(frame). + * + * @param gif_file_h [in] the handle of the gif data. + * @param gif_image_h [out] the handle of the single image. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_image_destory + * @post mm_util_gif_image_destory + */ +int mm_util_gif_image_create(mm_gif_file_h gif_file_h, mm_gif_image_h *gif_image_h); + +/** + * This function sets the position of the single image(frame). + * + * @param gif_image_h [in] the handle of the single image. + * @param left [in] the left of the single image. + * @param top [in] the top of the single image. + * @param width [in] the width of the single image. + * @param height [in] the height of the single image. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_image_create + * @pre mm_util_gif_image_create + */ +int mm_util_gif_image_set_position(mm_gif_image_h gif_image_h, int left, int top, int width, int height); + +/** + * This function sets the disposal mode(graphic control) of the single image(frame). + * + * @param gif_image_h [in] the handle of the single image. + * @param disposal_mode [in] the disposal mode of the single image. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_image_create + * @pre mm_util_gif_image_create + */ +int mm_util_gif_image_set_disposal_mode(mm_gif_image_h gif_image_h, const mm_util_gif_disposal disposal_mode); + /** - * This function sets width of the encoded image. + * This function sets the delay time of the single image(frame). * - * @param data [in] pointer of mm_util_gif_data. - * @param width [in] width of the encoded image. - * @return None. + * @param gif_image_h [in] the handle of the single image. + * @param delay_time [in] the delay time of the single image. + * @return This function returns zero on success, or negative value with error code. * @remark - * @see mm_util_gif_data - * @since R1, 1.0 + * @since R1, 2.0 + * @see mm_util_gif_image_create + * @pre mm_util_gif_image_create */ -void mm_util_gif_encode_set_width(mm_util_gif_data * data, unsigned long width); +int mm_util_gif_image_set_delay_time(mm_gif_image_h gif_image_h, const int delay_time); /** - * This function sets height of the encoded image. + * This function sets the buffer of the single image(frame). * - * @param data [in] pointer of mm_util_gif_data. - * @param height [in] height of the encoded image. - * @return None. + * @param gif_image_h [in] the handle of the single image. + * @param image_data [in] the buffer which has image data. + * @param size [in] the length of the buffer. + * @return This function returns zero on success, or negative value with error code. * @remark - * @see mm_util_gif_data - * @since R1, 1.0 + * @since R1, 2.0 + * @see mm_util_gif_image_create + * @pre mm_util_gif_image_create */ -void mm_util_gif_encode_set_height(mm_util_gif_data * data, unsigned long height); +int mm_util_gif_image_set_image(mm_gif_image_h gif_image_h, unsigned char *image_data, unsigned long size); /** - * This function sets number of images of the encoded gif image. + * This function destroys the handle of the single image(frame). * - * @param data [in] pointer of mm_util_gif_data. - * @param height [in] Image count of the encoded image. - * @return None. + * @param gif_image_h [in] the handle of the single image. + * @return None. * @remark - * @see mm_util_gif_data - * @since R1, 1.0 + * @since R1, 2.0 + * @see mm_util_gif_image_create + * @pre mm_util_gif_image_create */ -void mm_util_gif_encode_set_image_count(mm_util_gif_data * data, unsigned int image_count); +void mm_util_gif_image_destory(mm_gif_image_h gif_image_h); /** - * This function sets the time delay after which the particular image of the gif should be displayed. + * This function creates the handle of gif data. * - * @param data [in] pointer of mm_util_gif_frame_data. - * @param delay_time [in] time delay of the frame in the encoded image in 0.01sec units. - * @return None. + * @param gif_file_h [in] the handle of the gif data. + * @return This function returns zero on success, or negative value with error code. * @remark - * @see mm_util_gif_frame_data - * @since R1, 1.0 + * @since R1, 2.0 + * @see mm_util_gif_encode_destroy + * @post mm_util_gif_encode_destroy */ -void mm_util_gif_encode_set_frame_delay_time(mm_util_gif_frame_data * frame, unsigned long long delay_time); +int mm_util_gif_encode_create(mm_gif_file_h *gif_file_h); /** - * This function sets the disposal mode of each frame in the gif as defined by mm_util_gif_disposal. + * This function sets the filepath to save encoded gif. * - * @param data [in] pointer of mm_util_gif_frame_data. - * @param disposal_mode [in] Disposal mode of the frame in the encoded image. - * @return None. + * @param gif_file_h [in] the handle of the gif data. + * @param file_name [in] the filepath to save encoded image. + * @return This function returns zero on success, or negative value with error code. * @remark - * @see mm_util_gif_frame_data - * @since R1, 1.0 + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_create */ -void mm_util_gif_encode_set_frame_disposal_mode(mm_util_gif_frame_data *frame, mm_util_gif_disposal disposal_mode); +int mm_util_gif_encode_set_file(mm_gif_file_h gif_file_h, const char *file_name); /** - * This function sets the position of each frame in the gif. + * This function sets the buffer to save encoded gif. * - * @param data [in] pointer of mm_util_gif_frame_data. - * @param x [in] x position of the frame in the encoded image. - * @param y [in] y position of the frame in the encoded image. - * @return None. + * @param gif_file_h [in] the handle of the gif data. + * @param data [in] the buffer to save encoded image. + * @return This function returns zero on success, or negative value with error code. * @remark - * @see mm_util_gif_frame_data - * @since R1, 1.0 + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_create */ -void mm_util_gif_encode_set_frame_position(mm_util_gif_frame_data *frame, unsigned long x, unsigned long y); +int mm_util_gif_encode_set_mem(mm_gif_file_h gif_file_h, void **data, unsigned long *data_size); /** - * This function sets the transparent color for the frame in the gif. + * This function sets the resolution(width, height) for gif. * - * @param data [in] pointer of mm_util_gif_frame_data. - * @param transparent_color [in] The color in the frame which should be transparent. - * @return None. + * @param gif_file_h [in] the handle of the gif data. + * @param width [in] the width of the image to encode. + * @param height [in] the height of the image to encode. + * @return This function returns zero on success, or negative value with error code. * @remark - * @see mm_util_gif_frame_data - * @since R1, 1.0 + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_create */ -void mm_util_gif_encode_set_frame_transparency_color(mm_util_gif_frame_data *frame, GifColorType transparent_color); +int mm_util_gif_encode_set_resolution(mm_gif_file_h gif_file_h, const int width, const int height); + +/** + * This function sets the times to play animated gif repeatly. + * If set the repeat mode to false, play animated gif once. + * else set the repeat to true, play animated gif infinately. + * If set the repeat_count as 1, animated gif was played one more times. It mean that animated gif will be played two times. + * + * @param gif_file_h [in] the handle of the gif data. + * @param repeat [in] the repeat mode to play animated gif. + * @param repeat_count [in] the times to play animated gif repeatly. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_create + */ +int mm_util_gif_encode_set_repeat(mm_gif_file_h gif_file_h, gboolean repeat_mode, unsigned short repeat_count); + +/** + * This function starts encoding image. + * + * @param gif_file_h [in] the handle of the gif data. + * @param bg_color [in] the background color of the image to encode. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @post mm_util_gif_encode_add_image + */ +int mm_util_gif_encode_start(mm_gif_file_h gif_file_h); + +/** + * This function encodes the single image(frame). + * + * @param gif_file_h [in] the handle of the gif data. + * @param gif_image_h [in] the handle of the single image to encode. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_start + * @post mm_util_gif_encode_save + */ +int mm_util_gif_encode_add_image(mm_gif_file_h gif_file_h, mm_gif_image_h gif_image_h); + +/** + * This function saves encoded image. + * + * @param gif_file_h [in] the handle of the gif data. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_add_image + */ +int mm_util_gif_encode_save(mm_gif_file_h gif_file_h); + +/** + * This function sets image into gif handle, the image will be encoded when @mm_util_gif_encode() is called. + * + * @param gif_file_h [in] the handle of the gif data. + * @param gif_image_h [in] the handle of the image to set into gif handle. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_add_image + */ +int mm_util_gif_enocde_set_image_handle(mm_gif_file_h gif_file_h, mm_gif_image_h gif_image_h); + +/** + * This function gets image from gif handle, can change the values in image after call this function. + * + * @param gif_file_h [in] the handle of the gif data. + * @param gif_file_h [in] the index of the image. + * @param gif_image_h [out] the handle of the image to get from gif handle. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_add_image + */ +int mm_util_gif_enocde_get_image_handle(mm_gif_file_h gif_file_h, int index, mm_gif_image_h *gif_image_h); + +/** + * This function encodes gif image with images set into gif handle. + * + * @param gif_file_h [in] the handle of the gif data. + * @return This function returns zero on success, or negative value with error code. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_add_image + */ +int mm_util_gif_encode(mm_gif_file_h gif_file_h); + +/** + * This function destroys the handle of gif data. + * + * @param gif_image_h [in] the handle of gif data. + * @return None. + * @remark + * @since R1, 2.0 + * @see mm_util_gif_encode_create + * @pre mm_util_gif_encode_create + */ +void mm_util_gif_encode_destroy(mm_gif_file_h gif_file_h); +#endif #ifdef __cplusplus } diff --git a/gif/include/mm_util_gif_private.h b/gif/include/mm_util_gif_private.h new file mode 100755 index 0000000..d032df5 --- /dev/null +++ b/gif/include/mm_util_gif_private.h @@ -0,0 +1,74 @@ +/* + * libmm-utility + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Vineeth T M + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __MM_UTIL_GIF_PRIVATE_H__ +#define __MM_UTIL_GIF_PRIVATE_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + /* for giflib, Same as SavedImage */ + GifImageDesc image_desc; + void *image_data; + unsigned long image_data_size; + int ext_block_count; + ExtensionBlock *ext_blocks; + + /* for libmm */ + GifWord width; + GifWord height; + gboolean has_color_map; + gboolean has_transparent; + GifColorType transparent_color; + GraphicsControlBlock graphic_control_block; + GifByteType *intermediate_image; + unsigned long intermediate_image_size; +} gif_image_s; + +typedef struct { + GifFileType *GifFile; /**< GifFile opened */ + char *filename; + void **enc_buffer; /**< Encoded output data attached to callback */ + unsigned long *enc_buffer_size; + unsigned char *buffer; + write_data write_data_ptr; /**< Encoded output data attached to callback */ + gboolean is_started; + + int saved_image_count; + gif_image_s **saved_image; + GifWord width; + GifWord height; + GifWord color_res; + GifWord background_color; + ColorMapObject *color_map; + gboolean is_repeat; + unsigned short repeat_count; +} gif_file_s; + +#ifdef __cplusplus +} +#endif +#endif /*__MM_UTIL_GIF_PRIVATE_H__*/ diff --git a/gif/mm_util_gif.c b/gif/mm_util_gif.c index 635727d..3f42c4e 100755 --- a/gif/mm_util_gif.c +++ b/gif/mm_util_gif.c @@ -34,8 +34,16 @@ #include #include "mm_util_gif.h" +#include "mm_util_gif_private.h" #include "mm_util_debug.h" +#include +#define GIF_TMP_FILE "/tmp/libmm_gif.gif" + +#define COLORMAP_FREE(map) { if (map != NULL) { GifFreeMapObject(map); map = NULL; } } +#define RGB_ALLOC(r, g, b, number, size) { r = calloc(1, number * size); g = calloc(1, number * size); b = calloc(1, number * size); } +#define RGB_FREE(r, g, b) { MMUTIL_SAFE_FREE(r); MMUTIL_SAFE_FREE(g); MMUTIL_SAFE_FREE(b); } + static int __convert_gif_to_rgba(mm_util_gif_data *decoded, ColorMapObject *color_map, GifRowType *screen_buffer, unsigned long width, unsigned long height) { unsigned long i, j; @@ -47,17 +55,18 @@ static int __convert_gif_to_rgba(mm_util_gif_data *decoded, ColorMapObject *colo if ((decoded->frames = (mm_util_gif_frame_data **) realloc(decoded->frames, sizeof(mm_util_gif_frame_data *))) == NULL) { mm_util_error("Failed to allocate memory required, aborted."); - return MM_UTIL_ERROR_INVALID_OPERATION; + return MM_UTIL_ERROR_OUT_OF_MEMORY; } + if ((decoded->frames[0] = (mm_util_gif_frame_data *) calloc(1, sizeof(mm_util_gif_frame_data))) == NULL) { mm_util_error("Failed to allocate memory required, aborted."); - return MM_UTIL_ERROR_INVALID_OPERATION; + return MM_UTIL_ERROR_OUT_OF_MEMORY; } if ((decoded->frames[0]->data = (void *)malloc(width * height * 4)) == NULL) { mm_util_error("Failed to allocate memory required, aborted."); - return MM_UTIL_ERROR_INVALID_OPERATION; + return MM_UTIL_ERROR_OUT_OF_MEMORY; } buffer = (GifByteType *) decoded->frames[0]->data; @@ -277,23 +286,6 @@ int mm_util_decode_from_gif_memory(mm_util_gif_data *decoded, void **memory) return ret; } -#if 0 -unsigned long mm_util_decode_get_width(mm_util_gif_data *data) -{ - return data->width; -} - -unsigned long mm_util_decode_get_height(mm_util_gif_data *data) -{ - return data->height; -} - -unsigned long long mm_util_decode_get_size(mm_util_gif_data *data) -{ - return data->size; -} -#endif - static void __load_rgb_from_buffer(GifByteType *buffer, GifByteType **red, GifByteType **green, GifByteType **blue, unsigned long width, unsigned long height) { unsigned long i, j; @@ -323,7 +315,6 @@ static void __load_rgb_from_buffer(GifByteType *buffer, GifByteType **red, GifBy return; } -#define ABS(x) ((x) > 0 ? (x) : (-(x))) static int __save_buffer_to_gif(GifFileType *GifFile, GifByteType *OutputBuffer, ColorMapObject *OutputColorMap, mm_util_gif_frame_data * frame) { unsigned long i, j, pixels = 0; @@ -447,8 +438,6 @@ int mm_util_encode_close_gif(mm_util_gif_data *encoded) return MM_UTIL_ERROR_NONE; } -#define RGB_FREE(r, g, b) { free(r); free(g); free(b); } - static int __write_gif(mm_util_gif_data *encoded) { int ColorMapSize; @@ -484,20 +473,20 @@ static int __write_gif(mm_util_gif_data *encoded) if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL) { mm_util_error("could not map object"); - RGB_FREE((char *)red, (char *)green, (char *)blue); + RGB_FREE(red, green, blue); free(OutputBuffer); mm_util_encode_close_gif(encoded); return MM_UTIL_ERROR_INVALID_OPERATION; } if (GifQuantizeBuffer(encoded->frames[i]->width, encoded->frames[i]->height, &ColorMapSize, red, green, blue, OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) { mm_util_error("could not quantize buffer"); - RGB_FREE((char *)red, (char *)green, (char *)blue); + RGB_FREE(red, green, blue); free(OutputBuffer); GifFreeMapObject(OutputColorMap); mm_util_encode_close_gif(encoded); return MM_UTIL_ERROR_INVALID_OPERATION; } - RGB_FREE((char *)red, (char *)green, (char *)blue); + RGB_FREE(red, green, blue); encoded->frames[i]->transparent_color.Red = 0xff; encoded->frames[i]->transparent_color.Green = 0xff; @@ -540,39 +529,915 @@ int mm_util_encode_gif(mm_util_gif_data *encoded) return ret; } -void mm_util_gif_encode_set_width(mm_util_gif_data *data, unsigned long width) +#ifdef GIF_ENCODER_V2 +static const int DEFAULT_COLORMAP_SIZE = (1 << 8); + +int _gif_image_alloc_glob_ext_block(GifFileType *gif_file, int function, int byte_count, ExtensionBlock **ext_block); + +static int __gif_extract_rgb(gif_image_s *gif_image, unsigned long num_of_pixels, GifByteType **red, GifByteType **green, GifByteType **blue) { - data->width = width; + GifWord i, j; + GifByteType *redP, *greenP, *blueP; + GifByteType *buffer = (GifByteType*)gif_image->image_data; + + mm_util_debug("__gif_extract_rgb_from_image"); + + RGB_ALLOC(redP, greenP, blueP, num_of_pixels, sizeof(GifByteType)); + + *red = redP; + *green = greenP; + *blue = blueP; + mm_util_retvm_if((redP == NULL || greenP == NULL || blueP == NULL), MM_UTIL_ERROR_OUT_OF_MEMORY, "Memory allocation failed"); + + for (i = 0; i < gif_image->image_desc.Height; i++) { + for (j = 0; j < gif_image->image_desc.Width; j++) { + *redP++ = *buffer++; + *greenP++ = *buffer++; + *blueP++ = *buffer++; + buffer++; + } + } + + return MM_UTIL_ERROR_NONE; } -void mm_util_gif_encode_set_height(mm_util_gif_data *data, unsigned long height) +int __gif_make_color_map(gif_image_s *gif_image) { - data->height = height; + int ret = MM_UTIL_ERROR_NONE; + int colormap_size = DEFAULT_COLORMAP_SIZE; + GifByteType *red = NULL, *green = NULL, *blue = NULL; + + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + unsigned long num_of_pixels = gif_image->width * gif_image->height; + + /* make colormap and quantization for the color table of gif */ + ret = __gif_extract_rgb(gif_image, num_of_pixels, &red, &green, &blue); + if (ret != MM_UTIL_ERROR_NONE) { + mm_util_error("To extract rgb from image is failed"); + goto FAIL; + } + + if ((gif_image->image_desc.ColorMap = GifMakeMapObject(colormap_size, NULL)) == NULL) { + mm_util_error("To make color map is failed"); + ret = MM_UTIL_ERROR_INVALID_OPERATION; + goto FAIL; + } + + /* allocate output buffer */ + gif_image->intermediate_image = (GifByteType *) calloc(1, num_of_pixels * sizeof(GifByteType)); + if (gif_image->intermediate_image == NULL) { + mm_util_error("Memory allocation failed"); + ret = MM_UTIL_ERROR_INVALID_OPERATION; + goto FAIL; + } + gif_image->intermediate_image_size = num_of_pixels * sizeof(GifByteType); + + if (GifQuantizeBuffer(gif_image->image_desc.Width, gif_image->image_desc.Height, &colormap_size, red, green, blue, gif_image->intermediate_image, gif_image->image_desc.ColorMap->Colors) == GIF_ERROR) { + mm_util_error("could not quantize buffer"); + ret = MM_UTIL_ERROR_INVALID_OPERATION; + goto FAIL; + } + goto SUCCESS; + +FAIL: + if (gif_image->intermediate_image) { + free(gif_image->intermediate_image); + gif_image->intermediate_image = NULL; + } + gif_image->intermediate_image_size = 0; + COLORMAP_FREE(gif_image->image_desc.ColorMap); +SUCCESS: + RGB_FREE(red, green, blue); + + return ret; } -void mm_util_gif_encode_set_image_count(mm_util_gif_data *data, unsigned int image_count) +int __gif_get_index_in_colormap(ColorMapObject * color_map, GifColorType get_color, int *index) { - data->image_count = image_count; + int i = 0; + mm_util_retvm_if(color_map == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + *index = -1; + + for (i = 0; i < color_map->ColorCount; i++) { + if(color_map->Colors[i].Red == get_color.Red && + color_map->Colors[i].Green == get_color.Green && + color_map->Colors[i].Blue == get_color.Blue) { + *index = i; + break; + } + } + + return MM_UTIL_ERROR_NONE; } -void mm_util_gif_encode_set_frame_delay_time(mm_util_gif_frame_data *frame, unsigned long long delay_time) +int _gif_encode_open_file(gif_file_s *gif_file) { - frame->delay_time = delay_time; + mm_util_fenter(); + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + if ((gif_file->GifFile = EGifOpenFileName(GIF_TMP_FILE, 0, NULL)) == NULL) { + mm_util_error("could not open file"); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + return MM_UTIL_ERROR_NONE; } -void mm_util_gif_encode_set_frame_disposal_mode(mm_util_gif_frame_data *frame, mm_util_gif_disposal disposal_mode) +int _gif_encode_open_mem(gif_file_s *gif_file) { - frame->disposal_mode = disposal_mode; + mm_util_fenter(); + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + gif_file->write_data_ptr.mem = (void**)&(gif_file->buffer); + gif_file->write_data_ptr.size = 0; + + if ((gif_file->GifFile = EGifOpen(&(gif_file->write_data_ptr), __write_function, NULL)) == NULL) { + mm_util_error("could not open File"); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + return MM_UTIL_ERROR_NONE; } -void mm_util_gif_encode_set_frame_position(mm_util_gif_frame_data *frame, unsigned long x, unsigned long y) +int _gif_encode_init(gif_file_s *gif_file) { - frame->x = x; - frame->y = y; + int ret = MM_UTIL_ERROR_NONE; + ExtensionBlock *_ext_block = NULL; + + mm_util_fenter(); + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + if (gif_file->is_repeat) { + /* set netscape 2.0 application data to play animated gif */ + ret = _gif_image_alloc_glob_ext_block(gif_file->GifFile, APPLICATION_EXT_FUNC_CODE, APP_EXT_BLOCK_SIZE, &_ext_block); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_alloc_ext_block failed"); + mm_util_retvm_if(_ext_block->Bytes == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "_gif_image_alloc_ext_block failed"); + + memcpy(_ext_block->Bytes, APP_EXT_BLOCK_DATA, APP_EXT_BLOCK_SIZE); + + /* write sub block of application data to play animated gif with repeat count */ + ret = _gif_image_alloc_glob_ext_block(gif_file->GifFile, CONTINUE_EXT_FUNC_CODE, SUB_BLOCK_SIZE, &_ext_block); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_alloc_ext_block failed"); + mm_util_retvm_if(_ext_block->Bytes == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "_gif_image_alloc_ext_block failed"); + _ext_block->Bytes[0] = NETSCAPE_SUB_BLOCK_INDEX; + if (gif_file->repeat_count == 0) { + _ext_block->Bytes[1] = 0x00; + _ext_block->Bytes[2] = 0x00; + } else { + _ext_block->Bytes[1] = (gif_file->repeat_count & 0xff); + _ext_block->Bytes[2] = ((gif_file->repeat_count >> 8) & 0xff); + } + } else { + /* change gif version to 89a due to extension blocks. */ + EGifSetGifVersion(gif_file->GifFile, TRUE); + } + + return MM_UTIL_ERROR_NONE; } -void mm_util_gif_encode_set_frame_transparency_color(mm_util_gif_frame_data *frame, GifColorType transparent_color) +int _gif_encode_close_file(gif_file_s *gif_file) { - frame->is_transparent = true; - frame->transparent_color = transparent_color; + mm_util_fenter(); + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_file->GifFile == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + if (EGifCloseFile(gif_file->GifFile, NULL) == GIF_ERROR) { + mm_util_error("could not close file"); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + gif_file->GifFile = NULL; + + return MM_UTIL_ERROR_NONE; } + +int _gif_encode_move_to_origin(const char *origin) +{ + const char *command = "/bin/cp"; + long ARG_MAX = sysconf(_SC_ARG_MAX); + char *command_line = NULL; + + mm_util_retvm_if((((long)strlen(command) + (long)strlen(GIF_TMP_FILE) + (long)strlen(origin) + 3) >= ARG_MAX), + MM_UTIL_ERROR_INVALID_OPERATION, "command is too long"); + + command_line = (char *)calloc(1, ARG_MAX); + mm_util_retvm_if(command_line == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "memory allocation failed"); + + snprintf(command_line, ARG_MAX, "%s %s %s", command, GIF_TMP_FILE, origin); + + mm_util_debug("[MV COMMAND: %s]", command_line); + WEXITSTATUS(system(command_line)); + + MMUTIL_SAFE_FREE(command_line); + + return MM_UTIL_ERROR_NONE; +} + +int _gif_encode_move_to_origin_mem(const unsigned char *src, unsigned long src_size, void **dst, unsigned long *dst_size) +{ + unsigned char *buffer = NULL; + mm_util_retvm_if(src == NULL || dst == NULL || dst_size == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(src_size == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + buffer = (unsigned char *)calloc(1, src_size); + mm_util_retvm_if(buffer == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "memory allocation failed"); + + mm_util_debug("src_size: %lu", src_size); + + memcpy(buffer, src, src_size); + + *dst = buffer; + *dst_size = src_size; + + return MM_UTIL_ERROR_NONE; +} + +int _gif_image_write_image_desc(gif_file_s *gif_file, gif_image_s *gif_image) +{ + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_image->image_data == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + if (EGifPutImageDesc(gif_file->GifFile, gif_image->image_desc.Left, gif_image->image_desc.Top, + gif_image->image_desc.Width, gif_image->image_desc.Height, gif_image->image_desc.Interlace, + gif_image->image_desc.ColorMap) == GIF_ERROR) { + mm_util_error("EGifPutImageDesc failed due to %s", GifErrorString(gif_file->GifFile->Error)); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + return MM_UTIL_ERROR_NONE; +} + +int _gif_image_write_image_data(gif_file_s *gif_file, gif_image_s *gif_image) +{ + unsigned long idx = 0; + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_image->image_data == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + /* encode OutputBuffer to GifFile */ + unsigned char *ptr = gif_image->intermediate_image; + mm_util_retvm_if(gif_image->intermediate_image == NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Intermediate_image is null"); + + mm_util_debug("put pixel count: %lu", (gif_image->intermediate_image_size / sizeof(GifPixelType))); + + for (idx = 0; idx < (gif_image->intermediate_image_size / sizeof(GifPixelType)); idx++) { + GifPixelType pixel = (GifPixelType)ptr[idx]; + if (EGifPutPixel(gif_file->GifFile, pixel) == GIF_ERROR) { + mm_util_error("could not put pixel"); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + } + + return MM_UTIL_ERROR_NONE; +} + +int _gif_image_alloc_ext_block(gif_image_s *gif_image, int function, int byte_count, ExtensionBlock **ext_block) +{ + int i = 0; + ExtensionBlock *_ext_block = NULL; + + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(byte_count == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(ext_block == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + if (gif_image->ext_blocks == NULL) { + gif_image->ext_blocks = (ExtensionBlock *)calloc(1, sizeof(ExtensionBlock)); + if (gif_image->ext_blocks == NULL) { + mm_util_error("Memory allocation failed"); + *ext_block = NULL; + return MM_UTIL_ERROR_OUT_OF_MEMORY; + } + } else { + /* check exist function block */ + for (i = 0; i < gif_image->ext_block_count; i++) { + _ext_block = &gif_image->ext_blocks[i]; + if (_ext_block != NULL && _ext_block->Function == function) { + mm_util_error("[Not Error] Ext block has already exist"); + *ext_block = _ext_block; + return MM_UTIL_ERROR_NONE; + } + } + ExtensionBlock *realloc_ptr = (ExtensionBlock *)realloc(gif_image->ext_blocks, (sizeof(ExtensionBlock) * (gif_image->ext_block_count + 1))); + if (realloc_ptr == NULL) { + mm_util_error("Memory reallocation failed"); + *ext_block = NULL; + return MM_UTIL_ERROR_OUT_OF_MEMORY; + } + gif_image->ext_blocks = realloc_ptr; + } + + /* get allocated extention block */ + _ext_block = &gif_image->ext_blocks[gif_image->ext_block_count]; + + _ext_block->Function = function; + _ext_block->ByteCount = byte_count; + _ext_block->Bytes = (GifByteType *)calloc(1, (sizeof(GifByteType) * byte_count)); + if (_ext_block->Bytes == NULL) { + mm_util_error("Memory allocation failed"); + /* free all ext_blocks, TODO: free current ext_blocks */ + for (i = 0; i < gif_image->ext_block_count; i++) { + _ext_block = &gif_image->ext_blocks[i]; + MMUTIL_SAFE_FREE(_ext_block->Bytes); + } + MMUTIL_SAFE_FREE(gif_image->ext_blocks); + gif_image->ext_block_count = 0; + return MM_UTIL_ERROR_OUT_OF_MEMORY; + } + + gif_image->ext_block_count++; + + *ext_block = _ext_block; + return MM_UTIL_ERROR_NONE; +} + +int _gif_image_write_ext_blocks(gif_file_s *gif_file, gif_image_s *gif_image) +{ + int ret = MM_UTIL_ERROR_NONE; + int i = 0; + ExtensionBlock *_ext_block = NULL; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + ret = _gif_image_alloc_ext_block(gif_image, GRAPHICS_EXT_FUNC_CODE, GRAPHIC_EXT_BLOCK_SIZE, &_ext_block); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_alloc_ext_block failed"); + mm_util_retvm_if(_ext_block->Bytes == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "_gif_image_alloc_ext_block failed"); + + EGifGCBToExtension(&gif_image->graphic_control_block, _ext_block->Bytes); + + for (i = 0; i < gif_image->ext_block_count; i++) { + _ext_block = &gif_image->ext_blocks[i]; + mm_util_retvm_if(_ext_block == NULL, MM_UTIL_ERROR_INVALID_OPERATION, "extension block is empty"); + + if (EGifPutExtension(gif_file->GifFile, _ext_block->Function, _ext_block->ByteCount, _ext_block->Bytes) == GIF_ERROR) { + mm_util_error("EGifPutExtension failed due to %s", GifErrorString(gif_file->GifFile->Error)); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + } + + return MM_UTIL_ERROR_NONE; +} + +int _gif_image_alloc_glob_ext_block(GifFileType *gif_file, int function, int byte_count, ExtensionBlock **ext_block) +{ + int i = 0; + ExtensionBlock *_ext_block = NULL; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(byte_count == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(ext_block == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + if (gif_file->ExtensionBlocks == NULL) { + gif_file->ExtensionBlocks = (ExtensionBlock *)calloc(1, sizeof(ExtensionBlock)); + if (gif_file->ExtensionBlocks == NULL) { + mm_util_error("Memory allocation failed"); + *ext_block = NULL; + return MM_UTIL_ERROR_OUT_OF_MEMORY; + } + } else { + /* check exist function block */ + for (i = 0; i < gif_file->ExtensionBlockCount; i++) { + _ext_block = &gif_file->ExtensionBlocks[i]; + if (_ext_block != NULL && _ext_block->Function == function) { + mm_util_error("[Not Error] Ext block has already exist"); + *ext_block = _ext_block; + return MM_UTIL_ERROR_NONE; + } + } + ExtensionBlock *realloc_ptr = (ExtensionBlock *)realloc(gif_file->ExtensionBlocks, (sizeof(ExtensionBlock) * (gif_file->ExtensionBlockCount + 1))); + if (realloc_ptr == NULL) { + mm_util_error("Memory reallocation failed"); + *ext_block = NULL; + return MM_UTIL_ERROR_OUT_OF_MEMORY; + } + gif_file->ExtensionBlocks = realloc_ptr; + } + + /* get allocated extention block */ + _ext_block = &gif_file->ExtensionBlocks[gif_file->ExtensionBlockCount]; + + _ext_block->Function = function; + _ext_block->ByteCount = byte_count; + _ext_block->Bytes = (GifByteType *)calloc(1, (sizeof(GifByteType) * byte_count)); + if (_ext_block->Bytes == NULL) { + mm_util_error("Memory allocation failed"); + /* free all ext_blocks, TODO: free current ext_blocks */ + for (i = 0; i < gif_file->ExtensionBlockCount; i++) { + _ext_block = &gif_file->ExtensionBlocks[i]; + MMUTIL_SAFE_FREE(_ext_block->Bytes); + } + MMUTIL_SAFE_FREE(gif_file->ExtensionBlocks); + gif_file->ExtensionBlockCount = 0; + return MM_UTIL_ERROR_OUT_OF_MEMORY; + } + + gif_file->ExtensionBlockCount++; + + *ext_block = _ext_block; + return MM_UTIL_ERROR_NONE; +} + +int _gif_image_write_glob_ext_blocks(gif_file_s *gif_file) +{ + int i = 0; + ExtensionBlock *_ext_block = NULL; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + for (i = 0; i < gif_file->GifFile->ExtensionBlockCount; i++) { + _ext_block = &gif_file->GifFile->ExtensionBlocks[i]; + mm_util_retvm_if(_ext_block == NULL, MM_UTIL_ERROR_NONE, "extension block is empty"); + + if (_ext_block->Function != CONTINUE_EXT_FUNC_CODE) { + if (EGifPutExtensionLeader(gif_file->GifFile, _ext_block->Function) == GIF_ERROR) { + mm_util_error("EGifPutExtensionLeader failed due to %s", GifErrorString(gif_file->GifFile->Error)); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + } + if (EGifPutExtensionBlock(gif_file->GifFile, _ext_block->ByteCount, _ext_block->Bytes) == GIF_ERROR) { + mm_util_error("EGifPutExtensionBlock failed due to %s", GifErrorString(gif_file->GifFile->Error)); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + if (i == gif_file->GifFile->ExtensionBlockCount - 1 || (_ext_block + 1)->Function != CONTINUE_EXT_FUNC_CODE) { + if (EGifPutExtensionTrailer(gif_file->GifFile) == GIF_ERROR) { + mm_util_error("EGifPutExtensionTrailer failed due to %s", GifErrorString(gif_file->GifFile->Error)); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + } + } + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_image_create(mm_gif_file_h gif_file_h, mm_gif_image_h *gif_image_h) +{ + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + gif_image_s *pImage = NULL; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + pImage = (gif_image_s *)calloc(1, sizeof(gif_image_s)); + mm_util_retvm_if(pImage == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "Memory allocation failed"); + + /* init image when it is allocated */ + /* set default width & height */ + pImage->width = pImage->image_desc.Width = gif_file->width; + pImage->height = pImage->image_desc.Height = gif_file->height; + pImage->image_desc.Interlace = FALSE; /* don't change this, fixed value */ + pImage->ext_block_count = 0; + pImage->ext_blocks = NULL; + pImage->has_color_map = FALSE; + pImage->has_transparent = FALSE; + pImage->graphic_control_block.DisposalMode = MM_UTIL_GIF_DISPOSAL_UNSPECIFIED; + pImage->graphic_control_block.UserInputFlag = FALSE; + pImage->graphic_control_block.TransparentColor = NO_TRANSPARENT_COLOR; + + *gif_image_h = (mm_gif_image_h)pImage; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_image_set_position(mm_gif_image_h gif_image_h, int left, int top, int width, int height) +{ + gif_image_s *gif_image = (gif_image_s *)gif_image_h; + + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(left < 0 || top < 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(width <= 0 || height <= 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(left + width > gif_image->width, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(top + height > gif_image->height, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + mm_util_info("%s l = %d, t = %d, w= %d, h = %d", __func__, left, top, width, height); + + gif_image->image_desc.Left = left; + gif_image->image_desc.Top = top; + gif_image->image_desc.Width = width; + gif_image->image_desc.Height = height; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_image_set_disposal_mode(mm_gif_image_h gif_image_h, const mm_util_gif_disposal disposal_mode) +{ + gif_image_s *gif_image = (gif_image_s *)gif_image_h; + + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + mm_util_info("%s mode = %d", __func__, disposal_mode); + + gif_image->graphic_control_block.DisposalMode = (int)disposal_mode; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_image_set_delay_time(mm_gif_image_h gif_image_h, const int delay_time) +{ + gif_image_s *gif_image = (gif_image_s *)gif_image_h; + + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(delay_time == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + mm_util_info("%s mode = %lu", __func__, delay_time); + + gif_image->graphic_control_block.DelayTime = delay_time; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_image_set_tp_color(mm_gif_image_h gif_image_h, const mm_util_gif_color_s tp_color) +{ + gif_image_s *gif_image = (gif_image_s *)gif_image_h; + + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + gif_image->has_transparent = TRUE; + gif_image->transparent_color.Red = tp_color.red; + gif_image->transparent_color.Green = tp_color.green; + gif_image->transparent_color.Blue = tp_color.blue; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_image_set_color_map(mm_gif_image_h gif_image_h, int number_of_colors, mm_util_gif_color_s *colors) +{ + gif_image_s *gif_image = (gif_image_s *)gif_image_h; + + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(number_of_colors == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(colors == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + mm_util_info("%s ptr = %p, size = %lu", __func__, colors, number_of_colors); + + GifColorType *color_map = (GifColorType*)colors; + + gif_image->image_desc.ColorMap = GifMakeMapObject(number_of_colors, color_map); + mm_util_retvm_if(gif_image->image_desc.ColorMap == NULL, MM_UTIL_ERROR_INVALID_OPERATION, "To make color map is failed"); + gif_image->has_color_map = TRUE; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_image_set_image(mm_gif_image_h gif_image_h, unsigned char *image_data, unsigned long size) +{ + gif_image_s *gif_image = (gif_image_s *)gif_image_h; + + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(image_data == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(size == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + mm_util_info("%s ptr = %p, size = %lu", __func__, image_data, size); + + gif_image->image_data = image_data; + gif_image->image_data_size = size; + + return MM_UTIL_ERROR_NONE; +} + +void mm_util_gif_image_destory(mm_gif_image_h gif_image_h) +{ + int i = 0; + ExtensionBlock *_ext_block = NULL; + gif_image_s *gif_image = (gif_image_s *)gif_image_h; + + mm_util_retm_if(gif_image == NULL, "Invalid parameter"); + + /* release color map */ + COLORMAP_FREE(gif_image->image_desc.ColorMap); + + /* release extension blocks */ + if (gif_image->ext_blocks != NULL) { + for (i = 0; i < gif_image->ext_block_count; i++) { + _ext_block = &gif_image->ext_blocks[i]; + MMUTIL_SAFE_FREE(_ext_block->Bytes); + } + MMUTIL_SAFE_FREE(gif_image->ext_blocks); + } + + /* release intermediate_image */ + MMUTIL_SAFE_FREE(gif_image->intermediate_image); + MMUTIL_SAFE_FREE(gif_image); +} + +int mm_util_gif_encode_create(mm_gif_file_h *gif_file_h) +{ + gif_file_s *gif_file = NULL; + + mm_util_retvm_if(gif_file_h == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + gif_file = (gif_file_s *)calloc(1, sizeof(gif_file_s)); + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "Memory allocation failed"); + + /* initialize data before set */ + gif_file->color_res = 8; + gif_file->background_color = 0; + gif_file->color_map = NULL; + gif_file->is_started = FALSE; + gif_file->is_repeat = FALSE; + gif_file->repeat_count = 0; + + *gif_file_h = (mm_gif_file_h)gif_file; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_encode_set_file(mm_gif_file_h gif_file_h, const char *file_name) +{ + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(file_name == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(strlen(file_name) == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_file->is_started, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started"); + + gif_file->filename = g_strdup(file_name); + mm_util_retvm_if(gif_file->filename == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "Memory allocation failed"); + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_encode_set_mem(mm_gif_file_h gif_file_h, void **data, unsigned long *data_size) +{ + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(data == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(data_size == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_file->is_started, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started"); + + gif_file->enc_buffer = data; + gif_file->enc_buffer_size = data_size; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_encode_set_resolution(mm_gif_file_h gif_file_h, const int width, const int height) +{ + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(width == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(height == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_file->is_started, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started"); + + mm_util_info("%s w = %lu, h = %lu", __func__, width, height); + + gif_file->width = width; + gif_file->height = height; + + return MM_UTIL_ERROR_NONE; +} + +#if 0 +int mm_util_gif_encode_set_bg_color(mm_gif_file_h gif_file_h, const mm_util_gif_color_s bg_color) +{ + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + GifColorType *colors = NULL; + int color_count = 1 << gif_file->color_res; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + colors = (GifColorType *)calloc(1, color_count * sizeof(GifColorType)); + mm_util_retvm_if(colors == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "memory allocation failed"); + + colors[0].Red = bg_color.red; + colors[0].Green = bg_color.green; + colors[0].Blue = bg_color.blue; + + gif_file->color_map = GifMakeMapObject(color_count, colors); + mm_util_retvm_if(gif_file->color_map == NULL, MM_UTIL_ERROR_INVALID_OPERATION, "could not make color map"); + + gif_file->background_color = 0; /* index of color map */ + + return MM_UTIL_ERROR_NONE; +} +#endif + +int mm_util_gif_encode_set_repeat(mm_gif_file_h gif_file_h, gboolean repeat_mode, unsigned short repeat_count) +{ + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_file->is_started, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started"); + + mm_util_info("%s repeat = %d, count = %d", __func__, repeat_mode, repeat_count); + + gif_file->is_repeat = repeat_mode; + gif_file->repeat_count = repeat_count; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_encode_start(mm_gif_file_h gif_file_h) +{ + int ret = MM_UTIL_ERROR_NONE; + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_file->is_started, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started"); + + mm_util_fenter(); + + if (gif_file->filename != NULL) { + ret = _gif_encode_open_file(gif_file); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_encode_open_file failed"); + } else if (gif_file->enc_buffer != NULL && gif_file->enc_buffer_size != NULL) { + ret = _gif_encode_open_mem(gif_file); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_encode_open_mem failed"); + } else { + mm_util_error("could not find output"); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + /* To initialize data after GifFile opened */ + ret = _gif_encode_init(gif_file); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_encode_init failed"); + + /* Write screen description */ + if (EGifPutScreenDesc(gif_file->GifFile, gif_file->width, gif_file->height, + gif_file->color_res, gif_file->background_color, gif_file->color_map) == GIF_ERROR) { + mm_util_error("could not put screen description"); + _gif_encode_close_file(gif_file); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + /* Write global extension blocks */ + ret = _gif_image_write_glob_ext_blocks(gif_file); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_write_glob_ext_blocks failed"); + + gif_file->is_started = TRUE; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_encode_add_image(mm_gif_file_h gif_file_h, mm_gif_image_h gif_image_h) +{ + int ret = MM_UTIL_ERROR_NONE; + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + gif_image_s *gif_image = (gif_image_s *)gif_image_h; + + mm_util_fenter(); + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + + if (gif_file->is_started == FALSE) { + mm_util_info("first added image, mm_util_gif_encode_start is needed"); + mm_util_gif_encode_start(gif_file_h); + } + + /* Make local color map */ + if (gif_image->has_color_map == FALSE) { + ret = __gif_make_color_map(gif_image); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_make_color_map failed"); + } + + if (gif_image->has_transparent) { + ret = __gif_get_index_in_colormap(gif_image->image_desc.ColorMap, + gif_image->transparent_color, &gif_image->graphic_control_block.TransparentColor); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_get_index_in_colormap failed"); + } + + /* Write extension blocks */ + ret = _gif_image_write_ext_blocks(gif_file, gif_image); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_write_ext_blocks failed"); + + /* Write image description */ + ret = _gif_image_write_image_desc(gif_file, gif_image);\ + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_write_image_desc failed"); + + /* Write image data */ + ret = _gif_image_write_image_data(gif_file, gif_image); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_image_write_image_data failed"); + + gif_file->GifFile->ImageCount++; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_encode_save(mm_gif_file_h gif_file_h) +{ + int ret = MM_UTIL_ERROR_NONE; + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(!gif_file->is_started, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has not started"); + + mm_util_fenter(); + + ret = _gif_encode_close_file(gif_file); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_encode_close_file failed"); + + if (gif_file->filename != NULL) { + ret= _gif_encode_move_to_origin(gif_file->filename); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__move_tmp_to_origin failed"); + } else if (gif_file->write_data_ptr.mem != NULL) { + ret = _gif_encode_move_to_origin_mem(gif_file->buffer, gif_file->write_data_ptr.size, + gif_file->enc_buffer, gif_file->enc_buffer_size); + MMUTIL_SAFE_FREE(gif_file->buffer); + gif_file->write_data_ptr.size = 0; + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "_gif_encode_move_to_origin_mem failed"); + } + + gif_file->is_started = FALSE; + + return MM_UTIL_ERROR_NONE; +} + + +int mm_util_gif_enocde_set_image_handle(mm_gif_file_h gif_file_h, mm_gif_image_h gif_image_h) +{ + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + gif_image_s *gif_image = (gif_image_s *)gif_image_h; + + mm_util_fenter(); + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_file->is_started, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started"); + + if (gif_file->saved_image == NULL) { + gif_file->saved_image = (gif_image_s **)calloc(1, sizeof(mm_gif_image_h)); + mm_util_retvm_if(gif_file->saved_image == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "memory allocation failed"); + } else { + gif_image_s **tmp = (gif_image_s **)realloc(gif_file->saved_image, sizeof(mm_gif_image_h) * (gif_file->saved_image_count + 1)); + mm_util_retvm_if(tmp == NULL, MM_UTIL_ERROR_OUT_OF_MEMORY, "memory allocation failed"); + gif_file->saved_image = tmp; + } + + gif_file->saved_image[gif_file->saved_image_count] = gif_image; + gif_file->saved_image_count++; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_enocde_get_image_handle(mm_gif_file_h gif_file_h, int index, mm_gif_image_h *gif_image_h) +{ + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + + mm_util_fenter(); + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_image_h == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_file->is_started, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started"); + + if (index < gif_file->saved_image_count) { + *gif_image_h = (mm_gif_image_h)gif_file->saved_image[index]; + } else { + *gif_image_h = NULL; + } + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_gif_encode(mm_gif_file_h gif_file_h) +{ + int ret = MM_UTIL_ERROR_NONE; + int i = 0; + + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + mm_gif_image_h gif_image = NULL; + + mm_util_retvm_if(gif_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter"); + mm_util_retvm_if(gif_file->saved_image == NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Invalid parameter"); + + ret = mm_util_gif_encode_start(gif_file_h); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_start failed"); + + for (i = 0; i < gif_file->saved_image_count; i++) { + gif_image = (mm_gif_image_h)gif_file->saved_image[i]; + ret = mm_util_gif_encode_add_image(gif_file_h, gif_image); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_add_image failed"); + mm_util_gif_image_destory(gif_image); + } + + ret = mm_util_gif_encode_save(gif_file_h); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_save failed"); + + return MM_UTIL_ERROR_NONE; +} + +void mm_util_gif_encode_destroy(mm_gif_file_h gif_file_h) +{ + int ret = MM_UTIL_ERROR_NONE; + int i = 0; + gif_file_s *gif_file = (gif_file_s *)gif_file_h; + + mm_util_retm_if(gif_file == NULL, "Invalid parameter"); + mm_util_retm_if(gif_file->GifFile == NULL, "GifFile is already closed"); + + if (gif_file->GifFile != NULL) { + ret = _gif_encode_close_file(gif_file); + mm_util_retm_if(ret != MM_UTIL_ERROR_NONE, "_gif_encode_close_file failed"); + } + + if (gif_file->saved_image != NULL) { + for (i = 0; i < gif_file->saved_image_count; i++) { + mm_gif_image_h tmp = (mm_gif_image_h)gif_file->saved_image[i]; + mm_util_gif_image_destory(tmp); + } + MMUTIL_SAFE_FREE(gif_file->saved_image); + } + + COLORMAP_FREE(gif_file->color_map); + + MMUTIL_SAFE_FREE(gif_file->filename); + MMUTIL_SAFE_FREE(gif_file->buffer); +} +#endif diff --git a/imgp/include/mm_util_debug.h b/imgp/include/mm_util_debug.h index e72aca6..ce8afe9 100755 --- a/imgp/include/mm_util_debug.h +++ b/imgp/include/mm_util_debug.h @@ -75,6 +75,7 @@ extern "C" } \ } while (0) +#define MMUTIL_SAFE_FREE(x) { if (x) { free(x); x = NULL; } } /** * @} */ diff --git a/packaging/libmm-utility.spec b/packaging/libmm-utility.spec index 2882b4e..27b1457 100755 --- a/packaging/libmm-utility.spec +++ b/packaging/libmm-utility.spec @@ -1,6 +1,6 @@ Name: libmm-utility Summary: Multimedia Framework Utility Library -Version: 0.33 +Version: 0.34 Release: 0 Group: System/Libraries License: Apache-2.0 @@ -54,6 +54,7 @@ export CFLAGS+=" -Wextra -Wno-array-bounds" export CFLAGS+=" -Wno-ignored-qualifiers -Wno-unused-parameter -Wshadow" export CFLAGS+=" -Wwrite-strings -Wswitch-default" export CFLAGS+=" -Werror" +export CFLAGS+=" -DGIF_ENCODER_V2" CFLAGS="$CFLAGS -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" -D_MM_PROJECT_FLOATER" \ LDFLAGS="$LDFLAGS -Wl,--rpath=%{_libdir} -Wl,--hash-style=both -Wl,--as-needed" \ %reconfigure