Add memory usage reduction for creating image thumbnail 30/288630/1 accepted/tizen_7.0_unified tizen_7.0 accepted/tizen/7.0/unified/20230302.145956 accepted/tizen/7.0/unified/20230303.030103
authorjiyong.min <jiyong.min@samsung.com>
Sun, 12 Feb 2023 22:52:11 +0000 (07:52 +0900)
committerjiyong.min <jiyong.min@samsung.com>
Tue, 21 Feb 2023 00:00:13 +0000 (09:00 +0900)
 - Please use this option for low memory limit due to memory policy.
  For large-sized images, memory usage may decrease to less than 100MB,
  but thumbnail quality may be slightly lowered in some cases.
 - A-Gif has been changed from mmutil_magick to mmutil_gif.
 - Jpeg has been added to use the downscale option.

Change-Id: Id0cff4690e4ebea10649859ee2ff678d67d850db

Add image size limitation for specific device

 - This patch is for specific device, do not use it on other devices.
  If necessary in the future, we will provide a hal-config that can be used on the others.

Change-Id: I6ee8f6fb8f03874d7c9cb31bd2b09e56af8a3a8f

Separate functions for 'USE_MEMORY_USAGE_REDUCTION'

Change-Id: I3967b917078d2cefb277a1627729005856a16ae7

Add to convert RGBA to BGRA for raw data

 - In case of raw data, if it is an image,
   thumbnail should be returned as BGRA colorspace.
https://docs.tizen.org/application/native/guides/multimedia/thumbnail-images/

Change-Id: I11065190336962db66f228d6d93279dd180e2e6e

CMakeLists.txt
packaging/libmedia-thumbnail.spec
src/media-thumbnail.c

index c74eb53..41534a0 100644 (file)
@@ -26,8 +26,13 @@ MESSAGE("Build type: ${CMAKE_BUILD_TYPE}")
 
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
 
+IF(WITH_DA_PROFILE)
+INCLUDE(FindPkgConfig)
+       pkg_check_modules(pkgs REQUIRED glib-2.0 dlog mm-fileinfo aul libmedia-utils libtzplatform-config mmutil-common mmutil-magick mmutil-gif mmutil-jpeg libexif)
+ELSE(WITH_DA_PROFILE)
 INCLUDE(FindPkgConfig)
        pkg_check_modules(pkgs REQUIRED glib-2.0 dlog mm-fileinfo aul libmedia-utils libtzplatform-config mmutil-common mmutil-magick)
+ENDIF(WITH_DA_PROFILE)
 
 FOREACH(flag ${pkgs_CFLAGS})
        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
@@ -50,6 +55,11 @@ ADD_DEFINITIONS("-DPACKAGE_NAME=\"${PKGNAME}\"")
 ADD_DEFINITIONS("-DPREFIX=\"${PREFIX}\"")
 ADD_DEFINITIONS("-D_GNU_SOURCE")
 
+IF(WITH_DA_PROFILE)
+       ADD_DEFINITIONS("-DUSE_MEMORY_USAGE_REDUCTION")
+       ADD_DEFINITIONS("-DENABLE_IMAGE_SIZE_LIMITATION")
+ENDIF(WITH_DA_PROFILE)
+
 SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--hash-style=both")
 CONFIGURE_FILE(media-thumbnail.pc.in media-thumbnail.pc @ONLY)
 INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/media-thumbnail.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
index c5fb01f..ec50ef8 100644 (file)
@@ -1,6 +1,6 @@
 Name:       libmedia-thumbnail
 Summary:    Media thumbnail service library for multimedia applications
-Version:    0.4.3
+Version:    0.4.4
 Release:    0
 Group:      Multimedia/Libraries
 License:    Apache-2.0
@@ -17,7 +17,12 @@ BuildRequires: pkgconfig(libtzplatform-config)
 BuildRequires: pkgconfig(mmutil-common)
 BuildRequires: pkgconfig(mmutil-magick)
 %if 0%{?gtests:1}
-BuildRequires:  pkgconfig(gmock)
+BuildRequires: pkgconfig(gmock)
+%endif
+%if 0%{?_with_da_profile:1}
+BuildRequires: pkgconfig(mmutil-gif)
+BuildRequires: pkgconfig(mmutil-jpeg)
+BuildRequires: pkgconfig(libexif)
 %endif
 
 %description
@@ -38,7 +43,8 @@ cp %{SOURCE1001} %{SOURCE1002} .
 
 
 %build
-%cmake . -DBUILD_GTESTS=%{?gtests:1}%{!?gtests:0}
+%cmake . -DBUILD_GTESTS=%{?gtests:1}%{!?gtests:0} \
+    -DWITH_DA_PROFILE=%{?_with_da_profile:1}%{!?_with_da_profile:0}
 make %{?_smp_mflags}
 
 %install
