From: jiyong.min Date: Tue, 12 Apr 2022 08:19:35 +0000 (+0900) Subject: Add JPEG-XL decode/encode functions X-Git-Tag: submit/tizen/20220422.045752^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=54f23543abbc3b7ea8aaa81224d6b73e1f74e59f;p=platform%2Fcore%2Fmultimedia%2Flibmm-utility.git Add JPEG-XL decode/encode functions Change-Id: I970b99074965a0374cd61ecd2484091ba5e88d56 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index aef68f6..8ef328b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ ADD_SUBDIRECTORY(magick) ADD_SUBDIRECTORY(webp) ADD_SUBDIRECTORY(anim) ADD_SUBDIRECTORY(heif) +ADD_SUBDIRECTORY(jxl) IF(UNIX) diff --git a/common/include/mm_util_option.h b/common/include/mm_util_option.h index c5b54aa..ffcfce7 100755 --- a/common/include/mm_util_option.h +++ b/common/include/mm_util_option.h @@ -33,7 +33,7 @@ typedef void *mm_util_enc_opt_h; int mm_util_enc_opt_create(mm_util_enc_opt_h *enc_opt); int mm_util_enc_opt_set_codec(mm_util_enc_opt_h enc_opt, mm_util_img_codec_type codec); int mm_util_enc_opt_set_png_compression(mm_util_enc_opt_h enc_opt, unsigned int compression); -int mm_util_enc_opt_set_webp_lossless(mm_util_enc_opt_h enc_opt, bool lossless); +int mm_util_enc_opt_set_lossless(mm_util_enc_opt_h enc_opt, bool lossless); void mm_util_enc_opt_destroy(mm_util_enc_opt_h enc_opt); #ifdef __cplusplus diff --git a/common/include/mm_util_type.h b/common/include/mm_util_type.h index 76c65ee..3846d2a 100755 --- a/common/include/mm_util_type.h +++ b/common/include/mm_util_type.h @@ -93,6 +93,7 @@ typedef enum { IMG_CODEC_GIF, IMG_CODEC_BMP, IMG_CODEC_WEBP, + IMG_CODEC_JPEGXL, IMG_CODEC_WBMP = 100, // used by only media-content IMG_CODEC_UNKNOWN_TYPE, } mm_util_img_codec_type; diff --git a/common/mm_util_option.c b/common/mm_util_option.c index 91a7c96..c898423 100755 --- a/common/mm_util_option.c +++ b/common/mm_util_option.c @@ -62,12 +62,12 @@ int mm_util_enc_opt_set_png_compression(mm_util_enc_opt_h enc_opt, unsigned int return MM_UTIL_ERROR_NONE; } -int mm_util_enc_opt_set_webp_lossless(mm_util_enc_opt_h enc_opt, bool lossless) +int mm_util_enc_opt_set_lossless(mm_util_enc_opt_h enc_opt, bool lossless) { mm_util_enc_opt_t *enc_option = (mm_util_enc_opt_t *)enc_opt; mm_util_retvm_if(!enc_opt, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid enc_opt"); - mm_util_retvm_if(enc_option->codec != IMG_CODEC_WEBP, MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT, "not supported format [%d]", enc_option->codec); + mm_util_retvm_if(enc_option->codec != IMG_CODEC_WEBP && enc_option->codec != IMG_CODEC_JPEGXL, MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT, "not supported format [%d]", enc_option->codec); enc_option->lossless = lossless; diff --git a/jxl/CMakeLists.txt b/jxl/CMakeLists.txt new file mode 100644 index 0000000..eb8288a --- /dev/null +++ b/jxl/CMakeLists.txt @@ -0,0 +1,73 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +SET(fw_name "mmutil_jxl") + +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 + ../jpeg/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 jxl jxl_threads) +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-jxl) +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/jxl/include/mm_util_jxl.h b/jxl/include/mm_util_jxl.h new file mode 100644 index 0000000..8022d85 --- /dev/null +++ b/jxl/include/mm_util_jxl.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 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_JPEGXL_H__ +#define __MM_UTIL_JPEGXL_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int mm_util_decode_jxl_from_file(const char *path, mm_util_color_format_e format, mm_util_image_h *decoded_image); +int mm_util_decode_jxl_from_buffer(const void *encoded_buf, size_t encoded_buf_size, mm_util_color_format_e format, mm_util_image_h *decoded_image); +int mm_util_encode_jxl_to_file(mm_util_image_h decoded_image, mm_util_enc_opt_h enc_opt, const char *path); +int mm_util_encode_jxl_to_buffer(mm_util_image_h decoded_image, mm_util_enc_opt_h enc_opt, void **encoded_buf, size_t *encoded_buf_size); + +#ifdef __cplusplus +} +#endif + +#endif /*__MM_UTIL_JPEGXL_H__*/ diff --git a/jxl/mm_util_jxl.c b/jxl/mm_util_jxl.c new file mode 100644 index 0000000..34ee730 --- /dev/null +++ b/jxl/mm_util_jxl.c @@ -0,0 +1,501 @@ +/* +* Copyright (c) 2022 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 + +#include "mm_util_jxl.h" +#include "mm_util_private.h" + + +// TODO: it will be moved into .ini of mmfw_config +#define NUM_OF_THREADS 4 + + +static JxlPixelFormat jxl_formats[] = { + [MM_UTIL_COLOR_RGB24] = {3, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0}, // RGB -> RGB + [MM_UTIL_COLOR_RGBA] = {4, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0}, // RGBA -> RGBA +}; + +static int __convert_dec_error(JxlDecoderStatus status) +{ + int err = MM_UTIL_ERROR_NONE; + + switch (status) { + case JXL_DEC_SUCCESS: + err = MM_UTIL_ERROR_NONE; + break; + case JXL_DEC_NEED_MORE_INPUT: + case JXL_DEC_NEED_PREVIEW_OUT_BUFFER: + case JXL_DEC_NEED_DC_OUT_BUFFER: + case JXL_DEC_NEED_IMAGE_OUT_BUFFER: + err = MM_UTIL_ERROR_INVALID_PARAMETER; + break; + case JXL_DEC_JPEG_NEED_MORE_OUTPUT: + err = MM_UTIL_ERROR_OUT_OF_MEMORY; + break; + case JXL_DEC_ERROR: + default: + err = MM_UTIL_ERROR_INVALID_OPERATION; + break; + } + + mm_util_warn("convert err(%d) from status(%d)", err, status); + + return err; +} + +static int __convert_enc_error(JxlEncoderStatus status) +{ + int err = MM_UTIL_ERROR_NONE; + + switch (status) { + case JXL_ENC_SUCCESS: + err = MM_UTIL_ERROR_NONE; + break; + case JXL_ENC_NEED_MORE_OUTPUT: + err = MM_UTIL_ERROR_OUT_OF_MEMORY; + break; + case JXL_ENC_NOT_SUPPORTED: + err = MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT; + break; + case JXL_ENC_ERROR: + default: + err = MM_UTIL_ERROR_INVALID_OPERATION; + break; + } + + mm_util_warn("convert err(%d) from status(%d)", err, status); + + return err; +} + +static int __get_file_size(const char *path, size_t *size) +{ + GStatBuf buf; + + mm_util_retvm_if(!path, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid path"); + mm_util_retvm_if(!size, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid size"); + + mm_util_fenter(); + + if (g_stat(path, &buf) < 0) { + mm_util_stderror("Fail to open path"); + return MM_UTIL_ERROR_INVALID_PARAMETER; + } + + *size = (size_t)buf.st_size; + + mm_util_info("size: %zu", *size); + + mm_util_fleave(); + + return MM_UTIL_ERROR_NONE; +} + +static int __get_decoded_data(mm_util_image_h decoded, uint32_t *width, uint32_t *height, JxlPixelFormat *format, uint8_t **pixels, size_t *pixels_size) +{ + mm_image_info_s *_image = (mm_image_info_s *)decoded; + + mm_util_retvm_if(!mm_image_is_valid_image(decoded), MM_UTIL_ERROR_INVALID_PARAMETER, "invalid decoded"); + mm_util_retvm_if((_image->color != MM_UTIL_COLOR_RGB24) && (_image->color != MM_UTIL_COLOR_RGBA), + MM_UTIL_ERROR_INVALID_PARAMETER, "invalid format %d", _image->color); + mm_image_debug_image(decoded, "SOURCE"); + + *width = (uint32_t)_image->width; + *height = (uint32_t)_image->height; + *format = jxl_formats[_image->color]; + *pixels = _image->data; + *pixels_size = _image->size; + + return MM_UTIL_ERROR_NONE; +} + +static bool __read_file(const char *path, void **data, size_t size) +{ + gsize read_n = 0; + gchar *read_data = NULL; + GError *error = NULL; + + mm_util_retvm_if(!path, false, "invalid path"); + mm_util_retvm_if(!data, false, "invalid data"); + mm_util_retvm_if(size == 0, false, "invalid size"); + + mm_util_fenter(); + + if (!g_file_get_contents(path, &read_data, &read_n, &error)) { + mm_util_error("g_file_get_contents error(%s)", (error ? error->message : "none")); + if (error) + g_error_free(error); + return false; + } + + if (read_n != size) { + mm_util_stderror("read"); + mm_util_error("read_n: %zu, size: %zu", read_n, size); + g_free(read_data); + return false; + } + + *data = read_data; + + mm_util_fleave(); + + return true; +} + +static bool __write_file(const char *path, void *data, size_t size) +{ + GError *error = NULL; + + mm_util_retvm_if(!path, false, "invalid path"); + mm_util_retvm_if(!data, false, "invalid data"); + mm_util_retvm_if(size == 0, false, "invalid size"); + + mm_util_fenter(); + + if (!g_file_set_contents(path, (const gchar *)data, (gssize)size, &error)) { + mm_util_error("g_file_set_contents error(%s)", (error ? error->message : "none")); + if (error) + g_error_free(error); + return false; + } + + mm_util_fleave(); + + return true; +} + +static int __mm_util_decode_jpegxl(const void *buf, size_t buf_size, mm_util_color_format_e format, mm_util_image_h *decoded_image) +{ + int ret = MM_UTIL_ERROR_NONE; + JxlDecoderStatus status = JXL_DEC_ERROR; + JxlDecoder *jxl_dec = NULL; + void *jxl_thread = NULL; + JxlPixelFormat *jxl_format = &jxl_formats[format]; + JxlBasicInfo info; + uint8_t* pixels = NULL; + size_t pixels_size = 0; + + mm_util_retvm_if(!buf, MM_UTIL_ERROR_INVALID_PARAMETER, "buf is null"); + mm_util_retvm_if(buf_size == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid buf_size %zu", buf_size); + mm_util_retvm_if((format != MM_UTIL_COLOR_RGB24) && (format != MM_UTIL_COLOR_RGBA), + MM_UTIL_ERROR_INVALID_PARAMETER, "invalid format %d", format); + mm_util_retvm_if(!decoded_image, MM_UTIL_ERROR_INVALID_PARAMETER, "decoded_image is null"); + + mm_util_fenter(); + + jxl_dec = JxlDecoderCreate(NULL); + if (!jxl_dec) { + mm_util_error("failed to JxlDecoderCreate"); + goto Exit; + } + + jxl_thread = JxlThreadParallelRunnerCreate(NULL, NUM_OF_THREADS); + if (!jxl_thread) { + mm_util_error("failed to JxlThreadParallelRunnerCreate"); + goto Exit; + } + + status = JxlDecoderSetParallelRunner(jxl_dec, JxlThreadParallelRunner, jxl_thread); + if (status != JXL_DEC_SUCCESS) { + mm_util_error("failed to JxlDecoderSetParallelRunner(%d)", status); + goto Exit; + } + + // set the events for process to invoke + status = JxlDecoderSubscribeEvents(jxl_dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE); + if (status != JXL_DEC_SUCCESS) { + mm_util_error("failed to JxlDecoderSubscribeEvents(%d)", status); + goto Exit; + } + + status = JxlDecoderSetInput(jxl_dec, buf, buf_size); + if (status != JXL_DEC_SUCCESS) { + mm_util_error("failed to JxlDecoderSetInput(%d)", status); + goto Exit; + } + + // process to start container parsing + status = JxlDecoderProcessInput(jxl_dec); + if (status != JXL_DEC_BASIC_INFO) { + mm_util_error("failed to JxlDecoderProcessInput to start parsing (%d)", status); + goto Exit; + } + + status = JxlDecoderImageOutBufferSize(jxl_dec, jxl_format, &pixels_size); + if (status != JXL_DEC_SUCCESS) { + mm_util_error("failed to JxlDecoderImageOutBufferSize(%d)", status); + goto Exit; + } + + status = JxlDecoderGetBasicInfo(jxl_dec, &info); + if (status != JXL_DEC_SUCCESS) { + mm_util_error("failed to JxlDecoderGetBasicInfo(%d)", status); + goto Exit; + } + + // calculate the size of output buffer + pixels = g_malloc0(pixels_size); + + // process to get frame + status = JxlDecoderProcessInput(jxl_dec); + if (status != JXL_DEC_FRAME) { + mm_util_error("failed to JxlDecoderProcessInput to get frame (%d)", status); + goto Exit; + } + + // process to set output buffer + status = JxlDecoderProcessInput(jxl_dec); + if (status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + mm_util_error("failed to JxlDecoderProcessInput to set output buffer (%d)", status); + goto Exit; + } + + status = JxlDecoderSetImageOutBuffer(jxl_dec, jxl_format, pixels, pixels_size); + if (status != JXL_DEC_SUCCESS) { + mm_util_error("failed to JxlDecoderSetImageOutBuffer(%d)", status); + goto Exit; + } + + // process to get full image + status = JxlDecoderProcessInput(jxl_dec); + if (status != JXL_DEC_FULL_IMAGE) { + mm_util_error("failed to JxlDecoderProcessInput to get full image (%d)", status); + goto Exit; + } + + // process to finish decoding + status = JxlDecoderProcessInput(jxl_dec); + if (status != JXL_DEC_SUCCESS) { + mm_util_error("failed to JxlDecoderProcessInput to finish decoding (%d)", status); + goto Exit; + } + + ret = mm_image_create_image(info.xsize, info.ysize, format, pixels, pixels_size, decoded_image); + if (ret != MM_UTIL_ERROR_NONE) { + mm_util_error("failed to mm_image_create_image"); + goto Exit; + } + + mm_image_debug_image(decoded_image, "RESULT"); + +Exit: + + g_free(pixels); + + JxlThreadParallelRunnerDestroy(jxl_thread); + JxlDecoderDestroy(jxl_dec); + + mm_util_fleave(); + + return __convert_dec_error(status); +} + +static int __mm_util_encode_jpegxl(mm_util_image_h decoded_image, mm_util_enc_opt_t *enc_opt, void **buf, size_t *buf_size) +{ + int ret = MM_UTIL_ERROR_NONE; + JxlEncoderStatus status = JXL_ENC_ERROR; + JxlEncoder *jxl_enc = NULL; + void *jxl_thread = NULL; + JxlPixelFormat jxl_format; + JxlBasicInfo basic_info; + JxlColorEncoding color_encoding; + JxlEncoderOptions* options = NULL; + uint8_t *pixels = NULL; + size_t pixels_size = 0; + size_t compressed_size = 64; + uint8_t *compressed = NULL; + uint8_t *next_out = NULL; + size_t avail_out = 0; + + mm_util_retvm_if(!buf, MM_UTIL_ERROR_INVALID_PARAMETER, "buf is null"); + mm_util_retvm_if(!buf_size, MM_UTIL_ERROR_INVALID_PARAMETER, "buf_size is null"); + + mm_util_fenter(); + + JxlEncoderInitBasicInfo(&basic_info); + + ret = __get_decoded_data(decoded_image, &basic_info.xsize, &basic_info.ysize, + &jxl_format, &pixels, &pixels_size); + if (ret != MM_UTIL_ERROR_NONE) { + mm_util_error("failed to __get_decoded_data"); + return ret; + } + + basic_info.uses_original_profile = (enc_opt && enc_opt->lossless) ? JXL_TRUE : JXL_FALSE; + basic_info.num_color_channels = jxl_format.num_channels; + if (jxl_format.num_channels == 4) + basic_info.alpha_bits = basic_info.bits_per_sample; + + mm_util_info("num_color_channels = %u, alpha_bits = %u", + basic_info.num_color_channels, basic_info.alpha_bits); + + jxl_enc = JxlEncoderCreate(NULL); + if (!jxl_enc) { + mm_util_error("failed to JxlEncoderCreate"); + goto Exit; + } + + jxl_thread = JxlThreadParallelRunnerCreate(NULL, NUM_OF_THREADS); + if (!jxl_thread) { + mm_util_error("failed to JxlThreadParallelRunnerCreate"); + goto Exit; + } + + status = JxlEncoderSetParallelRunner(jxl_enc, JxlThreadParallelRunner, jxl_thread); + if (status != JXL_ENC_SUCCESS) { + mm_util_error("failed to JxlDecoderSetParallelRunner(%d)", status); + goto Exit; + } + + status = JxlEncoderUseContainer(jxl_enc, JXL_TRUE); + if (status != JXL_ENC_SUCCESS) { + mm_util_error("failed to JxlEncoderUseContainer(%d)", status); + goto Exit; + } + + status = JxlEncoderSetBasicInfo(jxl_enc, &basic_info); + if (status != JXL_ENC_SUCCESS) { + mm_util_error("failed to JxlEncoderSetBasicInfo(%d)", status); + goto Exit; + } + + JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE); + status = JxlEncoderSetColorEncoding(jxl_enc, &color_encoding); + if (status != JXL_ENC_SUCCESS) { + mm_util_error("failed to JxlEncoderSetColorEncoding(%d)", status); + goto Exit; + } + + // set default options + // 'options' will be destroyed when 'JxlEncoderDestroy' is called + options = JxlEncoderOptionsCreate(jxl_enc, NULL); + + // set lossless into options + status = JxlEncoderOptionsSetLossless(options, (enc_opt && enc_opt->lossless) ? JXL_TRUE : JXL_FALSE); + if (status != JXL_ENC_SUCCESS) { + mm_util_error("failed to JxlEncoderOptionsSetLossless(%d)", status); + goto Exit; + } + + status = JxlEncoderAddImageFrame(options, &jxl_format, pixels, pixels_size); + if (status != JXL_ENC_SUCCESS) { + mm_util_error("failed to JxlEncoderAddImageFrame(%d)", status); + goto Exit; + } + + JxlEncoderCloseInput(jxl_enc); + + compressed = g_malloc0(compressed_size); + next_out = compressed; + avail_out = compressed_size; + + status = JXL_ENC_NEED_MORE_OUTPUT; + while (status == JXL_ENC_NEED_MORE_OUTPUT) { + status = JxlEncoderProcessOutput(jxl_enc, &next_out, &avail_out); + mm_util_debug("JxlEncoderProcessOutput status(%d)", status); + if (status == JXL_ENC_NEED_MORE_OUTPUT) { + size_t offset = next_out - compressed; + compressed_size *= 2; + compressed = g_realloc(compressed, compressed_size); + next_out = compressed + offset; + avail_out = compressed_size - offset; + } + } + + mm_util_warn("JxlEncoderProcessOutput was completed"); + + if (status != JXL_ENC_SUCCESS) { + mm_util_error("failed to JxlEncoderProcessOutput(%d)", status); + goto Exit; + } + + compressed_size -= (next_out - compressed); + *buf = g_memdup2(compressed, next_out - compressed); + *buf_size = next_out - compressed; + + mm_util_info("[Success] buffer: %p, size: %zu", *buf, *buf_size); + +Exit: + + g_free(compressed); + JxlThreadParallelRunnerDestroy(jxl_thread); + JxlEncoderDestroy(jxl_enc); + + mm_util_fleave(); + + return __convert_enc_error(status); +} + +int mm_util_decode_jxl_from_file(const char *path, mm_util_color_format_e format, mm_util_image_h *decoded_image) +{ + int ret = MM_UTIL_ERROR_NONE; + void *encoded_buf = NULL; + size_t encoded_buf_size = 0; + + mm_util_retvm_if(__get_file_size(path, &encoded_buf_size) != MM_UTIL_ERROR_NONE, + MM_UTIL_ERROR_INVALID_PARAMETER, "invalid path %s", path); + mm_util_retvm_if(!__read_file(path, &encoded_buf, encoded_buf_size), + MM_UTIL_ERROR_INVALID_PARAMETER, "invalid path %s:%zu", path, encoded_buf_size); + + ret =__mm_util_decode_jpegxl(encoded_buf, encoded_buf_size, format, decoded_image); + + g_free(encoded_buf); + + return ret; +} + +int mm_util_decode_jxl_from_buffer(const void *encoded_buf, size_t encoded_buf_size, mm_util_color_format_e format, mm_util_image_h *decoded_image) +{ + return __mm_util_decode_jpegxl(encoded_buf, encoded_buf_size, format, decoded_image); +} + +int mm_util_encode_jxl_to_file(mm_util_image_h decoded_image, mm_util_enc_opt_h enc_opt, const char *path) +{ + int ret = MM_UTIL_ERROR_NONE; + void *encoded_buf = NULL; + size_t encoded_buf_size = 0; + + ret = __mm_util_encode_jpegxl(decoded_image, (mm_util_enc_opt_t *)enc_opt, &encoded_buf, &encoded_buf_size); + if (ret != MM_UTIL_ERROR_NONE) { + mm_util_error("failed to __mm_util_encode_jxl(%d)", ret); + return ret; + } + + if (!__write_file(path, encoded_buf, encoded_buf_size)) { + mm_util_error("failed to __write_file"); + ret = MM_UTIL_ERROR_INVALID_PARAMETER; + } + + g_free(encoded_buf); + + return ret; +} + +int mm_util_encode_jxl_to_buffer(mm_util_image_h decoded_image, mm_util_enc_opt_h enc_opt, void **encoded_buf, size_t *encoded_buf_size) +{ + return __mm_util_encode_jpegxl(decoded_image, (mm_util_enc_opt_t *)enc_opt, encoded_buf, encoded_buf_size); +} diff --git a/jxl/mmutil-jxl.pc.in b/jxl/mmutil-jxl.pc.in new file mode 100644 index 0000000..91f801d --- /dev/null +++ b/jxl/mmutil-jxl.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/jxl/test/CMakeLists.txt b/jxl/test/CMakeLists.txt new file mode 100644 index 0000000..8e99fe5 --- /dev/null +++ b/jxl/test/CMakeLists.txt @@ -0,0 +1,18 @@ +SET(fw_name "mmutil_jxl") +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 -Werror") + +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_jpeg) +ENDFOREACH() diff --git a/jxl/test/mm_util_jxl_testsuite.c b/jxl/test/mm_util_jxl_testsuite.c new file mode 100644 index 0000000..5b3b449 --- /dev/null +++ b/jxl/test/mm_util_jxl_testsuite.c @@ -0,0 +1,369 @@ +/* +* Copyright (c) 2022 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 + +#define SAFE_FREE(x) { g_free(x); x = NULL; } +#define SAFE_IMAGE_FREE(x) { if (x != NULL) { mm_image_destroy_image(x); x = NULL; } } +#define MAX_QUALITY 4 + +typedef enum { + TEST_AUTO, + TEST_DECODE_FILE, + TEST_DECODE_BUFFER, + TEST_ENCODE_FILE, + TEST_ENCODE_BUFFER, + TEST_NUM, +} jxl_test_mode_e; + +static char *MODE_TO_STR[] = { + "AUTO", + "DECODE_FILE", + "DECODE_BUFFER", + "ENCODE_FILE", + "ENCODE_BUFFER", + "", +}; + +/* for arguments */ +static int g_test_mode = 0; +static char *g_path = NULL; +static unsigned int g_width = 0; +static unsigned int g_height = 0; +static int g_color = MM_UTIL_COLOR_RGB24; +static bool g_lossless = false; + +/* for reading file */ +static gchar *g_readed_data = NULL; +static gsize g_readed_size = 0; + +/* for saving result */ +static const char *g_test_filename[TEST_NUM][MM_UTIL_COLOR_NUM] = { + [TEST_DECODE_FILE] = { + [MM_UTIL_COLOR_RGB24] = "/opt/usr/home/owner/media/test_jxl_file_rgb24.jpg", + [MM_UTIL_COLOR_RGBA] = "/opt/usr/home/owner/media/test_jxl_file_rgba.jpg", + }, + [TEST_DECODE_BUFFER] = { + [MM_UTIL_COLOR_RGB24] = "/opt/usr/home/owner/media/test_jxl_buff_rgb24.jpg", + [MM_UTIL_COLOR_RGBA] = "/opt/usr/home/owner/media/test_jxl_buff_rgba.jpg", + }, + [TEST_ENCODE_FILE] = { + [MM_UTIL_COLOR_RGB24] = "/opt/usr/home/owner/media/test_jxl_file_rgb24.jxl", + [MM_UTIL_COLOR_RGBA] = "/opt/usr/home/owner/media/test_jxl_file_rgba.jxl", + }, + [TEST_ENCODE_BUFFER] = { + [MM_UTIL_COLOR_RGB24] = "/opt/usr/home/owner/media/test_jxl_buff_rgb24.jxl", + [MM_UTIL_COLOR_RGBA] = "/opt/usr/home/owner/media/test_jxl_buff_rgba.jxl", + }, +}; + +static mm_util_image_h g_decoded_data = NULL; +static mm_util_enc_opt_h g_enc_opt = NULL; + +static gboolean __get_input_data(const char *argv, const long min, const long max, int *data) +{ + if (!argv || strlen(argv) == 0) + return FALSE; + + if (!data) + return FALSE; + + long temp = g_ascii_strtoll(argv, NULL, 10); + if (temp < min || temp > max) + return FALSE; + + *data = (int)temp; + + return TRUE; +} + +static void __print_help(const char *argv0) +{ + g_print("\t[usage]\n"); + g_print("\t\t1. decode : %s mode path color_format(opt.) lossless(opt.)\n", argv0); + g_print("\t\t2. mode : %d - auto, %d - decode from file, %d - decode from buffer, %d - encode to file, %d - encode to buffer\n", + TEST_AUTO, TEST_DECODE_FILE, TEST_DECODE_BUFFER, TEST_ENCODE_FILE, TEST_ENCODE_BUFFER); + g_print("\t\t\t e.g. %s %d test.jxl\n", argv0, TEST_AUTO); + g_print("\t\t\t e.g. %s %d test.jxl 7\n", argv0, TEST_DECODE_FILE); + g_print("\t\t\t e.g. %s %d test.jxl 1920 1280 7\n", argv0, TEST_ENCODE_FILE); +} + +static gboolean __get_arguments(int argc, char *argv[]) +{ + int width = 0, height = 0; + int lossless = 0; + + if (!__get_input_data(argv[1], TEST_AUTO, TEST_NUM - 1, &g_test_mode)) { + g_print("\t[JXL_testsuite] wrong mode(%s) for test\n", argv[1]); + __print_help(argv[0]); + return FALSE; + } + + g_path = g_strdup(argv[2]); + + if (g_test_mode == TEST_AUTO) { + // optional : lossless + if (!__get_input_data(argv[3], 0, 1, &lossless)) + g_print("\t[JXL_testsuite] lossless is default(%d)\n", g_lossless); + else + g_lossless = (lossless == 1) ? true : false; + } else if ((g_test_mode == TEST_DECODE_FILE) || (g_test_mode == TEST_DECODE_BUFFER)) { + if (!__get_input_data(argv[3], MM_UTIL_COLOR_YUV420, MM_UTIL_COLOR_NUM - 1, &g_color)) + g_print("\t[JXL_testsuite] color is default(%d)\n", g_color); + } else if (g_test_mode == TEST_ENCODE_FILE || g_test_mode == TEST_ENCODE_BUFFER) { + if (argc < 5) { + g_print("\t[JPEG_testsuite] not enough args\n"); + __print_help(argv[0]); + return FALSE; + } + + if (!__get_input_data(argv[3], 0, INT_MAX, &width)) { + g_print("\t[JXL_testsuite] wrong width %s\n", argv[3]); + return FALSE; + } + g_width = (unsigned int)width; + + if (!__get_input_data(argv[4], 0, INT_MAX, &height)) { + g_print("\t[JXL_testsuite] wrong height %s\n", argv[4]); + return FALSE; + } + g_height = (unsigned int)height; + + if (!__get_input_data(argv[5], 0, MM_UTIL_COLOR_NUM - 1, &g_color)) { + g_print("\t[JXL_testsuite] wrong color %s\n", argv[5]); + return FALSE; + } + + // optional : lossless + if (!__get_input_data(argv[6], 0, 1, &lossless)) + g_print("\t[JXL_testsuite] lossless is default(%d)\n", g_lossless); + else + g_lossless = (lossless == 1) ? true : false; + } else { + g_print("\t[JXL_testsuite] wrong mode for test %s\n", argv[1]); + return FALSE; + } + + return TRUE; +} + +static gboolean __get_decoded_data(void) +{ + int ret = 0; + GError *error = NULL; + + if (!g_file_get_contents(g_path, &g_readed_data, &g_readed_size, &error)) { + g_print("\t[JXL_testsuite] reading file error(%s)\n", (error ? error->message : "none")); + if (error) + g_error_free(error); + return FALSE; + } + ret = mm_image_create_image(g_width, g_height, g_color, + (const unsigned char*)g_readed_data, g_readed_size, &g_decoded_data); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[JXL_testsuite] mm_image_create_image failed : %d\n", ret); + return FALSE; + } + + return TRUE; +} + +static void __set_enc_opt(bool lossless) +{ + int ret = 0; + + if (!g_enc_opt) { + ret = mm_util_enc_opt_create(&g_enc_opt); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[JXL_testsuite] mm_util_enc_opt_create failed : %d\n", ret); + return; + } + + ret = mm_util_enc_opt_set_codec(g_enc_opt, IMG_CODEC_JPEGXL); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[JXL_testsuite] mm_util_enc_opt_set_codec failed : %d\n", ret); + return; + } + } + + ret = mm_util_enc_opt_set_lossless(g_enc_opt, lossless); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[JXL_testsuite] mm_util_enc_opt_set_lossless failed : %d\n", ret); + return; + } + g_print("\t[JXL_testsuite] __set_enc_opt lossless: %s\n", (lossless) ? "lossless" : "lossy"); +} + +static gboolean __test_decode(const jxl_test_mode_e mode, mm_util_color_format_e color) +{ + int ret = 0; + GError *error = NULL; + + SAFE_FREE(g_readed_data); + SAFE_IMAGE_FREE(g_decoded_data); + + if ((mode != TEST_DECODE_FILE) && (mode != TEST_DECODE_BUFFER)) + return TRUE; + + /* test decoding jxl */ + if (mode == TEST_DECODE_FILE) { + ret = mm_util_decode_jxl_from_file(g_path, color, &g_decoded_data); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[JXL_testsuite] mm_util_decode_jxl_from_file failed %d\n", ret); + return FALSE; + } + } else if (mode == TEST_DECODE_BUFFER) { + if (!g_file_get_contents(g_path, &g_readed_data, &g_readed_size, &error)) { + g_print("\t[JXL_testsuite] reading file error(%s)\n", (error ? error->message : "none")); + if (error) + g_error_free(error); + return FALSE; + } + + ret = mm_util_decode_jxl_from_buffer(g_readed_data, g_readed_size, color, &g_decoded_data); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[JXL_testsuite] mm_util_decode_jxl_from_buffer failed %d\n", ret); + return FALSE; + } + } + + ret = mm_util_jpeg_encode_to_file(g_decoded_data, 100, g_test_filename[mode][color]); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[JXL_testsuite] mm_util_jpeg_encode_to_file failed %d : %s\n", ret, g_test_filename[mode][color]); + return FALSE; + } + + return TRUE; +} + +static gboolean __test_encode(const jxl_test_mode_e mode, mm_util_color_format_e color) +{ + int ret = 0; + void *encoded_data = NULL; + size_t encoded_size = 0; + GError *error = NULL; + + if ((mode != TEST_ENCODE_FILE) && (mode != TEST_ENCODE_BUFFER)) + return TRUE; + + /* test encoding jxl */ + if (mode == TEST_ENCODE_FILE) { + ret = mm_util_encode_jxl_to_file(g_decoded_data, g_enc_opt, g_test_filename[mode][color]); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[JXL_testsuite] mm_util_encode_jxl_to_file failed : %d\n", ret); + return FALSE; + } + } else if (mode == TEST_ENCODE_BUFFER) { + ret = mm_util_encode_jxl_to_buffer(g_decoded_data, g_enc_opt, &encoded_data, &encoded_size); + if (ret != MM_UTIL_ERROR_NONE) { + g_print("\t[JXL_testsuite] mm_util_encode_jxl_to_buffer failed : %d\n", ret); + SAFE_FREE(encoded_data); + return FALSE; + } + if (FALSE == g_file_set_contents(g_test_filename[mode][color], encoded_data, encoded_size, &error)) + g_print("\t[JXL_testsuite] g_file_set_contents failed : %s\n", (error ? error->message : "none")); + + SAFE_FREE(encoded_data); + } + + return TRUE; +} + +static void __test_auto(void) +{ + mm_util_color_format_e color = 0; + gboolean result = FALSE; + + /* test for both decoding & encoding */ + for (color = 0; color < MM_UTIL_COLOR_NUM; color++) { + if (g_test_filename[TEST_DECODE_FILE][color]) { // check supported + result = __test_decode(TEST_DECODE_FILE, color); + g_print("\t[JXL_testsuite] >>>>>>>>>>>>>>>>>>>>>> \'%s(%d)\' TEST %s\n", + MODE_TO_STR[TEST_DECODE_FILE], color, (result) ? "SUCCESS" : "FAIL"); + } + + if (g_test_filename[TEST_ENCODE_FILE][color]) { // check supported + result = __test_encode(TEST_ENCODE_FILE, color); + g_print("\t[JXL_testsuite] >>>>>>>>>>>>>>>>>>>>>> \'%s(%d)\' TEST %s\n", + MODE_TO_STR[TEST_ENCODE_FILE], color, (result) ? "SUCCESS" : "FAIL"); + } + + if (g_test_filename[TEST_DECODE_BUFFER][color]) { // check supported + result = __test_decode(TEST_DECODE_BUFFER, color); + g_print("\t[JXL_testsuite] >>>>>>>>>>>>>>>>>>>>>> \'%s(%d)\' TEST %s\n", + MODE_TO_STR[TEST_DECODE_BUFFER], color, (result) ? "SUCCESS" : "FAIL"); + } + + if (g_test_filename[TEST_ENCODE_BUFFER][color]) { // check supported + result = __test_encode(TEST_ENCODE_BUFFER, color); + g_print("\t[JXL_testsuite] >>>>>>>>>>>>>>>>>>>>>> \'%s(%d)\' TEST %s\n", + MODE_TO_STR[TEST_ENCODE_BUFFER], color, (result) ? "SUCCESS" : "FAIL"); + } + } +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + __print_help(argv[0]); + return 0; + } + + if (!__get_arguments(argc, argv)) { + g_print("\t[JXL_testsuite] _get_arguments failed\n"); + goto out; + } + + /* test all functions automatically */ + switch (g_test_mode) { + case TEST_AUTO: + __set_enc_opt(g_lossless); + __test_auto(); + break; + case TEST_DECODE_FILE: + case TEST_DECODE_BUFFER: + g_print("\t[JXL_testsuite] >>>>>>>>>>>>>>>>>>>>>> \'%s(%d)\' TEST %s\n", MODE_TO_STR[g_test_mode], + g_color, (__test_decode(g_test_mode, g_color)) ? "SUCCESS" : "FAIL"); + break; + case TEST_ENCODE_FILE: + case TEST_ENCODE_BUFFER: + if (!__get_decoded_data()) { + g_print("\t[JXL_testsuite] __get_decoded_data failed\n"); + goto out; + } + __set_enc_opt(g_lossless); + g_print("\t[JXL_testsuite] >>>>>>>>>>>>>>>>>>>>>> \'%s(%d)\' TEST %s\n", MODE_TO_STR[g_test_mode], + g_color, (__test_encode(g_test_mode, g_color)) ? "SUCCESS" : "FAIL"); + break; + default: + __print_help(argv[0]); + break; + } + +out: + g_free(g_path); + SAFE_FREE(g_readed_data); + SAFE_IMAGE_FREE(g_decoded_data); + mm_util_enc_opt_destroy(g_enc_opt); + + return 0; +} diff --git a/magick/test/mm_util_magick_testsuite.c b/magick/test/mm_util_magick_testsuite.c index fd06832..06d43fb 100644 --- a/magick/test/mm_util_magick_testsuite.c +++ b/magick/test/mm_util_magick_testsuite.c @@ -589,9 +589,9 @@ static int __magick_decenc_file_webp_test(mm_util_color_format_e format, bool lo goto END; } - ret = mm_util_enc_opt_set_webp_lossless(enc_option, lossless); + ret = mm_util_enc_opt_set_lossless(enc_option, lossless); if (ret != MM_UTIL_ERROR_NONE) { - printf("Fail mm_util_enc_opt_set_webp_lossless [0x%x]\n", ret); + printf("Fail mm_util_enc_opt_set_lossless [0x%x]\n", ret); goto END; } @@ -652,9 +652,9 @@ static int __magick_decenc_buffer_webp_test(mm_util_color_format_e format, bool goto END; } - ret = mm_util_enc_opt_set_webp_lossless(enc_option, lossless); + ret = mm_util_enc_opt_set_lossless(enc_option, lossless); if (ret != MM_UTIL_ERROR_NONE) { - printf("Fail mm_util_enc_opt_set_webp_lossless [0x%x]\n", ret); + printf("Fail mm_util_enc_opt_set_lossless [0x%x]\n", ret); goto END; } diff --git a/packaging/libmm-utility.spec b/packaging/libmm-utility.spec index 1d8e6f9..e3fbc34 100644 --- a/packaging/libmm-utility.spec +++ b/packaging/libmm-utility.spec @@ -1,6 +1,6 @@ Name: libmm-utility Summary: Multimedia Framework Utility Library -Version: 0.3.6 +Version: 0.4.0 Release: 0 Group: System/Libraries License: Apache-2.0 @@ -23,6 +23,8 @@ BuildRequires: pkgconfig(libwebpmux) BuildRequires: giflib-devel BuildRequires: pkgconfig(GraphicsMagick) BuildRequires: pkgconfig(heif) +BuildRequires: pkgconfig(libjxl) +BuildRequires: pkgconfig(libjxl_threads) %if 0%{?gtests:1} BuildRequires: pkgconfig(gmock) %endif