Add animated image encoder for both WEBP and GIF 00/239600/12 accepted/tizen/unified/20200828.121607 submit/tizen/20200827.013104
authorjiyong.min <jiyong.min@samsung.com>
Tue, 28 Jul 2020 05:21:21 +0000 (14:21 +0900)
committerjiyong.min <jiyong.min@samsung.com>
Tue, 25 Aug 2020 06:39:46 +0000 (15:39 +0900)
  - Unify 'mm_util_anim_xxx' function for animated WEBP and GIF
  - Add 'anim_module_register' function for dlopen of each codec

Change-Id: I50bcd6c61228c1f3be92ef65e65ec4145825506a

14 files changed:
CMakeLists.txt
anim/CMakeLists.txt [new file with mode: 0644]
anim/include/mm_util_anim.h [new file with mode: 0644]
anim/mm_util_anim.c [new file with mode: 0644]
anim/mmutil-anim.pc.in [new file with mode: 0644]
anim/test/CMakeLists.txt [new file with mode: 0644]
anim/test/mm_util_anim_testsuite.c [new file with mode: 0644]
common/include/mm_util_private.h
common/include/mm_util_type.h
gif/include/mm_util_gif.h
gif/mm_util_gif.c
magick/include/mm_util_magick.h
packaging/libmm-utility.spec
webp/mm_util_webp.c

index 102e468..7dba1fb 100644 (file)
@@ -23,6 +23,7 @@ ADD_SUBDIRECTORY(imgp)
 ADD_SUBDIRECTORY(jpeg)
 ADD_SUBDIRECTORY(magick)
 ADD_SUBDIRECTORY(webp)
+ADD_SUBDIRECTORY(anim)
 
 IF(UNIX)
 