index 6b4960e..4671f82 100755 (executable)
 #include "media-thumbnail.h"
 #include "media-thumbnail-debug.h"
 #include <aul.h>
+#if defined(USE_MEMORY_USAGE_REDUCTION)
+#include <mm_util_gif.h>
+#include <mm_util_jpeg.h>
+#include <libexif/exif-data.h>
+#endif
+
 
 #define MAX_THUMB_SIZE 2000
 #define MIME_TYPE_TIFF "image/tiff"
@@ -368,34 +374,199 @@ int create_video_thumbnail_to_buffer(const char *path,
        return err;
 }
 
-static int __adjust_thumb_ratio(const char *path, unsigned int *width, unsigned int *height)
+static int __adjust_thumb_ratio(const char *path, unsigned int *width, unsigned int *height,
+                               unsigned int *image_w, unsigned int *image_h, mm_util_img_codec_type *image_type)
 {
        int err = MS_MEDIA_ERR_NONE;
-       unsigned int image_w = 0;
-       unsigned int image_h = 0;
-       mm_util_img_codec_type image_type = 0;
-
-       err = mm_util_extract_image_info(path, &image_type, &image_w, &image_h);
+#if defined(ENABLE_IMAGE_SIZE_LIMITATION)
+       const size_t image_size_limit[IMG_CODEC_UNKNOWN_TYPE] = {
+               [IMG_CODEC_JPEG] = (4096 * 4096),
+               [IMG_CODEC_PNG] = (3000 * 3000),
+               [IMG_CODEC_GIF] = (3000 * 3000),
+       };
+#endif
+
+       err = mm_util_extract_image_info(path, image_type, image_w, image_h);
        thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_extract_image_info: %d", err);
-       thumb_retvm_if(image_type == IMG_CODEC_UNKNOWN_TYPE, MS_MEDIA_ERR_THUMB_UNSUPPORTED, "Unsupported image codec");
+       thumb_retvm_if(*image_type == IMG_CODEC_UNKNOWN_TYPE, MS_MEDIA_ERR_THUMB_UNSUPPORTED, "Unsupported image codec");
+
+#if defined(ENABLE_IMAGE_SIZE_LIMITATION)
+       if ((*image_type == IMG_CODEC_JPEG) ||
+               (*image_type == IMG_CODEC_PNG) ||
+               (*image_type == IMG_CODEC_GIF))
+               if ((size_t)((*image_w) * (*image_h)) > image_size_limit[*image_type]) {
+                       thumb_err("Unsupported image size [%u x %u] limit [%zu]", *image_w, *image_h, image_size_limit[*image_type]);
+                       return MS_MEDIA_ERR_UNSUPPORTED_CONTENT;
+               }
+#endif
+       __media_thumb_get_proper_thumb_size(*image_w, *image_h, width, height);
+
+       return MS_MEDIA_ERR_NONE;
+}
+
+#if defined(USE_MEMORY_USAGE_REDUCTION)
+static mm_util_rotate_type_e __get_image_orientation(const char *path)
+{
+       ExifData *ed = NULL;
+       ExifEntry *entry = NULL;
+       short orientation = 0;
+       mm_util_rotate_type_e rotation = MM_UTIL_ROTATE_0;
+
+       ed = exif_data_new_from_file(path);
+       thumb_retvm_if(!ed, MM_UTIL_ROTATE_0, "no exif data");
+
+       entry = exif_data_get_entry(ed, EXIF_TAG_ORIENTATION);
+       if (!entry) {
+               thumb_info("no orientation entry");
+               exif_data_unref(ed);
+               return MM_UTIL_ROTATE_0;
+       }
 
-       __media_thumb_get_proper_thumb_size(image_w, image_h, width, height);
+       orientation = exif_get_short(entry->data, exif_data_get_byte_order(ed));
+
+       /* exif orientation
+        * 0 - not available, 1- normal, 2 - hflip, 3 - rot_180,
+        * 4 - vflip, 5 - tranpose, 6 - rot_90, 7 - transverse, 8 - rot_270
+        */
+       switch (orientation) {
+       case 3:
+               rotation = MM_UTIL_ROTATE_180;
+               break;
+       case 6:
+               rotation = MM_UTIL_ROTATE_90;
+               break;
+       case 8:
+               rotation = MM_UTIL_ROTATE_270;
+               break;
+       default:
+               thumb_warn("not supported orientation[%d], rotation will be 0", orientation);
+               rotation = MM_UTIL_ROTATE_0;
+               break;
+       };
+
+       exif_data_unref(ed);
+       thumb_info("orientation: %d, rotation: %d", orientation, rotation);
+
+       return rotation;
+}
+
+static int __create_gif_thumbnail_to_file(const char *path, unsigned int thumb_w, unsigned int thumb_h, const char *thumb_path)
+{
+       int err = MS_MEDIA_ERR_NONE;
+       mm_util_image_h decode_image = NULL;
+
+       err = mm_util_decode_from_gif_file(path, &decode_image);
+       thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_decode_from_gif_file failed : %d", err);
+
+       err = mm_util_resize_B_P(decode_image, thumb_w, thumb_h, thumb_path);
+       mm_image_destroy_image(decode_image);
+       thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_resize_B_P failed : %d", err);
 
        return MS_MEDIA_ERR_NONE;
 }
 
