diff --git a/include/utility/image.h b/include/utility/image.h index 84e32a6..5af2596 100644 --- a/include/utility/image.h +++ b/include/utility/image.h @@ -132,6 +132,10 @@ HRESULT SKIV_Image_CaptureDesktop (DirectX::ScratchImage& image, POINT pos, int void SKIV_Image_CaptureRegion (ImRect capture_area); HRESULT SKIV_Image_TonemapToSDR (const DirectX::Image& image, DirectX::ScratchImage& final_sdr, float mastering_max_nits, float mastering_sdr_nits); +bool SKIV_Image_IsUltraHDR (const wchar_t* wszFileName); +bool SKIV_Image_IsUltraHDR (void* data, int size); +HRESULT SKIV_Image_LoadUltraHDR (DirectX::ScratchImage& image, void* data, int size); + // Structs struct skiv_image_desktop_s { diff --git a/src/tabs/viewer.cpp b/src/tabs/viewer.cpp index 1f808e8..32101a4 100644 --- a/src/tabs/viewer.cpp +++ b/src/tabs/viewer.cpp @@ -723,6 +723,7 @@ enum ImageDecoder { ImageDecoder_EXR, #endif ImageDecoder_HDR, + ImageDecoder_UHDR, ImageDecoder_AVIF // TODO }; @@ -1006,7 +1007,9 @@ LoadLibraryTexture (image_s& image) PLOG_INFO << "Detected an " << type.mime_type << " image"; decoder = - (type.mime_type == L"image/jpeg" ) ? ImageDecoder_stbi : // covers both .jpeg and .jpg + (type.mime_type == L"image/jpeg" ) ? + (SKIV_Image_IsUltraHDR (imagePath.c_str ()) ? ImageDecoder_UHDR : + ImageDecoder_stbi): (type.mime_type == L"image/png" ) ? ImageDecoder_stbi : // Use WIC for proper color correction (type.mime_type == L"image/bmp" ) ? ImageDecoder_stbi : (type.mime_type == L"image/vnd.adobe.photoshop" ) ? ImageDecoder_stbi : @@ -1062,10 +1065,29 @@ LoadLibraryTexture (image_s& image) PLOG_DEBUG_IF(decoder == ImageDecoder_EXR ) << "Using OpenEXR decoder..."; #endif PLOG_DEBUG_IF(decoder == ImageDecoder_HDR ) << "Using Radiance HDR decoder..."; + PLOG_DEBUG_IF(decoder == ImageDecoder_UHDR) << "Using Ultra HDR decoder..."; if (decoder == ImageDecoder_None) return false; + if (decoder == ImageDecoder_UHDR) + { + fseek (pImageFile, 0, SEEK_SET ); + fread (_scratchMemory.get (), _.getInitialSize (), 1, pImageFile); + rewind (pImageFile); + + image.light_info.isHDR = true; + image.is_hdr = true; + + SKIV_Image_LoadUltraHDR (img, _scratchMemory.get (), _.getInitialSize ()); + + meta = img.GetMetadata (); + meta.dimension = DirectX::TEX_DIMENSION_TEXTURE2D; + + converted = true; + succeeded = true; + } + if (decoder == ImageDecoder_stbi) { // If desired_channels is non-zero, *channels_in_file has the number of components that _would_ have been diff --git a/src/utility/image.cpp b/src/utility/image.cpp index 6aeb608..90ab590 100644 --- a/src/utility/image.cpp +++ b/src/utility/image.cpp @@ -1788,7 +1788,7 @@ SKIV_Image_SaveToDisk_SDR (const DirectX::Image& image, const wchar_t* wszFileNa if (percent <= 99.85) { - PLOG_INFO << "99.85th percentile luminance: " << + PLOG_INFO << "99.5th percentile luminance: " << 80.0f * (XMVectorGetY (minLum) + (fLumRange * ((float)i / 65536.0f))) << " nits"; @@ -1974,33 +1974,66 @@ SKIV_Image_SaveToDisk_SDR (const DirectX::Image& image, const wchar_t* wszFileNa #include -using uhdr_create_encoder_pfn = uhdr_codec_private_t* (*)(void); -using uhdr_enc_set_quality_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc, int quality, uhdr_img_label_t intent); -using uhdr_enc_set_raw_image_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc, uhdr_raw_image_t* img, uhdr_img_label_t intent); -using uhdr_enc_set_output_format_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc, uhdr_codec_t media_type); -using uhdr_encode_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc); -using uhdr_get_encoded_stream_pfn = uhdr_compressed_image_t* (*)(uhdr_codec_private_t* enc); -using uhdr_release_encoder_pfn = void (*)(uhdr_codec_private_t* enc); - -uhdr_create_encoder_pfn sk_uhdr_create_encoder = nullptr; -uhdr_enc_set_quality_pfn sk_uhdr_enc_set_quality = nullptr; -uhdr_enc_set_raw_image_pfn sk_uhdr_enc_set_raw_image = nullptr; -uhdr_enc_set_output_format_pfn sk_uhdr_enc_set_output_format = nullptr; -uhdr_encode_pfn sk_uhdr_encode = nullptr; -uhdr_get_encoded_stream_pfn sk_uhdr_get_encoded_stream = nullptr; -uhdr_release_encoder_pfn sk_uhdr_release_encoder = nullptr; - -bool isUHDREncoderAvailable (void) +using uhdr_create_encoder_pfn = uhdr_codec_private_t* (*)(void); +using uhdr_enc_set_quality_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc, int quality, uhdr_img_label_t intent); +using uhdr_enc_set_raw_image_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc, uhdr_raw_image_t* img, uhdr_img_label_t intent); +using uhdr_enc_set_output_format_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc, uhdr_codec_t media_type); +using uhdr_encode_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc); +using uhdr_get_encoded_stream_pfn = uhdr_compressed_image_t* (*)(uhdr_codec_private_t* enc); +using uhdr_release_encoder_pfn = void (*)(uhdr_codec_private_t* enc); +using uhdr_enc_set_min_max_content_boost_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc, float min_boost, float max_boost); +using uhdr_enc_set_preset_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* enc, uhdr_enc_preset_t preset); + +using is_uhdr_image_pfn = int (*)(void* data, int size); + +using uhdr_create_decoder_pfn = uhdr_codec_private_t* (*)(void); +using uhdr_release_decoder_pfn = void (*)(uhdr_codec_private_t* dec); +using uhdr_dec_set_image_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* dec, uhdr_compressed_image_t* img); +using uhdr_dec_set_out_color_transfer_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* dec, uhdr_color_transfer_t ct); +using uhdr_dec_set_out_img_format_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* dec, uhdr_img_fmt_t fmt); +using uhdr_dec_set_out_max_display_boost_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* dec, float display_boost); +using uhdr_dec_probe_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* dec); +using uhdr_decode_pfn = uhdr_error_info_t (*)(uhdr_codec_private_t* dec); +using uhdr_get_decoded_image_pfn = uhdr_raw_image_t* (*)(uhdr_codec_private_t* dec); +using uhdr_get_gain_map_image_pfn = uhdr_raw_image_t* (*)(uhdr_codec_private_t* dec); +using uhdr_dec_get_gain_map_metadata_pfn = uhdr_gainmap_metadata_t* (*)(uhdr_codec_private_t* dec); + +uhdr_create_encoder_pfn sk_uhdr_create_encoder = nullptr; +uhdr_enc_set_quality_pfn sk_uhdr_enc_set_quality = nullptr; +uhdr_enc_set_raw_image_pfn sk_uhdr_enc_set_raw_image = nullptr; +uhdr_enc_set_output_format_pfn sk_uhdr_enc_set_output_format = nullptr; +uhdr_encode_pfn sk_uhdr_encode = nullptr; +uhdr_get_encoded_stream_pfn sk_uhdr_get_encoded_stream = nullptr; +uhdr_release_encoder_pfn sk_uhdr_release_encoder = nullptr; +uhdr_enc_set_min_max_content_boost_pfn sk_uhdr_enc_set_min_max_content_boost = nullptr; +uhdr_enc_set_preset_pfn sk_uhdr_enc_set_preset = nullptr; + +is_uhdr_image_pfn sk_is_uhdr_image = nullptr; + +uhdr_create_decoder_pfn sk_uhdr_create_decoder = nullptr; +uhdr_release_decoder_pfn sk_uhdr_release_decoder = nullptr; +uhdr_dec_set_image_pfn sk_uhdr_dec_set_image = nullptr; +uhdr_dec_set_out_color_transfer_pfn sk_uhdr_dec_set_out_color_transfer = nullptr; +uhdr_dec_set_out_img_format_pfn sk_uhdr_dec_set_out_img_format = nullptr; +uhdr_dec_set_out_max_display_boost_pfn sk_uhdr_dec_set_out_max_display_boost = nullptr; +uhdr_dec_probe_pfn sk_uhdr_dec_probe = nullptr; +uhdr_decode_pfn sk_uhdr_decode = nullptr; +uhdr_get_decoded_image_pfn sk_uhdr_get_decoded_image = nullptr; +uhdr_get_gain_map_image_pfn sk_uhdr_get_gain_map_image = nullptr; +uhdr_dec_get_gain_map_metadata_pfn sk_uhdr_dec_get_gain_map_metadata = nullptr; + +bool isUHDRCodecAvailable (void) { - // Disable for now - return false; - static HMODULE hModUHDR = nullptr; static const wchar_t* wszPluginArch = SK_RunLHIfBitness ( 64, LR"(x64\)", LR"(x86\)" ); + static const wchar_t* wszDownloadURL = + SK_RunLHIfBitness ( 64, LR"(https://sk-data.special-k.info/addon/ImageCodecs/libuhdr/x64/uhdr.dll)", + LR"(https://sk-data.special-k.info/addon/ImageCodecs/libuhdr/x86/uhdr.dll)" ); + SK_RunOnce ( { SKIF_RegistrySettings& _registry = @@ -2022,11 +2055,8 @@ bool isUHDREncoderAvailable (void) std::wstring path_to_uhdr = path_to_sk + L"uhdr.dll"; if (! std::filesystem::exists (path_to_uhdr, ec)) -//#ifdef _M_X64 - SKIF_Util_GetWebResource (L"https://sk-data.special-k.info/addon/ImageCodecs/libuhdr/x64/uhdr.dll", path_to_uhdr); -//#else -// SKIF_Util_GetWebResource (L"https://sk-data.special-k.info/addon/ImageCodecs/libuhdr/x86/uhdr.dll", path_to_uhdr); -//#endif + SKIF_Util_GetWebResource (wszDownloadURL, path_to_uhdr); + hModUHDR = LoadLibraryW (path_to_uhdr.c_str ()); if (hModUHDR != nullptr) @@ -2048,13 +2078,29 @@ bool isUHDREncoderAvailable (void) if (hModUHDR != nullptr) { - sk_uhdr_create_encoder = (uhdr_create_encoder_pfn) GetProcAddress (hModUHDR, "uhdr_create_encoder"); - sk_uhdr_enc_set_quality = (uhdr_enc_set_quality_pfn) GetProcAddress (hModUHDR, "uhdr_enc_set_quality"); - sk_uhdr_enc_set_raw_image = (uhdr_enc_set_raw_image_pfn) GetProcAddress (hModUHDR, "uhdr_enc_set_raw_image"); - sk_uhdr_enc_set_output_format = (uhdr_enc_set_output_format_pfn)GetProcAddress (hModUHDR, "uhdr_enc_set_output_format"); - sk_uhdr_encode = (uhdr_encode_pfn) GetProcAddress (hModUHDR, "uhdr_encode"); - sk_uhdr_get_encoded_stream = (uhdr_get_encoded_stream_pfn) GetProcAddress (hModUHDR, "uhdr_get_encoded_stream"); - sk_uhdr_release_encoder = (uhdr_release_encoder_pfn) GetProcAddress (hModUHDR, "uhdr_release_encoder"); + sk_uhdr_create_encoder = (uhdr_create_encoder_pfn) GetProcAddress (hModUHDR, "uhdr_create_encoder"); + sk_uhdr_enc_set_quality = (uhdr_enc_set_quality_pfn) GetProcAddress (hModUHDR, "uhdr_enc_set_quality"); + sk_uhdr_enc_set_raw_image = (uhdr_enc_set_raw_image_pfn) GetProcAddress (hModUHDR, "uhdr_enc_set_raw_image"); + sk_uhdr_enc_set_output_format = (uhdr_enc_set_output_format_pfn) GetProcAddress (hModUHDR, "uhdr_enc_set_output_format"); + sk_uhdr_encode = (uhdr_encode_pfn) GetProcAddress (hModUHDR, "uhdr_encode"); + sk_uhdr_get_encoded_stream = (uhdr_get_encoded_stream_pfn) GetProcAddress (hModUHDR, "uhdr_get_encoded_stream"); + sk_uhdr_release_encoder = (uhdr_release_encoder_pfn) GetProcAddress (hModUHDR, "uhdr_release_encoder"); + sk_uhdr_enc_set_min_max_content_boost = (uhdr_enc_set_min_max_content_boost_pfn)GetProcAddress (hModUHDR, "uhdr_enc_set_min_max_content_boost"); + sk_uhdr_enc_set_preset = (uhdr_enc_set_preset_pfn) GetProcAddress (hModUHDR, "uhdr_enc_set_preset"); + + sk_is_uhdr_image = (is_uhdr_image_pfn) GetProcAddress (hModUHDR, "is_uhdr_image"); + + sk_uhdr_create_decoder = (uhdr_create_decoder_pfn) GetProcAddress (hModUHDR, "uhdr_create_decoder"); + sk_uhdr_release_decoder = (uhdr_release_decoder_pfn) GetProcAddress (hModUHDR, "uhdr_release_decoder"); + sk_uhdr_dec_set_image = (uhdr_dec_set_image_pfn) GetProcAddress (hModUHDR, "uhdr_dec_set_image"); + sk_uhdr_dec_set_out_color_transfer = (uhdr_dec_set_out_color_transfer_pfn) GetProcAddress (hModUHDR, "uhdr_dec_set_out_color_transfer"); + sk_uhdr_dec_set_out_img_format = (uhdr_dec_set_out_img_format_pfn) GetProcAddress (hModUHDR, "uhdr_dec_set_out_img_format"); + sk_uhdr_dec_set_out_max_display_boost = (uhdr_dec_set_out_max_display_boost_pfn)GetProcAddress (hModUHDR, "uhdr_dec_set_out_max_display_boost"); + sk_uhdr_dec_probe = (uhdr_dec_probe_pfn) GetProcAddress (hModUHDR, "uhdr_dec_probe"); + sk_uhdr_decode = (uhdr_decode_pfn) GetProcAddress (hModUHDR, "uhdr_decode"); + sk_uhdr_get_decoded_image = (uhdr_get_decoded_image_pfn) GetProcAddress (hModUHDR, "uhdr_get_decoded_image"); + sk_uhdr_get_gain_map_image = (uhdr_get_gain_map_image_pfn) GetProcAddress (hModUHDR, "uhdr_get_gain_map_image"); + sk_uhdr_dec_get_gain_map_metadata = (uhdr_dec_get_gain_map_metadata_pfn) GetProcAddress (hModUHDR, "uhdr_dec_get_gain_map_metadata"); return true; } @@ -2081,7 +2127,7 @@ bool isUHDREncoderAvailable (void) void SKIV_Image_SaveToDisk_UltraHDR (const DirectX::Image& image, const wchar_t* wszFileName) { - if (! isUHDREncoderAvailable ()) + if (! isUHDRCodecAvailable ()) return; uhdr_raw_image raw_hdr; @@ -2209,11 +2255,108 @@ SKIV_Image_SaveToDisk_UltraHDR (const DirectX::Image& image, const wchar_t* wszF sk_uhdr_release_encoder (encoder); } +bool +SKIV_Image_IsUltraHDR (const wchar_t* wszFileName) +{ + if (! isUHDRCodecAvailable ()) + return false; + + FILE* fImageFile = + _wfopen (wszFileName, L"rb"); + + if (fImageFile != nullptr) + { + fseek ( fImageFile, 0, SEEK_END); + auto size = ftell (fImageFile); + auto data = + std::make_unique (size); + + rewind ( fImageFile); + fread (data.get (), 1, size, fImageFile); + fclose ( fImageFile); + + return + sk_is_uhdr_image (data.get (), size) != 0; + } + + return false; +} + +bool +SKIV_Image_IsUltraHDR (void* data, int size) +{ + if (! isUHDRCodecAvailable ()) + return false; + + return + sk_is_uhdr_image (data, size) != 0; +} + HRESULT -SKIV_Image_SaveToDisk_HDR (const DirectX::Image& image, const wchar_t* wszFileName) +SKIV_Image_LoadUltraHDR (DirectX::ScratchImage& image, void* data, int size) { - SKIV_Image_SaveToDisk_UltraHDR (image, L"test.jpg"); + auto decoder = + sk_uhdr_create_decoder (); + + uhdr_compressed_image_t uhdr_image; + uhdr_image.data = data; + uhdr_image.data_sz = size; + uhdr_image.capacity = size; + uhdr_image.cg = UHDR_CG_BT_709;//UHDR_CG_UNSPECIFIED; + uhdr_image.ct = UHDR_CT_LINEAR;//UHDR_CT_UNSPECIFIED; + uhdr_image.range = UHDR_CR_FULL_RANGE;//UHDR_CR_UNSPECIFIED; + + sk_uhdr_dec_set_image (decoder, &uhdr_image); + sk_uhdr_dec_probe (decoder); + sk_uhdr_dec_set_out_color_transfer (decoder, UHDR_CT_LINEAR); + sk_uhdr_dec_set_out_img_format (decoder, UHDR_IMG_FMT_64bppRGBAHalfFloat); + sk_uhdr_decode (decoder); + + auto decoded_img = + sk_uhdr_get_decoded_image (decoder); + + DirectX::Image img; + + img.pixels = (uint8_t *)decoded_img->planes [UHDR_PLANE_PACKED]; + img.rowPitch = decoded_img->stride [UHDR_PLANE_PACKED] * sizeof (uint16_t) * 4; + img.width = decoded_img->w; + img.height = decoded_img->h; + img.format = DXGI_FORMAT_R16G16B16A16_FLOAT; + + DirectX::ScratchImage + unscaled_image; + unscaled_image.InitializeFromImage (img); + + auto metadata = + sk_uhdr_dec_get_gain_map_metadata (decoder); + + DirectX::TransformImage (*unscaled_image.GetImage (0,0,0), + [&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t width, size_t y) + { + using namespace DirectX; + + for (size_t j = 0; j < width; ++j) + { + XMVECTOR value = inPixels [j]; + + value = + XMVectorMultiply (value, XMVectorReplicate (metadata->hdr_capacity_max)); + + outPixels [j] = value; + } + + UNREFERENCED_PARAMETER(y); + }, image); + + sk_uhdr_release_decoder (decoder); + + return S_OK; +} + +HRESULT +SKIV_Image_SaveToDisk_HDR (const DirectX::Image& image, const wchar_t* wszFileName) +{ using namespace DirectX; const Image* pOutputImage = ℑ