diff --git a/anim/CMakeLists.txt b/anim/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e694c13
--- /dev/null
@@ -0,0 +1,73 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+SET(fw_name "mmutil_anim")
+
+PROJECT(${fw_name})
+
+SET(VERSION_MAJOR 0)
+SET(VERSION "${VERSION_MAJOR}.0.0")
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+SET(INC_DIR
+    include
+)
+INCLUDE_DIRECTORIES(${INC_DIR}
+    ../common/include
+    ../magick/include
+)
+
+SET(dependents "dlog glib-2.0")
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(${fw_name} REQUIRED ${dependents})
+FOREACH(flag ${${fw_name}_CFLAGS})
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -Wall -Werror")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+
+IF("${ARCH}" STREQUAL "arm")
+    ADD_DEFINITIONS("-DTARGET")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DLIBPREFIX=\"${LIB_INSTALL_DIR}\"")
+ADD_DEFINITIONS("-DTIZEN_DEBUG")
+
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}")
+
+aux_source_directory(. SOURCES)
+ADD_LIBRARY(${fw_name} SHARED ${SOURCES})
+TARGET_LINK_LIBRARIES(${fw_name} ${${fw_name}_LDFLAGS} mmutil_common)
+SET_TARGET_PROPERTIES(${fw_name}
+    PROPERTIES
+    VERSION ${VERSION}
+    SOVERSION ${VERSION_MAJOR}
+    CLEAN_DIRECT_OUTPUT 1
+)
+
+INSTALL(TARGETS ${fw_name} DESTINATION ${LIB_INSTALL_DIR})
+INSTALL(
+        DIRECTORY ${INC_DIR}/ DESTINATION include/mmf
+        FILES_MATCHING
+        PATTERN "*_internal.h" EXCLUDE
+        PATTERN "*_private.h" EXCLUDE
+        PATTERN "${INC_DIR}/*.h"
+)
+
+SET(PC_NAME mmutil-anim)
+SET(PC_LDFLAGS -l${fw_name})
+SET(PC_FILE_NAME ${PC_NAME}.pc.in)
+
+CONFIGURE_FILE(
+    ${PC_FILE_NAME}
+    ${CMAKE_CURRENT_SOURCE_DIR}/${PC_NAME}.pc
+    @ONLY
+)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${PC_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+ADD_SUBDIRECTORY(test)
+#IF(BUILD_GTESTS)
+#    ADD_SUBDIRECTORY(unittest)
+#ENDIF(BUILD_GTESTS)
diff --git a/anim/include/mm_util_anim.h b/anim/include/mm_util_anim.h
new file mode 100644 (file)
index 0000000..34aa4ce
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* 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_ANIM_H__
+#define __MM_UTIL_ANIM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include "mm_util_type.h"
+
+typedef void *mm_util_anim_enc_h;
+
+int mm_util_anim_enc_create(mm_util_anim_codec_type type, mm_util_anim_enc_h *anim_enc_h);
+int mm_util_anim_enc_set_bgcolor(mm_util_anim_enc_h anim_enc_h, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
+int mm_util_anim_enc_set_loop_count(mm_util_anim_enc_h anim_enc_h, unsigned int loop_count);
+int mm_util_anim_enc_set_lossless(mm_util_anim_enc_h anim_enc_h, bool lossless);
+int mm_util_anim_enc_add_image(mm_util_anim_enc_h anim_enc_h, mm_util_image_h image);
+int mm_util_anim_enc_save_to_file(mm_util_anim_enc_h anim_enc_h, const char *path);
+int mm_util_anim_enc_save_to_buffer(mm_util_anim_enc_h anim_enc_h, unsigned char **buf, size_t *buf_size);
+void mm_util_anim_enc_destroy(mm_util_anim_enc_h anim_enc_h);
+
+#ifdef __cplusplus
+}
+#endif
+#endif   /*__MM_UTIL_ANIM_H__*/
diff --git a/anim/mm_util_anim.c b/anim/mm_util_anim.c
new file mode 100644 (file)
index 0000000..d5a90dc
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* 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.
+*/
+
+#include <stdio.h>
+#include <dlfcn.h> /* for dlopen, dlsym and dlclose */
+#include <glib.h>
+
+#include "mm_util_anim.h"
+#include "mm_util_private.h"
+
+typedef anim_enc_module_t *(*ANIM_ENC_REGISTER)(void);
+
+typedef struct {
+       void *dl_h;
+       anim_enc_module_t *module;
+       void *enc_h;
+} anim_enc_t;
+
+static const char *mm_util_anim_libs[] = {
+       LIBPREFIX"/libmmutil_gif.so",   /* IMG_CODEC_GIF */
+       LIBPREFIX"/libmmutil_webp.so",  /* IMG_CODEC_WEBP */
+       NULL,
+};
+
+static int __anim_open_module(mm_util_anim_codec_type type, anim_enc_t *anim_enc)
+{
+       void *dl_handle = NULL;
+       void *module_register = NULL;
+       anim_enc_module_t *module = NULL;
+
+       mm_util_retvm_if(!anim_enc, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
+       mm_util_retvm_if((type >= ANIM_CODEC_UNKNOWN_TYPE) || !mm_util_anim_libs[type], MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT,
+                                       "invalid type(%d)", type);
+
+       dl_handle = dlopen(mm_util_anim_libs[type], RTLD_LAZY);
+       mm_util_retvm_if(!dl_handle, MM_UTIL_ERROR_INVALID_OPERATION, "%s", dlerror());
+
+       module_register = dlsym(dl_handle, "anim_enc_module_register");
+       if (!module_register) {
+               mm_util_error("Error! can't register module: %s", dlerror());
+               goto FAIL;
+       }
+
+       module = ((ANIM_ENC_REGISTER)module_register)();
+       if (!module) {
+               mm_util_error("Error! module_register");
+               goto FAIL;
+       }
+
+       anim_enc->dl_h = dl_handle;
+       anim_enc->module = module;
+
+       return MM_UTIL_ERROR_NONE;
+
+FAIL:
+       dlclose(dl_handle);
+       return MM_UTIL_ERROR_INVALID_OPERATION;
+}
+
+static void __anim_close_module(anim_enc_t *anim_enc)
+{
+       if (!anim_enc || !anim_enc->dl_h)
+               return;
+
+       g_free(anim_enc->module);
+       anim_enc->module = NULL;
+
+       dlclose(anim_enc->dl_h);
+       anim_enc->dl_h = NULL;
+}
+
+int mm_util_anim_enc_create(mm_util_anim_codec_type type, mm_util_anim_enc_h *anim_enc_h)
+{
+       int ret = MM_UTIL_ERROR_NONE;
+       anim_enc_t *anim_enc = NULL;
+
+       mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
+
+       anim_enc = g_new0(anim_enc_t, 1);
+
+       ret = __anim_open_module(type, anim_enc);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               mm_util_error("Error! __open_module");
+               goto FAIL;
+       }
+
+       if (!anim_enc->module->create) {
+               mm_util_error("Error! not supported create (%d)", ret);
+               ret = MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT;
+               goto FAIL;
+       }
+
+       ret = anim_enc->module->create(&(anim_enc->enc_h));
+       if (ret != MM_UTIL_ERROR_NONE) {
+               mm_util_error("Error! module->create (%d)", ret);
+               goto FAIL;
+       }
+
+       *anim_enc_h = anim_enc;
+       return MM_UTIL_ERROR_NONE;
+
+FAIL:
+       __anim_close_module(anim_enc);
+       g_free(anim_enc);
+       return ret;
+}
+
+int mm_util_anim_enc_set_bgcolor(mm_util_anim_enc_h anim_enc_h, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+       anim_enc_t *anim_enc = (anim_enc_t *)anim_enc_h;
+
+       mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
+       mm_util_retvm_if(!anim_enc->module, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid module");
+       mm_util_retvm_if(!anim_enc->module->set_bgcolor, MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT, "not supported format");
+
+       return anim_enc->module->set_bgcolor(anim_enc->enc_h, a, r, g, b);
+}
+
+int mm_util_anim_enc_set_loop_count(mm_util_anim_enc_h anim_enc_h, unsigned int loop_count)
+{
+       anim_enc_t *anim_enc = (anim_enc_t *)anim_enc_h;
+
+       mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
+       mm_util_retvm_if(!anim_enc->module, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid module");
+       mm_util_retvm_if(!anim_enc->module->set_loop_count, MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT, "not supported format");
+
+       return anim_enc->module->set_loop_count(anim_enc->enc_h, loop_count);
+}
+
+int mm_util_anim_enc_set_lossless(mm_util_anim_enc_h anim_enc_h, bool lossless)
+{
+       anim_enc_t *anim_enc = (anim_enc_t *)anim_enc_h;
+
+       mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
+       mm_util_retvm_if(!anim_enc->module, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid module");
+       mm_util_retvm_if(!anim_enc->module->set_lossless, MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT, "not supported format");
+
+       return anim_enc->module->set_lossless(anim_enc->enc_h, lossless);
+}
+
+int mm_util_anim_enc_add_image(mm_util_anim_enc_h anim_enc_h, mm_util_image_h image)
+{
+       anim_enc_t *anim_enc = (anim_enc_t *)anim_enc_h;
+
+       mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
+       mm_util_retvm_if(!anim_enc->module, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid module");
+       mm_util_retvm_if(!anim_enc->module->add_image, MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT, "not supported format");
+
+       return anim_enc->module->add_image(anim_enc->enc_h, image);
+}
+
+int mm_util_anim_enc_save_to_file(mm_util_anim_enc_h anim_enc_h, const char *path)
+{
+       anim_enc_t *anim_enc = (anim_enc_t *)anim_enc_h;
+
+       mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
+       mm_util_retvm_if(!anim_enc->module, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid module");
+       mm_util_retvm_if(!anim_enc->module->save_to_file, MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT, "not supported format");
+
+       return anim_enc->module->save_to_file(anim_enc->enc_h, path);
+}
+
+int mm_util_anim_enc_save_to_buffer(mm_util_anim_enc_h anim_enc_h, unsigned char **buf, size_t *buf_size)
+{
+       anim_enc_t *anim_enc = (anim_enc_t *)anim_enc_h;
+
+       mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
+       mm_util_retvm_if(!anim_enc->module, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid module");
+       mm_util_retvm_if(!anim_enc->module->save_to_buffer, MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT, "not supported format");
+
+       return anim_enc->module->save_to_buffer(anim_enc->enc_h, (void **)buf, buf_size);
+}
+
+void mm_util_anim_enc_destroy(mm_util_anim_enc_h anim_enc_h)
+{
+       anim_enc_t *anim_enc = (anim_enc_t *)anim_enc_h;
+
+       if (!anim_enc_h)
+               return;
+
+       if (anim_enc->module && anim_enc->module->destroy)
+               anim_enc->module->destroy(anim_enc->enc_h);
+
+       __anim_close_module(anim_enc);
+
+       g_free(anim_enc);
+}
diff --git a/anim/mmutil-anim.pc.in b/anim/mmutil-anim.pc.in
new file mode 100644 (file)
index 0000000..91f801d
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@PREFIX@
+exec_prefix=/usr
+libdir=@LIB_INSTALL_DIR@
+includedir=/usr/include/mmf
+
+Name : @PC_NAME@
+Description : Multimedia Framework Utility Library
+Requires :
+Version : @VERSION@
+Libs : -L${libdir} @PC_LDFLAGS@
+Cflags : -I${includedir}
diff --git a/anim/test/CMakeLists.txt b/anim/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0ff45cd
--- /dev/null
@@ -0,0 +1,18 @@
+SET(fw_name "mmutil_anim")
+SET(fw_test "${fw_name}-test")
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(${fw_test} REQUIRED glib-2.0 dlog libtzplatform-config)
+FOREACH(flag ${${fw_test}_CFLAGS})
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -fPIE")
+
+aux_source_directory(. sources)
+FOREACH(src ${sources})
+    GET_FILENAME_COMPONENT(src_name ${src} NAME_WE)
+    MESSAGE("${src_name}")
+    ADD_EXECUTABLE(${src_name} ${src})
+    TARGET_LINK_LIBRARIES(${src_name} ${fw_name} ${${fw_test}_LDFLAGS} mmutil_common mmutil_magick)
+ENDFOREACH()
diff --git a/anim/test/mm_util_anim_testsuite.c b/anim/test/mm_util_anim_testsuite.c
new file mode 100644 (file)
index 0000000..4d89a20
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <limits.h>
+
+#include <mm_util_anim.h>
+#include <mm_util_image.h>
+#include <mm_util_magick.h>
+#include <tzplatform_config.h>
+
+#define TEST_STRING_VALID(str) ((str != NULL && strlen(str) > 0) ? TRUE : FALSE)
+
+#define WEBP_ENCODE_FILE_PATH  tzplatform_mkpath(TZ_USER_CONTENT, "anim_test_enc_file.webp")
+#define WEBP_ENCODE_MEM_PATH   tzplatform_mkpath(TZ_USER_CONTENT, "anim_test_enc_mem.webp")
+#define GIF_ENCODE_FILE_PATH   tzplatform_mkpath(TZ_USER_CONTENT, "anim_test_enc_file.gif")
+#define GIF_ENCODE_MEM_PATH            tzplatform_mkpath(TZ_USER_CONTENT, "anim_test_enc_mem.gif")
+
+/* for test parameters */
+#define TEST_COLOR_SPACE       MM_UTIL_COLOR_RGBA
+#define TEST_DELAY_TIME                500  // 500ms
+#define TEST_LOOP_COUNT                3  // if loop_count is 0, it is infinite loop
+
+static char *g_path = NULL;
+
+/* for reading files */
+static GQueue *g_queue_files = NULL;
+static GQueue *g_queue_images = NULL;
+
+/* for test parameters */
+static uint8_t g_bgcolor[] = { 0xFF, 0x00, 0x00, 0x00 };  // black
+
+static void __decode_func(gpointer data, gpointer user_data)
+{
+       int ret = MM_UTIL_ERROR_NONE;
+       char *path = (char *)data;
+       mm_util_image_h image = NULL;
+
+       if (!TEST_STRING_VALID(path)) {
+               g_print("\t[ANIM_testsuite] invalid path %s\n", path);
+               return;
+       }
+
+       ret = mm_util_decode_image_from_file(path, TEST_COLOR_SPACE, &image);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               g_print("\t[ANIM_testsuite] mm_util_decode_image_from_file failed %d\n", ret);
+               return;
+       }
+
+       ret = mm_image_set_delay_time(image, TEST_DELAY_TIME);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               g_print("\t[ANIM_testsuite] mm_image_set_delay_time failed %d\n", ret);
+               return;
+       }
+
+       g_queue_push_tail(g_queue_images, image);
+}
+
+static gboolean __decode_files_in_queue()
+{
+       if (g_queue_images)
+               g_queue_free_full(g_queue_images, mm_image_destroy_image);
+
+       g_queue_images = g_queue_new();
+
+       g_queue_foreach(g_queue_files, __decode_func, NULL);
+
+       if (g_queue_images->length == 0) {
+               g_print("\t[ANIM_testsuite] no test image\n");
+               return FALSE;
+       }
+
+       g_print("[ANIM_testsuite] %u images have been decoded.\n", g_queue_images->length);
+
+       return TRUE;
+}
+
+static int __sort_compare(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+       const char *_a = (const char *)a;
+       const char *_b = (const char *)b;
+       if (strlen(_a) < strlen(_b))
+               return -1;
+
+       if (strlen(_a) > strlen(_b))
+               return 1;
+
+       return g_ascii_strcasecmp(_a, _b);
+}
+
+static gboolean __set_input_dir(const char *path)
+{
+       GDir *dir = NULL;
+       const gchar *filename = NULL;
+       GError *g_error = NULL;
+
+       g_print("[ANIM_testsuite] path: %s\n", path);
+
+       dir = g_dir_open(path, 0, &g_error);
+       if (!dir) {
+               g_print("\t[ANIM_testsuite] invalid directory: %s (%s)\n", path, g_error ? g_error->message : "none");
+               if (g_error)
+                       g_error_free(g_error);
+               return FALSE;
+       }
+
+       if (g_queue_files)
+               g_queue_free_full(g_queue_files, g_free);
+
+       g_queue_files = g_queue_new();
+
+       /* push files of dir into queue */
+       while (1) {
+               filename = g_dir_read_name(dir);
+               if (!filename)
+                       break;
+
+               g_queue_insert_sorted(g_queue_files, g_strdup_printf("%s/%s", path, filename), __sort_compare, NULL);
+       }
+
+       g_dir_close(dir);
+
+       if (g_queue_files->length == 0) {
+               g_print("\t[ANIM_testsuite] no test file in directory!\n");
+               return FALSE;
+       }
+
+       /* decode files of dir */
+       if (!__decode_files_in_queue()) {
+               g_print("\t[ANIM_testsuite] fail to decode images!\n");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void __print_help(const char *argv0)
+{
+       fprintf(stderr, "\t[usage] Encode animation webp with images\n");
+       fprintf(stderr, "\t\t1. encode : %s directory(has numeric named files)\n", argv0);
+       fprintf(stderr, "\t\t2. support jpeg/png/gif/bmp/webp files to animation webp\n");
+}
+
+static gboolean __get_arguments(int argc, char *argv[])
+{
+       g_path = g_strdup(argv[1]);
+
+       if (!TEST_STRING_VALID(g_path)) {
+               fprintf(stderr, "\t[ANIM_testsuite] invalid path %s\n", argv[1]);
+               return FALSE;
+       }
+
+       if (!__set_input_dir(g_path)) {
+               fprintf(stderr, "\t[ANIM_testsuite] __set_input_dir failed\n");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void __add_image(gpointer data, gpointer user_data)
+{
+       mm_util_anim_enc_h anim_enc = (mm_util_anim_enc_h)user_data;
+       int ret = 0;
+
+       if (!anim_enc) {
+               fprintf(stderr, "\t[ANIM_testsuite] invalid user_data\n");
+               return ;
+       }
+
+       ret = mm_util_anim_enc_add_image(anim_enc, (mm_util_image_h)data);
+       if (ret != MM_UTIL_ERROR_NONE)
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_webp_anim_enc_add_image failed : %d\n", ret);
+}
+
+static gboolean __test_encode_to_file(mm_util_img_codec_type type, const char *path)
+{
+       int ret = 0;
+       mm_util_anim_enc_h anim_enc = NULL;
+
+       fprintf(stderr, "[ANIM_testsuite] The test begins: %d, %s\n", type, path);
+
+       ret = mm_util_anim_enc_create(type, &anim_enc);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_webp_anim_enc_create failed : %d\n", ret);
+               return FALSE;
+       }
+
+       ret = mm_util_anim_enc_set_bgcolor(anim_enc, g_bgcolor[0], g_bgcolor[1], g_bgcolor[2], g_bgcolor[3]);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_webp_anim_enc_set_bgcolor failed : %d\n", ret);
+               if (ret != MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT)
+                       goto END;
+       }
+
+       ret = mm_util_anim_enc_set_loop_count(anim_enc, TEST_LOOP_COUNT);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_webp_anim_enc_set_loop_count failed : %d\n", ret);
+               if (ret != MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT)
+                       goto END;
+       }
+
+       ret = mm_util_anim_enc_set_lossless(anim_enc, TRUE);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_anim_enc_set_lossless failed : %d\n", ret);
+               if (ret != MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT)
+                       goto END;
+       }
+
+       g_queue_foreach(g_queue_images, __add_image, anim_enc);
+
+       ret = mm_util_anim_enc_save_to_file(anim_enc, path);
+       if (ret != MM_UTIL_ERROR_NONE)
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_encode_webp_to_file failed : %d\n", ret);
+
+END:
+       mm_util_anim_enc_destroy(anim_enc);
+
+       fprintf(stderr, "[ANIM_testsuite] The test ended\n");
+
+       if (ret != MM_UTIL_ERROR_NONE)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean __test_encode_to_buffer(mm_util_img_codec_type type, const char *path)
+{
+       int ret = 0;
+       mm_util_anim_enc_h anim_enc = NULL;
+       /* for encoding webp to memory */
+       unsigned char *encoded_data = NULL;
+       size_t encoded_size = 0;
+       GError *g_error = NULL;
+
+       fprintf(stderr, "[ANIM_testsuite] The test begins: %d, %s\n", type, path);
+
+       ret = mm_util_anim_enc_create(type, &anim_enc);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_webp_anim_enc_create failed : %d\n", ret);
+               return FALSE;
+       }
+
+       ret = mm_util_anim_enc_set_bgcolor(anim_enc, g_bgcolor[0], g_bgcolor[1], g_bgcolor[2], g_bgcolor[3]);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_webp_anim_enc_set_bgcolor failed : %d\n", ret);
+               if (ret != MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT)
+                       goto END;
+       }
+
+       ret = mm_util_anim_enc_set_loop_count(anim_enc, TEST_LOOP_COUNT);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_webp_anim_enc_set_loop_count failed : %d\n", ret);
+               if (ret != MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT)
+                       goto END;
+       }
+
+       ret = mm_util_anim_enc_set_lossless(anim_enc, true);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_anim_enc_set_lossless failed : %d\n", ret);
+               if (ret != MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT)
+                       goto END;
+       }
+
+       g_queue_foreach(g_queue_images, __add_image, anim_enc);
+
+       ret = mm_util_anim_enc_save_to_buffer(anim_enc, &encoded_data, &encoded_size);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               fprintf(stderr, "\t[ANIM_testsuite] mm_util_webp_anim_enc_save_to_buffer failed : %d\n", ret);
+               goto END;
+       }
+
+       if (!g_file_set_contents(path, (gchar *)encoded_data, (gssize)encoded_size, &g_error)) {
+               fprintf(stderr, "\t[ANIM_testsuite] g_file_set_contents failed : %s\n", g_error ? g_error->message : "none");
+               if (g_error)
+                       g_error_free(g_error);
+       }
+
+END:
+       mm_util_anim_enc_destroy(anim_enc);
+       g_free(encoded_data);
+
+       fprintf(stderr, "[ANIM_testsuite] The test ended\n");
+
+       if (ret != MM_UTIL_ERROR_NONE)
+               return FALSE;
+
+       return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+       if (argc < 2) {
+               __print_help(argv[0]);
+               return 0;
+       }
+
+       if (FALSE == __get_arguments(argc, argv)) {
+               fprintf(stderr, "\t[ANIM_testsuite] _get_arguments failed\n");
+               goto out;
+       }
+
+       /* test encoding webp animation to file */
+       if (FALSE == __test_encode_to_file(ANIM_CODEC_WEBP, WEBP_ENCODE_FILE_PATH)) {
+               fprintf(stderr, "\t[ANIM_testsuite] __test_encode_to_file failed\n");
+               goto out;
+       }
+
+       /* test encoding webp animation to buffer */
+       if (FALSE == __test_encode_to_buffer(ANIM_CODEC_WEBP, WEBP_ENCODE_MEM_PATH)) {
+               fprintf(stderr, "\t[ANIM_testsuite] __test_encode_to_buffer failed\n");
+               goto out;
+       }
+
+       /* test encoding agif animation to file */
+       if (FALSE == __test_encode_to_file(ANIM_CODEC_GIF, GIF_ENCODE_FILE_PATH)) {
+               fprintf(stderr, "\t[ANIM_testsuite] __test_encode_to_file failed\n");
+               goto out;
+       }
+
+       /* test encoding agif animation to buffer */
+       if (FALSE == __test_encode_to_buffer(ANIM_CODEC_GIF, GIF_ENCODE_MEM_PATH)) {
+               fprintf(stderr, "\t[ANIM_testsuite] __test_encode_to_buffer failed\n");
+               goto out;
+       }
+
+out:
+       g_free(g_path);
+       g_queue_free_full(g_queue_images, mm_image_destroy_image);
+       g_queue_free_full(g_queue_files, g_free);
+
+       return 0;
+}
+
index 8c4f73e..d9c8da0 100644 (file)
@@ -23,6 +23,7 @@
 #define __MM_UTIL_PRIVATE_H__
 
 #include <glib.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include "mm_util_type.h"
@@ -51,6 +52,26 @@ extern "C" {
 #define IS_VALID_COLOR(color)  mm_util_is_valid_color_format(color)
 #define IS_VALID_IMAGE(image)  mm_image_is_valid_image(image)
 
+typedef int (*ANIM_ENC_CREATE)(void *);
+typedef int (*ANIM_ENC_SET_BG_COLOR)(void *, unsigned char, unsigned char, unsigned char, unsigned char);
+typedef int (*ANIM_ENC_SET_LOOP_COUNT)(void *, unsigned int);
+typedef int (*ANIM_ENC_SET_LOSSLESS)(void *, bool);
+typedef int (*ANIM_ENC_ADD_IMAGE)(void *, mm_util_image_h);
+typedef int (*ANIM_ENC_SAVE_TO_FILE)(void *, const char *);
+typedef int (*ANIM_ENC_SAVE_TO_BUFFER)(void *, void **, size_t *);
+typedef void (*ANIM_ENC_DESTROY)(void *);
+
+typedef struct {
+       ANIM_ENC_CREATE                 create;
+       ANIM_ENC_SET_BG_COLOR   set_bgcolor;
+       ANIM_ENC_SET_LOOP_COUNT set_loop_count;
+       ANIM_ENC_SET_LOSSLESS   set_lossless;
+       ANIM_ENC_ADD_IMAGE              add_image;
+       ANIM_ENC_SAVE_TO_FILE   save_to_file;
+       ANIM_ENC_SAVE_TO_BUFFER save_to_buffer;
+       ANIM_ENC_DESTROY                destroy;
+} anim_enc_module_t;
+
 gboolean mm_util_is_valid_color_format(mm_util_color_format_e color);
 
 int mm_util_safe_fopen(const char *path, const char *mode, FILE **fp);
index fa04c6a..76c65ee 100755 (executable)
@@ -87,6 +87,22 @@ typedef enum {
        MM_UTIL_ROTATE_NUM              /**< Number of rotation types */
 } mm_util_rotate_type_e;
 
+typedef enum {
+       IMG_CODEC_JPEG = 0,
+       IMG_CODEC_PNG,
+       IMG_CODEC_GIF,
+       IMG_CODEC_BMP,
+       IMG_CODEC_WEBP,
+       IMG_CODEC_WBMP = 100,           // used by only media-content
+       IMG_CODEC_UNKNOWN_TYPE,
+} mm_util_img_codec_type;
+
+typedef enum {
+       ANIM_CODEC_GIF = 0,
+       ANIM_CODEC_WEBP,
+       ANIM_CODEC_UNKNOWN_TYPE,
+} mm_util_anim_codec_type;
+
 typedef struct {
        unsigned int width;
        unsigned int height;
index cd34a88..d767fe7 100644 (file)
@@ -39,10 +39,10 @@ int mm_util_encode_to_gif_memory(mm_util_image_h *images, const unsigned int ima
 
 /* a-gif extras */
 int mm_util_gif_encode_create(mm_gif_file_h *gif_file_h);
-int mm_util_gif_encode_set_file(mm_gif_file_h gif_file_h, const char *file_path);
-int mm_util_gif_encode_set_mem(mm_gif_file_h gif_file_h, void **buffer, size_t *buffer_size);
+int mm_util_gif_encode_set_loop_count(mm_gif_file_h gif_file_h, unsigned int loop_count);
 int mm_util_gif_encode_add_image(mm_gif_file_h gif_file_h, mm_image_info_s * gif_image_h);
-int mm_util_gif_encode_save(mm_gif_file_h gif_file_h);
+int mm_util_gif_encode_save_to_file(mm_gif_file_h gif_file_h, const char *path);
+int mm_util_gif_encode_save_to_buffer(mm_gif_file_h gif_file_h, void **buffer, size_t *buffer_size);
 void mm_util_gif_encode_destroy(mm_gif_file_h gif_file_h);
 /* a-gif extras */
 
index c463a1c..9807d9c 100644 (file)
@@ -40,6 +40,7 @@ typedef struct {
 typedef struct {
        GifFileType *GifFile;                     /**< GifFile opened */
        char *filename;
+       unsigned int loop_count;
        void **enc_buffer;                        /**< Encoded output data attached to callback */
        size_t *enc_buffer_size;
        gif_io_buf_s io_buf;
@@ -438,28 +439,28 @@ static int __gif_encode_open_mem(gif_file_s *gif_file)
 
 static int __gif_encode_close_file(GifFileType *gft)
 {
-       mm_util_fenter();
-       mm_util_retvm_if(gft == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
+       int gif_error = E_GIF_SUCCEEDED;
 
-       if (EGifCloseFile(gft, NULL) == GIF_ERROR) {
-               mm_util_error("could not close file");
+       mm_util_retvm_if(!gft, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
+
+       if (EGifCloseFile(gft, &gif_error) == GIF_ERROR) {
+               mm_util_error("could not close file (%d) ", gif_error);
                return MM_UTIL_ERROR_INVALID_OPERATION;
        }
-       gft = NULL;
 
        return MM_UTIL_ERROR_NONE;
 }
 
-static int __write_gif_to_file(gif_file_s *handle)
+static int __write_gif_to_file(gif_file_s *handle, const char *path)
 {
        int ret = MM_UTIL_ERROR_NONE;
        FILE *fp = NULL;
 
        mm_util_retvm_if(!handle, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid handle");
        mm_util_retvm_if((!handle->io_buf.buf || handle->io_buf.buf_size == 0), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid io_buf");
-       mm_util_retvm_if(!MMUTIL_STRING_VALID(handle->filename), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid filename");
+       mm_util_retvm_if(!MMUTIL_STRING_VALID(path), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid path");
 
-       ret = mm_util_safe_fopen(handle->filename, "wb", &fp);
+       ret = mm_util_safe_fopen(path, "wb", &fp);
        mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_safe_fopen fail (%d)", ret);
 
        ret = mm_util_safe_fwrite(fp, handle->io_buf.buf, handle->io_buf.buf_size);
@@ -471,14 +472,14 @@ static int __write_gif_to_file(gif_file_s *handle)
        return ret;
 }
 
-static int __write_gif_to_buffer(gif_file_s *handle)
+static int __write_gif_to_buffer(gif_file_s *handle, void **buffer, size_t *buffer_size)
 {
        mm_util_retvm_if(!handle, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid handle");
        mm_util_retvm_if(!handle->io_buf.buf || handle->io_buf.buf_size == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid io_buf");
-       mm_util_retvm_if(!handle->enc_buffer || !handle->enc_buffer_size, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid buffer");
+       mm_util_retvm_if(!buffer || !buffer_size, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid buffer");
 
-       *handle->enc_buffer = g_memdup(handle->io_buf.buf, handle->io_buf.buf_size);;
-       *handle->enc_buffer_size = handle->io_buf.buf_size;
+       *buffer = g_memdup(handle->io_buf.buf, handle->io_buf.buf_size);;
+       *buffer_size = handle->io_buf.buf_size;
 
        return MM_UTIL_ERROR_NONE;
 }
@@ -531,7 +532,7 @@ static int __gif_image_create_ext_block(int function, int byte_count, ExtensionB
        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");
 
-       /* get allocated extention block */
+       /* get allocated extension block */
        _ext_block = g_new0(ExtensionBlock, 1);
 
        _ext_block->Function = function;
@@ -543,6 +544,15 @@ static int __gif_image_create_ext_block(int function, int byte_count, ExtensionB
        return MM_UTIL_ERROR_NONE;
 }
 
+static void __gif_image_destroy_ext_block(ExtensionBlock *ext_block)
+{
+       if (!ext_block)
+               return;
+
+       g_free(ext_block->Bytes);
+       g_free(ext_block);
+}
+
 static int __gif_image_write_ext_block(gif_file_s *gif_file, unsigned int delay_time)
 {
        int ret = MM_UTIL_ERROR_NONE;
@@ -568,48 +578,89 @@ static int __gif_image_write_ext_block(gif_file_s *gif_file, unsigned int delay_
        }
 
        /* release extension blocks */
-       g_free(_ext_block->Bytes);
-       g_free(_ext_block);
+       __gif_image_destroy_ext_block(_ext_block);
 
        return MM_UTIL_ERROR_NONE;
 }
 
-int mm_util_gif_encode_create(mm_gif_file_h *gif_file_h)
+static int __gif_image_write_loop_count(gif_file_s *gif_file, unsigned int loop_count)
 {
-       gif_file_s *gif_file = NULL;
+#define EXT_BLCOK_COUNT                2
+#define APP_EXT_BLOCK_DATA     "NETSCAPE2.0"
+#define APP_EXT_BLOCK_SIZE     strlen(APP_EXT_BLOCK_DATA)
+#define SUB_BLOCK_SIZE 3
+       int ret = MM_UTIL_ERROR_NONE;
+       ExtensionBlock *app_ext_block = NULL;
+       ExtensionBlock *app_sub_block = NULL;
 
-       mm_util_retvm_if(!gif_file_h, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid handle");
+       ret = __gif_image_create_ext_block(APPLICATION_EXT_FUNC_CODE, APP_EXT_BLOCK_SIZE, &app_ext_block);
+       mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_image_create_ext_block failed");
 
-       gif_file = g_new0(gif_file_s, 1);
+       memcpy(app_ext_block->Bytes, APP_EXT_BLOCK_DATA, APP_EXT_BLOCK_SIZE);
 
-       *gif_file_h = (mm_gif_file_h)gif_file;
+       ret = __gif_image_create_ext_block(CONTINUE_EXT_FUNC_CODE, SUB_BLOCK_SIZE, &app_sub_block);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               mm_util_error("__gif_image_create_ext_block failed");
+               goto END;
+       }
 
-       return MM_UTIL_ERROR_NONE;
+       app_sub_block->Bytes[0] = 1;  /* index of sub block for netscape */
+       app_sub_block->Bytes[1] = loop_count & 0xff;
+       app_sub_block->Bytes[2] = (loop_count >> 8) & 0xff;
+
+       if (EGifPutExtensionLeader(gif_file->GifFile, app_ext_block->Function) == GIF_ERROR) {
+               mm_util_error("EGifPutExtensionLeader failed");
+               ret = MM_UTIL_ERROR_INVALID_OPERATION;
+               goto END;
+       }
+
+       if (EGifPutExtensionBlock(gif_file->GifFile, app_ext_block->ByteCount, app_ext_block->Bytes) == GIF_ERROR) {
+               mm_util_error("EGifPutExtensionBlock(app_ext_block) failed");
+               ret = MM_UTIL_ERROR_INVALID_OPERATION;
+               goto END;
+       }
+
+       if (EGifPutExtensionBlock(gif_file->GifFile, app_sub_block->ByteCount, app_sub_block->Bytes) == GIF_ERROR) {
+               mm_util_error("EGifPutExtensionBlock(app_sub_block) failed");
+               ret = MM_UTIL_ERROR_INVALID_OPERATION;
+               goto END;
+       }
+
+       if (EGifPutExtensionTrailer(gif_file->GifFile) == GIF_ERROR) {
+               mm_util_error("EGifPutExtensionTrailer failed");
+               ret = MM_UTIL_ERROR_INVALID_OPERATION;
+               goto END;
+       }
+
+END:
+       __gif_image_destroy_ext_block(app_ext_block);
+       __gif_image_destroy_ext_block(app_sub_block);
+
+       return ret;
 }
 
-int mm_util_gif_encode_set_file(mm_gif_file_h gif_file_h, const char *file_path)
+int mm_util_gif_encode_create(mm_gif_file_h *gif_file_h)
 {
-       gif_file_s *gif_file = (gif_file_s *)gif_file_h;
+       gif_file_s *gif_file = NULL;
 
-       mm_util_retvm_if(!gif_file, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid handle");
-       mm_util_retvm_if(!MMUTIL_STRING_VALID(file_path), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid file_path");
+       mm_util_retvm_if(!gif_file_h, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
 
-       g_free(gif_file->filename);
-       gif_file->filename = g_strdup(file_path);
+       gif_file = g_new0(gif_file_s, 1);
+
+       gif_file->loop_count = 1;
+
+       *gif_file_h = (mm_gif_file_h)gif_file;
 
        return MM_UTIL_ERROR_NONE;
 }
 
-int mm_util_gif_encode_set_mem(mm_gif_file_h gif_file_h, void **buffer, size_t *buffer_size)
+int mm_util_gif_encode_set_loop_count(mm_gif_file_h gif_file_h, unsigned int loop_count)
 {
        gif_file_s *gif_file = (gif_file_s *)gif_file_h;
 
-       mm_util_retvm_if(!gif_file, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid handle");
-       mm_util_retvm_if(!buffer, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid buffer");
-       mm_util_retvm_if(!buffer_size, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid buffer_size");
+       mm_util_retvm_if(!gif_file, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
 
-       gif_file->enc_buffer = buffer;
-       gif_file->enc_buffer_size = buffer_size;
+       gif_file->loop_count = loop_count;
 
        return MM_UTIL_ERROR_NONE;
 }
@@ -619,7 +670,7 @@ static int __mm_util_gif_encode_start(mm_gif_file_h gif_file_h, unsigned int wid
        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 == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
        mm_util_retvm_if(gif_file->GifFile != NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has already started");
 
        mm_util_fenter();
@@ -637,8 +688,15 @@ static int __mm_util_gif_encode_start(mm_gif_file_h gif_file_h, unsigned int wid
                return MM_UTIL_ERROR_INVALID_OPERATION;
        }
 
+       /* Write extension block for loop count */
+       ret = __gif_image_write_loop_count(gif_file, gif_file->loop_count);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               __gif_encode_close_file(gif_file->GifFile);
+               mm_util_error("__gif_image_write_loop_count failed");
+       }
+
        mm_util_fleave();
-       return MM_UTIL_ERROR_NONE;
+       return ret;
 }
 
 int mm_util_gif_encode_add_image(mm_gif_file_h gif_file_h, mm_image_info_s *gif_image_h)
@@ -648,7 +706,7 @@ int mm_util_gif_encode_add_image(mm_gif_file_h gif_file_h, mm_image_info_s *gif_
        mm_image_info_s *gif_image = (mm_image_info_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_file == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
        mm_util_retvm_if(gif_image == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
        mm_util_retvm_if((gif_image->width == 0) || (gif_image->height == 0), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
        mm_util_retvm_if(gif_image->data == NULL, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid parameter");
@@ -661,7 +719,7 @@ int mm_util_gif_encode_add_image(mm_gif_file_h gif_file_h, mm_image_info_s *gif_
        }
 
        /* Write graphic control block */
-       ret = __gif_image_write_ext_block(gif_file, gif_image->delay_time);
+       ret = __gif_image_write_ext_block(gif_file, gif_image->delay_time / 10);
        mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_image_write_ext_block failed");
 
        /* Write image description & data */
@@ -673,25 +731,49 @@ int mm_util_gif_encode_add_image(mm_gif_file_h gif_file_h, mm_image_info_s *gif_
        return MM_UTIL_ERROR_NONE;
 }
 
-int mm_util_gif_encode_save(mm_gif_file_h gif_file_h)
+int mm_util_gif_encode_save_to_file(mm_gif_file_h gif_file_h, const char *path)
 {
        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->GifFile == NULL, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has not started");
+       mm_util_retvm_if(!gif_file_h, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
+       mm_util_retvm_if(!MMUTIL_STRING_VALID(path), MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid path");
+       mm_util_retvm_if(!gif_file->GifFile, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has not started");
        mm_util_retvm_if(gif_file->GifFile->ImageCount <= 0, MM_UTIL_ERROR_INVALID_OPERATION, "No frame has encoded");
 
        mm_util_fenter();
 
        ret = __gif_encode_close_file(gif_file->GifFile);
+       mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_encode_close_file failed");
        gif_file->GifFile = NULL;
+
+       ret = __write_gif_to_file(gif_file, path);
+       mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__write_gif_to_file failed (%d)", ret);
+
+       MMUTIL_SAFE_FREE(gif_file->io_buf.buf);
+       gif_file->io_buf.buf_size = 0;
+
+       return ret;
+}
+
+int mm_util_gif_encode_save_to_buffer(mm_gif_file_h gif_file_h, void **buffer, size_t *buffer_size)
+{
+       int ret = MM_UTIL_ERROR_NONE;
+       gif_file_s *gif_file = (gif_file_s *)gif_file_h;
+
+       mm_util_retvm_if(!gif_file_h, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid gif_file_h");
+       mm_util_retvm_if(!buffer || !buffer_size, MM_UTIL_ERROR_INVALID_PARAMETER, "Invalid buffer");
+       mm_util_retvm_if(!gif_file->GifFile, MM_UTIL_ERROR_INVALID_OPERATION, "Encoding has not started");
+       mm_util_retvm_if(gif_file->GifFile->ImageCount <= 0, MM_UTIL_ERROR_INVALID_OPERATION, "No frame has encoded");
+
+       mm_util_fenter();
+
+       ret = __gif_encode_close_file(gif_file->GifFile);
        mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__gif_encode_close_file failed");
+       gif_file->GifFile = NULL;
 
-       if (gif_file->filename != NULL)
-               ret = __write_gif_to_file(gif_file);
-       else if (gif_file->enc_buffer != NULL)
-               ret = __write_gif_to_buffer(gif_file);
+       ret = __write_gif_to_buffer(gif_file, buffer, buffer_size);
+       mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "__write_gif_to_buffer failed (%d)", ret);
 
        MMUTIL_SAFE_FREE(gif_file->io_buf.buf);
        gif_file->io_buf.buf_size = 0;
@@ -715,13 +797,6 @@ int mm_util_encode_to_gif_file(mm_util_image_h *images, const unsigned int image
        ret = mm_util_gif_encode_create(&gif_file_h);
        mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_create failed %d", ret);
 
-       ret = mm_util_gif_encode_set_file(gif_file_h, file_path);
-       if (ret != MM_UTIL_ERROR_NONE) {
-               mm_util_error("mm_util_gif_encode_set_file failed");
-               mm_util_gif_encode_destroy(gif_file_h);
-               return ret;
-       }
-
        for (i = 0; i < image_count; i++) {
                ret = mm_util_gif_encode_add_image(gif_file_h, _images[i]);
                if (ret != MM_UTIL_ERROR_NONE) {
@@ -731,9 +806,9 @@ int mm_util_encode_to_gif_file(mm_util_image_h *images, const unsigned int image
                }
        }
 
-       ret = mm_util_gif_encode_save(gif_file_h);
+       ret = mm_util_gif_encode_save_to_file(gif_file_h, file_path);
        if (ret != MM_UTIL_ERROR_NONE)
-               mm_util_error("mm_util_gif_encode_save failed");
+               mm_util_error("mm_util_gif_encode_save_to_file failed (%d)", ret);
 
        mm_util_gif_encode_destroy(gif_file_h);
 
@@ -758,13 +833,6 @@ int mm_util_encode_to_gif_memory(mm_util_image_h *images, const unsigned int ima
        ret = mm_util_gif_encode_create(&gif_file_h);
        mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "mm_util_gif_encode_create failed %d", ret);
 
-       ret = mm_util_gif_encode_set_mem(gif_file_h, buffer, size);
-       if (ret != MM_UTIL_ERROR_NONE) {
-               mm_util_error("mm_util_gif_encode_set_mem failed");
-               mm_util_gif_encode_destroy(gif_file_h);
-               return ret;
-       }
-
        for (i = 0; i < image_count; i++) {
                ret = mm_util_gif_encode_add_image(gif_file_h, _images[i]);
                if (ret != MM_UTIL_ERROR_NONE) {
@@ -774,9 +842,9 @@ int mm_util_encode_to_gif_memory(mm_util_image_h *images, const unsigned int ima
                }
        }
 
-       ret = mm_util_gif_encode_save(gif_file_h);
+       ret = mm_util_gif_encode_save_to_buffer(gif_file_h, buffer, size);
        if (ret != MM_UTIL_ERROR_NONE)
-               mm_util_error("mm_util_gif_encode_save failed");
+               mm_util_error("mm_util_gif_encode_save_to_buffer failed (%d)", ret);
 
        mm_util_gif_encode_destroy(gif_file_h);
 
@@ -792,7 +860,7 @@ void mm_util_gif_encode_destroy(mm_gif_file_h gif_file_h)
 
        mm_util_retm_if(gif_file == NULL, "Invalid parameter");
 
-       if (gif_file->GifFile != NULL) {
+       if (gif_file->GifFile) {
                ret = __gif_encode_close_file(gif_file->GifFile);
                mm_util_retm_if(ret != MM_UTIL_ERROR_NONE, "__gif_encode_close_file failed");
        }
@@ -801,3 +869,25 @@ void mm_util_gif_encode_destroy(mm_gif_file_h gif_file_h)
        MMUTIL_SAFE_FREE(gif_file->io_buf.buf);
        g_free(gif_file);
 }
+
+anim_enc_module_t *anim_enc_module_register(void)
+{
+       anim_enc_module_t *module = NULL;
+
+       mm_util_fenter();
+
+       module = g_new0(anim_enc_module_t, 1);
+
+       module->create = (ANIM_ENC_CREATE)mm_util_gif_encode_create;
+       module->set_bgcolor = NULL;
+       module->set_loop_count = (ANIM_ENC_SET_LOOP_COUNT)mm_util_gif_encode_set_loop_count;
+       module->set_lossless = NULL;
+       module->add_image = (ANIM_ENC_ADD_IMAGE)mm_util_gif_encode_add_image;
+       module->save_to_file = (ANIM_ENC_SAVE_TO_FILE)mm_util_gif_encode_save_to_file;
+       module->save_to_buffer = (ANIM_ENC_SAVE_TO_BUFFER)mm_util_gif_encode_save_to_buffer;
+       module->destroy = (ANIM_ENC_DESTROY)mm_util_gif_encode_destroy;
+
+       mm_util_fleave();
+
+       return module;
+}
index 4c155a2..531a209 100644 (file)
@@ -33,16 +33,6 @@ extern "C" {
 
 typedef void *mm_util_enc_opt_h;
 
-typedef enum {
-       IMG_CODEC_JPEG = 0,
-       IMG_CODEC_PNG,
-       IMG_CODEC_GIF,
-       IMG_CODEC_BMP,
-       IMG_CODEC_WEBP,
-       IMG_CODEC_WBMP = 100,           // used by only media-content
-       IMG_CODEC_UNKNOWN_TYPE,
-} mm_util_img_codec_type;
-
 int mm_util_rotate_B_B(mm_util_image_h src_handle, mm_util_rotate_type_e rotation, mm_util_image_h *dst_handle);
 int mm_util_rotate_B_P(mm_util_image_h src_handle, mm_util_rotate_type_e rotation, const char *dst_path);
 int mm_util_rotate_P_B(const char *src_path, mm_util_rotate_type_e rotation, mm_util_color_format_e req_format, mm_util_image_h *dst_handle);
index 03dd1f8..62ae616 100644 (file)
@@ -63,6 +63,8 @@ rm -rf %{buildroot}
 %defattr(-,root,root,-)
 %{_libdir}/*.so.*
 %{_libdir}/libmmutil_imgcv.so
+%{_libdir}/libmmutil_gif.so
+%{_libdir}/libmmutil_webp.so
 %if 0%{?gtests:1}
 %{_bindir}/gtest*
 %{_bindir}/libmm*unittest*
index 3b8b118..b898e35 100644 (file)
@@ -176,7 +176,7 @@ int mm_util_webp_anim_enc_create(mm_util_webp_anim_enc_h *anim_enc_h)
        anim_enc = g_new0(mm_util_webp_anim_enc_t, 1);
        anim_enc->anim_params.bgcolor = 0xFFFFFFFF;     // white
        anim_enc->anim_params.loop_count = 0;           // infinite
-       anim_enc->lossless = false;
+       anim_enc->lossless = false;                     // lossy
 
        *anim_enc_h = anim_enc;
 
@@ -329,3 +329,25 @@ void mm_util_webp_anim_enc_destroy(mm_util_webp_anim_enc_h anim_enc_h)
        __webp_anim_enc_deinit((mm_util_webp_anim_enc_t *)anim_enc_h);
        g_free(anim_enc_h);
 }
+
+anim_enc_module_t *anim_enc_module_register(void)
+{
+       anim_enc_module_t *module = NULL;
+
+       mm_util_fenter();
+
+       module = g_new0(anim_enc_module_t, 1);
+
+       module->create = (ANIM_ENC_CREATE)mm_util_webp_anim_enc_create;
+       module->set_bgcolor = (ANIM_ENC_SET_BG_COLOR)mm_util_webp_anim_enc_set_bgcolor;
+       module->set_loop_count = (ANIM_ENC_SET_LOOP_COUNT)mm_util_webp_anim_enc_set_loop_count;
+       module->set_lossless = (ANIM_ENC_SET_LOSSLESS)mm_util_webp_anim_enc_set_lossless;
+       module->add_image = (ANIM_ENC_ADD_IMAGE)mm_util_webp_anim_enc_add_image;
+       module->save_to_file = (ANIM_ENC_SAVE_TO_FILE)mm_util_webp_anim_enc_save_to_file;
+       module->save_to_buffer = (ANIM_ENC_SAVE_TO_BUFFER)mm_util_webp_anim_enc_save_to_buffer;
+       module->destroy = (ANIM_ENC_DESTROY)mm_util_webp_anim_enc_destroy;
+
+       mm_util_fleave();
+
+       return module;
+}