From 01d6a29e19c8a9a09c0e84c151c606db42b2da64 Mon Sep 17 00:00:00 2001 From: "jiyong.min" Date: Thu, 2 Jul 2020 08:11:35 +0900 Subject: [PATCH] Support new animation encoding function for WebP Change-Id: I2f7f481ca2ce518284eee3b1ffb0208f479d8c00 --- CMakeLists.txt | 1 + packaging/libmm-utility.spec | 2 + webp/CMakeLists.txt | 73 ++++++++ webp/include/mm_util_webp.h | 43 +++++ webp/mm_util_webp.c | 331 +++++++++++++++++++++++++++++++++++++ webp/mmutil-webp.pc.in | 11 ++ webp/test/CMakeLists.txt | 18 ++ webp/test/mm_util_webp_testsuite.c | 297 +++++++++++++++++++++++++++++++++ 8 files changed, 776 insertions(+) create mode 100644 webp/CMakeLists.txt create mode 100644 webp/include/mm_util_webp.h create mode 100644 webp/mm_util_webp.c create mode 100644 webp/mmutil-webp.pc.in create mode 100644 webp/test/CMakeLists.txt create mode 100644 webp/test/mm_util_webp_testsuite.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 86d803a..102e468 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ ADD_SUBDIRECTORY(imgcv) ADD_SUBDIRECTORY(imgp) ADD_SUBDIRECTORY(jpeg) ADD_SUBDIRECTORY(magick) +ADD_SUBDIRECTORY(webp) IF(UNIX) diff --git a/packaging/libmm-utility.spec b/packaging/libmm-utility.spec index 72f7546..03dd1f8 100644 --- a/packaging/libmm-utility.spec +++ b/packaging/libmm-utility.spec @@ -18,6 +18,8 @@ BuildRequires: pkgconfig(gmodule-2.0) BuildRequires: libjpeg-turbo-devel BuildRequires: pkgconfig(libtzplatform-config) BuildRequires: pkgconfig(opencv) >= 3.4.1 +BuildRequires: pkgconfig(libwebp) +BuildRequires: pkgconfig(libwebpmux) BuildRequires: giflib-devel BuildRequires: pkgconfig(GraphicsMagick) %if 0%{?gtests:1} diff --git a/webp/CMakeLists.txt b/webp/CMakeLists.txt new file mode 100644 index 0000000..b5d4f26 --- /dev/null +++ b/webp/CMakeLists.txt @@ -0,0 +1,73 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +SET(fw_name "mmutil_webp") + +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 libwebp libwebpmux") + +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-webp) +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/webp/include/mm_util_webp.h b/webp/include/mm_util_webp.h new file mode 100644 index 0000000..4d50f0c --- /dev/null +++ b/webp/include/mm_util_webp.h @@ -0,0 +1,43 @@ +/* +* 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_WEBP_H__ +#define __MM_UTIL_WEBP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "mm_util_type.h" + + +typedef void *mm_util_webp_anim_enc_h; + +int mm_util_webp_anim_enc_create(mm_util_webp_anim_enc_h *anim_enc_h); +int mm_util_webp_anim_enc_set_bgcolor(mm_util_webp_anim_enc_h anim_enc_h, unsigned char alpha, unsigned char red, unsigned char green, unsigned char blue); +int mm_util_webp_anim_enc_set_loop_count(mm_util_webp_anim_enc_h anim_enc_h, unsigned int loop_count); +int mm_util_webp_anim_enc_set_lossless(mm_util_webp_anim_enc_h anim_enc_h, bool lossless); +int mm_util_webp_anim_enc_add_image(mm_util_webp_anim_enc_h anim_enc_h, mm_util_image_h image); +int mm_util_webp_anim_enc_save_to_file(mm_util_webp_anim_enc_h anim_enc_h, const char *path); +int mm_util_webp_anim_enc_save_to_buffer(mm_util_webp_anim_enc_h anim_enc_h, void **buf, size_t *buf_size); +void mm_util_webp_anim_enc_destroy(mm_util_webp_anim_enc_h anim_enc_h); + + +#ifdef __cplusplus +} +#endif +#endif /*__MM_UTIL_WEBP_H__*/ diff --git a/webp/mm_util_webp.c b/webp/mm_util_webp.c new file mode 100644 index 0000000..3b8b118 --- /dev/null +++ b/webp/mm_util_webp.c @@ -0,0 +1,331 @@ +/* +* 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 + +#include +#include +#include + +#include "mm_util_webp.h" +#include "mm_util_private.h" + +typedef struct { + WebPAnimEncoder *enc; + WebPMuxAnimParams anim_params; + bool lossless; + unsigned int timestamp; +} mm_util_webp_anim_enc_t; + + +static int __webp_anim_enc_init(mm_util_webp_anim_enc_t *anim_enc, mm_image_info_s *image) +{ + WebPAnimEncoderOptions enc_options; + + mm_util_retvm_if(!anim_enc, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc"); + mm_util_retvm_if(!image, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid image_info"); + + mm_util_retvm_if(!WebPAnimEncoderOptionsInit(&enc_options), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPAnimEncoderOptionsInit"); + + anim_enc->enc = WebPAnimEncoderNew(image->width, image->height, &enc_options); + mm_util_retvm_if(!anim_enc->enc, MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPAnimEncoderNew"); + + anim_enc->timestamp = 0; + + return MM_UTIL_ERROR_NONE; +} + +static void __webp_anim_enc_deinit(mm_util_webp_anim_enc_t *anim_enc) +{ + if (!anim_enc) + return; + + WebPAnimEncoderDelete(anim_enc->enc); + anim_enc->enc = NULL; +} + +static int __mm_image_info_to_webp_picture(mm_image_info_s *image, WebPPicture *picture) +{ + size_t idx = 0; + unsigned char *ptr = NULL, *a = NULL, *r = NULL, *g = NULL, *b = NULL; + unsigned int bpp = 4; + + mm_util_retvm_if(!image, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid image"); + mm_util_retvm_if(!picture, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid picture"); + + picture->width = image->width; + picture->height = image->height; + // use ARGB because of libwebp recommendation(YUV incurs loss). + picture->use_argb = 1; + + mm_util_retvm_if(!WebPPictureAlloc(picture), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPPictureAlloc"); + + // convert 'unsigned char'(LSB) type to 'uint32_t'(MSB) type + // If we did not change the data type, the color of encoded webp was broken. + mm_util_debug("image colorspace: %d", image->color); + + // make uint32_t(MSB) 'ARGB' from unsigned char(LSB) 'ARGB/BGRA/RGBA'. + ptr = (unsigned char*)(image->data); + switch(image->color) { + case MM_UTIL_COLOR_ARGB: + a = ptr; + r = ptr + 1; + g = ptr + 2; + b = ptr + 3; + bpp = 4; + break; + case MM_UTIL_COLOR_BGRA: + a = ptr + 3; + r = ptr + 2; + g = ptr + 1; + b = ptr; + bpp = 4; + break; + case MM_UTIL_COLOR_RGBA: + a = ptr + 3; + r = ptr; + g = ptr + 1; + b = ptr + 2; + bpp = 4; + break; + default: + mm_util_error("not supported colorspace %d", image->color); + WebPPictureFree(picture); + return MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT; + } + + while(idx < (size_t)(picture->argb_stride * picture->height)) { + picture->argb[idx++] = ((*a << 24) | (*r << 16) | (*g << 8) | (*b)); + + a += bpp; + r += bpp; + g += bpp; + b += bpp; + } + + return MM_UTIL_ERROR_NONE; +} + +static int __mm_util_webp_anim_enc_save(mm_util_webp_anim_enc_t *anim_enc, WebPData *webp_data) +{ + WebPMux *mux = NULL; + WebPMuxError err = WEBP_MUX_OK; + + mm_util_retvm_if(!anim_enc, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc"); + mm_util_retvm_if(!webp_data, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid webp_data"); + + mm_util_fenter(); + + // add NULL to end animation + if (!WebPAnimEncoderAdd(anim_enc->enc, NULL, anim_enc->timestamp, NULL)) { + mm_util_error("Error! WebPAnimEncoderAdd(%s)", WebPAnimEncoderGetError(anim_enc->enc)); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + if (!WebPAnimEncoderAssemble(anim_enc->enc, webp_data)) { + mm_util_error("Error! WebPAnimEncoderAssemble(%s)", WebPAnimEncoderGetError(anim_enc->enc)); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + // add animation container into webp + mux = WebPMuxCreate(webp_data, 1); + if (!mux) { + mm_util_error("Error! WebPMuxCreate"); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + err = WebPMuxSetAnimationParams(mux, &anim_enc->anim_params); + if (err != WEBP_MUX_OK) { + mm_util_error("Error! WebPMuxSetAnimationParams(%d)", err); + WebPMuxDelete(mux); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + err = WebPMuxAssemble(mux, webp_data); + if (err != WEBP_MUX_OK) { + mm_util_error("Error! WebPMuxAssemble(%d)", err); + WebPMuxDelete(mux); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + WebPMuxDelete(mux); + + mm_util_fleave(); + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_webp_anim_enc_create(mm_util_webp_anim_enc_h *anim_enc_h) +{ + mm_util_webp_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(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_h = anim_enc; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_webp_anim_enc_set_bgcolor(mm_util_webp_anim_enc_h anim_enc_h, unsigned char alpha, unsigned char red, unsigned char green, unsigned char blue) +{ + mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h; + + mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h"); + + // background color of the animation, default is white + anim_enc->anim_params.bgcolor = (uint32_t)((alpha << 24) | (red << 16) | (green << 8) | blue); + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_webp_anim_enc_set_loop_count(mm_util_webp_anim_enc_h anim_enc_h, unsigned int loop_count) +{ + mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h; + + mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h"); + + // number of times to repeat the animation, default is 0[infinite] + anim_enc->anim_params.loop_count = (int)loop_count; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_webp_anim_enc_set_lossless(mm_util_webp_anim_enc_h anim_enc_h, bool lossless) +{ + mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h; + + mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h"); + + // lossless, default is false due to performance + anim_enc->lossless = lossless; + + return MM_UTIL_ERROR_NONE; +} + +int mm_util_webp_anim_enc_add_image(mm_util_webp_anim_enc_h anim_enc_h, mm_util_image_h image) +{ + int ret = MM_UTIL_ERROR_NONE; + mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h; + mm_image_info_s *frame = (mm_image_info_s *)image; + WebPPicture picture; + WebPConfig config; + + mm_util_fenter(); + + mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h"); + mm_util_retvm_if(!image, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid image"); + + mm_util_retvm_if(!WebPPictureInit(&picture), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPPictureInit"); + mm_util_retvm_if(!WebPConfigInit(&config), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPConfigInit"); + + config.lossless = anim_enc->lossless; + + ret = __mm_image_info_to_webp_picture(frame, &picture); + mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "Error! __mm_image_info_to_webp_picture(%d)", ret); + + // if first frame, create animation container of webp + if (!anim_enc->enc) { + ret = __webp_anim_enc_init(anim_enc, frame); + if (ret != MM_UTIL_ERROR_NONE) { + mm_util_error("Error! __webp_anim_enc_init(%d)", ret); + WebPPictureFree(&picture); + return ret; + } + } + + // add picture into animation container + if (!WebPAnimEncoderAdd(anim_enc->enc, &picture, (int)anim_enc->timestamp, &config)) { + mm_util_error("Error! WebPAnimEncoderAdd(%s)", WebPAnimEncoderGetError(anim_enc->enc)); + __webp_anim_enc_deinit(anim_enc); + ret = MM_UTIL_ERROR_INVALID_OPERATION; + } else { + // convert the delay between frames to timestamp of each frame + anim_enc->timestamp += frame->delay_time; + } + + WebPPictureFree(&picture); + + mm_util_fleave(); + + return ret; +} + +int mm_util_webp_anim_enc_save_to_file(mm_util_webp_anim_enc_h anim_enc_h, const char *path) +{ + int ret = MM_UTIL_ERROR_NONE; + mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h; + WebPData webp_data; + GError *g_error = NULL; + + mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h"); + + WebPDataInit(&webp_data); + + ret = __mm_util_webp_anim_enc_save(anim_enc, &webp_data); + if (ret != MM_UTIL_ERROR_NONE) { + mm_util_error("Error! __mm_util_webp_anim_enc_save(%d)", ret); + goto END; + } + + if (!g_file_set_contents(path, (gchar *)webp_data.bytes, (gssize)webp_data.size, &g_error)) + mm_util_error("Error! g_file_set_contents(%s)", (g_error ? g_error->message : "none")); + + if (g_error) + g_error_free(g_error); +END: + + WebPDataClear(&webp_data); + __webp_anim_enc_deinit(anim_enc); + + return ret; +} + +int mm_util_webp_anim_enc_save_to_buffer(mm_util_webp_anim_enc_h anim_enc_h, void **buf, size_t *buf_size) +{ + int ret = MM_UTIL_ERROR_NONE; + mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h; + WebPData webp_data; + + mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h"); + + WebPDataInit(&webp_data); + + ret = __mm_util_webp_anim_enc_save(anim_enc, &webp_data); + if (ret == MM_UTIL_ERROR_NONE) { + *buf = g_memdup(webp_data.bytes, webp_data.size); + *buf_size = webp_data.size; + } else { + mm_util_error("Error! __mm_util_webp_anim_enc_save(%d)", ret); + } + + WebPDataClear(&webp_data); + __webp_anim_enc_deinit(anim_enc); + + return MM_UTIL_ERROR_NONE; +} + +void mm_util_webp_anim_enc_destroy(mm_util_webp_anim_enc_h anim_enc_h) +{ + if (!anim_enc_h) + return; + + __webp_anim_enc_deinit((mm_util_webp_anim_enc_t *)anim_enc_h); + g_free(anim_enc_h); +} diff --git a/webp/mmutil-webp.pc.in b/webp/mmutil-webp.pc.in new file mode 100644 index 0000000..91f801d --- /dev/null +++ b/webp/mmutil-webp.pc.in @@ -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/webp/test/CMakeLists.txt b/webp/test/CMakeLists.txt new file mode 100644 index 0000000..b459d27 --- /dev/null +++ b/webp/test/CMakeLists.txt @@ -0,0 +1,18 @@ +SET(fw_name "mmutil_webp") +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/webp/test/mm_util_webp_testsuite.c b/webp/test/mm_util_webp_testsuite.c new file mode 100644 index 0000000..fbf71b6 --- /dev/null +++ b/webp/test/mm_util_webp_testsuite.c @@ -0,0 +1,297 @@ +/* +* 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TEST_STRING_VALID(str) ((str != NULL && strlen(str) > 0) ? TRUE : FALSE) + +#define ENCODE_FILE_PATH tzplatform_mkpath(TZ_USER_CONTENT, "webp_test_enc_file.webp") +#define ENCODE_MEM_PATH tzplatform_mkpath(TZ_USER_CONTENT, "webp_test_enc_mem.webp") + +static char *g_path = NULL; + +/* for reading files */ +static GQueue *g_queue_files = NULL; +static GQueue *g_queue_images = NULL; + +static const mm_util_color_format_e g_color = MM_UTIL_COLOR_ARGB; +static const unsigned int g_delay_time = 500; // 500ms +static const int g_loop_count = 0; // if loop_count is 0, it is infinite loop +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("invalid path %s\n", path); + return; + } + + ret = mm_util_decode_image_from_file(path, g_color, &image); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("mm_util_decode_image_from_file failed %d\n", ret); + return; + } + + ret = mm_image_set_delay_time(image, g_delay_time); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("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("No valid image\n"); + return FALSE; + } + + g_print("%u valid image 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; + + dir = g_dir_open(path, 0, &g_error); + if (!dir) { + g_print("invalid dir %s (%s)\n", path, g_error ? g_error->message : "none"); + 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 ((filename = g_dir_read_name(dir)) != NULL) + 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("\tNo test file in directory(%s)!\n", path); + return FALSE; + } + + /* decode files of dir */ + if (!__decode_files_in_queue()) { + g_print("Fail to decode files from dir! %s\n", path); + 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[WEBP_testsuite] invalid path %s\n", argv[1]); + return FALSE; + } + + if (!__set_input_dir(g_path)) { + fprintf(stderr, "\t[WEBP_testsuite] __set_input_dir failed\n"); + return FALSE; + } + + return TRUE; +} + +static void __add_image(gpointer data, gpointer user_data) +{ + int ret = 0; + + ret = mm_util_webp_anim_enc_add_image((mm_util_webp_anim_enc_h)user_data, (mm_util_image_h)data); + if (ret != MM_UTIL_ERROR_NONE) + fprintf(stderr, "\t[WEBP_testsuite] mm_util_webp_anim_enc_add_image failed : %d\n", ret); +} + +static gboolean __test_encode_to_file() +{ + int ret = 0; + mm_util_webp_anim_enc_h anim_enc = NULL; + + ret = mm_util_webp_anim_enc_create(&anim_enc); + if (ret != MM_UTIL_ERROR_NONE) { + fprintf(stderr, "\t[WEBP_testsuite] mm_util_webp_anim_enc_create failed : %d\n", ret); + return FALSE; + } + + ret = mm_util_webp_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[WEBP_testsuite] mm_util_webp_anim_enc_set_bgcolor failed : %d\n", ret); + goto END; + } + + ret = mm_util_webp_anim_enc_set_loop_count(anim_enc, g_loop_count); + if (ret != MM_UTIL_ERROR_NONE) { + fprintf(stderr, "\t[WEBP_testsuite] mm_util_webp_anim_enc_set_loop_count failed : %d\n", ret); + goto END; + } + + g_queue_foreach(g_queue_images, __add_image, anim_enc); + + ret = mm_util_webp_anim_enc_save_to_file(anim_enc, ENCODE_FILE_PATH); + if (ret != MM_UTIL_ERROR_NONE) { + fprintf(stderr, "\t[WEBP_testsuite] mm_util_encode_webp_to_file failed : %d\n", ret); + goto END; + } + +END: + mm_util_webp_anim_enc_destroy(anim_enc); + + if (ret != MM_UTIL_ERROR_NONE) + return FALSE; + + return TRUE; +} + +static gboolean __test_encode_to_buffer() +{ + int ret = 0; + mm_util_webp_anim_enc_h anim_enc = NULL; + /* for encoding webp to memory */ + void *encoded_data = NULL; + size_t encoded_size = 0; + GError *g_error = NULL; + + ret = mm_util_webp_anim_enc_create(&anim_enc); + if (ret != MM_UTIL_ERROR_NONE) { + fprintf(stderr, "\t[WEBP_testsuite] mm_util_webp_anim_enc_create failed : %d\n", ret); + return FALSE; + } + + ret = mm_util_webp_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[WEBP_testsuite] mm_util_webp_anim_enc_set_bgcolor failed : %d\n", ret); + goto END; + } + + ret = mm_util_webp_anim_enc_set_loop_count(anim_enc, g_loop_count); + if (ret != MM_UTIL_ERROR_NONE) { + fprintf(stderr, "\t[WEBP_testsuite] mm_util_webp_anim_enc_set_loop_count failed : %d\n", ret); + goto END; + } + + g_queue_foreach(g_queue_images, __add_image, anim_enc); + + ret = mm_util_webp_anim_enc_save_to_buffer(anim_enc, &encoded_data, &encoded_size); + if (ret != MM_UTIL_ERROR_NONE) { + fprintf(stderr, "\t[WEBP_testsuite] mm_util_webp_anim_enc_save_to_buffer failed : %d\n", ret); + goto END; + } + if (!g_file_set_contents(ENCODE_MEM_PATH, (gchar *)encoded_data, (gssize)encoded_size, &g_error)) { + fprintf(stderr, "\t[WEBP_testsuite] g_file_set_contents failed : %s\n", ENCODE_MEM_PATH); + if (g_error) + g_error_free(g_error); + goto END; + } + +END: + mm_util_webp_anim_enc_destroy(anim_enc); + + g_free(encoded_data); + + 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[WEBP_testsuite] _get_arguments failed\n"); + goto out; + } + + /* test encoding animation webp to file */ + if (FALSE == __test_encode_to_file()) { + fprintf(stderr, "\t[WEBP_testsuite] __test_encode_to_file failed\n"); + goto out; + } + + /* test encoding animation webp to buffer */ + if (FALSE == __test_encode_to_buffer()) { + fprintf(stderr, "\t[WEBP_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; +} + -- 2.7.4