diff options
Diffstat (limited to 'src/helpers')
| -rw-r--r-- | src/helpers/Drm.cpp | 2 | ||||
| -rw-r--r-- | src/helpers/Monitor.cpp | 70 | ||||
| -rw-r--r-- | src/helpers/Monitor.hpp | 4 | ||||
| -rw-r--r-- | src/helpers/MonitorResources.cpp | 37 | ||||
| -rw-r--r-- | src/helpers/MonitorResources.hpp | 4 | ||||
| -rw-r--r-- | src/helpers/cm/ColorManagement.cpp | 30 | ||||
| -rw-r--r-- | src/helpers/cm/ColorManagement.hpp | 60 |
7 files changed, 172 insertions, 35 deletions
diff --git a/src/helpers/Drm.cpp b/src/helpers/Drm.cpp index f91c5692..4f3119f8 100644 --- a/src/helpers/Drm.cpp +++ b/src/helpers/Drm.cpp @@ -32,7 +32,7 @@ std::optional<dev_t> DRM::devIDFromFD(int fd) { } bool DRM::sameGpu(int fd1, int fd2) { - if (fd1 >= 0 && fd1 == fd2) + if (fd1 < 0 || fd2 < 0 || fd1 == fd2) return true; static std::mutex cacheMutex; diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index eef68c2f..5fb6cf31 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -62,7 +62,7 @@ using namespace NColorManagement; using namespace Render::GL; using namespace Monitor; -CMonitor::CMonitor(SP<Aquamarine::IOutput> output_) : m_state(this), m_output(output_), m_imageDescription(DEFAULT_IMAGE_DESCRIPTION) { +CMonitor::CMonitor(SP<Aquamarine::IOutput> output_) : m_state(this), m_output(output_), m_imageDescription(getDefaultImageDescription()) { g_pAnimationManager->createAnimation(0.f, m_specialFade, Config::animationTree()->getAnimationPropertyConfig("specialWorkspaceIn"), AVARDAMAGE_NONE); m_specialFade->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); }); static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor"); @@ -511,9 +511,9 @@ static NColorManagement::eTransferFunction chooseTF(NTransferFunction::eTF tf) { const auto sdrEOTF = NTransferFunction::fromConfig(); switch (tf) { - case NTransferFunction::TF_DEFAULT: case NTransferFunction::TF_GAMMA22: case NTransferFunction::TF_FORCED_GAMMA22: return NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; + case NTransferFunction::TF_DEFAULT: case NTransferFunction::TF_SRGB: return NColorManagement::CM_TRANSFER_FUNCTION_SRGB; case NTransferFunction::TF_AUTO: // use global setting @@ -949,7 +949,7 @@ bool CMonitor::applyMonitorRule(Config::CMonitorRule&& pMonitorRule, bool force) m_enabled10bit = set10bit; - m_supportsWideColor = RULE->m_supportsHDR; + m_supportsWideColor = RULE->m_supportsWideColor; m_supportsHDR = RULE->m_supportsHDR; if (RULE->m_iccFile.empty()) { @@ -1853,7 +1853,6 @@ bool CMonitor::updateTearing() { uint16_t CMonitor::isDSBlocked(bool full) { uint16_t reasons = 0; static auto PDIRECTSCANOUT = CConfigValue<Hyprlang::INT>("render:direct_scanout"); - static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough"); static auto PNONSHADER = CConfigValue<Hyprlang::INT>("render:non_shader_cm"); const auto PWORKSPACE = m_activeWorkspace; @@ -1930,9 +1929,14 @@ uint16_t CMonitor::isDSBlocked(bool full) { const bool surfaceIsHDR = PSURFACE->m_colorManagement.valid() && PSURFACE->m_colorManagement->isHDR(); const bool surfaceIsScRGB = surfaceIsHDR && PSURFACE->m_colorManagement->isWindowsScRGB(); - if (needsCM() && (*PNONSHADER != CM_NS_IGNORE || surfaceIsScRGB) && !canNoShaderCM() && - ((inHDR() && (*PPASS == 0 || !surfaceIsHDR || surfaceIsScRGB)) || (!inHDR() && (*PPASS != 1 || surfaceIsHDR)))) - reasons |= DS_BLOCK_CM; + if (surfaceIsScRGB) + reasons |= DS_BLOCK_CM; // block scRGB + else if (*PNONSHADER != CM_NS_IGNORE) { + if (!surfaceIsHDR && needsCM() && !canNoShaderCM(true)) + reasons |= DS_BLOCK_CM; // block SDR that needs CM while non-shader CM isn't available + else if (surfaceIsHDR && !inHDR()) + reasons |= DS_BLOCK_CM; // block HDR while monitor isn't in HDR mode + } return reasons; } @@ -1987,6 +1991,7 @@ bool CMonitor::attemptDirectScanout() { if (m_lastScanout.expired()) m_prevDrmFormat = m_drmFormat; + const bool NEEDS_TEST = !m_lastScanout || m_drmFormat != params.format; // do not retest while it's active if (m_drmFormat != params.format) { m_output->state->setFormat(params.format); m_drmFormat = params.format; @@ -1997,7 +2002,7 @@ bool CMonitor::attemptDirectScanout() { m_output->state->setPresentationMode(m_tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE : Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC); - if (!m_state.test()) { + if (NEEDS_TEST && !m_state.test()) { Log::logger->log(Log::TRACE, "attemptDirectScanout: failed basic test"); return false; } @@ -2278,7 +2283,7 @@ std::optional<NColorManagement::PImageDescription> CMonitor::getFSImageDescripti const auto ROOT_SURF = FS_WINDOW->wlSurface()->resource(); const auto SURF = ROOT_SURF->findWithCM(); - return SURF ? NColorManagement::CImageDescription::from(SURF->m_colorManagement->imageDescription()) : DEFAULT_IMAGE_DESCRIPTION; + return SURF ? NColorManagement::CImageDescription::from(SURF->m_colorManagement->imageDescription()) : getDefaultImageDescription(); } NColorManagement::SPCPRimaries CMonitor::getMasteringPrimaries() { @@ -2317,8 +2322,20 @@ bool CMonitor::needsCM() { return SRC_DESC.has_value() && SRC_DESC.value() != m_imageDescription; } +static bool isCompatibleTF(eTransferFunction sourceTF, eTransferFunction targetTF) { + static auto PNONSHADER = CConfigValue<Hyprlang::INT>("render:non_shader_cm"); + const auto sdrEOTF = NTransferFunction::fromConfig(); + return sourceTF == targetTF // same + || (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && sourceTF == NColorManagement::CM_TRANSFER_FUNCTION_SRGB // forced source gamma22 to output gamma22 + && targetTF == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22) // + || (*PNONSHADER == CM_NS_ONDEMAND // FIXME incorrect but good enough for DS + && (sourceTF == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 || sourceTF == NColorManagement::CM_TRANSFER_FUNCTION_SRGB) // + && (targetTF == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 || targetTF == NColorManagement::CM_TRANSFER_FUNCTION_SRGB)) // + ; +} + // TODO support more drm properties -bool CMonitor::canNoShaderCM() { +bool CMonitor::canNoShaderCM(bool forDSmode) { static auto PNONSHADER = CConfigValue<Hyprlang::INT>("render:non_shader_cm"); if (*PNONSHADER == CM_NS_DISABLE) return false; @@ -2327,22 +2344,22 @@ bool CMonitor::canNoShaderCM() { if (!SRC_DESC.has_value()) return false; - if (SRC_DESC.value() == m_imageDescription) + const auto& DST_DESC = forDSmode ? m_imageDescription : resources()->m_imageDescription; + if (SRC_DESC.value() == DST_DESC) return true; // no CM needed - const auto SRC_DESC_VALUE = SRC_DESC.value()->value(); + const auto& SRC_DESC_VALUE = SRC_DESC.value()->value(); if (m_imageDescription->value().icc.present) return false; - const auto sdrEOTF = NTransferFunction::fromConfig(); + Log::logger->log(Log::TRACE, "CM: can no shder compares src={} to output={}", SRC_DESC_VALUE, m_imageDescription->value()); + // only primaries differ return ( - (SRC_DESC_VALUE.transferFunction == m_imageDescription->value().transferFunction || - (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && SRC_DESC_VALUE.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB && - m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22)) && - SRC_DESC_VALUE.transferFunctionPower == m_imageDescription->value().transferFunctionPower && - (!inHDR() || SRC_DESC_VALUE.luminances == m_imageDescription->value().luminances) + isCompatibleTF(SRC_DESC_VALUE.transferFunction, DST_DESC->value().transferFunction) // + && SRC_DESC_VALUE.transferFunctionPower == DST_DESC->value().transferFunctionPower // + && (!inHDR() || SRC_DESC_VALUE.luminances == DST_DESC->value().luminances) // not used by shaders atm // && SRC_DESC_VALUE.masteringLuminances == m_imageDescription->value().masteringLuminances && SRC_DESC_VALUE.maxCLL == m_imageDescription->value().maxCLL && SRC_DESC_VALUE.maxFALL == m_imageDescription->value().maxFALL ); @@ -2505,6 +2522,7 @@ bool CMonitor::needsUnmodifiedCopy() { if (!HAS_MODS) return false; + // TODO handle some FP16 cases if (m_imageDescription->value().transferFunction != CM_TRANSFER_FUNCTION_ST2084_PQ && m_imageDescription->value().transferFunction != CM_TRANSFER_FUNCTION_HLG) return false; @@ -2512,15 +2530,25 @@ bool CMonitor::needsUnmodifiedCopy() { } bool CMonitor::useFP16() { - static const auto PFP16 = CConfigValue<Hyprlang::INT>("render:use_fp16"); - return *PFP16 == 1 || (*PFP16 == 2 && m_imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ); + static const auto PFP16 = CConfigValue<Hyprlang::INT>("render:use_fp16"); + bool shouldUse = *PFP16 == 1 || (*PFP16 == 2 && m_imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ); + static bool usedBefore = shouldUse; + if (usedBefore != shouldUse) { + usedBefore = shouldUse; + m_blurFBDirty = true; + } + return shouldUse; } WP<CMonitorResources> CMonitor::resources() { const auto DRM_FORMAT = useFP16() ? DRM_FORMAT_ABGR16161616F : m_output->state->state().drmFormat; + const auto DESC = useFP16() ? LINEAR_IMAGE_DESCRIPTION : m_imageDescription; if (!m_resources || m_resources->m_drmFormat != DRM_FORMAT || m_resources->m_size != m_pixelSize) - m_resources = makeUnique<CMonitorResources>(m_self, DRM_FORMAT, m_pixelSize, useFP16() ? LINEAR_IMAGE_DESCRIPTION : m_imageDescription); + m_resources = makeUnique<CMonitorResources>(m_self, DRM_FORMAT, m_pixelSize, DESC); + + if (m_resources->m_imageDescription != DESC) + m_resources->setImageDescription(DESC); return m_resources; } diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 74fe676b..59a64237 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -365,8 +365,8 @@ class CMonitor { uint32_t getPreferredReadFormat(); bool needsCM(); - /// Can do CM without shader - bool canNoShaderCM(); + /// Can do CM without shader (forDSmode ? check output image description : check workbuffer image description) + bool canNoShaderCM(bool forDSmode = false); bool doesNoShaderCM(); bool m_enabled = false; diff --git a/src/helpers/MonitorResources.cpp b/src/helpers/MonitorResources.cpp index 6f5c4d33..8e7723c2 100644 --- a/src/helpers/MonitorResources.cpp +++ b/src/helpers/MonitorResources.cpp @@ -15,6 +15,7 @@ CMonitorResources::CMonitorResources(WP<CMonitor> monitor, DRMFormat format, Vec m_blurFB(g_pHyprRenderer->createFB(std::format("Monitor {} blur FB", monitor->m_name))), m_monitor(monitor), m_drmFormat(format), m_size(size), m_imageDescription(imageDescription) { initFB(m_blurFB); + monitor->m_blurFBDirty = true; } void CMonitorResources::initFB(SP<Render::IFramebuffer> fb) { @@ -23,6 +24,19 @@ void CMonitorResources::initFB(SP<Render::IFramebuffer> fb) { fb->setImageDescription(m_imageDescription); } +void CMonitorResources::setImageDescription(NColorManagement::PImageDescription imageDescription) { + if (m_imageDescription == imageDescription) + return; + m_imageDescription = imageDescription; + m_blurFB->setImageDescription(imageDescription); + for (const auto& res : m_workBuffers) + res.buffer->setImageDescription(imageDescription); + if (m_monitorMirrorFB) + m_monitorMirrorFB->setImageDescription(NColorManagement::getDefaultImageDescription()); + if (m_mirrorTex) + m_mirrorTex->m_imageDescription = getMirrorTexImageDescription(); +} + SP<Render::IFramebuffer> CMonitorResources::getUnusedWorkBuffer() { std::erase_if(m_workBuffers, [](const auto& res) { return res.lastUsed.getSeconds() >= MAX_UNUSED_SECONDS; }); @@ -65,7 +79,7 @@ SP<Render::IFramebuffer> CMonitorResources::mirrorFB() { if (!m_monitorMirrorFB->isAllocated()) { m_monitorMirrorFB->alloc(m_size.x, m_size.y, DRM_FORMAT_XRGB8888); - m_monitorMirrorFB->setImageDescription(NColorManagement::DEFAULT_IMAGE_DESCRIPTION); + m_monitorMirrorFB->setImageDescription(NColorManagement::getDefaultImageDescription()); } return m_monitorMirrorFB; @@ -75,13 +89,9 @@ SP<Render::ITexture> CMonitorResources::getMirrorTexture() { return hasMirrorFB() ? mirrorFB()->getTexture() : nullptr; } -void CMonitorResources::enableMirror() { - if (m_mirrorTex) - return; - m_mirrorTex = g_pHyprRenderer->createTexture(); - m_mirrorTex->allocate({m_size.x, m_size.y}, DRM_FORMAT_XRGB8888); - m_mirrorTex->m_imageDescription = CImageDescription::from(SImageDescription{ - .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22, +NColorManagement::PImageDescription CMonitorResources::getMirrorTexImageDescription() { + return CImageDescription::from(SImageDescription{ + .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_SRGB, .primariesNameSet = m_imageDescription->value().primariesNameSet, .primariesNamed = m_imageDescription->value().primariesNamed, .primaries = m_imageDescription->value().primaries, @@ -89,6 +99,17 @@ void CMonitorResources::enableMirror() { }); } +void CMonitorResources::enableMirror() { + if (m_mirrorTex) + return; + m_mirrorTex = g_pHyprRenderer->createTexture(); + m_mirrorTex->allocate({m_size.x, m_size.y}, DRM_FORMAT_XRGB8888); + m_mirrorTex->m_imageDescription = getMirrorTexImageDescription(); + m_monitor->m_blurFBDirty = true; +} + void CMonitorResources::disableMirror() { + if (m_mirrorTex) + m_monitor->m_blurFBDirty = true; m_mirrorTex.reset(); } diff --git a/src/helpers/MonitorResources.hpp b/src/helpers/MonitorResources.hpp index a5a60fa5..6092b9c9 100644 --- a/src/helpers/MonitorResources.hpp +++ b/src/helpers/MonitorResources.hpp @@ -25,7 +25,9 @@ namespace Monitor { SP<Render::IFramebuffer> m_blurFB; private: - void initFB(SP<Render::IFramebuffer> fb); + void initFB(SP<Render::IFramebuffer> fb); + void setImageDescription(NColorManagement::PImageDescription imageDescription); + NColorManagement::PImageDescription getMirrorTexImageDescription(); struct SResource { SP<Render::IFramebuffer> buffer; diff --git a/src/helpers/cm/ColorManagement.cpp b/src/helpers/cm/ColorManagement.cpp index 829adbe7..5e4725d4 100644 --- a/src/helpers/cm/ColorManagement.cpp +++ b/src/helpers/cm/ColorManagement.cpp @@ -1,10 +1,12 @@ #include "ColorManagement.hpp" #include "../../macros.hpp" +#include "helpers/TransferFunction.hpp" #include <hyprutils/memory/UniquePtr.hpp> #include <map> #include <vector> using namespace NColorManagement; +using namespace NTransferFunction; namespace NColorManagement { // expected to be small @@ -110,6 +112,20 @@ WP<const CPrimaries> CImageDescription::getPrimaries() const { return CPrimaries::from(m_primariesId); } +bool CImageDescription::needsCM(WP<const CImageDescription> target) const { + if (m_id == target->m_id) + return false; + + return m_imageDescription.icc.present || target->m_imageDescription.icc.present // TODO compare ICC + || m_imageDescription.transferFunction != target->m_imageDescription.transferFunction // + //|| m_imageDescription.transferFunctionPower != target->m_imageDescription.transferFunctionPower // TODO unsupported + || m_imageDescription.getPrimaries() != target->m_imageDescription.getPrimaries() // + // || m_imageDescription.masteringPrimaries != target->m_imageDescription.masteringPrimaries // TODO unused + || m_imageDescription.luminances != target->m_imageDescription.luminances // + // || m_imageDescription.masteringLuminances != target->m_imageDescription.masteringLuminances // TODO unused + ; +} + static Mat3x3 diag3(const std::array<float, 3>& s) { return Mat3x3{std::array<float, 9>{s[0], 0, 0, 0, s[1], 0, 0, 0, s[2]}}; } @@ -190,4 +206,16 @@ Mat3x3 NColorManagement::adaptBradford(Hyprgraphics::CColor::xy srcW, Hyprgraphi result.multiply(diag3(scale)).multiply(Bradford); return result; -}
\ No newline at end of file +} + +PImageDescription NColorManagement::getDefaultImageDescription() { + const auto TF = fromConfig(); + switch (TF) { + case TF_AUTO: + case TF_GAMMA22: + case TF_FORCED_GAMMA22: return DEFAULT_GAMMA22_IMAGE_DESCRIPTION; + case TF_DEFAULT: + case TF_SRGB: return DEFAULT_SRGB_IMAGE_DESCRIPTION; + default: UNREACHABLE(); + } +} diff --git a/src/helpers/cm/ColorManagement.hpp b/src/helpers/cm/ColorManagement.hpp index dccbb972..183a8544 100644 --- a/src/helpers/cm/ColorManagement.hpp +++ b/src/helpers/cm/ColorManagement.hpp @@ -1,11 +1,13 @@ #pragma once #include "color-management-v1.hpp" +#include <format> #include <hyprgraphics/color/Color.hpp> #include "../../helpers/memory/Memory.hpp" #include "../../helpers/math/Math.hpp" #include <filesystem> +#include <string> #include <vector> #include <expected> @@ -78,6 +80,24 @@ namespace NColorManagement { default: return sc<eTransferFunction>(tf); } } + inline std::string tfToString(eTransferFunction tf) { + switch (tf) { + case CM_TRANSFER_FUNCTION_BT1886: return "TF:BT1886"; + case CM_TRANSFER_FUNCTION_GAMMA22: return "TF:GAMMA22"; + case CM_TRANSFER_FUNCTION_GAMMA28: return "TF:GAMMA28"; + case CM_TRANSFER_FUNCTION_ST240: return "TF:ST240"; + case CM_TRANSFER_FUNCTION_EXT_LINEAR: return "TF:EXT_LINEAR"; + case CM_TRANSFER_FUNCTION_LOG_100: return "TF:LOG_100"; + case CM_TRANSFER_FUNCTION_LOG_316: return "TF:LOG_316"; + case CM_TRANSFER_FUNCTION_XVYCC: return "TF:XVYCC"; + case CM_TRANSFER_FUNCTION_SRGB: return "TF:SRGB"; + case CM_TRANSFER_FUNCTION_EXT_SRGB: return "TF:EXT_SRGB"; + case CM_TRANSFER_FUNCTION_ST2084_PQ: return "TF:ST2084_PQ"; + case CM_TRANSFER_FUNCTION_ST428: return "TF:ST428"; + case CM_TRANSFER_FUNCTION_HLG: return "TF:HLG"; + default: return "TF:ERROR"; + } + } using SPCPRimaries = Hyprgraphics::SPCPRimaries; @@ -320,6 +340,7 @@ namespace NColorManagement { uint64_t id() const; WP<const CPrimaries> getPrimaries() const; + bool needsCM(WP<const CImageDescription> target) const; private: CImageDescription(const SImageDescription& imageDescription, const uint64_t imageDescriptionId); @@ -330,7 +351,9 @@ namespace NColorManagement { using PImageDescription = WP<const CImageDescription>; - static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ + PImageDescription getDefaultImageDescription(); + + static const auto DEFAULT_GAMMA22_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22, .primariesNameSet = true, .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, @@ -338,6 +361,14 @@ namespace NColorManagement { .luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}, }); + static const auto DEFAULT_SRGB_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ + .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_SRGB, + .primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, + .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB), + .luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}, + }); + static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, .primariesNameSet = true, @@ -363,3 +394,30 @@ namespace NColorManagement { .luminances = {.min = 0, .max = 10000, .reference = 80}, }); } + +template <typename CharT> +struct std::formatter<Hyprgraphics::SPCPRimaries, CharT> : std::formatter<CharT> { + template <typename FormatContext> + auto format(const Hyprgraphics::SPCPRimaries& primaries, FormatContext& ctx) const { + return std::format_to(ctx.out(), "[r={},{} g={},{} b={},{} w={},{}]", primaries.red.x, primaries.red.y, primaries.green.x, primaries.green.y, primaries.blue.x, + primaries.blue.y, primaries.white.x, primaries.white.y); + } +}; + +template <typename CharT> +struct std::formatter<NColorManagement::SImageDescription::SPCLuminances, CharT> : std::formatter<CharT> { + template <typename FormatContext> + auto format(const NColorManagement::SImageDescription::SPCLuminances& luminances, FormatContext& ctx) const { + return std::format_to(ctx.out(), "[{}-{}({})]", luminances.min, luminances.max, luminances.reference); + } +}; + +template <typename CharT> +struct std::formatter<NColorManagement::SImageDescription, CharT> : std::formatter<CharT> { + template <typename FormatContext> + auto format(const NColorManagement::SImageDescription& imageDescription, FormatContext& ctx) const { + return std::format_to(ctx.out(), "[{}{}, primaries={}, luminances={}]", NColorManagement::tfToString(imageDescription.transferFunction), + imageDescription.transferFunctionPower != 1.0f ? std::format("^{}", imageDescription.transferFunctionPower) : "", imageDescription.getPrimaries(), + imageDescription.luminances); + } +}; |
