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