+static int __create_gif_thumbnail_to_buffer(const char *path, unsigned int thumb_w, unsigned int thumb_h, mm_util_image_h *thumbnail)
+{
+       int err = MS_MEDIA_ERR_NONE;
+       mm_util_image_h decode_image = NULL;
+       mm_util_image_h rgba_thumbnail = NULL;
+
+       err = mm_util_decode_from_gif_file(path, &decode_image);
+       thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_decode_from_gif_file failed : %d", err);
+
+       err = mm_util_resize_B_B(decode_image, thumb_w, thumb_h, &rgba_thumbnail);
+       mm_image_destroy_image(decode_image);
+       thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_resize_B_B failed : %d", err);
+
+       err = mm_util_convert_B_B(rgba_thumbnail, MM_UTIL_COLOR_BGRA, thumbnail);
+       mm_image_destroy_image(rgba_thumbnail);
+       thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_convert_B_B failed : %d", err);
+
+       return MS_MEDIA_ERR_NONE;
+}
+
+static int __decode_jpeg_with_downscale(const char *path, size_t image_size, size_t thumb_size, mm_util_image_h *decode_image)
+{
+       mm_util_jpeg_decode_downscale downscale = MM_UTIL_JPEG_DECODE_DOWNSCALE_1_1;
+
+       // The downscale divide each width & height, so we should use squares 4(2*2), 16(4*4), 64(8*8).
+       if ((image_size >= thumb_size * 4) && (image_size < thumb_size * 16))
+               downscale = MM_UTIL_JPEG_DECODE_DOWNSCALE_1_2;
+       else if ((image_size >= thumb_size * 16) && (image_size < thumb_size * 64))
+               downscale = MM_UTIL_JPEG_DECODE_DOWNSCALE_1_4;
+       else if (image_size >= thumb_size * 64)
+               downscale = MM_UTIL_JPEG_DECODE_DOWNSCALE_1_8;
+
+       thumb_info("downscale: %d", downscale);
+
+       return mm_util_decode_from_jpeg_file(path, MM_UTIL_COLOR_BGRA, downscale, decode_image);
+}
+
+static int __create_jpeg_thumbnail_to_file(const char *path, size_t image_size, unsigned int thumb_w, unsigned int thumb_h, const char *thumb_path, bool auto_rotate)
+{
+       int err = MS_MEDIA_ERR_NONE;
+       mm_util_image_h decode_image = NULL;
+       mm_util_image_h resize_image = NULL;
+       mm_util_rotate_type_e rotation = __get_image_orientation(path);
+
+       err = __decode_jpeg_with_downscale(path, image_size, (size_t)(thumb_w * thumb_h), &decode_image);
+       thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_decode_from_jpeg_file failed : %d", err);
+
+       if (auto_rotate && (rotation != MM_UTIL_ROTATE_0)) {
+               err = mm_util_resize_B_B(decode_image, thumb_w, thumb_h, &resize_image);
+               mm_image_destroy_image(decode_image);
+               thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_resize_B_B failed : %d", err);
+
+               err = mm_util_rotate_B_P(resize_image, rotation, thumb_path);
+               mm_image_destroy_image(resize_image);
+               thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_rotate_B_P failed : %d", err);
+       } else {
+               err = mm_util_resize_B_P(decode_image, thumb_w, thumb_h, thumb_path);
+               mm_image_destroy_image(decode_image);
+               thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_resize_B_P failed : %d", err);
+       }
+
+       return MS_MEDIA_ERR_NONE;
+}
+
+static int __create_jpeg_thumbnail_to_buffer(const char *path, size_t image_size, unsigned int thumb_w, unsigned int thumb_h, mm_util_image_h *thumbnail)
+{
+       int err = MS_MEDIA_ERR_NONE;
+       mm_util_image_h decode_image = NULL;
+
+       err = __decode_jpeg_with_downscale(path, image_size, (size_t)(thumb_w * thumb_h), &decode_image);
+       thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_decode_from_jpeg_file failed : %d", err);
+
+       err = mm_util_resize_B_B(decode_image, thumb_w, thumb_h, thumbnail);
+       mm_image_destroy_image(decode_image);
+       thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_resize_B_B failed : %d", err);
+
+       return MS_MEDIA_ERR_NONE;
+}
+#endif
+
 int create_image_thumbnail_to_file(const char *path, unsigned int width, unsigned int height, const char *thumb_path, bool auto_rotate)
 {
        int err = MS_MEDIA_ERR_NONE;
+       unsigned int image_w = 0;
+       unsigned int image_h = 0;
        unsigned int thumb_w = width;
        unsigned int thumb_h = height;
+       mm_util_img_codec_type image_type = 0;
 
        err = __check_parameter_validity_for_file(path, width, height, thumb_path);
        thumb_retvm_if(err != MS_MEDIA_ERR_NONE, err, "Invalid parameter");
 
-       err = __adjust_thumb_ratio(path, &thumb_w, &thumb_h);
+       err = __adjust_thumb_ratio(path, &thumb_w, &thumb_h, &image_w, &image_h, &image_type);
        thumb_retvm_if(err != MS_MEDIA_ERR_NONE, err, "__adjust_thumb_ratio failed");
 
+#if defined(USE_MEMORY_USAGE_REDUCTION)
+       if (image_type == IMG_CODEC_GIF)
+               return __create_gif_thumbnail_to_file(path, thumb_w, thumb_h, thumb_path);
+       else if (image_type == IMG_CODEC_JPEG)
+               return __create_jpeg_thumbnail_to_file(path, (size_t)(image_w * image_h), thumb_w, thumb_h, thumb_path, auto_rotate);
+#endif
+
        if (auto_rotate)
                err = mm_util_resize_and_rotate_P_P(path, thumb_w, thumb_h, thumb_path);
        else
@@ -408,18 +579,32 @@ int create_image_thumbnail_to_file(const char *path, unsigned int width, unsigne
 int create_image_thumbnail_to_buffer(const char *path, unsigned int width, unsigned int height, unsigned char **thumb_buffer, size_t *thumb_size, unsigned int *thumb_width, unsigned int *thumb_height)
 {
        int err = MS_MEDIA_ERR_NONE;
+       unsigned int image_w = 0;
+       unsigned int image_h = 0;
        unsigned int thumb_w = width;
        unsigned int thumb_h = height;
        mm_util_image_h img = NULL;
+       mm_util_img_codec_type image_type = 0;
 
        err = __check_parameter_validity_for_buffer(path, width, height, thumb_buffer, thumb_size, thumb_width, thumb_height);
        thumb_retvm_if(err != MS_MEDIA_ERR_NONE, err, "Invalid parameter");
 
-       err = __adjust_thumb_ratio(path, &thumb_w, &thumb_h);
+       err = __adjust_thumb_ratio(path, &thumb_w, &thumb_h, &image_w, &image_h, &image_type);
        thumb_retvm_if(err != MS_MEDIA_ERR_NONE, err, "__adjust_thumb_ratio failed");
 
-       err = mm_util_resize_P_B(path, thumb_w, thumb_h, MM_UTIL_COLOR_BGRA, &img);
-       thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_resize_P_B failed : %d", err);
+#if defined(USE_MEMORY_USAGE_REDUCTION)
+       if (image_type == IMG_CODEC_GIF) {
+               err = __create_gif_thumbnail_to_buffer(path, thumb_w, thumb_h, &img);
+               thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "__create_gif_thumbnail_to_buffer failed : %d", err);
+       } else if (image_type == IMG_CODEC_JPEG) {
+               err = __create_jpeg_thumbnail_to_buffer(path, (size_t)(image_w * image_h), thumb_w, thumb_h, &img);
+               thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "__create_jpeg_thumbnail_to_buffer failed : %d", err);
+       } else
+#endif
+       {
+               err = mm_util_resize_P_B(path, thumb_w, thumb_h, MM_UTIL_COLOR_BGRA, &img);
+               thumb_retvm_if(err != MM_UTIL_ERROR_NONE, MS_MEDIA_ERR_INTERNAL, "mm_util_resize_P_B failed : %d", err);
+       }
 
        err = mm_image_get_image(img, thumb_width, thumb_height, NULL, thumb_buffer, thumb_size);
 
@@ -491,4 +676,4 @@ int create_thumbnail_to_file(const char *path, unsigned int width, unsigned int
        }
 
        return create_video_thumbnail_to_file(path, width, height, thumb_path, false);
-}
\ No newline at end of file
+}