From 2bdfff7afa56f186351cd5c849d74094a7448433 Mon Sep 17 00:00:00 2001 From: Ram Mohan M Date: Fri, 23 Aug 2024 09:19:46 +0530 Subject: [PATCH] add support for configuring hdr display max brightness If the input hdr intent color transfer is linear, mastering display max luminance configuration is missing. For HLG, this defaults to 1000 but as it is not an absolute standard, there is room for setting different peak brightness levels. Also, this helps configuring hdrCapacityMax of gainmap metadata. Test: ./ultrahdr_app -L 4000 --- examples/ultrahdr_app.cpp | 61 +++++++++++++++++---------- lib/include/ultrahdr/gainmapmath.h | 5 --- lib/include/ultrahdr/jpegr.h | 12 +++++- lib/include/ultrahdr/ultrahdrcommon.h | 1 + lib/src/gainmapmath.cpp | 17 -------- lib/src/gpu/applygainmap_gl.cpp | 9 ++-- lib/src/jpegr.cpp | 46 +++++++++++++++----- lib/src/ultrahdr_api.cpp | 38 ++++++++++++++++- ultrahdr_api.h | 14 ++++++ 9 files changed, 141 insertions(+), 62 deletions(-) diff --git a/examples/ultrahdr_app.cpp b/examples/ultrahdr_app.cpp index f2c354f1..c4dba8bf 100644 --- a/examples/ultrahdr_app.cpp +++ b/examples/ultrahdr_app.cpp @@ -245,21 +245,19 @@ static bool writeFile(const char* filename, uhdr_raw_image_t* img) { class UltraHdrAppInput { public: - UltraHdrAppInput(const char* hdrIntentRawFile, const char* sdrIntentRawFile, - const char* sdrIntentCompressedFile, const char* gainmapCompressedFile, - const char* gainmapMetadataCfgFile, const char* exifFile, const char* outputFile, - size_t width, size_t height, - uhdr_img_fmt_t hdrCf = UHDR_IMG_FMT_32bppRGBA1010102, - uhdr_img_fmt_t sdrCf = UHDR_IMG_FMT_32bppRGBA8888, - uhdr_color_gamut_t hdrCg = UHDR_CG_DISPLAY_P3, - uhdr_color_gamut_t sdrCg = UHDR_CG_BT_709, - uhdr_color_transfer_t hdrTf = UHDR_CT_HLG, int quality = 95, - uhdr_color_transfer_t oTf = UHDR_CT_HLG, - uhdr_img_fmt_t oFmt = UHDR_IMG_FMT_32bppRGBA1010102, bool isHdrCrFull = false, - int gainmapScaleFactor = 4, int gainmapQuality = 85, - bool enableMultiChannelGainMap = false, float gamma = 1.0f, - bool enableGLES = false, uhdr_enc_preset_t encPreset = UHDR_USAGE_REALTIME, - float minContentBoost = FLT_MIN, float maxContentBoost = FLT_MAX) + UltraHdrAppInput( + const char* hdrIntentRawFile, const char* sdrIntentRawFile, + const char* sdrIntentCompressedFile, const char* gainmapCompressedFile, + const char* gainmapMetadataCfgFile, const char* exifFile, const char* outputFile, + size_t width, size_t height, uhdr_img_fmt_t hdrCf = UHDR_IMG_FMT_32bppRGBA1010102, + uhdr_img_fmt_t sdrCf = UHDR_IMG_FMT_32bppRGBA8888, + uhdr_color_gamut_t hdrCg = UHDR_CG_DISPLAY_P3, uhdr_color_gamut_t sdrCg = UHDR_CG_BT_709, + uhdr_color_transfer_t hdrTf = UHDR_CT_HLG, int quality = 95, + uhdr_color_transfer_t oTf = UHDR_CT_HLG, uhdr_img_fmt_t oFmt = UHDR_IMG_FMT_32bppRGBA1010102, + bool isHdrCrFull = false, int gainmapScaleFactor = 4, int gainmapQuality = 85, + bool enableMultiChannelGainMap = false, float gamma = 1.0f, bool enableGLES = false, + uhdr_enc_preset_t encPreset = UHDR_USAGE_REALTIME, float minContentBoost = FLT_MIN, + float maxContentBoost = FLT_MAX, float maxDispBrightness = -1.0f) : mHdrIntentRawFile(hdrIntentRawFile), mSdrIntentRawFile(sdrIntentRawFile), mSdrIntentCompressedFile(sdrIntentCompressedFile), @@ -287,6 +285,7 @@ class UltraHdrAppInput { mEncPreset(encPreset), mMinContentBoost(minContentBoost), mMaxContentBoost(maxContentBoost), + mMaxDispBrightness(maxDispBrightness), mMode(0){}; UltraHdrAppInput(const char* gainmapMetadataCfgFile, const char* uhdrFile, const char* outputFile, @@ -319,6 +318,7 @@ class UltraHdrAppInput { mEncPreset(UHDR_USAGE_REALTIME), mMinContentBoost(FLT_MIN), mMaxContentBoost(FLT_MAX), + mMaxDispBrightness(-1.0f), mMode(1){}; ~UltraHdrAppInput() { @@ -401,6 +401,7 @@ class UltraHdrAppInput { const uhdr_enc_preset_t mEncPreset; const float mMinContentBoost; const float mMaxContentBoost; + const float mMaxDispBrightness; const int mMode; uhdr_raw_image_t mRawP010Image{}; @@ -701,6 +702,9 @@ bool UltraHdrAppInput::encode() { if (mMinContentBoost != FLT_MIN || mMaxContentBoost != FLT_MAX) { RET_IF_ERR(uhdr_enc_set_min_max_content_boost(handle, mMinContentBoost, mMaxContentBoost)) } + if (mMaxDispBrightness != -1.0f) { + RET_IF_ERR(uhdr_enc_set_max_display_brightness(handle, mMaxDispBrightness)) + } if (mEnableGLES) { RET_IF_ERR(uhdr_enable_gpu_acceleration(handle, mEnableGLES)) } @@ -1373,6 +1377,13 @@ static void usage(const char* name) { " -k min content boost recommendation, must be in linear scale, optional \n"); fprintf(stderr, " -K max content boost recommendation, must be in linear scale, optional \n"); + fprintf(stderr, + " -L set maximum hdr display brightness in nits. \n" + " required if the input color transfer is linear. \n" + " optional if the input color transfer is HLG or PQ. \n" + " For HLG content, this defaults to 1000 nits. \n" + " For PQ content, this defaults to 10000 nits. \n" + " any real number in range [203, 10000]. \n"); fprintf(stderr, " -x binary input resource containing exif data to insert, optional. \n"); fprintf(stderr, "\n## decoder options : \n"); fprintf(stderr, " -j ultra hdr compressed input resource, required. \n"); @@ -1460,7 +1471,7 @@ static void usage(const char* name) { } int main(int argc, char* argv[]) { - char opt_string[] = "p:y:i:g:f:w:h:C:c:t:q:o:O:m:j:e:a:b:z:R:s:M:Q:G:x:u:D:k:K:"; + char opt_string[] = "p:y:i:g:f:w:h:C:c:t:q:o:O:m:j:e:a:b:z:R:s:M:Q:G:x:u:D:k:K:L:"; char *hdr_intent_raw_file = nullptr, *sdr_intent_raw_file = nullptr, *uhdr_file = nullptr, *sdr_intent_compressed_file = nullptr, *gainmap_compressed_file = nullptr, *gainmap_metadata_cfg_file = nullptr, *output_file = nullptr, *exif_file = nullptr; @@ -1484,6 +1495,7 @@ int main(int argc, char* argv[]) { uhdr_enc_preset_t enc_preset = UHDR_USAGE_REALTIME; float min_content_boost = FLT_MIN; float max_content_boost = FLT_MAX; + float max_disp_brightness = -1.0f; int ch; while ((ch = getopt_s(argc, argv, opt_string)) != -1) { switch (ch) { @@ -1578,6 +1590,9 @@ int main(int argc, char* argv[]) { case 'K': max_content_boost = atof(optarg_s); break; + case 'L': + max_disp_brightness = atof(optarg_s); + break; default: usage(argv[0]); return -1; @@ -1600,13 +1615,13 @@ int main(int argc, char* argv[]) { std::cerr << "did not receive raw resources for encoding." << std::endl; return -1; } - UltraHdrAppInput appInput(hdr_intent_raw_file, sdr_intent_raw_file, sdr_intent_compressed_file, - gainmap_compressed_file, gainmap_metadata_cfg_file, exif_file, - output_file ? output_file : "out.jpeg", width, height, hdr_cf, sdr_cf, - hdr_cg, sdr_cg, hdr_tf, quality, out_tf, out_cf, - use_full_range_color_hdr, gainmap_scale_factor, - gainmap_compression_quality, use_multi_channel_gainmap, gamma, - enable_gles, enc_preset, min_content_boost, max_content_boost); + UltraHdrAppInput appInput( + hdr_intent_raw_file, sdr_intent_raw_file, sdr_intent_compressed_file, + gainmap_compressed_file, gainmap_metadata_cfg_file, exif_file, + output_file ? output_file : "out.jpeg", width, height, hdr_cf, sdr_cf, hdr_cg, sdr_cg, + hdr_tf, quality, out_tf, out_cf, use_full_range_color_hdr, gainmap_scale_factor, + gainmap_compression_quality, use_multi_channel_gainmap, gamma, enable_gles, enc_preset, + min_content_boost, max_content_boost, max_disp_brightness); if (!appInput.encode()) return -1; if (compute_psnr == 1) { if (!appInput.decode()) return -1; diff --git a/lib/include/ultrahdr/gainmapmath.h b/lib/include/ultrahdr/gainmapmath.h index 8e65ba14..8ba0cb26 100644 --- a/lib/include/ultrahdr/gainmapmath.h +++ b/lib/include/ultrahdr/gainmapmath.h @@ -490,11 +490,6 @@ PutPixelFn putPixelFn(uhdr_img_fmt_t format); */ bool isPixelFormatRgb(uhdr_img_fmt_t format); -/* - * Get max display mastering luminance in nits - */ -float getMaxDisplayMasteringLuminance(uhdr_color_transfer_t transfer); - /* * Convert between YUV encodings, according to ITU-R BT.709-6, ITU-R BT.601-7, and ITU-R BT.2100-2. * diff --git a/lib/include/ultrahdr/jpegr.h b/lib/include/ultrahdr/jpegr.h index 120b3eee..a0704a27 100644 --- a/lib/include/ultrahdr/jpegr.h +++ b/lib/include/ultrahdr/jpegr.h @@ -78,7 +78,8 @@ class JpegR { int mapCompressQuality = kMapCompressQualityDefault, bool useMultiChannelGainMap = kUseMultiChannelGainMapDefault, float gamma = kGainMapGammaDefault, uhdr_enc_preset_t preset = UHDR_USAGE_REALTIME, - float minContentBoost = FLT_MIN, float maxContentBoost = FLT_MAX); + float minContentBoost = FLT_MIN, float maxContentBoost = FLT_MAX, + float maxDispBrightness = -1.0f); /*!\brief Encode API-0. * @@ -547,6 +548,14 @@ class JpegR { uhdr_error_info_t convertYuv(uhdr_raw_image_t* image, uhdr_color_gamut_t src_encoding, uhdr_color_gamut_t dst_encoding); + /*!\brief Get max display brightness in nits + * + * \param[in] transfer intent's color transfer characteristics + * + * \return max display brightness in nits + */ + float getMasteringDisplayMaxLuminance(uhdr_color_transfer_t transfer); + /* * This method will check the validity of the input arguments. * @@ -593,6 +602,7 @@ class JpegR { uhdr_enc_preset_t mEncPreset; // encoding speed preset float mMinContentBoost; // min content boost recommendation float mMaxContentBoost; // max content boost recommendation + float mMaxDispBrightness; // mastering display max luminance in nits }; struct GlobalTonemapOutputs { diff --git a/lib/include/ultrahdr/ultrahdrcommon.h b/lib/include/ultrahdr/ultrahdrcommon.h index 03bbfc57..8572361a 100644 --- a/lib/include/ultrahdr/ultrahdrcommon.h +++ b/lib/include/ultrahdr/ultrahdrcommon.h @@ -358,6 +358,7 @@ struct uhdr_encoder_private : uhdr_codec_private { uhdr_enc_preset_t m_enc_preset; float m_min_content_boost; float m_max_content_boost; + float m_max_disp_brightness; // internal data std::unique_ptr m_compressed_output_buffer; diff --git a/lib/src/gainmapmath.cpp b/lib/src/gainmapmath.cpp index 47e9eac3..5f069e32 100644 --- a/lib/src/gainmapmath.cpp +++ b/lib/src/gainmapmath.cpp @@ -512,23 +512,6 @@ bool isPixelFormatRgb(uhdr_img_fmt_t format) { format == UHDR_IMG_FMT_32bppRGBA1010102; } -float getMaxDisplayMasteringLuminance(uhdr_color_transfer_t transfer) { - switch (transfer) { - case UHDR_CT_LINEAR: - // TODO: configure MDML correctly for linear tf - return kHlgMaxNits; - case UHDR_CT_HLG: - return kHlgMaxNits; - case UHDR_CT_PQ: - return kPqMaxNits; - case UHDR_CT_SRGB: - return kSdrWhiteNits; - case UHDR_CT_UNSPECIFIED: - return -1.0f; - } - return -1.0f; -} - // All of these conversions are derived from the respective input YUV->RGB conversion followed by // the RGB->YUV for the receiving encoding. They are consistent with the RGB<->YUV functions in // gainmapmath.cpp, given that we use BT.709 encoding for sRGB and BT.601 encoding for Display-P3, diff --git a/lib/src/gpu/applygainmap_gl.cpp b/lib/src/gpu/applygainmap_gl.cpp index 334936ad..cd3a0ae6 100644 --- a/lib/src/gpu/applygainmap_gl.cpp +++ b/lib/src/gpu/applygainmap_gl.cpp @@ -136,13 +136,13 @@ static const std::string applyGainMapShader = R"__SHADER__( uniform float logMinBoost; uniform float logMaxBoost; uniform float weight; - uniform float displayBoost; + uniform float hdrCapacityMax; float applyGainMapSample(const float channel, float gain) { gain = pow(gain, 1.0f / gamma); float logBoost = logMinBoost * (1.0f - gain) + logMaxBoost * gain; logBoost = exp2(logBoost * weight); - return channel * logBoost / displayBoost; + return channel * logBoost / hdrCapacityMax; } vec3 applyGain(const vec3 color, const vec3 gain) { @@ -302,7 +302,7 @@ uhdr_error_info_t applyGainMapGLES(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_ GLint logMinBoostLocation = glGetUniformLocation(shaderProgram, "logMinBoost"); GLint logMaxBoostLocation = glGetUniformLocation(shaderProgram, "logMaxBoost"); GLint weightLocation = glGetUniformLocation(shaderProgram, "weight"); - GLint displayBoostLocation = glGetUniformLocation(shaderProgram, "displayBoost"); + GLint hdrCapacityMaxLocation = glGetUniformLocation(shaderProgram, "hdrCapacityMax"); glUniform1i(pWidthLocation, sdr_intent->w); glUniform1i(pHeightLocation, sdr_intent->h); @@ -310,7 +310,8 @@ uhdr_error_info_t applyGainMapGLES(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_ glUniform1f(logMinBoostLocation, log2(gainmap_metadata->min_content_boost)); glUniform1f(logMaxBoostLocation, log2(gainmap_metadata->max_content_boost)); glUniform1f(weightLocation, display_boost / gainmap_metadata->hdr_capacity_max); - glUniform1f(displayBoostLocation, display_boost); + glUniform1f(hdrCapacityMaxLocation, output_ct == UHDR_CT_PQ ? (kSdrWhiteNits / kPqMaxNits) + : gainmap_metadata->hdr_capacity_max); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, yuvTexture); diff --git a/lib/src/jpegr.cpp b/lib/src/jpegr.cpp index 90053f20..6ccc67d7 100644 --- a/lib/src/jpegr.cpp +++ b/lib/src/jpegr.cpp @@ -147,7 +147,7 @@ int GetCPUCoreCount() { JpegR::JpegR(void* uhdrGLESCtxt, size_t mapDimensionScaleFactor, int mapCompressQuality, bool useMultiChannelGainMap, float gamma, uhdr_enc_preset_t preset, - float minContentBoost, float maxContentBoost) { + float minContentBoost, float maxContentBoost, float maxDispBrightness) { mUhdrGLESCtxt = uhdrGLESCtxt; mMapDimensionScaleFactor = mapDimensionScaleFactor; mMapCompressQuality = mapCompressQuality; @@ -156,6 +156,7 @@ JpegR::JpegR(void* uhdrGLESCtxt, size_t mapDimensionScaleFactor, int mapCompress mEncPreset = preset; mMinContentBoost = minContentBoost; mMaxContentBoost = maxContentBoost; + mMaxDispBrightness = maxDispBrightness; } /* @@ -498,6 +499,22 @@ uhdr_error_info_t JpegR::convertYuv(uhdr_raw_image_t* image, uhdr_color_gamut_t return status; } +float JpegR::getMasteringDisplayMaxLuminance(uhdr_color_transfer_t transfer) { + switch (transfer) { + case UHDR_CT_LINEAR: + return mMaxDispBrightness; + case UHDR_CT_HLG: + return mMaxDispBrightness != -1.0f ? mMaxDispBrightness : kHlgMaxNits; + case UHDR_CT_PQ: + return kPqMaxNits; + case UHDR_CT_SRGB: + return kSdrWhiteNits; + case UHDR_CT_UNSPECIFIED: + return -1.0f; + } + return -1.0f; +} + uhdr_error_info_t JpegR::compressGainMap(uhdr_raw_image_t* gainmap_img, JpegEncoderHelper* jpeg_enc_obj) { return jpeg_enc_obj->compressImage(gainmap_img, mMapCompressQuality, nullptr, 0); @@ -555,7 +572,7 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ return status; } - float hdr_white_nits = getMaxDisplayMasteringLuminance(hdr_intent->ct); + float hdr_white_nits = getMasteringDisplayMaxLuminance(hdr_intent->ct); if (hdr_white_nits == -1.0f) { status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE; status.has_detail = 1; @@ -658,7 +675,11 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ gainmap_metadata->offset_sdr = 0.0f; gainmap_metadata->offset_hdr = 0.0f; gainmap_metadata->hdr_capacity_min = 1.0f; - gainmap_metadata->hdr_capacity_max = gainmap_metadata->max_content_boost; + if (this->mMaxDispBrightness != -1.0f) { + gainmap_metadata->hdr_capacity_max = this->mMaxDispBrightness / kSdrWhiteNits; + } else { + gainmap_metadata->hdr_capacity_max = gainmap_metadata->max_content_boost; + } float log2MinBoost = log2(gainmap_metadata->min_content_boost); float log2MaxBoost = log2(gainmap_metadata->max_content_boost); @@ -937,7 +958,11 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ gainmap_metadata->offset_sdr = 0.0f; gainmap_metadata->offset_hdr = 0.0f; gainmap_metadata->hdr_capacity_min = 1.0f; - gainmap_metadata->hdr_capacity_max = hdr_white_nits / kSdrWhiteNits; + if (this->mMaxDispBrightness != -1.0f) { + gainmap_metadata->hdr_capacity_max = this->mMaxDispBrightness / kSdrWhiteNits; + } else { + gainmap_metadata->hdr_capacity_max = hdr_white_nits / kSdrWhiteNits; + } }; if (mEncPreset == UHDR_USAGE_REALTIME) { @@ -1425,11 +1450,8 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima JobQueue jobQueue; std::function applyRecMap = [sdr_intent, gainmap_img, dest, &jobQueue, &idwTable, - output_ct, &gainLUT, display_boost, -#if !USE_APPLY_GAIN_LUT - gainmap_metadata, -#endif - map_scale_factor, get_pixel_fn]() -> void { + output_ct, &gainLUT, gainmap_metadata, map_scale_factor, + get_pixel_fn]() -> void { size_t width = sdr_intent->w; size_t rowStart, rowEnd; @@ -1478,16 +1500,17 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima #endif } - rgb_hdr = rgb_hdr / display_boost; size_t pixel_idx = x + y * dest->stride[UHDR_PLANE_PACKED]; switch (output_ct) { case UHDR_CT_LINEAR: { + rgb_hdr = rgb_hdr / gainmap_metadata->hdr_capacity_max; uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr); reinterpret_cast(dest->planes[UHDR_PLANE_PACKED])[pixel_idx] = rgba_f16; break; } case UHDR_CT_HLG: { + rgb_hdr = rgb_hdr / gainmap_metadata->hdr_capacity_max; #if USE_HLG_OETF_LUT ColorTransformFn hdrOetf = hlgOetfLUT; #else @@ -1500,6 +1523,7 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima break; } case UHDR_CT_PQ: { + rgb_hdr = rgb_hdr * kSdrWhiteNits / kPqMaxNits; #if USE_PQ_OETF_LUT ColorTransformFn hdrOetf = pqOetfLUT; #else @@ -1776,7 +1800,7 @@ uhdr_error_info_t JpegR::toneMap(uhdr_raw_image_t* hdr_intent, uhdr_raw_image_t* return status; } - float hdr_white_nits = getMaxDisplayMasteringLuminance(hdr_intent->ct); + float hdr_white_nits = getMasteringDisplayMaxLuminance(hdr_intent->ct); if (hdr_white_nits == -1.0f) { uhdr_error_info_t status; status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE; diff --git a/lib/src/ultrahdr_api.cpp b/lib/src/ultrahdr_api.cpp index 29eb34be..9a5f788d 100644 --- a/lib/src/ultrahdr_api.cpp +++ b/lib/src/ultrahdr_api.cpp @@ -619,6 +619,7 @@ uhdr_error_info_t uhdr_enc_set_min_max_content_boost(uhdr_codec_private_t* enc, status.has_detail = 1; snprintf(status.detail, sizeof status.detail, "Invalid min boost configuration. configured min boost %f is less than 0", min_boost); + return status; } @@ -639,6 +640,40 @@ uhdr_error_info_t uhdr_enc_set_min_max_content_boost(uhdr_codec_private_t* enc, return status; } +uhdr_error_info_t uhdr_enc_set_max_display_brightness(uhdr_codec_private_t* enc, float nits) { + uhdr_error_info_t status = g_no_error; + + if (dynamic_cast(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"); + return status; + } + + if (nits < ultrahdr::kSdrWhiteNits || nits > ultrahdr::kPqMaxNits) { + status.error_code = UHDR_CODEC_INVALID_PARAM; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, + "unexpected max display brightness nits %f, expects to be with in range [%f, %f]", + nits, ultrahdr::kSdrWhiteNits, ultrahdr::kPqMaxNits); + } + + uhdr_encoder_private* handle = dynamic_cast(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_max_disp_brightness = nits; + + return status; +} + 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; @@ -1097,7 +1132,7 @@ uhdr_error_info_t uhdr_encode(uhdr_codec_private_t* enc) { ultrahdr::JpegR jpegr( nullptr, handle->m_gainmap_scale_factor, handle->m_quality.find(UHDR_GAIN_MAP_IMG)->second, handle->m_use_multi_channel_gainmap, handle->m_gamma, handle->m_enc_preset, - handle->m_min_content_boost, handle->m_max_content_boost); + handle->m_min_content_boost, handle->m_max_content_boost, handle->m_max_disp_brightness); 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; @@ -1200,6 +1235,7 @@ void uhdr_reset_encoder(uhdr_codec_private_t* enc) { handle->m_enc_preset = UHDR_USAGE_REALTIME; handle->m_min_content_boost = FLT_MIN; handle->m_max_content_boost = FLT_MAX; + handle->m_max_disp_brightness = -1.0f; handle->m_compressed_output_buffer.reset(); handle->m_encode_call_status = g_no_error; diff --git a/ultrahdr_api.h b/ultrahdr_api.h index 2f34826a..25cee92b 100644 --- a/ultrahdr_api.h +++ b/ultrahdr_api.h @@ -426,6 +426,20 @@ UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_preset(uhdr_codec_private_t* enc, UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_output_format(uhdr_codec_private_t* enc, uhdr_codec_t media_type); +/*!\brief Set maximum hdr display brightness in nits. For #UHDR_CT_HLG content, this defaults to + * 1000 nits. For #UHDR_CT_PQ content, this defaults to 10000 nits. This configuration is required + * if the hdr intent color transfer is #UHDR_CT_LINEAR. This value determines the weight by which + * the gain map coefficients are scaled during decode. + * + * \param[in] enc encoder instance. + * \param[in] nits max display brightness in nits. Any positive real number in range [203, 10000] + * + * \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_max_display_brightness(uhdr_codec_private_t* enc, + float nits); + /*!\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