apply HEIF library 40/253140/31 accepted/tizen/unified/20210309.140634 submit/tizen/20210309.051823
authorjiyong.min <jiyong.min@samsung.com>
Thu, 4 Feb 2021 23:19:52 +0000 (08:19 +0900)
committerjiyong.min <jiyong.min@samsung.com>
Mon, 8 Mar 2021 00:51:17 +0000 (09:51 +0900)
 Multimedia Framework Library for decoding HEIF(ISO/IEC 23008-12) image
 License: Apache 2.0
 Dependency: FFmpeg

 How to test
 1. After building this library by GBS,
   please install the rpm package on a target device or an emulator.
 2. Run test/test.sh
   (You can modify test/test.sh as enviroment)
 3. Check the result images on PC

Change-Id: Ida20d33dd2dce8f45b05d6fc2bc66036fabeede2

39 files changed:
CMakeLists.txt [new file with mode: 0755]
LICENSE.APLv2.0 [new file with mode: 0644]
include/heif.h [new file with mode: 0644]
include/heif_debug.h [new file with mode: 0644]
include/heif_decoder.h [new file with mode: 0644]
include/heif_extractor.h [new file with mode: 0644]
include/heif_itemtable.h [new file with mode: 0644]
include/heif_source.h [new file with mode: 0644]
include/heif_type.h [new file with mode: 0644]
libheif.pc.in [new file with mode: 0644]
packaging/libheif.manifest [new file with mode: 0644]
packaging/libheif.spec [new file with mode: 0644]
src/heif.c [new file with mode: 0644]
src/heif_box_idat.c [new file with mode: 0644]
src/heif_box_idat.h [new file with mode: 0644]
src/heif_box_iinf.c [new file with mode: 0644]
src/heif_box_iinf.h [new file with mode: 0644]
src/heif_box_iloc.c [new file with mode: 0644]
src/heif_box_iloc.h [new file with mode: 0644]
src/heif_box_iprp.c [new file with mode: 0644]
src/heif_box_iprp.h [new file with mode: 0644]
src/heif_box_iref.c [new file with mode: 0644]
src/heif_box_iref.h [new file with mode: 0644]
src/heif_box_pitm.c [new file with mode: 0644]
src/heif_box_pitm.h [new file with mode: 0644]
src/heif_box_util.c [new file with mode: 0644]
src/heif_box_util.h [new file with mode: 0644]
src/heif_decode_ffmpeg.c [new file with mode: 0644]
src/heif_decode_ffmpeg.h [new file with mode: 0644]
src/heif_decode_plugin.c [new file with mode: 0644]
src/heif_decode_plugin.h [new file with mode: 0644]
src/heif_decoder.c [new file with mode: 0644]
src/heif_extractor.c [new file with mode: 0644]
src/heif_itemtable.c [new file with mode: 0644]
src/heif_source.c [new file with mode: 0644]
test/CMakeLists.txt [new file with mode: 0644]
test/heif_testsuite.c [new file with mode: 0644]
test/sample_test.heic [new file with mode: 0644]
test/test.sh [new file with mode: 0755]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..d2cefa3
--- /dev/null
@@ -0,0 +1,78 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+SET(fw_name "libheif")
+
+# project
+SET(prefix "/usr")
+SET(maintainer "Haejeong Kim <backto.kim@samsung.com>" "Jiyong Min <jiyong.min@samsung.com>" "Minje Ahn <minje.ahn@samsung.com>")
+SET(description "A heif decoder library in Tizen Multimedia Framework")
+SET(service "mmf")
+
+# for package file
+SET(dependents "dlog glib-2.0 mm-common libavcodec libavformat libavutil libswscale")
+SET(pc_dependents "")
+
+PROJECT(${fw_name} C CXX)
+
+SET(CMAKE_INSTALL_PREFIX ${prefix})
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+SET(VERSION_MAJOR 1)
+SET(VERSION "${VERSION_MAJOR}.0.0")
+
+SET(INC_DIR include)
+INCLUDE_DIRECTORIES(${INC_DIR})
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(${fw_name} REQUIRED ${dependents})
+FOREACH(cflag ${${fw_name}_CFLAGS})
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${cflag}")
+ENDFOREACH(cflag)
+FOREACH(cxxflag ${${fw_name}_CXXFLAGS})
+    SET(EXTRA_CXXFLAGS "${EXTRA_CXXFLAGS} ${cxxflag}")
+ENDFOREACH(cxxflag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -Wall -Werror")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS} -std=c++11 -fPIC -Wall")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+
+IF("${ARCH}" MATCHES "^arm.*")
+    ADD_DEFINITIONS("-DTARGET")
+ENDIF("${ARCH}" MATCHES "^arm.*")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DTIZEN_DEBUG")
+ADD_DEFINITIONS("-D_FILE_OFFSET_BITS=64")
+
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}")
+
+aux_source_directory(src SOURCES)
+ADD_LIBRARY(${fw_name} SHARED ${SOURCES})
+TARGET_LINK_LIBRARIES(${fw_name} ${${fw_name}_LDFLAGS})
+
+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/${service}
+        FILES_MATCHING
+        PATTERN "*_debug.h" EXCLUDE
+        PATTERN "*_private.h" EXCLUDE
+        PATTERN "${INC_DIR}/*.h"
+        )
+
+SET(PC_NAME ${fw_name})
+SET(PC_REQUIRED ${pc_dependents})
+SET(PC_LDFLAGS -l${fw_name})
+
+CONFIGURE_FILE(
+    ${fw_name}.pc.in
+    ${CMAKE_CURRENT_SOURCE_DIR}/${fw_name}.pc
+    @ONLY
+)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${fw_name}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+ADD_SUBDIRECTORY(test)
diff --git a/LICENSE.APLv2.0 b/LICENSE.APLv2.0
new file mode 100644 (file)
index 0000000..29f81d8
--- /dev/null
@@ -0,0 +1,201 @@
+                                 Apache License\r
+                           Version 2.0, January 2004\r
+                        http://www.apache.org/licenses/\r
+\r
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r
+\r
+   1. Definitions.\r
+\r
+      "License" shall mean the terms and conditions for use, reproduction,\r
+      and distribution as defined by Sections 1 through 9 of this document.\r
+\r
+      "Licensor" shall mean the copyright owner or entity authorized by\r
+      the copyright owner that is granting the License.\r
+\r
+      "Legal Entity" shall mean the union of the acting entity and all\r
+      other entities that control, are controlled by, or are under common\r
+      control with that entity. For the purposes of this definition,\r
+      "control" means (i) the power, direct or indirect, to cause the\r
+      direction or management of such entity, whether by contract or\r
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the\r
+      outstanding shares, or (iii) beneficial ownership of such entity.\r
+\r
+      "You" (or "Your") shall mean an individual or Legal Entity\r
+      exercising permissions granted by this License.\r
+\r
+      "Source" form shall mean the preferred form for making modifications,\r
+      including but not limited to software source code, documentation\r
+      source, and configuration files.\r
+\r
+      "Object" form shall mean any form resulting from mechanical\r
+      transformation or translation of a Source form, including but\r
+      not limited to compiled object code, generated documentation,\r
+      and conversions to other media types.\r
+\r
+      "Work" shall mean the work of authorship, whether in Source or\r
+      Object form, made available under the License, as indicated by a\r
+      copyright notice that is included in or attached to the work\r
+      (an example is provided in the Appendix below).\r
+\r
+      "Derivative Works" shall mean any work, whether in Source or Object\r
+      form, that is based on (or derived from) the Work and for which the\r
+      editorial revisions, annotations, elaborations, or other modifications\r
+      represent, as a whole, an original work of authorship. For the purposes\r
+      of this License, Derivative Works shall not include works that remain\r
+      separable from, or merely link (or bind by name) to the interfaces of,\r
+      the Work and Derivative Works thereof.\r
+\r
+      "Contribution" shall mean any work of authorship, including\r
+      the original version of the Work and any modifications or additions\r
+      to that Work or Derivative Works thereof, that is intentionally\r
+      submitted to Licensor for inclusion in the Work by the copyright owner\r
+      or by an individual or Legal Entity authorized to submit on behalf of\r
+      the copyright owner. For the purposes of this definition, "submitted"\r
+      means any form of electronic, verbal, or written communication sent\r
+      to the Licensor or its representatives, including but not limited to\r
+      communication on electronic mailing lists, source code control systems,\r
+      and issue tracking systems that are managed by, or on behalf of, the\r
+      Licensor for the purpose of discussing and improving the Work, but\r
+      excluding communication that is conspicuously marked or otherwise\r
+      designated in writing by the copyright owner as "Not a Contribution."\r
+\r
+      "Contributor" shall mean Licensor and any individual or Legal Entity\r
+      on behalf of whom a Contribution has been received by Licensor and\r
+      subsequently incorporated within the Work.\r
+\r
+   2. Grant of Copyright License. Subject to the terms and conditions of\r
+      this License, each Contributor hereby grants to You a perpetual,\r
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+      copyright license to reproduce, prepare Derivative Works of,\r
+      publicly display, publicly perform, sublicense, and distribute the\r
+      Work and such Derivative Works in Source or Object form.\r
+\r
+   3. Grant of Patent License. Subject to the terms and conditions of\r
+      this License, each Contributor hereby grants to You a perpetual,\r
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+      (except as stated in this section) patent license to make, have made,\r
+      use, offer to sell, sell, import, and otherwise transfer the Work,\r
+      where such license applies only to those patent claims licensable\r
+      by such Contributor that are necessarily infringed by their\r
+      Contribution(s) alone or by combination of their Contribution(s)\r
+      with the Work to which such Contribution(s) was submitted. If You\r
+      institute patent litigation against any entity (including a\r
+      cross-claim or counterclaim in a lawsuit) alleging that the Work\r
+      or a Contribution incorporated within the Work constitutes direct\r
+      or contributory patent infringement, then any patent licenses\r
+      granted to You under this License for that Work shall terminate\r
+      as of the date such litigation is filed.\r
+\r
+   4. Redistribution. You may reproduce and distribute copies of the\r
+      Work or Derivative Works thereof in any medium, with or without\r
+      modifications, and in Source or Object form, provided that You\r
+      meet the following conditions:\r
+\r
+      (a) You must give any other recipients of the Work or\r
+          Derivative Works a copy of this License; and\r
+\r
+      (b) You must cause any modified files to carry prominent notices\r
+          stating that You changed the files; and\r
+\r
+      (c) You must retain, in the Source form of any Derivative Works\r
+          that You distribute, all copyright, patent, trademark, and\r
+          attribution notices from the Source form of the Work,\r
+          excluding those notices that do not pertain to any part of\r
+          the Derivative Works; and\r
+\r
+      (d) If the Work includes a "NOTICE" text file as part of its\r
+          distribution, then any Derivative Works that You distribute must\r
+          include a readable copy of the attribution notices contained\r
+          within such NOTICE file, excluding those notices that do not\r
+          pertain to any part of the Derivative Works, in at least one\r
+          of the following places: within a NOTICE text file distributed\r
+          as part of the Derivative Works; within the Source form or\r
+          documentation, if provided along with the Derivative Works; or,\r
+          within a display generated by the Derivative Works, if and\r
+          wherever such third-party notices normally appear. The contents\r
+          of the NOTICE file are for informational purposes only and\r
+          do not modify the License. You may add Your own attribution\r
+          notices within Derivative Works that You distribute, alongside\r
+          or as an addendum to the NOTICE text from the Work, provided\r
+          that such additional attribution notices cannot be construed\r
+          as modifying the License.\r
+\r
+      You may add Your own copyright statement to Your modifications and\r
+      may provide additional or different license terms and conditions\r
+      for use, reproduction, or distribution of Your modifications, or\r
+      for any such Derivative Works as a whole, provided Your use,\r
+      reproduction, and distribution of the Work otherwise complies with\r
+      the conditions stated in this License.\r
+\r
+   5. Submission of Contributions. Unless You explicitly state otherwise,\r
+      any Contribution intentionally submitted for inclusion in the Work\r
+      by You to the Licensor shall be under the terms and conditions of\r
+      this License, without any additional terms or conditions.\r
+      Notwithstanding the above, nothing herein shall supersede or modify\r
+      the terms of any separate license agreement you may have executed\r
+      with Licensor regarding such Contributions.\r
+\r
+   6. Trademarks. This License does not grant permission to use the trade\r
+      names, trademarks, service marks, or product names of the Licensor,\r
+      except as required for reasonable and customary use in describing the\r
+      origin of the Work and reproducing the content of the NOTICE file.\r
+\r
+   7. Disclaimer of Warranty. Unless required by applicable law or\r
+      agreed to in writing, Licensor provides the Work (and each\r
+      Contributor provides its Contributions) on an "AS IS" BASIS,\r
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r
+      implied, including, without limitation, any warranties or conditions\r
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r
+      PARTICULAR PURPOSE. You are solely responsible for determining the\r
+      appropriateness of using or redistributing the Work and assume any\r
+      risks associated with Your exercise of permissions under this License.\r
+\r
+   8. Limitation of Liability. In no event and under no legal theory,\r
+      whether in tort (including negligence), contract, or otherwise,\r
+      unless required by applicable law (such as deliberate and grossly\r
+      negligent acts) or agreed to in writing, shall any Contributor be\r
+      liable to You for damages, including any direct, indirect, special,\r
+      incidental, or consequential damages of any character arising as a\r
+      result of this License or out of the use or inability to use the\r
+      Work (including but not limited to damages for loss of goodwill,\r
+      work stoppage, computer failure or malfunction, or any and all\r
+      other commercial damages or losses), even if such Contributor\r
+      has been advised of the possibility of such damages.\r
+\r
+   9. Accepting Warranty or Additional Liability. While redistributing\r
+      the Work or Derivative Works thereof, You may choose to offer,\r
+      and charge a fee for, acceptance of support, warranty, indemnity,\r
+      or other liability obligations and/or rights consistent with this\r
+      License. However, in accepting such obligations, You may act only\r
+      on Your own behalf and on Your sole responsibility, not on behalf\r
+      of any other Contributor, and only if You agree to indemnify,\r
+      defend, and hold each Contributor harmless for any liability\r
+      incurred by, or claims asserted against, such Contributor by reason\r
+      of your accepting any such warranty or additional liability.\r
+\r
+   END OF TERMS AND CONDITIONS\r
+\r
+   APPENDIX: How to apply the Apache License to your work.\r
+\r
+      To apply the Apache License to your work, attach the following\r
+      boilerplate notice, with the fields enclosed by brackets "[]"\r
+      replaced with your own identifying information. (Don't include\r
+      the brackets!)  The text should be enclosed in the appropriate\r
+      comment syntax for the file format. We also recommend that a\r
+      file or class name and description of purpose be included on the\r
+      same "printed page" as the copyright notice for easier\r
+      identification within third-party archives.\r
+\r
+   Copyright [yyyy] [name of copyright owner]\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
diff --git a/include/heif.h b/include/heif.h
new file mode 100644 (file)
index 0000000..51c0022
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_H__
+#define __HEIF_H__
+
+#include <heif_type.h>
+
+/**
+* @brief Decodes the image from the heif file.
+* @details This function emits the primary image in heif.
+* @since_tizen 6.5
+*
+* @remarks The @a image should be released using heif_image_free().\n
+*          The heif image can have some displayable images, this function emits only the primary image.
+*
+* @param[in] path The path of heif image
+* @param[in] format The color format of the emitted image
+* @param[out] image The handle for a decoded image
+*
+* @return @c 0 on success,
+*             otherwise a negative error value
+*
+* @see heif_image_free()
+* @see heif_image_get_image()
+*
+
+#include <heif.h>
+
+int ret = 0;
+heif_image_h image = NULL;
+
+ret = heif_decode_image_from_file("/opt/usr/home/owner/media/sample_test.heic", HEIF_COLOR_FORMAT_YUV420P, &image);
+if (ret < 0)
+       g_print("heif_decode_image_from_file fail %d\n", ret);
+
+*/
+int heif_decode_image_from_file(const char *path, heif_color_format_e format, heif_image_h *image);
+
+/**
+* @brief Decodes the image from the heif buffer.
+* @details This function emits the primary image in heif.
+* @since_tizen 6.5
+*
+* @remarks The @a image should be released using heif_image_free().
+*
+* @param[in] data The buffer of heif image
+* @param[in] size The size of buffer
+* @param[in] format The color format of the emitted image
+* @param[out] image The handle for a decoded image
+*
+* @return @c 0 on success,
+*             otherwise a negative error value
+*
+* @see heif_image_free()
+* @see heif_image_get_image()
+*
+*/
+int heif_decode_image_from_buffer(unsigned char *data, size_t size, heif_color_format_e format, heif_image_h *image);
+
+/**
+* @brief Decodes the thumbnail from the heif file.
+* @details This function emits the thumbnail of the primary image in heif.
+* @since_tizen 6.5
+*
+* @remarks The @a thumb should be released using heif_image_free().
+*
+* @param[in] path The path of heif image
+* @param[in] format The color format of the emitted thumbnail
+* @param[out] image The handle for a decoded image
+*
+* @return @c 0 on success,
+*             otherwise a negative error value
+*
+* @see heif_image_free()
+* @see heif_image_get_image()
+*
+
+#include <heif.h>
+
+int ret = 0;
+heif_image_h thumbnail = NULL;
+
+ret = heif_decode_thumb_from_file("/opt/usr/home/owner/media/sample_test.heic", HEIF_COLOR_FORMAT_YUV420P, &thumbnail);
+if (ret < 0)
+       g_print("heif_decode_thumb_from_file fail %d\n", ret);
+
+*/
+int heif_decode_thumb_from_file(const char *path, heif_color_format_e format, heif_image_h *thumb);
+
+/**
+* @brief Decodes the thumbnail from the heif buffer.
+* @details This function emits the thumbnail of the primary image in heif.
+* @since_tizen 6.5
+*
+* @remarks The @a thumb should be released using heif_image_free().
+*
+* @param[in] data The buffer of heif image
+* @param[in] size The size of buffer
+* @param[in] format The color format of the emitted thumbnail
+* @param[out] image The handle for a decoded image
+*
+* @return @c 0 on success,
+*             otherwise a negative error value
+*
+* @see heif_image_free()
+* @see heif_image_get_image()
+*
+*/
+int heif_decode_thumb_from_buffer(unsigned char *data, size_t size, heif_color_format_e format, heif_image_h *thumb);
+
+/**
+* @brief Gets the data in the image handle.
+* @details This function emits the data of an image.
+* @since_tizen 6.5
+*
+* @param[in] image The handle for the decoded image
+* @param[out] width The width of the image
+* @param[out] height The height of the image
+* @param[out] format The color format of the image
+* @param[out] data The data of the image
+* @param[out] size The size of the @a data
+*
+* @return @c 0 on success,
+*             otherwise a negative error value
+*
+*/
+int heif_image_get_image(heif_image_h image, unsigned int *width, unsigned int *height, heif_color_format_e *format, unsigned char **data, size_t *size);
+
+/**
+* @brief  Destroys an image handle.
+* @since_tizen 6.5
+*
+* @param[in] image The handle of an image
+*
+*/
+void heif_image_free(heif_image_h image);
+
+/**
+* @brief Gets metadata of a heif image file.
+* @details This function emits the handle of an image information.
+* @since_tizen 6.5
+*
+* @remarks The @a image_info should be released using heif_image_info_free().
+*
+* @param[in] path The path of heif image
+* @param[out] image_info The handle for image information
+*
+* @return @c 0 on success,
+*             otherwise a negative error value
+*
+* @see heif_image_info_free()
+*
+
+#include <heif.h>
+
+int ret = 0;
+heif_image_info_h image_info = NULL;
+
+ret = heif_get_image_info_from_file("/opt/usr/home/owner/media/sample_test.heic", &image_info);
+if (ret < 0)
+       g_print("heif_get_image_info_from_file fail %d\n", ret);
+
+*/
+int heif_get_image_info_from_file(const char *path, heif_image_info_h *image_info);
+
+/**
+* @brief Gets metadata of a heif image buffer.
+* @details This function emits the handle of an image information.
+* @since_tizen 6.5
+*
+* @remarks The @a image_info should be released using heif_image_info_free().
+*
+* @param[in] data The buffer of heif image
+* @param[in] size The size of buffer
+* @param[out] image_info The handle for an image information
+*
+* @return @c 0 on success,
+*             otherwise a negative error value
+*
+* @see heif_image_info_free()
+*
+*/
+int heif_get_image_info_from_buffer(unsigned char *data, size_t size, heif_image_info_h *image_info);
+
+/**
+* @brief Gets attributes from the image information handle.
+* @since_tizen 6.5
+*
+* @remarks This function must be terminated by NULL argument.\n
+*
+* @param[in] image_info The handle for an image information
+* @param[in] first_attribute_name The name of the first attribute to get
+* @param[in] ... The value of the first attribute, followed optionally by more name/value pairs, terminated by NULL.
+*
+* @return @c 0 on success,
+*             otherwise a negative error value
+*
+
+#include <heif.h>
+
+int ret = 0;
+int width = 0, height = 0, orientation = 0, mirror = 0, mimetype_len = 0;
+char *mimetype = NULL;
+
+// get basic information
+ret = heif_image_info_get_attrs(image_info,
+                                                       HEIF_IMAGE_MIMETYPE, &mimetype, &mimetype_len,
+                                                       HEIF_IMAGE_WIDTH, &width,
+                                                       HEIF_IMAGE_HEIGHT, &height,
+                                                       HEIF_IMAGE_ORIENTATION, &orientation,
+                                                       HEIF_IMAGE_MIRROR, &mirror,
+                                                       NULL);
+
+if (ret < 0)
+       g_print("heif_image_info_get_attrs fail %d\n", ret);
+
+g_print("Subtype\t: %s\n", mimetype);
+g_print("Width\t: %d\n", width);
+g_print("Height\t: %d\n", height);
+g_print("Orientation\t: %d\n", orientation);
+g_print("Mirror\t: %d\n", mirror);
+
+*/
+int heif_image_info_get_attrs(heif_image_info_h image_info, const char *first_attribute_name, ...);
+
+/**
+* @brief  Destroys an image information handle.
+* @since_tizen 6.5
+*
+* @param[in] image_info The handle for an image information
+*
+*/
+void heif_image_info_free(heif_image_info_h image_info);
+
+
+#endif /* __HEIF_H__ */
diff --git a/include/heif_debug.h b/include/heif_debug.h
new file mode 100644 (file)
index 0000000..75bf54b
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+* Copyright (c) 2011 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 __HEIF_DEBUG_H__
+#define __HEIF_DEBUG_H__
+
+#include <dlog.h>
+#include <errno.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define        LOG_TAG                 "LIBHEIF"
+#define        BUF_LENGTH              256
+
+#define FONT_COLOR_RESET    "\033[0m"
+#define FONT_COLOR_RED      "\033[31m"
+#define FONT_COLOR_GREEN    "\033[32m"
+#define FONT_COLOR_YELLOW   "\033[33m"
+#define FONT_COLOR_BLUE     "\033[34m"
+#define FONT_COLOR_PURPLE   "\033[35m"
+#define FONT_COLOR_CYAN     "\033[36m"
+#define FONT_COLOR_GRAY     "\033[37m"
+
+#define heif_debug(fmt, arg...) do { \
+               LOGD(FONT_COLOR_RESET""fmt"", ##arg);     \
+       } while (0)
+
+#define heif_info(fmt, arg...) do { \
+               LOGI(FONT_COLOR_GREEN""fmt""FONT_COLOR_RESET, ##arg);     \
+       } while (0)
+
+#define heif_warning(fmt, arg...) do { \
+               LOGW(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg);     \
+       } while (0)
+
+#define heif_error(fmt, arg...) do { \
+               LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg);     \
+       } while (0)
+
+#define heif_secure_debug(fmt, arg...) do { \
+               SECURE_LOGD(FONT_COLOR_CYAN""fmt""FONT_COLOR_RESET, ##arg);     \
+       } while (0)
+
+#define heif_secure_warning(fmt, arg...) do { \
+               SECURE_LOGW(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg);     \
+       } while (0)
+
+#define heif_secure_error(fmt, arg...) do { \
+               SECURE_LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg);     \
+       } while (0)
+
+#define heif_debug_fenter() do { \
+               LOGD(FONT_COLOR_YELLOW"<ENTER>"FONT_COLOR_RESET); \
+       } while (0)
+
+#define heif_debug_fleave() do { \
+               LOGD(FONT_COLOR_YELLOW"<LEAVE>"FONT_COLOR_RESET);     \
+       } while (0)
+
+#define heif_info_fenter() do { \
+               LOGI(FONT_COLOR_GREEN"<ENTER>"FONT_COLOR_RESET); \
+       } while (0)
+
+#define heif_info_fleave() do { \
+               LOGI(FONT_COLOR_GREEN"<LEAVE>"FONT_COLOR_RESET);     \
+       } while (0)
+
+#define heif_retm_if_failed(expr) do { \
+               if (!(expr)) { \
+                       heif_error("invalid %s", #expr);     \
+                       return; \
+               } \
+       } while (0)
+
+#define heif_retvm_if_failed(expr, val, fmt, arg...) do { \
+               if (!(expr)) { \
+                       LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg);             \
+                       return (val); \
+               } \
+       } while (0)
+
+#define heif_strerror(fmt) do { \
+               char buf[BUF_LENGTH] = {0,}; \
+               strerror_r(errno, buf, BUF_LENGTH); \
+               LOGE(FONT_COLOR_RED""fmt""" : STANDARD ERROR [%s]"FONT_COLOR_RESET, buf); \
+       } while (0)
+
+#endif /* __HEIF_DEBUG_H__ */
diff --git a/include/heif_decoder.h b/include/heif_decoder.h
new file mode 100644 (file)
index 0000000..b72a393
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_DECODER_H__
+#define __HEIF_DECODER_H__
+
+#include "heif_type.h"
+#include "heif_itemtable.h"
+
+typedef void * heif_decoder_h;
+
+int heif_decoder_decode_primary(heif_itemtable_h item_table, heif_color_format_e format, heif_image_h *image);
+int heif_decoder_decode_thumbnail(heif_itemtable_h item_table, heif_color_format_e format, heif_image_h *image);
+int heif_decoder_get_image(heif_image_h image, unsigned int *width, unsigned int *height, heif_color_format_e *format, unsigned char **data, size_t *size);
+void heif_decoder_destroy_image(heif_image_h image);
+
+#endif /* __HEIF_DECODER_H__ */
diff --git a/include/heif_extractor.h b/include/heif_extractor.h
new file mode 100644 (file)
index 0000000..d8a8738
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_EXTRACTOR_H__
+#define __HEIF_EXTRACTOR_H__
+
+#include "heif_source.h"
+#include "heif_itemtable.h"
+
+int heif_extractor_extract(heif_source_h source, heif_itemtable_h *item_table);
+
+#endif /* __HEIF_EXTRACTOR_H__ */
diff --git a/include/heif_itemtable.h b/include/heif_itemtable.h
new file mode 100644 (file)
index 0000000..cdb55da
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_ITEMTABLE_H__
+#define __HEIF_ITEMTABLE_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include "heif_source.h"
+
+typedef struct {
+       uint8_t *data;
+       size_t size;
+} heif_buffer_t;
+
+typedef void * heif_itemtable_h;
+typedef void * heif_image_item_h;
+
+int heif_itemtable_create(heif_itemtable_h *handle, heif_source_h source);
+int heif_itemtable_parse(heif_itemtable_h handle, int32_t chunk_type, off_t data_offset, off_t chunk_data_size);
+bool heif_itemtable_is_valid(heif_itemtable_h handle);
+bool heif_itemtable_has_grid(heif_itemtable_h handle);
+int heif_itemtable_get_primary_image(heif_itemtable_h handle, unsigned int *primary_id);
+int heif_itemtable_get_image_item(heif_itemtable_h handle, unsigned int item_id, heif_image_item_h *image_item);
+int heif_itemtable_get_thumb_item(heif_itemtable_h handle, heif_image_item_h *thumb_item);
+int heif_itemtable_get_image_resolution(heif_image_item_h image_item, unsigned int *width, unsigned int *height);
+int heif_itemtable_get_image_orientation(heif_image_item_h image_item, unsigned int *orientation);
+int heif_itemtable_get_image_mirror_mode(heif_image_item_h image_item, unsigned int *axis);
+int heif_itemtable_get_grid_info(heif_image_item_h image_item, unsigned int *row, unsigned int *column, GSList **img_refs);
+int heif_itemtable_get_coded_data(heif_itemtable_h handle, heif_image_item_h image_item, heif_buffer_t **coded_data);
+int heif_itemtable_get_exif(heif_itemtable_h handle, heif_image_item_h image_item, void **exif, size_t *exif_size);
+int heif_itemtable_get_icc_profile(heif_itemtable_h handle, heif_image_item_h image_item, void **icc, size_t *icc_size);
+void heif_itemtable_print_info(heif_itemtable_h handle);
+void heif_itemtable_destroy(heif_itemtable_h handle);
+void heif_buffer_destroy(heif_buffer_t *buffer);
+
+#endif /* __HEIF_ITEMTABLE_H__ */
diff --git a/include/heif_source.h b/include/heif_source.h
new file mode 100644 (file)
index 0000000..e8a06a3
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_SOURCE_H__
+#define __HEIF_SOURCE_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef void * heif_source_h;
+
+int heif_source_create_from_file(const char *path, heif_source_h *source);
+int heif_source_create_from_buffer(unsigned char *buf, size_t size, heif_source_h *source);
+size_t heif_source_get_size(heif_source_h source);
+size_t heif_source_read_at(heif_source_h source, off_t offset, void *data, size_t size);
+void heif_source_destroy(heif_source_h source);
+
+// Convenience methods:
+bool heif_source_get_uint16(heif_source_h source, off_t offset, uint16_t *x);
+bool heif_source_get_uint24(heif_source_h source, off_t offset, uint32_t *x);
+bool heif_source_get_uint32(heif_source_h source, off_t offset, uint32_t *x);
+bool heif_source_get_uint64(heif_source_h source, off_t offset, uint64_t *x);
+bool heif_source_get_uint16var(heif_source_h source, off_t offset, uint16_t *x, size_t size);
+bool heif_source_get_uint32var(heif_source_h source, off_t offset, uint32_t *x, size_t size);
+bool heif_source_get_uint64var(heif_source_h source, off_t offset, uint64_t *x, size_t size);
+
+#endif /* __HEIF_SOURCE_H__ */
diff --git a/include/heif_type.h b/include/heif_type.h
new file mode 100644 (file)
index 0000000..827e432
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_TYPE_H__
+#define __HEIF_TYPE_H__
+
+#include <tizen.h>
+
+/**
+ * heif attributes.
+ */
+#define HEIF_IMAGE_MIMETYPE                    "heif-image-mimetype"           /**< Mimetype brand of heif */
+#define HEIF_IMAGE_WIDTH                       "heif-image-width"                      /**< Width of primary image in heif */
+#define HEIF_IMAGE_HEIGHT                      "heif-image-height"                     /**< Height of primary image in heif */
+#define HEIF_IMAGE_ORIENTATION         "heif-image-orientation"        /**< Orientation of primary image in heif */
+#define HEIF_IMAGE_MIRROR                      "heif-image-mirror"                     /**< Mirror of primary image in heif */
+#define HEIF_IMAGE_EXIF                                "heif-image-exif"                       /**< Exif of primary image in heif */
+#define HEIF_IMAGE_ICC_PROFILE         "heif-image-icc-profile"        /**< ICC profile of primary image in heif */
+
+typedef void *heif_image_h;
+typedef void *heif_image_info_h;
+
+typedef enum {
+       LIBHEIF_ERROR_NONE                                              = TIZEN_ERROR_NONE,
+       LIBHEIF_ERROR_INVALID_PARAMETER                 = TIZEN_ERROR_INVALID_PARAMETER,
+       LIBHEIF_ERROR_PERMISSION_DENIED                 = TIZEN_ERROR_PERMISSION_DENIED,
+       LIBHEIF_ERROR_NOT_SUPPORTED                             = TIZEN_ERROR_NOT_SUPPORTED,
+       LIBHEIF_ERROR_INVALID_OPERATION                 = TIZEN_ERROR_INVALID_OPERATION,
+} heif_error_e;
+
+/**
+ * heif color format.
+ */
+typedef enum {
+       HEIF_COLOR_FORMAT_NONE,
+       HEIF_COLOR_FORMAT_YUV420P,
+       HEIF_COLOR_FORMAT_RGB24,
+       HEIF_COLOR_FORMAT_ARGB,
+       HEIF_COLOR_FORMAT_BGRA,
+       HEIF_COLOR_FORMAT_RGBA,
+       HEIF_COLOR_FORMAT_MAX,
+} heif_color_format_e;
+
+/**
+ * mirror mode of a heif image.
+ */
+typedef enum {
+       HEIF_MIRROR_NONE,
+       HEIF_MIRROR_VERTICAL,
+       HEIF_MIRROR_HORIZONTAL,
+} heif_mirror_mode_e;
+
+#endif /* __HEIF_TYPE_H__ */
diff --git a/libheif.pc.in b/libheif.pc.in
new file mode 100644 (file)
index 0000000..6718003
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@PREFIX@
+exec_prefix=/usr
+libdir=@LIB_INSTALL_DIR@
+includedir=/usr/include/mmf
+
+Name : @PC_NAME@
+Description : Read HEIF files
+Requires :
+Version : @VERSION@
+Libs : -L${libdir} @PC_LDFLAGS@
+Cflags : -I${includedir} -DLIBPREFIX=\"${libdir}\"
diff --git a/packaging/libheif.manifest b/packaging/libheif.manifest
new file mode 100644 (file)
index 0000000..017d22d
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+ <request>
+    <domain name="_"/>
+ </request>
+</manifest>
diff --git a/packaging/libheif.spec b/packaging/libheif.spec
new file mode 100644 (file)
index 0000000..0754ad7
--- /dev/null
@@ -0,0 +1,69 @@
+Name:       libheif
+Summary:    Multimedia Framework Library for HEIF(ISO/IEC 23008-12) image
+Version:    0.0.1
+Release:    0
+Group:      Multimedia/Libraries
+License:    Apache-2.0
+Source0:    %{name}-%{version}.tar.gz
+Source1001: libheif.manifest
+Requires(post):    /sbin/ldconfig
+Requires(postun):  /sbin/ldconfig
+
+BuildRequires:  cmake
+BuildRequires:  pkgconfig(dlog)
+BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(libavcodec)
+BuildRequires:  pkgconfig(libavformat)
+BuildRequires: pkgconfig(libavutil)
+BuildRequires: pkgconfig(libswscale)
+BuildRequires:  pkgconfig(mm-common)
+BuildRequires:  pkgconfig(mmutil-common)
+BuildRequires:  pkgconfig(mmutil-jpeg)
+BuildRequires:  pkgconfig(mmutil-magick)
+BuildRoot:  %{_tmppath}/%{name}-%{version}-build
+
+%description
+Multimedia Framework Library for HEIF(ISO/IEC 23008-12) image - Main package.
+
+%package devel
+Summary:    Multimedia Framework Utility Library (DEV)
+Group:      Development/Libraries
+Requires:   %{name} = %{version}-%{release}
+
+%description devel
+Multimedia Framework Utility for HEIF(ISO/IEC 23008-12) image - Development files.
+
+%prep
+%setup -q
+cp %{SOURCE1001} .
+
+%build
+export CFLAGS="$CFLAGS -DGMAGICK_DEBUG=0 -D_FORTIFY_SOURCE=2"
+# enable debug options if you need
+#export CFLAGS+=" -D__ENABLE_DEBUG_MODE -D__DEBUG_SOURCE -D__DEBUG_CODEC_CONFIG"
+
+%cmake .
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+%clean
+rm -rf %{buildroot}
+
+%post -p /sbin/ldconfig
+%postun -p /sbin/ldconfig
+
+%files
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/*.so.*
+%license LICENSE.APLv2.0
+
+%files devel
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%{_includedir}/*
+%{_libdir}/*.so
+%{_libdir}/pkgconfig/*
diff --git a/src/heif.c b/src/heif.c
new file mode 100644 (file)
index 0000000..3dc636a
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <unistd.h>
+
+#include <mm_attrs.h>
+#include <mm_error.h>
+
+#include "heif.h"
+#include "heif_debug.h"
+#include "heif_source.h"
+#include "heif_extractor.h"
+#include "heif_decoder.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr)                (sizeof(arr) / sizeof((arr)[0]))
+#endif
+#define HEIF_ATTRS_NAME                "heif-image"
+#define HEIC_MIMETYPE          "image/heic"
+#define DEFAULT_MIMETYPE       HEIC_MIMETYPE
+
+
+/**
+ * global values.
+ */
+static MMAttrsConstructInfo g_heif_attrs[] = {
+       {(char *)HEIF_IMAGE_MIMETYPE,                   MM_ATTRS_TYPE_STRING,   MM_ATTRS_FLAG_RW, (void *)DEFAULT_MIMETYPE},
+       {(char *)HEIF_IMAGE_WIDTH,                              MM_ATTRS_TYPE_INT,              MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)HEIF_IMAGE_HEIGHT,                             MM_ATTRS_TYPE_INT,              MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)HEIF_IMAGE_ORIENTATION,                MM_ATTRS_TYPE_INT,              MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)HEIF_IMAGE_MIRROR,                             MM_ATTRS_TYPE_INT,              MM_ATTRS_FLAG_RW, (void *)HEIF_MIRROR_NONE},
+       {(char *)HEIF_IMAGE_EXIF,                               MM_ATTRS_TYPE_DATA,             MM_ATTRS_FLAG_RW, (void *)NULL},
+       {(char *)HEIF_IMAGE_ICC_PROFILE,                MM_ATTRS_TYPE_DATA,             MM_ATTRS_FLAG_RW, (void *)NULL},
+};
+
+static int __heif_decode_image(heif_source_h source, heif_color_format_e format, heif_image_h *image);
+static int __heif_decode_thumb(heif_source_h source, heif_color_format_e format, heif_image_h *thumb);
+static int __heif_get_image_info(heif_source_h source, MMHandleType attrs);
+
+
+int heif_decode_image_from_file(const char *path, heif_color_format_e format, heif_image_h *image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_source_h source = NULL;
+
+       ret = heif_source_create_from_file(path, &source);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_source_create_from_file fail %d", ret);
+               return ret;
+       }
+
+       ret = __heif_decode_image(source, format, image);
+       if (ret != LIBHEIF_ERROR_NONE)
+               heif_error("__heif_decode_image fail %d", ret);
+
+       heif_source_destroy(source);
+
+       return ret;
+}
+
+int heif_decode_image_from_buffer(unsigned char *data, size_t size, heif_color_format_e format, heif_image_h *image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_source_h source = NULL;
+
+       ret = heif_source_create_from_buffer(data, size, &source);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_source_create_from_buffer fail %d", ret);
+               return ret;
+       }
+
+       ret = __heif_decode_image(source, format, image);
+       if (ret != LIBHEIF_ERROR_NONE)
+               heif_error("__heif_decode_image fail %d", ret);
+
+       heif_source_destroy(source);
+
+       return ret;
+}
+
+int heif_decode_thumb_from_file(const char *path, heif_color_format_e format, heif_image_h *thumb)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_source_h source = NULL;
+
+       ret = heif_source_create_from_file(path, &source);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_source_create_from_file fail %d", ret);
+               return ret;
+       }
+
+       ret = __heif_decode_thumb(source, format, thumb);
+       if (ret != LIBHEIF_ERROR_NONE)
+               heif_error("__heif_decode_thumb fail %d", ret);
+
+       heif_source_destroy(source);
+
+       return ret;
+}
+
+int heif_decode_thumb_from_buffer(unsigned char *data, size_t size, heif_color_format_e format, heif_image_h *thumb)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_source_h source = NULL;
+
+       ret = heif_source_create_from_buffer(data, size, &source);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_source_create_from_buffer fail %d", ret);
+               return ret;
+       }
+
+       ret = __heif_decode_thumb(source, format, thumb);
+       if (ret != LIBHEIF_ERROR_NONE)
+               heif_error("__heif_decode_thumb fail %d", ret);
+
+       heif_source_destroy(source);
+
+       return ret;
+}
+
+int heif_image_get_image(heif_image_h image, unsigned int *width, unsigned int *height, heif_color_format_e *format, unsigned char **data, size_t *size)
+{
+       return heif_decoder_get_image(image, width, height, format, data, size);
+}
+
+void heif_image_free(heif_image_h image)
+{
+       heif_decoder_destroy_image(image);
+}
+
+int heif_get_image_info_from_file(const char *path, heif_image_info_h *image_info)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_source_h source = NULL;
+       MMHandleType attrs = NULL;
+
+       ret = heif_source_create_from_file(path, &source);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_source_create_from_file fail %d", ret);
+               return ret;
+       }
+
+       ret = mm_attrs_new(g_heif_attrs, ARRAY_SIZE(g_heif_attrs), HEIF_ATTRS_NAME, NULL, NULL, &attrs);
+       if (ret != MM_ERROR_NONE) {
+               heif_error("mm_attrs_new fail %d", ret);
+               ret = LIBHEIF_ERROR_INVALID_OPERATION;
+               goto END;
+       }
+
+       ret = __heif_get_image_info(source, attrs);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__heif_get_image_info fail %d", ret);
+               mm_attrs_free(attrs);
+               goto END;
+       }
+
+       *image_info = (heif_image_info_h)attrs;
+
+END:
+       heif_source_destroy(source);
+
+       return ret;
+}
+
+int heif_get_image_info_from_buffer(unsigned char *data, size_t size, heif_image_info_h *image_info)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_source_h source = NULL;
+       MMHandleType attrs = NULL;
+
+       ret = heif_source_create_from_buffer(data, size, &source);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_source_create_from_buffer fail %d", ret);
+               return ret;
+       }
+
+       ret = mm_attrs_new(g_heif_attrs, ARRAY_SIZE(g_heif_attrs), HEIF_ATTRS_NAME, NULL, NULL, &attrs);
+       if (ret != MM_ERROR_NONE) {
+               heif_error("mm_attrs_new fail %d", ret);
+               ret = LIBHEIF_ERROR_INVALID_OPERATION;
+               goto END;
+       }
+
+       ret = __heif_get_image_info(source, attrs);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__heif_get_image_info fail %d", ret);
+               mm_attrs_free(attrs);
+               goto END;
+       }
+
+       *image_info = (heif_image_info_h)attrs;
+
+END:
+       heif_source_destroy(source);
+
+       return ret;
+}
+
+int heif_image_info_get_attrs(heif_image_info_h image_info, const char *first_attribute_name, ...)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       MMHandleType attrs = (MMHandleType)image_info;
+       va_list var_args;
+       char *err_attr_name = NULL;
+
+       heif_retvm_if_failed(first_attribute_name, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid first_attribute_name");
+
+       /* get requested attributes */
+       va_start(var_args, first_attribute_name);
+       ret = mm_attrs_get_valist(attrs, &err_attr_name, first_attribute_name, var_args);
+       va_end(var_args);
+
+       if (ret != MM_ERROR_NONE) {
+               if (err_attr_name) {
+                       heif_error("mm_attrs_get_valist fail %d: %s", ret, err_attr_name);
+                       free(err_attr_name);
+               }
+               if (ret == MM_ERROR_COMMON_INVALID_ARGUMENT)
+                       ret = LIBHEIF_ERROR_INVALID_PARAMETER;
+               else
+                       ret = LIBHEIF_ERROR_INVALID_OPERATION;
+       }
+
+       return ret;
+}
+
+void heif_image_info_free(heif_image_info_h image_info)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       MMHandleType attrs = (MMHandleType)image_info;
+       void *exif = NULL;
+       void *icc_profile = NULL;
+
+       heif_retm_if_failed(image_info);
+
+       ret = mm_attrs_get_data_by_name(attrs, HEIF_IMAGE_EXIF, &exif);
+       if (ret == MM_ERROR_NONE)
+               g_free(exif);
+
+       ret = mm_attrs_get_data_by_name(attrs, HEIF_IMAGE_ICC_PROFILE, &icc_profile);
+       if (ret == MM_ERROR_NONE)
+               g_free(icc_profile);
+
+       mm_attrs_free(attrs);
+}
+
+static int __heif_decode_image(heif_source_h source, heif_color_format_e format, heif_image_h *image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_itemtable_h item_table = NULL;
+       heif_image_h _image = NULL;
+
+       heif_retvm_if_failed(image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image");
+
+       ret = heif_extractor_extract(source, &item_table);
+       heif_retvm_if_failed(ret == LIBHEIF_ERROR_NONE, ret, "heif_extractor_extract fail %d", ret);
+
+       ret = heif_decoder_decode_primary(item_table, format, &_image);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_decoder_decode_primary fail %d", ret);
+               goto END;
+       }
+
+       *image = _image;
+
+END:
+       heif_itemtable_destroy(item_table);
+
+       return ret;
+}
+
+static int __heif_decode_thumb(heif_source_h source, heif_color_format_e format, heif_image_h *thumb)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_itemtable_h item_table = NULL;
+       heif_image_h _thumb = NULL;
+
+       heif_retvm_if_failed(thumb, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid thumb");
+
+       ret = heif_extractor_extract(source, &item_table);
+       heif_retvm_if_failed(ret == LIBHEIF_ERROR_NONE, ret, "heif_extractor_extract fail %d", ret);
+
+       ret = heif_decoder_decode_thumbnail(item_table, format, &_thumb);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_decoder_decode_thumbnail fail %d", ret);
+               goto END;
+       }
+
+       *thumb = _thumb;
+
+END:
+       heif_itemtable_destroy(item_table);
+
+       return ret;
+}
+
+static int __heif_get_image_info(heif_source_h source, MMHandleType attrs)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_itemtable_h item_table = NULL;
+       unsigned int primary_id = 0;
+       heif_image_item_h primary_item = NULL;
+       unsigned int width = 0, height = 0, orientation = 0;
+       heif_mirror_mode_e mirror_mode = HEIF_MIRROR_NONE;
+       void *exif = NULL, *icc_profile = NULL;
+       size_t exif_size = 0, icc_profile_size = 0;
+
+       heif_retvm_if_failed(attrs, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid attrs");
+
+       /* Extract heif image */
+       ret = heif_extractor_extract(source, &item_table);
+       heif_retvm_if_failed(ret == LIBHEIF_ERROR_NONE, ret, "heif_extractor_extract fail %d", ret);
+
+       /* Get information of primary image in HEIF */
+       ret = heif_itemtable_get_primary_image(item_table, &primary_id);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_itemtable_get_primary_image fail %d", ret);
+               goto END;
+       }
+
+       ret = heif_itemtable_get_image_item(item_table, primary_id, &primary_item);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_itemtable_get_image_item fail %d", ret);
+               goto END;
+       }
+
+       /* Set basic information of primary image into MM_ATTRS */
+       ret = heif_itemtable_get_image_resolution(primary_item, &width, &height);
+       if (ret == LIBHEIF_ERROR_NONE) {
+               ret = mm_attrs_set_int_by_name(attrs, HEIF_IMAGE_WIDTH, (int)width);
+               if (ret != MM_ERROR_NONE)
+                       heif_error("mm_attrs_set_int_by_name fail %d: %s", ret, HEIF_IMAGE_WIDTH);
+
+               ret = mm_attrs_set_int_by_name(attrs, HEIF_IMAGE_HEIGHT, (int)height);
+               if (ret != MM_ERROR_NONE)
+                       heif_error("mm_attrs_set_int_by_name fail %d: %s", ret, HEIF_IMAGE_HEIGHT);
+       } else {
+               heif_warning("heif_itemtable_get_image_resolution fail %d", ret);
+       }
+
+       ret = heif_itemtable_get_image_orientation(primary_item, &orientation);
+       if (ret == LIBHEIF_ERROR_NONE) {
+               ret = mm_attrs_set_int_by_name(attrs, HEIF_IMAGE_ORIENTATION, (int)orientation);
+               if (ret != MM_ERROR_NONE)
+                       heif_error("mm_attrs_set_int_by_name fail %d: %s", ret, HEIF_IMAGE_ORIENTATION);
+       } else {
+               heif_warning("heif_itemtable_get_image_orientation fail %d", ret);
+       }
+
+       ret = heif_itemtable_get_image_mirror_mode(primary_item, &mirror_mode);
+       if (ret == LIBHEIF_ERROR_NONE) {
+               ret = mm_attrs_set_int_by_name(attrs, HEIF_IMAGE_MIRROR, (int)mirror_mode);
+               if (ret != MM_ERROR_NONE)
+                       heif_error("mm_attrs_set_int_by_name fail %d: %s", ret, HEIF_IMAGE_MIRROR);
+       } else {
+               heif_warning("heif_itemtable_get_image_orientation fail %d", ret);
+       }
+
+       /* Set extra information of primary image into MM_ATTRS */
+       ret = heif_itemtable_get_exif(item_table, primary_item, &exif, &exif_size);
+       if (ret == LIBHEIF_ERROR_NONE) {
+               ret = mm_attrs_set_data_by_name(attrs, HEIF_IMAGE_EXIF, exif, exif_size);
+               if (ret != MM_ERROR_NONE)
+                       heif_error("mm_attrs_set_data_by_name fail %d: %s", ret, HEIF_IMAGE_EXIF);
+       } else {
+               heif_warning("heif_itemtable_get_exif fail %d", ret);
+       }
+
+       ret = heif_itemtable_get_icc_profile(item_table, primary_item, &icc_profile, &icc_profile_size);
+       if (ret == LIBHEIF_ERROR_NONE) {
+               ret = mm_attrs_set_data_by_name(attrs, HEIF_IMAGE_ICC_PROFILE, icc_profile, icc_profile_size);
+               if (ret != MM_ERROR_NONE)
+                       heif_error("mm_attrs_set_data_by_name fail %d: %s", ret, HEIF_IMAGE_ICC_PROFILE);
+       } else {
+               heif_warning("heif_itemtable_get_icc_profile fail %d", ret);
+       }
+
+       /* Commit attrs */
+       ret = mm_attrs_commit_all(attrs);
+END:
+       heif_itemtable_destroy(item_table);
+
+       return ret;
+}
diff --git a/src/heif_box_idat.c b/src/heif_box_idat.c
new file mode 100644 (file)
index 0000000..3ac04d3
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <glib.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_box_idat.h"
+
+typedef struct {
+       off_t idat_offset;
+       size_t idat_size;
+} idat_box_t;
+
+
+int heif_box_idat_create(heif_box_idat_h *idat_box)
+{
+       idat_box_t *_idat_box = NULL;
+
+       heif_retvm_if_failed(idat_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid idat_box");
+
+       _idat_box = g_new0(idat_box_t, 1);
+
+       *idat_box = (heif_box_idat_h)_idat_box;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_box_idat_destroy(heif_box_idat_h idat_box)
+{
+       heif_retm_if_failed(idat_box);
+
+       g_free(idat_box);
+}
+
+int heif_box_idat_get_idat(heif_box_idat_h idat_box, off_t *offset, size_t *size)
+{
+       idat_box_t *_idat_box = (idat_box_t *)idat_box;
+
+       heif_retvm_if_failed(idat_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid idat_box");
+       heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset");
+       heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size");
+
+       // only remember the offset and size of idat box for later use
+       *offset = _idat_box->idat_offset;
+       *size = _idat_box->idat_size;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_box_idat_parse(heif_box_idat_h idat_box, off_t offset, size_t size)
+{
+       idat_box_t *_idat_box = (idat_box_t *)idat_box;
+
+       heif_retvm_if_failed(idat_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid idat_box");
+
+       // only remember the offset and size of idat box for later use
+       _idat_box->idat_offset = offset;
+       _idat_box->idat_size = size;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_box_idat_info(heif_box_idat_h idat_box)
+{
+       idat_box_t *_idat_box = (idat_box_t *)idat_box;
+
+       heif_retm_if_failed(idat_box);
+
+       heif_info("============ [IDAT] Start ===========");
+       heif_info("[IDAT] idatOffset: %jd", _idat_box->idat_offset);
+       heif_info("[IDAT] idatSize: %zu", _idat_box->idat_size);
+       heif_info("============ [IDAT] End ===========\n");
+}
diff --git a/src/heif_box_idat.h b/src/heif_box_idat.h
new file mode 100644 (file)
index 0000000..7b4e075
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_BOX_IDAT_H__
+#define __HEIF_BOX_IDAT_H__
+
+#include <stdint.h>
+
+typedef void * heif_box_idat_h;
+
+int heif_box_idat_create(heif_box_idat_h *idat_box);
+void heif_box_idat_destroy(heif_box_idat_h idat_box);
+int heif_box_idat_get_idat(heif_box_idat_h idat_box, off_t *offset, size_t *size);
+int heif_box_idat_parse(heif_box_idat_h idat_box, off_t offset, size_t size);
+void heif_box_idat_info(heif_box_idat_h idat_box);
+
+#endif /* __HEIF_BOX_IDAT_H__ */
diff --git a/src/heif_box_iinf.c b/src/heif_box_iinf.c
new file mode 100644 (file)
index 0000000..25a4e78
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <glib.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_box_util.h"
+#include "heif_box_iinf.h"
+
+typedef struct {
+       _full_box_t full_box;
+       GSList *item_infos;
+} iinf_box_t;
+
+static int __parse_infe_box(_item_info_t *item_info, heif_source_h source, off_t offset, size_t size);
+static int __iinf_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data);
+static gint __compare_item_type(gconstpointer data, gconstpointer user_data);
+static gpointer __copy_item_info(gconstpointer src, gpointer user_data);
+
+
+int heif_box_iinf_create(heif_box_iinf_h *iinf_box)
+{
+       iinf_box_t *_iinf_box = NULL;
+
+       heif_retvm_if_failed(iinf_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iinf_box");
+
+       _iinf_box = g_new0(iinf_box_t, 1);
+       _iinf_box->item_infos = NULL;
+
+       *iinf_box = (heif_box_iinf_h)_iinf_box;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_box_iinf_destroy(heif_box_iinf_h iinf_box)
+{
+       iinf_box_t *_iinf_box = (iinf_box_t *)iinf_box;
+
+       heif_retm_if_failed(iinf_box);
+
+       g_slist_free_full(_iinf_box->item_infos, g_free);
+
+       g_free(iinf_box);
+}
+
+bool heif_box_iinf_has_type(heif_box_iinf_h iinf_box, uint32_t type)
+{
+       heif_retvm_if_failed(iinf_box, false, "invalid iinf_box");
+
+       return g_slist_find_custom(((iinf_box_t *)iinf_box)->item_infos, GUINT_TO_POINTER(type), __compare_item_type) ? true : false;
+}
+
+int heif_box_iinf_get_item_infos(heif_box_iinf_h iinf_box, GSList **item_infos)
+{
+       iinf_box_t *_iinf_box = (iinf_box_t *)iinf_box;
+
+       heif_retvm_if_failed(iinf_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iinf_box");
+       heif_retvm_if_failed(item_infos, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_infos");
+
+       *item_infos = g_slist_copy_deep(_iinf_box->item_infos, __copy_item_info, NULL);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_box_iinf_parse(heif_box_iinf_h iinf_box, heif_source_h source, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       iinf_box_t *_iinf_box = (iinf_box_t *)iinf_box;
+       size_t entry_count_size = 0;
+       uint32_t entry_count = 0;
+       off_t stop_offset = 0;
+       size_t i = 0;
+
+       heif_retvm_if_failed(iinf_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iinf_box");
+
+       ret = _parse_full_box_header(&(_iinf_box->full_box), source, &offset, &size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("_parse_full_box_header fail %d", ret);
+               return ret;
+       }
+
+       entry_count_size = _iinf_box->full_box.version == 0 ? 2 : 4;
+       if (size < entry_count_size) {
+               heif_error("invalid entry_count_size %zu", entry_count_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       if (!heif_source_get_uint32var(source, offset, &entry_count, entry_count_size))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+#ifdef __ENABLE_DEBUG_MODE
+       heif_info("entry_count %u", entry_count);
+#endif
+
+       stop_offset = offset + size;
+       offset += entry_count_size;
+       for (i = 0; i < entry_count && offset < stop_offset; i++) {
+#ifdef __ENABLE_DEBUG_MODE
+               heif_info("entry %zu", i);
+#endif
+               ret = _parse_box_chunk(source, &offset, (_parse_chunk_cb)__iinf_chunk_data_cb, _iinf_box);
+               if (ret != LIBHEIF_ERROR_NONE) {
+                       heif_error("_parse_box_chunk fail %d", ret);
+                       return ret;
+               }
+       }
+
+       if (offset != stop_offset) {
+               heif_error("offset[%jd] did not match stop_offset[%jd]", offset, stop_offset);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static void __print_item_infos(gpointer data, gpointer user_data)
+{
+       _item_info_t *item_info = (_item_info_t *)data;
+       unsigned int *idx = (unsigned int *)user_data;
+
+       heif_retm_if_failed(data);
+       heif_retm_if_failed(user_data);
+
+       heif_info("============ [ITEM INFO (%u)] ===========", *idx);
+       heif_info("[INFE] item_id: %u", item_info->item_id);
+       heif_info("[INFE] item_type: %u", item_info->item_type);
+       DEBUG_CHUNK_TYPE("INFE", item_info->item_type); // print string of item_type
+       heif_info("[INFE] hidden: %d", item_info->hidden);
+       heif_info("============ [ITEM INFO] End ===========");
+
+       (*idx)++;
+}
+
+void heif_box_iinf_info(heif_box_iinf_h iinf_box)
+{
+       iinf_box_t *_iinf_box = (iinf_box_t *)iinf_box;
+       unsigned int idx = 0;
+
+       heif_retm_if_failed(iinf_box);
+
+       heif_info("============ [IINF] Start ===========");
+       heif_info("[IINF] INFE count: %u", g_slist_length(_iinf_box->item_infos));
+       g_slist_foreach(_iinf_box->item_infos, __print_item_infos, &idx);
+       heif_info("============ [IINF] End ===========\n");
+}
+
+static int __parse_infe_box(_item_info_t *item_info, heif_source_h source, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       _full_box_t full_box = { 0, };
+       uint32_t item_id = 0;
+       size_t item_id_size = 0;
+       uint16_t item_protection_index = 0;
+       uint32_t item_type = 0;
+
+       heif_retvm_if_failed(item_info, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_info");
+
+       ret = _parse_full_box_header(&full_box, source, &offset, &size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("_parse_full_box_header fail %d", ret);
+               return ret;
+       }
+
+       if (full_box.version == 0 || full_box.version == 1) {
+               heif_error("version[%u] must be 2 or more", full_box.version);
+               return LIBHEIF_ERROR_NOT_SUPPORTED;
+       }
+
+       item_id_size = (full_box.version == 2) ? 2 : 4;
+       if (size < item_id_size + 6) {
+               heif_error("size[%zu] is smaller than item_id_size[%zu]", size, item_id_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       if (!heif_source_get_uint32var(source, offset, &item_id, item_id_size))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+#ifdef __ENABLE_DEBUG_MODE
+       heif_info("item_id %u", item_id);
+#endif
+       offset += item_id_size;
+
+       if (!heif_source_get_uint16(source, offset, &item_protection_index))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+#ifdef __ENABLE_DEBUG_MODE
+       heif_info("item_protection_index %u", item_protection_index);
+#endif
+       offset += sizeof(item_protection_index);
+
+       if (!heif_source_get_uint32(source, offset, &item_type))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       item_info->item_id = item_id;
+       item_info->item_type = item_type;
+       // According to HEIF spec, (flags & 1) indicates the image is hidden
+       // and not supposed to be displayed.
+       item_info->hidden = (full_box.flags & 1);
+
+       DEBUG_CHUNK_TYPE("item_type", item_type);
+       offset += sizeof(item_type);
+       size -= item_id_size + 6;
+
+       _skip_string(source, &offset, &size, "item_name");
+
+       if (item_type == FOURCC('m', 'i', 'm', 'e')) {
+               _skip_string(source, &offset, &size, "content_type");
+
+               // content_encoding is optional; can be omitted if would be empty
+               if (size > 0)
+                       _skip_string(source, &offset, &size, "content_encoding");
+       } else if (item_type == FOURCC('u', 'r', 'i', ' ')) {
+               _skip_string(source, &offset, &size, "item_uri_type");
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __iinf_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       iinf_box_t *iinf_box = (iinf_box_t *)user_data;
+       _item_info_t *item_info;
+
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(user_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid user_data");
+       heif_retvm_if_failed(type == FOURCC('i', 'n', 'f', 'e'), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid type %u", type);
+
+       item_info = g_new0(_item_info_t, 1);
+       ret = __parse_infe_box(item_info, source, offset, size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               g_free(item_info);
+               heif_error("__parse_infe_box fail %d", ret);
+               return (ret == LIBHEIF_ERROR_NOT_SUPPORTED) ? LIBHEIF_ERROR_NONE : ret;
+       }
+
+       iinf_box->item_infos = g_slist_append(iinf_box->item_infos, item_info);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static gint __compare_item_type(gconstpointer data, gconstpointer user_data)
+{
+       _item_info_t *item_info = (_item_info_t *)data;
+       uint32_t get_type = (uint32_t)GPOINTER_TO_UINT(user_data);
+
+       if (!item_info)
+               return -1;
+
+       return (item_info->item_type - get_type);
+}
+
+static gpointer __copy_item_info(gconstpointer src, gpointer user_data)
+{
+       _item_info_t *src_item = (_item_info_t *)src;
+       _item_info_t *copy_item = NULL;
+
+       heif_retvm_if_failed(src, NULL, "invalid src");
+
+       copy_item = g_new0(_item_info_t, 1);
+       copy_item->item_id = src_item->item_id;
+       copy_item->item_type = src_item->item_type;
+       copy_item->hidden = src_item->hidden;
+
+       return copy_item;
+}
diff --git a/src/heif_box_iinf.h b/src/heif_box_iinf.h
new file mode 100644 (file)
index 0000000..eff910f
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_BOX_IINF_H__
+#define __HEIF_BOX_IINF_H__
+
+#include <stdint.h>
+#include "heif_source.h"
+
+typedef void * heif_box_iinf_h;
+
+int heif_box_iinf_create(heif_box_iinf_h *iinf_box);
+void heif_box_iinf_destroy(heif_box_iinf_h iinf_box);
+bool heif_box_iinf_has_type(heif_box_iinf_h iinf_box, uint32_t type);
+int heif_box_iinf_get_item_infos(heif_box_iinf_h iinf_box, GSList **item_infos);
+int heif_box_iinf_parse(heif_box_iinf_h iinf_box, heif_source_h source, off_t offset, size_t size);
+void heif_box_iinf_info(heif_box_iinf_h iinf_box);
+
+#endif /* __HEIF_BOX_IINF_H__ */
diff --git a/src/heif_box_iloc.c b/src/heif_box_iloc.c
new file mode 100644 (file)
index 0000000..f836e7f
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <stdbool.h>
+#include <glib.h>
+#include <inttypes.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_box_util.h"
+#include "heif_box_iloc.h"
+
+typedef struct {
+       _full_box_t full_box;
+       unsigned int item_count;
+       bool has_construction_method;
+       GSList *item_locs;
+} iloc_box_t;
+
+
+static bool __is_valid_sizefield(uint32_t size);
+static gpointer __dup_extent(gconstpointer src, gpointer user_data);
+static gpointer __dup_item_loc(gconstpointer src, gpointer user_data);
+
+
+int heif_box_iloc_create(heif_box_iloc_h *iloc_box)
+{
+       iloc_box_t *_iloc_box = NULL;
+
+       heif_retvm_if_failed(iloc_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iloc_box");
+
+       _iloc_box = g_new0(iloc_box_t, 1);
+
+       *iloc_box = (heif_box_iloc_h)_iloc_box;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_box_iloc_destroy(heif_box_iloc_h iloc_box)
+{
+       heif_retm_if_failed(iloc_box);
+
+       g_slist_free_full(((iloc_box_t *)iloc_box)->item_locs, _free_item_loc);
+
+       g_free(iloc_box);
+}
+
+bool heif_box_iloc_has_construction_method(heif_box_iloc_h iloc_box)
+{
+       heif_retvm_if_failed(iloc_box, false, "invalid iloc_box");
+
+       return ((iloc_box_t *)iloc_box)->has_construction_method;
+}
+
+int heif_box_iloc_get_item_locs(heif_box_iloc_h iloc_box, GSList **item_locs)
+{
+       iloc_box_t *_iloc_box = (iloc_box_t *)iloc_box;
+
+       heif_retvm_if_failed(iloc_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iloc_box");
+       heif_retvm_if_failed(item_locs, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_locs");
+
+       *item_locs = g_slist_copy_deep(_iloc_box->item_locs, __dup_item_loc, NULL);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_box_iloc_parse(heif_box_iloc_h iloc_box, heif_source_h source, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       iloc_box_t *_iloc_box = (iloc_box_t *)iloc_box;
+       _full_box_t *full_box = NULL;
+       uint8_t offset_size = 0;
+       uint8_t length_size = 0;
+       uint8_t base_offset_size = 0;
+       uint8_t index_size = 0;
+       uint32_t item_count = 0;
+       uint8_t item_field_size = 0;
+       uint32_t i = 0;
+
+       heif_retvm_if_failed(iloc_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iloc_box");
+
+       full_box = &(_iloc_box->full_box);
+
+       ret = _parse_full_box_header(full_box, source, &offset, &size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("_parse_full_box_header fail %d", ret);
+               return ret;
+       }
+
+       if (full_box->version > 2) {
+               heif_error("invalid version %u", full_box->version);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       if (size < 2) {
+               heif_error("size[%zu] is too small", size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       if (heif_source_read_at(source, offset++, &offset_size, 1) != 1)
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       length_size = (offset_size & 0xF);
+       offset_size >>= 4;
+
+       if (heif_source_read_at(source, offset++, &base_offset_size, 1) != 1)
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       if (full_box->version == 1 || full_box->version == 2)
+               index_size = (base_offset_size & 0xF);
+
+       base_offset_size >>= 4;
+       size -= 2;
+
+       if (!__is_valid_sizefield(offset_size)
+                       || !__is_valid_sizefield(length_size)
+                       || !__is_valid_sizefield(base_offset_size)
+                       || !__is_valid_sizefield(index_size)) {
+               heif_error("loc offset/size not valid: %u, %u, %u, %u",
+                               offset_size, length_size, base_offset_size, index_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       item_field_size = full_box->version < 2 ? 2 : 4;
+       if (size < item_field_size) {
+               heif_error("size[%zu] is smaller than item_field_size[%u]", size, item_field_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+       if (!heif_source_get_uint32var(source, offset, &item_count, (size_t)item_field_size))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       _iloc_box->item_count = (unsigned int)item_count;
+#ifdef __ENABLE_DEBUG_MODE
+       heif_info("item_count %u", _iloc_box->item_count);
+#endif
+       offset += item_field_size;
+       size -= item_field_size;
+
+       for (i = 0; i < item_count; i++) {
+               uint32_t item_id = 0;
+               uint8_t construction_method = 0;
+               uint16_t data_reference_index = 0;
+               uint64_t base_offset = 0;
+               _item_loc_t *item_loc = NULL;
+               uint16_t extent_count = 0;
+
+               if (!heif_source_get_uint32var(source, offset, &item_id, (size_t)item_field_size))
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+#ifdef __ENABLE_DEBUG_MODE
+               heif_info("item[%u]: id %u", i, item_id);
+#endif
+               offset += item_field_size;
+
+               if (full_box->version == 1 || full_box->version == 2) {
+                       uint8_t buf[2];
+                       if (heif_source_read_at(source, offset, buf, 2) != 2)
+                               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+                       construction_method = (buf[1] & 0xF);
+#ifdef __ENABLE_DEBUG_MODE
+                       heif_info("construction_method %u", construction_method);
+#endif
+                       if (construction_method == 1)
+                               _iloc_box->has_construction_method = true;
+
+                       offset += 2;
+               }
+
+               if (!heif_source_get_uint16(source, offset, &data_reference_index))
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+               if (data_reference_index != 0) {
+                       // we don't support reference to other files
+                       heif_error("invalid data_reference_index[%u]", data_reference_index);
+                       return LIBHEIF_ERROR_INVALID_OPERATION;
+               }
+               offset += 2;
+
+               if (base_offset_size != 0) {
+                       if (!heif_source_get_uint64var(source, offset, &base_offset, base_offset_size))
+                               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+                       offset += base_offset_size;
+               }
+#ifdef __ENABLE_DEBUG_MODE
+               heif_info("base_offset %"PRIu64, base_offset);
+#endif
+
+               item_loc = g_new0(_item_loc_t, 1);
+               item_loc->item_id = item_id;
+               item_loc->construction_method = construction_method;
+               item_loc->data_reference_index = data_reference_index;
+               item_loc->base_offset = base_offset;
+
+               _iloc_box->item_locs = g_slist_append(_iloc_box->item_locs, item_loc);
+
+               if (!heif_source_get_uint16(source, offset, &extent_count))
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+#ifdef __ENABLE_DEBUG_MODE
+               heif_info("extent_count %u", extent_count);
+#endif
+
+               if (extent_count > 1) {
+                       heif_error("invalid extent_count %d", extent_count);
+                       return LIBHEIF_ERROR_INVALID_OPERATION;
+               }
+               offset += 2;
+
+               for (size_t j = 0; j < extent_count; j++) {
+                       uint64_t extent_index = 1; // default=1
+                       uint64_t extent_offset = 0; // default=0
+                       uint64_t extent_length = 0; // this indicates full length of file
+
+                       if ((full_box->version == 1 || full_box->version == 2) && (index_size > 0)) {
+                               if (!heif_source_get_uint64var(source, offset, &extent_index, index_size))
+                                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+                               // TODO: add support for this mode
+                               offset += index_size;
+#ifdef __ENABLE_DEBUG_MODE
+                               heif_info("extent_index %"PRIu64, extent_index);
+#endif
+                       }
+
+                       if (offset_size > 0) {
+                               if (!heif_source_get_uint64var(source, offset, &extent_offset, offset_size))
+                                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+                               offset += offset_size;
+                       }
+#ifdef __ENABLE_DEBUG_MODE
+                       heif_info("extent_offset %"PRIu64, extent_offset);
+#endif
+
+                       if (length_size > 0) {
+                               if (!heif_source_get_uint64var(source, offset, &extent_length, length_size))
+                                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+                               offset += length_size;
+                       }
+#ifdef __ENABLE_DEBUG_MODE
+                       heif_info("extent_length %"PRIu64, extent_length);
+#endif
+                       _extent_entry_t *extent_entry = g_new0(_extent_entry_t, 1);
+                       extent_entry->extent_index = extent_index;
+                       extent_entry->extent_offset = extent_offset;
+                       extent_entry->extent_length = extent_length;
+
+                       item_loc->extents = g_slist_append(item_loc->extents, extent_entry);
+               }
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static void __print_extent_entry(gpointer data, gpointer user_data)
+{
+       _extent_entry_t *extent_entry = (_extent_entry_t *)data;
+
+       heif_retm_if_failed(data);
+
+       heif_info("\t [EXTENT_ENTRY] extent_index: %"PRIu64, extent_entry->extent_index);
+       heif_info("\t [EXTENT_ENTRY] extent_offset: %"PRIu64, extent_entry->extent_offset);
+       heif_info("\t [EXTENT_ENTRY] extent_length: %"PRIu64, extent_entry->extent_length);
+}
+
+static void __print_item_loc(gpointer data, gpointer user_data)
+{
+       _item_loc_t *item_loc = (_item_loc_t *)data;
+
+       heif_retm_if_failed(data);
+
+       heif_info("[ILOC_ITEM] item_id: %u", item_loc->item_id);
+       heif_info("[ILOC_ITEM] construction_method: %u", item_loc->construction_method);
+       heif_info("[ILOC_ITEM] data_reference_index: %u", item_loc->data_reference_index);
+       heif_info("[ILOC_ITEM] base_offset: %jd", item_loc->base_offset);
+       g_slist_foreach(item_loc->extents, __print_extent_entry, NULL);
+}
+
+void heif_box_iloc_info(heif_box_iloc_h iloc_box)
+{
+       iloc_box_t *_iloc_box = (iloc_box_t *)iloc_box;
+
+       heif_retm_if_failed(iloc_box);
+
+       heif_info("============ [ILOC] Start ===========");
+       DEBUG_FULL_BOX(&_iloc_box->full_box, "ILOC");
+       heif_info("[ILOC] itemCount: %u", _iloc_box->item_count);
+       heif_info("[ILOC] hasConstructionMethod: %d", _iloc_box->has_construction_method);
+       g_slist_foreach(_iloc_box->item_locs, __print_item_loc, NULL);
+       heif_info("============ [ILOC] End ===========\n");
+}
+
+static bool __is_valid_sizefield(uint32_t size)
+{
+       return size == 0 || size == 4 || size == 8;
+}
+
+static gpointer __dup_extent(gconstpointer src, gpointer user_data)
+{
+       _extent_entry_t *src_ext = (_extent_entry_t *)src;
+       _extent_entry_t *copy_ext = NULL;
+
+       heif_retvm_if_failed(src, NULL, "invalid src");
+
+       copy_ext = g_new0(_extent_entry_t, 1);
+       copy_ext->extent_index = src_ext->extent_index;
+       copy_ext->extent_offset = src_ext->extent_offset;
+       copy_ext->extent_length = src_ext->extent_length;
+
+       return copy_ext;
+}
+
+static gpointer __dup_item_loc(gconstpointer src, gpointer user_data)
+{
+       _item_loc_t *src_iloc = (_item_loc_t *)src;
+       _item_loc_t *copy_iloc = NULL;
+
+       heif_retvm_if_failed(src, NULL, "invalid src");
+
+       copy_iloc = g_new0(_item_loc_t, 1);
+       copy_iloc->item_id = src_iloc->item_id;
+       copy_iloc->construction_method = src_iloc->construction_method;
+       copy_iloc->data_reference_index = src_iloc->data_reference_index;
+       copy_iloc->base_offset = src_iloc->base_offset;
+       copy_iloc->extents = g_slist_copy_deep(src_iloc->extents, __dup_extent, NULL);
+
+       return copy_iloc;
+}
diff --git a/src/heif_box_iloc.h b/src/heif_box_iloc.h
new file mode 100644 (file)
index 0000000..2a1a550
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_BOX_ILOC_H__
+#define __HEIF_BOX_ILOC_H__
+
+#include <stdint.h>
+
+#include "heif_source.h"
+
+typedef void * heif_box_iloc_h;
+
+int heif_box_iloc_create(heif_box_iloc_h *iloc_box);
+void heif_box_iloc_destroy(heif_box_iloc_h iloc_box);
+bool heif_box_iloc_has_construction_method(heif_box_iloc_h iloc_box);
+int heif_box_iloc_get_item_locs(heif_box_iloc_h iloc_box, GSList **item_locs);
+int heif_box_iloc_parse(heif_box_iloc_h iloc_box, heif_source_h source, off_t offset, size_t size);
+void heif_box_iloc_info(heif_box_iloc_h iloc_box);
+
+#endif /* __HEIF_BOX_ILOC_H__ */
diff --git a/src/heif_box_iprp.c b/src/heif_box_iprp.c
new file mode 100644 (file)
index 0000000..8f59825
--- /dev/null
@@ -0,0 +1,699 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <stdbool.h>
+#include <glib.h>
+#include <inttypes.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_box_util.h"
+#include "heif_box_iprp.h"
+
+typedef struct {
+       GSList *properties;
+} ipco_box_t;
+
+typedef struct {
+       _full_box_t full_box;
+       GSList *associations;
+} ipma_box_t;
+
+typedef struct {
+       ipco_box_t *ipco_box;
+       ipma_box_t *ipma_box;
+} iprp_box_t;
+
+static gpointer __copy_properties(gconstpointer src, gpointer data);
+static gpointer __copy_associations(gconstpointer src, gpointer data);
+
+static void __ipco_box_info(ipco_box_t *ipco_box);
+static int __ipco_box_create(ipco_box_t **ipco_box);
+static void __ipco_box_destroy(ipco_box_t *ipco_box);
+static int __ipco_box_parse(ipco_box_t *ipco_box, heif_source_h source, off_t offset, size_t size);
+
+static void __ipma_box_info(ipma_box_t *ipma_box);
+static int __ipma_box_create(ipma_box_t **ipma_box);
+static void __ipma_box_destroy(ipma_box_t *ipma_box);
+static int __ipma_box_parse(ipma_box_t *ipma_box, heif_source_h source, off_t offset, size_t size);
+
+static int __iprp_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       iprp_box_t *_iprp_box = (iprp_box_t *)user_data;
+
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(user_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid user_data");
+
+       heif_info("offset %jd, size %zu", offset, size);
+
+       switch (type) {
+       case FOURCC('i', 'p', 'c', 'o'):
+               if (!_iprp_box->ipco_box) {
+                       ret = __ipco_box_create(&_iprp_box->ipco_box);
+                       if (ret != LIBHEIF_ERROR_NONE) {
+                               heif_error("__ipco_box_create fail (%d)", ret);
+                               return ret;
+                       }
+               }
+               return __ipco_box_parse(_iprp_box->ipco_box, source, offset, size);
+
+       case FOURCC('i', 'p', 'm', 'a'):
+               if (!_iprp_box->ipma_box) {
+                       ret = __ipma_box_create(&_iprp_box->ipma_box);
+                       if (ret != LIBHEIF_ERROR_NONE) {
+                               heif_error("__ipco_box_create fail (%d)", ret);
+                               return ret;
+                       }
+               }
+               return __ipma_box_parse(_iprp_box->ipma_box, source, offset, size);
+
+       default:
+               DEBUG_CHUNK_TYPE("Unrecognized", type);
+               break;
+       }
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_box_iprp_info(heif_box_iprp_h iprp_box)
+{
+       iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box;
+
+       heif_retm_if_failed(iprp_box);
+
+       heif_info("============ [IPRP] Start ===========");
+       __ipco_box_info(_iprp_box->ipco_box);
+       __ipma_box_info(_iprp_box->ipma_box);
+       heif_info("============ [IPRP] End ===========");
+}
+
+int heif_box_iprp_create(heif_box_iprp_h *iprp_box)
+{
+       iprp_box_t *_iprp_box = NULL;
+
+       heif_retvm_if_failed(iprp_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iprp_box");
+
+       _iprp_box = g_new0(iprp_box_t, 1);
+
+       *iprp_box = (heif_box_iprp_h)_iprp_box;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_box_iprp_destroy(heif_box_iprp_h iprp_box)
+{
+       iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box;
+
+       heif_retm_if_failed(iprp_box);
+
+       __ipco_box_destroy(_iprp_box->ipco_box);
+       __ipma_box_destroy(_iprp_box->ipma_box);
+
+       g_free(iprp_box);
+}
+
+int heif_box_iprp_get_properties(heif_box_iprp_h iprp_box, GSList **properties)
+{
+       iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box;
+
+       heif_retvm_if_failed(iprp_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iprp_box");
+       heif_retvm_if_failed(properties, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid properties");
+
+       *properties = g_slist_copy_deep(_iprp_box->ipco_box->properties, __copy_properties, NULL);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_box_iprp_get_associations(heif_box_iprp_h iprp_box, GSList **associations)
+{
+       iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box;
+
+       heif_retvm_if_failed(iprp_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iprp_box");
+       heif_retvm_if_failed(associations, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid associations");
+
+       *associations = g_slist_copy_deep(_iprp_box->ipma_box->associations, __copy_associations, NULL);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_box_iprp_parse(heif_box_iprp_h iprp_box, heif_source_h source, off_t offset, size_t size)
+{
+       iprp_box_t *_iprp_box = (iprp_box_t *)iprp_box;
+
+       heif_retvm_if_failed(iprp_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iprp_box");
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+
+       return _parse_box_chunks(source, offset, size, (_parse_chunk_cb)__iprp_chunk_data_cb, _iprp_box);
+}
+
+static gpointer __copy_properties(gconstpointer src, gpointer data)
+{
+       _item_prop_t *src_prop = (_item_prop_t *)src;
+       _item_prop_t *copy_prop = NULL;
+
+       heif_retvm_if_failed(src, NULL, "invalid src");
+       heif_retvm_if_failed(src_prop->property, NULL, "invalid src");
+
+       copy_prop = g_new0(_item_prop_t, 1);
+       copy_prop->type = src_prop->type;
+
+       switch (src_prop->type) {
+       case FOURCC('h', 'v', 'c', 'C'):
+       {
+               _hvcC_prop_t *src_hvcC = (_hvcC_prop_t *)src_prop->property;
+               _hvcC_prop_t *copy_hvcC = g_new0(_hvcC_prop_t, 1);
+
+               copy_hvcC->data = g_memdup(src_hvcC->data, src_hvcC->size);
+               copy_hvcC->size = src_hvcC->size;
+               copy_prop->property = copy_hvcC;
+               break;
+       }
+
+       case FOURCC('i', 's', 'p', 'e'):
+       {
+               _ispe_prop_t *src_ispe = (_ispe_prop_t *)src_prop->property;
+               _ispe_prop_t *copy_ispe = g_new0(_ispe_prop_t, 1);
+
+               copy_ispe->width = src_ispe->width;
+               copy_ispe->height = src_ispe->height;
+               copy_prop->property = copy_ispe;
+               break;
+       }
+
+       case FOURCC('i', 'r', 'o', 't'):
+       {
+               _irot_prop_t *src_irot = (_irot_prop_t *)src_prop->property;
+               _irot_prop_t *copy_irot = g_new0(_irot_prop_t, 1);
+
+               copy_irot->angle = src_irot->angle;
+               copy_prop->property = copy_irot;
+               break;
+       }
+
+       case FOURCC('c', 'o', 'l', 'r'):
+       {
+               _colr_prop_t *src_colr = (_colr_prop_t *)src_prop->property;
+               _colr_prop_t *copy_colr = g_new0(_colr_prop_t, 1);
+
+               copy_colr->icc_data = g_memdup(src_colr->icc_data, src_colr->size);
+               copy_colr->size = src_colr->size;
+               copy_prop->property = copy_colr;
+               break;
+       }
+
+       case FOURCC('i', 'm', 'i', 'r'):
+       {
+               _imir_prop_t *src_imir = (_imir_prop_t *)src_prop->property;
+               _imir_prop_t *copy_imir = g_new0(_imir_prop_t, 1);
+
+               copy_imir->axis = src_imir->axis;
+               copy_prop->property = copy_imir;
+               break;
+       }
+
+       default:
+               // do nothing...
+               break;
+       }
+
+       return copy_prop;
+}
+
+static gpointer __copy_associations(gconstpointer src, gpointer data)
+{
+       _association_t *src_assoc = (_association_t *)src;
+       _association_t *copy_assoc = NULL;
+
+       heif_retvm_if_failed(src, NULL, "invalid src");
+
+       copy_assoc = g_new0(_association_t, 1);
+       copy_assoc->item_id = src_assoc->item_id;
+       copy_assoc->essential = src_assoc->essential;
+       copy_assoc->index = src_assoc->index;
+
+       return copy_assoc;
+}
+
+void __print_hvcC_box_info(_hvcC_prop_t *hvcC_prop)
+{
+       heif_retm_if_failed(hvcC_prop);
+
+       heif_info("============ [HVCC] Start ===========");
+       heif_info("[HVCC] size: %zu", hvcC_prop->size);
+       heif_info("============ [HVCC] End ===========");
+}
+
+static int __parse_hvcC_box(_hvcC_prop_t *hvcC_prop, heif_source_h source, off_t offset, size_t size)
+{
+       heif_retvm_if_failed(hvcC_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid hvcC_prop");
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(size >= 1, LIBHEIF_ERROR_NONE, "invalid size %zu", size);
+
+       hvcC_prop->data = g_malloc0(size);
+       hvcC_prop->size = size;
+       if (heif_source_read_at(source, offset, hvcC_prop->data, size) < (ssize_t)size)
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       heif_info("property hvcC: size %zu", hvcC_prop->size);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void __print_ispe_box_info(_ispe_prop_t *ispe_prop)
+{
+       heif_retm_if_failed(ispe_prop);
+
+       heif_info("============ [ISPE] Start ===========");
+       DEBUG_FULL_BOX(&ispe_prop->full_box, "ISPE");
+       heif_info("[ISPE] width: %u", ispe_prop->width);
+       heif_info("[ISPE] height: %u", ispe_prop->height);
+       heif_info("============ [ISPE] End ===========");
+}
+
+static int __parse_ispe_box(_ispe_prop_t *ispe_prop, heif_source_h source, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+
+       heif_retvm_if_failed(ispe_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ispe_prop");
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(size >= 8, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size %zu", size);
+
+       ret = _parse_full_box_header(&ispe_prop->full_box, source, &offset, &size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("_parse_full_box_header fail %d", ret);
+               return ret;
+       }
+
+       if (!heif_source_get_uint32(source, offset, &ispe_prop->width) ||
+               !heif_source_get_uint32(source, offset + 4, &ispe_prop->height)) {
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       // Validate that the dimension doesn't cause overflow on calculated max input size.
+       // Max input size is width*height*1.5, restrict width*height to 1<<29 so that
+       if (ispe_prop->width == 0 || ispe_prop->height == 0 || ispe_prop->width > (1 << 29) / ispe_prop->height) {
+               heif_error("invalid width[%u] & height[%u]", ispe_prop->width, ispe_prop->height);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       heif_info("property ispe: %ux%u", ispe_prop->width, ispe_prop->height);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void __print_irot_box_info(_irot_prop_t *irot_prop)
+{
+       heif_retm_if_failed(irot_prop);
+
+       heif_info("============ [IROT] Start ===========");
+       heif_info("[IROT] angle: %u", irot_prop->angle);
+       heif_info("============ [IROT] End ===========");
+}
+
+static int __parse_irot_box(_irot_prop_t *irot_prop, heif_source_h source, off_t offset, size_t size)
+{
+       heif_retvm_if_failed(irot_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid irot_prop");
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(size >= 1, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size %zu", size);
+
+       if (heif_source_read_at(source, offset, &irot_prop->angle, 1) != 1)
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       irot_prop->angle &= 0x3;
+       heif_info("property irot: %u", irot_prop->angle);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void __print_colr_box_info(_colr_prop_t *colr_prop)
+{
+       heif_retm_if_failed(colr_prop);
+
+       heif_info("============ [COLR] Start ===========");
+       heif_info("[COLR] size: %zu", colr_prop->size);
+       heif_info("============ [COLR] End ===========");
+}
+
+static int __parse_colr_box(_colr_prop_t *colr_prop, heif_source_h source, off_t offset, size_t size)
+{
+       uint32_t colour_type;
+
+       heif_retvm_if_failed(colr_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid colr_prop");
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(size >= 4, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size %zu", size);
+
+       if (!heif_source_get_uint32(source, offset, &colour_type))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       offset += 4;
+       size -= 4;
+
+       if (colour_type == FOURCC('n', 'c', 'l', 'x')) {
+               DEBUG_CHUNK_TYPE("Not supported", colour_type);
+               return LIBHEIF_ERROR_NONE;
+       }
+
+       if ((colour_type != FOURCC('r', 'I', 'C', 'C')) &&
+               (colour_type != FOURCC('p', 'r', 'o', 'f'))) {
+               DEBUG_CHUNK_TYPE("invalid colour type", colour_type);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       colr_prop->icc_data = g_malloc0(size);
+       colr_prop->size = size;
+
+       if (heif_source_read_at(source, offset, colr_prop->icc_data, size) != (ssize_t)size)
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       heif_info("property Colr: size %zd", colr_prop->size);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void __print_imir_box_info(_imir_prop_t *imir_prop)
+{
+       heif_retm_if_failed(imir_prop);
+
+       heif_info("============ [IMIR] Start ===========");
+       heif_info("[IMIR] axis: %u", imir_prop->axis);
+       heif_info("============ [IMIR] End ===========");
+}
+
+static int __parse_imir_box(_imir_prop_t *imir_prop, heif_source_h source, off_t offset, size_t size)
+{
+       uint8_t axis = 0;
+
+       heif_retvm_if_failed(imir_prop, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid imir_prop");
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(size >= 1, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size %zu", size);
+
+       if (!heif_source_read_at(source, offset, &axis, 1))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       imir_prop->axis = axis;
+       heif_info("property mirror(axis): %u", axis);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static void __print_image_prop(gpointer data, gpointer user_data)
+{
+       _item_prop_t *image_prop = (_item_prop_t *)data;
+       unsigned int *idx = (unsigned int *)user_data;
+
+       heif_retm_if_failed(data);
+       heif_retm_if_failed(user_data);
+
+       heif_info("============ [IMAGE PROP (%u)] ===========", *idx);
+       switch (image_prop->type) {
+       case FOURCC('h', 'v', 'c', 'C'):
+               __print_hvcC_box_info((_hvcC_prop_t *)image_prop->property);
+               break;
+
+       case FOURCC('i', 's', 'p', 'e'):
+               __print_ispe_box_info((_ispe_prop_t *)image_prop->property);
+               break;
+
+       case FOURCC('i', 'r', 'o', 't'):
+               __print_irot_box_info((_irot_prop_t *)image_prop->property);
+               break;
+
+       case FOURCC('c', 'o', 'l', 'r'):
+               __print_colr_box_info((_colr_prop_t *)image_prop->property);
+               break;
+
+       case FOURCC('i', 'm', 'i', 'r'):
+               __print_imir_box_info((_imir_prop_t *)image_prop->property);
+               break;
+
+       default:
+               DEBUG_CHUNK_TYPE("Not supported", image_prop->type);
+               break;
+       }
+
+       (*idx)++;
+}
+
+static int __ipco_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       _item_prop_t *image_prop;
+       GSList **properties = (GSList **)user_data;
+
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(user_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid user_data");
+
+       heif_info("offset %jd, size %zu", offset, size);
+
+       image_prop = g_new0(_item_prop_t, 1);
+       image_prop->type = type;
+
+       switch (type) {
+       case FOURCC('h', 'v', 'c', 'C'):
+               image_prop->property = g_new0(_hvcC_prop_t, 1);
+               ret = __parse_hvcC_box(image_prop->property, source, offset, size);
+               break;
+
+       case FOURCC('i', 's', 'p', 'e'):
+               image_prop->property = g_new0(_ispe_prop_t, 1);
+               ret = __parse_ispe_box(image_prop->property, source, offset, size);
+               break;
+
+       case FOURCC('i', 'r', 'o', 't'):
+               image_prop->property = g_new0(_irot_prop_t, 1);
+               ret = __parse_irot_box(image_prop->property, source, offset, size);
+               break;
+
+       case FOURCC('c', 'o', 'l', 'r'):
+               image_prop->property = g_new0(_colr_prop_t, 1);
+               ret = __parse_colr_box(image_prop->property, source, offset, size);
+               break;
+
+       case FOURCC('i', 'm', 'i', 'r'):
+               image_prop->property = g_new0(_imir_prop_t, 1);
+               ret = __parse_imir_box(image_prop->property, source, offset, size);
+               break;
+
+       default:
+               DEBUG_CHUNK_TYPE("Not supported", type);
+               g_free(image_prop);
+               return LIBHEIF_ERROR_NONE;
+       }
+
+       if (ret != LIBHEIF_ERROR_NONE) {
+               DEBUG_CHUNK_TYPE("Invalid property", type);
+               _free_image_prop(image_prop);
+               return ret;
+       }
+
+       *properties = g_slist_append(*properties, image_prop);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static void __ipco_box_info(ipco_box_t *ipco_box)
+{
+       ipco_box_t *_ipco_box = (ipco_box_t *)ipco_box;
+       unsigned int idx = 1;
+
+       heif_retm_if_failed(ipco_box);
+
+       heif_info("============ [IPCO] Start ===========");
+       heif_info("[IPCO] image prop: %u", g_slist_length(_ipco_box->properties));
+       g_slist_foreach(_ipco_box->properties, __print_image_prop, &idx);
+       heif_info("============ [IMAGE PROP] End ===========");
+       heif_info("============ [IPCO] End ===========\n");
+}
+
+static int __ipco_box_create(ipco_box_t **ipco_box)
+{
+       ipco_box_t *_ipco_box;
+
+       heif_retvm_if_failed(ipco_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ipco_box");
+
+       _ipco_box = g_new0(ipco_box_t, 1);
+       _ipco_box->properties = NULL;
+
+       *ipco_box = (ipco_box_t *)_ipco_box;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static void __ipco_box_destroy(ipco_box_t *ipco_box)
+{
+       ipco_box_t *_ipco_box = (ipco_box_t *)ipco_box;
+
+       heif_retm_if_failed(ipco_box);
+
+       g_slist_free_full(_ipco_box->properties, _free_image_prop);
+
+       g_free(ipco_box);
+}
+
+static int __ipco_box_parse(ipco_box_t *ipco_box, heif_source_h source, off_t offset, size_t size)
+{
+       ipco_box_t *_ipco_box = (ipco_box_t *)ipco_box;
+
+       heif_retvm_if_failed(ipco_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ipco_box");
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+
+       // push a placeholder as the index is 1-based
+       return _parse_box_chunks(source, offset, size, (_parse_chunk_cb)__ipco_chunk_data_cb, &_ipco_box->properties);
+}
+
+static void __print_association(gpointer data, gpointer user_data)
+{
+       _association_t *association = (_association_t *)data;
+       unsigned int *idx = (unsigned int *)user_data;
+
+       heif_retm_if_failed(data);
+
+       heif_info("============ [ASSOCIATION (%u)] ===========", *idx);
+       heif_info("[ASSOCIATION] item id %u", association->item_id);
+       heif_info("[ASSOCIATION] associated to property %u", association->index);
+       heif_info("[ASSOCIATION] essential %d", association->essential);
+
+       (*idx)++;
+}
+
+static void __ipma_box_info(ipma_box_t *ipma_box)
+{
+       ipma_box_t *_ipma_box = (ipma_box_t *)ipma_box;
+       unsigned int idx = 1;
+
+       heif_retm_if_failed(ipma_box);
+
+       heif_info("============ [IPMA] Start ===========");
+       heif_info("[IPMA] associations: %u", g_slist_length(_ipma_box->associations));
+       g_slist_foreach(_ipma_box->associations, __print_association, &idx);
+       heif_info("============ [ASSOCIATION] End ===========");
+       heif_info("============ [IPMA] End ===========\n");
+}
+
+static int __ipma_box_create(ipma_box_t **ipma_box)
+{
+       ipma_box_t *_ipma_box;
+
+       heif_retvm_if_failed(ipma_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ipma_box");
+
+       _ipma_box = g_new0(ipma_box_t, 1);
+       _ipma_box->associations = NULL;
+
+       *ipma_box = (ipma_box_t *)_ipma_box;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static void __ipma_box_destroy(ipma_box_t *ipma_box)
+{
+       ipma_box_t *_ipma_box = (ipma_box_t *)ipma_box;
+
+       heif_retm_if_failed(ipma_box);
+
+       g_slist_free_full(_ipma_box->associations, g_free);
+
+       g_free(ipma_box);
+}
+
+static int __ipma_box_parse(ipma_box_t *ipma_box, heif_source_h source, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       ipma_box_t *_ipma_box = (ipma_box_t *)ipma_box;
+       uint32_t entryCount = 0;
+       uint32_t item_id = 0;
+       size_t item_id_size = 0;
+       uint8_t associationCount = 0;
+       _association_t *assoct = NULL;
+       size_t propIndexSize = 0;
+       uint16_t propIndex = 0;
+       uint16_t bitmask = 0;
+       size_t k, i;
+
+       heif_retvm_if_failed(ipma_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid ipma_box");
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+
+       ret = _parse_full_box_header(&(_ipma_box->full_box), source, &offset, &size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("_parse_full_box_header fail %d", ret);
+               return ret;
+       }
+
+       if (size < 4) {
+               heif_error("size[%zu] is too small", size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       if (!heif_source_get_uint32(source, offset, &entryCount))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       offset += 4;
+       size -= 4;
+
+       for (k = 0; k < entryCount; ++k) {
+               item_id_size = (_ipma_box->full_box.version < 1) ? 2 : 4;
+
+               if (size < item_id_size + 1) {
+                       heif_error("size[%zu] is smaller than item_id_size[%zu]", size, item_id_size);
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+               }
+
+               if (!heif_source_get_uint32var(source, offset, &item_id, item_id_size))
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+               offset += item_id_size;
+               size -= item_id_size;
+
+               if (heif_source_read_at(source, offset, &associationCount, 1) != 1)
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+               offset++;
+               size--;
+
+               for (i = 0; i < associationCount; ++i) {
+                       propIndexSize = (_ipma_box->full_box.flags & 1) ? 2 : 1;
+
+                       if (size < propIndexSize) {
+                               heif_error("size[%zu] is smaller than propIndexSize[%zu]", size, propIndexSize);
+                               return LIBHEIF_ERROR_INVALID_PARAMETER;
+                       }
+
+                       if (!heif_source_get_uint16var(source, offset, &propIndex, propIndexSize))
+                               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+                       offset += propIndexSize;
+                       size -= propIndexSize;
+                       bitmask = (1 << (8 * propIndexSize - 1));
+
+                       assoct = g_new0(_association_t, 1);
+                       assoct->item_id = item_id;
+                       assoct->essential = !!(propIndex & bitmask);
+                       assoct->index = (uint16_t) (propIndex & ~bitmask);
+
+#ifdef __ENABLE_DEBUG_MODE
+                       heif_info("item id %u associated to property %u (essential %d)",
+                                       item_id, assoct->index, assoct->essential);
+#endif
+
+                       _ipma_box->associations = g_slist_append(_ipma_box->associations, assoct);
+               }
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
diff --git a/src/heif_box_iprp.h b/src/heif_box_iprp.h
new file mode 100644 (file)
index 0000000..c6a48d9
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_BOX_IPRP_H__
+#define __HEIF_BOX_IPRP_H__
+
+#include <stdint.h>
+
+#include "heif_source.h"
+
+typedef void * heif_box_iprp_h;
+typedef void * heif_ipco_box_h;
+typedef void * heif_ipma_box_h;
+
+void heif_box_iprp_info(heif_box_iprp_h iprp_box);
+int heif_box_iprp_create(heif_box_iprp_h *iprp_box);
+void heif_box_iprp_destroy(heif_box_iprp_h iprp_box);
+int heif_box_iprp_get_properties(heif_box_iprp_h iprp_box, GSList **properties);
+int heif_box_iprp_get_associations(heif_box_iprp_h iprp_box, GSList **associations);
+int heif_box_iprp_parse(heif_box_iprp_h iprp_box, heif_source_h source, off_t offset, size_t size);
+
+#endif /* __HEIF_BOX_IPRP_H__ */
diff --git a/src/heif_box_iref.c b/src/heif_box_iref.c
new file mode 100644 (file)
index 0000000..8f1dfe3
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <glib.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_box_util.h"
+#include "heif_box_iref.h"
+
+typedef struct {
+       _full_box_t full_box;
+       uint32_t ref_id_size;
+       GSList *item_refs;
+} iref_box_t;
+
+static int __parse_item_ref(_item_ref_t *item_ref, heif_source_h source, off_t offset, size_t size);
+static int __iref_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data);
+static gpointer __copy_item_refs(gconstpointer src, gpointer user_data);
+
+
+int heif_box_iref_create(heif_box_iref_h *iref_box)
+{
+       iref_box_t *_iref_box = NULL;
+
+       heif_retvm_if_failed(iref_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iref_box");
+
+       _iref_box = g_new0(iref_box_t, 1);
+
+       *iref_box = (heif_box_iref_h)_iref_box;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_box_iref_destroy(heif_box_iref_h iref_box)
+{
+       iref_box_t *_iref_box = (iref_box_t *)iref_box;
+
+       heif_retm_if_failed(iref_box);
+
+       g_slist_free_full(_iref_box->item_refs, _free_item_ref);
+
+       g_free(iref_box);
+}
+
+int heif_box_iref_get_item_refs(heif_box_iref_h iref_box, GSList **item_refs)
+{
+       iref_box_t *_iref_box = (iref_box_t *)iref_box;
+
+       heif_retvm_if_failed(iref_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iref_box");
+       heif_retvm_if_failed(item_refs, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_refs");
+
+       *item_refs = g_slist_copy_deep(_iref_box->item_refs, __copy_item_refs, NULL);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_box_iref_parse(heif_box_iref_h iref_box, heif_source_h source, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       iref_box_t *_iref_box = (iref_box_t *)iref_box;
+
+       heif_retvm_if_failed(iref_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid iref_box");
+
+       ret = _parse_full_box_header(&_iref_box->full_box, source, &offset, &size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("_parse_full_box_header fail %d", ret);
+               return ret;
+       }
+
+       _iref_box->ref_id_size = (_iref_box->full_box.version == 0) ? 2 : 4;
+
+       return _parse_box_chunks(source, offset, size, (_parse_chunk_cb)__iref_chunk_data_cb, _iref_box);
+}
+
+static void __print_ref_item_id(gpointer data, gpointer user_data)
+{
+       uint32_t ref_item_id = (uint32_t)GPOINTER_TO_UINT(data);
+
+       heif_retm_if_failed(data);
+
+       heif_info("[ITEM_REF] refs: %u", ref_item_id);
+}
+
+static void __print_item_ref(gpointer data, gpointer user_data)
+{
+       _item_ref_t *item_ref = (_item_ref_t *)data;
+       unsigned int *idx = (unsigned int *)user_data;
+
+       heif_retm_if_failed(data);
+       heif_retm_if_failed(user_data);
+
+       heif_info("[ITEM_REF/%u]", *idx);
+       heif_info("[ITEM_REF] item_type: %u", item_ref->item_type);
+       DEBUG_CHUNK_TYPE("ITEM_REF", item_ref->item_type);
+       heif_info("[ITEM_REF] item_id: %u", item_ref->item_id);
+       heif_info("[ITEM_REF] ref_size: %u", item_ref->ref_size);
+       g_slist_foreach(item_ref->refs, __print_ref_item_id, NULL);
+
+       (*idx)++;
+}
+
+void heif_box_iref_info(heif_box_iref_h iref_box)
+{
+       iref_box_t *_iref_box = (iref_box_t *)iref_box;
+       unsigned int idx = 0;
+
+       heif_retm_if_failed(iref_box);
+
+       heif_info("============ [IREF] Start ===========");
+       heif_info("[IREF] ref_size: %u", _iref_box->ref_id_size);
+       g_slist_foreach(_iref_box->item_refs, __print_item_ref, &idx);
+       heif_info("============ [IREF] End ===========\n");
+}
+
+static int __parse_item_ref(_item_ref_t *item_ref, heif_source_h source, off_t offset, size_t size)
+{
+       uint16_t count = 0;
+       uint32_t refItemId = 0;
+       size_t i = 0;
+
+       heif_retvm_if_failed(item_ref, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_ref");
+
+       if (size < item_ref->ref_size + 2) {
+               heif_error("size[%zu] is smaller than ref_size[%u]", size, item_ref->ref_size + 2);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+       if (!heif_source_get_uint32var(source, offset, &item_ref->item_id, item_ref->ref_size))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       offset += item_ref->ref_size;
+
+       if (!heif_source_get_uint16(source, offset, &count))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       offset += 2;
+       size -= (item_ref->ref_size + 2);
+
+       if (size < count * item_ref->ref_size) {
+               heif_error("size[%zu] is smaller than refs[%u]", size, count * item_ref->ref_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (!heif_source_get_uint32var(source, offset, &refItemId, item_ref->ref_size))
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+               offset += item_ref->ref_size;
+               item_ref->refs = g_slist_append(item_ref->refs, GUINT_TO_POINTER(refItemId));
+#ifdef __ENABLE_DEBUG_MODE
+               heif_info("item id %u: referencing item id %u", item_ref->item_id, refItemId);
+#endif
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __iref_chunk_data_cb(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       iref_box_t *iref_box = (iref_box_t *)user_data;
+       _item_ref_t *item_ref;
+
+       heif_retvm_if_failed(user_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid user_data");
+
+       item_ref = g_new0(_item_ref_t, 1);
+       item_ref->item_type = type;
+       item_ref->ref_size = iref_box->ref_id_size;
+
+       ret = __parse_item_ref(item_ref, source, offset, size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__parse_item_ref fail %d", ret);
+               g_free(item_ref);
+               return ret;
+       }
+
+       iref_box->item_refs = g_slist_append(iref_box->item_refs, item_ref);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static gpointer __copy_item_refs(gconstpointer src, gpointer user_data)
+{
+       _item_ref_t *src_ref = (_item_ref_t *)src;
+       _item_ref_t *copy_ref = NULL;
+
+       heif_retvm_if_failed(src, NULL, "invalid src");
+
+       copy_ref = g_new0(_item_ref_t, 1);
+       copy_ref->item_type = src_ref->item_type;
+       copy_ref->item_id = src_ref->item_id;
+       copy_ref->ref_size = src_ref->ref_size;
+       copy_ref->refs = g_slist_copy(src_ref->refs);
+
+       return copy_ref;
+}
diff --git a/src/heif_box_iref.h b/src/heif_box_iref.h
new file mode 100644 (file)
index 0000000..062eaa5
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_BOX_IREF_H__
+#define __HEIF_BOX_IREF_H__
+
+#include <stdint.h>
+#include "heif_source.h"
+
+typedef void * heif_box_iref_h;
+
+int heif_box_iref_create(heif_box_iref_h *iref_box);
+void heif_box_iref_destroy(heif_box_iref_h iref_box);
+int heif_box_iref_get_item_refs(heif_box_iref_h iref_box, GSList **item_refs);
+int heif_box_iref_parse(heif_box_iref_h iref_box, heif_source_h source, off_t offset, size_t size);
+void heif_box_iref_info(heif_box_iref_h iref_box);
+
+#endif /* __HEIF_BOX_IREF_H__ */
diff --git a/src/heif_box_pitm.c b/src/heif_box_pitm.c
new file mode 100644 (file)
index 0000000..b72ac1d
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <stdbool.h>
+#include <glib.h>
+#include <inttypes.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_box_util.h"
+#include "heif_box_pitm.h"
+
+typedef struct {
+       _full_box_t full_box;
+       uint32_t primary_item_id;
+} pitm_box_t;
+
+
+int heif_box_pitm_create(heif_box_pitm_h *pitm_box)
+{
+       pitm_box_t *_pitm_box = NULL;
+
+       heif_retvm_if_failed(pitm_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pitm_box");
+
+       _pitm_box = g_new0(pitm_box_t, 1);
+
+       *pitm_box = (heif_box_pitm_h)_pitm_box;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_box_pitm_destroy(heif_box_pitm_h pitm_box)
+{
+       heif_retm_if_failed(pitm_box);
+
+       g_free(pitm_box);
+}
+
+int heif_box_pitm_get_primary_id(heif_box_pitm_h pitm_box, uint32_t *primary_id)
+{
+       pitm_box_t *_pitm_box = (pitm_box_t *)pitm_box;
+
+       heif_retvm_if_failed(pitm_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pitm_box");
+       heif_retvm_if_failed(primary_id, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid primary_id");
+
+       // only remember the primary id of pitm box for later use
+       *primary_id = _pitm_box->primary_item_id;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_box_pitm_parse(heif_box_pitm_h pitm_box, heif_source_h source, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       pitm_box_t *_pitm_box = (pitm_box_t *)pitm_box;
+       size_t item_id_size = 0;
+       uint32_t item_id = 0;
+
+       heif_retvm_if_failed(pitm_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pitm_box");
+
+       ret = _parse_full_box_header(&(_pitm_box->full_box), source, &offset, &size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("_parse_full_box_header fail %d", ret);
+               return ret;
+       }
+
+       item_id_size = (_pitm_box->full_box.version == 0) ? 2 : 4;
+       if (size < item_id_size) {
+               heif_error("size[%zu] is smaller than item_id_size[%zu]", size, item_id_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       if (!heif_source_get_uint32var(source, offset, &item_id, item_id_size))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       heif_info("primary id %u", item_id);
+       _pitm_box->primary_item_id = item_id;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_box_pitm_info(heif_box_pitm_h pitm_box)
+{
+       heif_retm_if_failed(pitm_box);
+
+       heif_info("============ [PITM] Start ===========");
+       heif_info("[PITM] primary_item_id: %u", ((pitm_box_t *)pitm_box)->primary_item_id);
+       heif_info("============ [PITM] End ===========\n");
+}
diff --git a/src/heif_box_pitm.h b/src/heif_box_pitm.h
new file mode 100644 (file)
index 0000000..d3fae63
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_BOX_PITM_H__
+#define __HEIF_BOX_PITM_H__
+
+#include <stdint.h>
+
+#include "heif_source.h"
+
+typedef void * heif_box_pitm_h;
+
+int heif_box_pitm_create(heif_box_pitm_h *pitm_box);
+void heif_box_pitm_destroy(heif_box_pitm_h pitm_box);
+int heif_box_pitm_get_primary_id(heif_box_pitm_h pitm_box, uint32_t *primary_id);
+int heif_box_pitm_parse(heif_box_pitm_h pitm_box, heif_source_h source, off_t offset, size_t size);
+void heif_box_pitm_info(heif_box_pitm_h pitm_box);
+
+#endif /* __HEIF_BOX_PITM_H__ */
diff --git a/src/heif_box_util.c b/src/heif_box_util.c
new file mode 100644 (file)
index 0000000..fba6887
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <stdbool.h>
+#include <glib.h>
+#include <inttypes.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_box_util.h"
+
+
+#ifdef __ENABLE_DEBUG_MODE
+static void __type_to_fourcc_string(uint32_t type, char *str);
+#endif
+
+
+// XXX warning: these won't work on big-endian host.
+uint64_t ntoh64(uint64_t x)
+{
+       return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32);
+}
+
+uint64_t hton64(uint64_t x)
+{
+       return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32);
+}
+
+void _debug_chunk_type(const char *msg, uint32_t chunk_type)
+{
+#ifdef __ENABLE_DEBUG_MODE
+       char chunk[5];
+       __type_to_fourcc_string(chunk_type, chunk);
+       heif_secure_debug("[%s] chunk: %s", msg, chunk);
+#endif
+}
+
+void _debug_chunk_offset(const char *msg, uint32_t chunk_type, uint64_t chunk_size, off_t offset)
+{
+#ifdef __ENABLE_DEBUG_MODE
+       char chunk[5];
+       __type_to_fourcc_string(chunk_type, chunk);
+       heif_secure_debug("[%s] chunk: %s %"PRIu64" @ %jd", msg, chunk, chunk_size, offset);
+#endif
+}
+
+void _debug_chunk_info(const char *msg, uint32_t chunk_type, uint64_t chunk_size, off_t offset, int depth)
+{
+#ifdef __ENABLE_DEBUG_MODE
+       char chunk[5];
+       __type_to_fourcc_string(chunk_type, chunk);
+       heif_secure_debug("[%s] chunk: %s %"PRIu64" @ %jd, %d", msg, chunk, chunk_size, offset, depth);
+#endif
+}
+
+void _debug_full_box_info(_full_box_t *full_box, const char* parent)
+{
+#ifdef __ENABLE_DEBUG_MODE
+       heif_retm_if_failed(full_box);
+
+       heif_secure_debug("[%s] version: %u", parent, full_box->version);
+       heif_secure_debug("[%s] flags: %u", parent, full_box->flags);
+#endif
+}
+
+int _parse_hdr(heif_source_h source, off_t *offset, uint64_t *chunk_size, int32_t *chunk_type, off_t *data_offset)
+{
+       uint32_t hdr[2];
+
+       heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset");
+       heif_retvm_if_failed(chunk_size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid chunk_size");
+       heif_retvm_if_failed(chunk_type, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid chunk_type");
+       heif_retvm_if_failed(data_offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid data_offset");
+
+       if (heif_source_read_at(source, *offset, hdr, 8) < 8)
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       *chunk_size = ntohl(hdr[0]);
+       *chunk_type = ntohl(hdr[1]);
+       *data_offset = *offset + 8;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int _parse_full_box_header(_full_box_t *full_box, heif_source_h source, off_t *offset, size_t *size)
+{
+       heif_retvm_if_failed(full_box, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid full_box");
+       heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset");
+       heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size");
+
+       if (*size < 4) {
+               heif_error("size[%zu] is too small", *size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       if (heif_source_read_at(source, *offset, &full_box->version, 1) != 1)
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       if (!heif_source_get_uint24(source, *offset + 1, &full_box->flags))
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+       *offset += 4;
+       *size -= 4;
+
+#ifdef __ENABLE_DEBUG_MODE
+       heif_info("version: %u flag: %u", full_box->version, full_box->flags);
+#endif
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int _parse_box_chunk(heif_source_h source, off_t *offset, _parse_chunk_cb parse_chunk_cb, void *user_data)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       uint64_t chunk_size = 0;
+       int32_t chunk_type = 0;
+       off_t data_offset = 0;
+       off_t chunk_data_size = 0;
+
+       heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset");
+       heif_retvm_if_failed(parse_chunk_cb, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid parse_chunk_cb");
+
+       if (*offset < 0) {
+               heif_error("only continue parsing if the offset was advanced");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       ret = _parse_hdr(source, offset, &chunk_size, &chunk_type, &data_offset);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("_parse_hdr fail %d", ret);
+               return ret;
+       }
+
+       if (chunk_size == 1) {
+               if (heif_source_read_at(source, *offset + 8, &chunk_size, 8) < 8)
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+               chunk_size = ntoh64(chunk_size);
+               data_offset += 8;
+
+               if (chunk_size < 16) {
+                       // The smallest valid chunk is 16 bytes long in this case.
+                       heif_error("too short chunk size %"PRIu64, chunk_size);
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+               }
+       } else if (chunk_size == 0) {
+               // This shouldn't happen since we should never be top level
+               heif_error("invalid chunk size 0 for non-top level box");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       } else if (chunk_size < 8) {
+               // The smallest valid chunk is 8 bytes long.
+               heif_error("invalid chunk size: %"PRIu64, chunk_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       DEBUG_CHUNK_TYPE_AND_OFFSET("sub_box", chunk_type, chunk_size, data_offset);
+
+       chunk_data_size = chunk_size - (data_offset - *offset);
+       if (chunk_data_size < 0) {
+               heif_error("invalid chunk size: %jd", chunk_data_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       ret = parse_chunk_cb(source, chunk_type, data_offset, chunk_data_size, user_data);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("parse_chunk_cb fail %d", ret);
+               return ret;
+       }
+
+       *offset += chunk_size;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int _parse_box_chunks(heif_source_h source, off_t offset, size_t size, _parse_chunk_cb parse_chunk_cb, void *user_data)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       off_t stop_offset = offset + size;
+
+       while (offset < stop_offset) {
+               ret = _parse_box_chunk(source, &offset, parse_chunk_cb, user_data);
+               if (ret != LIBHEIF_ERROR_NONE) {
+                       heif_error("_parse_box_chunk fail %d", ret);
+                       return ret;
+               }
+       }
+
+       if (offset != stop_offset) {
+               heif_error("offset[%jd] did not match stop_offset[%jd]", offset, stop_offset);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+bool _parse_string(heif_source_h source, off_t *offset, size_t *size, char **out)
+{
+       char tmp;
+       off_t new_offset = 0;
+       off_t stop_offset = 0;
+       char buf[256];
+       unsigned int index = 0;
+
+       heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset");
+       heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size");
+       heif_retvm_if_failed(out, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid out");
+
+       new_offset = *offset;
+       stop_offset = *offset + *size;
+
+       while (new_offset < stop_offset) {
+               if (heif_source_read_at(source, new_offset++, &tmp, 1) != 1)
+                       return false;
+
+               buf[index++] = tmp;
+               if (tmp == 0) {
+                       *out = g_strdup(buf);
+
+                       *offset = new_offset;
+                       *size = stop_offset - new_offset;
+
+                       return true;
+               }
+       }
+       return false;
+}
+
+// if you don't want to parse string meta, just skip it.
+// this function will update source(cur_pos of reading), offset and size.
+bool _skip_string(heif_source_h source, off_t *offset, size_t *size, char *type_name)
+{
+       char tmp;
+       off_t new_offset = 0;
+       off_t stop_offset = 0;
+#ifdef __ENABLE_DEBUG_MODE
+       char buf[256];
+       unsigned int index = 0;
+#endif
+
+       heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset");
+       heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size");
+
+       new_offset = *offset;
+       stop_offset = *offset + *size;
+
+       while (new_offset < stop_offset) {
+               if (heif_source_read_at(source, new_offset++, &tmp, 1) != 1)
+                       return false;
+
+#ifdef __ENABLE_DEBUG_MODE
+               buf[index++] = tmp;
+#endif
+               if (tmp == 0) {
+#ifdef __ENABLE_DEBUG_MODE
+                       heif_info("skip [%s:%s]", type_name, buf);
+#endif
+                       *offset = new_offset;
+                       *size = stop_offset - new_offset;
+
+                       return true;
+               }
+       }
+       return false;
+}
+
+int _get_item_loc(_item_loc_t *item, off_t *offset, size_t *size, off_t idatOffset, size_t idatSize)
+{
+       _extent_entry_t *extent_item;
+
+       // not support fix extent handling and construction_method = 2
+       heif_retvm_if_failed(item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item");
+       heif_retvm_if_failed(g_slist_length(item->extents) == 1, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid extents %u", g_slist_length(item->extents));
+       heif_retvm_if_failed(offset, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset");
+       heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size");
+
+       extent_item = (_extent_entry_t *)item->extents->data;
+
+#ifdef __ENABLE_DEBUG_MODE
+       heif_info("base_offset: %jd extent_offset: %"PRIu64" extent_len: %"PRIu64,
+                               item->base_offset, extent_item->extent_offset, extent_item->extent_length);
+#endif
+
+       switch (item->construction_method) {
+       case 0:
+               *offset = item->base_offset + extent_item->extent_offset;
+               *size = extent_item->extent_length;
+               break;
+
+       case 1:
+               if (item->base_offset + extent_item->extent_offset + extent_item->extent_length
+                               > idatSize) {
+                       heif_error("the offset of location is too large, overflow");
+                       heif_error("base_offset: %jd extent_offset: %"PRIu64" extent_len: %"PRIu64" data_size: %zu",
+                                               item->base_offset, extent_item->extent_offset, extent_item->extent_length, idatSize);
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+               }
+               *offset = item->base_offset + extent_item->extent_offset + idatOffset;
+               *size = extent_item->extent_length;
+               break;
+
+       default:
+               heif_error("invalid construction_method %d", item->construction_method);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void _free_item_loc(gpointer data)
+{
+       heif_retm_if_failed(data);
+
+       g_slist_free_full(((_item_loc_t *)data)->extents, g_free);
+
+       g_free(data);
+}
+
+void _free_image_prop(gpointer data)
+{
+       _item_prop_t *image_prop = (_item_prop_t *)data;
+
+       heif_retm_if_failed(data);
+
+       switch (image_prop->type) {
+       case FOURCC('h', 'v', 'c', 'C'):
+               if (image_prop->property)
+                       g_free(((_hvcC_prop_t *)image_prop->property)->data);
+               break;
+
+       case FOURCC('c', 'o', 'l', 'r'):
+               if (image_prop->property)
+                       g_free(((_colr_prop_t *)image_prop->property)->icc_data);
+               break;
+
+       case FOURCC('i', 's', 'p', 'e'):
+               // fall through
+       case FOURCC('i', 'r', 'o', 't'):
+               // fall through
+       case FOURCC('i', 'm', 'i', 'r'):
+               // fall through
+       default:
+               break;
+       }
+
+       g_free(image_prop->property);
+       g_free(image_prop);
+}
+
+void _free_item_ref(gpointer data)
+{
+       heif_retm_if_failed(data);
+
+       g_slist_free(((_item_ref_t *)data)->refs);
+
+       g_free(data);
+}
+
+#ifdef __ENABLE_DEBUG_MODE
+static void __type_to_fourcc_string(uint32_t type, char *str)
+{
+       heif_retm_if_failed(str);
+
+       str[0] = type >> 24;
+       str[1] = (type >> 16) & 0xff;
+       str[2] = (type >> 8) & 0xff;
+       str[3] = type & 0xff;
+       str[4] = '\0';
+}
+#endif
diff --git a/src/heif_box_util.h b/src/heif_box_util.h
new file mode 100644 (file)
index 0000000..cf8d8d8
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_BOX_UTIL_H__
+#define __HEIF_BOX_UTIL_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+
+#include "heif_source.h"
+
+// utils for all
+#define FOURCC(a, b, c, d) ((a << 24) + ((b) << 16) + ((c) << 8) + ((d)))
+#define DEBUG_CHUNK_TYPE(m, t)                         _debug_chunk_type(m, t);
+#define DEBUG_CHUNK_TYPE_AND_OFFSET(m, t, s, o)        _debug_chunk_offset(m, t, s, o);
+#define DEBUG_CHUNK_ALL(m, t, s, o, d) _debug_chunk_info(m, t, s, o, d);
+#define DEBUG_FULL_BOX(b, p)                   _debug_full_box_info(b, p);
+
+// base container of container boxes
+// some container boxes has full box to behave differently for version & flag
+// Full box
+typedef struct {
+       uint8_t version;
+       uint32_t flags;
+} _full_box_t;
+
+// IINF container (META > IINF)
+// IINF item information
+// item_id: the identity value for each item (e.g. 1, 2, 3, 1001, 1002..)
+// item_type: the type of item (e.g. hvc1, grid, Exif)
+// hidden: determined by item_type
+typedef struct {
+       uint32_t item_id;
+       uint32_t item_type;
+       bool hidden;
+} _item_info_t;
+
+// extent entry of ILOC
+typedef struct {
+       uint64_t extent_index;
+       uint64_t extent_offset;
+       uint64_t extent_length;
+} _extent_entry_t;
+
+// ILOC container (META > ILOC)
+// ILOC item location
+// item_id: the item id at the location in source
+// construction_method: not supported yet
+// data_reference_index: not supported yet
+// base_offset: the base offset in source, generally 0
+// extents: the extents offset and size of item
+typedef struct {
+       uint32_t item_id;
+       uint16_t construction_method;
+       uint16_t data_reference_index;
+       off_t base_offset;
+       GSList *extents;
+} _item_loc_t;
+
+// HVCC container (META > IPRP > IPCO > HVCC)
+// data: the hevc configuration include nal
+// size: the size of hevc configuration
+typedef struct {
+       uint8_t *data;
+       size_t size;
+} _hvcC_prop_t;
+
+// ISPE container (META > IPRP > IPCO > ISPE)
+// width: the width of item
+// height: the height of item
+typedef struct {
+       _full_box_t full_box;
+       uint32_t width;
+       uint32_t height;
+} _ispe_prop_t;
+
+// IROT container (META > IPRP > IPCO > IROT)
+// angle: the orientation of item (e.g. 90, 180, 270)
+// not support the flip(mirror) value, it exist at different container box
+typedef struct {
+       uint8_t angle;
+} _irot_prop_t;
+
+// COLR container (META > IPRP > IPCO > COLR)
+// icc_data: the ICC profile for display, generally not used on hands-on device
+// size: the size of ICC profile
+typedef struct {
+       uint8_t *icc_data;
+       size_t size;
+} _colr_prop_t;
+
+// IMIR container (META > IPRP > IPCO > IMIR)
+// angle: the axis for mirroring operation of item
+// vertial(axis = 0) or horizontal(axis = 1)
+// if image has not mirroring operation, 'imir' property not exist.
+typedef struct {
+       uint8_t axis;
+} _imir_prop_t;
+
+// IPRP container (META > IPRP)
+// type: the chunk type of property (e.g. hvcC, ispe, irot...)
+// please see __attach_property() function
+typedef struct {
+       uint32_t type;
+       void *property;
+} _item_prop_t;
+
+// IPMA container (META > IPRP > IPMA)
+// item_id: the item id
+// essential: the value whether the app should handle it or not
+// index: the index of property
+// please see __attach_property() function
+typedef struct {
+       uint32_t item_id;
+       bool essential;
+       uint16_t index;
+} _association_t;
+
+// IREF container (META > IREF)
+// item_type: the type of item (e.g. dimg, thmb, cdsc)
+// item_id: the item id referencing the references
+// ref_size: the count of reference id
+// refs: the reference ids list
+typedef struct {
+       uint32_t item_type;
+       uint32_t item_id;
+       uint32_t ref_size;
+       GSList *refs;
+} _item_ref_t;
+
+typedef int (*_parse_chunk_cb)(heif_source_h source, uint32_t type, off_t offset, size_t size, void *user_data);
+
+// utility functions for reading 64-bit
+uint64_t ntoh64(uint64_t x);
+uint64_t hton64(uint64_t x);
+
+// utility debug function
+void _debug_chunk_type(const char *msg, uint32_t chunk_type);
+void _debug_chunk_offset(const char *msg, uint32_t chunk_type, uint64_t chunk_size, off_t offset);
+void _debug_chunk_info(const char *msg, uint32_t chunk_type, uint64_t chunk_size, off_t offset, int depth);
+void _debug_full_box_info(_full_box_t *full_box, const char* parent);
+
+// utility functions
+int _parse_hdr(heif_source_h source, off_t *offset, uint64_t *chunk_size, int32_t *chunk_type, off_t *data_offset);
+int _parse_full_box_header(_full_box_t *full_box, heif_source_h source, off_t *offset, size_t *size);
+int _parse_box_chunk(heif_source_h source, off_t *offset, _parse_chunk_cb parse_chunk_cb, void *user_data);
+int _parse_box_chunks(heif_source_h source, off_t offset, size_t size, _parse_chunk_cb parse_chunk_cb, void *user_data);
+bool _parse_string(heif_source_h source, off_t *offset, size_t *size, char **out);
+bool _skip_string(heif_source_h source, off_t *offset, size_t *size, char *type_name);
+int _get_item_loc(_item_loc_t *item, off_t *offset, size_t *size, off_t idatOffset, size_t idatSize);
+
+// release memory
+void _free_item_loc(gpointer data);
+void _free_image_prop(gpointer data);
+void _free_item_ref(gpointer data);
+
+#endif /* __HEIF_BOX_UTIL_H__ */
diff --git a/src/heif_decode_ffmpeg.c b/src/heif_decode_ffmpeg.c
new file mode 100644 (file)
index 0000000..1602f2e
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <glib.h>
+
+#include <libavcodec/avcodec.h>
+#include <libavutil/frame.h>
+#include <libavutil/imgutils.h>
+#include <libavutil/pixfmt.h>
+#include <libswscale/swscale.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_decode_ffmpeg.h"
+
+
+// decoder of hevc codec
+typedef struct {
+       AVCodecContext *ctx;
+       AVCodec *codec;
+       AVPacket pkt;
+} _ffmpeg_decoder_t;
+
+
+static int __ffmpeg_create_decoder(plugin_decode_h *decoder);
+static void __ffmpeg_destroy_decoder(plugin_decode_h decoder);
+static int __ffmpeg_decode_image(plugin_decode_h decoder, heif_buffer_t *media_data, heif_color_format_e colr_fmt, heif_image_t **decoded_image);
+
+
+void register_ffmpeg_decoder(plugin_decode_h *decoder)
+{
+       plugin_decode_t *new_module;
+
+       heif_retm_if_failed(decoder);
+
+       new_module = g_new0(plugin_decode_t, 1);
+
+       new_module->name = g_strdup("ffmpeg");
+       new_module->priority = HEIF_PRIOR_MID;
+       new_module->init = (init_decoder)__ffmpeg_create_decoder;
+       new_module->deinit = (deinit_decoder)__ffmpeg_destroy_decoder;
+       new_module->decode = (do_decode)__ffmpeg_decode_image;
+
+       new_module->supported_codec = g_slist_append(new_module->supported_codec, GINT_TO_POINTER(HEIF_CODEC_HEVC));
+
+       *decoder = (plugin_decode_h)new_module;
+}
+
+static int __ffmpeg_get_image_from_frame(AVFrame *frame, heif_color_format_e colr_fmt, heif_image_t **libav_output)
+{
+       struct SwsContext *img_convert_ctx = NULL;
+
+       uint8_t *dst_data[4];
+       int dst_linesize[4];
+       int pix_fmt = AV_PIX_FMT_NONE;
+       heif_image_t *output;
+
+       heif_retvm_if_failed(frame, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid frame");
+       heif_retvm_if_failed(libav_output, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid libav_output");
+
+       switch (colr_fmt) {
+       case HEIF_COLOR_FORMAT_YUV420P:
+               pix_fmt = AV_PIX_FMT_YUV420P;
+               break;
+
+       case HEIF_COLOR_FORMAT_RGB24:
+               pix_fmt = AV_PIX_FMT_RGB24;
+               break;
+
+       case HEIF_COLOR_FORMAT_ARGB:
+               pix_fmt = AV_PIX_FMT_ARGB;
+               break;
+
+       case HEIF_COLOR_FORMAT_BGRA:
+               pix_fmt = AV_PIX_FMT_BGRA;
+               break;
+
+       case HEIF_COLOR_FORMAT_RGBA:
+               pix_fmt = AV_PIX_FMT_RGBA;
+               break;
+
+       default:
+               heif_error("invalid colr_fmt %d", colr_fmt);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       heif_secure_debug("frame_format: %d, color_format: %d, pix_fmt: %d", frame->format, colr_fmt, pix_fmt);
+
+       output = g_new0(heif_image_t, 1);
+
+       output->size = (size_t)av_image_get_buffer_size(pix_fmt, frame->width, frame->height, 1);
+       if (output->size <= 0) {
+               heif_error("av_image_get_buffer_size fail [%d x %d]", frame->width, frame->height);
+               goto FAIL;
+       }
+
+       output->width = (unsigned int)frame->width;
+       output->height = (unsigned int)frame->height;
+       output->format = colr_fmt;
+       output->data = av_malloc(output->size);
+       if (!output->data) {
+               heif_error("av_malloc fail");
+               goto FAIL;
+       }
+
+       if (av_image_fill_arrays(dst_data, dst_linesize, output->data, pix_fmt, frame->width, frame->height, 1) < 0) {
+               heif_error("avpicture_fill fail");
+               goto FAIL;
+       }
+
+       img_convert_ctx = sws_getContext(frame->width, frame->height, frame->format, frame->width, frame->height, pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
+
+       if (NULL == img_convert_ctx) {
+               heif_error("sws_getContext fail");
+               goto FAIL;
+       }
+
+       if (sws_scale(img_convert_ctx, (const uint8_t * const *)frame->data, frame->linesize, 0, frame->height, dst_data, dst_linesize) < 0) {
+               heif_error("sws_scale to convert color format fail");
+               sws_freeContext(img_convert_ctx);
+               goto FAIL;
+       }
+
+       sws_freeContext(img_convert_ctx);
+
+       *libav_output = output;
+
+       return LIBHEIF_ERROR_NONE;
+
+FAIL:
+       av_freep(&output->data);
+       g_free(output);
+
+       return LIBHEIF_ERROR_INVALID_OPERATION;
+}
+
+static int __ffmpeg_send_media(_ffmpeg_decoder_t *decoder, heif_buffer_t *media_data)
+{
+       uint8_t *pkt_buf;
+
+       heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder");
+       heif_retvm_if_failed(media_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid media_data");
+       heif_retvm_if_failed(media_data->data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid media_data->data");
+       heif_retvm_if_failed(media_data->size > 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid media_data->size");
+
+       av_packet_unref(&(decoder->pkt));
+
+       av_init_packet(&(decoder->pkt));
+
+       // this buffer should be released by av_packet_unref
+       pkt_buf = g_memdup(media_data->data, media_data->size);
+
+       if (av_packet_from_data(&decoder->pkt, pkt_buf, media_data->size) < 0) {
+               heif_error("av_packet_from_data fail");
+               g_free(pkt_buf);
+               return LIBHEIF_ERROR_INVALID_OPERATION;
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __ffmpeg_get_decoder(int codec_id, _ffmpeg_decoder_t *decoder)
+{
+       heif_debug_fenter();
+
+       heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder");
+
+       if (decoder->ctx) {
+               avcodec_close(decoder->ctx);
+               av_freep(&decoder->ctx);
+       }
+
+       decoder->codec = avcodec_find_decoder(codec_id);
+       if (!decoder->codec) {
+               heif_error("Can't find decoder for %d codec.", codec_id);
+               return LIBHEIF_ERROR_INVALID_OPERATION;
+       }
+
+       decoder->ctx = avcodec_alloc_context3(decoder->codec);
+       if (!decoder->ctx) {
+               heif_error("Can't allocate context for '%s' codec.", decoder->codec->name);
+               return LIBHEIF_ERROR_INVALID_OPERATION;
+       }
+
+       decoder->ctx->codec = decoder->codec;
+       decoder->ctx->codec_id = codec_id;
+
+       if (avcodec_open2(decoder->ctx, decoder->codec, NULL) < 0) {
+               heif_error("Can't open context for '%s' codec.", decoder->codec->name);
+               av_freep(&decoder->ctx);
+               decoder->ctx = NULL;
+               return LIBHEIF_ERROR_INVALID_OPERATION;
+       }
+
+       // hevc decoder doesn't support any other fmt.
+       decoder->ctx->pix_fmt = AV_PIX_FMT_YUVJ420P;
+       decoder->ctx->thread_count = 1; // sw decoding should set '1' because ffmpeg is thread-unsafety
+
+       heif_debug_fleave();
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __ffmpeg_create_decoder(plugin_decode_h *decoder)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       _ffmpeg_decoder_t *new_decoder;
+
+       heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder");
+
+       heif_debug_fenter();
+
+       new_decoder = g_new0(_ffmpeg_decoder_t, 1);
+
+#ifdef __ENABLE_DEBUG_MODE
+       av_log_set_level(AV_LOG_TRACE);
+#else
+       av_log_set_level(AV_LOG_WARNING);
+#endif
+
+       // set codec and get decoder
+       ret = __ffmpeg_get_decoder(AV_CODEC_ID_HEVC, new_decoder);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__ffmpeg_set_codec fail (%d)", ret);
+               goto FAIL;
+       }
+
+       *decoder = (plugin_decode_h)new_decoder;
+
+       heif_debug_fleave();
+
+       return LIBHEIF_ERROR_NONE;
+
+FAIL:
+       __ffmpeg_destroy_decoder(new_decoder);
+
+       return LIBHEIF_ERROR_NOT_SUPPORTED;
+}
+
+static void __ffmpeg_destroy_decoder(plugin_decode_h decoder)
+{
+       _ffmpeg_decoder_t *_decoder = (_ffmpeg_decoder_t *)decoder;
+
+       heif_debug_fenter();
+
+       heif_retm_if_failed(decoder);
+
+       av_packet_unref(&_decoder->pkt);
+       if (_decoder->ctx) {
+               avcodec_close(_decoder->ctx);
+               av_freep(&_decoder->ctx);
+       }
+       g_free(_decoder);
+
+       heif_debug_fleave();
+}
+
+static int __ffmpeg_decode_image(plugin_decode_h decoder, heif_buffer_t *media_data, heif_color_format_e colr_fmt, heif_image_t **decoded_image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       AVFrame *frame = NULL;
+       _ffmpeg_decoder_t *_decoder = (_ffmpeg_decoder_t *)decoder;
+
+       heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder");
+
+       heif_debug_fenter();
+
+       ret = __ffmpeg_send_media(_decoder, media_data);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__ffmpeg_send_media fail (%d)", ret);
+               return LIBHEIF_ERROR_INVALID_OPERATION;
+       }
+
+       avcodec_flush_buffers(_decoder->ctx);
+
+       frame = av_frame_alloc();
+       if (!frame) {
+               heif_error("av_frame_alloc fail");
+               ret = LIBHEIF_ERROR_INVALID_OPERATION;
+               goto END;
+       }
+
+       // decode
+       if (avcodec_send_packet(_decoder->ctx, &_decoder->pkt) < 0) {
+               heif_error("avcodec_send_packet fail");
+               ret = LIBHEIF_ERROR_INVALID_OPERATION;
+               goto END;
+       }
+
+       if (avcodec_receive_frame(_decoder->ctx, frame) < 0) {
+               heif_error("avcodec_receive_frame fail");
+               ret = LIBHEIF_ERROR_INVALID_OPERATION;
+               goto END;
+       }
+
+       ret = __ffmpeg_get_image_from_frame(frame, colr_fmt, decoded_image);
+       if (ret != LIBHEIF_ERROR_NONE)
+               heif_error("__ffmpeg_get_image_from_frame fail (%d)", ret);
+
+END:
+       av_frame_free(&frame);
+
+       heif_debug_fleave();
+
+       return ret;
+}
diff --git a/src/heif_decode_ffmpeg.h b/src/heif_decode_ffmpeg.h
new file mode 100644 (file)
index 0000000..3c40310
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_DECODE_FFMPEG_H__
+#define __HEIF_DECODE_FFMPEG_H__
+
+#include "heif_decode_plugin.h"
+
+void register_ffmpeg_decoder(plugin_decode_h *decoder);
+
+
+#endif /* __HEIF_DECODE_FFMPEG_H__ */
diff --git a/src/heif_decode_plugin.c b/src/heif_decode_plugin.c
new file mode 100644 (file)
index 0000000..4dcbffd
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <glib.h>
+
+#include "heif_debug.h"
+
+#include "heif_decode_plugin.h"
+#include "heif_decode_ffmpeg.h"
+
+static gint __compare_name(gconstpointer data, gconstpointer user_data);
+static gint __compare_codec(gconstpointer data, gconstpointer user_data);
+static gint __compare_supported_codec(gconstpointer data, gconstpointer user_data);
+static gint __compare_priority(gconstpointer a, gconstpointer b);
+static void __init_decoder(gpointer data, gpointer user_data);
+static void __unregister_decoder(gpointer data);
+
+
+void load_decode_modules(decode_module_h *decode_modules)
+{
+       GSList *new_decode_modules = NULL;
+       plugin_decode_h module = NULL;
+
+       heif_retm_if_failed(decode_modules);
+
+       register_ffmpeg_decoder(&module);
+       new_decode_modules = g_slist_insert_sorted(new_decode_modules, module, __compare_priority);
+
+       /*
+        * register_XXX_decoder(&XXXmodule);
+        * new_decode_modules = g_slist_insert_sorted(new_decode_modules, XXXmodule, __compare_priority);
+        */
+
+       g_slist_foreach(new_decode_modules, __init_decoder, NULL);
+
+       *decode_modules = (decode_module_h)new_decode_modules;
+}
+
+void foreach_supported_module(decode_module_h decode_modules, heif_codec_type_e codec, foreach_supported_decoder_cb callback, void *user_data)
+{
+       GSList *find_module = NULL, *iter = NULL;
+
+       find_module = g_slist_find_custom((GSList *)decode_modules, GINT_TO_POINTER(codec), __compare_supported_codec);
+
+       for (iter = find_module; iter; iter = g_slist_next(iter)) {
+               if (!callback((plugin_decode_t *)iter->data, user_data))
+                       break;
+       }
+}
+
+int get_decoder_by_name(decode_module_h decode_modules, char *name, plugin_decode_t **decoder)
+{
+       GSList *find_module = NULL;
+
+       heif_retvm_if_failed(decode_modules, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decode_modules");
+       heif_retvm_if_failed(name, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid name");
+       heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder");
+
+       find_module = g_slist_find_custom((GSList *)decode_modules, name, __compare_name);
+
+       heif_retvm_if_failed(find_module, LIBHEIF_ERROR_INVALID_PARAMETER, "not found %s", name);
+
+       *decoder = (plugin_decode_t *)find_module->data;
+
+       heif_debug("found %s decoder", (*decoder)->name);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int get_decoder_by_codec(decode_module_h decode_modules, heif_codec_type_e codec, plugin_decode_t **decoder)
+{
+       GSList *find_module = NULL;
+
+       heif_retvm_if_failed(decode_modules, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decode_modules");
+       heif_retvm_if_failed(codec > 0 && codec < HEIF_CODEC_MAX, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid codec %d", codec);
+       heif_retvm_if_failed(decoder, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid decoder");
+
+       find_module = g_slist_find_custom((GSList *)decode_modules, GINT_TO_POINTER(codec), __compare_supported_codec);
+
+       heif_retvm_if_failed(find_module, LIBHEIF_ERROR_INVALID_PARAMETER, "not found %d", codec);
+
+       *decoder = (plugin_decode_t *)find_module->data;
+
+       heif_debug("found %s decoder", (*decoder)->name);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void unload_decode_modules(decode_module_h decode_modules)
+{
+       g_slist_free_full((GSList *)decode_modules, __unregister_decoder);
+}
+
+static gint __compare_name(gconstpointer data, gconstpointer user_data)
+{
+       plugin_decode_t *decoder = (plugin_decode_t *)data;
+       char *get_name = (char *)user_data;
+
+       heif_retvm_if_failed(data, 1, "invalid data");
+       heif_retvm_if_failed(decoder->initialized, 1, "invalid decoder");
+
+       return g_strcmp0(decoder->name, get_name);
+}
+
+static gint __compare_codec(gconstpointer data, gconstpointer user_data)
+{
+       heif_codec_type_e codec = GPOINTER_TO_INT(data);
+       heif_codec_type_e get_codec = GPOINTER_TO_INT(user_data);
+
+       heif_retvm_if_failed(data, 1, "invalid data");
+       heif_retvm_if_failed(user_data, -1, "invalid user_data");
+
+       return ((codec == get_codec) ? 0 : 1);
+}
+
+static gint __compare_supported_codec(gconstpointer data, gconstpointer user_data)
+{
+       plugin_decode_t *decoder = (plugin_decode_t *)data;
+       heif_codec_type_e get_codec = GPOINTER_TO_INT(user_data);
+
+       heif_retvm_if_failed(data, 1, "invalid data");
+       heif_retvm_if_failed(user_data, -1, "invalid user_data");
+       heif_retvm_if_failed(decoder->initialized, 1, "invalid decoder");
+
+       return (g_slist_find_custom(decoder->supported_codec, GINT_TO_POINTER(get_codec), __compare_codec) != NULL) ? 0 : 1;
+}
+
+static gint __compare_priority(gconstpointer a, gconstpointer b)
+{
+       plugin_decode_t *decoder_a = (plugin_decode_t *)a;
+       plugin_decode_t *decoder_b = (plugin_decode_t *)b;
+
+       heif_retvm_if_failed(a, -1, "invalid a");
+       heif_retvm_if_failed(b, 1, "invalid b");
+
+       if (decoder_a->priority < decoder_b->priority)
+               return -1;
+       else if (decoder_a->priority > decoder_b->priority)
+               return 1;
+
+       return 0;
+}
+
+static void __init_decoder(gpointer data, gpointer user_data)
+{
+       plugin_decode_t *decoder = (plugin_decode_t *)data;
+
+       heif_retm_if_failed(data);
+
+       if (decoder->init)
+               decoder->initialized = (decoder->init(&decoder->handle) == 0) ? true : false;
+
+       heif_info("decoder: %s was %s", decoder->name, decoder->initialized ? "initialized" : "not initialized");
+}
+
+static void __unregister_decoder(gpointer data)
+{
+       plugin_decode_t *decoder = (plugin_decode_t *)data;
+
+       heif_retm_if_failed(data);
+
+       if (decoder->deinit)
+               decoder->deinit(decoder->handle);
+
+       g_slist_free(decoder->supported_codec);
+       g_free(decoder->name);
+       g_free(decoder);
+}
diff --git a/src/heif_decode_plugin.h b/src/heif_decode_plugin.h
new file mode 100644 (file)
index 0000000..6cec089
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 __HEIF_DECODE_PLUGIN_H__
+#define __HEIF_DECODE_PLUGIN_H__
+
+#include "heif_type.h"
+#include "heif_itemtable.h"
+
+typedef enum {
+       HEIF_CODEC_HEVC,
+       HEIF_CODEC_MAX,
+} heif_codec_type_e;
+
+typedef enum {
+       HEIF_PRIOR_HIGH,
+       HEIF_PRIOR_MID,
+       HEIF_PRIOR_LOW,
+} heif_priority_e;
+
+// output of decoding heif
+typedef struct {
+       unsigned int width;
+       unsigned int height;
+       heif_color_format_e format;
+       unsigned char *data;
+       size_t size;
+} heif_image_t;
+
+typedef void *plugin_decode_h;
+
+typedef int (*init_decoder)(plugin_decode_h *decoder);
+typedef void (*deinit_decoder)(plugin_decode_h decoder);
+typedef int (*do_decode)(plugin_decode_h decoder, heif_buffer_t *media_data, heif_color_format_e colr_fmt, heif_image_t **decoded_image);
+
+typedef struct {
+       bool initialized;
+       char *name;
+       int priority;   // not used yet
+       plugin_decode_h handle;
+       init_decoder init;
+       deinit_decoder deinit;
+       do_decode decode;
+       GSList *supported_codec;        // not used yet
+} plugin_decode_t;
+
+typedef void *decode_module_h;
+
+typedef bool (*foreach_supported_decoder_cb)(plugin_decode_t *decoder, void *user_data);
+
+void load_decode_modules(decode_module_h *decode_modules);
+void foreach_supported_module(decode_module_h decode_modules, heif_codec_type_e codec, foreach_supported_decoder_cb callback, void *user_data);
+int get_decoder_by_name(decode_module_h decode_modules, char *name, plugin_decode_t **decoder);
+int get_decoder_by_codec(decode_module_h decode_modules, heif_codec_type_e codec, plugin_decode_t **decoder);
+void unload_decode_modules(decode_module_h decode_modules);
+
+#endif /* __HEIF_DECODE_PLUGIN_H__ */
diff --git a/src/heif_decoder.c b/src/heif_decoder.c
new file mode 100644 (file)
index 0000000..1ba5684
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <glib.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_decoder.h"
+#include "heif_decode_plugin.h"
+
+
+static bool __is_valid_heif_image(heif_image_t *image);
+static int __decode_single_image(heif_itemtable_h item_table, heif_color_format_e format, heif_image_item_h image_item, heif_image_t **image);
+static int __create_output_image(heif_image_item_h image_item, heif_color_format_e color_format, heif_image_t **output_image);
+static int __combine_rgb(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image);
+static int __combine_yuv(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image);
+static int __combine_tile_image(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image);
+static int __decode_grid_image(heif_itemtable_h item_table, heif_color_format_e format, heif_image_item_h image_item, heif_image_t **image);
+
+
+int heif_decoder_decode_primary(heif_itemtable_h item_table, heif_color_format_e format, heif_image_h *image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       unsigned int primary_id = 0;
+       heif_image_item_h primary_item;
+
+       heif_debug_fenter();
+
+       ret = heif_itemtable_get_primary_image(item_table, &primary_id);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_itemtable_get_primary_image fail (%d)", ret);
+               goto END;
+       }
+
+       ret = heif_itemtable_get_image_item(item_table, primary_id, &primary_item);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_itemtable_get_image_item fail (%d)", ret);
+               goto END;
+       }
+
+       if (heif_itemtable_has_grid(item_table)) {
+               heif_info("has grid!");
+               ret = __decode_grid_image(item_table, format, primary_item, (heif_image_t **)image);
+               if (ret != LIBHEIF_ERROR_NONE)
+                       heif_error("__decode_grid_image fail (%d)", ret);
+       } else {
+               heif_info("single!");
+               ret = __decode_single_image(item_table, format, primary_item, (heif_image_t **)image);
+               if (ret != LIBHEIF_ERROR_NONE)
+                       heif_error("__decode_single_image fail (%d)", ret);
+       }
+
+END:
+       heif_debug_fleave();
+
+       return ret;
+}
+
+int heif_decoder_decode_thumbnail(heif_itemtable_h item_table, heif_color_format_e format, heif_image_h *image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_image_item_h thumb_item;
+
+       heif_debug_fenter();
+
+       ret = heif_itemtable_get_thumb_item(item_table, &thumb_item);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_itemtable_get_thumb_item fail (%d)", ret);
+               goto END;
+       }
+
+       ret = __decode_single_image(item_table, format, thumb_item, (heif_image_t **)image);
+       if (ret != LIBHEIF_ERROR_NONE)
+               heif_error("__decode_single_image fail (%d)", ret);
+
+END:
+       heif_debug_fleave();
+
+       return ret;
+}
+
+int heif_decoder_get_image(heif_image_h image, unsigned int *width, unsigned int *height, heif_color_format_e *format, unsigned char **data, size_t *size)
+{
+       heif_image_t *_image = (heif_image_t *)image;
+
+       heif_retvm_if_failed(image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image");
+
+       heif_info("width: %u, height: %u, format: %u, data: %p, size: %zu", _image->width, _image->height, _image->format, _image->data, _image->size);
+
+       if (width)
+               *width = _image->width;
+       if (height)
+               *height = _image->height;
+       if (format)
+               *format = _image->format;
+       if (data)
+               *data = _image->data;
+       if (size)
+               *size = _image->size;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+void heif_decoder_destroy_image(heif_image_h image)
+{
+       heif_retm_if_failed(image);
+
+       g_free(((heif_image_t *)image)->data);
+       g_free(image);
+}
+
+static bool __is_valid_heif_image(heif_image_t *image)
+{
+       heif_retvm_if_failed(image, false, "invalid image");
+       heif_retvm_if_failed(image->width > 0, false, "invalid width");
+       heif_retvm_if_failed(image->height > 0, false, "invalid height");
+       heif_retvm_if_failed(image->format > HEIF_COLOR_FORMAT_NONE && image->format < HEIF_COLOR_FORMAT_MAX, false, "invalid format %d", image->format);
+       heif_retvm_if_failed(image->data, false, "invalid data");
+       heif_retvm_if_failed(image->size > 0, false, "invalid size");
+
+       return true;
+}
+
+static int __decode_single_image(heif_itemtable_h item_table, heif_color_format_e format, heif_image_item_h image_item, heif_image_t **image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       plugin_decode_t *ffmpeg_decoder = NULL;
+       heif_image_t *decoded_image = NULL;
+       heif_buffer_t *coded_data = NULL;
+       decode_module_h decode_modules;
+
+       heif_retvm_if_failed(image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image");
+
+       ret = heif_itemtable_get_coded_data(item_table, image_item, &coded_data);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_itemtable_get_coded_data fail (%d)", ret);
+               return ret;
+       }
+
+       //ret = get_decoder_by_codec(decoder->decode_modules, HEIF_CODEC_HEVC, &hevc_decoder)
+       load_decode_modules(&decode_modules);
+       ret = get_decoder_by_name(decode_modules, "ffmpeg", &ffmpeg_decoder);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("get_decoder_by_name fail (%d)", ret);
+               goto END;
+       }
+
+       ret = ffmpeg_decoder->decode(ffmpeg_decoder->handle, coded_data, format, &decoded_image);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("decode fail (%d)", ret);
+               goto END;
+       }
+
+       *image = decoded_image;
+
+END:
+       heif_buffer_destroy(coded_data);
+       unload_decode_modules(decode_modules);
+
+       return ret;
+}
+
+static size_t __get_image_data_size(unsigned int width, unsigned int height, heif_color_format_e color_format)
+{
+       size_t data_size = 0;
+       size_t y_size = 0, u_size = 0, v_size = 0;
+
+       heif_retvm_if_failed(width > 0, 0, "invalid width");
+       heif_retvm_if_failed(height > 0, 0, "invalid height");
+
+       switch (color_format) {
+       case HEIF_COLOR_FORMAT_RGB24:
+               data_size = width * height * 3;
+               break;
+
+       case HEIF_COLOR_FORMAT_ARGB:
+               // fall through
+       case HEIF_COLOR_FORMAT_BGRA:
+               // fall through
+       case HEIF_COLOR_FORMAT_RGBA:
+               data_size = width * height * 4;
+               break;
+
+       case HEIF_COLOR_FORMAT_YUV420P:
+               y_size = width * height;
+               u_size = width / 2 * height / 2;
+               v_size = width / 2 * height / 2;
+               heif_info("y_size: %zu, u_size: %zu, v_size: %zu", y_size, u_size, v_size);
+               data_size = y_size + u_size + v_size;
+               break;
+
+       default:
+               heif_error("invalid color_format (%d)", color_format);
+               return 0;
+       }
+
+       heif_info("data_size: %zu, width: %u, height: %u, format: %u", data_size, width, height, color_format);
+
+       return data_size;
+}
+
+static int __create_output_image(heif_image_item_h image_item, heif_color_format_e color_format, heif_image_t **output_image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       unsigned int width = 0, height = 0;
+       heif_image_t *image = NULL;
+       size_t data_size = 0;
+
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid heif_decoder");
+       heif_retvm_if_failed(color_format > HEIF_COLOR_FORMAT_NONE && color_format < HEIF_COLOR_FORMAT_MAX, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid color_format");
+       heif_retvm_if_failed(output_image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid output_image");
+
+       ret = heif_itemtable_get_image_resolution(image_item, &width, &height);
+       heif_retvm_if_failed(ret == LIBHEIF_ERROR_NONE, ret, "heif_itemtable_get_image_resolution fail (%d)", ret);
+
+       data_size = __get_image_data_size(width, height, color_format);
+       heif_retvm_if_failed(data_size > 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid data_size");
+
+       image = g_new0(heif_image_t, 1);
+       image->width = width;
+       image->height = height;
+       image->format = color_format;
+       image->size = data_size;
+       image->data = g_malloc0(image->size);
+
+       heif_info("width: %u, height: %u, format: %u, size: %zu", image->width, image->height, image->format, image->size);
+
+       *output_image = image;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __combine_rgb(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image)
+{
+       unsigned int bpp = 0;   /* byte per pixel */
+       unsigned int combine_stride = 0, tile_stride = 0;
+       unsigned int row = 0;
+       size_t copy_n = 0;
+       size_t total_copied_n = 0;
+       unsigned char *ptr_src = NULL, *ptr_dst = NULL;
+
+       heif_retvm_if_failed(__is_valid_heif_image(tile_image), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid tile_image");
+       heif_retvm_if_failed(pos_x < combine_image->width, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pos_x %u", pos_x);
+       heif_retvm_if_failed(pos_y < combine_image->height, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pos_y %u", pos_y);
+       heif_retvm_if_failed(__is_valid_heif_image(combine_image), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid combine_image");
+
+       switch (tile_image->format) {
+       case HEIF_COLOR_FORMAT_RGB24:
+               bpp = 3;
+               break;
+
+       case HEIF_COLOR_FORMAT_ARGB:
+               // fall through
+       case HEIF_COLOR_FORMAT_BGRA:
+               // fall through
+       case HEIF_COLOR_FORMAT_RGBA:
+               bpp = 4;
+               break;
+
+       default:
+               heif_error("not support %u format yet", tile_image->format);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       // combine_stride: row-bytes of combine_image
+       combine_stride = combine_image->width * bpp;
+       // tile_stride: row-bytes of tile
+       tile_stride = tile_image->width * bpp;
+       // copy_n: bytes to copy to combine_image
+       // Because the tile image was designed by the encoder limit of the device that was taken.
+       // The tile image doesn't make up the whole image(combine_image) properly.
+       // As a result, the tile image has over size data.
+       // So we should check the width/height to avoid over read & write.
+       // To explain it easily,
+       // When you want to tile the walls in the bathroom, the tiles are sized at the factory.
+       // Therefore, you may not be able to tile it to fit exactly the walls of the bathroom.
+       // If it doesn't fit, the tiles at the corner must be cut and pasted.
+       copy_n = (combine_image->width < (pos_x + tile_image->width)) ? (combine_image->width - pos_x) * bpp : tile_stride;
+
+       ptr_src = tile_image->data;
+       ptr_dst = combine_image->data + pos_y * combine_stride + pos_x * bpp;
+
+       heif_info("combine_stride: %u, tile_stride: %u", combine_stride, tile_stride);
+       heif_info("ptr_src: %p, ptr_dst: %p, copy_n: %zu", ptr_src, ptr_dst, copy_n);
+
+       // check the size of tile_image to avoid over-read
+       if (tile_stride * tile_image->height > tile_image->size) {
+               heif_error("Invalid source size! stride: %u, height: %u, src_size: %zu",
+                                               tile_stride, tile_image->height, tile_image->size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       for (row = 0; row < tile_image->height; row++) {
+               // check the height to avoid over-write
+               if (pos_y + row >= combine_image->height) {
+                       heif_warning("[No-Error] height limited! tile_h: %u, pos_y: %u, comb_h: %u",
+                                                       tile_image->height, pos_y, combine_image->height);
+                       break;
+               }
+               // check the size to copy to avoid 'ptr_dst' overflow
+               if (total_copied_n + copy_n > combine_image->size) {
+                       heif_error("dest buffer overflow! total_copied_n: %zu, copy_size: %zu, dest_size: %zu",
+                                                       total_copied_n, copy_n, combine_image->size);
+                       break;
+               }
+               memcpy(ptr_dst, ptr_src, copy_n);
+
+               ptr_src += tile_stride;         // update ptr to next row in tile_image
+               ptr_dst += combine_stride;      // update ptr to next row in combile_image
+               total_copied_n += copy_n;               // to check data overflow
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __combine_yuv(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image)
+{
+       unsigned int combine_stride = 0;
+       unsigned int tile_stride = 0;
+       unsigned int row = 0;
+       size_t copy_n = 0;
+       size_t total_copied_n[3] = { 0, };
+       size_t dst_size = 0;
+       unsigned char *ptr_src[3] = { NULL, }, *ptr_dst[3] = { NULL, }; // y = 0, u = 1, v =2
+
+       heif_retvm_if_failed(__is_valid_heif_image(tile_image), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid tile_image");
+       heif_retvm_if_failed(pos_x < combine_image->width, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pos_x %u", pos_x);
+       heif_retvm_if_failed(pos_y < combine_image->height, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid pos_y %u", pos_y);
+       heif_retvm_if_failed(__is_valid_heif_image(combine_image), LIBHEIF_ERROR_INVALID_PARAMETER, "invalid combine_image");
+
+       switch (tile_image->format) {
+       case HEIF_COLOR_FORMAT_YUV420P:
+               break;
+       default:
+               heif_error("not support %u format yet", tile_image->format);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       // combine_stride: bytes of the width for entire image
+       // tile_stride: bytes of the width for tile image
+       // offset_x: bytes of x for tile image
+       // offset_y: bytes of y for tile image, each row increase by combine_stride
+
+       // copy 'y' data of tile
+       combine_stride = combine_image->width;
+       tile_stride = tile_image->width;
+       dst_size = combine_stride * combine_image->height;
+
+       ptr_src[0] = tile_image->data;
+       ptr_dst[0] = combine_image->data + pos_y * combine_stride + pos_x;
+
+       // copy 'y' data of tile
+       copy_n = ((combine_stride - pos_x) < tile_stride) ? (combine_stride - pos_x) : tile_stride;
+
+       heif_info("combine_stride: %u, tile_stride: %u", combine_stride, tile_stride);
+       heif_info("ptr_src: %p, ptr_dst: %p, copy_n: %zu", ptr_src[0], ptr_dst[0], copy_n);
+
+       // check the size of tile_image to avoid over-read
+       if (((tile_stride * tile_image->height) * 3) / 2 > tile_image->size) {
+               heif_error("Invalid source size! stride: %u, height: %u, src_size: %zu",
+                                               tile_stride, tile_image->height, tile_image->size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       for (row = 0; row < tile_image->height; row++) {
+               // check the height to avoid over-write
+               if (pos_y + row >= combine_image->height) {
+                       heif_warning("[No-Error] height limited! tile_h: %u, pos_y: %u, comb_h: %u",
+                                                       tile_image->height, pos_y, combine_image->height);
+                       break;
+               }
+               // check the size to copy to avoid 'ptr_dst' overflow
+               if (total_copied_n[0] + copy_n > dst_size) {
+                       heif_error("dest buffer overflow! total_copied_n: %zu, copy_size: %zu, dest_size: %zu",
+                                                       total_copied_n[0], copy_n, dst_size);
+                       break;
+               }
+               memcpy(ptr_dst[0], ptr_src[0], copy_n);
+
+               ptr_src[0] += tile_stride;
+               ptr_dst[0] += combine_stride;
+               total_copied_n[0] += copy_n;
+       }
+
+       // copy 'uv' data of tile
+       // uv_stride = y_stride / 2
+       // uv_height = y_height / 2
+       combine_stride = combine_stride / 2;
+       tile_stride = tile_stride / 2;
+       dst_size = dst_size / 4;        // (combine_stride * combine_image->height / 2)
+
+       ptr_src[1] = tile_image->data + tile_image->width * tile_image->height;
+       ptr_src[2] = ptr_src[1] + (tile_image->width / 2) * (tile_image->height / 2);
+
+       ptr_dst[1] = combine_image->data + combine_image->width * combine_image->height + pos_y / 2 * combine_stride + pos_x / 2;
+       ptr_dst[2] = ptr_dst[1] + (combine_image->width / 2) * (combine_image->height / 2);
+
+       copy_n = ((combine_stride - (pos_x / 2)) < tile_stride) ? (combine_stride - (pos_x / 2)) : tile_stride;
+
+       heif_info("combine_stride: %u, tile_stride: %u", combine_stride, tile_stride);
+       heif_info("ptr_src: %p, ptr_dst: %p, copy_n: %zu", ptr_src[1], ptr_dst[1], copy_n);
+       heif_info("ptr_src: %p, ptr_dst: %p, copy_n: %zu", ptr_src[2], ptr_dst[2], copy_n);
+
+       for (row = 0; row < tile_image->height / 2; row++) {
+               if (pos_y + row * 2 >= combine_image->height) {
+                       heif_warning("[No-Error] height limited! tile_h: %u, pos_y: %u, comb_h: %u",
+                                                       tile_image->height, pos_y, combine_image->height);
+                       break;
+               }
+               if (total_copied_n[1] + copy_n > dst_size) {
+                       heif_error("dest buffer overflow! total_copied_n: %zu, copy_size: %zu, dest_size: %zu",
+                                                       total_copied_n[1], copy_n, dst_size);
+                       break;
+               }
+               if (total_copied_n[2] + copy_n > dst_size) {
+                       heif_error("dest buffer overflow! total_copied_n: %zu, copy_size: %zu, dest_size: %zu",
+                                                       total_copied_n[2], copy_n, dst_size);
+                       break;
+               }
+               memcpy(ptr_dst[1], ptr_src[1], copy_n);
+               memcpy(ptr_dst[2], ptr_src[2], copy_n);
+
+               ptr_src[1] += tile_stride;
+               ptr_dst[1] += combine_stride;
+               total_copied_n[1] += copy_n;
+               ptr_src[2] += tile_stride;
+               ptr_dst[2] += combine_stride;
+               total_copied_n[2] += copy_n;
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __combine_tile_image(heif_image_t *tile_image, unsigned int pos_x, unsigned int pos_y, heif_image_t *combine_image)
+{
+       heif_retvm_if_failed(tile_image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid tile_image");
+
+       switch (tile_image->format) {
+       case HEIF_COLOR_FORMAT_RGB24:
+               // fall through
+       case HEIF_COLOR_FORMAT_ARGB:
+               // fall through
+       case HEIF_COLOR_FORMAT_BGRA:
+               // fall through
+       case HEIF_COLOR_FORMAT_RGBA:
+               return __combine_rgb(tile_image, pos_x, pos_y, combine_image);
+
+       case HEIF_COLOR_FORMAT_YUV420P:
+               return __combine_yuv(tile_image, pos_x, pos_y, combine_image);
+
+       default:
+               heif_error("not support %u format yet", tile_image->format);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+}
+
+static int __decode_grid_image(heif_itemtable_h item_table, heif_color_format_e format, heif_image_item_h image_item, heif_image_t **image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       unsigned int rows = 0, columns = 0;
+       GSList *tiles, *iter;
+       heif_image_item_h tile_item;
+       heif_image_t *tile_image;
+       unsigned int image_index = 0;
+       unsigned int pos_x = 0, pos_y = 0;
+       heif_image_t *combine_image;
+
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+       heif_retvm_if_failed(image, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image");
+
+       ret = heif_itemtable_get_grid_info(image_item, &rows, &columns, &tiles);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_itemtable_get_grid_info fail (%d)", ret);
+               return ret;
+       }
+
+       if (g_slist_length(tiles) < rows * columns) {
+               heif_error("tiles are not enough, invalid source");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       // create output buffer to combine image
+       ret = __create_output_image(image_item, format, &combine_image);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__create_output_image fail (%d)", ret);
+               return ret;
+       }
+
+       // decode and combine derived images
+       for (iter = tiles; iter; iter = g_slist_next(iter)) {
+               ret = heif_itemtable_get_image_item(item_table, GPOINTER_TO_UINT(iter->data), &tile_item);
+               if (ret != LIBHEIF_ERROR_NONE) {
+                       heif_error("heif_itemtable_get_image_item fail (%d)", ret);
+                       heif_decoder_destroy_image(combine_image);
+                       return ret;
+               }
+
+               ret = __decode_single_image(item_table, format, tile_item, &tile_image);
+               if (ret != LIBHEIF_ERROR_NONE) {
+                       heif_error("__decode_single_image fail (%d)", ret);
+                       heif_decoder_destroy_image(tile_image);
+                       heif_decoder_destroy_image(combine_image);
+                       return ret;
+               }
+
+               heif_info("__decode_single_image(%u) succeeded.", GPOINTER_TO_UINT(iter->data));
+
+               // combine tile image
+               ret = __combine_tile_image(tile_image, pos_x, pos_y, combine_image);
+
+               // calculate the position of next tile
+               if ((++image_index % columns) != 0) {
+                       pos_x += tile_image->width;
+               } else {
+                       pos_y += tile_image->height;
+                       pos_x = 0;
+               }
+               heif_decoder_destroy_image(tile_image);
+
+               if (ret != LIBHEIF_ERROR_NONE) {
+                       heif_error("__combine_tile_image fail (%d)", ret);
+                       heif_decoder_destroy_image(combine_image);
+                       return ret;
+               }
+               heif_info("__combine_tile_image(%u) succeeded.", GPOINTER_TO_UINT(iter->data));
+       }
+
+       *image = combine_image;
+
+       return LIBHEIF_ERROR_NONE;
+}
diff --git a/src/heif_extractor.c b/src/heif_extractor.c
new file mode 100644 (file)
index 0000000..4dcf431
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <glib.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_box_util.h"
+#include "heif_itemtable.h"
+#include "heif_extractor.h"
+
+typedef struct {
+       bool is_heic;
+       heif_source_h source;
+       heif_itemtable_h item_table;    // the item's table after reading all metadata
+} heif_extractor_t;
+
+static gint __extractor_compare_brand(gconstpointer data, gconstpointer user_data);
+static int __extractor_parse_chunk(heif_extractor_t *extractor, off_t *offset, unsigned int depth);
+static int __extractor_read_metadata(heif_extractor_t *extractor);
+static int __extractor_read_brandset(heif_extractor_t *extractor, off_t offset, off_t chunk_data_size);
+
+
+int heif_extractor_extract(heif_source_h source, heif_itemtable_h *item_table)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_extractor_t *_extractor = NULL;
+
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table");
+
+       _extractor = g_new0(heif_extractor_t, 1);
+       _extractor->is_heic = false;
+       _extractor->item_table = NULL;
+       _extractor->source = source;
+
+       ret = __extractor_read_metadata(_extractor);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__extractor_read_metadata fail %d", ret);
+               g_free(_extractor);
+               return ret;
+       }
+
+       if (_extractor->is_heic) {
+               *item_table = _extractor->item_table;
+       } else {
+               heif_itemtable_destroy(_extractor->item_table);
+               ret = LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       g_free(_extractor);
+
+       return ret;
+}
+
+static gint __extractor_compare_brand(gconstpointer data, gconstpointer user_data)
+{
+       heif_retvm_if_failed(data, -1, "invalid data");
+       heif_retvm_if_failed(user_data, -1, "invalid user_data");
+
+       uint32_t brand = (uint32_t)GPOINTER_TO_UINT(data);
+       uint32_t get_brand = (uint32_t)GPOINTER_TO_UINT(user_data);
+
+       return (brand == get_brand) ? 0 : 1;
+}
+
+static int __extractor_parse_chunk(heif_extractor_t *extractor, off_t *offset, unsigned int depth)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       uint64_t chunk_size = 0;
+       int32_t chunk_type = 0;
+       off_t data_offset = 0;
+       off_t chunk_data_size = 0;
+       off_t stop_offset = 0;
+
+       heif_info("entering __extractor_parse_chunk %jd/%u", *offset, depth);
+
+       heif_retvm_if_failed(extractor, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid extractor");
+       heif_retvm_if_failed(offset && (*offset >= 0), LIBHEIF_ERROR_INVALID_PARAMETER,
+                                               "invalid offset %jd", offset ? *offset : 0);
+       heif_retvm_if_failed(depth <= 100, LIBHEIF_ERROR_INVALID_PARAMETER, "max depth of container was reached");
+
+       ret = _parse_hdr(extractor->source, offset, &chunk_size, &chunk_type, &data_offset);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("_parse_hdr fail %d", ret);
+               return ret;
+       }
+
+       if (chunk_size == 1) {
+               if (heif_source_read_at(extractor->source, *offset + 8, &chunk_size, 8) != 8)
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+
+               chunk_size = ntoh64(chunk_size);
+               data_offset += 8;
+
+               if (chunk_size < 16) {
+                       // The smallest valid chunk is 16 bytes long in this case.
+                       heif_error("The smallest valid chunk is 16 bytes long in this case %"PRIu64, chunk_size);
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+               }
+       } else if (chunk_size == 0) {
+               if (depth == 0) {
+                       // atom extends to end of file
+                       size_t source_size = heif_source_get_size(extractor->source);
+                       if (source_size == 0) {
+                               heif_error("invalid source_size %zu", source_size);
+                               return LIBHEIF_ERROR_INVALID_PARAMETER;
+                       }
+                       chunk_size = (source_size - *offset);
+               } else {
+                       // not allowed for non-toplevel atoms, skip it
+                       *offset += 4;
+                       return LIBHEIF_ERROR_NONE;
+               }
+       } else if (chunk_size < 8) {
+               // The smallest valid chunk is 8 bytes long.
+               heif_error("invalid chunk size: %"PRIu64, chunk_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       DEBUG_CHUNK_ALL("hdr", chunk_type, chunk_size, *offset, depth);
+
+       // (data_offset - *offset) is either 8 or 16
+       chunk_data_size = chunk_size - (data_offset - *offset);
+       heif_info("chunk_data_size: %jd", chunk_data_size);
+       if (chunk_data_size < 0) {
+               heif_error("invalid chunk size: %jd", chunk_data_size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       switch (chunk_type) {
+       case FOURCC('m', 'e', 't', 'a'):
+               // read 'meta' container at the end of 'meta', the usage of 'stop_offset' in library is the same.
+               // *offset: the start position for read
+               // chunk_size: the size of 'meta' container
+               // data_offset: the start position of data exclude chunk_type & chunk_size in 'meta'
+               stop_offset = *offset + chunk_size;
+               *offset = data_offset;
+
+               while (*offset < stop_offset) {
+                       ret = __extractor_parse_chunk(extractor, offset, depth + 1);
+                       if (ret != LIBHEIF_ERROR_NONE) {
+                               heif_error("__extractor_parse_chunk fail %d", ret);
+                               return ret;
+                       }
+               }
+
+               if (*offset != stop_offset) {
+                       heif_error("offset did not advance till stop: %jd != %jd", *offset, stop_offset);
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+               }
+               break;
+
+       case FOURCC('i', 'l', 'o', 'c'):
+               // fall through
+       case FOURCC('i', 'i', 'n', 'f'):
+               // fall through
+       case FOURCC('i', 'p', 'r', 'p'):
+               // fall through
+       case FOURCC('p', 'i', 't', 'm'):
+               // fall through
+       case FOURCC('i', 'd', 'a', 't'):
+               // fall through
+       case FOURCC('i', 'r', 'e', 'f'):
+               // fall through
+       case FOURCC('i', 'p', 'r', 'o'):
+               DEBUG_CHUNK_TYPE("top", chunk_type);
+               if (!extractor->is_heic) {
+                       heif_error("invalid heic: %d", extractor->is_heic);
+                       *offset += chunk_size;
+                       break;
+               }
+
+               if (!extractor->item_table) {
+                       ret = heif_itemtable_create(&extractor->item_table, extractor->source);
+                       if (ret != LIBHEIF_ERROR_NONE) {
+                               heif_error("heif_itemtable_create fail %d", ret);
+                               return ret;
+                       }
+               }
+               ret = heif_itemtable_parse(extractor->item_table, chunk_type, data_offset, chunk_data_size);
+               if (ret != LIBHEIF_ERROR_NONE) {
+                       heif_error("heif_itemtable_parse fail %d", ret);
+                       heif_itemtable_destroy(extractor->item_table);
+                       extractor->item_table = NULL;
+                       return ret;
+               }
+
+               *offset += chunk_size;
+               break;
+
+       case FOURCC('f', 't', 'y', 'p'):
+               heif_retvm_if_failed(chunk_data_size > 8, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid chunk_data_size: %jd", chunk_data_size);
+               heif_retvm_if_failed(depth == 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid 'ftyp' depth: %u", depth);
+
+               stop_offset = *offset + chunk_size;
+
+               ret = __extractor_read_brandset(extractor, data_offset, chunk_data_size);
+               if (ret != LIBHEIF_ERROR_NONE) {
+                       heif_error("__extractor_read_brandset fail %d", ret);
+                       break;
+               }
+
+               *offset = stop_offset;
+               break;
+
+       default:
+               DEBUG_CHUNK_TYPE("default", chunk_type);
+               *offset += chunk_size;
+               break;
+       }
+
+       return ret;
+}
+
+static int __extractor_read_metadata(heif_extractor_t *extractor)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       off_t offset = 0, orig_offset = 0;
+
+       heif_debug_fenter();
+
+       heif_retvm_if_failed(extractor, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid extractor");
+
+       while (!(extractor->is_heic && extractor->item_table)) {
+               orig_offset = offset;
+
+               ret = __extractor_parse_chunk(extractor, &offset, 0);
+               if (ret != LIBHEIF_ERROR_NONE) {
+                       heif_error("__extractor_parse_chunk fail %d", ret);
+                       break;
+               }
+
+               if (offset <= orig_offset) {
+                       // only continue parsing if the offset was advanced,
+                       // otherwise we might end up in an infinite loop
+                       heif_error("offset did not advance: %jd->%jd", orig_offset, offset);
+                       ret = LIBHEIF_ERROR_INVALID_PARAMETER;
+                       break;
+               }
+       }
+
+       heif_debug_fleave();
+
+       return ret;
+}
+
+static int __extractor_read_brandset(heif_extractor_t *extractor, off_t offset, off_t chunk_data_size)
+{
+       uint32_t brand = 0;                     // value of brand
+       uint32_t brand_n = 0;           // number of brand
+       GSList *brand_set = NULL;
+       GSList *mif1_brand = NULL, *heic_brand = NULL;
+       size_t i = 0;
+
+       heif_retvm_if_failed(extractor, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid extractor");
+       heif_retvm_if_failed(offset >= 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid offset");
+       heif_retvm_if_failed(chunk_data_size >= 8, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid chunk_data_size");
+
+       brand_n = (chunk_data_size - 8) / 4;
+
+       for (i = 0; i < brand_n + 2; ++i) {
+               if (i == 1) {
+                       // Skip this index, it refers to the minorVersion,
+                       // not a brand.
+                       continue;
+               }
+
+               if (heif_source_read_at(extractor->source, offset + 4 * i, &brand, 4) != 4) {
+                       g_slist_free(brand_set);
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+               }
+
+               brand = ntohl(brand);
+               DEBUG_CHUNK_TYPE("brand", brand);
+               brand_set = g_slist_append(brand_set, GUINT_TO_POINTER(brand));
+       }
+
+       // Note:
+       // 'mif1': The source has a still image container. (ref. 'mifs': image sequence/multi-image)
+       // 'heic': The source has HEVC image compression container.
+       mif1_brand = g_slist_find_custom(brand_set, GUINT_TO_POINTER(FOURCC('m', 'i', 'f', '1')), __extractor_compare_brand);
+       heic_brand = g_slist_find_custom(brand_set, GUINT_TO_POINTER(FOURCC('h', 'e', 'i', 'c')), __extractor_compare_brand);
+
+       if (mif1_brand && heic_brand) {
+               heif_info("identified heic image");
+               extractor->is_heic = true;
+       } else {
+               heif_error("%s", (mif1_brand ?  "source is not heic" : "source is not still image"));
+       }
+
+       g_slist_free(brand_set);
+
+       return LIBHEIF_ERROR_NONE;
+}
diff --git a/src/heif_itemtable.c b/src/heif_itemtable.c
new file mode 100644 (file)
index 0000000..6eee72a
--- /dev/null
@@ -0,0 +1,1331 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <inttypes.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_itemtable.h"
+
+#include "heif_box_util.h"
+#include "heif_box_iloc.h"
+#include "heif_box_iinf.h"
+#include "heif_box_iprp.h"
+#include "heif_box_pitm.h"
+#include "heif_box_idat.h"
+#include "heif_box_iref.h"
+
+
+#define CHECK_HVCC_OFFSET(hvcC, offset) do { \
+                       if (((heif_buffer_t *)hvcC)->size < offset) { \
+                               heif_error("invalid hvcC size(%zu) or too large offset(%jd)", ((heif_buffer_t *)hvcC)->size, offset); \
+                               return false; \
+                       } \
+               } while (0) \
+
+#define START_CODE_LEN         4
+
+typedef struct {
+       uint8_t completeness;
+       uint8_t unit_type;
+       heif_buffer_t nal_unit;
+} _hvc_config_t;
+
+typedef struct {
+       // common
+       uint32_t type;          // the item type of IINF, (e.g. hvc1, grid, exif)
+       uint32_t item_id;       // the item id
+       off_t offset;           // the offset of the item in source
+       size_t size;            // the size of the item
+
+       // for image(hvc1 or grid) item
+       bool hidden;            // the displayable or not
+       bool is_grid;
+       int32_t width;          // the width of the item
+       int32_t height;         // the height of the item
+       int32_t rotation;       // the rotation of the item
+       bool has_mirror;
+       int8_t axis;            // the mirroring operation of the item
+       _hvc_config_t hvc_config;       // nal unit created with hvcc by __get_hvc_config_from_hvcC()
+       heif_buffer_t icc;      // the icc profile
+
+       // for only grid
+       int32_t rows;           // the number of tiles in row, only grid item has this value
+       int32_t columns;        // the number of tiles in column, only grid item has this value
+
+       // for ref
+       GSList *thumbnails;     // item ids of thumbnail
+       GSList *dimg_refs;      // item ids of tile
+       GSList *cdsc_refs;      // item ids of content description(exif, xml)
+} _image_item_t;
+
+typedef struct {
+       // validity
+       bool is_valid;
+       GSList *seen_types;             // the container boxes read from source
+       GSList *require_types;  // the mandatory container boxes list
+
+       // source
+       heif_source_h source;
+
+       // building image items
+       GSList *item_infos;
+       GSList *item_locs;
+       GSList *properties;
+       GSList *associations;
+       GSList *item_refs;
+       off_t idat_offset;
+       size_t idat_size;
+
+       // datas after building image item tables
+       bool has_grid;
+       uint32_t primary_id;
+       GSList *displayables;   // item id list, '0' index is primary item id
+       GSList *image_items;    // _image_item_t list of hvc1 and grid
+       GSList *exif_items;             // _image_item_t list of exif
+} heif_itemtable_t;
+
+static const uint8_t START_CODE[START_CODE_LEN] = { 0, 0, 0, 1 };      // start_code is 0x00000001
+
+static gint __compare_item_type(gconstpointer a, gconstpointer b);
+static gint __compare_image_item_id(gconstpointer data, gconstpointer user_data);
+static gint __compare_iloc_item_id(gconstpointer data, gconstpointer user_data);
+static void __print_image_item(gpointer data, gpointer user_data);
+static void __print_exif_item(gpointer data, gpointer user_data);
+static bool __get_hvc_config_from_hvcC(_hvcC_prop_t *hvcC, _hvc_config_t *config);
+static void __attach_property(_association_t *association, GSList *image_items, GSList *properties);
+static void __attach_refs(_item_ref_t *item_ref, GSList *image_items, GSList *exif_items);
+static bool __has_all_require_types(GSList *seen_types, GSList *require_types);
+static int __build_image_if_possible(heif_itemtable_t *item_table);
+static int __heif_itemtable_parse_iloc_box(heif_itemtable_t *item_table, off_t offset, size_t size);
+static int __heif_itemtable_parse_iinf_box(heif_itemtable_t *item_table, off_t offset, size_t size);
+static int __heif_itemtable_parse_iprp_box(heif_itemtable_t *item_table, off_t offset, size_t size);
+static int __heif_itemtable_parse_pitm_box(heif_itemtable_t *item_table, off_t offset, size_t size);
+static int __heif_itemtable_parse_idat_box(heif_itemtable_t *item_table, off_t offset, size_t size);
+static int __heif_itemtable_parse_iref_box(heif_itemtable_t *item_table, off_t offset, size_t size);
+
+
+int heif_itemtable_create(heif_itemtable_h *handle, heif_source_h source)
+{
+       heif_itemtable_t *new_itemtable = NULL;
+
+       heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+
+       new_itemtable = g_new0(heif_itemtable_t, 1);
+
+       // set as 'false' for clarity
+       new_itemtable->is_valid = false;
+       new_itemtable->source = source;
+
+       // add minimum set of boxes to 'require_types'.
+       new_itemtable->require_types = g_slist_insert_sorted(new_itemtable->require_types, GUINT_TO_POINTER(FOURCC('i', 'p', 'r', 'p')), __compare_item_type);
+       new_itemtable->require_types = g_slist_insert_sorted(new_itemtable->require_types, GUINT_TO_POINTER(FOURCC('i', 'l', 'o', 'c')), __compare_item_type);
+       new_itemtable->require_types = g_slist_insert_sorted(new_itemtable->require_types, GUINT_TO_POINTER(FOURCC('p', 'i', 't', 'm')), __compare_item_type);
+       new_itemtable->require_types = g_slist_insert_sorted(new_itemtable->require_types, GUINT_TO_POINTER(FOURCC('i', 'i', 'n', 'f')), __compare_item_type);
+
+       *handle = (heif_itemtable_h)new_itemtable;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_itemtable_parse(heif_itemtable_h handle, int32_t chunk_type, off_t data_offset, off_t chunk_data_size)
+{
+       switch (chunk_type) {
+       case FOURCC('i', 'l', 'o', 'c'):
+               return __heif_itemtable_parse_iloc_box((heif_itemtable_t *)handle, data_offset, chunk_data_size);
+
+       case FOURCC('i', 'i', 'n', 'f'):
+               return __heif_itemtable_parse_iinf_box((heif_itemtable_t *)handle, data_offset, chunk_data_size);
+
+       case FOURCC('i', 'p', 'r', 'p'):
+               return __heif_itemtable_parse_iprp_box((heif_itemtable_t *)handle, data_offset, chunk_data_size);
+
+       case FOURCC('p', 'i', 't', 'm'):
+               return __heif_itemtable_parse_pitm_box((heif_itemtable_t *)handle, data_offset, chunk_data_size);
+
+       case FOURCC('i', 'd', 'a', 't'):
+               return __heif_itemtable_parse_idat_box((heif_itemtable_t *)handle, data_offset, chunk_data_size);
+
+       case FOURCC('i', 'r', 'e', 'f'):
+               return __heif_itemtable_parse_iref_box((heif_itemtable_t *)handle, data_offset, chunk_data_size);
+
+       case FOURCC('i', 'p', 'r', 'o'):
+               heif_warning("ipro box not supported!");
+               break;
+
+       default:
+               DEBUG_CHUNK_TYPE("Unrecognized", chunk_type);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+bool heif_itemtable_is_valid(heif_itemtable_h handle)
+{
+       heif_retvm_if_failed(handle, false, "invalid handle");
+
+       return ((heif_itemtable_t *)handle)->is_valid;
+}
+
+bool heif_itemtable_has_grid(heif_itemtable_h handle)
+{
+       heif_itemtable_t *item_table = (heif_itemtable_t *)handle;
+
+       heif_retvm_if_failed(handle, false, "invalid handle");
+       heif_retvm_if_failed(item_table->is_valid, false, "invalid handle");
+
+       return item_table->has_grid;
+}
+
+int heif_itemtable_get_primary_image(heif_itemtable_h handle, unsigned int *primary_id)
+{
+       heif_itemtable_t *item_table = (heif_itemtable_t *)handle;
+
+       heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(item_table->is_valid, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(item_table->displayables, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(primary_id, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid primary_id");
+
+       *primary_id = (unsigned int)GPOINTER_TO_UINT(item_table->displayables->data);
+
+       heif_secure_debug("primary_id (%u)", *primary_id);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_itemtable_get_image_item(heif_itemtable_h handle, unsigned int item_id, heif_image_item_h *image_item)
+{
+       heif_itemtable_t *item_table = (heif_itemtable_t *)handle;
+       GSList *found;
+
+       heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(item_table->is_valid, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+
+       // find image_item
+       found = g_slist_find_custom(item_table->image_items, GUINT_TO_POINTER(item_id), __compare_image_item_id);
+       if (!found) {
+               heif_error("not exist item_id (%u)", item_id);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       *image_item = (heif_image_item_h)found->data;
+
+       heif_secure_debug("item_id (%u)", ((_image_item_t *)found->data)->item_id);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_itemtable_get_thumb_item(heif_itemtable_h handle, heif_image_item_h *thumb_item)
+{
+       heif_itemtable_t *item_table = (heif_itemtable_t *)handle;
+       uint32_t primary_id;
+       _image_item_t *primary_item;
+       GSList *found;
+
+       heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(item_table->is_valid, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(item_table->displayables, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(thumb_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid thumb_item");
+
+       primary_id = (uint32_t)GPOINTER_TO_UINT(item_table->displayables->data);
+
+       // find image_item
+       found = g_slist_find_custom(item_table->image_items, GUINT_TO_POINTER(primary_id), __compare_image_item_id);
+       if (!found) {
+               heif_error("not exist primary_id (%u)", primary_id);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       primary_item = (_image_item_t *)found->data;
+       if (!primary_item->thumbnails) {
+               heif_error("not exist thumbnail");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       heif_secure_debug("thumb item_id (%u)", GPOINTER_TO_UINT(primary_item->thumbnails->data));
+
+       return heif_itemtable_get_image_item(handle, GPOINTER_TO_UINT(primary_item->thumbnails->data), thumb_item);
+}
+
+int heif_itemtable_get_image_resolution(heif_image_item_h image_item, unsigned int *width, unsigned int *height)
+{
+       _image_item_t *_image_item = (_image_item_t *)image_item;
+
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+       heif_retvm_if_failed(width, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid width");
+       heif_retvm_if_failed(height, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid height");
+
+       *width = _image_item->width;
+       *height = _image_item->height;
+
+       heif_secure_debug("width: %u, height: %u", *width, *height);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_itemtable_get_image_orientation(heif_image_item_h image_item, unsigned int *orientation)
+{
+       _image_item_t *_image_item = (_image_item_t *)image_item;
+
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+       heif_retvm_if_failed(orientation, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid orientation");
+
+       *orientation = _image_item->rotation;
+
+       heif_secure_debug("orientation: %u", *orientation);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_itemtable_get_image_mirror_mode(heif_image_item_h image_item, heif_mirror_mode_e *mirror)
+{
+       _image_item_t *_image_item = (_image_item_t *)image_item;
+
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+       heif_retvm_if_failed(mirror, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid mirror");
+
+       // initial value
+       *mirror = HEIF_MIRROR_NONE;
+
+       if (_image_item->has_mirror)
+               *mirror = (_image_item->axis == 0) ? HEIF_MIRROR_VERTICAL : HEIF_MIRROR_HORIZONTAL;
+
+       heif_secure_debug("mirror: %d", *mirror);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_itemtable_get_grid_info(heif_image_item_h image_item, unsigned int *row, unsigned int *column, GSList **img_refs)
+{
+       _image_item_t *_image_item = (_image_item_t *)image_item;
+
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+       heif_retvm_if_failed(_image_item->is_grid, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+       heif_retvm_if_failed(row, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid row");
+       heif_retvm_if_failed(column, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid column");
+       heif_retvm_if_failed(img_refs, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid img_refs");
+
+       *row = _image_item->rows;
+       *column = _image_item->columns;
+       *img_refs = _image_item->dimg_refs;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_itemtable_get_coded_data(heif_itemtable_h handle, heif_image_item_h image_item, heif_buffer_t **coded_data)
+{
+       _image_item_t *_image_item = (_image_item_t *)image_item;
+       heif_buffer_t *nal_unit = NULL;
+       heif_buffer_t *mdat = NULL;
+       size_t read_n = 0;
+
+       heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+       heif_retvm_if_failed(coded_data, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid coded_data");
+
+       nal_unit = g_new0(heif_buffer_t, 1);
+       nal_unit->data = _image_item->hvc_config.nal_unit.data;
+       nal_unit->size = _image_item->hvc_config.nal_unit.size;
+
+       if (!nal_unit->data || nal_unit->size == 0) {
+               heif_error("nal_unit: %p, nal_unit: %zu", nal_unit->data, nal_unit->size);
+               g_free(nal_unit);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+#ifdef __DEBUG_CODEC_CONFIG
+       {
+               size_t i = 0;
+               for (i = 0; i < nal_unit->size; i++)
+                       heif_error("nal_unit: %x", nal_unit->data[i]);
+       }
+#endif
+
+       mdat = g_new0(heif_buffer_t, 1);
+       mdat->size = nal_unit->size + _image_item->size;
+       mdat->data = (uint8_t *)g_malloc0(mdat->size);
+
+       // attach nal_unit in front of media
+       memcpy(mdat->data, nal_unit->data, nal_unit->size);
+       memcpy(mdat->data + nal_unit->size, START_CODE, START_CODE_LEN);
+
+       read_n = _image_item->size - START_CODE_LEN;
+
+       // read media
+       if (heif_source_read_at(((heif_itemtable_t *)handle)->source,
+                                                       _image_item->offset + START_CODE_LEN,
+                                                       mdat->data + nal_unit->size + START_CODE_LEN,
+                                                       read_n) != read_n) {
+               heif_error("heif_source_read_at fail");
+               heif_buffer_destroy(mdat);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       *coded_data = mdat;
+       heif_secure_debug("heif_itemtable_get_coded_data success %p, %zu", (*coded_data)->data, (*coded_data)->size);
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_itemtable_get_exif(heif_itemtable_h handle, heif_image_item_h image_item, void **exif, size_t *exif_size)
+{
+       _image_item_t *_image_item = (_image_item_t *)image_item;
+       _image_item_t *exif_item = NULL;
+       GSList *found = NULL;
+       uint8_t *_exif;
+       size_t _exif_size;
+
+       heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+       heif_retvm_if_failed(exif, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid exif");
+       heif_retvm_if_failed(exif_size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid exif_size");
+
+       heif_debug_fenter();
+
+       // find exif_item referenced by image_item
+       if (!_image_item->cdsc_refs) {
+               heif_error("not exist cdsc");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       found = g_slist_find_custom(((heif_itemtable_t *)handle)->exif_items, GUINT_TO_POINTER(_image_item->cdsc_refs->data), __compare_image_item_id);
+       if (!found) {
+               heif_error("not exist item_id (%u)", GPOINTER_TO_UINT(_image_item->cdsc_refs->data));
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       exif_item = (_image_item_t *)found->data;
+       if (!exif_item) {
+               heif_error("not exist exif");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       _exif_size = exif_item->size;
+       _exif = (uint8_t *)g_malloc0(_exif_size);
+
+       heif_error("_exif: %p offset: %jd exif_size: %zu", _exif, exif_item->offset, exif_item->size);
+
+       if (heif_source_read_at(((heif_itemtable_t *)handle)->source, exif_item->offset, _exif, _exif_size) != _exif_size) {
+               heif_error("heif_source_read_at fail");
+               g_free(_exif);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       *exif = (void *)_exif;
+       *exif_size = _exif_size;
+       heif_secure_debug("heif_itemtable_get_exif success %p, %zu", *exif, *exif_size);
+
+       heif_debug_fleave();
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_itemtable_get_icc_profile(heif_itemtable_h handle, heif_image_item_h image_item, void **icc, size_t *icc_size)
+{
+       _image_item_t *_image_item = (_image_item_t *)image_item;
+
+       heif_retvm_if_failed(handle, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid handle");
+       heif_retvm_if_failed(image_item, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid image_item");
+       heif_retvm_if_failed(icc, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid icc");
+       heif_retvm_if_failed(icc_size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid icc_size");
+
+       if (!_image_item->icc.data || (_image_item->icc.size == 0)) {
+               heif_error("not exist icc profile");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       *icc = g_memdup(_image_item->icc.data, _image_item->icc.size);
+       *icc_size = _image_item->icc.size;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+// TODO: for only debug, this is no need for app developer. we can remove or replace it.
+void heif_itemtable_print_info(heif_itemtable_h handle)
+{
+       heif_itemtable_t *item_table = (heif_itemtable_t *)handle;
+
+       heif_retm_if_failed(handle);
+
+       // print image & exif item in table(context)
+       g_slist_foreach(item_table->image_items, __print_image_item, NULL);
+       g_slist_foreach(item_table->exif_items, __print_exif_item, NULL);
+}
+
+void heif_itemtable_destroy(heif_itemtable_h handle)
+{
+       heif_itemtable_t *item_table = (heif_itemtable_t *)handle;
+
+       heif_retm_if_failed(handle);
+
+       // release intermediate data
+       g_slist_free_full(item_table->item_infos, g_free);
+       g_slist_free_full(item_table->item_locs, _free_item_loc);
+       g_slist_free_full(item_table->properties, _free_image_prop);
+       g_slist_free_full(item_table->associations, g_free);
+       g_slist_free_full(item_table->item_refs, _free_item_ref);
+
+       g_slist_free(item_table->seen_types);
+       g_slist_free(item_table->require_types);
+
+       // release item table resource
+       g_slist_free(item_table->displayables);
+       g_slist_free_full(item_table->image_items, g_free);
+       g_slist_free_full(item_table->exif_items, g_free);
+
+       g_free(handle);
+}
+
+void heif_buffer_destroy(heif_buffer_t *buffer)
+{
+       heif_retm_if_failed(buffer);
+
+       g_free(buffer->data);
+       g_free(buffer);
+}
+
+static gint __compare_item_type(gconstpointer a, gconstpointer b)
+{
+       uint32_t item_type = (uint32_t)GPOINTER_TO_UINT(a);
+       uint32_t get_type = (uint32_t)GPOINTER_TO_UINT(b);
+
+       if (item_type > get_type)
+               return 1;
+
+       if (item_type < get_type)
+               return -1;
+
+       return 0;
+}
+
+static gint __compare_image_item_id(gconstpointer data, gconstpointer user_data)
+{
+       _image_item_t *image_item = (_image_item_t *)data;
+       uint32_t get_id = (uint32_t)GPOINTER_TO_UINT(user_data);
+
+       heif_retvm_if_failed(image_item, 0, "invalid data");
+
+       return (image_item->item_id == get_id) ? 0 : 1;
+}
+
+static gint __compare_iloc_item_id(gconstpointer data, gconstpointer user_data)
+{
+       _item_loc_t *item_loc = (_item_loc_t *)data;
+       uint32_t get_id = (uint32_t)GPOINTER_TO_UINT(user_data);
+
+       heif_retvm_if_failed(item_loc, 0, "invalid data");
+
+       return (item_loc->item_id == get_id) ? 0 : 1;
+}
+
+static void __print_ref_item(gpointer data, gpointer user_data)
+{
+       uint32_t item_id = (uint32_t)GPOINTER_TO_UINT(data);
+       char *msg = (char *)user_data;
+
+       heif_info("%s : %u", msg, item_id);
+}
+
+// TODO: for only debug, this is no need for app developer. we can remove or replace it.
+static void __print_image_item(gpointer data, gpointer user_data)
+{
+       _image_item_t *image_item = (_image_item_t *)data;
+       const char *type = image_item->type == FOURCC('g', 'r', 'i', 'd') ? "GRID" : "IMAGE";
+
+       heif_retm_if_failed(data);
+
+       heif_info("=============== [%s/%u] ==============", type, image_item->item_id);
+
+       heif_info("hidden : %s", image_item->hidden ? "hidden" : "displayable (primary)");
+       if (image_item->is_grid) {
+               heif_info("rows : %d", image_item->rows);
+               heif_info("columns : %d", image_item->columns);
+       }
+       heif_info("-----------------------------");
+       heif_info("width : %d", image_item->width);
+       heif_info("height : %d", image_item->height);
+       heif_info("rotation : %d", image_item->rotation * 90);
+       heif_info("-----------------------------");
+
+       heif_info("offset : %jd", image_item->offset);
+       heif_info("size : %zu", image_item->size);
+
+       heif_info("-----------------------------");
+       if (image_item->thumbnails)
+               g_slist_foreach(image_item->thumbnails, __print_ref_item, "thumbnail");
+       else
+               heif_info("thumbnail : none");
+
+       heif_info("-----------------------------");
+       if (image_item->dimg_refs)
+               g_slist_foreach(image_item->dimg_refs, __print_ref_item, "dimg_refs");
+       else
+               heif_info("dimg_refs : none");
+
+       heif_info("-----------------------------");
+       if (image_item->cdsc_refs)
+               g_slist_foreach(image_item->cdsc_refs, __print_ref_item, "cdsc_refs");
+       else
+               heif_info("cdsc_refs : none");
+
+#ifdef __DEBUG_CODEC_CONFIG
+       heif_info("-----------------------------");
+       {
+               size_t i = 0;
+               if (image_item->hvc_config.nal_unit.data && image_item->hvc_config.nal_unit.size > 0) {
+                       heif_info("nal_unit.size: %zu", image_item->hvc_config.nal_unit.size);
+                       for (i = 0; i < image_item->hvc_config.nal_unit.size; i++)
+                               heif_info("nal_unit: %x", image_item->hvc_config.nal_unit.data[i]);
+               }
+       }
+#endif
+
+       heif_info("=============== [%s] End ==============\n", type);
+}
+
+// TODO: for only debug, this is no need for app developer. we can remove or replace it.
+static void __print_exif_item(gpointer data, gpointer user_data)
+{
+       _image_item_t *image_item = (_image_item_t *)data;
+
+       heif_retm_if_failed(data);
+
+       heif_info("=============== [EXIF/%u] ==============", image_item->item_id);
+
+       heif_info("offset : %jd", image_item->offset);
+       heif_info("size : %zu", image_item->size);
+
+       heif_info("=============== [EXIF] End ==============\n");
+}
+
+// convert property to nal unit
+static bool __get_hvc_config_from_hvcC(_hvcC_prop_t *hvcC, _hvc_config_t *config)
+{
+       off_t hvcc_offset = 22; // nal start with 22
+       off_t total_data_size = 0;
+
+       uint8_t config_n = 0;
+       uint16_t unit_n = 0;
+       uint16_t unit_size = 0;
+       uint16_t cfg_idx = 0, unit_idx = 0;
+
+#ifdef __DEBUG_CODEC_CONFIG
+       heif_info("get nal unit from hvcC property size: %zu", hvcC->size);
+#endif
+
+       heif_retvm_if_failed(hvcC, false , "invalid hvcC");
+       heif_retvm_if_failed(hvcC->data && hvcC->size > 22, false , "invalid hvcC");
+       heif_retvm_if_failed(config, false , "invalid config");
+
+       config_n = (uint8_t)hvcC->data[hvcc_offset];
+       hvcc_offset += 1;
+#ifdef __DEBUG_CODEC_CONFIG
+       heif_info("read config_n: %u", config_n);
+#endif
+
+       for (cfg_idx = 0; cfg_idx < config_n; cfg_idx++) {
+#ifdef __DEBUG_CODEC_CONFIG
+               heif_info("read config: %x", hvcC->data[hvcc_offset]);
+#endif
+
+               CHECK_HVCC_OFFSET(hvcC, hvcc_offset + 1);
+               config->completeness = (hvcC->data[hvcc_offset] >> 6) & 1;
+               config->unit_type = (hvcC->data[hvcc_offset] & 0x3F);
+               hvcc_offset += 1;
+
+               CHECK_HVCC_OFFSET(hvcC, hvcc_offset + 2);
+               unit_n = (((uint8_t)hvcC->data[hvcc_offset]) << 8) + ((uint8_t)hvcC->data[hvcc_offset + 1]);
+               hvcc_offset += 2;
+
+#ifdef __DEBUG_CODEC_CONFIG
+               heif_info("read unit_n: %u", unit_n);
+#endif
+
+               for (unit_idx = 0; unit_idx < unit_n; unit_idx++) {
+                       CHECK_HVCC_OFFSET(hvcC, hvcc_offset + 2);
+                       unit_size = (((uint8_t)hvcC->data[hvcc_offset]) << 8) + ((uint8_t)hvcC->data[hvcc_offset + 1]);
+                       hvcc_offset += 2;
+
+#ifdef __DEBUG_CODEC_CONFIG
+                       heif_info("read unit_size: %u", unit_size);
+#endif
+
+                       if (unit_size == 0)
+                               continue;
+
+                       CHECK_HVCC_OFFSET(hvcC, hvcc_offset + unit_size);
+                       if (config->nal_unit.data)
+                               config->nal_unit.data = g_realloc(config->nal_unit.data, total_data_size + START_CODE_LEN + unit_size);
+                       else
+                               config->nal_unit.data = g_malloc0(START_CODE_LEN + unit_size);
+
+                       memcpy(config->nal_unit.data + total_data_size, START_CODE, START_CODE_LEN);
+                       total_data_size += START_CODE_LEN;
+                       memcpy(config->nal_unit.data + total_data_size, hvcC->data + hvcc_offset, unit_size);
+                       total_data_size += unit_size;
+
+                       hvcc_offset += unit_size;
+               }
+       }
+
+       config->nal_unit.size = total_data_size;
+
+       return true;
+}
+
+static void __attach_property(_association_t *association, GSList *image_items, GSList *properties)
+{
+       GSList *found = NULL;
+       uint16_t property_index = 0;
+       _image_item_t *image_item = NULL;
+       _item_prop_t *item_prop = NULL;
+#ifdef __DEBUG_CODEC_CONFIG
+       size_t i = 0;
+#endif
+
+       heif_retm_if_failed(association);
+       heif_retm_if_failed(image_items);
+       heif_retm_if_failed(properties);
+
+       // ignore non-image items
+       found = g_slist_find_custom(image_items, GUINT_TO_POINTER(association->item_id), __compare_image_item_id);
+       if (!found) {
+               heif_warning("ignoring non-image items!");
+               return;
+       }
+
+       // property index starts with 1
+       property_index = association->index;
+       // check underflow
+       if (property_index < 1) {
+               heif_warning("ignoring invalid property index %u", property_index);
+               return;
+       }
+       // check overflow
+       if (property_index > g_slist_length(properties)) {
+               heif_warning("ignoring invalid property index %u, list_length %u", property_index, g_slist_length(properties));
+               return;
+       }
+
+#ifdef __ENABLE_DEBUG_MODE
+       heif_info("attach property_index %u to item id %u", property_index, association->item_id);
+#endif
+
+       image_item = (_image_item_t *)found->data;
+       // The index of 'properties' starts with 0, but 'property_index' starts with 1.
+       // so '-1' needs for the difference between 'property_index' and the index of 'properties'.
+       item_prop = (_item_prop_t *)g_slist_nth_data(properties, property_index - 1);
+
+       // ignore non-image items
+       if (!item_prop || !item_prop->property) {
+               heif_warning("ignoring invalid property index %u", property_index);
+               return;
+       }
+
+       DEBUG_CHUNK_TYPE("property", item_prop->type);
+
+       switch (item_prop->type) {
+       case FOURCC('h', 'v', 'c', 'C'):
+               // get nal_unit for hevc decoder
+               if (!__get_hvc_config_from_hvcC((_hvcC_prop_t *)item_prop->property, &image_item->hvc_config))
+                       heif_error("__get_hvc_config_from_hvcC failed");
+
+#ifdef __DEBUG_CODEC_CONFIG
+               if (image_item->hvc_config.nal_unit.data && image_item->hvc_config.nal_unit.size > 0) {
+                       for (i = 0; i < image_item->hvc_config.nal_unit.size; i++)
+                               heif_info("nal_unit: %x", image_item->hvc_config.nal_unit.data[i]);
+               }
+#endif
+               break;
+
+       case FOURCC('i', 's', 'p', 'e'):
+               image_item->width = ((_ispe_prop_t *)item_prop->property)->width;
+               image_item->height = ((_ispe_prop_t *)item_prop->property)->height;
+               break;
+
+       case FOURCC('i', 'r', 'o', 't'):
+               image_item->rotation = ((_irot_prop_t *)item_prop->property)->angle;
+               break;
+
+       case FOURCC('c', 'o', 'l', 'r'):
+               image_item->icc.data = ((_colr_prop_t *)item_prop->property)->icc_data;
+               image_item->icc.size = ((_colr_prop_t *)item_prop->property)->size;
+               break;
+
+       case FOURCC('i', 'm', 'i', 'r'):
+               // vertial(axis = 0) or horizontal(axis = 1) in property
+               image_item->has_mirror = true;
+               image_item->axis = ((_imir_prop_t *)item_prop->property)->axis;
+               break;
+
+       default:
+               break;
+       }
+}
+
+static void __attach_refs(_item_ref_t *item_ref, GSList *image_items, GSList *exif_items)
+{
+       GSList *iter = NULL, *found = NULL;
+       _image_item_t *ref_item = NULL;
+
+       heif_info("attach reference type 0x%x to item id %u", item_ref->item_type, item_ref->item_id);
+
+       heif_retm_if_failed(item_ref);
+       heif_retm_if_failed(image_items);
+
+       switch (item_ref->item_type) {
+       case FOURCC('d', 'i', 'm', 'g'):
+               found = g_slist_find_custom(image_items, GUINT_TO_POINTER(item_ref->item_id), __compare_image_item_id);
+
+               // ignore non-image items
+               if (!found) {
+                       heif_warning("ignoring non-image items!");
+                       return;
+               }
+
+               _image_item_t *derived_image = (_image_item_t *)found->data;
+               if (!derived_image->dimg_refs)
+                       heif_warning("dimg_refs not clean!");
+
+               derived_image->dimg_refs = g_slist_copy(item_ref->refs);
+
+               // hide reference sources
+               for (iter = item_ref->refs; iter; iter = g_slist_next(iter)) {
+                       found = g_slist_find_custom(image_items, GUINT_TO_POINTER(iter->data), __compare_image_item_id);
+
+                       // ignore non-image items
+                       if (!found) {
+                               heif_warning("ignoring non-image items!");
+                               continue;
+                       }
+                       heif_info("Item(%u) is the derived image of item(%u)", GPOINTER_TO_UINT(iter->data), item_ref->item_id);
+                       ref_item = (_image_item_t *)found->data;
+
+                       // mark the derived image as hidden
+                       ref_item->hidden = true;
+               }
+               break;
+
+       case FOURCC('t', 'h', 'm', 'b'):
+               found = g_slist_find_custom(image_items, GUINT_TO_POINTER(item_ref->item_id), __compare_image_item_id);
+
+               // ignore non-image items
+               if (!found) {
+                       heif_warning("ignoring non-image items!");
+                       return;
+               }
+
+               // mark thumbnail image as hidden, these can be retrieved if the client
+               // request thumbnail explicitly, but won't be exposed as displayables.
+               _image_item_t *thumb_image = (_image_item_t *)found->data;
+               thumb_image->hidden = true;
+
+               for (iter = item_ref->refs; iter; iter = g_slist_next(iter)) {
+                       found = g_slist_find_custom(image_items, GUINT_TO_POINTER(iter->data), __compare_image_item_id);
+
+                       // ignore non-image items
+                       if (!found) {
+                               heif_warning("ignoring non-image items!");
+                               continue;
+                       }
+                       heif_info("Item(%u) is the thumbnail of item(%u)", item_ref->item_id, GPOINTER_TO_UINT(iter->data));
+                       ref_item = (_image_item_t *)found->data;
+                       if (g_slist_length(ref_item->thumbnails) > 0)
+                               heif_warning("already has thumbnails!");
+
+                       ref_item->thumbnails = g_slist_append(ref_item->thumbnails, GUINT_TO_POINTER(item_ref->item_id));
+               }
+               break;
+
+       case FOURCC('c', 'd', 's', 'c'):
+               heif_retm_if_failed(exif_items);
+
+               found = g_slist_find_custom(exif_items, GUINT_TO_POINTER(item_ref->item_id), __compare_image_item_id);
+
+               // ignore non-exif block items
+               if (!found) {
+                       heif_warning("ignoring non-exif items!");
+                       return;
+               }
+
+               for (iter = item_ref->refs; iter; iter = g_slist_next(iter)) {
+                       found = g_slist_find_custom(image_items, GUINT_TO_POINTER(iter->data), __compare_image_item_id);
+
+                       // ignore non-image items
+                       if (!found) {
+                               heif_warning("ignoring non-image items!");
+                               continue;
+                       }
+                       heif_info("Item(%u) is the Content-Description of item(%u)", item_ref->item_id, GPOINTER_TO_UINT(iter->data));
+                       ref_item = (_image_item_t *)found->data;
+
+                       ref_item->cdsc_refs = g_slist_append(ref_item->cdsc_refs, GUINT_TO_POINTER(item_ref->item_id));
+               }
+               break;
+
+       case FOURCC('a', 'u', 'x', 'l'):
+               found = g_slist_find_custom(image_items, GUINT_TO_POINTER(item_ref->item_id), __compare_image_item_id);
+
+               // ignore non-image block items
+               if (!found) {
+                       heif_warning("ignoring non-image items!");
+                       return;
+               }
+
+               // mark the auxiliary image as hidden
+               ref_item = (_image_item_t *)found->data;
+               ref_item->hidden = true;
+               break;
+
+       default:
+               heif_warning("ignoring unsupported ref type 0x%x", item_ref->item_type);
+               break;
+       }
+}
+
+static bool __has_all_require_types(GSList *seen_types, GSList *require_types)
+{
+       GSList *seen_iter = NULL, *require_iter = NULL;
+
+       heif_retvm_if_failed(seen_types, false, "invalid seen_types");
+       heif_retvm_if_failed(require_types, false, "invalid require_types");
+
+       seen_iter = seen_types;
+       require_iter = require_types;
+
+       // cond. Both seen_types and require_types are sorted.
+       for (; seen_iter && require_iter; seen_iter = g_slist_next(seen_iter)) {
+               uint32_t seen_type = (uint32_t)GPOINTER_TO_UINT(seen_iter->data);
+               uint32_t require_type = (uint32_t)GPOINTER_TO_UINT(require_iter->data);
+
+               if (seen_type == require_type)
+                       require_iter = g_slist_next(require_iter);
+       }
+
+       // need at least 'iprp', 'iloc', 'pitm', 'iinf';
+       // need 'idat' if any items used construction_method of 2;
+       // need 'iref' if source has 'grid' item.
+       if (require_iter) {
+               DEBUG_CHUNK_TYPE("Not Found!", (uint32_t)GPOINTER_TO_UINT(require_iter->data));
+               return false;
+       }
+
+       return true;
+}
+
+// build image items
+static int __build_image_if_possible(heif_itemtable_t *item_table)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       GSList *iter = NULL;
+       GSList *found = NULL; // for check duplicate or exist, so it is reused for some lists.
+       _image_item_t *image_item;
+       off_t offset = 0;
+       size_t size = 0;
+
+       if (item_table->is_valid) {
+               heif_warning("already item_table has been built!");
+               return LIBHEIF_ERROR_NONE;
+       }
+
+       if (!__has_all_require_types(item_table->seen_types, item_table->require_types))
+               return LIBHEIF_ERROR_NONE;
+
+       heif_warning("building image table...");
+
+       // insert image items
+       for (iter = item_table->item_infos; iter; iter = g_slist_next(iter)) {
+               _item_info_t *item_info = (_item_info_t *)iter->data;
+
+               // Only handle 3 types of items, all others are ignored:
+               //       'grid': derived image from tiles
+               //       'hvc1': coded image (or tile)
+               //       'Exif': EXIF metadata
+               if (item_info->item_type != FOURCC('g', 'r', 'i', 'd') &&
+                       item_info->item_type != FOURCC('h', 'v', 'c', '1') &&
+                       item_info->item_type != FOURCC('E', 'x', 'i', 'f')) {
+                       continue;
+               }
+
+               DEBUG_CHUNK_TYPE("Build/ItemType", item_info->item_type);
+
+               found = g_slist_find_custom(item_table->image_items, GUINT_TO_POINTER(item_info->item_id), __compare_image_item_id);
+               if (found) {
+                       heif_warning("ignoring duplicate image item id %u", item_info->item_id);
+                       continue;
+               }
+
+               found = g_slist_find_custom(item_table->item_locs, GUINT_TO_POINTER(item_info->item_id), __compare_iloc_item_id);
+               if (!found) {
+                       heif_warning("iloc missing for image item id %u", item_info->item_id);
+                       continue;
+               }
+
+               // get location from iloc box
+               ret = _get_item_loc((_item_loc_t*)found->data, &offset, &size, item_table->idat_offset, item_table->idat_size);
+               if (ret != LIBHEIF_ERROR_NONE) {
+                       heif_error("location of item was missed %d", ret);
+                       return ret;
+               }
+
+               if (item_info->item_type == FOURCC('E', 'x', 'i', 'f')) {
+                       // Only add if the Exif data is non-empty. The first 4 bytes contain
+                       // the offset to TIFF header, which the Exif parser doesn't use.
+                       if (size > 4) {
+                               heif_info("adding Exif information");
+
+                               _image_item_t *exif_item = g_new0(_image_item_t, 1);
+                               exif_item->type = item_info->item_type;
+                               exif_item->item_id = item_info->item_id;
+                               // just keep offset and size, no read exif immediately
+                               exif_item->offset = offset;
+                               exif_item->size = size;
+
+                               item_table->exif_items = g_slist_append(item_table->exif_items, exif_item);
+                       }
+                       continue;
+               }
+
+               image_item = g_new0(_image_item_t, 1);
+               image_item->type = item_info->item_type;
+               image_item->item_id = item_info->item_id;
+               image_item->hidden = item_info->hidden;
+               image_item->is_grid = (image_item->type == FOURCC('g', 'r', 'i', 'd'));
+
+#ifdef __ENABLE_DEBUG_MODE
+               heif_info("adding %s: item_id %u", image_item->is_grid ? "grid" : "image", image_item->item_id);
+#endif
+
+               if (image_item->is_grid) {
+                       // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1)
+                       if (size < 8 || size > 12) {
+                               g_free(image_item);
+                               return LIBHEIF_ERROR_INVALID_PARAMETER;
+                       }
+                       uint8_t buf[12];
+                       if (heif_source_read_at(item_table->source, offset, buf, size) != size) {
+                               g_free(image_item);
+                               return LIBHEIF_ERROR_INVALID_PARAMETER;
+                       }
+
+                       image_item->rows = buf[2] + 1;
+                       image_item->columns = buf[3] + 1;
+
+                       item_table->has_grid = true;
+                       heif_info("rows %d, columns %d", image_item->rows, image_item->columns);
+               } else {
+                       image_item->offset = offset;
+                       image_item->size = size;
+               }
+
+               item_table->image_items = g_slist_append(item_table->image_items, image_item);
+       }
+
+       // attach property(width&height/hvcC/rotation...) for each image item
+       for (iter = item_table->associations; iter; iter = g_slist_next(iter))
+               __attach_property((_association_t *)iter->data, item_table->image_items, item_table->properties);
+
+       // attach reference(grid/exif/thumb) for each image item
+       for (iter = item_table->item_refs; iter; iter = g_slist_next(iter))
+               __attach_refs((_item_ref_t *)iter->data, item_table->image_items, item_table->exif_items);
+
+       // check primary image
+       found = g_slist_find_custom(item_table->image_items, GUINT_TO_POINTER(item_table->primary_id), __compare_image_item_id);
+       if (!found) {
+               heif_error("can't find item of primary id");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       item_table->displayables = g_slist_append(item_table->displayables, GUINT_TO_POINTER(item_table->primary_id));
+
+       heif_info("found %u displayables", g_slist_length(item_table->displayables));
+
+       // fail if no displayables
+       if (!item_table->displayables) {
+               heif_error("can't find displayable item");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       item_table->is_valid = true;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __heif_itemtable_parse_iloc_box(heif_itemtable_t *item_table, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_box_iloc_h iloc_box = NULL;
+       heif_info("offset %jd, size %zu", offset, size);
+
+       heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table");
+
+       ret = heif_box_iloc_create(&iloc_box);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iloc_create fail (%d)", ret);
+               return ret;
+       }
+
+       ret = heif_box_iloc_parse(iloc_box, item_table->source, offset, size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iloc_parse fail (%d)", ret);
+               goto END;
+       }
+
+       item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'l', 'o', 'c')), __compare_item_type);
+
+       ret = heif_box_iloc_get_item_locs(iloc_box, &item_table->item_locs);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iloc_get_item_locs fail (%d)", ret);
+               goto END;
+       }
+
+       if (heif_box_iloc_has_construction_method(iloc_box))
+               item_table->require_types = g_slist_insert_sorted(item_table->require_types, GUINT_TO_POINTER(FOURCC('i', 'd', 'a', 't')), __compare_item_type);
+
+END:
+#ifdef __ENABLE_DEBUG_MODE
+       heif_box_iloc_info(iloc_box);
+#endif
+       heif_box_iloc_destroy(iloc_box);
+       iloc_box = NULL;
+
+       if (ret != LIBHEIF_ERROR_NONE)
+               return ret;
+
+       return __build_image_if_possible(item_table);
+}
+
+static int __heif_itemtable_parse_iinf_box(heif_itemtable_t *item_table, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_box_iinf_h iinf_box = NULL;
+       heif_info("offset %jd, size %zu", offset, size);
+
+       heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table");
+
+       ret = heif_box_iinf_create(&iinf_box);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iinf_create fail (%d)", ret);
+               return ret;
+       }
+
+       ret = heif_box_iinf_parse(iinf_box, item_table->source, offset, size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iinf_parse fail (%d)", ret);
+               goto END;
+       } else {
+               item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'i', 'n', 'f')), __compare_item_type);
+       }
+
+       ret = heif_box_iinf_get_item_infos(iinf_box, &item_table->item_infos);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iinf_get_item_infos fail (%d)", ret);
+               goto END;
+       }
+
+       if (heif_box_iinf_has_type(iinf_box, FOURCC('g', 'r', 'i', 'd')) ||
+               heif_box_iinf_has_type(iinf_box, FOURCC('E', 'x', 'i', 'f'))) {
+               item_table->require_types = g_slist_insert_sorted(item_table->require_types, GUINT_TO_POINTER(FOURCC('i', 'r', 'e', 'f')), __compare_item_type);
+       }
+
+END:
+#ifdef __ENABLE_DEBUG_MODE
+       heif_box_iinf_info(iinf_box);
+#endif
+       heif_box_iinf_destroy(iinf_box);
+       iinf_box = NULL;
+
+       if (ret != LIBHEIF_ERROR_NONE)
+               return ret;
+
+       return __build_image_if_possible(item_table);
+}
+
+static int __heif_itemtable_parse_iprp_box(heif_itemtable_t *item_table, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_box_iprp_h iprp_box;
+       heif_info("offset %jd, size %zu", offset, size);
+
+       heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table");
+
+       ret = heif_box_iprp_create(&iprp_box);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iprp_create fail (%d)", ret);
+               return ret;
+       }
+
+       ret = heif_box_iprp_parse(iprp_box, item_table->source, offset, size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iprp_parse fail (%d)", ret);
+               goto END;
+       } else {
+               item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'p', 'r', 'p')), __compare_item_type);
+       }
+
+       ret = heif_box_iprp_get_properties(iprp_box, &item_table->properties);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iprp_get_properties fail (%d)", ret);
+               goto END;
+       }
+
+       ret = heif_box_iprp_get_associations(iprp_box, &item_table->associations);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iprp_get_associations fail (%d)", ret);
+               goto END;
+       }
+
+END:
+#ifdef __ENABLE_DEBUG_MODE
+       heif_box_iprp_info(iprp_box);
+#endif
+       heif_box_iprp_destroy(iprp_box);
+       iprp_box = NULL;
+
+       if (ret != LIBHEIF_ERROR_NONE)
+               return ret;
+
+       return __build_image_if_possible(item_table);
+}
+
+static int __heif_itemtable_parse_pitm_box(heif_itemtable_t *item_table, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_box_iprp_h pitm_box = NULL;
+       heif_info("offset %jd, size %zu", offset, size);
+
+       heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table");
+
+       ret = heif_box_pitm_create(&pitm_box);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_pitm_create fail (%d)", ret);
+               return ret;
+       }
+
+       ret = heif_box_pitm_parse(pitm_box, item_table->source, offset, size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_pitm_parse fail (%d)", ret);
+               goto END;
+       }
+
+       if (heif_box_pitm_get_primary_id(pitm_box, &item_table->primary_id) != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_pitm_get_primary_id fail (%d)", ret);
+               goto END;
+       }
+
+       item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('p', 'i', 't', 'm')), __compare_item_type);
+
+END:
+#ifdef __ENABLE_DEBUG_MODE
+       heif_box_pitm_info(pitm_box);
+#endif
+       heif_box_pitm_destroy(pitm_box);
+       pitm_box = NULL;
+
+       if (ret != LIBHEIF_ERROR_NONE)
+               return ret;
+
+       return __build_image_if_possible(item_table);
+}
+
+static int __heif_itemtable_parse_idat_box(heif_itemtable_t *item_table, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_box_idat_h idat_box = NULL;
+       heif_info("offset %jd, size %zu", offset, size);
+
+       heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table");
+
+       ret = heif_box_idat_create(&idat_box);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_idat_create fail (%d)", ret);
+               return ret;
+       }
+
+       ret = heif_box_idat_parse(idat_box, offset, size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_idat_parse fail (%d)", ret);
+               goto END;
+       }
+
+       item_table->idat_offset = 0;
+       item_table->idat_size = 0;
+       ret = heif_box_idat_get_idat(idat_box, &item_table->idat_offset, &item_table->idat_size);
+       // because idat is not mandatory, just warning and no return
+       if (ret != LIBHEIF_ERROR_NONE)
+               heif_warning("idat was missed");
+
+       item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'd', 'a', 't')), __compare_item_type);
+
+END:
+#ifdef __ENABLE_DEBUG_MODE
+       heif_box_idat_info(idat_box);
+#endif
+       heif_box_idat_destroy(idat_box);
+       idat_box = NULL;
+
+       if (ret != LIBHEIF_ERROR_NONE)
+               return ret;
+
+       return __build_image_if_possible(item_table);
+}
+
+static int __heif_itemtable_parse_iref_box(heif_itemtable_t *item_table, off_t offset, size_t size)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_box_iref_h iref_box = NULL;
+       heif_info("offset %jd, size %zu", offset, size);
+
+       heif_retvm_if_failed(item_table, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid item_table");
+
+       ret = heif_box_iref_create(&iref_box);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iref_create fail (%d)", ret);
+               return ret;
+       }
+
+       ret = heif_box_iref_parse(iref_box, item_table->source, offset, size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iref_parse fail (%d)", ret);
+               goto END;
+       }
+
+       ret = heif_box_iref_get_item_refs(iref_box, &item_table->item_refs);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("heif_box_iref_get_item_refs fail (%d)", ret);
+               goto END;
+       }
+
+       item_table->seen_types = g_slist_insert_sorted(item_table->seen_types, GUINT_TO_POINTER(FOURCC('i', 'r', 'e', 'f')), __compare_item_type);
+
+END:
+#ifdef __ENABLE_DEBUG_MODE
+       heif_box_iref_info(iref_box);
+#endif
+       heif_box_iref_destroy(iref_box);
+       iref_box = NULL;
+
+       if (ret != LIBHEIF_ERROR_NONE)
+               return ret;
+
+       return __build_image_if_possible(item_table);
+}
diff --git a/src/heif_source.c b/src/heif_source.c
new file mode 100644 (file)
index 0000000..99649a7
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <glib.h>
+#include <glib/gstdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include "heif_type.h"
+#include "heif_debug.h"
+#include "heif_source.h"
+
+typedef struct {
+       char *path;
+       int fd;
+       uint8_t *buf;
+       size_t size;
+       off_t cur_pos;
+} heif_source_t;
+
+static int __source_get_file_size(const char *path, size_t *size);
+static int __source_set_path(heif_source_t *source, const char *path);
+static int __source_set_buffer(heif_source_t *source, unsigned char *buf, size_t size);
+static void __source_free(heif_source_t *source);
+
+
+int heif_source_create_from_file(const char *path, heif_source_h *source)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_source_t *new_source = NULL;
+
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+#ifdef __DEBUG_SOURCE
+       heif_secure_debug("path: %s", path);
+#endif
+
+       new_source = g_new0(heif_source_t, 1);
+
+       ret = __source_set_path(new_source, path);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__source_set_path fail %d", ret);
+               g_free(new_source);
+               return ret;
+       }
+
+       *source = (heif_source_h)new_source;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+int heif_source_create_from_buffer(unsigned char *buf, size_t size, heif_source_h *source)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       heif_source_t *new_source = NULL;
+
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+#ifdef __DEBUG_SOURCE
+       heif_info("buf: %p buf_size: %zu", buf, size);
+#endif
+
+       new_source = g_new0(heif_source_t, 1);
+
+       ret = __source_set_buffer(new_source, buf, size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__source_set_buffer fail %d", ret);
+               g_free(new_source);
+               return ret;
+       }
+
+       *source = (heif_source_h)new_source;
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+size_t heif_source_get_size(heif_source_h source)
+{
+       heif_retvm_if_failed(source, 0, "invalid source");
+
+       return ((heif_source_t *)source)->size;
+}
+
+size_t heif_source_read_at(heif_source_h source, off_t offset, void *data, size_t size)
+{
+       size_t read_n = 0;
+       heif_source_t *_src = (heif_source_t *)source;
+
+       heif_retvm_if_failed(source, 0, "invalid source");
+       heif_retvm_if_failed(_src->path || _src->buf, 0, "invalid source data");
+       heif_retvm_if_failed(_src->size > 0, 0, "invalid source size");
+       heif_retvm_if_failed(data, 0, "invalid data");
+       heif_retvm_if_failed(size > 0, 0, "invalid size");
+
+       // check offset advance
+       // if it needs to check underflow, we can use '(_src->cur_pos + offset) < 0'
+       // checking offset advance is more efficient because '_src->cur_pos' is always >= 0.
+       heif_retvm_if_failed(offset >= 0, 0, "invalid offset: %jd", offset);
+
+       // check 'offset + size' overflow
+       heif_retvm_if_failed(offset + size <= _src->size, 0, "invalid offset: %jd, size: %zu", offset, size);
+
+#ifdef __DEBUG_SOURCE
+       heif_info("offset: %jd cur_pos: %jd size: %zu", offset, _src->cur_pos, size);
+#endif
+
+       if (_src->path) {
+               if (lseek(_src->fd, offset - _src->cur_pos, SEEK_CUR) < 0) {
+                       heif_strerror("lseek");
+                       return 0;
+               }
+
+               read_n = read(_src->fd, data, size);
+               if (read_n != size) {
+                       heif_strerror("read");
+                       return 0;
+               }
+       } else {
+               memcpy(data, _src->buf + offset, size);
+               read_n = size;
+       }
+
+       _src->cur_pos = offset + size;
+#ifdef __DEBUG_SOURCE
+       heif_info("updated read_at cur_pos: %jd", _src->cur_pos);
+#endif
+
+       return read_n;
+}
+
+void heif_source_destroy(heif_source_h source)
+{
+       __source_free((heif_source_t *)source);
+
+       g_free(source);
+}
+
+// Convenience functions:
+bool heif_source_get_uint16(heif_source_h source, off_t offset, uint16_t *x)
+{
+       uint8_t byte[2];
+
+       heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x");
+
+       *x = 0;
+
+       if (heif_source_read_at(source, offset, byte, 2) != 2)
+               return false;
+
+       *x = (byte[0] << 8) | byte[1];
+
+       return true;
+}
+
+// 3 byte int, returned as a 32-bit int
+bool heif_source_get_uint24(heif_source_h source, off_t offset, uint32_t *x)
+{
+       uint8_t byte[3];
+
+       heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x");
+
+       *x = 0;
+
+       if (heif_source_read_at(source, offset, byte, 3) != 3)
+               return false;
+
+       *x = (byte[0] << 16) | (byte[1] << 8) | byte[2];
+
+       return true;
+}
+
+bool heif_source_get_uint32(heif_source_h source, off_t offset, uint32_t *x)
+{
+       uint32_t tmp;
+
+       heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x");
+
+       *x = 0;
+
+       if (heif_source_read_at(source, offset, &tmp, 4) != 4)
+               return false;
+
+       *x = ntohl(tmp);
+
+       return true;
+}
+
+bool heif_source_get_uint64(heif_source_h source, off_t offset, uint64_t *x)
+{
+       uint64_t tmp;
+
+       heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x");
+
+       *x = 0;
+
+       if (heif_source_read_at(source, offset, &tmp, 8) != 8)
+               return false;
+
+       *x = ((uint64_t)ntohl(tmp & 0xffffffff) << 32) | ntohl(tmp >> 32);
+
+       return true;
+}
+
+// read either int<N> or int<2N> into a uint<2N>_t, size is the int size in bytes.
+bool heif_source_get_uint16var(heif_source_h source, off_t offset, uint16_t *x, size_t size)
+{
+       heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x");
+
+       if (size == 2)
+               return heif_source_get_uint16(source, offset, x);
+
+       if (size == 1) {
+               uint8_t tmp;
+               if (heif_source_read_at(source, offset, &tmp, 1) == 1) {
+                       *x = tmp;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+bool heif_source_get_uint32var(heif_source_h source, off_t offset, uint32_t *x, size_t size)
+{
+       heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x");
+
+       if (size == 4)
+               return heif_source_get_uint32(source, offset, x);
+
+       if (size == 2) {
+               uint16_t tmp;
+               if (heif_source_get_uint16(source, offset, &tmp)) {
+                       *x = tmp;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+bool heif_source_get_uint64var(heif_source_h source, off_t offset, uint64_t *x, size_t size)
+{
+       heif_retvm_if_failed(x, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid x");
+
+       if (size == 8)
+               return heif_source_get_uint64(source, offset, x);
+
+       if (size == 4) {
+               uint32_t tmp;
+               if (heif_source_get_uint32(source, offset, &tmp)) {
+                       *x = tmp;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static int __source_get_file_size(const char *path, size_t *size)
+{
+       GStatBuf buf;
+
+       heif_retvm_if_failed(path, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid path");
+       heif_retvm_if_failed(size, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size");
+
+#ifdef __DEBUG_SOURCE
+       heif_debug_fenter();
+#endif
+
+       if (g_stat(path, &buf) < 0) {
+               if (errno == EACCES || errno == EPERM) {
+                       heif_error("Fail to open path[%s] due to permission denied", path);
+                       return LIBHEIF_ERROR_PERMISSION_DENIED;
+               } else {
+                       heif_error("Fail to open path[%s] due to invalid path", path);
+                       return LIBHEIF_ERROR_INVALID_PARAMETER;
+               }
+       }
+
+       *size = (size_t)buf.st_size;
+
+       heif_info("size: %zu", *size);
+
+#ifdef __DEBUG_SOURCE
+       heif_debug_fleave();
+#endif
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __source_set_path(heif_source_t *source, const char *path)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(path, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid path");
+
+#ifdef __DEBUG_SOURCE
+       heif_debug_fenter();
+#endif
+
+       ret = __source_get_file_size(path, &source->size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               heif_error("__source_get_file_size fail %d", ret);
+               return ret;
+       }
+
+       if (source->size == 0) {
+               heif_error("invalid size %zu", source->size);
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       source->fd = open(path, O_RDONLY);
+       if (source->fd < 0) {
+               heif_strerror("open");
+               return LIBHEIF_ERROR_INVALID_PARAMETER;
+       }
+
+       source->path = g_strdup(path);
+
+#ifdef __DEBUG_SOURCE
+       heif_info("path: %s, size: %zu", source->path, source->size);
+       heif_debug_fleave();
+#endif
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __source_set_buffer(heif_source_t *source, unsigned char *buf, size_t size)
+{
+       heif_retvm_if_failed(source, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid source");
+       heif_retvm_if_failed(buf, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid buf");
+       heif_retvm_if_failed(size > 0, LIBHEIF_ERROR_INVALID_PARAMETER, "invalid size");
+
+#ifdef __DEBUG_SOURCE
+       heif_debug_fenter();
+#endif
+
+       source->buf = (uint8_t *)g_memdup(buf, size);
+       source->size = size;
+
+#ifdef __DEBUG_SOURCE
+       heif_info("buf: %p, size: %zu", source->buf, source->size);
+       heif_debug_fleave();
+#endif
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static void __source_free(heif_source_t *source)
+{
+       heif_retm_if_failed(source);
+#ifdef __DEBUG_SOURCE
+       heif_debug_fenter();
+#endif
+
+       g_free(source->path);
+       source->path = NULL;
+
+       if (source->fd >= 0) {
+               close(source->fd);
+               source->fd = -1;
+       }
+
+       g_free(source->buf);
+       source->buf = NULL;
+
+#ifdef __DEBUG_SOURCE
+       heif_debug_fleave();
+#endif
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f5118ab
--- /dev/null
@@ -0,0 +1,18 @@
+SET(fw_name "libheif")
+SET(fw_test "${fw_name}-test")
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(${fw_test} REQUIRED glib-2.0 dlog mmutil-common mmutil-jpeg mmutil-magick)
+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})
+ENDFOREACH()
diff --git a/test/heif_testsuite.c b/test/heif_testsuite.c
new file mode 100644 (file)
index 0000000..116f6c7
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * libheif
+ *
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Jiyong Min <jiyong.min@samsung.com>
+ *
+ * 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 <glib.h>
+
+#include <mm_util_image.h>
+#include <mm_util_jpeg.h>
+#include <mm_util_magick.h>
+
+#include <heif.h>
+#include <heif_source.h>
+#include <heif_extractor.h>
+#include <heif_decoder.h>
+
+typedef enum {
+       TEST_AUTO,
+       TEST_GETINFO_FILE,
+       TEST_GETINFO_MEMORY,
+       TEST_DECODE_FILE,
+       TEST_DECODE_MEMORY,
+       TEST_THUMB_FILE,
+       TEST_THUMB_MEMORY,
+       TEST_NUM,
+} heif_test_mode_e;
+
+static const char *g_test_decode_file_jpeg[] = {
+       [HEIF_COLOR_FORMAT_NONE] = NULL,
+       [HEIF_COLOR_FORMAT_YUV420P] = "/opt/usr/home/owner/media/test_heif_file_yuv420p.jpg",
+       [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_file_rgb24.jpg",
+       [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_file_argb.jpg",
+       [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_file_bgra.jpg",
+       [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_file_rgba.jpg",
+};
+static const char *g_test_decode_file_png[] = {
+       [HEIF_COLOR_FORMAT_NONE] = NULL,
+       [HEIF_COLOR_FORMAT_YUV420P] = NULL,     // not supported yuv420p in png
+       [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_file_rgb24.png",
+       [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_file_argb.png",
+       [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_file_bgra.png",
+       [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_file_rgba.png",
+};
+static const char *g_test_decode_buff_jpeg[] = {
+       [HEIF_COLOR_FORMAT_NONE] = NULL,
+       [HEIF_COLOR_FORMAT_YUV420P] = "/opt/usr/home/owner/media/test_heif_buff_yuv420p.jpg",
+       [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_buff_rgb24.jpg",
+       [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_buff_argb.jpg",
+       [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_buff_bgra.jpg",
+       [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_buff_rgba.jpg",
+};
+static const char *g_test_decode_buff_png[] = {
+       [HEIF_COLOR_FORMAT_NONE] = NULL,
+       [HEIF_COLOR_FORMAT_YUV420P] = NULL,
+       [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_buff_rgb24.png",
+       [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_buff_argb.png",
+       [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_buff_bgra.png",
+       [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_buff_rgba.png",
+};
+static const char *g_test_thumb_file_jpeg[] = {
+       [HEIF_COLOR_FORMAT_NONE] = NULL,
+       [HEIF_COLOR_FORMAT_YUV420P] = "/opt/usr/home/owner/media/test_heif_thumb_file_yuv420p.jpg",
+       [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_thumb_file_rgb24.jpg",
+       [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_thumb_file_argb.jpg",
+       [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_thumb_file_bgra.jpg",
+       [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_thumb_file_rgba.jpg",
+};
+static const char *g_test_thumb_file_png[] = {
+       [HEIF_COLOR_FORMAT_NONE] = NULL,
+       [HEIF_COLOR_FORMAT_YUV420P] = NULL,
+       [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_thumb_file_rgb24.png",
+       [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_thumb_file_argb.png",
+       [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_thumb_file_bgra.png",
+       "/opt/usr/home/owner/media/test_heif_thumb_file_rgba.png",
+};
+static const char *g_test_thumb_buff_jpeg[] = {
+       [HEIF_COLOR_FORMAT_NONE] = NULL,
+       [HEIF_COLOR_FORMAT_YUV420P] = "/opt/usr/home/owner/media/test_heif_thumb_buff_yuv420p.jpg",
+       [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_thumb_buff_rgb24.jpg",
+       [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_thumb_buff_argb.jpg",
+       [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_thumb_buff_bgra.jpg",
+       [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_thumb_buff_rgba.jpg",
+};
+static const char *g_test_thumb_buff_png[] = {
+       [HEIF_COLOR_FORMAT_NONE] = NULL,
+       [HEIF_COLOR_FORMAT_YUV420P] = NULL,
+       [HEIF_COLOR_FORMAT_RGB24] = "/opt/usr/home/owner/media/test_heif_thumb_buff_rgb24.png",
+       [HEIF_COLOR_FORMAT_ARGB] = "/opt/usr/home/owner/media/test_heif_thumb_buff_argb.png",
+       [HEIF_COLOR_FORMAT_BGRA] = "/opt/usr/home/owner/media/test_heif_thumb_buff_bgra.png",
+       [HEIF_COLOR_FORMAT_RGBA] = "/opt/usr/home/owner/media/test_heif_thumb_buff_rgba.png",
+};
+
+static int g_test_mode = TEST_AUTO;
+static char *g_path = NULL;
+static int g_color = HEIF_COLOR_FORMAT_RGB24;
+
+// for only test
+static void __print_help(const char *argv0);
+static bool __get_input_data(const char *argv, const long min, const long max, int *data);
+static bool __get_arguments(int argc, char *argv[]);
+// library tests
+static bool __test_auto(void);
+static bool __test_get_info(int mode);
+static bool __test_decode(int mode);
+static bool __test_thumb(int mode);
+static int __get_image_attrs(heif_image_info_h image_info);
+// encode image using libmm-utility
+static int __convert_heif_image_to_mm_image(heif_image_h heif_image, mm_util_image_h *mm_image);
+static int __save_to_jpeg(heif_image_h source, const char *path);
+static int __save_to_png(heif_image_h source, const char *path);
+
+
+int main(int argc, char *argv[])
+{
+       if (argc < 2) {
+               __print_help(argv[0]);
+               return 0;
+       }
+
+       if (false == __get_arguments(argc, argv)) {
+               g_print("\t[HEIF_testsuite] _get_arguments failed\n");
+               goto END;
+       }
+
+       /* test all functions automatically */
+       if (g_test_mode == TEST_AUTO) {
+               if (false == __test_auto())
+                       g_print("\t[HEIF_testsuite] _test_auto failed\n");
+               goto END;
+       }
+
+       /* test of getting image information from file or memory */
+       if (g_test_mode == TEST_GETINFO_FILE || g_test_mode == TEST_GETINFO_MEMORY) {
+               if (false == __test_get_info(g_test_mode))
+                       g_print("\t[HEIF_testsuite] __test_decode failed\n");
+               goto END;
+       }
+
+       /* test of decoding image from file or memory */
+       if (g_test_mode == TEST_DECODE_FILE || g_test_mode == TEST_DECODE_MEMORY) {
+               if (false == __test_decode(g_test_mode))
+                       g_print("\t[HEIF_testsuite] __test_decode failed\n");
+               goto END;
+       }
+
+       /* test of getting thumbnail from file or memory */
+       if (g_test_mode == TEST_THUMB_FILE || g_test_mode == TEST_THUMB_MEMORY) {
+               if (false == __test_thumb(g_test_mode))
+                       g_print("\t[HEIF_testsuite] __test_thumb failed\n");
+               goto END;
+       }
+
+END:
+       g_free(g_path);
+
+       return 0;
+}
+
+static void __print_help(const char *argv0)
+{
+       g_print("\t[usage]\n");
+       g_print("\t\t1. decode & encode : %s {mode} {path} {color_format(opt)}\n", argv0);
+       g_print("\t\t2. decode : %s 0 /home/owner/media/test.heif\n", argv0);
+       g_print("\t\t3. encode : %s 1 /home/owner/media/test.heif\n", argv0);
+       g_print("\t\t4. encode : %s 3 /home/owner/media/test.heif 2(HEIF_COLOR_FORMAT_RGB24)\n", argv0);
+       g_print("\t\t5. [mode]\n");
+       g_print("\t\t  0 - auto\n");
+       g_print("\t\t  1 - get information from file\n");
+       g_print("\t\t  2 - get information from memory\n");
+       g_print("\t\t  3 - decode from file\n");
+       g_print("\t\t  4 - decode from memory\n");
+       g_print("\t\t  5 - get thumbnail from file\n");
+       g_print("\t\t  6 - get thumbnail from memory\n");
+}
+
+static bool __get_input_data(const char *argv, const long min, const long max, int *data)
+{
+       if (argv == NULL || strlen(argv) == 0)
+               return false;
+
+       long temp = g_ascii_strtoll(argv, NULL, 10);
+       if (temp < min || temp > max)
+               return false;
+
+       *data  = (int)temp;
+
+       return true;
+}
+
+static bool __get_arguments(int argc, char *argv[])
+{
+       if (false == __get_input_data(argv[1], TEST_AUTO, TEST_NUM - 1, &g_test_mode)) {
+               g_print("\t[HEIF_testsuite] invalid mode(%s)\n", argv[1]);
+               __print_help(argv[0]);
+               return false;
+       }
+
+       g_path = g_strdup(argv[2]);
+       if (!g_path) {
+               g_print("\t[HEIF_testsuite] invalid path(%s)\n", argv[2]);
+               __print_help(argv[0]);
+               return false;
+       }
+
+       if (g_test_mode == TEST_AUTO ||
+               g_test_mode == TEST_GETINFO_FILE ||
+               g_test_mode == TEST_GETINFO_MEMORY) {
+               /* do nothing... */
+       } else if (g_test_mode == TEST_DECODE_FILE ||
+                       g_test_mode == TEST_DECODE_MEMORY ||
+                       g_test_mode == TEST_THUMB_FILE ||
+                       g_test_mode == TEST_THUMB_MEMORY) {
+               if (false == __get_input_data(argv[3], HEIF_COLOR_FORMAT_YUV420P, HEIF_COLOR_FORMAT_MAX - 1, &g_color))
+                       g_print("\t[HEIF_testsuite] used default color(%d)\n", g_color);
+       } else {
+               g_print("\t[HEIF_testsuite] invalid mode for test %s\n", argv[1]);
+               return false;
+       }
+
+       return true;
+}
+
+static bool __test_auto(void)
+{
+       bool result = true;
+
+       if (!__test_get_info(TEST_GETINFO_FILE)) {
+               g_print("\t[HEIF_testsuite] __test_get_info(%d) failed\n", TEST_GETINFO_FILE);
+               result = false;
+       }
+
+       if (!__test_get_info(TEST_GETINFO_MEMORY)) {
+               g_print("\t[HEIF_testsuite] __test_get_info(%d) failed\n", TEST_GETINFO_MEMORY);
+               result = false;
+       }
+
+       for (g_color = HEIF_COLOR_FORMAT_YUV420P; g_color < HEIF_COLOR_FORMAT_MAX; g_color++) {
+               if (!__test_decode(TEST_DECODE_FILE)) {
+                       g_print("\t[HEIF_TEST] __test_decode(%d/format:%u) fail\n", TEST_DECODE_FILE, g_color);
+                       result = false;
+               }
+       }
+
+       for (g_color = HEIF_COLOR_FORMAT_YUV420P; g_color < HEIF_COLOR_FORMAT_MAX; g_color++) {
+               if (!__test_decode(TEST_DECODE_MEMORY)) {
+                       g_print("\t[HEIF_TEST] __test_decode(%d/format:%u) fail\n", TEST_DECODE_MEMORY, g_color);
+                       result = false;
+               }
+       }
+
+       for (g_color = HEIF_COLOR_FORMAT_YUV420P; g_color < HEIF_COLOR_FORMAT_MAX; g_color++) {
+               if (!__test_thumb(TEST_THUMB_FILE)) {
+                       g_print("\t[HEIF_TEST] __test_thumb(%d/format:%u) fail\n", TEST_THUMB_FILE, g_color);
+                       result = false;
+               }
+       }
+
+       for (g_color = HEIF_COLOR_FORMAT_YUV420P; g_color < HEIF_COLOR_FORMAT_MAX; g_color++) {
+               if (!__test_thumb(TEST_THUMB_MEMORY)) {
+                       g_print("\t[HEIF_TEST] __test_thumb(%d/format:%u) fail\n", TEST_THUMB_MEMORY, g_color);
+                       result = false;
+               }
+       }
+
+       return result;
+}
+
+static bool __test_get_info(int mode)
+{
+       bool result = true;
+       int ret = 0;
+       heif_image_info_h image_info = NULL;
+       gchar *buffer = NULL;
+       gsize size = 0;
+       GError *error = NULL;
+
+       switch (mode) {
+       case TEST_GETINFO_FILE:
+               ret = heif_get_image_info_from_file(g_path, &image_info);
+               if (ret < 0) {
+                       g_print("\t[HEIF_TEST] heif_get_image_info_from_file fail %d\n", ret);
+                       return false;
+               }
+
+               g_print("[HEIF_TEST] heif_get_image_info_from_file succeed !!!\n");
+
+               ret = __get_image_attrs(image_info);
+               if (ret < 0) {
+                       g_print("\t[HEIF_TEST] __get_image_attrs fail %d\n", ret);
+                       result = false;
+               }
+               break;
+
+       case TEST_GETINFO_MEMORY:
+               if (!g_file_get_contents(g_path, &buffer, &size, &error)) {
+                       g_print("\t[HEIF_TEST] g_file_get_contents fail [%s: %s]\n", g_path, (error ? error->message : "none"));
+                       if (error)
+                               g_error_free(error);
+                       return false;
+               }
+
+               ret = heif_get_image_info_from_buffer((unsigned char *)((void *)buffer), (size_t)size, &image_info);
+               if (ret < 0) {
+                       g_print("\t[HEIF_TEST] heif_get_image_info_from_buffer fail %d\n", ret);
+                       g_free(buffer);
+                       return false;
+               }
+
+               g_print("[HEIF_TEST] heif_get_image_info_from_buffer succeed !!!\n");
+
+               ret = __get_image_attrs(image_info);
+               if (ret < 0) {
+                       g_print("\t[HEIF_TEST] __get_image_attrs fail %d\n", ret);
+                       result = false;
+               }
+
+               g_free(buffer);
+               break;
+
+       default:
+               g_print("\t[HEIF_TEST] invalid mode %d\n", mode);
+               return false;
+       }
+
+       heif_image_info_free(image_info);
+       return result;
+}
+
+static bool __test_decode(int mode)
+{
+       int ret = 0;
+       heif_image_h image = NULL;
+       gchar *buffer = NULL;
+       gsize size = 0;
+       GError *error = NULL;
+
+       switch (mode) {
+       case TEST_DECODE_FILE:
+               ret = heif_decode_image_from_file(g_path, g_color, &image);
+               if (ret < 0) {
+                       g_print("\t[HEIF_TEST] heif_decode_image_from_file(format:%u) fail %d\n", g_color, ret);
+                       return false;
+               }
+
+               g_print("[HEIF_TEST] heif_decode_image_from_file succeed !!!\n");
+
+               ret = __save_to_jpeg(image, g_test_decode_file_jpeg[g_color]);
+               if (ret < 0)
+                       g_print("\t[HEIF_TEST] __save_to_jpeg fail %d\n", ret);
+
+               ret = __save_to_png(image, g_test_decode_file_png[g_color]);
+               if (ret < 0)
+                       g_print("\t[HEIF_TEST] __save_to_png fail %d\n", ret);
+
+               break;
+
+       case TEST_DECODE_MEMORY:
+               if (!g_file_get_contents(g_path, &buffer, &size, &error)) {
+                       g_print("\t[HEIF_TEST] g_file_get_contents fail [%s: %s]\n", g_path, (error ? error->message : "none"));
+                       if (error)
+                               g_error_free(error);
+                       return false;
+               }
+
+               ret = heif_decode_image_from_buffer((unsigned char *)((void *)buffer), (size_t)size, g_color, &image);
+               if (ret < 0) {
+                       g_print("\t[HEIF_TEST] heif_decode_image_from_buffer fail %d\n", ret);
+                       return false;
+               }
+
+               g_print("[HEIF_TEST] heif_decode_image_from_buffer succeed !!!\n");
+
+               ret = __save_to_jpeg(image, g_test_decode_buff_jpeg[g_color]);
+               if (ret < 0)
+                       g_print("\t[HEIF_TEST] __save_to_jpeg fail %d\n", ret);
+
+               ret = __save_to_png(image, g_test_decode_buff_png[g_color]);
+               if (ret < 0)
+                       g_print("\t[HEIF_TEST] __save_to_png fail %d\n", ret);
+
+               g_free(buffer);
+               break;
+
+       default:
+               g_print("\t[HEIF_TEST] invalid mode %d\n", mode);
+               return false;
+       }
+
+       heif_decoder_destroy_image(image);
+       return true;
+}
+
+static bool __test_thumb(int mode)
+{
+       int ret = 0;
+       heif_image_h thumbnail = NULL;
+       gchar *buffer = NULL;
+       gsize size = 0;
+       GError *error = NULL;
+
+       switch (mode) {
+       case TEST_THUMB_FILE:
+               ret = heif_decode_thumb_from_file(g_path, g_color, &thumbnail);
+               if (ret < 0) {
+                       g_print("\t[HEIF_TEST] heif_decode_thumb_from_file(format:%u) fail %d\n", g_color, ret);
+                       return false;
+               }
+
+               g_print("[HEIF_TEST] heif_decode_thumb_from_file succeed !!!\n");
+
+               ret = __save_to_jpeg(thumbnail, g_test_thumb_file_jpeg[g_color]);
+               if (ret < 0)
+                       g_print("\t[HEIF_TEST] __save_to_jpeg fail %d\n", ret);
+
+               ret = __save_to_png(thumbnail, g_test_thumb_file_png[g_color]);
+               if (ret < 0)
+                       g_print("\t[HEIF_TEST] __save_to_png fail %d\n", ret);
+
+               break;
+
+       case TEST_THUMB_MEMORY:
+               if (!g_file_get_contents(g_path, &buffer, &size, &error)) {
+                       g_print("\t[HEIF_TEST] g_file_get_contents fail [%s: %s]\n", g_path, (error ? error->message : "none"));
+                       if (error)
+                               g_error_free(error);
+                       return false;
+               }
+
+               ret = heif_decode_thumb_from_buffer((unsigned char *)((void *)buffer), (size_t)size, g_color, &thumbnail);
+               if (ret < 0) {
+                       g_print("\t[HEIF_TEST] heif_decode_thumb_from_buffer fail %d\n", ret);
+                       return false;
+               }
+
+               g_print("[HEIF_TEST] heif_decode_thumb_from_buffer succeed !!!\n");
+
+               ret = __save_to_jpeg(thumbnail, g_test_thumb_buff_jpeg[g_color]);
+               if (ret < 0)
+                       g_print("\t[HEIF_TEST] __save_to_jpeg fail %d\n", ret);
+
+               ret = __save_to_png(thumbnail, g_test_thumb_buff_png[g_color]);
+               if (ret < 0)
+                       g_print("\t[HEIF_TEST] __save_to_png fail %d\n", ret);
+
+               g_free(buffer);
+               break;
+
+       default:
+               g_print("\t[HEIF_TEST] invalid mode %d\n", mode);
+               return false;
+       }
+
+       heif_decoder_destroy_image(thumbnail);
+       return true;
+}
+
+static int __get_image_attrs(heif_image_info_h image_info)
+{
+       int ret = 0;
+       int width = 0, height = 0, orientation = 0, mirror = 0;
+       char *mimetype = NULL;
+       void *exif = NULL, *icc_profile = NULL;
+       int mimetype_len = 0, exif_len = 0, icc_profile_len = 0;
+
+       /* get basic information */
+       ret = heif_image_info_get_attrs(image_info,
+                                                               HEIF_IMAGE_MIMETYPE, &mimetype, &mimetype_len,
+                                                               HEIF_IMAGE_WIDTH, &width,
+                                                               HEIF_IMAGE_HEIGHT, &height,
+                                                               HEIF_IMAGE_ORIENTATION, &orientation,
+                                                               HEIF_IMAGE_MIRROR, &mirror,
+                                                               NULL);
+       if (ret != LIBHEIF_ERROR_NONE)
+               g_print("\t[HEIF_TEST] heif_image_info_get_attrs fail %d\n", ret);
+
+       g_print("[HEIF_TEST] Mimetype\t: %s\n", mimetype);
+       g_print("[HEIF_TEST] Width\t: %d\n", width);
+       g_print("[HEIF_TEST] Height\t: %d\n", height);
+       g_print("[HEIF_TEST] Orientation\t: %d\n", orientation);
+       g_print("[HEIF_TEST] Mirror\t: %d\n", mirror);
+
+       /* get extra information */
+       ret = heif_image_info_get_attrs(image_info,
+                                                               HEIF_IMAGE_EXIF, &exif, &exif_len,
+                                                               HEIF_IMAGE_ICC_PROFILE, &icc_profile, &icc_profile_len,
+                                                               NULL);
+       if (ret != LIBHEIF_ERROR_NONE)
+               g_print("\t[HEIF_TEST] heif_image_info_get_attrs fail %d\n", ret);
+
+       g_print("[HEIF_TEST] Exif\t: %p/%d\n", exif, exif_len);
+       g_print("[HEIF_TEST] ICC profile\t: %p/%d\n", icc_profile, icc_profile_len);
+
+       return ret;
+}
+
+static int __convert_heif_image_to_mm_image(heif_image_h heif_image, mm_util_image_h *mm_image)
+{
+       int ret = LIBHEIF_ERROR_NONE;
+       mm_util_color_format_e color = MM_UTIL_COLOR_RGB24;
+       unsigned int width = 0, height = 0;
+       heif_color_format_e format;
+       unsigned char *data = NULL;
+       size_t size = 0;
+
+       ret = heif_image_get_image(heif_image, &width, &height, &format, &data, &size);
+       if (ret != LIBHEIF_ERROR_NONE) {
+               g_print("\t[HEIF_TEST] heif_image_get_image fail %d\n", ret);
+               return ret;
+       }
+
+       g_print("[HEIF_TEST][SOURCE] width: %u, height: %u format: %u data: %p, size: %zu\n", width, height, format, data, size);
+
+       switch (format) {
+       case HEIF_COLOR_FORMAT_RGB24:
+               color = MM_UTIL_COLOR_RGB24;
+               break;
+
+       case HEIF_COLOR_FORMAT_ARGB:
+               color = MM_UTIL_COLOR_ARGB;
+               break;
+
+       case HEIF_COLOR_FORMAT_BGRA:
+               color = MM_UTIL_COLOR_BGRA;
+               break;
+
+       case HEIF_COLOR_FORMAT_RGBA:
+               color = MM_UTIL_COLOR_RGBA;
+               break;
+
+       case HEIF_COLOR_FORMAT_YUV420P:
+               color = MM_UTIL_COLOR_YUV420;
+               break;
+
+       default:
+               g_print("\t[HEIF_TEST] invalid color: %d\n", format);
+               return MM_UTIL_ERROR_INVALID_PARAMETER;
+       }
+
+       ret = mm_image_create_image(width, height, color, data, size, mm_image);
+       if (ret != MM_UTIL_ERROR_NONE) {
+               g_print("\t[HEIF_TEST] mm_image_create_image fail %d\n", ret);
+               return ret;
+       }
+
+       return LIBHEIF_ERROR_NONE;
+}
+
+static int __save_to_jpeg(heif_image_h source, const char *path)
+{
+       int ret = 0;
+       mm_util_image_h mm_image = NULL;
+
+       if (!source) {
+               g_print("\t[HEIF_TEST] source: %p\n", source);
+               return MM_UTIL_ERROR_INVALID_PARAMETER;
+       }
+
+       ret = __convert_heif_image_to_mm_image(source, &mm_image);
+       if (ret < 0) {
+               g_print("\t[HEIF_TEST] __convert_heif_image_to_mm_image fail %d\n", ret);
+               goto END;
+       }
+
+       ret = mm_util_jpeg_encode_to_file(mm_image, 100, path);
+       if (ret < 0)
+               g_print("\t[HEIF_TEST] mm_util_jpeg_encode_to_file fail %d\n", ret);
+
+END:
+       mm_image_destroy_image(mm_image);
+
+       return ret;
+}
+
+static int __save_to_png(heif_image_h source, const char *path)
+{
+       int ret = 0;
+       mm_util_image_h mm_image = NULL;
+       mm_util_enc_opt_h enc_opt = NULL;
+
+       if (!source) {
+               g_print("\t[HEIF_TEST] source: %p\n", source);
+               return MM_UTIL_ERROR_INVALID_PARAMETER;
+       }
+
+       ret = __convert_heif_image_to_mm_image(source, &mm_image);
+       if (ret < 0) {
+               g_print("\t[HEIF_TEST] __convert_heif_image_to_mm_image fail %d\n", ret);
+               goto END;
+       }
+
+       ret = mm_util_enc_opt_create(&enc_opt);
+       if (ret < 0) {
+               g_print("\t[HEIF_TEST] mm_util_enc_opt_create fail %d\n", ret);
+               goto END;
+       }
+
+       ret = mm_util_enc_opt_set_codec(enc_opt, IMG_CODEC_PNG);
+       if (ret < 0) {
+               g_print("\t[HEIF_TEST] mm_util_enc_opt_set_codec fail %d\n", ret);
+               goto END;
+       }
+
+       ret = mm_util_enc_opt_set_png_compression(enc_opt, 1);
+       if (ret < 0) {
+               g_print("\t[HEIF_TEST] mm_util_enc_opt_set_png_compression fail %d\n", ret);
+               goto END;
+       }
+
+       ret = mm_util_encode_image_to_file(mm_image, enc_opt, path);
+       if (ret < 0)
+               g_print("\t[HEIF_TEST] mm_util_encode_image_to_file fail %d\n", ret);
+
+END:
+       mm_image_destroy_image(mm_image);
+       mm_util_enc_opt_destroy(enc_opt);
+
+       return ret;
+}
diff --git a/test/sample_test.heic b/test/sample_test.heic
new file mode 100644 (file)
index 0000000..a605aac
Binary files /dev/null and b/test/sample_test.heic differ
diff --git a/test/test.sh b/test/test.sh
new file mode 100755 (executable)
index 0000000..90dfc35
--- /dev/null
@@ -0,0 +1,85 @@
+#
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
+#
+# Contact: Jiyong Min <jiyong.min@samsung.com>
+#
+# 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.
+#
+
+@echo off
+# VERSION should be same with library version.
+VERSION=0.0.1
+ARM=armv7l
+X86=i586
+AARCH64=aarch64
+X86_64=x86_64
+
+# This script run tests automatically.
+# To run the test successfully,
+# please modify below 'ARCH' and 'LOCAL_DIR' to suit your enviroment.
+#
+ARCH=$X86
+BUILD_ROOTS=~/GBS-ROOT/local/BUILD-ROOTS/scratch.$ARCH.0/home/abuild/rpmbuild/BUILD
+LOCAL_DIR=~/TestHeif/
+
+@echo on
+sdb push $BUILD_ROOTS/libheif-$VERSION/test/heif_testsuite /home/owner/media/
+sdb push $BUILD_ROOTS/libheif-$VERSION/test/sample_test.heic /home/owner/media/
+
+sdb shell "cd /home/owner/media/;rm -rf test_heif_*;./heif_testsuite 0 sample_test.heic"
+
+TEST_FILELIST=(
+"test_heif_file_yuv420p.jpg"
+"test_heif_file_rgb24.jpg"
+"test_heif_file_rgb24.png"
+"test_heif_file_argb.jpg"
+"test_heif_file_argb.png"
+"test_heif_file_bgra.jpg"
+"test_heif_file_bgra.png"
+"test_heif_file_rgba.jpg"
+"test_heif_file_rgba.png"
+
+"test_heif_buff_yuv420p.jpg"
+"test_heif_buff_rgb24.jpg"
+"test_heif_buff_rgb24.png"
+"test_heif_buff_argb.jpg"
+"test_heif_buff_argb.png"
+"test_heif_buff_bgra.jpg"
+"test_heif_buff_bgra.png"
+"test_heif_buff_rgba.jpg"
+"test_heif_buff_rgba.png"
+
+"test_heif_thumb_file_yuv420p.jpg"
+"test_heif_thumb_file_rgb24.jpg"
+"test_heif_thumb_file_rgb24.png"
+"test_heif_thumb_file_argb.jpg"
+"test_heif_thumb_file_argb.png"
+"test_heif_thumb_file_bgra.jpg"
+"test_heif_thumb_file_bgra.png"
+"test_heif_thumb_file_rgba.jpg"
+"test_heif_thumb_file_rgba.png"
+
+"test_heif_thumb_buff_yuv420p.jpg"
+"test_heif_thumb_buff_rgb24.jpg"
+"test_heif_thumb_buff_rgb24.png"
+"test_heif_thumb_buff_argb.jpg"
+"test_heif_thumb_buff_argb.png"
+"test_heif_thumb_buff_bgra.jpg"
+"test_heif_thumb_buff_bgra.png"
+"test_heif_thumb_buff_rgba.jpg"
+"test_heif_thumb_buff_rgba.png"
+)
+
+for file_name in "${TEST_FILELIST[@]}"; do
+       sdb pull /home/owner/media/$file_name $LOCAL_DIR
+done