aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRam Mohan M <ram.mohan@ittiam.com>2024-03-19 04:53:42 +0530
committerRam Mohan M <ram.mohan@ittiam.com>2024-03-19 04:54:03 +0530
commit63359d26be78fc80f422f0aad722047e02ada7dd (patch)
tree538e437cf0f6d9dd2c111e924c3bb1b1afacfdb9
parenta463f1210389cd724889229b86fa61edbb19b016 (diff)
downloadlibultrahdr-63359d26be78fc80f422f0aad722047e02ada7dd.tar.gz
Add C compatible interface for libultrahdr library
Updated sample application and unit tests for new interface Test: ./ultrahdr_unit_test Co-authored-by: Aayush Soni <aayush.soni@ittiam.com> Change-Id: I99d8ada4b0850630bc14a1c33a764a00e6f09e9f
-rw-r--r--Android.bp6
-rw-r--r--CMakeLists.txt3
-rw-r--r--README.md2
-rw-r--r--examples/ultrahdr_app.cpp770
-rw-r--r--lib/include/ultrahdr/jpegdecoderhelper.h4
-rw-r--r--lib/include/ultrahdr/ultrahdrcommon.h85
-rw-r--r--lib/src/ultrahdr_api.cpp1146
-rw-r--r--tests/jpegr_test.cpp235
-rw-r--r--ultrahdr_api.h515
9 files changed, 2439 insertions, 327 deletions
diff --git a/Android.bp b/Android.bp
index caf0136..18b7e5b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,7 +35,10 @@ cc_library {
name: "libultrahdr",
host_supported: true,
vendor_available: true,
- export_include_dirs: ["lib/include"],
+ export_include_dirs: [
+ ".",
+ "lib/include",
+ ],
local_include_dirs: ["lib/include"],
srcs: [
@@ -44,6 +47,7 @@ cc_library {
"lib/src/gainmapmath.cpp",
"lib/src/jpegrutils.cpp",
"lib/src/multipictureformat.cpp",
+ "lib/src/ultrahdr_api.cpp",
],
shared_libs: [
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d576f12..4992b6e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -122,6 +122,7 @@ if(MSVC)
add_compile_options(/wd4267) # conversion from 'size_t' to 'type' possible loss of data
add_compile_options(/wd4305) # truncation from 'double' to 'float'
add_compile_options(/wd4838) # conversion from 'type1' to 'type2' requires a narrowing conversion
+ add_compile_options(/wd26812) # Prefer enum class over enum
else()
add_compile_options(-ffunction-sections)
add_compile_options(-fdata-sections)
@@ -299,7 +300,7 @@ file(GLOB UHDR_TEST_LIST "${TESTS_DIR}/*.cpp")
file(GLOB UHDR_BM_LIST "${BENCHMARK_DIR}/*.cpp")
file(GLOB IMAGE_IO_LIST "${THIRD_PARTY_DIR}/image_io/src/**/*.cc")
-set(COMMON_INCLUDE_LIST ${SOURCE_DIR}/include/ ${JPEG_INCLUDE_DIRS})
+set(COMMON_INCLUDE_LIST ${CMAKE_SOURCE_DIR} ${SOURCE_DIR}/include/ ${JPEG_INCLUDE_DIRS})
set(COMMON_LIBS_LIST ${JPEG_LIBRARIES} Threads::Threads)
###########################################################
diff --git a/README.md b/README.md
index 5798365..b8fa4e7 100644
--- a/README.md
+++ b/README.md
@@ -162,5 +162,5 @@ List of decode API:
| metadata | (optional, default to NULL) Destination of metadata (gain map version, min/max content boost). |
For more info:
-- Refer to [jpegr.h](lib/include/ultrahdr/jpegr.h) for detailed description of various encode and decode api.
+- Refer to [ultrahdr_api.h](ultrahdr_api.h) for detailed description of various encode and decode api.
- Refer to [ultrahdr_app.cpp](examples/ultrahdr_app.cpp) for examples of its usage.
diff --git a/examples/ultrahdr_app.cpp b/examples/ultrahdr_app.cpp
index 20b9329..c4e9c5b 100644
--- a/examples/ultrahdr_app.cpp
+++ b/examples/ultrahdr_app.cpp
@@ -24,14 +24,11 @@
#include <algorithm>
#include <cmath>
+#include <cstdint>
#include <fstream>
#include <iostream>
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/gainmapmath.h"
-#include "ultrahdr/jpegr.h"
-
-using namespace ultrahdr;
+#include "ultrahdr_api.h"
const float BT601YUVtoRGBMatrix[9] = {
1, 0, 1.402, 1, (-0.202008 / 0.587), (-0.419198 / 0.587), 1.0, 1.772, 0.0};
@@ -62,6 +59,10 @@ const float BT2020RGBtoYUVMatrix[9] = {0.2627,
(-0.6780 / 1.4746),
(-0.0593 / 1.4746)};
+// remove these once introduced in ultrahdr_api.h
+const int UHDR_IMG_FMT_24bppYCbCr444 = 100;
+const int UHDR_IMG_FMT_48bppYCbCr444 = 101;
+
int optind_s = 1;
int optopt_s = 0;
char* optarg_s = nullptr;
@@ -158,6 +159,26 @@ static bool loadFile(const char* filename, void*& result, int length) {
return false;
}
+static bool loadFile(const char* filename, uhdr_raw_image_t* handle) {
+ std::ifstream ifd(filename, std::ios::binary);
+ if (ifd.good()) {
+ if (handle->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
+ const int bpp = 2;
+ ifd.read(static_cast<char*>(handle->planes[0]), handle->w * handle->h * bpp);
+ ifd.read(static_cast<char*>(handle->planes[1]), (handle->w / 2) * (handle->h / 2) * bpp * 2);
+ return true;
+ } else if (handle->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
+ ifd.read(static_cast<char*>(handle->planes[0]), handle->w * handle->h);
+ ifd.read(static_cast<char*>(handle->planes[1]), (handle->w / 2) * (handle->h / 2));
+ ifd.read(static_cast<char*>(handle->planes[2]), (handle->w / 2) * (handle->h / 2));
+ return true;
+ }
+ return false;
+ }
+ std::cerr << "unable to open file : " << filename << std::endl;
+ return false;
+}
+
static bool writeFile(const char* filename, void*& result, int length) {
std::ofstream ofd(filename, std::ios::binary);
if (ofd.is_open()) {
@@ -168,14 +189,54 @@ static bool writeFile(const char* filename, void*& result, int length) {
return false;
}
+static bool writeFile(const char* filename, uhdr_raw_image_t* img) {
+ std::ofstream ofd(filename, std::ios::binary);
+ if (ofd.is_open()) {
+ if (img->fmt == UHDR_IMG_FMT_32bppRGBA8888 || img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ||
+ img->fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
+ char* data = static_cast<char*>(img->planes[0]);
+ int bpp = img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ? 8 : 4;
+ const size_t stride = img->stride[0] * bpp;
+ const size_t length = img->w * bpp;
+ for (unsigned i = 0; i < img->h; i++, data += stride) {
+ ofd.write(data, length);
+ }
+ return true;
+ } else if ((int)img->fmt == UHDR_IMG_FMT_24bppYCbCr444 ||
+ (int)img->fmt == UHDR_IMG_FMT_48bppYCbCr444) {
+ char* data = static_cast<char*>(img->planes[0]);
+ int bpp = (int)img->fmt == UHDR_IMG_FMT_48bppYCbCr444 ? 2 : 1;
+ size_t stride = img->stride[0] * bpp;
+ size_t length = img->w * bpp;
+ for (unsigned i = 0; i < img->h; i++, data += stride) {
+ ofd.write(data, length);
+ }
+ data = static_cast<char*>(img->planes[1]);
+ stride = img->stride[1] * bpp;
+ for (unsigned i = 0; i < img->h; i++, data += stride) {
+ ofd.write(data, length);
+ }
+ data = static_cast<char*>(img->planes[2]);
+ stride = img->stride[2] * bpp;
+ for (unsigned i = 0; i < img->h; i++, data += stride) {
+ ofd.write(data, length);
+ }
+ return true;
+ }
+ return false;
+ }
+ std::cerr << "unable to write to file : " << filename << std::endl;
+ return false;
+}
+
class UltraHdrAppInput {
public:
UltraHdrAppInput(const char* p010File, const char* yuv420File, const char* yuv420JpegFile,
- size_t width, size_t height,
- ultrahdr_color_gamut p010Cg = ULTRAHDR_COLORGAMUT_BT709,
- ultrahdr_color_gamut yuv420Cg = ULTRAHDR_COLORGAMUT_BT709,
- ultrahdr_transfer_function tf = ULTRAHDR_TF_HLG, int quality = 100,
- ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG)
+ size_t width, size_t height, uhdr_color_gamut_t p010Cg = UHDR_CG_BT_709,
+ uhdr_color_gamut_t yuv420Cg = UHDR_CG_BT_709,
+ uhdr_color_transfer_t p010Tf = UHDR_CT_HLG, int quality = 100,
+ uhdr_color_transfer_t oTf = UHDR_CT_HLG,
+ uhdr_img_fmt_t oFmt = UHDR_IMG_FMT_32bppRGBA1010102)
: mP010File(p010File),
mYuv420File(yuv420File),
mYuv420JpegFile(yuv420JpegFile),
@@ -184,38 +245,56 @@ class UltraHdrAppInput {
mHeight(height),
mP010Cg(p010Cg),
mYuv420Cg(yuv420Cg),
- mTf(tf),
+ mP010Tf(p010Tf),
mQuality(quality),
- mOf(of),
+ mOTf(oTf),
+ mOfmt(oFmt),
mMode(0){};
- UltraHdrAppInput(const char* jpegRFile, ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG)
+ UltraHdrAppInput(const char* jpegRFile, uhdr_color_transfer_t oTf = UHDR_CT_HLG,
+ uhdr_img_fmt_t oFmt = UHDR_IMG_FMT_32bppRGBA1010102)
: mP010File(nullptr),
mYuv420File(nullptr),
mJpegRFile(jpegRFile),
mWidth(0),
mHeight(0),
- mP010Cg(ULTRAHDR_COLORGAMUT_UNSPECIFIED),
- mYuv420Cg(ULTRAHDR_COLORGAMUT_UNSPECIFIED),
- mTf(ULTRAHDR_TF_UNSPECIFIED),
+ mP010Cg(UHDR_CG_UNSPECIFIED),
+ mYuv420Cg(UHDR_CG_UNSPECIFIED),
+ mP010Tf(UHDR_CT_UNSPECIFIED),
mQuality(100),
- mOf(of),
+ mOTf(oTf),
+ mOfmt(oFmt),
mMode(1){};
~UltraHdrAppInput() {
- if (mRawP010Image.data) free(mRawP010Image.data);
- if (mRawP010Image.chroma_data) free(mRawP010Image.chroma_data);
- if (mRawRgba1010102Image.data) free(mRawRgba1010102Image.data);
- if (mRawRgba1010102Image.chroma_data) free(mRawRgba1010102Image.chroma_data);
- if (mRawYuv420Image.data) free(mRawYuv420Image.data);
- if (mRawYuv420Image.chroma_data) free(mRawYuv420Image.chroma_data);
- if (mRawRgba8888Image.data) free(mRawRgba8888Image.data);
- if (mRawRgba8888Image.chroma_data) free(mRawRgba8888Image.chroma_data);
+ int count = sizeof mRawP010Image.planes / sizeof mRawP010Image.planes[0];
+ for (int i = 0; i < count; i++) {
+ if (mRawP010Image.planes[i]) {
+ free(mRawP010Image.planes[i]);
+ mRawP010Image.planes[i] = nullptr;
+ }
+ if (mRawRgba1010102Image.planes[i]) {
+ free(mRawRgba1010102Image.planes[i]);
+ mRawRgba1010102Image.planes[i] = nullptr;
+ }
+ if (mRawYuv420Image.planes[i]) {
+ free(mRawYuv420Image.planes[i]);
+ mRawYuv420Image.planes[i] = nullptr;
+ }
+ if (mRawRgba8888Image.planes[i]) {
+ free(mRawRgba8888Image.planes[i]);
+ mRawRgba8888Image.planes[i] = nullptr;
+ }
+ if (mDestImage.planes[i]) {
+ free(mDestImage.planes[i]);
+ mDestImage.planes[i] = nullptr;
+ }
+ if (mDestYUV444Image.planes[i]) {
+ free(mDestYUV444Image.planes[i]);
+ mDestYUV444Image.planes[i] = nullptr;
+ }
+ }
if (mJpegImgR.data) free(mJpegImgR.data);
- if (mDestImage.data) free(mDestImage.data);
- if (mDestImage.chroma_data) free(mDestImage.chroma_data);
- if (mDestYUV444Image.data) free(mDestYUV444Image.data);
- if (mDestYUV444Image.chroma_data) free(mDestYUV444Image.chroma_data);
}
bool fillJpegRImageHandle();
@@ -239,48 +318,70 @@ class UltraHdrAppInput {
const char* mJpegRFile;
const int mWidth;
const int mHeight;
- const ultrahdr_color_gamut mP010Cg;
- const ultrahdr_color_gamut mYuv420Cg;
- const ultrahdr_transfer_function mTf;
+ const uhdr_color_gamut_t mP010Cg;
+ const uhdr_color_gamut_t mYuv420Cg;
+ const uhdr_color_transfer_t mP010Tf;
const int mQuality;
- const ultrahdr_output_format mOf;
+ const uhdr_color_transfer_t mOTf;
+ const uhdr_img_fmt mOfmt;
const int mMode;
- jpegr_uncompressed_struct mRawP010Image{};
- jpegr_uncompressed_struct mRawRgba1010102Image{};
- jpegr_uncompressed_struct mRawYuv420Image{};
- jpegr_compressed_struct mYuv420JpegImage{};
- jpegr_uncompressed_struct mRawRgba8888Image{};
- jpegr_compressed_struct mJpegImgR{};
- jpegr_uncompressed_struct mDestImage{};
- jpegr_uncompressed_struct mDestYUV444Image{};
+
+ uhdr_raw_image_t mRawP010Image{};
+ uhdr_raw_image_t mRawRgba1010102Image{};
+ uhdr_raw_image_t mRawYuv420Image{};
+ uhdr_compressed_image_t mYuv420JpegImage{};
+ uhdr_raw_image_t mRawRgba8888Image{};
+ uhdr_compressed_image_t mJpegImgR{};
+ uhdr_raw_image_t mDestImage{};
+ uhdr_raw_image_t mDestYUV444Image{};
double mPsnr[3]{};
};
bool UltraHdrAppInput::fillP010ImageHandle() {
const int bpp = 2;
int p010Size = mWidth * mHeight * bpp * 1.5;
- mRawP010Image.width = mWidth;
- mRawP010Image.height = mHeight;
- mRawP010Image.colorGamut = mP010Cg;
- return loadFile(mP010File, mRawP010Image.data, p010Size);
+ mRawP010Image.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
+ mRawP010Image.cg = mP010Cg;
+ mRawP010Image.ct = mP010Tf;
+ mRawP010Image.range = UHDR_CR_LIMITED_RANGE;
+ mRawP010Image.w = mWidth;
+ mRawP010Image.h = mHeight;
+ mRawP010Image.planes[0] = malloc(mWidth * mHeight * bpp);
+ mRawP010Image.planes[1] = malloc((mWidth / 2) * (mHeight / 2) * bpp * 2);
+ mRawP010Image.planes[2] = nullptr;
+ mRawP010Image.stride[0] = mWidth;
+ mRawP010Image.stride[1] = mWidth;
+ mRawP010Image.stride[2] = 0;
+ return loadFile(mP010File, &mRawP010Image);
}
bool UltraHdrAppInput::fillYuv420ImageHandle() {
int yuv420Size = mWidth * mHeight * 1.5;
- mRawYuv420Image.width = mWidth;
- mRawYuv420Image.height = mHeight;
- mRawYuv420Image.colorGamut = mYuv420Cg;
- return loadFile(mYuv420File, mRawYuv420Image.data, yuv420Size);
+ mRawYuv420Image.fmt = UHDR_IMG_FMT_12bppYCbCr420;
+ mRawYuv420Image.cg = mYuv420Cg;
+ mRawYuv420Image.ct = UHDR_CT_SRGB;
+ mRawYuv420Image.range = UHDR_CR_FULL_RANGE;
+ mRawYuv420Image.w = mWidth;
+ mRawYuv420Image.h = mHeight;
+ mRawYuv420Image.planes[0] = malloc(mWidth * mHeight);
+ mRawYuv420Image.planes[1] = malloc((mWidth / 2) * (mHeight / 2));
+ mRawYuv420Image.planes[2] = malloc((mWidth / 2) * (mHeight / 2));
+ mRawYuv420Image.stride[0] = mWidth;
+ mRawYuv420Image.stride[1] = mWidth / 2;
+ mRawYuv420Image.stride[2] = mWidth / 2;
+ return loadFile(mYuv420File, &mRawYuv420Image);
}
bool UltraHdrAppInput::fillYuv420JpegImageHandle() {
std::ifstream ifd(mYuv420JpegFile, std::ios::binary | std::ios::ate);
if (ifd.good()) {
int size = ifd.tellg();
- mYuv420JpegImage.length = size;
- mYuv420JpegImage.maxLength = size;
+ mYuv420JpegImage.capacity = size;
+ mYuv420JpegImage.data_sz = size;
mYuv420JpegImage.data = nullptr;
- mYuv420JpegImage.colorGamut = mYuv420Cg;
+ mYuv420JpegImage.cg = mYuv420Cg;
+ mYuv420JpegImage.ct = UHDR_CT_UNSPECIFIED;
+ mYuv420JpegImage.range = UHDR_CR_UNSPECIFIED;
ifd.close();
return loadFile(mYuv420JpegFile, mYuv420JpegImage.data, size);
}
@@ -291,10 +392,12 @@ bool UltraHdrAppInput::fillJpegRImageHandle() {
std::ifstream ifd(mJpegRFile, std::ios::binary | std::ios::ate);
if (ifd.good()) {
int size = ifd.tellg();
- mJpegImgR.length = size;
- mJpegImgR.maxLength = size;
+ mJpegImgR.capacity = size;
+ mJpegImgR.data_sz = size;
mJpegImgR.data = nullptr;
- mJpegImgR.colorGamut = mYuv420Cg;
+ mYuv420JpegImage.cg = UHDR_CG_UNSPECIFIED;
+ mYuv420JpegImage.ct = UHDR_CT_UNSPECIFIED;
+ mYuv420JpegImage.range = UHDR_CR_UNSPECIFIED;
ifd.close();
return loadFile(mJpegRFile, mJpegImgR.data, size);
}
@@ -302,140 +405,152 @@ bool UltraHdrAppInput::fillJpegRImageHandle() {
}
bool UltraHdrAppInput::encode() {
- if (!fillP010ImageHandle()) return false;
- if (mYuv420File != nullptr && !fillYuv420ImageHandle()) return false;
- if (mYuv420JpegFile != nullptr && !fillYuv420JpegImageHandle()) return false;
-
- mJpegImgR.maxLength = (std::max)(static_cast<size_t>(8 * 1024) /* min size 8kb */,
- mRawP010Image.width * mRawP010Image.height * 3 * 2);
- mJpegImgR.data = malloc(mJpegImgR.maxLength);
- if (mJpegImgR.data == nullptr) {
- std::cerr << "unable to allocate memory to store compressed image" << std::endl;
- return false;
+#define CHECK_ERR(x) \
+ { \
+ uhdr_error_info_t status = (x); \
+ if (status.error_code != UHDR_CODEC_OK) { \
+ if (status.has_detail) { \
+ std::cerr << status.detail << std::endl; \
+ } \
+ return false; \
+ } \
}
- JpegR jpegHdr;
- status_t status = JPEGR_UNKNOWN_ERROR;
+ uhdr_codec_private_t* handle = uhdr_create_encoder();
+ if (mP010File != nullptr) {
+ if (!fillP010ImageHandle()) {
+ std::cerr << " failed to load file " << mP010File << std::endl;
+ return false;
+ }
+ CHECK_ERR(uhdr_enc_set_raw_image(handle, &mRawP010Image, UHDR_HDR_IMG))
+ }
+ if (mYuv420File != nullptr) {
+ if (!fillYuv420ImageHandle()) {
+ std::cerr << " failed to load file " << mYuv420File << std::endl;
+ return false;
+ }
+ CHECK_ERR(uhdr_enc_set_raw_image(handle, &mRawYuv420Image, UHDR_SDR_IMG))
+ }
+ if (mYuv420JpegFile != nullptr) {
+ if (!fillYuv420JpegImageHandle()) {
+ std::cerr << " failed to load file " << mYuv420JpegFile << std::endl;
+ return false;
+ }
+ CHECK_ERR(uhdr_enc_set_compressed_image(handle, &mYuv420JpegImage, UHDR_SDR_IMG))
+ }
+ CHECK_ERR(uhdr_enc_set_quality(handle, mQuality, UHDR_BASE_IMG))
#ifdef PROFILE_ENABLE
const int profileCount = 10;
Profiler profileEncode;
profileEncode.timerStart();
for (auto i = 0; i < profileCount; i++) {
#endif
- if (mYuv420File == nullptr && mYuv420JpegFile == nullptr) { // api-0
- status = jpegHdr.encodeJPEGR(&mRawP010Image, mTf, &mJpegImgR, mQuality, nullptr);
- if (JPEGR_NO_ERROR != status) {
- std::cerr << "Encountered error during encodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
- } else if (mYuv420File != nullptr && mYuv420JpegFile == nullptr) { // api-1
- status =
- jpegHdr.encodeJPEGR(&mRawP010Image, &mRawYuv420Image, mTf, &mJpegImgR, mQuality, nullptr);
- if (JPEGR_NO_ERROR != status) {
- std::cerr << "Encountered error during encodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
- } else if (mYuv420File != nullptr && mYuv420JpegFile != nullptr) { // api-2
- status =
- jpegHdr.encodeJPEGR(&mRawP010Image, &mRawYuv420Image, &mYuv420JpegImage, mTf, &mJpegImgR);
- if (JPEGR_NO_ERROR != status) {
- std::cerr << "Encountered error during encodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
- } else if (mYuv420File == nullptr && mYuv420JpegFile != nullptr) { // api-3
- status = jpegHdr.encodeJPEGR(&mRawP010Image, &mYuv420JpegImage, mTf, &mJpegImgR);
- if (JPEGR_NO_ERROR != status) {
- std::cerr << "Encountered error during encodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
- }
+ CHECK_ERR(uhdr_encode(handle))
#ifdef PROFILE_ENABLE
}
profileEncode.timerStop();
auto avgEncTime = profileEncode.elapsedTime() / (profileCount * 1000.f);
printf("Average encode time for res %d x %d is %f ms \n", mWidth, mHeight, avgEncTime);
#endif
- writeFile("out.jpeg", mJpegImgR.data, mJpegImgR.length);
+ auto output = uhdr_get_encoded_stream(handle);
+
+ // for decoding
+ mJpegImgR.data = malloc(output->data_sz);
+ memcpy(mJpegImgR.data, output->data, output->data_sz);
+ mJpegImgR.capacity = mJpegImgR.data_sz = output->data_sz;
+ mJpegImgR.cg = output->cg;
+ mJpegImgR.ct = output->ct;
+ mJpegImgR.range = output->range;
+ writeFile("out.jpeg", output->data, output->data_sz);
+
+ uhdr_release_encoder(handle);
return true;
}
bool UltraHdrAppInput::decode() {
- if (mMode == 1 && !fillJpegRImageHandle()) return false;
- std::vector<uint8_t> iccData(0);
- std::vector<uint8_t> exifData(0);
- jpegr_info_struct info{};
- JpegR jpegHdr;
- status_t status = jpegHdr.getJPEGRInfo(&mJpegImgR, &info);
- if (JPEGR_NO_ERROR == status) {
- size_t outSize = info.width * info.height * ((mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
- mDestImage.data = malloc(outSize);
- if (mDestImage.data == nullptr) {
- std::cerr << "failed to allocate memory to store decoded output" << std::endl;
- return false;
- }
+ if (mMode == 1 && !fillJpegRImageHandle()) {
+ std::cerr << " failed to load file " << mJpegRFile << std::endl;
+ return false;
+ }
+ uhdr_codec_private_t* handle = uhdr_create_decoder();
+ CHECK_ERR(uhdr_dec_set_image(handle, &mJpegImgR))
+ CHECK_ERR(uhdr_dec_set_out_color_transfer(handle, mOTf))
+ CHECK_ERR(uhdr_dec_set_out_img_format(handle, mOfmt))
+
#ifdef PROFILE_ENABLE
- const int profileCount = 10;
- Profiler profileDecode;
- profileDecode.timerStart();
- for (auto i = 0; i < profileCount; i++) {
+ const int profileCount = 10;
+ Profiler profileDecode;
+ profileDecode.timerStart();
+ for (auto i = 0; i < profileCount; i++) {
#endif
- status =
- jpegHdr.decodeJPEGR(&mJpegImgR, &mDestImage, FLT_MAX, nullptr, mOf, nullptr, nullptr);
- if (JPEGR_NO_ERROR != status) {
- std::cerr << "Encountered error during decodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
+ CHECK_ERR(uhdr_decode(handle))
#ifdef PROFILE_ENABLE
- }
- profileDecode.timerStop();
- auto avgDecTime = profileDecode.elapsedTime() / (profileCount * 1000.f);
- printf("Average decode time for res %ld x %ld is %f ms \n", info.width, info.height,
- avgDecTime);
+ }
+ profileDecode.timerStop();
+ auto avgDecTime = profileDecode.elapsedTime() / (profileCount * 1000.f);
+ printf("Average decode time for res %ld x %ld is %f ms \n", info.width, info.height, avgDecTime);
#endif
- writeFile("outrgb.raw", mDestImage.data, outSize);
- } else {
- std::cerr << "Encountered error during getJPEGRInfo call, error code " << status << std::endl;
- return false;
+ uhdr_raw_image_t* output = uhdr_get_decoded_image(handle);
+
+ mDestImage.fmt = output->fmt;
+ mDestImage.cg = output->cg;
+ mDestImage.ct = output->ct;
+ mDestImage.range = output->range;
+ mDestImage.w = output->w;
+ mDestImage.h = output->h;
+ int bpp = (output->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) ? 8 : 4;
+ mDestImage.planes[0] = malloc(output->w * output->h * bpp);
+ char* inData = static_cast<char*>(output->planes[0]);
+ char* outData = static_cast<char*>(mDestImage.planes[0]);
+ const size_t inStride = output->stride[0] * bpp;
+ const size_t outStride = output->w * bpp;
+ mDestImage.stride[0] = output->w;
+ const size_t length = output->w * bpp;
+ for (unsigned i = 0; i < output->h; i++, inData += inStride, outData += outStride) {
+ memcpy(outData, inData, length);
}
+ writeFile("outrgb.raw", output);
+ uhdr_release_decoder(handle);
return true;
}
+#define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
bool UltraHdrAppInput::convertP010ToRGBImage() {
const float* coeffs = BT2020YUVtoRGBMatrix;
- if (mP010Cg == ULTRAHDR_COLORGAMUT_BT709) {
+ if (mP010Cg == UHDR_CG_BT_709) {
coeffs = BT709YUVtoRGBMatrix;
- } else if (mP010Cg == ULTRAHDR_COLORGAMUT_BT2100) {
+ } else if (mP010Cg == UHDR_CG_BT_2100) {
coeffs = BT2020YUVtoRGBMatrix;
- } else if (mP010Cg == ULTRAHDR_COLORGAMUT_P3) {
+ } else if (mP010Cg == UHDR_CG_DISPLAY_P3) {
coeffs = BT601YUVtoRGBMatrix;
} else {
std::cerr << "color matrix not present for gamut " << mP010Cg << " using BT2020Matrix"
<< std::endl;
}
- mRawRgba1010102Image.data = malloc(mRawP010Image.width * mRawP010Image.height * 4);
- if (mRawRgba1010102Image.data == nullptr) {
- std::cerr << "failed to allocate memory to store Rgba1010102" << std::endl;
- return false;
- }
- mRawRgba1010102Image.width = mRawP010Image.width;
- mRawRgba1010102Image.height = mRawP010Image.height;
- mRawRgba1010102Image.colorGamut = mRawP010Image.colorGamut;
- uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba1010102Image.data);
- uint16_t* y = static_cast<uint16_t*>(mRawP010Image.data);
- uint16_t* u = y + mRawP010Image.width * mRawP010Image.height;
+ mRawRgba1010102Image.fmt = UHDR_IMG_FMT_32bppRGBA1010102;
+ mRawRgba1010102Image.cg = mRawP010Image.cg;
+ mRawRgba1010102Image.ct = mRawP010Image.ct;
+ mRawRgba1010102Image.range = UHDR_CR_FULL_RANGE;
+ mRawRgba1010102Image.w = mRawP010Image.w;
+ mRawRgba1010102Image.h = mRawP010Image.h;
+ mRawRgba1010102Image.planes[0] = malloc(mRawP010Image.w * mRawP010Image.h * 4);
+ mRawRgba1010102Image.planes[1] = nullptr;
+ mRawRgba1010102Image.planes[2] = nullptr;
+ mRawRgba1010102Image.stride[0] = mWidth;
+ mRawRgba1010102Image.stride[1] = 0;
+ mRawRgba1010102Image.stride[2] = 0;
+
+ uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba1010102Image.planes[0]);
+ uint16_t* y = static_cast<uint16_t*>(mRawP010Image.planes[0]);
+ uint16_t* u = static_cast<uint16_t*>(mRawP010Image.planes[1]);
uint16_t* v = u + 1;
- for (size_t i = 0; i < mRawP010Image.height; i++) {
- for (size_t j = 0; j < mRawP010Image.width; j++) {
- float y0 = float(y[mRawP010Image.width * i + j] >> 6);
- float u0 = float(u[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6);
- float v0 = float(v[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6);
+ for (size_t i = 0; i < mRawP010Image.h; i++) {
+ for (size_t j = 0; j < mRawP010Image.w; j++) {
+ float y0 = float(y[mRawP010Image.stride[0] * i + j] >> 6);
+ float u0 = float(u[mRawP010Image.stride[1] * (i / 2) + (j / 2) * 2] >> 6);
+ float v0 = float(v[mRawP010Image.stride[1] * (i / 2) + (j / 2) * 2] >> 6);
y0 = CLIP3(y0, 64.0f, 940.0f);
u0 = CLIP3(u0, 64.0f, 960.0f);
@@ -462,31 +577,35 @@ bool UltraHdrAppInput::convertP010ToRGBImage() {
rgbData++;
}
}
- writeFile("inRgba1010102.raw", mRawRgba1010102Image.data,
- mRawP010Image.width * mRawP010Image.height * 4);
+ writeFile("inRgba1010102.raw", &mRawRgba1010102Image);
return true;
}
bool UltraHdrAppInput::convertYuv420ToRGBImage() {
- mRawRgba8888Image.data = malloc(mRawYuv420Image.width * mRawYuv420Image.height * 4);
- if (mRawRgba8888Image.data == nullptr) {
- std::cerr << "failed to allocate memory to store rgba888" << std::endl;
- return false;
- }
- mRawRgba8888Image.width = mRawYuv420Image.width;
- mRawRgba8888Image.height = mRawYuv420Image.height;
- mRawRgba8888Image.colorGamut = mRawYuv420Image.colorGamut;
- uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba8888Image.data);
- uint8_t* y = static_cast<uint8_t*>(mRawYuv420Image.data);
- uint8_t* u = y + (mRawYuv420Image.width * mRawYuv420Image.height);
- uint8_t* v = u + (mRawYuv420Image.width * mRawYuv420Image.height / 4);
+ mRawRgba8888Image.fmt = UHDR_IMG_FMT_32bppRGBA8888;
+ mRawRgba8888Image.cg = mRawYuv420Image.cg;
+ mRawRgba8888Image.ct = mRawYuv420Image.ct;
+ mRawRgba8888Image.range = UHDR_CR_FULL_RANGE;
+ mRawRgba8888Image.w = mRawYuv420Image.w;
+ mRawRgba8888Image.h = mRawYuv420Image.h;
+ mRawRgba8888Image.planes[0] = malloc(mRawYuv420Image.w * mRawYuv420Image.h * 4);
+ mRawRgba8888Image.planes[1] = nullptr;
+ mRawRgba8888Image.planes[2] = nullptr;
+ mRawRgba8888Image.stride[0] = mWidth;
+ mRawRgba8888Image.stride[1] = 0;
+ mRawRgba8888Image.stride[2] = 0;
+
+ uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba8888Image.planes[0]);
+ uint8_t* y = static_cast<uint8_t*>(mRawYuv420Image.planes[0]);
+ uint8_t* u = static_cast<uint8_t*>(mRawYuv420Image.planes[1]);
+ uint8_t* v = static_cast<uint8_t*>(mRawYuv420Image.planes[2]);
const float* coeffs = BT601YUVtoRGBMatrix;
- for (size_t i = 0; i < mRawYuv420Image.height; i++) {
- for (size_t j = 0; j < mRawYuv420Image.width; j++) {
- float y0 = float(y[mRawYuv420Image.width * i + j]);
- float u0 = float(u[mRawYuv420Image.width / 2 * (i / 2) + (j / 2)] - 128);
- float v0 = float(v[mRawYuv420Image.width / 2 * (i / 2) + (j / 2)] - 128);
+ for (size_t i = 0; i < mRawYuv420Image.h; i++) {
+ for (size_t j = 0; j < mRawYuv420Image.w; j++) {
+ float y0 = float(y[mRawYuv420Image.stride[0] * i + j]);
+ float u0 = float(u[mRawYuv420Image.stride[1] * (i / 2) + (j / 2)] - 128);
+ float v0 = float(v[mRawYuv420Image.stride[2] * (i / 2) + (j / 2)] - 128);
y0 /= 255.0f;
u0 /= 255.0f;
@@ -512,33 +631,36 @@ bool UltraHdrAppInput::convertYuv420ToRGBImage() {
rgbData++;
}
}
- writeFile("inRgba8888.raw", mRawRgba8888Image.data,
- mRawYuv420Image.width * mRawYuv420Image.height * 4);
+ writeFile("inRgba8888.raw", &mRawRgba8888Image);
return true;
}
bool UltraHdrAppInput::convertRgba8888ToYUV444Image() {
- mDestYUV444Image.data = malloc(mDestImage.width * mDestImage.height * 3);
- if (mDestYUV444Image.data == nullptr) {
- std::cerr << "failed to allocate memory to store yuv444" << std::endl;
- return false;
- }
- mDestYUV444Image.width = mDestImage.width;
- mDestYUV444Image.height = mDestImage.height;
- mDestYUV444Image.colorGamut = mDestImage.colorGamut;
-
- uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.data);
-
- uint8_t* yData = static_cast<uint8_t*>(mDestYUV444Image.data);
- uint8_t* uData = yData + (mDestYUV444Image.width * mDestYUV444Image.height);
- uint8_t* vData = uData + (mDestYUV444Image.width * mDestYUV444Image.height);
+ mDestYUV444Image.fmt = static_cast<uhdr_img_fmt_t>(UHDR_IMG_FMT_24bppYCbCr444);
+ mDestYUV444Image.cg = mDestImage.cg;
+ mDestYUV444Image.ct = mDestImage.ct;
+ mDestYUV444Image.range = UHDR_CR_FULL_RANGE;
+ mDestYUV444Image.w = mDestImage.w;
+ mDestYUV444Image.h = mDestImage.h;
+ mDestYUV444Image.planes[0] = malloc(mDestImage.w * mDestImage.h);
+ mDestYUV444Image.planes[1] = malloc(mDestImage.w * mDestImage.h);
+ mDestYUV444Image.planes[2] = malloc(mDestImage.w * mDestImage.h);
+ mDestYUV444Image.stride[0] = mWidth;
+ mDestYUV444Image.stride[1] = mWidth;
+ mDestYUV444Image.stride[2] = mWidth;
+
+ uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.planes[0]);
+
+ uint8_t* yData = static_cast<uint8_t*>(mDestYUV444Image.planes[0]);
+ uint8_t* uData = static_cast<uint8_t*>(mDestYUV444Image.planes[1]);
+ uint8_t* vData = static_cast<uint8_t*>(mDestYUV444Image.planes[2]);
const float* coeffs = BT601RGBtoYUVMatrix;
- for (size_t i = 0; i < mDestImage.height; i++) {
- for (size_t j = 0; j < mDestImage.width; j++) {
- float r0 = float(rgbData[mDestImage.width * i + j] & 0xff);
- float g0 = float((rgbData[mDestImage.width * i + j] >> 8) & 0xff);
- float b0 = float((rgbData[mDestImage.width * i + j] >> 16) & 0xff);
+ for (size_t i = 0; i < mDestImage.h; i++) {
+ for (size_t j = 0; j < mDestImage.w; j++) {
+ float r0 = float(rgbData[mDestImage.stride[0] * i + j] & 0xff);
+ float g0 = float((rgbData[mDestImage.stride[0] * i + j] >> 8) & 0xff);
+ float b0 = float((rgbData[mDestImage.stride[0] * i + j] >> 16) & 0xff);
r0 /= 255.0f;
g0 /= 255.0f;
@@ -556,49 +678,52 @@ bool UltraHdrAppInput::convertRgba8888ToYUV444Image() {
u = CLIP3(u, 0.0f, 255.0f);
v = CLIP3(v, 0.0f, 255.0f);
- yData[mDestYUV444Image.width * i + j] = uint8_t(y);
- uData[mDestYUV444Image.width * i + j] = uint8_t(u);
- vData[mDestYUV444Image.width * i + j] = uint8_t(v);
+ yData[mDestYUV444Image.stride[0] * i + j] = uint8_t(y);
+ uData[mDestYUV444Image.stride[1] * i + j] = uint8_t(u);
+ vData[mDestYUV444Image.stride[2] * i + j] = uint8_t(v);
}
}
- writeFile("outyuv444.yuv", mDestYUV444Image.data,
- mDestYUV444Image.width * mDestYUV444Image.height * 3);
+ writeFile("outyuv444.yuv", &mDestYUV444Image);
return true;
}
bool UltraHdrAppInput::convertRgba1010102ToYUV444Image() {
const float* coeffs = BT2020RGBtoYUVMatrix;
- if (mP010Cg == ULTRAHDR_COLORGAMUT_BT709) {
+ if (mP010Cg == UHDR_CG_BT_709) {
coeffs = BT709RGBtoYUVMatrix;
- } else if (mP010Cg == ULTRAHDR_COLORGAMUT_BT2100) {
+ } else if (mP010Cg == UHDR_CG_BT_2100) {
coeffs = BT2020RGBtoYUVMatrix;
- } else if (mP010Cg == ULTRAHDR_COLORGAMUT_P3) {
+ } else if (mP010Cg == UHDR_CG_DISPLAY_P3) {
coeffs = BT601RGBtoYUVMatrix;
} else {
std::cerr << "color matrix not present for gamut " << mP010Cg << " using BT2020Matrix"
<< std::endl;
}
- mDestYUV444Image.data = malloc(mDestImage.width * mDestImage.height * 3 * 2);
- if (mDestYUV444Image.data == nullptr) {
- std::cerr << "failed to allocate memory to store yuv444" << std::endl;
- return false;
- }
- mDestYUV444Image.width = mDestImage.width;
- mDestYUV444Image.height = mDestImage.height;
- mDestYUV444Image.colorGamut = mDestImage.colorGamut;
-
- uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.data);
-
- uint16_t* yData = static_cast<uint16_t*>(mDestYUV444Image.data);
- uint16_t* uData = yData + (mDestYUV444Image.width * mDestYUV444Image.height);
- uint16_t* vData = uData + (mDestYUV444Image.width * mDestYUV444Image.height);
-
- for (size_t i = 0; i < mDestImage.height; i++) {
- for (size_t j = 0; j < mDestImage.width; j++) {
- float r0 = float(rgbData[mDestImage.width * i + j] & 0x3ff);
- float g0 = float((rgbData[mDestImage.width * i + j] >> 10) & 0x3ff);
- float b0 = float((rgbData[mDestImage.width * i + j] >> 20) & 0x3ff);
+ mDestYUV444Image.fmt = static_cast<uhdr_img_fmt_t>(UHDR_IMG_FMT_48bppYCbCr444);
+ mDestYUV444Image.cg = mDestImage.cg;
+ mDestYUV444Image.ct = mDestImage.ct;
+ mDestYUV444Image.range = UHDR_CR_FULL_RANGE;
+ mDestYUV444Image.w = mDestImage.w;
+ mDestYUV444Image.h = mDestImage.h;
+ mDestYUV444Image.planes[0] = malloc(mDestImage.w * mDestImage.h * 2);
+ mDestYUV444Image.planes[1] = malloc(mDestImage.w * mDestImage.h * 2);
+ mDestYUV444Image.planes[2] = malloc(mDestImage.w * mDestImage.h * 2);
+ mDestYUV444Image.stride[0] = mWidth;
+ mDestYUV444Image.stride[1] = mWidth;
+ mDestYUV444Image.stride[2] = mWidth;
+
+ uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.planes[0]);
+
+ uint16_t* yData = static_cast<uint16_t*>(mDestYUV444Image.planes[0]);
+ uint16_t* uData = static_cast<uint16_t*>(mDestYUV444Image.planes[1]);
+ uint16_t* vData = static_cast<uint16_t*>(mDestYUV444Image.planes[2]);
+
+ for (size_t i = 0; i < mDestImage.h; i++) {
+ for (size_t j = 0; j < mDestImage.w; j++) {
+ float r0 = float(rgbData[mDestImage.stride[0] * i + j] & 0x3ff);
+ float g0 = float((rgbData[mDestImage.stride[0] * i + j] >> 10) & 0x3ff);
+ float b0 = float((rgbData[mDestImage.stride[0] * i + j] >> 20) & 0x3ff);
r0 /= 1023.0f;
g0 /= 1023.0f;
@@ -616,35 +741,33 @@ bool UltraHdrAppInput::convertRgba1010102ToYUV444Image() {
u = CLIP3(u, 64.0f, 960.0f);
v = CLIP3(v, 64.0f, 960.0f);
- yData[mDestYUV444Image.width * i + j] = uint16_t(y);
- uData[mDestYUV444Image.width * i + j] = uint16_t(u);
- vData[mDestYUV444Image.width * i + j] = uint16_t(v);
+ yData[mDestYUV444Image.stride[0] * i + j] = uint16_t(y);
+ uData[mDestYUV444Image.stride[1] * i + j] = uint16_t(u);
+ vData[mDestYUV444Image.stride[2] * i + j] = uint16_t(v);
}
}
- writeFile("outyuv444.yuv", mDestYUV444Image.data,
- mDestYUV444Image.width * mDestYUV444Image.height * 3 * 2);
+ writeFile("outyuv444.yuv", &mDestYUV444Image);
return true;
}
void UltraHdrAppInput::computeRGBHdrPSNR() {
- if (mOf == ULTRAHDR_OUTPUT_SDR || mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) {
- std::cout << "psnr not supported for output format " << mOf << std::endl;
+ if (mOfmt != UHDR_IMG_FMT_32bppRGBA1010102) {
+ std::cout << "psnr not supported for output format " << mOfmt << std::endl;
return;
}
- uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba1010102Image.data);
- uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.data);
+ uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba1010102Image.planes[0]);
+ uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.planes[0]);
if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
return;
}
- if ((mOf == ULTRAHDR_OUTPUT_HDR_PQ && mTf != ULTRAHDR_TF_PQ) ||
- (mOf == ULTRAHDR_OUTPUT_HDR_HLG && mTf != ULTRAHDR_TF_HLG)) {
+ if (mOTf != mP010Tf) {
std::cout << "input transfer function and output format are not compatible, psnr results "
"may be unreliable"
<< std::endl;
}
uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
- for (size_t i = 0; i < mRawP010Image.width * mRawP010Image.height; i++) {
+ for (size_t i = 0; i < mRawP010Image.w * mRawP010Image.h; i++) {
int rSrc = *rgbDataSrc & 0x3ff;
int rDst = *rgbDataDst & 0x3ff;
rSqError += (rSrc - rDst) * (rSrc - rDst);
@@ -660,13 +783,13 @@ void UltraHdrAppInput::computeRGBHdrPSNR() {
rgbDataSrc++;
rgbDataDst++;
}
- double meanSquareError = (double)rSqError / (mRawP010Image.width * mRawP010Image.height);
+ double meanSquareError = (double)rSqError / (mRawP010Image.w * mRawP010Image.h);
mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
- meanSquareError = (double)gSqError / (mRawP010Image.width * mRawP010Image.height);
+ meanSquareError = (double)gSqError / (mRawP010Image.w * mRawP010Image.h);
mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
- meanSquareError = (double)bSqError / (mRawP010Image.width * mRawP010Image.height);
+ meanSquareError = (double)bSqError / (mRawP010Image.w * mRawP010Image.h);
mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
@@ -674,19 +797,19 @@ void UltraHdrAppInput::computeRGBHdrPSNR() {
}
void UltraHdrAppInput::computeRGBSdrPSNR() {
- if (mOf != ULTRAHDR_OUTPUT_SDR) {
- std::cout << "psnr not supported for output format " << mOf << std::endl;
+ if (mOfmt != UHDR_IMG_FMT_32bppRGBA8888) {
+ std::cout << "psnr not supported for output format " << mOfmt << std::endl;
return;
}
- uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba8888Image.data);
- uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.data);
+ uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba8888Image.planes[0]);
+ uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.planes[0]);
if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
return;
}
uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
- for (size_t i = 0; i < mRawYuv420Image.width * mRawYuv420Image.height; i++) {
+ for (size_t i = 0; i < mRawYuv420Image.w * mRawYuv420Image.h; i++) {
int rSrc = *rgbDataSrc & 0xff;
int rDst = *rgbDataDst & 0xff;
rSqError += (rSrc - rDst) * (rSrc - rDst);
@@ -702,13 +825,13 @@ void UltraHdrAppInput::computeRGBSdrPSNR() {
rgbDataSrc++;
rgbDataDst++;
}
- double meanSquareError = (double)rSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
+ double meanSquareError = (double)rSqError / (mRawYuv420Image.w * mRawYuv420Image.h);
mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
- meanSquareError = (double)gSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
+ meanSquareError = (double)gSqError / (mRawYuv420Image.w * mRawYuv420Image.h);
mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
- meanSquareError = (double)bSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
+ meanSquareError = (double)bSqError / (mRawYuv420Image.w * mRawYuv420Image.h);
mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
@@ -716,68 +839,65 @@ void UltraHdrAppInput::computeRGBSdrPSNR() {
}
void UltraHdrAppInput::computeYUVHdrPSNR() {
- if (mOf == ULTRAHDR_OUTPUT_SDR || mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) {
- std::cout << "psnr not supported for output format " << mOf << std::endl;
+ if (mOfmt != UHDR_IMG_FMT_32bppRGBA1010102) {
+ std::cout << "psnr not supported for output format " << mOfmt << std::endl;
return;
}
- uint16_t* yuvDataSrc = static_cast<uint16_t*>(mRawP010Image.data);
- uint16_t* yuvDataDst = static_cast<uint16_t*>(mDestYUV444Image.data);
- if (yuvDataSrc == nullptr || yuvDataDst == nullptr) {
+ uint16_t* yDataSrc = static_cast<uint16_t*>(mRawP010Image.planes[0]);
+ uint16_t* uDataSrc = static_cast<uint16_t*>(mRawP010Image.planes[1]);
+ uint16_t* vDataSrc = uDataSrc + 1;
+
+ uint16_t* yDataDst = static_cast<uint16_t*>(mDestYUV444Image.planes[0]);
+ uint16_t* uDataDst = static_cast<uint16_t*>(mDestYUV444Image.planes[1]);
+ uint16_t* vDataDst = static_cast<uint16_t*>(mDestYUV444Image.planes[2]);
+ if (yDataSrc == nullptr || uDataSrc == nullptr || yDataDst == nullptr || uDataDst == nullptr ||
+ vDataDst == nullptr) {
std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
return;
}
- if ((mOf == ULTRAHDR_OUTPUT_HDR_PQ && mTf != ULTRAHDR_TF_PQ) ||
- (mOf == ULTRAHDR_OUTPUT_HDR_HLG && mTf != ULTRAHDR_TF_HLG)) {
+ if (mOTf != mP010Tf) {
std::cout << "input transfer function and output format are not compatible, psnr results "
"may be unreliable"
<< std::endl;
}
- uint16_t* yDataSrc = static_cast<uint16_t*>(mRawP010Image.data);
- uint16_t* uDataSrc = yDataSrc + (mRawP010Image.width * mRawP010Image.height);
- uint16_t* vDataSrc = uDataSrc + 1;
-
- uint16_t* yDataDst = static_cast<uint16_t*>(mDestYUV444Image.data);
- uint16_t* uDataDst = yDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
- uint16_t* vDataDst = uDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
-
uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
- for (size_t i = 0; i < mDestYUV444Image.height; i++) {
- for (size_t j = 0; j < mDestYUV444Image.width; j++) {
- int ySrc = (yDataSrc[mRawP010Image.width * i + j] >> 6) & 0x3ff;
+ for (size_t i = 0; i < mDestYUV444Image.h; i++) {
+ for (size_t j = 0; j < mDestYUV444Image.w; j++) {
+ int ySrc = (yDataSrc[mRawP010Image.stride[0] * i + j] >> 6) & 0x3ff;
ySrc = CLIP3(ySrc, 64, 940);
- int yDst = yDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
+ int yDst = yDataDst[mDestYUV444Image.stride[0] * i + j] & 0x3ff;
ySqError += (ySrc - yDst) * (ySrc - yDst);
if (i % 2 == 0 && j % 2 == 0) {
- int uSrc = (uDataSrc[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
+ int uSrc = (uDataSrc[mRawP010Image.stride[1] * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
uSrc = CLIP3(uSrc, 64, 960);
- int uDst = uDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
- uDst += uDataDst[mDestYUV444Image.width * i + j + 1] & 0x3ff;
- uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
- uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ int uDst = uDataDst[mDestYUV444Image.stride[1] * i + j] & 0x3ff;
+ uDst += uDataDst[mDestYUV444Image.stride[1] * i + j + 1] & 0x3ff;
+ uDst += uDataDst[mDestYUV444Image.stride[1] * (i + 1) + j + 1] & 0x3ff;
+ uDst += uDataDst[mDestYUV444Image.stride[1] * (i + 1) + j + 1] & 0x3ff;
uDst = (uDst + 2) >> 2;
uSqError += (uSrc - uDst) * (uSrc - uDst);
- int vSrc = (vDataSrc[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
+ int vSrc = (vDataSrc[mRawP010Image.stride[1] * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
vSrc = CLIP3(vSrc, 64, 960);
- int vDst = vDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
- vDst += vDataDst[mDestYUV444Image.width * i + j + 1] & 0x3ff;
- vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
- vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ int vDst = vDataDst[mDestYUV444Image.stride[2] * i + j] & 0x3ff;
+ vDst += vDataDst[mDestYUV444Image.stride[2] * i + j + 1] & 0x3ff;
+ vDst += vDataDst[mDestYUV444Image.stride[2] * (i + 1) + j + 1] & 0x3ff;
+ vDst += vDataDst[mDestYUV444Image.stride[2] * (i + 1) + j + 1] & 0x3ff;
vDst = (vDst + 2) >> 2;
vSqError += (vSrc - vDst) * (vSrc - vDst);
}
}
}
- double meanSquareError = (double)ySqError / (mDestYUV444Image.width * mDestYUV444Image.height);
+ double meanSquareError = (double)ySqError / (mDestYUV444Image.w * mDestYUV444Image.h);
mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
- meanSquareError = (double)uSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ meanSquareError = (double)uSqError / (mDestYUV444Image.w * mDestYUV444Image.h / 4);
mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
- meanSquareError = (double)vSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ meanSquareError = (double)vSqError / (mDestYUV444Image.w * mDestYUV444Image.h / 4);
mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
std::cout << "psnr y :: " << mPsnr[0] << " psnr u :: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
@@ -785,52 +905,52 @@ void UltraHdrAppInput::computeYUVHdrPSNR() {
}
void UltraHdrAppInput::computeYUVSdrPSNR() {
- if (mOf != ULTRAHDR_OUTPUT_SDR) {
- std::cout << "psnr not supported for output format " << mOf << std::endl;
+ if (mOfmt != UHDR_IMG_FMT_32bppRGBA8888) {
+ std::cout << "psnr not supported for output format " << mOfmt << std::endl;
return;
}
- uint8_t* yDataSrc = static_cast<uint8_t*>(mRawYuv420Image.data);
- uint8_t* uDataSrc = yDataSrc + (mRawYuv420Image.width * mRawYuv420Image.height);
- uint8_t* vDataSrc = uDataSrc + (mRawYuv420Image.width * mRawYuv420Image.height / 4);
+ uint8_t* yDataSrc = static_cast<uint8_t*>(mRawYuv420Image.planes[0]);
+ uint8_t* uDataSrc = static_cast<uint8_t*>(mRawYuv420Image.planes[1]);
+ uint8_t* vDataSrc = static_cast<uint8_t*>(mRawYuv420Image.planes[2]);
- uint8_t* yDataDst = static_cast<uint8_t*>(mDestYUV444Image.data);
- uint8_t* uDataDst = yDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
- uint8_t* vDataDst = uDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
+ uint8_t* yDataDst = static_cast<uint8_t*>(mDestYUV444Image.planes[0]);
+ uint8_t* uDataDst = static_cast<uint8_t*>(mDestYUV444Image.planes[1]);
+ uint8_t* vDataDst = static_cast<uint8_t*>(mDestYUV444Image.planes[2]);
uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
- for (size_t i = 0; i < mDestYUV444Image.height; i++) {
- for (size_t j = 0; j < mDestYUV444Image.width; j++) {
- int ySrc = yDataSrc[mRawYuv420Image.width * i + j];
- int yDst = yDataDst[mDestYUV444Image.width * i + j];
+ for (size_t i = 0; i < mDestYUV444Image.h; i++) {
+ for (size_t j = 0; j < mDestYUV444Image.w; j++) {
+ int ySrc = yDataSrc[mRawYuv420Image.stride[0] * i + j];
+ int yDst = yDataDst[mDestYUV444Image.stride[0] * i + j];
ySqError += (ySrc - yDst) * (ySrc - yDst);
if (i % 2 == 0 && j % 2 == 0) {
- int uSrc = uDataSrc[mRawYuv420Image.width / 2 * (i / 2) + j / 2];
- int uDst = uDataDst[mDestYUV444Image.width * i + j];
- uDst += uDataDst[mDestYUV444Image.width * i + j + 1];
- uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j];
- uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1];
+ int uSrc = uDataSrc[mRawYuv420Image.stride[1] * (i / 2) + j / 2];
+ int uDst = uDataDst[mDestYUV444Image.stride[1] * i + j];
+ uDst += uDataDst[mDestYUV444Image.stride[1] * i + j + 1];
+ uDst += uDataDst[mDestYUV444Image.stride[1] * (i + 1) + j];
+ uDst += uDataDst[mDestYUV444Image.stride[1] * (i + 1) + j + 1];
uDst = (uDst + 2) >> 2;
uSqError += (uSrc - uDst) * (uSrc - uDst);
- int vSrc = vDataSrc[mRawYuv420Image.width / 2 * (i / 2) + j / 2];
- int vDst = vDataDst[mDestYUV444Image.width * i + j];
- vDst += vDataDst[mDestYUV444Image.width * i + j + 1];
- vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j];
- vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1];
+ int vSrc = vDataSrc[mRawYuv420Image.stride[2] * (i / 2) + j / 2];
+ int vDst = vDataDst[mDestYUV444Image.stride[2] * i + j];
+ vDst += vDataDst[mDestYUV444Image.stride[2] * i + j + 1];
+ vDst += vDataDst[mDestYUV444Image.stride[2] * (i + 1) + j];
+ vDst += vDataDst[mDestYUV444Image.stride[2] * (i + 1) + j + 1];
vDst = (vDst + 2) >> 2;
vSqError += (vSrc - vDst) * (vSrc - vDst);
}
}
}
- double meanSquareError = (double)ySqError / (mDestYUV444Image.width * mDestYUV444Image.height);
+ double meanSquareError = (double)ySqError / (mDestYUV444Image.w * mDestYUV444Image.h);
mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
- meanSquareError = (double)uSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ meanSquareError = (double)uSqError / (mDestYUV444Image.w * mDestYUV444Image.h / 4);
mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
- meanSquareError = (double)vSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ meanSquareError = (double)vSqError / (mDestYUV444Image.w * mDestYUV444Image.h / 4);
mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
std::cout << "psnr y :: " << mPsnr[0] << " psnr u:: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
@@ -850,7 +970,7 @@ static void usage(const char* name) {
fprintf(stderr, " -h input file height, mandatory. \n");
fprintf(stderr, " -C 10 bit input color gamut, optional. [0:bt709, 1:p3, 2:bt2100] \n");
fprintf(stderr, " -c 8 bit input color gamut, optional. [0:bt709, 1:p3, 2:bt2100] \n");
- fprintf(stderr, " -t input transfer function, optional. [0:linear, 1:hlg, 2:pq] \n");
+ fprintf(stderr, " -t 10 bit input transfer function, optional. [0:linear, 1:hlg, 2:pq] \n");
fprintf(stderr,
" -q quality factor to be used while encoding 8 bit image, optional. [0-100].\n"
" gain map image does not use this quality factor. \n"
@@ -859,22 +979,23 @@ static void usage(const char* name) {
fprintf(stderr, "\n## decoder options : \n");
fprintf(stderr, " -j ultra hdr input resource, mandatory in decode mode. \n");
fprintf(stderr,
- " -o output transfer function, optional. [0:sdr, 1:hdr_linear, 2:hdr_pq, "
- "3:hdr_hlg] \n");
+ " -o output transfer function, optional. [0:linear, 1:hlg, 2:pq, 3:srgb] \n");
+ fprintf(stderr,
+ " -O output color format, optional. [3:rgba8888, 4:rgbahalffloat, 5:rgba1010102] \n"
+ "It should be noted that not all combinations of output color format and output transfer "
+ "function are supported. srgb output color transfer shall be paired with rgba8888 only. "
+ "hlg, pq shall be paired with rgba1010102. linear shall be paired with rgbahalffloat");
fprintf(stderr, "\n## examples of usage :\n");
fprintf(stderr, "\n## encode api-0 :\n");
fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97\n");
fprintf(stderr,
- " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -C 2 -t 2\n");
+ " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -C 1 -t 2\n");
fprintf(stderr, "\n## encode api-1 :\n");
fprintf(stderr,
" ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
"-h 1080 -q 97\n");
fprintf(stderr,
" ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
- "-h 1080 -q 97\n");
- fprintf(stderr,
- " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
"-h 1080 -q 97 -C 2 -c 1 -t 1\n");
fprintf(stderr,
" ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
@@ -882,27 +1003,29 @@ static void usage(const char* name) {
fprintf(stderr, "\n## encode api-2 :\n");
fprintf(stderr,
" ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -i "
- "cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -e 1\n");
+ "cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -O 3 -e 1\n");
fprintf(stderr, "\n## encode api-3 :\n");
fprintf(stderr,
" ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -i cosmat_1920x1080_420_8bit.jpg -w "
- "1920 -h 1080 -t 1 -o 3 -e 1\n");
+ "1920 -h 1080 -t 1 -o 1 -O 5 -e 1\n");
fprintf(stderr, "\n## decode api :\n");
fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg \n");
- fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg -o 2\n");
+ fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg -o 3 -O 3\n");
+ fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg -o 1 -O 5\n");
fprintf(stderr, "\n");
}
int main(int argc, char* argv[]) {
- char opt_string[] = "p:y:i:w:h:C:c:t:q:o:m:j:e:";
+ char opt_string[] = "p:y:i:w:h:C:c:t:q:o:O:m:j:e:";
char *p010_file = nullptr, *yuv420_file = nullptr, *jpegr_file = nullptr,
*yuv420_jpeg_file = nullptr;
int width = 0, height = 0;
- ultrahdr_color_gamut p010Cg = ULTRAHDR_COLORGAMUT_BT709;
- ultrahdr_color_gamut yuv420Cg = ULTRAHDR_COLORGAMUT_BT709;
- ultrahdr_transfer_function tf = ULTRAHDR_TF_HLG;
+ uhdr_color_gamut_t p010Cg = UHDR_CG_BT_709;
+ uhdr_color_gamut_t yuv420Cg = UHDR_CG_BT_709;
+ uhdr_color_transfer_t p010Tf = UHDR_CT_HLG;
int quality = 100;
- ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG;
+ uhdr_color_transfer_t outTf = UHDR_CT_HLG;
+ uhdr_img_fmt_t outFmt = UHDR_IMG_FMT_32bppRGBA1010102;
int mode = 0;
int compute_psnr = 0;
int ch;
@@ -924,19 +1047,22 @@ int main(int argc, char* argv[]) {
height = atoi(optarg_s);
break;
case 'C':
- p010Cg = static_cast<ultrahdr_color_gamut>(atoi(optarg_s));
+ p010Cg = static_cast<uhdr_color_gamut_t>(atoi(optarg_s));
break;
case 'c':
- yuv420Cg = static_cast<ultrahdr_color_gamut>(atoi(optarg_s));
+ yuv420Cg = static_cast<uhdr_color_gamut_t>(atoi(optarg_s));
break;
case 't':
- tf = static_cast<ultrahdr_transfer_function>(atoi(optarg_s));
+ p010Tf = static_cast<uhdr_color_transfer_t>(atoi(optarg_s));
break;
case 'q':
quality = atoi(optarg_s);
break;
+ case 'O':
+ outFmt = static_cast<uhdr_img_fmt_t>(atoi(optarg_s));
+ break;
case 'o':
- of = static_cast<ultrahdr_output_format>(atoi(optarg_s));
+ outTf = static_cast<uhdr_color_transfer_t>(atoi(optarg_s));
break;
case 'm':
mode = atoi(optarg_s);
@@ -958,16 +1084,16 @@ int main(int argc, char* argv[]) {
return -1;
}
UltraHdrAppInput appInput(p010_file, yuv420_file, yuv420_jpeg_file, width, height, p010Cg,
- yuv420Cg, tf, quality, of);
+ yuv420Cg, p010Tf, quality, outTf, outFmt);
if (!appInput.encode()) return -1;
if (compute_psnr == 1) {
if (!appInput.decode()) return -1;
- if (of == ULTRAHDR_OUTPUT_SDR && yuv420_file != nullptr) {
+ if (outFmt == UHDR_IMG_FMT_32bppRGBA8888 && yuv420_file != nullptr) {
appInput.convertYuv420ToRGBImage();
appInput.computeRGBSdrPSNR();
appInput.convertRgba8888ToYUV444Image();
appInput.computeYUVSdrPSNR();
- } else if (of == ULTRAHDR_OUTPUT_HDR_HLG || of == ULTRAHDR_OUTPUT_HDR_PQ) {
+ } else if (outFmt == UHDR_IMG_FMT_32bppRGBA1010102) {
appInput.convertP010ToRGBImage();
appInput.computeRGBHdrPSNR();
appInput.convertRgba1010102ToYUV444Image();
@@ -979,7 +1105,7 @@ int main(int argc, char* argv[]) {
usage(argv[0]);
return -1;
}
- UltraHdrAppInput appInput(jpegr_file, of);
+ UltraHdrAppInput appInput(jpegr_file, outTf, outFmt);
if (!appInput.decode()) return -1;
} else {
std::cerr << "unrecognized input mode " << mode << std::endl;
diff --git a/lib/include/ultrahdr/jpegdecoderhelper.h b/lib/include/ultrahdr/jpegdecoderhelper.h
index d0b1f6f..4d5b42e 100644
--- a/lib/include/ultrahdr/jpegdecoderhelper.h
+++ b/lib/include/ultrahdr/jpegdecoderhelper.h
@@ -35,13 +35,13 @@ extern "C" {
#include <memory>
#include <vector>
+namespace ultrahdr {
+
// constraint on max width and max height is only due to device alloc constraints
// Can tune these values basing on the target device
static const int kMaxWidth = 8192;
static const int kMaxHeight = 8192;
-namespace ultrahdr {
-
typedef enum {
PARSE_ONLY = 0, // Dont decode. Parse for dimensions, EXIF, ICC, XMP
DECODE_TO_RGBA = 1, // Parse and decode to rgba
diff --git a/lib/include/ultrahdr/ultrahdrcommon.h b/lib/include/ultrahdr/ultrahdrcommon.h
index ba3a3b8..8a80f5a 100644
--- a/lib/include/ultrahdr/ultrahdrcommon.h
+++ b/lib/include/ultrahdr/ultrahdrcommon.h
@@ -19,6 +19,16 @@
//#define LOG_NDEBUG 0
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "ultrahdr_api.h"
+
+// ===============================================================================================
+// Function Macros
+// ===============================================================================================
+
#ifdef __ANDROID__
#include "log/log.h"
#else
@@ -61,4 +71,79 @@
#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m))
+namespace ultrahdr {
+
+// ===============================================================================================
+// Structure Definitions
+// ===============================================================================================
+
+/**\brief uhdr memory block */
+typedef struct uhdr_memory_block {
+ uhdr_memory_block(size_t capacity);
+
+ std::unique_ptr<uint8_t[]> m_buffer; /**< data */
+ size_t m_capacity; /**< capacity */
+} uhdr_memory_block_t; /**< alias for struct uhdr_memory_block */
+
+/**\brief extended raw image descriptor */
+typedef struct uhdr_raw_image_ext : uhdr_raw_image_t {
+ uhdr_raw_image_ext(uhdr_img_fmt_t fmt, uhdr_color_gamut_t cg, uhdr_color_transfer_t ct,
+ uhdr_color_range_t range, unsigned w, unsigned h, unsigned align_stride_to);
+
+ private:
+ std::unique_ptr<ultrahdr::uhdr_memory_block> m_block;
+} uhdr_raw_image_ext_t; /**< alias for struct uhdr_raw_image_ext */
+
+/**\brief extended compressed image descriptor */
+typedef struct uhdr_compressed_image_ext : uhdr_compressed_image_t {
+ uhdr_compressed_image_ext(uhdr_color_gamut_t cg, uhdr_color_transfer_t ct,
+ uhdr_color_range_t range, unsigned sz);
+
+ private:
+ std::unique_ptr<ultrahdr::uhdr_memory_block> m_block;
+} uhdr_compressed_image_ext_t; /**< alias for struct uhdr_compressed_image_ext */
+
+} // namespace ultrahdr
+
+// ===============================================================================================
+// Extensions of ultrahdr api definitions, so outside ultrahdr namespace
+// ===============================================================================================
+
+struct uhdr_codec_private {};
+
+struct uhdr_encoder_private : uhdr_codec_private {
+ // config data
+ std::map<uhdr_img_label, std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t>> m_raw_images;
+ std::map<uhdr_img_label, std::unique_ptr<ultrahdr::uhdr_compressed_image_ext_t>>
+ m_compressed_images;
+ std::map<uhdr_img_label, int> m_quality;
+ std::vector<uint8_t> m_exif;
+ uhdr_gainmap_metadata_t m_metadata;
+ uhdr_codec_t m_output_format;
+
+ // internal data
+ bool m_sailed;
+ std::unique_ptr<ultrahdr::uhdr_compressed_image_ext_t> m_compressed_output_buffer;
+ uhdr_error_info_t m_encode_call_status;
+};
+
+struct uhdr_decoder_private : uhdr_codec_private {
+ // config data
+ std::unique_ptr<ultrahdr::uhdr_compressed_image_ext_t> m_uhdr_compressed_img;
+ uhdr_img_fmt_t m_output_fmt;
+ uhdr_color_transfer_t m_output_ct;
+ float m_output_max_disp_boost;
+
+ // internal data
+ bool m_sailed;
+ std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> m_decoded_img_buffer;
+ std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> m_gainmap_img_buffer;
+ std::vector<uint8_t> m_exif;
+ std::vector<uint8_t> m_icc;
+ std::vector<uint8_t> m_base_xmp;
+ std::vector<uint8_t> m_gainmap_xmp;
+ uhdr_gainmap_metadata_t m_metadata;
+ uhdr_error_info_t m_decode_call_status;
+};
+
#endif // ULTRAHDR_ULTRAHDRCOMMON_H
diff --git a/lib/src/ultrahdr_api.cpp b/lib/src/ultrahdr_api.cpp
new file mode 100644
index 0000000..bf97d43
--- /dev/null
+++ b/lib/src/ultrahdr_api.cpp
@@ -0,0 +1,1146 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * 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 <cstdio>
+#include <cstring>
+
+#include "ultrahdr_api.h"
+#include "ultrahdr/ultrahdrcommon.h"
+#include "ultrahdr/jpegr.h"
+
+static const uhdr_error_info_t g_no_error = {UHDR_CODEC_OK, 0, ""};
+
+namespace ultrahdr {
+
+uhdr_memory_block::uhdr_memory_block(size_t capacity) {
+ m_buffer = std::make_unique<uint8_t[]>(capacity);
+ m_capacity = capacity;
+}
+
+uhdr_raw_image_ext::uhdr_raw_image_ext(uhdr_img_fmt_t fmt, uhdr_color_gamut_t cg,
+ uhdr_color_transfer_t ct, uhdr_color_range_t range,
+ unsigned w, unsigned h, unsigned align_stride_to) {
+ this->fmt = fmt;
+ this->cg = cg;
+ this->ct = ct;
+ this->range = range;
+
+ this->w = w;
+ this->h = h;
+
+ int aligned_width = ALIGNM(w, align_stride_to);
+
+ int bpp = 1;
+ if (fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
+ bpp = 2;
+ } else if (fmt == UHDR_IMG_FMT_32bppRGBA8888 || fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
+ bpp = 4;
+ } else if (fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
+ bpp = 8;
+ }
+
+ size_t plane_1_sz = bpp * aligned_width * h;
+ size_t plane_2_sz;
+ size_t plane_3_sz;
+ if (fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
+ plane_2_sz = (2 /* planes */ * ((aligned_width / 2) * (h / 2) * bpp));
+ plane_3_sz = 0;
+ } else if (fmt == UHDR_IMG_FMT_12bppYCbCr420) {
+ plane_2_sz = (((aligned_width / 2) * (h / 2) * bpp));
+ plane_3_sz = (((aligned_width / 2) * (h / 2) * bpp));
+ } else {
+ plane_2_sz = 0;
+ plane_3_sz = 0;
+ }
+ size_t total_size = plane_1_sz + plane_2_sz + plane_3_sz;
+ this->m_block = std::make_unique<uhdr_memory_block_t>(total_size);
+
+ uint8_t* data = this->m_block->m_buffer.get();
+ this->planes[0] = data;
+ this->stride[0] = aligned_width;
+ if (fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
+ this->planes[1] = data + plane_1_sz;
+ this->stride[1] = aligned_width;
+ this->planes[2] = nullptr;
+ this->stride[2] = 0;
+ } else if (fmt == UHDR_IMG_FMT_12bppYCbCr420) {
+ this->planes[1] = data + plane_1_sz;
+ this->stride[1] = aligned_width / 2;
+ this->planes[2] = data + plane_1_sz + plane_2_sz;
+ this->stride[2] = aligned_width / 2;
+ } else {
+ this->planes[1] = nullptr;
+ this->stride[1] = 0;
+ this->planes[2] = nullptr;
+ this->stride[2] = 0;
+ }
+}
+
+uhdr_compressed_image_ext::uhdr_compressed_image_ext(uhdr_color_gamut_t cg,
+ uhdr_color_transfer_t ct,
+ uhdr_color_range_t range, unsigned size) {
+ this->m_block = std::make_unique<uhdr_memory_block_t>(size);
+ this->data = this->m_block->m_buffer.get();
+ this->capacity = size;
+ this->data_sz = 0;
+ this->cg = cg;
+ this->ct = ct;
+ this->range = range;
+}
+
+} // namespace ultrahdr
+
+ultrahdr::ultrahdr_pixel_format map_pix_fmt_to_internal_pix_fmt(uhdr_img_fmt_t fmt) {
+ switch (fmt) {
+ case UHDR_IMG_FMT_12bppYCbCr420:
+ return ultrahdr::ULTRAHDR_PIX_FMT_YUV420;
+ case UHDR_IMG_FMT_24bppYCbCrP010:
+ return ultrahdr::ULTRAHDR_PIX_FMT_P010;
+ case UHDR_IMG_FMT_32bppRGBA1010102:
+ return ultrahdr::ULTRAHDR_PIX_FMT_RGBA1010102;
+ case UHDR_IMG_FMT_32bppRGBA8888:
+ return ultrahdr::ULTRAHDR_PIX_FMT_RGBA8888;
+ case UHDR_IMG_FMT_64bppRGBAHalfFloat:
+ return ultrahdr::ULTRAHDR_PIX_FMT_RGBAF16;
+ case UHDR_IMG_FMT_8bppYCbCr400:
+ return ultrahdr::ULTRAHDR_PIX_FMT_MONOCHROME;
+ default:
+ return ultrahdr::ULTRAHDR_PIX_FMT_UNSPECIFIED;
+ }
+}
+
+ultrahdr::ultrahdr_color_gamut map_cg_to_internal_cg(uhdr_color_gamut_t cg) {
+ switch (cg) {
+ case UHDR_CG_BT_2100:
+ return ultrahdr::ULTRAHDR_COLORGAMUT_BT2100;
+ case UHDR_CG_BT_709:
+ return ultrahdr::ULTRAHDR_COLORGAMUT_BT709;
+ case UHDR_CG_DISPLAY_P3:
+ return ultrahdr::ULTRAHDR_COLORGAMUT_P3;
+ default:
+ return ultrahdr::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ }
+}
+
+uhdr_color_gamut_t map_internal_cg_to_cg(ultrahdr::ultrahdr_color_gamut cg) {
+ switch (cg) {
+ case ultrahdr::ULTRAHDR_COLORGAMUT_BT2100:
+ return UHDR_CG_BT_2100;
+ case ultrahdr::ULTRAHDR_COLORGAMUT_BT709:
+ return UHDR_CG_BT_709;
+ case ultrahdr::ULTRAHDR_COLORGAMUT_P3:
+ return UHDR_CG_DISPLAY_P3;
+ default:
+ return UHDR_CG_UNSPECIFIED;
+ }
+}
+
+ultrahdr::ultrahdr_transfer_function map_ct_to_internal_ct(uhdr_color_transfer_t ct) {
+ switch (ct) {
+ case UHDR_CT_HLG:
+ return ultrahdr::ULTRAHDR_TF_HLG;
+ case UHDR_CT_PQ:
+ return ultrahdr::ULTRAHDR_TF_PQ;
+ case UHDR_CT_LINEAR:
+ return ultrahdr::ULTRAHDR_TF_LINEAR;
+ case UHDR_CT_SRGB:
+ return ultrahdr::ULTRAHDR_TF_SRGB;
+ default:
+ return ultrahdr::ULTRAHDR_TF_UNSPECIFIED;
+ }
+}
+
+ultrahdr::ultrahdr_output_format map_ct_fmt_to_internal_output_fmt(uhdr_color_transfer_t ct,
+ uhdr_img_fmt fmt) {
+ if (ct == UHDR_CT_HLG && fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
+ return ultrahdr::ULTRAHDR_OUTPUT_HDR_HLG;
+ } else if (ct == UHDR_CT_PQ && fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
+ return ultrahdr::ULTRAHDR_OUTPUT_HDR_PQ;
+ } else if (ct == UHDR_CT_LINEAR && fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
+ return ultrahdr::ULTRAHDR_OUTPUT_HDR_LINEAR;
+ } else if (ct == UHDR_CT_SRGB && fmt == UHDR_IMG_FMT_32bppRGBA8888) {
+ return ultrahdr::ULTRAHDR_OUTPUT_SDR;
+ }
+ return ultrahdr::ULTRAHDR_OUTPUT_UNSPECIFIED;
+}
+
+void map_internal_error_status_to_error_info(ultrahdr::status_t internal_status,
+ uhdr_error_info_t& status) {
+ if (internal_status == ultrahdr::JPEGR_NO_ERROR) {
+ status = g_no_error;
+ } else {
+ status.has_detail = 1;
+ if (internal_status == ultrahdr::ERROR_JPEGR_RESOLUTION_MISMATCH) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ snprintf(status.detail, sizeof status.detail,
+ "dimensions of sdr intent and hdr intent do not match");
+ } else if (internal_status == ultrahdr::ERROR_JPEGR_ENCODE_ERROR) {
+ status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
+ snprintf(status.detail, sizeof status.detail, "encountered unknown error during encoding");
+ } else if (internal_status == ultrahdr::ERROR_JPEGR_DECODE_ERROR) {
+ status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
+ snprintf(status.detail, sizeof status.detail, "encountered unknown error during decoding");
+ } else if (internal_status == ultrahdr::ERROR_JPEGR_NO_IMAGES_FOUND) {
+ status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
+ snprintf(status.detail, sizeof status.detail, "input uhdr image does not any valid images");
+ } else if (internal_status == ultrahdr::ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
+ status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
+ snprintf(status.detail, sizeof status.detail,
+ "input uhdr image does not contain gainmap image");
+ } else if (internal_status == ultrahdr::ERROR_JPEGR_BUFFER_TOO_SMALL) {
+ status.error_code = UHDR_CODEC_MEM_ERROR;
+ snprintf(status.detail, sizeof status.detail,
+ "output buffer to store compressed data is too small");
+ } else if (internal_status == ultrahdr::ERROR_JPEGR_MULTIPLE_EXIFS_RECEIVED) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ snprintf(status.detail, sizeof status.detail,
+ "received exif from uhdr_enc_set_exif_data() while the base image intent already "
+ "contains exif, unsure which one to use");
+ } else if (internal_status == ultrahdr::ERROR_JPEGR_UNSUPPORTED_MAP_SCALE_FACTOR) {
+ status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
+ snprintf(status.detail, sizeof status.detail,
+ "say base image wd to gain map image wd ratio is 'k1' and base image ht to gain map "
+ "image ht ratio is 'k2'. Either k1 is fractional or k2 is fractional or k1 != k2. "
+ "currently the library does not handle these scenarios");
+ } else {
+ status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
+ status.has_detail = 0;
+ }
+ }
+}
+
+uhdr_error_info_t uhdr_enc_validate_and_set_compressed_img(uhdr_codec_private_t* enc,
+ uhdr_compressed_image_t* img,
+ uhdr_img_label_t intent) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (enc == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ } else if (img == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for compressed image handle");
+ } else if (img->data == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received nullptr for compressed img->data field");
+ } else if (img->capacity < img->data_sz) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "img->capacity %d is less than img->data_sz %d",
+ img->capacity, img->data_sz);
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+ if (handle->m_sailed) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "An earlier call to uhdr_encode() has switched the context from configurable state to "
+ "end state. The context is no longer configurable. To reuse, call reset()");
+ return status;
+ }
+
+ auto entry = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(img->cg, img->ct, img->range,
+ img->data_sz);
+ memcpy(entry->data, img->data, img->data_sz);
+ entry->data_sz = img->data_sz;
+ handle->m_compressed_images.insert_or_assign(intent, std::move(entry));
+
+ return status;
+}
+
+uhdr_codec_private_t* uhdr_create_encoder(void) {
+ uhdr_encoder_private* handle = new uhdr_encoder_private();
+
+ if (handle != nullptr) {
+ uhdr_reset_encoder(handle);
+ }
+ return handle;
+}
+
+void uhdr_release_encoder(uhdr_codec_private_t* enc) {
+ if (enc != nullptr) {
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+ delete handle;
+ }
+}
+
+uhdr_error_info_t uhdr_enc_set_raw_image(uhdr_codec_private_t* enc, uhdr_raw_image_t* img,
+ uhdr_img_label_t intent) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (enc == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ } else if (img == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for raw image handle");
+ } else if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG}", intent);
+ } else if (img->fmt != UHDR_IMG_FMT_12bppYCbCr420 && img->fmt != UHDR_IMG_FMT_24bppYCbCrP010) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid input pixel format %d, expects one of {UHDR_IMG_FMT_12bppYCbCr420, "
+ "UHDR_IMG_FMT_24bppYCbCrP010}",
+ img->fmt);
+ } else if (img->cg != UHDR_CG_BT_2100 && img->cg != UHDR_CG_DISPLAY_P3 &&
+ img->cg != UHDR_CG_BT_709) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid input color gamut %d, expects one of {UHDR_CG_BT_2100, UHDR_CG_DISPLAY_P3, "
+ "UHDR_CG_BT_709}",
+ img->cg);
+ } else if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420 && img->ct != UHDR_CT_SRGB) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid input color transfer for sdr intent image %d, expects UHDR_CT_SRGB", img->ct);
+ } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010 &&
+ (img->ct != UHDR_CT_HLG && img->ct != UHDR_CT_LINEAR && img->ct != UHDR_CT_PQ)) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid input color transfer for hdr intent image %d, expects one of {UHDR_CT_HLG, "
+ "UHDR_CT_LINEAR, UHDR_CT_PQ}",
+ img->ct);
+ } else if (img->w % 2 != 0 || img->h % 2 != 0) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "image dimensions cannot be odd, received image dimensions %dx%d", img->w, img->h);
+ } else if (img->w < ultrahdr::kMinWidth || img->h < ultrahdr::kMinHeight) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "image dimensions cannot be less than %dx%d, received image dimensions %dx%d",
+ ultrahdr::kMinWidth, ultrahdr::kMinHeight, img->w, img->h);
+ } else if (img->w > ultrahdr::kMaxWidth || img->h > ultrahdr::kMaxHeight) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "image dimensions cannot be larger than %dx%d, received image dimensions %dx%d",
+ ultrahdr::kMaxWidth, ultrahdr::kMaxHeight, img->w, img->h);
+ } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
+ if (img->planes[UHDR_PLANE_Y] == nullptr || img->planes[UHDR_PLANE_UV] == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received nullptr for data field(s), luma ptr %p, chroma_uv ptr %p",
+ img->planes[UHDR_PLANE_Y], img->planes[UHDR_PLANE_UV]);
+ } else if (img->stride[UHDR_PLANE_Y] < img->w) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "luma stride must not be smaller than width, stride=%d, width=%d",
+ img->stride[UHDR_PLANE_Y], img->w);
+ } else if (img->stride[UHDR_PLANE_UV] < img->w) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "chroma_uv stride must not be smaller than width, stride=%d, width=%d",
+ img->stride[UHDR_PLANE_UV], img->w);
+ }
+ } else if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
+ if (img->planes[UHDR_PLANE_Y] == nullptr || img->planes[UHDR_PLANE_U] == nullptr ||
+ img->planes[UHDR_PLANE_V] == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received nullptr for data field(s) luma ptr %p, chroma_u ptr %p, chroma_v ptr %p",
+ img->planes[UHDR_PLANE_Y], img->planes[UHDR_PLANE_U], img->planes[UHDR_PLANE_V]);
+ } else if (img->stride[UHDR_PLANE_Y] < img->w) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "luma stride must not be smaller than width, stride=%d, width=%d",
+ img->stride[UHDR_PLANE_Y], img->w);
+ } else if (img->stride[UHDR_PLANE_U] < img->w / 2) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "chroma_u stride must not be smaller than width / 2, stride=%d, width=%d",
+ img->stride[UHDR_PLANE_U], img->w);
+ } else if (img->stride[UHDR_PLANE_V] < img->w / 2) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "chroma_v stride must not be smaller than width / 2, stride=%d, width=%d",
+ img->stride[UHDR_PLANE_V], img->w);
+ }
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+ if (intent == UHDR_HDR_IMG &&
+ handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
+ auto& sdr_raw_entry = handle->m_raw_images.find(UHDR_SDR_IMG)->second;
+ if (img->w != sdr_raw_entry->w || img->h != sdr_raw_entry->h) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "image resolutions mismatch: hdr intent: %dx%d, sdr intent: %dx%d", img->w, img->h,
+ sdr_raw_entry->w, sdr_raw_entry->h);
+ return status;
+ }
+ }
+ if (intent == UHDR_SDR_IMG &&
+ handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
+ auto& hdr_raw_entry = handle->m_raw_images.find(UHDR_HDR_IMG)->second;
+ if (img->w != hdr_raw_entry->w || img->h != hdr_raw_entry->h) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "image resolutions mismatch: sdr intent: %dx%d, hdr intent: %dx%d", img->w, img->h,
+ hdr_raw_entry->w, hdr_raw_entry->h);
+ return status;
+ }
+ }
+ if (handle->m_sailed) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "An earlier call to uhdr_encode() has switched the context from configurable state to "
+ "end state. The context is no longer configurable. To reuse, call reset()");
+ return status;
+ }
+
+ std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> entry =
+ std::make_unique<ultrahdr::uhdr_raw_image_ext_t>(img->fmt, img->cg, img->ct, img->range,
+ img->w, img->h, 64);
+
+ if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
+ uint8_t* y_dst = static_cast<uint8_t*>(entry->planes[UHDR_PLANE_Y]);
+ uint8_t* y_src = static_cast<uint8_t*>(img->planes[UHDR_PLANE_Y]);
+ uint8_t* u_dst = static_cast<uint8_t*>(entry->planes[UHDR_PLANE_U]);
+ uint8_t* u_src = static_cast<uint8_t*>(img->planes[UHDR_PLANE_U]);
+ uint8_t* v_dst = static_cast<uint8_t*>(entry->planes[UHDR_PLANE_V]);
+ uint8_t* v_src = static_cast<uint8_t*>(img->planes[UHDR_PLANE_V]);
+
+ // copy y
+ for (size_t i = 0; i < img->h; i++) {
+ memcpy(y_dst, y_src, img->w);
+ y_dst += entry->stride[UHDR_PLANE_Y];
+ y_src += img->stride[UHDR_PLANE_Y];
+ }
+ // copy cb & cr
+ for (size_t i = 0; i < img->h / 2; i++) {
+ memcpy(u_dst, u_src, img->w / 2);
+ memcpy(v_dst, v_src, img->w / 2);
+ u_dst += entry->stride[UHDR_PLANE_U];
+ v_dst += entry->stride[UHDR_PLANE_V];
+ u_src += img->stride[UHDR_PLANE_U];
+ v_src += img->stride[UHDR_PLANE_V];
+ }
+ } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
+ int bpp = 2;
+ uint8_t* y_dst = static_cast<uint8_t*>(entry->planes[UHDR_PLANE_Y]);
+ uint8_t* y_src = static_cast<uint8_t*>(img->planes[UHDR_PLANE_Y]);
+ uint8_t* uv_dst = static_cast<uint8_t*>(entry->planes[UHDR_PLANE_UV]);
+ uint8_t* uv_src = static_cast<uint8_t*>(img->planes[UHDR_PLANE_UV]);
+
+ // copy y
+ for (size_t i = 0; i < img->h; i++) {
+ memcpy(y_dst, y_src, img->w * bpp);
+ y_dst += (entry->stride[UHDR_PLANE_Y] * bpp);
+ y_src += (img->stride[UHDR_PLANE_Y] * bpp);
+ }
+ // copy cbcr
+ for (size_t i = 0; i < img->h / 2; i++) {
+ memcpy(uv_dst, uv_src, img->w * bpp);
+ uv_dst += (entry->stride[UHDR_PLANE_UV] * bpp);
+ uv_src += (img->stride[UHDR_PLANE_UV] * bpp);
+ }
+ }
+
+ handle->m_raw_images.insert_or_assign(intent, std::move(entry));
+
+ return status;
+}
+
+uhdr_error_info_t uhdr_enc_set_compressed_image(uhdr_codec_private_t* enc,
+ uhdr_compressed_image_t* img,
+ uhdr_img_label_t intent) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG && intent != UHDR_BASE_IMG) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG, UHDR_BASE_IMG}",
+ intent);
+ }
+
+ return uhdr_enc_validate_and_set_compressed_img(enc, img, intent);
+}
+
+uhdr_error_info_t uhdr_enc_set_gainmap_image(uhdr_codec_private_t* enc,
+ uhdr_compressed_image_t* img,
+ uhdr_gainmap_metadata_t* metadata) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (metadata == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received nullptr for gainmap metadata descriptor");
+ } else if (metadata->max_content_boost < metadata->min_content_boost) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received bad value for content boost min %f > max %f", metadata->min_content_boost,
+ metadata->max_content_boost);
+ } else if (metadata->gamma <= 0.0f) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received bad value for gamma %f, expects > 0.0f",
+ metadata->gamma);
+ } else if (metadata->offset_sdr < 0.0f) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received bad value for offset sdr %f, expects to be >= 0.0f", metadata->offset_sdr);
+ } else if (metadata->offset_hdr < 0.0f) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received bad value for offset hdr %f, expects to be >= 0.0f", metadata->offset_hdr);
+ } else if (metadata->hdr_capacity_max < metadata->hdr_capacity_min) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received bad value for hdr capacity min %f > max %f", metadata->hdr_capacity_min,
+ metadata->hdr_capacity_max);
+ } else if (metadata->hdr_capacity_min < 1.0f) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received bad value for hdr capacity min %f, expects to be >= 1.0f",
+ metadata->hdr_capacity_min);
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ status = uhdr_enc_validate_and_set_compressed_img(enc, img, UHDR_GAIN_MAP_IMG);
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+ memcpy(&handle->m_metadata, metadata, sizeof *metadata);
+
+ return status;
+}
+
+uhdr_error_info_t uhdr_enc_set_quality(uhdr_codec_private_t* enc, int quality,
+ uhdr_img_label_t intent) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (enc == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ } else if (quality < 0 || quality > 100) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid quality factor %d, expects in range [0-100]", quality);
+ } else if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG && intent != UHDR_BASE_IMG &&
+ intent != UHDR_GAIN_MAP_IMG) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG, UHDR_BASE_IMG, "
+ "UHDR_GAIN_MAP_IMG}",
+ intent);
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+ if (handle->m_sailed) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "An earlier call to uhdr_encode() has switched the context from configurable state to "
+ "end state. The context is no longer configurable. To reuse, call reset()");
+ return status;
+ }
+
+ handle->m_quality.insert_or_assign(intent, quality);
+
+ return status;
+}
+
+uhdr_error_info_t uhdr_enc_set_exif_data(uhdr_codec_private_t* enc, uhdr_mem_block_t* exif) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (enc == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ } else if (exif == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for exif image handle");
+ } else if (exif->data == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for exif->data field");
+ } else if (exif->capacity < exif->data_sz) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "exif->capacity %d is less than exif->data_sz %d",
+ exif->capacity, exif->data_sz);
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+ if (handle->m_sailed) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "An earlier call to uhdr_encode() has switched the context from configurable state to "
+ "end state. The context is no longer configurable. To reuse, call reset()");
+ return status;
+ }
+
+ uint8_t* data = static_cast<uint8_t*>(exif->data);
+ std::vector<uint8_t> entry(data, data + exif->data_sz);
+ handle->m_exif = std::move(entry);
+
+ return status;
+}
+
+uhdr_error_info_t uhdr_enc_set_output_format(uhdr_codec_private_t* enc, uhdr_codec_t media_type) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (enc == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ } else if (media_type != UHDR_CODEC_JPG) {
+ status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid output format %d, expects {UHDR_CODEC_JPG}", media_type);
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+ if (handle->m_sailed) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "An earlier call to uhdr_encode() has switched the context from configurable state to "
+ "end state. The context is no longer configurable. To reuse, call reset()");
+ return status;
+ }
+
+ handle->m_output_format = media_type;
+
+ return status;
+}
+
+uhdr_error_info_t uhdr_encode(uhdr_codec_private_t* enc) {
+ if (enc == nullptr) {
+ uhdr_error_info_t status;
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ return status;
+ }
+
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+
+ if (handle->m_sailed) {
+ return handle->m_encode_call_status;
+ }
+
+ handle->m_sailed = true;
+
+ uhdr_error_info_t& status = handle->m_encode_call_status;
+
+ ultrahdr::status_t internal_status = ultrahdr::JPEGR_NO_ERROR;
+ if (handle->m_output_format == UHDR_CODEC_JPG) {
+ ultrahdr::jpegr_exif_struct exif{};
+ if (handle->m_exif.size() > 0) {
+ exif.data = handle->m_exif.data();
+ exif.length = handle->m_exif.size();
+ }
+
+ ultrahdr::JpegR jpegr;
+ ultrahdr::jpegr_compressed_struct dest{};
+ if (handle->m_compressed_images.find(UHDR_BASE_IMG) != handle->m_compressed_images.end() &&
+ handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG) != handle->m_compressed_images.end()) {
+ auto& base_entry = handle->m_compressed_images.find(UHDR_BASE_IMG)->second;
+ ultrahdr::jpegr_compressed_struct primary_image;
+ primary_image.data = base_entry->data;
+ primary_image.length = primary_image.maxLength = base_entry->data_sz;
+ primary_image.colorGamut = map_cg_to_internal_cg(base_entry->cg);
+
+ auto& gainmap_entry = handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG)->second;
+ ultrahdr::jpegr_compressed_struct gainmap_image;
+ gainmap_image.data = gainmap_entry->data;
+ gainmap_image.length = gainmap_image.maxLength = gainmap_entry->data_sz;
+ gainmap_image.colorGamut = map_cg_to_internal_cg(gainmap_entry->cg);
+
+ ultrahdr::ultrahdr_metadata_struct metadata;
+ metadata.version = ultrahdr::kJpegrVersion;
+ metadata.maxContentBoost = handle->m_metadata.max_content_boost;
+ metadata.minContentBoost = handle->m_metadata.min_content_boost;
+ metadata.gamma = handle->m_metadata.gamma;
+ metadata.offsetSdr = handle->m_metadata.offset_sdr;
+ metadata.offsetHdr = handle->m_metadata.offset_hdr;
+ metadata.hdrCapacityMin = handle->m_metadata.hdr_capacity_min;
+ metadata.hdrCapacityMax = handle->m_metadata.hdr_capacity_max;
+
+ size_t size = (std::max)((8 * 1024), 2 * (primary_image.length + gainmap_image.length));
+ handle->m_compressed_output_buffer =
+ std::move(std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
+ UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, size));
+
+ dest.data = handle->m_compressed_output_buffer->data;
+ dest.length = 0;
+ dest.maxLength = handle->m_compressed_output_buffer->capacity;
+ dest.colorGamut = ultrahdr::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+
+ // api - 4
+ internal_status = jpegr.encodeJPEGR(&primary_image, &gainmap_image, &metadata, &dest);
+ map_internal_error_status_to_error_info(internal_status, status);
+ } else if (handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
+ auto& hdr_raw_entry = handle->m_raw_images.find(UHDR_HDR_IMG)->second;
+
+ size_t size = (std::max)((8u * 1024), hdr_raw_entry->w * hdr_raw_entry->h * 3 * 2);
+ handle->m_compressed_output_buffer =
+ std::move(std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
+ UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, size));
+
+ dest.data = handle->m_compressed_output_buffer->data;
+ dest.length = 0;
+ dest.maxLength = handle->m_compressed_output_buffer->capacity;
+ dest.colorGamut = ultrahdr::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+
+ ultrahdr::jpegr_uncompressed_struct p010_image;
+ p010_image.data = hdr_raw_entry->planes[UHDR_PLANE_Y];
+ p010_image.width = hdr_raw_entry->w;
+ p010_image.height = hdr_raw_entry->h;
+ p010_image.colorGamut = map_cg_to_internal_cg(hdr_raw_entry->cg);
+ p010_image.luma_stride = hdr_raw_entry->stride[UHDR_PLANE_Y];
+ p010_image.chroma_data = hdr_raw_entry->planes[UHDR_PLANE_UV];
+ p010_image.chroma_stride = hdr_raw_entry->stride[UHDR_PLANE_UV];
+ p010_image.pixelFormat = map_pix_fmt_to_internal_pix_fmt(hdr_raw_entry->fmt);
+
+ if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end() &&
+ handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
+ // api - 0
+ internal_status = jpegr.encodeJPEGR(&p010_image, map_ct_to_internal_ct(hdr_raw_entry->ct),
+ &dest, handle->m_quality.find(UHDR_BASE_IMG)->second,
+ handle->m_exif.size() > 0 ? &exif : nullptr);
+ } else if (handle->m_compressed_images.find(UHDR_SDR_IMG) !=
+ handle->m_compressed_images.end() &&
+ handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
+ auto& sdr_compressed_entry = handle->m_compressed_images.find(UHDR_SDR_IMG)->second;
+ ultrahdr::jpegr_compressed_struct sdr_compressed_image;
+ sdr_compressed_image.data = sdr_compressed_entry->data;
+ sdr_compressed_image.length = sdr_compressed_image.maxLength =
+ sdr_compressed_entry->data_sz;
+ sdr_compressed_image.colorGamut = map_cg_to_internal_cg(sdr_compressed_entry->cg);
+ // api - 3
+ internal_status = jpegr.encodeJPEGR(&p010_image, &sdr_compressed_image,
+ map_ct_to_internal_ct(hdr_raw_entry->ct), &dest);
+ } else if (handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
+ auto& sdr_raw_entry = handle->m_raw_images.find(UHDR_SDR_IMG)->second;
+
+ ultrahdr::jpegr_uncompressed_struct yuv420_image;
+ yuv420_image.data = sdr_raw_entry->planes[UHDR_PLANE_Y];
+ yuv420_image.width = sdr_raw_entry->w;
+ yuv420_image.height = sdr_raw_entry->h;
+ yuv420_image.colorGamut = map_cg_to_internal_cg(sdr_raw_entry->cg);
+ yuv420_image.luma_stride = sdr_raw_entry->stride[UHDR_PLANE_Y];
+ yuv420_image.chroma_data = nullptr;
+ yuv420_image.chroma_stride = 0;
+ yuv420_image.pixelFormat = map_pix_fmt_to_internal_pix_fmt(sdr_raw_entry->fmt);
+
+ if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end()) {
+ // api - 1
+ internal_status = jpegr.encodeJPEGR(&p010_image, &yuv420_image,
+ map_ct_to_internal_ct(hdr_raw_entry->ct), &dest,
+ handle->m_quality.find(UHDR_BASE_IMG)->second,
+ handle->m_exif.size() > 0 ? &exif : nullptr);
+ } else {
+ auto& sdr_compressed_entry = handle->m_compressed_images.find(UHDR_SDR_IMG)->second;
+ ultrahdr::jpegr_compressed_struct sdr_compressed_image;
+ sdr_compressed_image.data = sdr_compressed_entry->data;
+ sdr_compressed_image.length = sdr_compressed_image.maxLength =
+ sdr_compressed_entry->data_sz;
+ sdr_compressed_image.colorGamut = map_cg_to_internal_cg(sdr_compressed_entry->cg);
+
+ // api - 2
+ internal_status = jpegr.encodeJPEGR(&p010_image, &yuv420_image, &sdr_compressed_image,
+ map_ct_to_internal_ct(hdr_raw_entry->ct), &dest);
+ }
+ }
+ map_internal_error_status_to_error_info(internal_status, status);
+ } else {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "resources required for uhdr_encode() operation are not present");
+ }
+ if (status.error_code == UHDR_CODEC_OK) {
+ handle->m_compressed_output_buffer->data_sz = dest.length;
+ handle->m_compressed_output_buffer->cg = map_internal_cg_to_cg(dest.colorGamut);
+ }
+ }
+
+ return status;
+}
+
+uhdr_compressed_image_t* uhdr_get_encoded_stream(uhdr_codec_private_t* enc) {
+ if (enc == nullptr) {
+ return nullptr;
+ }
+
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+ if (!handle->m_sailed || handle->m_encode_call_status.error_code != UHDR_CODEC_OK) {
+ return nullptr;
+ }
+
+ return handle->m_compressed_output_buffer.get();
+}
+
+void uhdr_reset_encoder(uhdr_codec_private_t* enc) {
+ if (enc != nullptr) {
+ uhdr_encoder_private* handle = reinterpret_cast<uhdr_encoder_private*>(enc);
+
+ // clear entries and restore defaults
+ handle->m_raw_images.clear();
+ handle->m_compressed_images.clear();
+ handle->m_quality.clear();
+ handle->m_quality.emplace(UHDR_HDR_IMG, 95);
+ handle->m_quality.emplace(UHDR_SDR_IMG, 95);
+ handle->m_quality.emplace(UHDR_BASE_IMG, 95);
+ handle->m_quality.emplace(UHDR_GAIN_MAP_IMG, 85);
+ handle->m_exif.clear();
+ handle->m_output_format = UHDR_CODEC_JPG;
+
+ handle->m_sailed = false;
+ handle->m_compressed_output_buffer.reset();
+ handle->m_encode_call_status = g_no_error;
+ }
+}
+
+uhdr_codec_private_t* uhdr_create_decoder(void) {
+ uhdr_decoder_private* handle = new uhdr_decoder_private();
+
+ if (handle != nullptr) {
+ uhdr_reset_decoder(handle);
+ }
+ return handle;
+}
+
+void uhdr_release_decoder(uhdr_codec_private_t* dec) {
+ if (dec != nullptr) {
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+ delete handle;
+ }
+}
+
+uhdr_error_info_t uhdr_dec_set_image(uhdr_codec_private_t* dec, uhdr_compressed_image_t* img) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (dec == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ } else if (img == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for compressed image handle");
+ } else if (img->data == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "received nullptr for compressed img->data field");
+ } else if (img->capacity < img->data_sz) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "img->capacity %d is less than img->data_sz %d",
+ img->capacity, img->data_sz);
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+ if (handle->m_sailed) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "An earlier call to uhdr_decode() has switched the context from configurable state to "
+ "end state. The context is no longer configurable. To reuse, call reset()");
+ return status;
+ }
+
+ handle->m_uhdr_compressed_img = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
+ img->cg, img->ct, img->range, img->data_sz);
+ memcpy(handle->m_uhdr_compressed_img->data, img->data, img->data_sz);
+ handle->m_uhdr_compressed_img->data_sz = img->data_sz;
+
+ return status;
+}
+
+uhdr_error_info_t uhdr_dec_set_out_img_format(uhdr_codec_private_t* dec, uhdr_img_fmt_t fmt) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (dec == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ } else if (fmt != UHDR_IMG_FMT_32bppRGBA8888 && fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat &&
+ fmt != UHDR_IMG_FMT_32bppRGBA1010102) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid output format %d, expects one of {UHDR_IMG_FMT_32bppRGBA8888, "
+ "UHDR_IMG_FMT_64bppRGBAHalfFloat, UHDR_IMG_FMT_32bppRGBA1010102}",
+ fmt);
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+ if (handle->m_sailed) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "An earlier call to uhdr_decode() has switched the context from configurable state to "
+ "end state. The context is no longer configurable. To reuse, call reset()");
+ return status;
+ }
+
+ handle->m_output_fmt = fmt;
+
+ return status;
+}
+
+uhdr_error_info_t uhdr_dec_set_out_color_transfer(uhdr_codec_private_t* dec,
+ uhdr_color_transfer_t ct) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (dec == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ } else if (ct != UHDR_CT_HLG && ct != UHDR_CT_PQ && ct != UHDR_CT_LINEAR && ct != UHDR_CT_SRGB) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid output color transfer %d, expects one of {UHDR_CT_HLG, UHDR_CT_PQ, "
+ "UHDR_CT_LINEAR, UHDR_CT_SRGB}",
+ ct);
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+ if (handle->m_sailed) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "An earlier call to uhdr_decode() has switched the context from configurable state to "
+ "end state. The context is no longer configurable. To reuse, call reset()");
+ return status;
+ }
+
+ handle->m_output_ct = ct;
+
+ return status;
+}
+
+uhdr_error_info_t uhdr_dec_set_out_max_display_boost(uhdr_codec_private_t* dec,
+ float display_boost) {
+ uhdr_error_info_t status = g_no_error;
+
+ if (dec == nullptr) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ } else if (display_boost < 1.0f) {
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "invalid display boost %f, expects to be >= 1.0f}", display_boost);
+ }
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+ if (handle->m_sailed) {
+ status.error_code = UHDR_CODEC_INVALID_OPERATION;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail,
+ "An earlier call to uhdr_decode() has switched the context from configurable state to "
+ "end state. The context is no longer configurable. To reuse, call reset()");
+ return status;
+ }
+
+ handle->m_output_max_disp_boost = display_boost;
+
+ return status;
+}
+
+uhdr_error_info_t uhdr_decode(uhdr_codec_private_t* dec) {
+ if (dec == nullptr) {
+ uhdr_error_info_t status;
+ status.error_code = UHDR_CODEC_INVALID_PARAM;
+ status.has_detail = 1;
+ snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
+ return status;
+ }
+
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+
+ if (handle->m_sailed) {
+ return handle->m_decode_call_status;
+ }
+
+ handle->m_sailed = true;
+
+ uhdr_error_info_t& status = handle->m_decode_call_status;
+ ultrahdr::jpeg_info_struct primary_image;
+ ultrahdr::jpeg_info_struct gainmap_image;
+ ultrahdr::jpegr_info_struct jpegr_info;
+ jpegr_info.width = 0;
+ jpegr_info.height = 0;
+ jpegr_info.primaryImgInfo = &primary_image;
+ jpegr_info.gainmapImgInfo = &gainmap_image;
+
+ ultrahdr::jpegr_compressed_struct uhdr_image;
+ uhdr_image.data = handle->m_uhdr_compressed_img->data;
+ uhdr_image.length = uhdr_image.maxLength = handle->m_uhdr_compressed_img->data_sz;
+ uhdr_image.colorGamut = map_cg_to_internal_cg(handle->m_uhdr_compressed_img->cg);
+
+ ultrahdr::JpegR jpegr;
+ ultrahdr::status_t internal_status = jpegr.getJPEGRInfo(&uhdr_image, &jpegr_info);
+ map_internal_error_status_to_error_info(internal_status, status);
+ if (status.error_code != UHDR_CODEC_OK) return status;
+
+ handle->m_exif = std::move(primary_image.exifData);
+ handle->m_icc = std::move(primary_image.iccData);
+ handle->m_base_xmp = std::move(primary_image.xmpData);
+ handle->m_gainmap_xmp = std::move(gainmap_image.xmpData);
+
+ handle->m_decoded_img_buffer = std::make_unique<ultrahdr::uhdr_raw_image_ext_t>(
+ handle->m_output_fmt, UHDR_CG_UNSPECIFIED, handle->m_output_ct, UHDR_CR_UNSPECIFIED,
+ primary_image.width, primary_image.height, 1);
+ // alias
+ ultrahdr::jpegr_uncompressed_struct dest;
+ dest.data = handle->m_decoded_img_buffer->planes[UHDR_PLANE_PACKED];
+ dest.colorGamut = ultrahdr::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+
+ handle->m_gainmap_img_buffer = std::make_unique<ultrahdr::uhdr_raw_image_ext_t>(
+ UHDR_IMG_FMT_8bppYCbCr400, UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED,
+ gainmap_image.width, gainmap_image.height, 1);
+ // alias
+ ultrahdr::jpegr_uncompressed_struct dest_gainmap;
+ dest_gainmap.data = handle->m_gainmap_img_buffer->planes[UHDR_PLANE_Y];
+
+ ultrahdr::ultrahdr_metadata_struct metadata;
+ internal_status = jpegr.decodeJPEGR(
+ &uhdr_image, &dest, handle->m_output_max_disp_boost, nullptr,
+ map_ct_fmt_to_internal_output_fmt(handle->m_output_ct, handle->m_output_fmt), &dest_gainmap,
+ &metadata);
+ map_internal_error_status_to_error_info(internal_status, status);
+ if (status.error_code == UHDR_CODEC_OK) {
+ handle->m_decoded_img_buffer->cg = map_internal_cg_to_cg(dest.colorGamut);
+
+ handle->m_metadata.max_content_boost = metadata.maxContentBoost;
+ handle->m_metadata.min_content_boost = metadata.minContentBoost;
+ handle->m_metadata.gamma = metadata.gamma;
+ handle->m_metadata.offset_sdr = metadata.offsetSdr;
+ handle->m_metadata.offset_hdr = metadata.offsetHdr;
+ handle->m_metadata.hdr_capacity_min = metadata.hdrCapacityMin;
+ handle->m_metadata.hdr_capacity_max = metadata.hdrCapacityMax;
+ }
+
+ return status;
+}
+
+uhdr_raw_image_t* uhdr_get_decoded_image(uhdr_codec_private_t* dec) {
+ if (dec == nullptr) {
+ return nullptr;
+ }
+
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+ if (!handle->m_sailed || handle->m_decode_call_status.error_code != UHDR_CODEC_OK) {
+ return nullptr;
+ }
+
+ return handle->m_decoded_img_buffer.get();
+}
+
+uhdr_raw_image_t* uhdr_get_gain_map_image(uhdr_codec_private_t* dec) {
+ if (dec == nullptr) {
+ return nullptr;
+ }
+
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+ if (!handle->m_sailed || handle->m_decode_call_status.error_code != UHDR_CODEC_OK) {
+ return nullptr;
+ }
+
+ return handle->m_gainmap_img_buffer.get();
+}
+
+uhdr_gainmap_metadata_t* uhdr_get_gain_map_metadata(uhdr_codec_private_t* dec) {
+ if (dec == nullptr) {
+ return nullptr;
+ }
+
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+ if (!handle->m_sailed || handle->m_decode_call_status.error_code != UHDR_CODEC_OK) {
+ return nullptr;
+ }
+
+ return &handle->m_metadata;
+}
+
+void uhdr_reset_decoder(uhdr_codec_private_t* dec) {
+ if (dec != nullptr) {
+ uhdr_decoder_private* handle = reinterpret_cast<uhdr_decoder_private*>(dec);
+
+ // clear entries and restore defaults
+ handle->m_uhdr_compressed_img.reset();
+ handle->m_output_fmt = UHDR_IMG_FMT_64bppRGBAHalfFloat;
+ handle->m_output_ct = UHDR_CT_LINEAR;
+ handle->m_output_max_disp_boost = FLT_MAX;
+
+ // ready to be configured
+ handle->m_sailed = false;
+ handle->m_decoded_img_buffer.reset();
+ handle->m_gainmap_img_buffer.reset();
+ handle->m_exif.clear();
+ handle->m_icc.clear();
+ handle->m_base_xmp.clear();
+ handle->m_gainmap_xmp.clear();
+ memset(&handle->m_metadata, 0, sizeof handle->m_metadata);
+ handle->m_decode_call_status = g_no_error;
+ }
+}
diff --git a/tests/jpegr_test.cpp b/tests/jpegr_test.cpp
index 0b420e6..fb66fe1 100644
--- a/tests/jpegr_test.cpp
+++ b/tests/jpegr_test.cpp
@@ -24,6 +24,8 @@
#include <fstream>
#include <iostream>
+#include "ultrahdr_api.h"
+
#include "ultrahdr/ultrahdrcommon.h"
#include "ultrahdr/jpegr.h"
#include "ultrahdr/jpegrutils.h"
@@ -301,6 +303,34 @@ static bool readFile(const char* fileName, void*& result, int maxLength, int& le
return false;
}
+uhdr_color_gamut_t map_internal_cg_to_cg(ultrahdr::ultrahdr_color_gamut cg) {
+ switch (cg) {
+ case ultrahdr::ULTRAHDR_COLORGAMUT_BT2100:
+ return UHDR_CG_BT_2100;
+ case ultrahdr::ULTRAHDR_COLORGAMUT_BT709:
+ return UHDR_CG_BT_709;
+ case ultrahdr::ULTRAHDR_COLORGAMUT_P3:
+ return UHDR_CG_DISPLAY_P3;
+ default:
+ return UHDR_CG_UNSPECIFIED;
+ }
+}
+
+uhdr_color_transfer_t map_internal_ct_to_ct(ultrahdr::ultrahdr_transfer_function ct) {
+ switch (ct) {
+ case ultrahdr::ULTRAHDR_TF_HLG:
+ return UHDR_CT_HLG;
+ case ultrahdr::ULTRAHDR_TF_PQ:
+ return UHDR_CT_PQ;
+ case ultrahdr::ULTRAHDR_TF_LINEAR:
+ return UHDR_CT_LINEAR;
+ case ultrahdr::ULTRAHDR_TF_SRGB:
+ return UHDR_CT_SRGB;
+ default:
+ return UHDR_CT_UNSPECIFIED;
+ }
+}
+
void decodeJpegRImg(jr_compressed_ptr img, [[maybe_unused]] const char* outFileName) {
jpegr_info_struct info{};
JpegR jpegHdr;
@@ -319,6 +349,25 @@ void decodeJpegRImg(jr_compressed_ptr img, [[maybe_unused]] const char* outFileN
std::cerr << "unable to write output file" << std::endl;
}
#endif
+ uhdr_codec_private_t* obj = uhdr_create_decoder();
+ uhdr_compressed_image_t uhdr_image{};
+ uhdr_image.data = img->data;
+ uhdr_image.data_sz = img->length;
+ uhdr_image.capacity = img->length;
+ uhdr_image.cg = UHDR_CG_UNSPECIFIED;
+ uhdr_image.ct = UHDR_CT_UNSPECIFIED;
+ uhdr_image.range = UHDR_CR_UNSPECIFIED;
+ uhdr_error_info_t status = uhdr_dec_set_image(obj, &uhdr_image);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ status = uhdr_decode(obj);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ uhdr_raw_image_t* raw_image = uhdr_get_decoded_image(obj);
+ ASSERT_NE(nullptr, raw_image);
+ ASSERT_EQ(map_internal_cg_to_cg(destImage.colorGamut), raw_image->cg);
+ ASSERT_EQ(destImage.width, raw_image->w);
+ ASSERT_EQ(destImage.height, raw_image->h);
+ ASSERT_EQ(0, memcmp(destImage.data, raw_image->planes[0], outSize));
+ uhdr_release_decoder(obj);
}
// ============================================================================
@@ -1398,6 +1447,33 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) {
uHdrLib.encodeJPEGR(rawImg.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
JPEGR_NO_ERROR);
+
+ uhdr_codec_private_t* obj = uhdr_create_encoder();
+ uhdr_raw_image_t uhdrRawImg{};
+ uhdrRawImg.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
+ uhdrRawImg.cg = map_internal_cg_to_cg(mP010ColorGamut);
+ uhdrRawImg.ct = map_internal_ct_to_ct(ultrahdr_transfer_function::ULTRAHDR_TF_HLG);
+ uhdrRawImg.range = UHDR_CR_UNSPECIFIED;
+ uhdrRawImg.w = kImageWidth;
+ uhdrRawImg.h = kImageHeight;
+ uhdrRawImg.planes[0] = rawImg.getImageHandle()->data;
+ uhdrRawImg.stride[0] = kImageWidth;
+ uhdrRawImg.planes[1] =
+ ((uint8_t*)(rawImg.getImageHandle()->data)) + kImageWidth * kImageHeight * 2;
+ uhdrRawImg.stride[1] = kImageWidth;
+ uhdr_error_info_t status = uhdr_enc_set_raw_image(obj, &uhdrRawImg, UHDR_HDR_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ status = uhdr_enc_set_quality(obj, kQuality, UHDR_BASE_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ status = uhdr_encode(obj);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ uhdr_compressed_image_t* compressedImage = uhdr_get_encoded_stream(obj);
+ ASSERT_NE(nullptr, compressedImage);
+ ASSERT_EQ(jpgImg.getImageHandle()->length, compressedImage->data_sz);
+ ASSERT_EQ(0,
+ memcmp(jpgImg.getImageHandle()->data, compressedImage->data, compressedImage->data_sz));
+ uhdr_release_encoder(obj);
+
// encode with luma stride set
{
UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
@@ -1434,6 +1510,30 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) {
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+
+ uhdr_codec_private_t* obj = uhdr_create_encoder();
+ uhdr_raw_image_t uhdrRawImg{};
+ uhdrRawImg.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
+ uhdrRawImg.cg = map_internal_cg_to_cg(mP010ColorGamut);
+ uhdrRawImg.ct = map_internal_ct_to_ct(ultrahdr_transfer_function::ULTRAHDR_TF_HLG);
+ uhdrRawImg.range = UHDR_CR_UNSPECIFIED;
+ uhdrRawImg.w = kImageWidth;
+ uhdrRawImg.h = kImageHeight;
+ uhdrRawImg.planes[0] = rawImg2.getImageHandle()->data;
+ uhdrRawImg.stride[0] = rawImg2.getImageHandle()->luma_stride;
+ uhdrRawImg.planes[1] = rawImg2.getImageHandle()->chroma_data;
+ uhdrRawImg.stride[1] = rawImg2.getImageHandle()->chroma_stride;
+ uhdr_error_info_t status = uhdr_enc_set_raw_image(obj, &uhdrRawImg, UHDR_HDR_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ status = uhdr_enc_set_quality(obj, kQuality, UHDR_BASE_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ status = uhdr_encode(obj);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ uhdr_compressed_image_t* compressedImage = uhdr_get_encoded_stream(obj);
+ ASSERT_NE(nullptr, compressedImage);
+ ASSERT_EQ(jpg1->length, compressedImage->data_sz);
+ ASSERT_EQ(0, memcmp(jpg1->data, compressedImage->data, jpg1->length));
+ uhdr_release_encoder(obj);
}
// encode with chroma stride set
{
@@ -1610,6 +1710,49 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+
+ uhdr_codec_private_t* obj = uhdr_create_encoder();
+ uhdr_raw_image_t uhdrRawImg{};
+ uhdrRawImg.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
+ uhdrRawImg.cg = map_internal_cg_to_cg(mP010ColorGamut);
+ uhdrRawImg.ct = map_internal_ct_to_ct(ultrahdr_transfer_function::ULTRAHDR_TF_HLG);
+ uhdrRawImg.range = UHDR_CR_UNSPECIFIED;
+ uhdrRawImg.w = kImageWidth;
+ uhdrRawImg.h = kImageHeight;
+ uhdrRawImg.planes[0] = rawImgP010.getImageHandle()->data;
+ uhdrRawImg.stride[0] = kImageWidth;
+ uhdrRawImg.planes[1] =
+ ((uint8_t*)(rawImgP010.getImageHandle()->data)) + kImageWidth * kImageHeight * 2;
+ uhdrRawImg.stride[1] = kImageWidth;
+ uhdr_error_info_t status = uhdr_enc_set_raw_image(obj, &uhdrRawImg, UHDR_HDR_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+
+ uhdrRawImg.fmt = UHDR_IMG_FMT_12bppYCbCr420;
+ uhdrRawImg.cg = map_internal_cg_to_cg(mYuv420ColorGamut);
+ uhdrRawImg.ct = map_internal_ct_to_ct(ultrahdr_transfer_function::ULTRAHDR_TF_SRGB);
+ uhdrRawImg.range = UHDR_CR_UNSPECIFIED;
+ uhdrRawImg.w = kImageWidth;
+ uhdrRawImg.h = kImageHeight;
+ uhdrRawImg.planes[0] = rawImg2420.getImageHandle()->data;
+ uhdrRawImg.stride[0] = rawImg2420.getImageHandle()->luma_stride;
+ uhdrRawImg.planes[1] = rawImg2420.getImageHandle()->chroma_data;
+ uhdrRawImg.stride[1] = rawImg2420.getImageHandle()->chroma_stride;
+ uhdrRawImg.planes[2] = ((uint8_t*)(rawImg2420.getImageHandle()->chroma_data)) +
+ rawImg2420.getImageHandle()->chroma_stride * kImageHeight / 2;
+ uhdrRawImg.stride[2] = rawImg2420.getImageHandle()->chroma_stride;
+ status = uhdr_enc_set_raw_image(obj, &uhdrRawImg, UHDR_SDR_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+
+ status = uhdr_enc_set_quality(obj, kQuality, UHDR_BASE_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ status = uhdr_encode(obj);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ uhdr_compressed_image_t* compressedImage = uhdr_get_encoded_stream(obj);
+ ASSERT_NE(nullptr, compressedImage);
+ ASSERT_EQ(jpgImg.getImageHandle()->length, compressedImage->data_sz);
+ ASSERT_EQ(
+ 0, memcmp(jpgImg.getImageHandle()->data, compressedImage->data, compressedImage->data_sz));
+ uhdr_release_encoder(obj);
}
// encode with chroma stride set 420
{
@@ -1773,6 +1916,59 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+
+ uhdr_codec_private_t* obj = uhdr_create_encoder();
+ uhdr_raw_image_t uhdrRawImg{};
+ uhdrRawImg.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
+ uhdrRawImg.cg = map_internal_cg_to_cg(mP010ColorGamut);
+ uhdrRawImg.ct = map_internal_ct_to_ct(ultrahdr_transfer_function::ULTRAHDR_TF_HLG);
+ uhdrRawImg.range = UHDR_CR_UNSPECIFIED;
+ uhdrRawImg.w = kImageWidth;
+ uhdrRawImg.h = kImageHeight;
+ uhdrRawImg.planes[0] = rawImgP010.getImageHandle()->data;
+ uhdrRawImg.stride[0] = kImageWidth;
+ uhdrRawImg.planes[1] =
+ ((uint8_t*)(rawImgP010.getImageHandle()->data)) + kImageWidth * kImageHeight * 2;
+ uhdrRawImg.stride[1] = kImageWidth;
+ uhdr_error_info_t status = uhdr_enc_set_raw_image(obj, &uhdrRawImg, UHDR_HDR_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+
+ uhdrRawImg.fmt = UHDR_IMG_FMT_12bppYCbCr420;
+ uhdrRawImg.cg = map_internal_cg_to_cg(mYuv420ColorGamut);
+ uhdrRawImg.ct = map_internal_ct_to_ct(ultrahdr_transfer_function::ULTRAHDR_TF_SRGB);
+ uhdrRawImg.range = UHDR_CR_UNSPECIFIED;
+ uhdrRawImg.w = kImageWidth;
+ uhdrRawImg.h = kImageHeight;
+ uhdrRawImg.planes[0] = rawImg2420.getImageHandle()->data;
+ uhdrRawImg.stride[0] = rawImg2420.getImageHandle()->luma_stride;
+ uhdrRawImg.planes[1] = rawImg2420.getImageHandle()->chroma_data;
+ uhdrRawImg.stride[1] = rawImg2420.getImageHandle()->chroma_stride;
+ uhdrRawImg.planes[2] = ((uint8_t*)(rawImg2420.getImageHandle()->chroma_data)) +
+ rawImg2420.getImageHandle()->chroma_stride * kImageHeight / 2;
+ uhdrRawImg.stride[2] = rawImg2420.getImageHandle()->chroma_stride;
+ status = uhdr_enc_set_raw_image(obj, &uhdrRawImg, UHDR_SDR_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+
+ uhdr_compressed_image_t uhdrCompressedImg;
+ uhdrCompressedImg.data = sdr->data;
+ uhdrCompressedImg.data_sz = sdr->length;
+ uhdrCompressedImg.capacity = sdr->length;
+ uhdrCompressedImg.cg = map_internal_cg_to_cg(sdr->colorGamut);
+ uhdrCompressedImg.ct = UHDR_CT_UNSPECIFIED;
+ uhdrCompressedImg.range = UHDR_CR_UNSPECIFIED;
+ status = uhdr_enc_set_compressed_image(obj, &uhdrCompressedImg, UHDR_SDR_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+
+ status = uhdr_enc_set_quality(obj, kQuality, UHDR_BASE_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ status = uhdr_encode(obj);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ uhdr_compressed_image_t* compressedImage = uhdr_get_encoded_stream(obj);
+ ASSERT_NE(nullptr, compressedImage);
+ ASSERT_EQ(jpgImg.getImageHandle()->length, compressedImage->data_sz);
+ ASSERT_EQ(
+ 0, memcmp(jpgImg.getImageHandle()->data, compressedImage->data, compressedImage->data_sz));
+ uhdr_release_encoder(obj);
}
// encode with chroma stride set
{
@@ -1897,6 +2093,45 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) {
ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
+ {
+ uhdr_codec_private_t* obj = uhdr_create_encoder();
+ uhdr_raw_image_t uhdrRawImg{};
+ uhdrRawImg.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
+ uhdrRawImg.cg = map_internal_cg_to_cg(mP010ColorGamut);
+ uhdrRawImg.ct = map_internal_ct_to_ct(ultrahdr_transfer_function::ULTRAHDR_TF_HLG);
+ uhdrRawImg.range = UHDR_CR_UNSPECIFIED;
+ uhdrRawImg.w = kImageWidth;
+ uhdrRawImg.h = kImageHeight;
+ uhdrRawImg.planes[0] = rawImgP010.getImageHandle()->data;
+ uhdrRawImg.stride[0] = kImageWidth;
+ uhdrRawImg.planes[1] =
+ ((uint8_t*)(rawImgP010.getImageHandle()->data)) + kImageWidth * kImageHeight * 2;
+ uhdrRawImg.stride[1] = kImageWidth;
+ uhdr_error_info_t status = uhdr_enc_set_raw_image(obj, &uhdrRawImg, UHDR_HDR_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+
+ uhdr_compressed_image_t uhdrCompressedImg;
+ uhdrCompressedImg.data = sdr->data;
+ uhdrCompressedImg.data_sz = sdr->length;
+ uhdrCompressedImg.capacity = sdr->length;
+ uhdrCompressedImg.cg = map_internal_cg_to_cg(sdr->colorGamut);
+ uhdrCompressedImg.ct = UHDR_CT_UNSPECIFIED;
+ uhdrCompressedImg.range = UHDR_CR_UNSPECIFIED;
+ status = uhdr_enc_set_compressed_image(obj, &uhdrCompressedImg, UHDR_SDR_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+
+ status = uhdr_enc_set_quality(obj, kQuality, UHDR_BASE_IMG);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ status = uhdr_encode(obj);
+ ASSERT_EQ(UHDR_CODEC_OK, status.error_code) << status.detail;
+ uhdr_compressed_image_t* compressedImage = uhdr_get_encoded_stream(obj);
+ ASSERT_NE(nullptr, compressedImage);
+ ASSERT_EQ(jpgImg.getImageHandle()->length, compressedImage->data_sz);
+ ASSERT_EQ(
+ 0, memcmp(jpgImg.getImageHandle()->data, compressedImage->data, compressedImage->data_sz));
+ uhdr_release_encoder(obj);
+ }
+
auto jpg1 = jpgImg.getImageHandle();
#ifdef DUMP_OUTPUT
diff --git a/ultrahdr_api.h b/ultrahdr_api.h
new file mode 100644
index 0000000..889877f
--- /dev/null
+++ b/ultrahdr_api.h
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * 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.
+ */
+
+/** \file ultrahdr_api.h
+ *
+ * \brief
+ * Describes the encoder or decoder algorithm interface to applications.
+ */
+
+#ifndef ULTRAHDR_API_H
+#define ULTRAHDR_API_H
+
+#ifdef __cplusplus
+#define UHDR_EXTERN extern "C"
+#else
+#define UHDR_EXTERN extern
+#endif
+
+// ===============================================================================================
+// Enum Definitions
+// ===============================================================================================
+
+/*!\brief List of supported image formats */
+typedef enum uhdr_img_fmt {
+ UHDR_IMG_FMT_UNSPECIFIED = -1, /**< Unspecified */
+ UHDR_IMG_FMT_24bppYCbCrP010, /**< 10-bit-per component 4:2:0 YCbCr semiplanar format.
+ Each chroma and luma component has 16 allocated bits in
+ little-endian configuration with 10 MSB of actual data.*/
+ UHDR_IMG_FMT_12bppYCbCr420, /**< 8-bit-per component 4:2:0 YCbCr planar format */
+ UHDR_IMG_FMT_8bppYCbCr400, /**< 8-bit-per component Monochrome format */
+ UHDR_IMG_FMT_32bppRGBA8888, /**< 32 bits per pixel RGBA color format, with 8-bit red, green, blue
+ and alpha components. Using 32-bit little-endian representation,
+ colors stored as Red 7:0, Green 15:8, Blue 23:16, Alpha 31:24. */
+ UHDR_IMG_FMT_64bppRGBAHalfFloat, /**< 64 bits per pixel RGBA color format, with 16-bit signed
+ floating point red, green, blue, and alpha components */
+ UHDR_IMG_FMT_32bppRGBA1010102, /**< 32 bits per pixel RGBA color format, with 10-bit red, green,
+ blue, and 2-bit alpha components. Using 32-bit little-endian
+ representation, colors stored as Red 9:0, Green 19:10, Blue
+ 29:20, and Alpha 31:30. */
+} uhdr_img_fmt_t; /**< alias for enum uhdr_img_fmt */
+
+/*!\brief List of supported color gamuts */
+typedef enum uhdr_color_gamut {
+ UHDR_CG_UNSPECIFIED = -1, /**< Unspecified */
+ UHDR_CG_BT_709, /**< BT.709 */
+ UHDR_CG_DISPLAY_P3, /**< Display P3 */
+ UHDR_CG_BT_2100, /**< BT.2100 */
+} uhdr_color_gamut_t; /**< alias for enum uhdr_color_gamut */
+
+/*!\brief List of supported color transfers */
+typedef enum uhdr_color_transfer {
+ UHDR_CT_UNSPECIFIED = -1, /**< Unspecified */
+ UHDR_CT_LINEAR, /**< Linear */
+ UHDR_CT_HLG, /**< Hybrid log gamma */
+ UHDR_CT_PQ, /**< Perceptual Quantizer */
+ UHDR_CT_SRGB, /**< Gamma */
+} uhdr_color_transfer_t; /**< alias for enum uhdr_color_transfer */
+
+/*!\brief List of supported color ranges */
+typedef enum uhdr_color_range {
+ UHDR_CR_UNSPECIFIED = -1, /**< Unspecified */
+ UHDR_CR_LIMITED_RANGE, /**< Y {[16..235], UV [16..240]} * pow(2, (bpc - 8)) */
+ UHDR_CR_FULL_RANGE, /**< YUV/RGB {[0..255]} * pow(2, (bpc - 8)) */
+} uhdr_color_range_t; /**< alias for enum uhdr_color_range */
+
+/*!\brief List of supported codecs */
+typedef enum uhdr_codec {
+ UHDR_CODEC_JPG, /**< Compress {Hdr, Sdr rendition} to an {Sdr rendition + Gain Map} using
+ jpeg */
+} uhdr_codec_t; /**< alias for enum uhdr_codec */
+
+/*!\brief Image identifiers in gain map technology */
+typedef enum uhdr_img_label {
+ UHDR_HDR_IMG, /**< Hdr rendition image */
+ UHDR_SDR_IMG, /**< Sdr rendition image */
+ UHDR_BASE_IMG, /**< Base rendition image */
+ UHDR_GAIN_MAP_IMG, /**< Gain map image */
+} uhdr_img_label_t; /**< alias for enum uhdr_img_label */
+
+/*!\brief Algorithm return codes */
+typedef enum uhdr_codec_err {
+
+ /*!\brief Operation completed without error */
+ UHDR_CODEC_OK,
+
+ /*!\brief Unspecified error */
+ UHDR_CODEC_UNKNOWN_ERROR,
+
+ /*!\brief An application-supplied parameter is not valid. */
+ UHDR_CODEC_INVALID_PARAM,
+
+ /*!\brief Memory operation failed */
+ UHDR_CODEC_MEM_ERROR,
+
+ /*!\brief An application-invoked operation is not valid. */
+ UHDR_CODEC_INVALID_OPERATION,
+
+ /*!\brief The library does not implement a feature required for the operation */
+ UHDR_CODEC_UNSUPPORTED_FEATURE,
+
+ /*!\brief An iterator reached the end of list. */
+ UHDR_CODEC_LIST_END,
+
+} uhdr_codec_err_t; /**< alias for enum uhdr_codec_err */
+
+// ===============================================================================================
+// Structure Definitions
+// ===============================================================================================
+
+/*!\brief Detailed return status */
+typedef struct uhdr_error_info {
+ uhdr_codec_err_t error_code;
+ int has_detail;
+ char detail[128];
+} uhdr_error_info_t; /**< alias for struct uhdr_error_info */
+
+/**\brief Raw Image Descriptor */
+typedef struct uhdr_raw_image {
+ /* Color model, primaries, transfer, range */
+ uhdr_img_fmt_t fmt; /**< Image Format */
+ uhdr_color_gamut_t cg; /**< Color Gamut */
+ uhdr_color_transfer_t ct; /**< Color Transfer */
+ uhdr_color_range_t range; /**< Color Range */
+
+ /* Image storage dimensions */
+ unsigned int w; /**< Stored image width */
+ unsigned int h; /**< Stored image height */
+
+ /* Image data pointers. */
+#define UHDR_PLANE_PACKED 0 /**< To be used for all packed formats */
+#define UHDR_PLANE_Y 0 /**< Y (Luminance) plane */
+#define UHDR_PLANE_U 1 /**< U (Chroma) plane */
+#define UHDR_PLANE_UV 1 /**< UV (Chroma plane interleaved) To be used for semi planar format */
+#define UHDR_PLANE_V 2 /**< V (Chroma) plane */
+ void* planes[3]; /**< pointer to the top left pixel for each plane */
+ unsigned int stride[3]; /**< stride in pixels between rows for each plane */
+} uhdr_raw_image_t; /**< alias for struct uhdr_raw_image */
+
+/**\brief Compressed Image Descriptor */
+typedef struct uhdr_compressed_image {
+ void* data; /**< Pointer to a block of data to decode */
+ unsigned int data_sz; /**< size of the data buffer */
+ unsigned int capacity; /**< maximum size of the data buffer */
+ uhdr_color_gamut_t cg; /**< Color Gamut */
+ uhdr_color_transfer_t ct; /**< Color Transfer */
+ uhdr_color_range_t range; /**< Color Range */
+} uhdr_compressed_image_t; /**< alias for struct uhdr_compressed_image */
+
+/**\brief Buffer Descriptor */
+typedef struct uhdr_mem_block {
+ void* data; /**< Pointer to a block of data to decode */
+ unsigned int data_sz; /**< size of the data buffer */
+ unsigned int capacity; /**< maximum size of the data buffer */
+} uhdr_mem_block_t; /**< alias for struct uhdr_mem_block */
+
+/**\brief Gain map metadata.
+ * Note: all values stored in linear space. This differs from the metadata encoded in XMP, where
+ * max_content_boost (aka gainMapMax), min_content_boost (aka gainMapMin), hdr_capacity_min, and
+ * hdr_capacity_max are stored in log2 space.
+ */
+typedef struct uhdr_gainmap_metadata {
+ float max_content_boost; /**< Max Content Boost for the map */
+ float min_content_boost; /**< Min Content Boost for the map */
+ float gamma; /**< Gamma of the map data */
+ float offset_sdr; /**< Offset for SDR data in map calculations */
+ float offset_hdr; /**< Offset for HDR data in map calculations */
+ float hdr_capacity_min; /**< Min HDR capacity values for interpolating the Gain Map */
+ float hdr_capacity_max; /**< Max HDR capacity value for interpolating the Gain Map */
+} uhdr_gainmap_metadata_t; /**< alias for struct uhdr_gainmap_metadata */
+
+/**\brief ultrahdr codec context opaque descriptor */
+typedef struct uhdr_codec_private uhdr_codec_private_t;
+
+// ===============================================================================================
+// Function Declarations
+// ===============================================================================================
+
+// ===============================================================================================
+// Encoder APIs
+// ===============================================================================================
+
+/*!\brief Create a new encoder instance. The instance is initialized with default settings.
+ * To override the settings use uhdr_enc_set_*()
+ *
+ * \return nullptr if there was an error allocating memory else a fresh opaque encoder handle
+ */
+UHDR_EXTERN uhdr_codec_private_t* uhdr_create_encoder(void);
+
+/*!\brief Release encoder instance.
+ * Frees all allocated storage associated with encoder instance.
+ *
+ * \param[in] enc encoder instance.
+ *
+ * \return none
+ */
+UHDR_EXTERN void uhdr_release_encoder(uhdr_codec_private_t* enc);
+
+/*!\brief Add raw image descriptor to encoder context. The function goes through all the fields of
+ * the image descriptor and checks for their sanity. If no anomalies are seen then the image is
+ * added to internal list. Repeated calls to this function will replace the old entry with the
+ * current.
+ *
+ * \param[in] enc encoder instance.
+ * \param[in] img image descriptor.
+ * \param[in] intent UHDR_HDR_IMG for hdr intent and UHDR_SDR_IMG for sdr intent.
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_raw_image(uhdr_codec_private_t* enc,
+ uhdr_raw_image_t* img,
+ uhdr_img_label_t intent);
+
+/*!\brief Add compressed image descriptor to encoder context. The function goes through all the
+ * fields of the image descriptor and checks for their sanity. If no anomalies are seen then the
+ * image is added to internal list. Repeated calls to this function will replace the old entry with
+ * the current.
+ *
+ * If both uhdr_enc_add_raw_image() and uhdr_enc_add_compressed_image() are called during a session
+ * for the same intent, it is assumed that raw image descriptor and compressed image descriptor are
+ * relatable via compress <-> decompress process.
+ *
+ * \param[in] enc encoder instance.
+ * \param[in] img image descriptor.
+ * \param[in] intent UHDR_HDR_IMG for hdr intent,
+ * UHDR_SDR_IMG for sdr intent,
+ * UHDR_BASE_IMG for base image intent
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_compressed_image(uhdr_codec_private_t* enc,
+ uhdr_compressed_image_t* img,
+ uhdr_img_label_t intent);
+
+/*!\brief Add gain map image descriptor and gainmap metadata info to encoder context. The function
+ * internally goes through all the fields of the image descriptor and checks for their sanity. If no
+ * anomalies are seen then the image is added to internal list. Repeated calls to this function will
+ * replace the old entry with the current.
+ *
+ * \param[in] enc encoder instance.
+ * \param[in] img gain map image desciptor.
+ * \param[in] metadata gainmap metadata descriptor
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+uhdr_error_info_t uhdr_enc_set_gainmap_image(uhdr_codec_private_t* enc,
+ uhdr_compressed_image_t* img,
+ uhdr_gainmap_metadata_t* metadata);
+
+/*!\brief Set quality for compression
+ *
+ * \param[in] enc encoder instance.
+ * \param[in] quality quality factor.
+ * \param[in] intent UHDR_BASE_IMG for base image and UHDR_GAIN_MAP_IMG for gain map image.
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_quality(uhdr_codec_private_t* enc, int quality,
+ uhdr_img_label_t intent);
+
+/*!\brief Set Exif data that needs to be inserted in the output compressed stream
+ *
+ * \param[in] enc encoder instance.
+ * \param[in] img exif data descriptor.
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_exif_data(uhdr_codec_private_t* enc,
+ uhdr_mem_block_t* exif);
+
+/*!\brief Set output image compression format.
+ *
+ * \param[in] enc encoder instance.
+ * \param[in] media_type output image compression format.
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_output_format(uhdr_codec_private_t* enc,
+ uhdr_codec_t media_type);
+
+/*!\brief Encode process call
+ * After initializing the encoder context, call to this function will submit data for encoding. If
+ * the call is successful, the encoded output is stored internally and is accessible via
+ * uhdr_get_encoded_stream().
+ *
+ * The basic usage of uhdr encoder is as follows:
+ * - The program creates an instance of an encoder using,
+ * - uhdr_create_encoder().
+ * - The program registers input images to the encoder using,
+ * - uhdr_enc_set_raw_image(ctxt, img, UHDR_HDR_IMG)
+ * - uhdr_enc_set_raw_image(ctxt, img, UHDR_SDR_IMG)
+ * - The program overrides the default settings using uhdr_enc_set_*() functions
+ * - If the application wants to control the compression level
+ * - uhdr_enc_set_quality()
+ * - If the application wants to insert exif data
+ * - uhdr_enc_set_exif_data()
+ * - If the application wants to control target compression format
+ * - uhdr_enc_set_output_format()
+ * - The program calls uhdr_encode() to encode data. This call would initiate the process of
+ * computing gain map from hdr intent and sdr intent. The sdr intent and gain map image are
+ * compressed at the set quality using the codec of choice.
+ * - On success, the program can access the encoded output with uhdr_get_encoded_stream().
+ * - The program finishes the encoding with uhdr_release_encoder().
+ *
+ * The library allows setting Hdr and/or Sdr intent in compressed format,
+ * - uhdr_enc_set_compressed_image(ctxt, img, UHDR_HDR_IMG)
+ * - uhdr_enc_set_compressed_image(ctxt, img, UHDR_SDR_IMG)
+ * In this mode, the compressed image(s) are first decoded to raw image(s). These raw image(s) go
+ * through the aforth mentioned gain map computation and encoding process. In this case, the usage
+ * shall be like this:
+ * - uhdr_create_encoder()
+ * - uhdr_enc_set_compressed_image(ctxt, img, UHDR_HDR_IMG)
+ * - uhdr_enc_set_compressed_image(ctxt, img, UHDR_SDR_IMG)
+ * - uhdr_encode()
+ * - uhdr_get_encoded_stream()
+ * - uhdr_release_encoder()
+ * If the set compressed image media type of intent UHDR_SDR_IMG and output media type are
+ * identical, then this image is directly used for primary image. No re-encode of raw image is done.
+ * This implies base image quality setting is un-used. Only gain map image is encoded at the set
+ * quality using codec of choice. On the other hand, if the set compressed image media type and
+ * output media type are different, then transcoding is done.
+ *
+ * The library also allows directly setting base and gain map image in compressed format,
+ * - uhdr_enc_set_compressed_image(ctxt, img, UHDR_BASE_IMG)
+ * - uhdr_enc_set_gainmap_image(ctxt, img, metadata)
+ * In this mode, gain map computation is by-passed. The input images are transcoded (if necessary),
+ * combined and sent back.
+ *
+ * It is possible to create a uhdr image solely from Hdr intent. In this case, the usage shall look
+ * like this:
+ * - uhdr_create_encoder()
+ * - uhdr_enc_set_raw_image(ctxt, img, UHDR_HDR_IMG)
+ * - uhdr_enc_set_quality() // optional
+ * - uhdr_enc_set_exif_data() // optional
+ * - uhdr_enc_set_output_format() // optional
+ * - uhdr_encode()
+ * - uhdr_get_encoded_stream()
+ * - uhdr_release_encoder()
+ * In this mode, the Sdr rendition is created from Hdr intent by tone-mapping. The tone-mapped sdr
+ * image and hdr image go through the aforth mentioned gain map computation and encoding process to
+ * create uhdr image.
+ *
+ * In all modes, Exif data is inserted if requested.
+ *
+ * \param[in] enc encoder instance.
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_encode(uhdr_codec_private_t* enc);
+
+/*!\brief Get encoded ultra hdr stream
+ *
+ * \param[in] enc encoder instance.
+ *
+ * \return nullptr if encode process call is unsuccessful, uhdr image descriptor otherwise
+ */
+UHDR_EXTERN uhdr_compressed_image_t* uhdr_get_encoded_stream(uhdr_codec_private_t* enc);
+
+/*!\brief Reset encoder instance.
+ * Clears all previous settings and resets to default state and ready for re-initialization
+ *
+ * \param[in] enc encoder instance.
+ *
+ * \return none
+ */
+UHDR_EXTERN void uhdr_reset_encoder(uhdr_codec_private_t* enc);
+
+// ===============================================================================================
+// Decoder APIs
+// ===============================================================================================
+
+/*!\brief Create a new decoder instance. The instance is initialized with default settings.
+ * To override the settings use uhdr_dec_set_*()
+ *
+ * \return nullptr if there was an error allocating memory else a fresh opaque decoder handle
+ */
+UHDR_EXTERN uhdr_codec_private_t* uhdr_create_decoder(void);
+
+/*!\brief Release decoder instance.
+ * Frees all allocated storage associated with decoder instance.
+ *
+ * \param[in] dec decoder instance.
+ *
+ * \return none
+ */
+UHDR_EXTERN void uhdr_release_decoder(uhdr_codec_private_t* dec);
+
+/*!\brief Add compressed image descriptor to decoder context. The function goes through all the
+ * fields of the image descriptor and checks for their sanity. If no anomalies are seen then the
+ * image is added to internal list. Repeated calls to this function will replace the old entry with
+ * the current.
+ *
+ * \param[in] dec decoder instance.
+ * \param[in] img image descriptor.
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_dec_set_image(uhdr_codec_private_t* dec,
+ uhdr_compressed_image_t* img);
+
+/*!\brief Set output image format
+ *
+ * \param[in] dec decoder instance.
+ * \param[in] fmt output image format.
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_dec_set_out_img_format(uhdr_codec_private_t* dec,
+ uhdr_img_fmt_t fmt);
+
+/*!\brief Set output color transfer
+ *
+ * \param[in] dec decoder instance.
+ * \param[in] ct output color transfer
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_dec_set_out_color_transfer(uhdr_codec_private_t* dec,
+ uhdr_color_transfer_t ct);
+
+/*!\brief Set output max display boost
+ *
+ * \param[in] dec decoder instance.
+ * \param[in] display_boost max display boost
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds,
+ * #UHDR_CODEC_INVALID_PARAM otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_dec_set_out_max_display_boost(uhdr_codec_private_t* dec,
+ float display_boost);
+
+/*!\brief Decode process call
+ * After initializing the decoder context, call to this function will submit data for decoding. If
+ * the call is successful, the decoded output is stored internally and is accessible via
+ * uhdr_get_decoded_image().
+ *
+ * The basic usage of uhdr decoder is as follows:
+ * - The program creates an instance of a decoder using,
+ * - uhdr_create_decoder().
+ * - The program registers input images to the decoder using,
+ * - uhdr_dec_set_image(ctxt, img)
+ * - The program overrides the default settings using uhdr_dec_set_*() functions.
+ * - If the application wants to control the output image format,
+ * - uhdr_dec_set_out_img_format()
+ * - If the application wants to control the output transfer characteristics,
+ * - uhdr_dec_set_out_color_transfer()
+ * - If the application wants to control the output display boost,
+ * - uhdr_dec_set_out_max_display_boost()
+ * - The program calls uhdr_decompress() to decode uhdr stream. This call would initiate the process
+ * of decoding base image and gain map image. These two are combined to give the final rendition
+ * image.
+ * - The program can access the decoded output with uhdr_get_decoded_image().
+ * - The program finishes the decoding with uhdr_release_decoder().
+ *
+ * \param[in] dec decoder instance.
+ *
+ * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
+ */
+UHDR_EXTERN uhdr_error_info_t uhdr_decode(uhdr_codec_private_t* dec);
+
+/*!\brief Get final rendition image
+ *
+ * \param[in] dec decoder instance.
+ *
+ * \return nullptr if decoded process call is unsuccessful, raw image descriptor otherwise
+ */
+UHDR_EXTERN uhdr_raw_image_t* uhdr_get_decoded_image(uhdr_codec_private_t* dec);
+
+/*!\brief Get gain map image
+ *
+ * \param[in] dec decoder instance.
+ *
+ * \return nullptr if decoded process call is unsuccessful, raw image descriptor otherwise
+ */
+UHDR_EXTERN uhdr_raw_image_t* uhdr_get_gain_map_image(uhdr_codec_private_t* dec);
+
+/*!\brief Get gain map metadata
+ *
+ * \param[in] dec decoder instance.
+ *
+ * \return nullptr if decoded process call is unsuccessful, gainmap metadata descriptor otherwise
+ */
+UHDR_EXTERN uhdr_gainmap_metadata_t* uhdr_get_gain_map_metadata(uhdr_codec_private_t* dec);
+
+/*!\brief Reset decoder instance.
+ * Clears all previous settings and resets to default state and ready for re-initialization
+ *
+ * \param[in] dec decoder instance.
+ *
+ * \return none
+ */
+UHDR_EXTERN void uhdr_reset_decoder(uhdr_codec_private_t* dec);
+
+#endif // ULTRAHDR_API_H