render/cm: various updates, remove old protocols (#12693)

* fix named primaries

* default to gamma22

* mark mastering primaries as supported

* remove xx-cm and frog support

* immutable primaries and image descriptions

* clang-format
This commit is contained in:
UjinT34 2025-12-27 20:01:46 +03:00 committed by GitHub
parent 42447a50d6
commit 6d3b17ee83
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 334 additions and 3171 deletions

View file

@ -502,8 +502,6 @@ protocolnew("protocols" "kde-server-decoration" true)
protocolnew("protocols" "wlr-data-control-unstable-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
protocolnew("protocols" "wlr-layer-shell-unstable-v1" true)
protocolnew("protocols" "xx-color-management-v4" true)
protocolnew("protocols" "frog-color-management-v1" true)
protocolnew("protocols" "wayland-drm" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true)

View file

@ -1,366 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="frog_color_management_v1">
<copyright>
Copyright © 2023 Joshua Ashton for Valve Software
Copyright © 2023 Xaver Hugl
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="experimental color management protocol">
The aim of this color management extension is to get HDR games working quickly,
and have an easy way to test implementations in the wild before the upstream
protocol is ready to be merged.
For that purpose it's intentionally limited and cut down and does not serve
all uses cases.
</description>
<interface name="frog_color_management_factory_v1" version="1">
<description summary="color management factory">
The color management factory singleton creates color managed surface objects.
</description>
<request name="destroy" type="destructor"></request>
<request name="get_color_managed_surface">
<description summary="create color management interface for surface">
</description>
<arg name="surface" type="object" interface="wl_surface"
summary="target surface" />
<arg name="callback" type="new_id" interface="frog_color_managed_surface"
summary="new color managed surface object" />
</request>
</interface>
<interface name="frog_color_managed_surface" version="1">
<description summary="color managed surface">
Interface for changing surface color management and HDR state.
An implementation must: support every part of the version
of the frog_color_managed_surface interface it exposes.
Including all known enums associated with a given version.
</description>
<request name="destroy" type="destructor">
<description summary="destroy color managed surface">
Destroying the color managed surface resets all known color
state for the surface back to 'undefined' implementation-specific
values.
</description>
</request>
<enum name="transfer_function">
<description summary="known transfer functions">
Extended information on the transfer functions described
here can be found in the Khronos Data Format specification:
https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html
</description>
<entry name="undefined" value="0"
summary="specifies undefined, implementation-specific handling of the surface's transfer function." />
<entry name="srgb" value="1"
summary="specifies the sRGB non-linear EOTF. An implementation may: display this as Gamma 2.2 for the purposes of being consistent with content rendering across displays, rendering_intent and user expectations." />
<entry name="gamma_22" value="2" summary="specifies gamma 2.2 power curve as the EOTF" />
<entry name="st2084_pq" value="3"
summary="specifies the SMPTE ST2084 Perceptual Quantizer (PQ) EOTF" />
<entry name="scrgb_linear" value="4"
summary="specifies the scRGB (extended sRGB) linear EOTF. Note: Primaries outside the gamut triangle specified can be expressed with negative values for this transfer function." />
</enum>
<request name="set_known_transfer_function">
<description summary="sets a known transfer function for a surface" />
<arg name="transfer_function" type="uint" enum="transfer_function"
summary="transfer function for the surface" />
</request>
<enum name="primaries">
<description summary="known primaries" />
<entry name="undefined" value="0"
summary="specifies undefined, implementation-specific handling" />
<entry name="rec709" value="1" summary="specifies Rec.709/sRGB primaries with D65 white point" />
<entry name="rec2020" value="2"
summary="specifies Rec.2020/HDR10 primaries with D65 white point" />
</enum>
<request name="set_known_container_color_volume">
<description summary="sets the container color volume (primaries) for a surface" />
<arg name="primaries" type="uint" enum="primaries" summary="primaries for the surface" />
</request>
<enum name="render_intent">
<description summary="known render intents">
Extended information on render intents described
here can be found in ICC.1:2022:
https://www.color.org/specification/ICC.1-2022-05.pdf
</description>
<entry name="perceptual" value="0" summary="perceptual" />
</enum>
<request name="set_render_intent">
<description summary="sets the render intent for a surface">
NOTE: On a surface with "perceptual" (default) render intent, handling of the container's
color volume
is implementation-specific, and may differ between different transfer functions it is paired
with:
ie. sRGB + 709 rendering may have it's primaries widened to more of the available display's
gamut
to be be more pleasing for the viewer.
Compared to scRGB Linear + 709 being treated faithfully as 709
(including utilizing negatives out of the 709 gamut triangle)
</description>
<arg name="render_intent" type="uint" enum="render_intent"
summary="render intent for the surface" />
</request>
<request name="set_hdr_metadata">
<description summary="set HDR metadata for a surface">
Forwards HDR metadata from the client to the compositor.
HDR Metadata Infoframe as per CTA 861.G spec.
Usage of this HDR metadata is implementation specific and
outside of the scope of this protocol.
</description>
<arg name="mastering_display_primary_red_x" type="uint">
<description summary="red primary x coordinate">
Mastering Red Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_red_y" type="uint">
<description summary="red primary y coordinate">
Mastering Red Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_green_x" type="uint">
<description summary="green primary x coordinate">
Mastering Green Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_green_y" type="uint">
<description summary="green primary y coordinate">
Mastering Green Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_blue_x" type="uint">
<description summary="blue primary x coordinate">
Mastering Blue Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_display_primary_blue_y" type="uint">
<description summary="blue primary y coordinate">
Mastering Blue Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_white_point_x" type="uint">
<description summary="white point x coordinate">
Mastering White Point X Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="mastering_white_point_y" type="uint">
<description summary="white point y coordinate">
Mastering White Point Y Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="max_display_mastering_luminance" type="uint">
<description summary="max display mastering luminance">
Max Mastering Display Luminance.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="min_display_mastering_luminance" type="uint">
<description summary="min display mastering luminance">
Min Mastering Display Luminance.
This value is coded as an unsigned 16-bit value in units of
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
represents 6.5535 cd/m2.
</description>
</arg>
<arg name="max_cll" type="uint">
<description summary="max content light level">
Max Content Light Level.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="max_fall" type="uint">
<description summary="max frame average light level">
Max Frame Average Light Level.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
</request>
<event name="preferred_metadata">
<description summary="preferred metadata for a surface">
Current preferred metadata for a surface.
The application should use this information to tone-map its buffers
to this target before committing.
This metadata does not necessarily correspond to any physical output, but
rather what the compositor thinks would be best for a given surface.
</description>
<arg name="transfer_function" type="uint" enum="transfer_function">
<description summary="output's current transfer function">
Specifies a known transfer function that corresponds to the
output the surface is targeting.
</description>
</arg>
<arg name="output_display_primary_red_x" type="uint">
<description summary="red primary x coordinate">
Output Red Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_red_y" type="uint">
<description summary="red primary y coordinate">
Output Red Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_green_x" type="uint">
<description summary="green primary x coordinate">
Output Green Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_green_y" type="uint">
<description summary="green primary y coordinate">
Output Green Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_blue_x" type="uint">
<description summary="blue primary x coordinate">
Output Blue Color Primary X Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_display_primary_blue_y" type="uint">
<description summary="blue primary y coordinate">
Output Blue Color Primary Y Coordinate of the Data.
Coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_white_point_x" type="uint">
<description summary="white point x coordinate">
Output White Point X Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="output_white_point_y" type="uint">
<description summary="white point y coordinate">
Output White Point Y Coordinate of the Data.
These are coded as unsigned 16-bit values in units of
0.00002, where 0x0000 represents zero and 0xC350
represents 1.0000.
</description>
</arg>
<arg name="max_luminance" type="uint">
<description summary="maximum luminance">
Max Output Luminance
The max luminance in nits that the output is capable of rendering in small areas.
Content should: not exceed this value to avoid clipping.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
<arg name="min_luminance" type="uint">
<description summary="minimum luminance">
Min Output Luminance
The min luminance that the output is capable of rendering.
Content should: not exceed this value to avoid clipping.
This value is coded as an unsigned 16-bit value in units of
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
represents 6.5535 cd/m2.
</description>
</arg>
<arg name="max_full_frame_luminance" type="uint">
<description summary="maximum full frame luminance">
Max Full Frame Luminance
The max luminance in nits that the output is capable of rendering for the
full frame sustained.
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
</description>
</arg>
</event>
</interface>
</protocol>

File diff suppressed because it is too large Load diff

View file

@ -2956,35 +2956,31 @@ void CCompositor::onNewMonitor(SP<Aquamarine::IOutput> output) {
}
}
SImageDescription CCompositor::getPreferredImageDescription() {
PImageDescription CCompositor::getPreferredImageDescription() {
if (!PROTO::colorManagement) {
Log::logger->log(Log::ERR, "FIXME: color management protocol is not enabled, returning empty image description");
return SImageDescription{};
return DEFAULT_IMAGE_DESCRIPTION;
}
Log::logger->log(Log::WARN, "FIXME: color management protocol is enabled, determine correct preferred image description");
// should determine some common settings to avoid unnecessary transformations while keeping maximum displayable precision
return m_monitors.size() == 1 ? m_monitors[0]->m_imageDescription : SImageDescription{.primaries = NColorPrimaries::BT709};
return m_monitors.size() == 1 ? m_monitors[0]->m_imageDescription : CImageDescription::from(SImageDescription{.primaries = NColorPrimaries::BT709});
}
SImageDescription CCompositor::getHDRImageDescription() {
PImageDescription CCompositor::getHDRImageDescription() {
if (!PROTO::colorManagement) {
Log::logger->log(Log::ERR, "FIXME: color management protocol is not enabled, returning empty image description");
return SImageDescription{};
return DEFAULT_IMAGE_DESCRIPTION;
}
return m_monitors.size() == 1 && m_monitors[0]->m_output && m_monitors[0]->m_output->parsedEDID.hdrMetadata.has_value() ?
SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = m_monitors[0]->m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance,
.max = m_monitors[0]->m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance,
.reference = m_monitors[0]->m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance}} :
SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = 0, .max = 10000, .reference = 203}};
CImageDescription::from(SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = m_monitors[0]->m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance,
.max = m_monitors[0]->m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance,
.reference = m_monitors[0]->m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance}}) :
DEFAULT_HDR_IMAGE_DESCRIPTION;
}
bool CCompositor::shouldChangePreferredImageDescription() {

View file

@ -162,8 +162,8 @@ class CCompositor {
void ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace = nullptr);
std::optional<unsigned int> getVTNr();
NColorManagement::SImageDescription getPreferredImageDescription();
NColorManagement::SImageDescription getHDRImageDescription();
NColorManagement::PImageDescription getPreferredImageDescription();
NColorManagement::PImageDescription getHDRImageDescription();
bool shouldChangePreferredImageDescription();
bool supportsDrmSyncobjTimeline() const;

View file

@ -1562,10 +1562,10 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
},
SConfigOptionDescription{
.value = "render:cm_sdr_eotf",
.description = "Default transfer function for displaying SDR apps. 0 - Treat unspecified as sRGB, 1 - Treat unspecified as Gamma 2.2, 2 - Treat "
"unspecified and sRGB as Gamma 2.2",
.description = "Default transfer function for displaying SDR apps. 0 - Use default value (Gamma 2.2), 1 - Treat unspecified as Gamma 2.2, 2 - Treat "
"unspecified and sRGB as Gamma 2.2, 3 - Treat unspecified as sRGB",
.type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "srgb,gamma22,gamma22force"},
.data = SConfigOptionDescription::SChoiceData{0, "default,gamma22,gamma22force,srgb"},
},
/*
@ -2001,17 +2001,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* Experimental
*/
SConfigOptionDescription{
.value = "experimental:xx_color_management_v4",
.description = "enable color management protocol",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* Quirks
*/

View file

@ -771,8 +771,6 @@ CConfigManager::CConfigManager() {
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});
registerConfigVar("ecosystem:enforce_permissions", Hyprlang::INT{0});
registerConfigVar("experimental:xx_color_management_v4", Hyprlang::INT{0});
registerConfigVar("quirks:prefer_hdr", Hyprlang::INT{0});
// devices

View file

@ -112,6 +112,5 @@ namespace Desktop::View {
} m_listeners;
friend class ::CPointerConstraint;
friend class CXxColorManagerV4;
};
}

View file

@ -476,81 +476,76 @@ void CMonitor::onDisconnect(bool destroy) {
void CMonitor::applyCMType(NCMType::eCMType cmType, int cmSdrEotf) {
auto oldImageDescription = m_imageDescription;
static auto PSDREOTF = CConfigValue<Hyprlang::INT>("render:cm_sdr_eotf");
auto chosenSdrEotf = cmSdrEotf == 0 ? (*PSDREOTF > 0 ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB) :
auto chosenSdrEotf = cmSdrEotf == 0 ? (*PSDREOTF != 3 ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB) :
(cmSdrEotf == 1 ? NColorManagement::CM_TRANSFER_FUNCTION_SRGB : NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22);
switch (cmType) {
case NCMType::CM_SRGB: m_imageDescription = {.transferFunction = chosenSdrEotf}; break; // assumes SImageDescription defaults to sRGB
case NCMType::CM_SRGB: m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf}); break; // assumes SImageDescription defaults to sRGB
case NCMType::CM_WIDE:
m_imageDescription = {.transferFunction = chosenSdrEotf,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020)};
m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020)});
break;
case NCMType::CM_DCIP3:
m_imageDescription = {.transferFunction = chosenSdrEotf,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_DCI_P3,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DCI_P3)};
m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_DCI_P3,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DCI_P3)});
break;
case NCMType::CM_DP3:
m_imageDescription = {.transferFunction = chosenSdrEotf,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_DISPLAY_P3,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DISPLAY_P3)};
m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_DISPLAY_P3,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DISPLAY_P3)});
break;
case NCMType::CM_ADOBE:
m_imageDescription = {.transferFunction = chosenSdrEotf,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_ADOBE_RGB,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_ADOBE_RGB)};
m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_ADOBE_RGB,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_ADOBE_RGB)});
break;
case NCMType::CM_EDID:
m_imageDescription = {.transferFunction = chosenSdrEotf,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = {
.red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y},
.green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y},
.blue = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y},
.white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y},
}};
break;
case NCMType::CM_HDR:
m_imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = 0, .max = 10000, .reference = 203}};
m_imageDescription =
CImageDescription::from({.transferFunction = chosenSdrEotf,
.primariesNameSet = false,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = {
.red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y},
.green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y},
.blue = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y},
.white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y},
}});
break;
case NCMType::CM_HDR: m_imageDescription = DEFAULT_HDR_IMAGE_DESCRIPTION; break;
case NCMType::CM_HDR_EDID:
m_imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = false,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = m_output->parsedEDID.chromaticityCoords.has_value() ?
NColorManagement::SPCPRimaries{
.red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y},
.green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y},
.blue = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y},
.white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y},
} :
NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance,
.max = m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance,
.reference = m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance}};
m_imageDescription =
CImageDescription::from({.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = false,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = m_output->parsedEDID.chromaticityCoords.has_value() ?
NColorManagement::SPCPRimaries{
.red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y},
.green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y},
.blue = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y},
.white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y},
} :
NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance,
.max = m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance,
.reference = m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance}});
break;
default: UNREACHABLE();
}
if (m_minLuminance >= 0)
m_imageDescription.luminances.min = m_minLuminance;
if (m_maxLuminance >= 0)
m_imageDescription.luminances.max = m_maxLuminance;
if (m_maxAvgLuminance >= 0)
m_imageDescription.luminances.reference = m_maxAvgLuminance;
if (m_minLuminance >= 0 || m_maxLuminance >= 0 || m_maxAvgLuminance >= 0)
m_imageDescription = m_imageDescription->with({
.min = m_minLuminance >= 0 ? m_minLuminance : m_imageDescription->value().luminances.min, //
.max = m_maxLuminance >= 0 ? m_maxLuminance : m_imageDescription->value().luminances.max, //
.reference = m_maxAvgLuminance >= 0 ? m_maxAvgLuminance : m_imageDescription->value().luminances.reference //
});
if (oldImageDescription != m_imageDescription) {
m_imageDescription.updateId();
if (PROTO::colorManagement)
PROTO::colorManagement->onMonitorImageDescriptionChanged(m_self);
}
@ -1999,7 +1994,7 @@ int CMonitor::maxAvgLuminance(int defaultValue) {
}
bool CMonitor::wantsWideColor() {
return supportsWideColor() && (wantsHDR() || m_imageDescription.primariesNamed == CM_PRIMARIES_BT2020);
return supportsWideColor() && (wantsHDR() || m_imageDescription->value().primariesNamed == CM_PRIMARIES_BT2020);
}
bool CMonitor::wantsHDR() {
@ -2014,7 +2009,7 @@ bool CMonitor::inFullscreenMode() {
return m_activeWorkspace && m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN;
}
std::optional<NColorManagement::SImageDescription> CMonitor::getFSImageDescription() {
std::optional<NColorManagement::PImageDescription> CMonitor::getFSImageDescription() {
if (!inFullscreenMode())
return {};
@ -2024,7 +2019,7 @@ std::optional<NColorManagement::SImageDescription> CMonitor::getFSImageDescripti
const auto ROOT_SURF = FS_WINDOW->wlSurface()->resource();
const auto SURF = ROOT_SURF->findWithCM();
return SURF ? SURF->m_colorManagement->imageDescription() : SImageDescription{};
return SURF ? NColorManagement::CImageDescription::from(SURF->m_colorManagement->imageDescription()) : DEFAULT_IMAGE_DESCRIPTION;
}
bool CMonitor::needsCM() {
@ -2045,19 +2040,20 @@ bool CMonitor::canNoShaderCM() {
if (SRC_DESC.value() == m_imageDescription)
return true; // no CM needed
if (SRC_DESC->icc.fd >= 0 || m_imageDescription.icc.fd >= 0)
const auto SRC_DESC_VALUE = SRC_DESC.value()->value();
if (SRC_DESC_VALUE.icc.fd >= 0 || m_imageDescription->value().icc.fd >= 0)
return false; // no ICC support
static auto PSDREOTF = CConfigValue<Hyprlang::INT>("render:cm_sdr_eotf");
// only primaries differ
if ((SRC_DESC->transferFunction == m_imageDescription.transferFunction ||
(*PSDREOTF == 2 && SRC_DESC->transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB &&
m_imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22)) &&
SRC_DESC->transferFunctionPower == m_imageDescription.transferFunctionPower && (!inHDR() || SRC_DESC->luminances == m_imageDescription.luminances) &&
SRC_DESC->masteringLuminances == m_imageDescription.masteringLuminances && SRC_DESC->maxCLL == m_imageDescription.maxCLL && SRC_DESC->maxFALL == m_imageDescription.maxFALL)
return true;
return false;
return ((SRC_DESC_VALUE.transferFunction == m_imageDescription->value().transferFunction ||
(*PSDREOTF == 2 && 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) &&
SRC_DESC_VALUE.masteringLuminances == m_imageDescription->value().masteringLuminances && SRC_DESC_VALUE.maxCLL == m_imageDescription->value().maxCLL &&
SRC_DESC_VALUE.maxFALL == m_imageDescription->value().maxFALL);
}
bool CMonitor::doesNoShaderCM() {

View file

@ -329,7 +329,7 @@ class CMonitor {
/// Has an active workspace with a real fullscreen window
bool inFullscreenMode();
std::optional<NColorManagement::SImageDescription> getFSImageDescription();
std::optional<NColorManagement::PImageDescription> getFSImageDescription();
bool needsCM();
/// Can do CM without shader
@ -342,7 +342,7 @@ class CMonitor {
PHLWINDOWREF m_previousFSWindow;
bool m_needsHDRupdate = false;
NColorManagement::SImageDescription m_imageDescription;
NColorManagement::PImageDescription m_imageDescription;
bool m_noShaderCTM = false; // sets drm CTM, restore needed
// For the list lookup

View file

@ -57,8 +57,6 @@
#include "../protocols/core/Output.hpp"
#include "../protocols/core/Shm.hpp"
#include "../protocols/ColorManagement.hpp"
#include "../protocols/XXColorManagement.hpp"
#include "../protocols/FrogColorManagement.hpp"
#include "../protocols/ContentType.hpp"
#include "../protocols/XDGTag.hpp"
#include "../protocols/XDGBell.hpp"
@ -106,9 +104,8 @@ void CProtocolManager::onMonitorModeChange(PHLMONITOR pMonitor) {
CProtocolManager::CProtocolManager() {
static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
static const auto PENABLEXXCM = CConfigValue<Hyprlang::INT>("experimental:xx_color_management_v4");
static const auto PDEBUGCM = CConfigValue<Hyprlang::INT>("debug:full_cm_proto");
static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
static const auto PDEBUGCM = CConfigValue<Hyprlang::INT>("debug:full_cm_proto");
// Outputs are a bit dumb, we have to agree.
static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
@ -202,11 +199,6 @@ CProtocolManager::CProtocolManager() {
if (*PENABLECM)
PROTO::colorManagement = makeUnique<CColorManagementProtocol>(&wp_color_manager_v1_interface, 1, "ColorManagement", *PDEBUGCM);
if (*PENABLEXXCM && *PENABLECM) {
PROTO::xxColorManagement = makeUnique<CXXColorManagementProtocol>(&xx_color_manager_v4_interface, 1, "XXColorManagement");
PROTO::frogColorManagement = makeUnique<CFrogColorManagementProtocol>(&frog_color_management_factory_v1_interface, 1, "FrogColorManagement");
}
// ! please read the top of this file before adding another protocol
for (auto const& b : g_pCompositor->m_aqBackend->getImplementations()) {
@ -295,8 +287,6 @@ CProtocolManager::~CProtocolManager() {
PROTO::hyprlandSurface.reset();
PROTO::contentType.reset();
PROTO::colorManagement.reset();
PROTO::xxColorManagement.reset();
PROTO::frogColorManagement.reset();
PROTO::xdgTag.reset();
PROTO::xdgBell.reset();
PROTO::extWorkspace.reset();

View file

@ -18,12 +18,12 @@ CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resour
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_ICC_V2_V4);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_TF_POWER);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME);
}
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_SRGB);
@ -35,10 +35,7 @@ CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resour
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3);
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB);
if (PROTO::colorManagement->m_debug) {
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_CIE1931_XYZ);
}
m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_CIE1931_XYZ);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB);
m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22);
@ -171,14 +168,10 @@ CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resour
return;
}
RESOURCE->m_self = RESOURCE;
RESOURCE->m_settings.windowsScRGB = true;
RESOURCE->m_settings.primariesNamed = NColorManagement::CM_PRIMARIES_SRGB;
RESOURCE->m_settings.primariesNameSet = true;
RESOURCE->m_settings.primaries = NColorPrimaries::BT709;
RESOURCE->m_settings.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR;
RESOURCE->m_settings.luminances.reference = 203;
RESOURCE->resource()->sendReady(RESOURCE->m_settings.updateId());
RESOURCE->m_self = RESOURCE;
RESOURCE->m_settings = SCRGB_IMAGE_DESCRIPTION;
RESOURCE->resource()->sendReady(RESOURCE->m_settings->id());
});
m_resource->setOnDestroy([this](CWpColorManagerV1* r) { PROTO::colorManagement->destroyResource(this); });
@ -223,7 +216,7 @@ CColorManagementOutput::CColorManagementOutput(SP<CWpColorManagementOutputV1> re
RESOURCE->m_resource->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_NO_OUTPUT, "No output");
else {
RESOURCE->m_settings = m_output->m_monitor->m_imageDescription;
RESOURCE->m_resource->sendReady(RESOURCE->m_settings.updateId());
RESOURCE->m_resource->sendReady(RESOURCE->m_settings->id());
}
});
}
@ -236,10 +229,6 @@ wl_client* CColorManagementOutput::client() {
return m_client;
}
CColorManagementSurface::CColorManagementSurface(SP<CWLSurfaceResource> surface_) : m_surface(surface_) {
// only for frog cm until wayland cm is adopted
}
CColorManagementSurface::CColorManagementSurface(SP<CWpColorManagementSurfaceV1> resource, SP<CWLSurfaceResource> surface_) : m_surface(surface_), m_resource(resource) {
if UNLIKELY (!good())
return;
@ -280,7 +269,7 @@ CColorManagementSurface::CColorManagementSurface(SP<CWpColorManagementSurfaceV1>
});
m_resource->setUnsetImageDescription([this](CWpColorManagementSurfaceV1* r) {
LOGM(Log::TRACE, "Unset image description for surface={}", (uintptr_t)r);
m_imageDescription = SImageDescription{};
m_imageDescription = DEFAULT_IMAGE_DESCRIPTION;
setHasImageDescription(false);
});
}
@ -296,7 +285,8 @@ wl_client* CColorManagementSurface::client() {
const SImageDescription& CColorManagementSurface::imageDescription() {
if (!hasImageDescription())
LOGM(Log::WARN, "Reading imageDescription while none set. Returns default or empty values");
return m_imageDescription;
return m_imageDescription->value();
}
bool CColorManagementSurface::hasImageDescription() {
@ -327,13 +317,14 @@ bool CColorManagementSurface::needsHdrMetadataUpdate() {
}
bool CColorManagementSurface::isHDR() {
return m_imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ || m_imageDescription.transferFunction == CM_TRANSFER_FUNCTION_HLG || isWindowsScRGB();
return m_imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ || m_imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_HLG ||
isWindowsScRGB();
}
bool CColorManagementSurface::isWindowsScRGB() {
return m_imageDescription.windowsScRGB ||
return m_imageDescription->value().windowsScRGB ||
// autodetect scRGB, might be incorrect
(m_imageDescription.primariesNamed == CM_PRIMARIES_SRGB && m_imageDescription.transferFunction == CM_TRANSFER_FUNCTION_EXT_LINEAR);
(m_imageDescription->value().primariesNamed == CM_PRIMARIES_SRGB && m_imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_EXT_LINEAR);
}
CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorManagementSurfaceFeedbackV1> resource, SP<CWLSurfaceResource> surface_) :
@ -372,7 +363,7 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorMana
RESOURCE->m_self = RESOURCE;
RESOURCE->m_settings = m_surface->getPreferredImageDescription();
RESOURCE->resource()->sendReady(RESOURCE->m_settings.updateId());
RESOURCE->resource()->sendReady(RESOURCE->m_settings->id());
});
m_resource->setGetPreferredParametric([this](CWpColorManagementSurfaceFeedbackV1* r, uint32_t id) {
@ -394,9 +385,9 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorMana
RESOURCE->m_self = RESOURCE;
RESOURCE->m_settings = m_surface->getPreferredImageDescription();
m_currentPreferredId = RESOURCE->m_settings.updateId();
m_currentPreferredId = RESOURCE->m_settings->id();
if (!PROTO::colorManagement->m_debug && RESOURCE->m_settings.icc.fd >= 0) {
if (!PROTO::colorManagement->m_debug && RESOURCE->m_settings->value().icc.fd >= 0) {
LOGM(Log::ERR, "FIXME: parse icc profile");
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported");
return;
@ -411,7 +402,7 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorMana
void CColorManagementFeedbackSurface::onPreferredChanged() {
if (m_surface->m_enteredOutputs.size() == 1) {
const auto newId = m_surface->getPreferredImageDescription().updateId();
const auto newId = m_surface->getPreferredImageDescription()->id();
if (m_currentPreferredId != newId)
m_resource->sendPreferredChanged(newId);
}
@ -460,8 +451,8 @@ CColorManagementIccCreator::CColorManagementIccCreator(SP<CWpImageDescriptionCre
}
RESOURCE->m_self = RESOURCE;
RESOURCE->m_settings = m_settings;
RESOURCE->resource()->sendReady(m_settings.updateId());
RESOURCE->m_settings = CImageDescription::from(m_settings);
RESOURCE->resource()->sendReady(RESOURCE->m_settings->id());
PROTO::colorManagement->destroyResource(this);
});
@ -514,8 +505,8 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage
}
RESOURCE->m_self = RESOURCE;
RESOURCE->m_settings = m_settings;
RESOURCE->resource()->sendReady(m_settings.updateId());
RESOURCE->m_settings = CImageDescription::from(m_settings);
RESOURCE->resource()->sendReady(RESOURCE->m_settings->id());
PROTO::colorManagement->destroyResource(this);
});
@ -577,6 +568,7 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage
case WP_COLOR_MANAGER_V1_PRIMARIES_PAL:
case WP_COLOR_MANAGER_V1_PRIMARIES_NTSC:
case WP_COLOR_MANAGER_V1_PRIMARIES_GENERIC_FILM:
case WP_COLOR_MANAGER_V1_PRIMARIES_CIE1931_XYZ:
case WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3:
case WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3:
case WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB: break;
@ -627,10 +619,7 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Mastering primaries already set");
return;
}
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Mastering primaries are not supported");
return;
}
m_settings.masteringPrimaries = SPCPRimaries{.red = {.x = r_x / PRIMARIES_SCALE, .y = r_y / PRIMARIES_SCALE},
.green = {.x = g_x / PRIMARIES_SCALE, .y = g_y / PRIMARIES_SCALE},
.blue = {.x = b_x / PRIMARIES_SCALE, .y = b_y / PRIMARIES_SCALE},
@ -653,10 +642,7 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage
r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, "Invalid luminances");
return;
}
if (!PROTO::colorManagement->m_debug) {
r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Mastering luminances are not supported");
return;
}
m_settings.masteringLuminances = SImageDescription::SPCMasteringLuminances{.min = min, .max = max_lum};
m_valuesSet |= PC_MASTERING_LUMINANCES;
});
@ -705,7 +691,7 @@ CColorManagementImageDescription::CColorManagementImageDescription(SP<CWpImageDe
return;
}
auto RESOURCE = makeShared<CColorManagementImageDescriptionInfo>(makeShared<CWpImageDescriptionInfoV1>(r->client(), r->version(), id), m_settings);
auto RESOURCE = makeShared<CColorManagementImageDescriptionInfo>(makeShared<CWpImageDescriptionInfoV1>(r->client(), r->version(), id), m_settings->value());
if UNLIKELY (!RESOURCE->good())
r->noMemory();

View file

@ -46,7 +46,6 @@ class CColorManagementOutput {
class CColorManagementSurface {
public:
CColorManagementSurface(SP<CWLSurfaceResource> surface_); // temporary interface for frog CM
CColorManagementSurface(SP<CWpColorManagementSurfaceV1> resource, SP<CWLSurfaceResource> surface_);
bool good();
@ -67,14 +66,11 @@ class CColorManagementSurface {
private:
SP<CWpColorManagementSurfaceV1> m_resource;
wl_client* m_client = nullptr;
NColorManagement::SImageDescription m_imageDescription;
NColorManagement::SImageDescription m_lastImageDescription;
NColorManagement::PImageDescription m_imageDescription;
NColorManagement::PImageDescription m_lastImageDescription;
bool m_hasImageDescription = false;
bool m_needsNewMetadata = false;
hdr_output_metadata m_hdrMetadata;
friend class CXXColorManagementSurface;
friend class CFrogColorManagementSurface;
};
class CColorManagementFeedbackSurface {
@ -157,7 +153,7 @@ class CColorManagementImageDescription {
WP<CColorManagementImageDescription> m_self;
NColorManagement::SImageDescription m_settings;
NColorManagement::PImageDescription m_settings;
private:
SP<CWpImageDescriptionV1> m_resource;
@ -216,9 +212,6 @@ class CColorManagementProtocol : public IWaylandProtocol {
friend class CColorManagementIccCreator;
friend class CColorManagementParametricCreator;
friend class CColorManagementImageDescription;
friend class CXXColorManagementSurface;
friend class CFrogColorManagementSurface;
};
namespace PROTO {

View file

@ -1,181 +0,0 @@
#include "FrogColorManagement.hpp"
#include "color-management-v1.hpp"
#include "macros.hpp"
#include "protocols/ColorManagement.hpp"
#include "protocols/core/Subcompositor.hpp"
#include "protocols/types/ColorManagement.hpp"
using namespace NColorManagement;
static wpColorManagerV1TransferFunction getWPTransferFunction(frogColorManagedSurfaceTransferFunction tf) {
switch (tf) {
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
default: UNREACHABLE();
}
}
static wpColorManagerV1Primaries getWPPrimaries(frogColorManagedSurfacePrimaries primaries) {
return sc<wpColorManagerV1Primaries>(primaries + 1);
}
CFrogColorManager::CFrogColorManager(SP<CFrogColorManagementFactoryV1> resource_) : m_resource(resource_) {
if UNLIKELY (!good())
return;
m_resource->setDestroy([](CFrogColorManagementFactoryV1* r) { LOGM(Log::TRACE, "Destroy frog_color_management at {:x} (generated default)", (uintptr_t)r); });
m_resource->setOnDestroy([this](CFrogColorManagementFactoryV1* r) { PROTO::frogColorManagement->destroyResource(this); });
m_resource->setGetColorManagedSurface([](CFrogColorManagementFactoryV1* r, wl_resource* surface, uint32_t id) {
LOGM(Log::TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface);
auto SURF = CWLSurfaceResource::fromResource(surface);
if (!SURF) {
LOGM(Log::ERR, "No surface for resource {}", (uintptr_t)surface);
r->error(-1, "Invalid surface (2)");
return;
}
const auto RESOURCE =
PROTO::frogColorManagement->m_surfaces.emplace_back(makeShared<CFrogColorManagementSurface>(makeShared<CFrogColorManagedSurface>(r->client(), r->version(), id), SURF));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::frogColorManagement->m_surfaces.pop_back();
return;
}
RESOURCE->m_self = RESOURCE;
});
}
bool CFrogColorManager::good() {
return m_resource->resource();
}
CFrogColorManagementSurface::CFrogColorManagementSurface(SP<CFrogColorManagedSurface> resource_, SP<CWLSurfaceResource> surface_) : m_surface(surface_), m_resource(resource_) {
if UNLIKELY (!good())
return;
m_client = m_resource->client();
if (!m_surface->m_colorManagement.valid()) {
const auto RESOURCE = PROTO::colorManagement->m_surfaces.emplace_back(makeShared<CColorManagementSurface>(surface_));
if UNLIKELY (!RESOURCE) {
m_resource->noMemory();
PROTO::colorManagement->m_surfaces.pop_back();
return;
}
RESOURCE->m_self = RESOURCE;
m_surface->m_colorManagement = RESOURCE;
m_resource->setOnDestroy([this](CFrogColorManagedSurface* r) {
LOGM(Log::TRACE, "Destroy frog cm and xx cm for surface {}", (uintptr_t)m_surface);
if (m_surface.valid())
PROTO::colorManagement->destroyResource(m_surface->m_colorManagement.get());
PROTO::frogColorManagement->destroyResource(this);
});
} else
m_resource->setOnDestroy([this](CFrogColorManagedSurface* r) {
LOGM(Log::TRACE, "Destroy frog cm surface {}", (uintptr_t)m_surface);
PROTO::frogColorManagement->destroyResource(this);
});
m_resource->setDestroy([this](CFrogColorManagedSurface* r) {
LOGM(Log::TRACE, "Destroy frog cm surface {}", (uintptr_t)m_surface);
PROTO::frogColorManagement->destroyResource(this);
});
m_resource->setSetKnownTransferFunction([this](CFrogColorManagedSurface* r, frogColorManagedSurfaceTransferFunction tf) {
LOGM(Log::TRACE, "Set frog cm transfer function {} for {}", (uint32_t)tf, m_surface->id());
switch (tf) {
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ:
m_surface->m_colorManagement->m_imageDescription.transferFunction =
convertTransferFunction(getWPTransferFunction(FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ));
break;
;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22:
if (m_pqIntentSent) {
LOGM(Log::TRACE,
"FIXME: assuming broken enum value 2 (FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22) referring to eotf value 2 (TRANSFER_FUNCTION_ST2084_PQ)");
m_surface->m_colorManagement->m_imageDescription.transferFunction =
convertTransferFunction(getWPTransferFunction(FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ));
break;
};
[[fallthrough]];
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED:
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: LOGM(Log::TRACE, "FIXME: add tf support for {}", (uint32_t)tf); [[fallthrough]];
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB:
m_surface->m_colorManagement->m_imageDescription.transferFunction = convertTransferFunction(getWPTransferFunction(tf));
m_surface->m_colorManagement->setHasImageDescription(true);
}
});
m_resource->setSetKnownContainerColorVolume([this](CFrogColorManagedSurface* r, frogColorManagedSurfacePrimaries primariesName) {
LOGM(Log::TRACE, "Set frog cm primaries {}", (uint32_t)primariesName);
switch (primariesName) {
case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_UNDEFINED:
case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_REC709: m_surface->m_colorManagement->m_imageDescription.primaries = NColorPrimaries::BT709; break;
case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_REC2020: m_surface->m_colorManagement->m_imageDescription.primaries = NColorPrimaries::BT2020; break;
}
m_surface->m_colorManagement->m_imageDescription.primariesNamed = convertPrimaries(getWPPrimaries(primariesName));
m_surface->m_colorManagement->setHasImageDescription(true);
});
m_resource->setSetRenderIntent([this](CFrogColorManagedSurface* r, frogColorManagedSurfaceRenderIntent intent) {
LOGM(Log::TRACE, "Set frog cm intent {}", (uint32_t)intent);
m_pqIntentSent = intent == FROG_COLOR_MANAGED_SURFACE_RENDER_INTENT_PERCEPTUAL;
m_surface->m_colorManagement->setHasImageDescription(true);
});
m_resource->setSetHdrMetadata([this](CFrogColorManagedSurface* r, uint32_t r_x, uint32_t r_y, uint32_t g_x, uint32_t g_y, uint32_t b_x, uint32_t b_y, uint32_t w_x,
uint32_t w_y, uint32_t max_lum, uint32_t min_lum, uint32_t cll, uint32_t fall) {
LOGM(Log::TRACE, "Set frog primaries r:{},{} g:{},{} b:{},{} w:{},{} luminances {} - {} cll {} fall {}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y, min_lum, max_lum, cll,
fall);
m_surface->m_colorManagement->m_imageDescription.masteringPrimaries = SPCPRimaries{.red = {.x = r_x / 50000.0f, .y = r_y / 50000.0f},
.green = {.x = g_x / 50000.0f, .y = g_y / 50000.0f},
.blue = {.x = b_x / 50000.0f, .y = b_y / 50000.0f},
.white = {.x = w_x / 50000.0f, .y = w_y / 50000.0f}};
m_surface->m_colorManagement->m_imageDescription.masteringLuminances.min = min_lum / 10000.0f;
m_surface->m_colorManagement->m_imageDescription.masteringLuminances.max = max_lum;
m_surface->m_colorManagement->m_imageDescription.maxCLL = cll;
m_surface->m_colorManagement->m_imageDescription.maxFALL = fall;
m_surface->m_colorManagement->setHasImageDescription(true);
});
}
bool CFrogColorManagementSurface::good() {
return m_resource->resource();
}
wl_client* CFrogColorManagementSurface::client() {
return m_client;
}
CFrogColorManagementProtocol::CFrogColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CFrogColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_managers.emplace_back(makeShared<CFrogColorManager>(makeShared<CFrogColorManagementFactoryV1>(client, ver, id)));
if UNLIKELY (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_managers.pop_back();
return;
}
LOGM(Log::TRACE, "New frog_color_management at {:x}", (uintptr_t)RESOURCE.get());
}
void CFrogColorManagementProtocol::destroyResource(CFrogColorManager* resource) {
std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; });
}
void CFrogColorManagementProtocol::destroyResource(CFrogColorManagementSurface* resource) {
std::erase_if(m_surfaces, [&](const auto& other) { return other.get() == resource; });
}

View file

@ -1,54 +0,0 @@
#pragma once
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "protocols/core/Compositor.hpp"
#include "frog-color-management-v1.hpp"
class CFrogColorManager {
public:
CFrogColorManager(SP<CFrogColorManagementFactoryV1> resource_);
bool good();
private:
SP<CFrogColorManagementFactoryV1> m_resource;
};
class CFrogColorManagementSurface {
public:
CFrogColorManagementSurface(SP<CFrogColorManagedSurface> resource_, SP<CWLSurfaceResource> surface_);
bool good();
wl_client* client();
WP<CFrogColorManagementSurface> m_self;
WP<CWLSurfaceResource> m_surface;
bool m_pqIntentSent = false;
private:
SP<CFrogColorManagedSurface> m_resource;
wl_client* m_client = nullptr;
};
class CFrogColorManagementProtocol : public IWaylandProtocol {
public:
CFrogColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
private:
void destroyResource(CFrogColorManager* resource);
void destroyResource(CFrogColorManagementSurface* resource);
std::vector<SP<CFrogColorManager>> m_managers;
std::vector<SP<CFrogColorManagementSurface>> m_surfaces;
friend class CFrogColorManager;
friend class CFrogColorManagementSurface;
};
namespace PROTO {
inline UP<CFrogColorManagementProtocol> frogColorManagement;
};

View file

@ -1,666 +0,0 @@
#include "XXColorManagement.hpp"
#include "../Compositor.hpp"
#include "ColorManagement.hpp"
#include "color-management-v1.hpp"
#include "types/ColorManagement.hpp"
#include "xx-color-management-v4.hpp"
using namespace NColorManagement;
static wpColorManagerV1TransferFunction getWPTransferFunction(xxColorManagerV4TransferFunction tf) {
switch (tf) {
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT709:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT1361: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST240: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST240;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_100: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_100;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_316: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_316;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_XVYCC: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_XVYCC;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_EXT_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST428: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG;
default: UNREACHABLE();
}
}
static wpColorManagerV1Primaries getWPPrimaries(xxColorManagerV4Primaries primaries) {
return sc<wpColorManagerV1Primaries>(primaries + 1);
}
CXXColorManager::CXXColorManager(SP<CXxColorManagerV4> resource_) : m_resource(resource_) {
if UNLIKELY (!good())
return;
m_resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_PARAMETRIC);
m_resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_EXTENDED_TARGET_VOLUME);
m_resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES);
m_resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_PRIMARIES);
m_resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_LUMINANCES);
m_resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_SRGB);
m_resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M);
m_resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL);
m_resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_NTSC);
m_resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM);
m_resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_BT2020);
// resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_CIE1931_XYZ);
m_resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3);
m_resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3);
m_resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT709);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT1361);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST240);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_100);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_316);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_XVYCC);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_EXT_SRGB);
m_resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST428);
m_resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_ABSOLUTE);
// resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE_BPC);
m_resource->setDestroy([](CXxColorManagerV4* r) { LOGM(Log::TRACE, "Destroy xx_color_manager at {:x} (generated default)", (uintptr_t)r); });
m_resource->setGetOutput([](CXxColorManagerV4* r, uint32_t id, wl_resource* output) {
LOGM(Log::TRACE, "Get output for id={}, output={}", id, (uintptr_t)output);
const auto RESOURCE =
PROTO::xxColorManagement->m_outputs.emplace_back(makeShared<CXXColorManagementOutput>(makeShared<CXxColorManagementOutputV4>(r->client(), r->version(), id)));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::xxColorManagement->m_outputs.pop_back();
return;
}
RESOURCE->m_self = RESOURCE;
});
m_resource->setGetSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) {
LOGM(Log::TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface);
auto SURF = CWLSurfaceResource::fromResource(surface);
if (!SURF) {
LOGM(Log::ERR, "No surface for resource {}", (uintptr_t)surface);
r->error(-1, "Invalid surface (2)");
return;
}
if (SURF->m_colorManagement) {
r->error(XX_COLOR_MANAGER_V4_ERROR_SURFACE_EXISTS, "CM Surface already exists");
return;
}
const auto RESOURCE =
PROTO::xxColorManagement->m_surfaces.emplace_back(makeShared<CXXColorManagementSurface>(makeShared<CXxColorManagementSurfaceV4>(r->client(), r->version(), id), SURF));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::xxColorManagement->m_surfaces.pop_back();
return;
}
RESOURCE->m_self = RESOURCE;
});
m_resource->setGetFeedbackSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) {
LOGM(Log::TRACE, "Get feedback surface for id={}, surface={}", id, (uintptr_t)surface);
auto SURF = CWLSurfaceResource::fromResource(surface);
if (!SURF) {
LOGM(Log::ERR, "No surface for resource {}", (uintptr_t)surface);
r->error(-1, "Invalid surface (2)");
return;
}
const auto RESOURCE = PROTO::xxColorManagement->m_feedbackSurfaces.emplace_back(
makeShared<CXXColorManagementFeedbackSurface>(makeShared<CXxColorManagementFeedbackSurfaceV4>(r->client(), r->version(), id), SURF));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::xxColorManagement->m_feedbackSurfaces.pop_back();
return;
}
RESOURCE->m_self = RESOURCE;
});
m_resource->setNewIccCreator([](CXxColorManagerV4* r, uint32_t id) {
LOGM(Log::WARN, "New ICC creator for id={} (unsupported)", id);
r->error(XX_COLOR_MANAGER_V4_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported");
});
m_resource->setNewParametricCreator([](CXxColorManagerV4* r, uint32_t id) {
LOGM(Log::TRACE, "New parametric creator for id={}", id);
const auto RESOURCE = PROTO::xxColorManagement->m_parametricCreators.emplace_back(
makeShared<CXXColorManagementParametricCreator>(makeShared<CXxImageDescriptionCreatorParamsV4>(r->client(), r->version(), id)));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::xxColorManagement->m_parametricCreators.pop_back();
return;
}
RESOURCE->m_self = RESOURCE;
});
m_resource->setOnDestroy([this](CXxColorManagerV4* r) { PROTO::xxColorManagement->destroyResource(this); });
}
bool CXXColorManager::good() {
return m_resource->resource();
}
CXXColorManagementOutput::CXXColorManagementOutput(SP<CXxColorManagementOutputV4> resource_) : m_resource(resource_) {
if UNLIKELY (!good())
return;
m_client = m_resource->client();
m_resource->setDestroy([this](CXxColorManagementOutputV4* r) { PROTO::xxColorManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CXxColorManagementOutputV4* r) { PROTO::xxColorManagement->destroyResource(this); });
m_resource->setGetImageDescription([this](CXxColorManagementOutputV4* r, uint32_t id) {
LOGM(Log::TRACE, "Get image description for output={}, id={}", (uintptr_t)r, id);
if (m_imageDescription.valid())
PROTO::xxColorManagement->destroyResource(m_imageDescription.get());
const auto RESOURCE = PROTO::xxColorManagement->m_imageDescriptions.emplace_back(
makeShared<CXXColorManagementImageDescription>(makeShared<CXxImageDescriptionV4>(r->client(), r->version(), id), true));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::xxColorManagement->m_imageDescriptions.pop_back();
return;
}
RESOURCE->m_self = RESOURCE;
});
}
bool CXXColorManagementOutput::good() {
return m_resource->resource();
}
wl_client* CXXColorManagementOutput::client() {
return m_client;
}
CXXColorManagementSurface::CXXColorManagementSurface(SP<CWLSurfaceResource> surface_) : m_surface(surface_) {
// only for frog cm until wayland cm is adopted
}
CXXColorManagementSurface::CXXColorManagementSurface(SP<CXxColorManagementSurfaceV4> resource_, SP<CWLSurfaceResource> surface_) : m_surface(surface_), m_resource(resource_) {
if UNLIKELY (!good())
return;
m_client = m_resource->client();
if (!m_surface->m_colorManagement.valid()) {
const auto RESOURCE = PROTO::colorManagement->m_surfaces.emplace_back(makeShared<CColorManagementSurface>(surface_));
if UNLIKELY (!RESOURCE) {
m_resource->noMemory();
PROTO::colorManagement->m_surfaces.pop_back();
return;
}
RESOURCE->m_self = RESOURCE;
m_surface->m_colorManagement = RESOURCE;
m_resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) {
LOGM(Log::TRACE, "Destroy wp cm and xx cm for surface {}", (uintptr_t)m_surface);
if (m_surface.valid())
PROTO::colorManagement->destroyResource(m_surface->m_colorManagement.get());
PROTO::xxColorManagement->destroyResource(this);
});
} else
m_resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) {
LOGM(Log::TRACE, "Destroy xx cm surface {}", (uintptr_t)m_surface);
PROTO::xxColorManagement->destroyResource(this);
});
m_resource->setDestroy([this](CXxColorManagementSurfaceV4* r) {
LOGM(Log::TRACE, "Destroy xx cm surface {}", (uintptr_t)m_surface);
PROTO::xxColorManagement->destroyResource(this);
});
m_resource->setSetImageDescription([this](CXxColorManagementSurfaceV4* r, wl_resource* image_description, uint32_t render_intent) {
LOGM(Log::TRACE, "Set image description for surface={}, desc={}, intent={}", (uintptr_t)r, (uintptr_t)image_description, render_intent);
const auto PO = sc<CXxImageDescriptionV4*>(wl_resource_get_user_data(image_description));
if (!PO) { // FIXME check validity
r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description creation failed");
return;
}
if (render_intent != XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL) {
r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_RENDER_INTENT, "Unsupported render intent");
return;
}
const auto imageDescription =
std::ranges::find_if(PROTO::xxColorManagement->m_imageDescriptions, [&](const auto& other) { return other->resource()->resource() == image_description; });
if (imageDescription == PROTO::xxColorManagement->m_imageDescriptions.end()) {
r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description not found");
return;
}
if (m_surface.valid()) {
m_surface->m_colorManagement->m_imageDescription = imageDescription->get()->m_settings;
m_surface->m_colorManagement->setHasImageDescription(true);
} else
LOGM(Log::ERR, "Set image description for invalid surface");
});
m_resource->setUnsetImageDescription([this](CXxColorManagementSurfaceV4* r) {
LOGM(Log::TRACE, "Unset image description for surface={}", (uintptr_t)r);
if (m_surface.valid()) {
m_surface->m_colorManagement->m_imageDescription = SImageDescription{};
m_surface->m_colorManagement->setHasImageDescription(false);
} else
LOGM(Log::ERR, "Unset image description for invalid surface");
});
}
bool CXXColorManagementSurface::good() {
return m_resource && m_resource->resource();
}
wl_client* CXXColorManagementSurface::client() {
return m_client;
}
const SImageDescription& CXXColorManagementSurface::imageDescription() {
if (!hasImageDescription())
LOGM(Log::WARN, "Reading imageDescription while none set. Returns default or empty values");
return m_imageDescription;
}
bool CXXColorManagementSurface::hasImageDescription() {
return m_hasImageDescription;
}
void CXXColorManagementSurface::setHasImageDescription(bool has) {
m_hasImageDescription = has;
m_needsNewMetadata = true;
}
const hdr_output_metadata& CXXColorManagementSurface::hdrMetadata() {
return m_hdrMetadata;
}
void CXXColorManagementSurface::setHDRMetadata(const hdr_output_metadata& metadata) {
m_hdrMetadata = metadata;
m_needsNewMetadata = false;
}
bool CXXColorManagementSurface::needsHdrMetadataUpdate() {
return m_needsNewMetadata;
}
CXXColorManagementFeedbackSurface::CXXColorManagementFeedbackSurface(SP<CXxColorManagementFeedbackSurfaceV4> resource_, SP<CWLSurfaceResource> surface_) :
m_surface(surface_), m_resource(resource_) {
if UNLIKELY (!good())
return;
m_client = m_resource->client();
m_resource->setDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) {
LOGM(Log::TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)m_surface);
if (m_currentPreferred.valid())
PROTO::xxColorManagement->destroyResource(m_currentPreferred.get());
PROTO::xxColorManagement->destroyResource(this);
});
m_resource->setOnDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) {
LOGM(Log::TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)m_surface);
if (m_currentPreferred.valid())
PROTO::xxColorManagement->destroyResource(m_currentPreferred.get());
PROTO::xxColorManagement->destroyResource(this);
});
m_resource->setGetPreferred([this](CXxColorManagementFeedbackSurfaceV4* r, uint32_t id) {
LOGM(Log::TRACE, "Get preferred for id {}", id);
if (m_currentPreferred.valid())
PROTO::xxColorManagement->destroyResource(m_currentPreferred.get());
const auto RESOURCE = PROTO::xxColorManagement->m_imageDescriptions.emplace_back(
makeShared<CXXColorManagementImageDescription>(makeShared<CXxImageDescriptionV4>(r->client(), r->version(), id), true));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::xxColorManagement->m_imageDescriptions.pop_back();
return;
}
RESOURCE->m_self = RESOURCE;
m_currentPreferred = RESOURCE;
m_currentPreferred->m_settings = g_pCompositor->getPreferredImageDescription();
RESOURCE->resource()->sendReady(id);
});
}
bool CXXColorManagementFeedbackSurface::good() {
return m_resource->resource();
}
wl_client* CXXColorManagementFeedbackSurface::client() {
return m_client;
}
CXXColorManagementParametricCreator::CXXColorManagementParametricCreator(SP<CXxImageDescriptionCreatorParamsV4> resource_) : m_resource(resource_) {
if UNLIKELY (!good())
return;
//
m_client = m_resource->client();
m_resource->setOnDestroy([this](CXxImageDescriptionCreatorParamsV4* r) { PROTO::xxColorManagement->destroyResource(this); });
m_resource->setCreate([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t id) {
LOGM(Log::TRACE, "Create image description from params for id {}", id);
// FIXME actually check completeness
if (!m_valuesSet) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCOMPLETE_SET, "Missing required settings");
return;
}
// FIXME actually check consistency
if (!m_valuesSet) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCONSISTENT_SET, "Set is not consistent");
return;
}
const auto RESOURCE = PROTO::xxColorManagement->m_imageDescriptions.emplace_back(
makeShared<CXXColorManagementImageDescription>(makeShared<CXxImageDescriptionV4>(r->client(), r->version(), id), false));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::xxColorManagement->m_imageDescriptions.pop_back();
return;
}
// FIXME actually check support
if (!m_valuesSet) {
RESOURCE->resource()->sendFailed(XX_IMAGE_DESCRIPTION_V4_CAUSE_UNSUPPORTED, "unsupported");
return;
}
RESOURCE->m_self = RESOURCE;
RESOURCE->m_settings = m_settings;
RESOURCE->resource()->sendReady(id);
PROTO::xxColorManagement->destroyResource(this);
});
m_resource->setSetTfNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t tf) {
LOGM(Log::TRACE, "Set image description transfer function to {}", tf);
if (m_valuesSet & PC_TF) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function already set");
return;
}
switch (tf) {
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT709:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT1361:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST240:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_100:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_316:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_XVYCC:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_EXT_SRGB:
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST428: break;
default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, "Unsupported transfer function"); return;
}
m_settings.transferFunction = convertTransferFunction(getWPTransferFunction(sc<xxColorManagerV4TransferFunction>(tf)));
m_valuesSet |= PC_TF;
});
m_resource->setSetTfPower([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t eexp) {
LOGM(Log::TRACE, "Set image description tf power to {}", eexp);
if (m_valuesSet & PC_TF_POWER) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function power already set");
return;
}
m_settings.transferFunctionPower = eexp / 10000.0f;
m_valuesSet |= PC_TF_POWER;
});
m_resource->setSetPrimariesNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t primaries) {
LOGM(Log::TRACE, "Set image description primaries by name {}", primaries);
if (m_valuesSet & PC_PRIMARIES) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set");
return;
}
switch (primaries) {
case XX_COLOR_MANAGER_V4_PRIMARIES_SRGB:
case XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M:
case XX_COLOR_MANAGER_V4_PRIMARIES_PAL:
case XX_COLOR_MANAGER_V4_PRIMARIES_NTSC:
case XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM:
case XX_COLOR_MANAGER_V4_PRIMARIES_BT2020:
case XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3:
case XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3:
case XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB:
m_settings.primariesNameSet = true;
m_settings.primariesNamed = convertPrimaries(getWPPrimaries(sc<xxColorManagerV4Primaries>(primaries)));
m_settings.primaries = getPrimaries(m_settings.primariesNamed);
m_valuesSet |= PC_PRIMARIES;
break;
default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_PRIMARIES, "Unsupported primaries");
}
});
m_resource->setSetPrimaries(
[this](CXxImageDescriptionCreatorParamsV4* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) {
LOGM(Log::TRACE, "Set image description primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y);
if (m_valuesSet & PC_PRIMARIES) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set");
return;
}
m_settings.primariesNameSet = false;
m_settings.primaries = SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}};
m_valuesSet |= PC_PRIMARIES;
});
m_resource->setSetLuminances([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) {
auto min = min_lum / 10000.0f;
LOGM(Log::TRACE, "Set image description luminances to {} - {} ({})", min, max_lum, reference_lum);
if (m_valuesSet & PC_LUMINANCES) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Luminances already set");
return;
}
if (max_lum < reference_lum || reference_lum <= min) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances");
return;
}
m_settings.luminances = SImageDescription::SPCLuminances{.min = min, .max = max_lum, .reference = reference_lum};
m_valuesSet |= PC_LUMINANCES;
});
m_resource->setSetMasteringDisplayPrimaries(
[this](CXxImageDescriptionCreatorParamsV4* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) {
LOGM(Log::TRACE, "Set image description mastering primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y);
// if (valuesSet & PC_MASTERING_PRIMARIES) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering primaries already set");
// return;
// }
m_settings.masteringPrimaries = SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}};
m_valuesSet |= PC_MASTERING_PRIMARIES;
});
m_resource->setSetMasteringLuminance([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum) {
auto min = min_lum / 10000.0f;
LOGM(Log::TRACE, "Set image description mastering luminances to {} - {}", min, max_lum);
// if (valuesSet & PC_MASTERING_LUMINANCES) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering luminances already set");
// return;
// }
if (min > 0 && max_lum > 0 && max_lum <= min) {
r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances");
return;
}
m_settings.masteringLuminances = SImageDescription::SPCMasteringLuminances{.min = min, .max = max_lum};
m_valuesSet |= PC_MASTERING_LUMINANCES;
});
m_resource->setSetMaxCll([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_cll) {
LOGM(Log::TRACE, "Set image description max content light level to {}", max_cll);
// if (valuesSet & PC_CLL) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max CLL already set");
// return;
// }
m_settings.maxCLL = max_cll;
m_valuesSet |= PC_CLL;
});
m_resource->setSetMaxFall([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_fall) {
LOGM(Log::TRACE, "Set image description max frame-average light level to {}", max_fall);
// if (valuesSet & PC_FALL) {
// r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max FALL already set");
// return;
// }
m_settings.maxFALL = max_fall;
m_valuesSet |= PC_FALL;
});
}
bool CXXColorManagementParametricCreator::good() {
return m_resource->resource();
}
wl_client* CXXColorManagementParametricCreator::client() {
return m_client;
}
CXXColorManagementImageDescription::CXXColorManagementImageDescription(SP<CXxImageDescriptionV4> resource_, bool allowGetInformation) :
m_resource(resource_), m_allowGetInformation(allowGetInformation) {
if UNLIKELY (!good())
return;
m_client = m_resource->client();
m_resource->setDestroy([this](CXxImageDescriptionV4* r) { PROTO::xxColorManagement->destroyResource(this); });
m_resource->setOnDestroy([this](CXxImageDescriptionV4* r) { PROTO::xxColorManagement->destroyResource(this); });
m_resource->setGetInformation([this](CXxImageDescriptionV4* r, uint32_t id) {
LOGM(Log::TRACE, "Get image information for image={}, id={}", (uintptr_t)r, id);
if (!m_allowGetInformation) {
r->error(XX_IMAGE_DESCRIPTION_V4_ERROR_NO_INFORMATION, "Image descriptions doesn't allow get_information request");
return;
}
auto RESOURCE = makeShared<CXXColorManagementImageDescriptionInfo>(makeShared<CXxImageDescriptionInfoV4>(r->client(), r->version(), id), m_settings);
if UNLIKELY (!RESOURCE->good())
r->noMemory();
// CXXColorManagementImageDescriptionInfo should send everything in the constructor and be ready for destroying at this point
RESOURCE.reset();
});
}
bool CXXColorManagementImageDescription::good() {
return m_resource->resource();
}
wl_client* CXXColorManagementImageDescription::client() {
return m_client;
}
SP<CXxImageDescriptionV4> CXXColorManagementImageDescription::resource() {
return m_resource;
}
CXXColorManagementImageDescriptionInfo::CXXColorManagementImageDescriptionInfo(SP<CXxImageDescriptionInfoV4> resource_, const SImageDescription& settings_) :
m_resource(resource_), m_settings(settings_) {
if UNLIKELY (!good())
return;
m_client = m_resource->client();
const auto toProto = [](float value) { return sc<int32_t>(std::round(value * 10000)); };
if (m_settings.icc.fd >= 0)
m_resource->sendIccFile(m_settings.icc.fd, m_settings.icc.length);
// send preferred client paramateres
m_resource->sendPrimaries(toProto(m_settings.primaries.red.x), toProto(m_settings.primaries.red.y), toProto(m_settings.primaries.green.x),
toProto(m_settings.primaries.green.y), toProto(m_settings.primaries.blue.x), toProto(m_settings.primaries.blue.y),
toProto(m_settings.primaries.white.x), toProto(m_settings.primaries.white.y));
if (m_settings.primariesNameSet)
m_resource->sendPrimariesNamed(m_settings.primariesNamed);
m_resource->sendTfPower(std::round(m_settings.transferFunctionPower * 10000));
m_resource->sendTfNamed(m_settings.transferFunction);
m_resource->sendLuminances(std::round(m_settings.luminances.min * 10000), m_settings.luminances.max, m_settings.luminances.reference);
// send expected display paramateres
m_resource->sendTargetPrimaries(toProto(m_settings.masteringPrimaries.red.x), toProto(m_settings.masteringPrimaries.red.y), toProto(m_settings.masteringPrimaries.green.x),
toProto(m_settings.masteringPrimaries.green.y), toProto(m_settings.masteringPrimaries.blue.x), toProto(m_settings.masteringPrimaries.blue.y),
toProto(m_settings.masteringPrimaries.white.x), toProto(m_settings.masteringPrimaries.white.y));
m_resource->sendTargetLuminance(std::round(m_settings.masteringLuminances.min * 10000), m_settings.masteringLuminances.max);
m_resource->sendTargetMaxCll(m_settings.maxCLL);
m_resource->sendTargetMaxFall(m_settings.maxFALL);
m_resource->sendDone();
}
bool CXXColorManagementImageDescriptionInfo::good() {
return m_resource->resource();
}
wl_client* CXXColorManagementImageDescriptionInfo::client() {
return m_client;
}
CXXColorManagementProtocol::CXXColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CXXColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_managers.emplace_back(makeShared<CXXColorManager>(makeShared<CXxColorManagerV4>(client, ver, id)));
if UNLIKELY (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_managers.pop_back();
return;
}
LOGM(Log::TRACE, "New xx_color_manager at {:x}", (uintptr_t)RESOURCE.get());
}
void CXXColorManagementProtocol::onImagePreferredChanged() {
for (auto const& feedback : m_feedbackSurfaces) {
feedback->m_resource->sendPreferredChanged();
}
}
void CXXColorManagementProtocol::destroyResource(CXXColorManager* resource) {
std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; });
}
void CXXColorManagementProtocol::destroyResource(CXXColorManagementOutput* resource) {
std::erase_if(m_outputs, [&](const auto& other) { return other.get() == resource; });
}
void CXXColorManagementProtocol::destroyResource(CXXColorManagementSurface* resource) {
std::erase_if(m_surfaces, [&](const auto& other) { return other.get() == resource; });
}
void CXXColorManagementProtocol::destroyResource(CXXColorManagementFeedbackSurface* resource) {
std::erase_if(m_feedbackSurfaces, [&](const auto& other) { return other.get() == resource; });
}
void CXXColorManagementProtocol::destroyResource(CXXColorManagementParametricCreator* resource) {
std::erase_if(m_parametricCreators, [&](const auto& other) { return other.get() == resource; });
}
void CXXColorManagementProtocol::destroyResource(CXXColorManagementImageDescription* resource) {
std::erase_if(m_imageDescriptions, [&](const auto& other) { return other.get() == resource; });
}

View file

@ -1,188 +0,0 @@
#pragma once
#include <drm_mode.h>
#include <vector>
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "core/Compositor.hpp"
#include "xx-color-management-v4.hpp"
#include "types/ColorManagement.hpp"
class CXXColorManager;
class CXXColorManagementOutput;
class CXXColorManagementImageDescription;
class CXXColorManagementProtocol;
class CXXColorManager {
public:
CXXColorManager(SP<CXxColorManagerV4> resource_);
bool good();
private:
SP<CXxColorManagerV4> m_resource;
};
class CXXColorManagementOutput {
public:
CXXColorManagementOutput(SP<CXxColorManagementOutputV4> resource_);
bool good();
wl_client* client();
WP<CXXColorManagementOutput> m_self;
WP<CXXColorManagementImageDescription> m_imageDescription;
private:
SP<CXxColorManagementOutputV4> m_resource;
wl_client* m_client = nullptr;
friend class CXXColorManagementProtocol;
friend class CXXColorManagementImageDescription;
};
class CXXColorManagementSurface {
public:
CXXColorManagementSurface(SP<CWLSurfaceResource> surface_); // temporary interface for frog CM
CXXColorManagementSurface(SP<CXxColorManagementSurfaceV4> resource_, SP<CWLSurfaceResource> surface_);
bool good();
wl_client* client();
WP<CXXColorManagementSurface> m_self;
WP<CWLSurfaceResource> m_surface;
const NColorManagement::SImageDescription& imageDescription();
bool hasImageDescription();
void setHasImageDescription(bool has);
const hdr_output_metadata& hdrMetadata();
void setHDRMetadata(const hdr_output_metadata& metadata);
bool needsHdrMetadataUpdate();
private:
SP<CXxColorManagementSurfaceV4> m_resource;
wl_client* m_client = nullptr;
NColorManagement::SImageDescription m_imageDescription;
bool m_hasImageDescription = false;
bool m_needsNewMetadata = false;
hdr_output_metadata m_hdrMetadata;
friend class CFrogColorManagementSurface;
};
class CXXColorManagementFeedbackSurface {
public:
CXXColorManagementFeedbackSurface(SP<CXxColorManagementFeedbackSurfaceV4> resource_, SP<CWLSurfaceResource> surface_);
bool good();
wl_client* client();
WP<CXXColorManagementFeedbackSurface> m_self;
WP<CWLSurfaceResource> m_surface;
private:
SP<CXxColorManagementFeedbackSurfaceV4> m_resource;
wl_client* m_client = nullptr;
WP<CXXColorManagementImageDescription> m_currentPreferred;
friend class CXXColorManagementProtocol;
};
class CXXColorManagementParametricCreator {
public:
CXXColorManagementParametricCreator(SP<CXxImageDescriptionCreatorParamsV4> resource_);
bool good();
wl_client* client();
WP<CXXColorManagementParametricCreator> m_self;
NColorManagement::SImageDescription m_settings;
private:
enum eValuesSet : uint32_t { // NOLINT
PC_TF = (1 << 0),
PC_TF_POWER = (1 << 1),
PC_PRIMARIES = (1 << 2),
PC_LUMINANCES = (1 << 3),
PC_MASTERING_PRIMARIES = (1 << 4),
PC_MASTERING_LUMINANCES = (1 << 5),
PC_CLL = (1 << 6),
PC_FALL = (1 << 7),
};
SP<CXxImageDescriptionCreatorParamsV4> m_resource;
wl_client* m_client = nullptr;
uint32_t m_valuesSet = 0; // enum eValuesSet
};
class CXXColorManagementImageDescription {
public:
CXXColorManagementImageDescription(SP<CXxImageDescriptionV4> resource_, bool allowGetInformation);
bool good();
wl_client* client();
SP<CXxImageDescriptionV4> resource();
WP<CXXColorManagementImageDescription> m_self;
NColorManagement::SImageDescription m_settings;
private:
SP<CXxImageDescriptionV4> m_resource;
wl_client* m_client = nullptr;
bool m_allowGetInformation = false;
friend class CXXColorManagementOutput;
};
class CXXColorManagementImageDescriptionInfo {
public:
CXXColorManagementImageDescriptionInfo(SP<CXxImageDescriptionInfoV4> resource_, const NColorManagement::SImageDescription& settings_);
bool good();
wl_client* client();
private:
SP<CXxImageDescriptionInfoV4> m_resource;
wl_client* m_client = nullptr;
NColorManagement::SImageDescription m_settings;
};
class CXXColorManagementProtocol : public IWaylandProtocol {
public:
CXXColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
void onImagePreferredChanged();
private:
void destroyResource(CXXColorManager* resource);
void destroyResource(CXXColorManagementOutput* resource);
void destroyResource(CXXColorManagementSurface* resource);
void destroyResource(CXXColorManagementFeedbackSurface* resource);
void destroyResource(CXXColorManagementParametricCreator* resource);
void destroyResource(CXXColorManagementImageDescription* resource);
std::vector<SP<CXXColorManager>> m_managers;
std::vector<SP<CXXColorManagementOutput>> m_outputs;
std::vector<SP<CXXColorManagementSurface>> m_surfaces;
std::vector<SP<CXXColorManagementFeedbackSurface>> m_feedbackSurfaces;
std::vector<SP<CXXColorManagementParametricCreator>> m_parametricCreators;
std::vector<SP<CXXColorManagementImageDescription>> m_imageDescriptions;
friend class CXXColorManager;
friend class CXXColorManagementOutput;
friend class CXXColorManagementSurface;
friend class CXXColorManagementFeedbackSurface;
friend class CXXColorManagementParametricCreator;
friend class CXXColorManagementImageDescription;
friend class CFrogColorManagementSurface;
};
namespace PROTO {
inline UP<CXXColorManagementProtocol> xxColorManagement;
};

View file

@ -556,7 +556,7 @@ void CWLSurfaceResource::commitState(SSurfaceState& state) {
dropCurrentBuffer();
}
SImageDescription CWLSurfaceResource::getPreferredImageDescription() {
PImageDescription CWLSurfaceResource::getPreferredImageDescription() {
static const auto PFORCE_HDR = CConfigValue<Hyprlang::INT>("quirks:prefer_hdr");
const auto WINDOW = m_hlSurface ? Desktop::View::CWindow::fromView(m_hlSurface->view()) : nullptr;

View file

@ -33,7 +33,6 @@ class CDRMSyncobjSurfaceResource;
class CFifoResource;
class CCommitTimerResource;
class CColorManagementSurface;
class CFrogColorManagementSurface;
class CContentType;
class CWLCallbackResource {
@ -126,7 +125,7 @@ class CWLSurfaceResource {
void presentFeedback(const Time::steady_tp& when, PHLMONITOR pMonitor, bool discarded = false);
void scheduleState(WP<SSurfaceState> state);
void commitState(SSurfaceState& state);
NColorManagement::SImageDescription getPreferredImageDescription();
NColorManagement::PImageDescription getPreferredImageDescription();
void sortSubsurfaces();
bool hasVisibleSubsurface();

View file

@ -1,11 +1,16 @@
#include "ColorManagement.hpp"
#include "../../macros.hpp"
#include <hyprutils/memory/UniquePtr.hpp>
#include <map>
#include <vector>
namespace NColorManagement {
static uint32_t lastImageID = 0;
static std::map<uint32_t, SImageDescription> knownDescriptionIds; // expected to be small
// expected to be small
static std::vector<UP<const CPrimaries>> knownPrimaries;
static std::vector<UP<const CImageDescription>> knownDescriptions;
static std::map<std::pair<uint, uint>, Hyprgraphics::CMatrix3> primariesConversion;
const SPCPRimaries& getPrimaries(ePrimaries name) {
const SPCPRimaries& getPrimaries(ePrimaries name) {
switch (name) {
case CM_PRIMARIES_SRGB: return NColorPrimaries::BT709;
case CM_PRIMARIES_BT2020: return NColorPrimaries::BT2020;
@ -13,7 +18,7 @@ namespace NColorManagement {
case CM_PRIMARIES_PAL: return NColorPrimaries::PAL;
case CM_PRIMARIES_NTSC: return NColorPrimaries::NTSC;
case CM_PRIMARIES_GENERIC_FILM: return NColorPrimaries::GENERIC_FILM;
case CM_PRIMARIES_CIE1931_XYZ: return NColorPrimaries::DEFAULT_PRIMARIES; // FIXME
case CM_PRIMARIES_CIE1931_XYZ: return NColorPrimaries::CIE1931_XYZ;
case CM_PRIMARIES_DCI_P3: return NColorPrimaries::DCI_P3;
case CM_PRIMARIES_DISPLAY_P3: return NColorPrimaries::DISPLAY_P3;
case CM_PRIMARIES_ADOBE_RGB: return NColorPrimaries::ADOBE_RGB;
@ -21,26 +26,85 @@ namespace NColorManagement {
}
}
// TODO make image descriptions immutable and always set an id
CPrimaries::CPrimaries(const SPCPRimaries& primaries, const uint primariesId) : m_id(primariesId), m_primaries(primaries) {
m_primaries2XYZ = m_primaries.toXYZ();
}
uint32_t SImageDescription::findId() const {
for (auto it = knownDescriptionIds.begin(); it != knownDescriptionIds.end(); ++it) {
if (it->second == *this)
return it->first;
WP<const CPrimaries> CPrimaries::from(const SPCPRimaries& primaries) {
for (const auto& known : knownPrimaries) {
if (known->value() == primaries)
return known;
}
const auto newId = ++lastImageID;
knownDescriptionIds.insert(std::make_pair(newId, *this));
return newId;
knownPrimaries.emplace_back(CUniquePointer(new CPrimaries(primaries, knownPrimaries.size() + 1)));
return knownPrimaries.back();
}
uint32_t SImageDescription::getId() const {
return id > 0 ? id : findId();
WP<const CPrimaries> CPrimaries::from(const ePrimaries name) {
return from(getPrimaries(name));
}
uint32_t SImageDescription::updateId() {
id = 0;
id = findId();
return id;
WP<const CPrimaries> CPrimaries::from(const uint primariesId) {
ASSERT(primariesId <= knownPrimaries.size());
return knownPrimaries[primariesId - 1];
}
const SPCPRimaries& CPrimaries::value() const {
return m_primaries;
}
uint CPrimaries::id() const {
return m_id;
}
const Hyprgraphics::CMatrix3& CPrimaries::toXYZ() const {
return m_primaries2XYZ;
}
const Hyprgraphics::CMatrix3& CPrimaries::convertMatrix(const WP<const CPrimaries> dst) const {
const auto cacheKey = std::make_pair(m_id, dst->m_id);
if (!primariesConversion.contains(cacheKey))
primariesConversion.insert(std::make_pair(cacheKey, m_primaries.convertMatrix(dst->m_primaries)));
return primariesConversion[cacheKey];
}
CImageDescription::CImageDescription(const SImageDescription& imageDescription, const uint imageDescriptionId) :
m_id(imageDescriptionId), m_imageDescription(imageDescription) {
m_primariesId = CPrimaries::from(m_imageDescription.getPrimaries())->id();
}
PImageDescription CImageDescription::from(const SImageDescription& imageDescription) {
for (const auto& known : knownDescriptions) {
if (known->value() == imageDescription)
return known;
}
knownDescriptions.emplace_back(CUniquePointer(new CImageDescription(imageDescription, knownDescriptions.size() + 1)));
return knownDescriptions.back();
}
PImageDescription CImageDescription::from(const uint imageDescriptionId) {
ASSERT(imageDescriptionId <= knownDescriptions.size());
return knownDescriptions[imageDescriptionId - 1];
}
PImageDescription CImageDescription::with(const SImageDescription::SPCLuminances& luminances) const {
auto desc = m_imageDescription;
desc.luminances = luminances;
return CImageDescription::from(desc);
}
const SImageDescription& CImageDescription::value() const {
return m_imageDescription;
}
uint CImageDescription::id() const {
return m_id;
}
WP<const CPrimaries> CImageDescription::getPrimaries() const {
return CPrimaries::from(m_primariesId);
}
}

View file

@ -75,30 +75,35 @@ namespace NColorManagement {
.blue = {.x = 0.15, .y = 0.06},
.white = {.x = 0.3127, .y = 0.3290},
};
static const auto PAL_M = SPCPRimaries{
.red = {.x = 0.67, .y = 0.33},
.green = {.x = 0.21, .y = 0.71},
.blue = {.x = 0.14, .y = 0.08},
.white = {.x = 0.310, .y = 0.316},
};
static const auto PAL = SPCPRimaries{
.red = {.x = 0.640, .y = 0.330},
.green = {.x = 0.290, .y = 0.600},
.blue = {.x = 0.150, .y = 0.060},
.white = {.x = 0.3127, .y = 0.3290},
};
static const auto NTSC = SPCPRimaries{
.red = {.x = 0.630, .y = 0.340},
.green = {.x = 0.310, .y = 0.595},
.blue = {.x = 0.155, .y = 0.070},
.white = {.x = 0.3127, .y = 0.3290},
};
static const auto GENERIC_FILM = SPCPRimaries{
.red = {.x = 0.243, .y = 0.692},
.green = {.x = 0.145, .y = 0.049},
.blue = {.x = 0.681, .y = 0.319}, // NOLINT(modernize-use-std-numbers)
.white = {.x = 0.310, .y = 0.316},
};
static const auto BT2020 = SPCPRimaries{
.red = {.x = 0.708, .y = 0.292},
.green = {.x = 0.170, .y = 0.797},
@ -106,7 +111,12 @@ namespace NColorManagement {
.white = {.x = 0.3127, .y = 0.3290},
};
// FIXME CIE1931_XYZ
static const auto CIE1931_XYZ = SPCPRimaries{
.red = {.x = 1.0, .y = 0.0},
.green = {.x = 0.0, .y = 1.0},
.blue = {.x = 0.0, .y = 0.0},
.white = {.x = 1.0 / 3.0, .y = 1.0 / 3.0},
};
static const auto DCI_P3 = SPCPRimaries{
.red = {.x = 0.680, .y = 0.320},
@ -121,6 +131,7 @@ namespace NColorManagement {
.blue = {.x = 0.150, .y = 0.060},
.white = {.x = 0.3127, .y = 0.3290},
};
static const auto ADOBE_RGB = SPCPRimaries{
.red = {.x = 0.6400, .y = 0.3300},
.green = {.x = 0.2100, .y = 0.7100},
@ -131,9 +142,27 @@ namespace NColorManagement {
const SPCPRimaries& getPrimaries(ePrimaries name);
struct SImageDescription {
uint32_t id = 0; // FIXME needs id setting
class CPrimaries {
public:
static WP<const CPrimaries> from(const SPCPRimaries& primaries);
static WP<const CPrimaries> from(const ePrimaries name);
static WP<const CPrimaries> from(const uint primariesId);
const SPCPRimaries& value() const;
uint id() const;
const Hyprgraphics::CMatrix3& toXYZ() const; // toXYZ() * rgb -> xyz
const Hyprgraphics::CMatrix3& convertMatrix(const WP<const CPrimaries> dst) const; // convertMatrix(dst) * rgb with "this" primaries -> rgb with dst primaries
private:
CPrimaries(const SPCPRimaries& primaries, const uint primariesId);
uint m_id;
SPCPRimaries m_primaries;
Hyprgraphics::CMatrix3 m_primaries2XYZ;
};
struct SImageDescription {
struct SIccFile {
int fd = -1;
uint32_t length = 0;
@ -145,16 +174,14 @@ namespace NColorManagement {
bool windowsScRGB = false;
eTransferFunction transferFunction = CM_TRANSFER_FUNCTION_SRGB;
eTransferFunction transferFunction = CM_TRANSFER_FUNCTION_GAMMA22;
float transferFunctionPower = 1.0f;
bool primariesNameSet = false;
ePrimaries primariesNamed = CM_PRIMARIES_SRGB;
// primaries are stored as FP values with the same scale as standard defines (0.0 - 1.0)
// wayland protocol expects int32_t values multiplied by 1000000
// xx protocol expects int32_t values multiplied by 10000
// drm expects uint16_t values multiplied by 50000
// frog protocol expects drm values
SPCPRimaries primaries, masteringPrimaries;
// luminances in cd/m²
@ -179,11 +206,10 @@ namespace NColorManagement {
uint32_t maxFALL = 0;
bool operator==(const SImageDescription& d2) const {
return (id != 0 && id == d2.id) ||
(icc == d2.icc && windowsScRGB == d2.windowsScRGB && transferFunction == d2.transferFunction && transferFunctionPower == d2.transferFunctionPower &&
(primariesNameSet == d2.primariesNameSet && (primariesNameSet ? primariesNamed == d2.primariesNamed : primaries == d2.primaries)) &&
masteringPrimaries == d2.masteringPrimaries && luminances == d2.luminances && masteringLuminances == d2.masteringLuminances && maxCLL == d2.maxCLL &&
maxFALL == d2.maxFALL);
return icc == d2.icc && windowsScRGB == d2.windowsScRGB && transferFunction == d2.transferFunction && transferFunctionPower == d2.transferFunctionPower &&
(primariesNameSet == d2.primariesNameSet && (primariesNameSet ? primariesNamed == d2.primariesNamed : primaries == d2.primaries)) &&
masteringPrimaries == d2.masteringPrimaries && luminances == d2.luminances && masteringLuminances == d2.masteringLuminances && maxCLL == d2.maxCLL &&
maxFALL == d2.maxFALL;
}
const SPCPRimaries& getPrimaries() const {
@ -249,9 +275,44 @@ namespace NColorManagement {
default: return sdrRefLuminance >= 0 ? sdrRefLuminance : SDR_REF_LUMINANCE;
}
};
uint32_t findId() const;
uint32_t getId() const;
uint32_t updateId();
};
class CImageDescription {
public:
static WP<const CImageDescription> from(const SImageDescription& imageDescription);
static WP<const CImageDescription> from(const uint imageDescriptionId);
WP<const CImageDescription> with(const SImageDescription::SPCLuminances& luminances) const;
const SImageDescription& value() const;
uint id() const;
WP<const CPrimaries> getPrimaries() const;
private:
CImageDescription(const SImageDescription& imageDescription, const uint imageDescriptionId);
uint m_id;
uint m_primariesId;
SImageDescription m_imageDescription;
};
using PImageDescription = WP<const CImageDescription>;
static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{});
static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = 0, .max = 10000, .reference = 203}});
;
static const auto SCRGB_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{
.windowsScRGB = true,
.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR,
.primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_SRGB,
.primaries = NColorPrimaries::BT709,
.luminances = {.reference = 203},
});
;
}

View file

@ -1560,52 +1560,52 @@ static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescriptio
targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22);
}
void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const NColorManagement::SImageDescription& imageDescription,
const NColorManagement::SImageDescription& targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) {
void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription,
bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) {
static auto PSDREOTF = CConfigValue<Hyprlang::INT>("render:cm_sdr_eotf");
if (m_renderData.surface.valid() &&
((!m_renderData.surface->m_colorManagement.valid() && *PSDREOTF >= 1) ||
(*PSDREOTF == 2 && m_renderData.surface->m_colorManagement.valid() &&
imageDescription.transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB))) {
imageDescription->value().transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB))) {
shader.setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22);
} else
shader.setUniformInt(SHADER_SOURCE_TF, imageDescription.transferFunction);
shader.setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction);
shader.setUniformInt(SHADER_TARGET_TF, targetImageDescription.transferFunction);
shader.setUniformInt(SHADER_TARGET_TF, targetImageDescription->value().transferFunction);
const auto targetPrimaries = targetImageDescription.primariesNameSet || targetImageDescription.primaries == SPCPRimaries{} ?
getPrimaries(targetImageDescription.primariesNamed) :
targetImageDescription.primaries;
const auto targetPrimaries = targetImageDescription->getPrimaries();
const std::array<GLfloat, 8> glTargetPrimaries = {
targetPrimaries.red.x, targetPrimaries.red.y, targetPrimaries.green.x, targetPrimaries.green.y,
targetPrimaries.blue.x, targetPrimaries.blue.y, targetPrimaries.white.x, targetPrimaries.white.y,
targetPrimaries->value().red.x, targetPrimaries->value().red.y, targetPrimaries->value().green.x, targetPrimaries->value().green.y,
targetPrimaries->value().blue.x, targetPrimaries->value().blue.y, targetPrimaries->value().white.x, targetPrimaries->value().white.y,
};
shader.setUniformMatrix4x2fv(SHADER_TARGET_PRIMARIES, 1, false, glTargetPrimaries);
const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription, targetImageDescription);
const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription, targetImageDescription);
const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription->value(), targetImageDescription->value());
const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription->value(), targetImageDescription->value());
shader.setUniformFloat2(SHADER_SRC_TF_RANGE, imageDescription.getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1),
imageDescription.getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1));
shader.setUniformFloat2(SHADER_DST_TF_RANGE, targetImageDescription.getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1),
targetImageDescription.getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1));
shader.setUniformFloat2(SHADER_SRC_TF_RANGE, imageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1),
imageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1));
shader.setUniformFloat2(SHADER_DST_TF_RANGE, targetImageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1),
targetImageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1));
shader.setUniformFloat(SHADER_SRC_REF_LUMINANCE, imageDescription.getTFRefLuminance(-1));
shader.setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription.getTFRefLuminance(-1));
shader.setUniformFloat(SHADER_SRC_REF_LUMINANCE, imageDescription->value().getTFRefLuminance(-1));
shader.setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription->value().getTFRefLuminance(-1));
const float maxLuminance =
needsHDRmod ? imageDescription.getTFMaxLuminance(-1) : (imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference);
const float maxLuminance = needsHDRmod ?
imageDescription->value().getTFMaxLuminance(-1) :
(imageDescription->value().luminances.max > 0 ? imageDescription->value().luminances.max : imageDescription->value().luminances.reference);
shader.setUniformFloat(SHADER_MAX_LUMINANCE,
maxLuminance * targetImageDescription.luminances.reference /
(needsHDRmod ? imageDescription.getTFRefLuminance(-1) : imageDescription.luminances.reference));
shader.setUniformFloat(SHADER_DST_MAX_LUMINANCE, targetImageDescription.luminances.max > 0 ? targetImageDescription.luminances.max : 10000);
maxLuminance * targetImageDescription->value().luminances.reference /
(needsHDRmod ? imageDescription->value().getTFRefLuminance(-1) : imageDescription->value().luminances.reference));
shader.setUniformFloat(SHADER_DST_MAX_LUMINANCE, targetImageDescription->value().luminances.max > 0 ? targetImageDescription->value().luminances.max : 10000);
shader.setUniformFloat(SHADER_SDR_SATURATION, needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f);
shader.setUniformFloat(SHADER_SDR_BRIGHTNESS, needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f);
const auto cacheKey = std::make_pair(imageDescription.getId(), targetImageDescription.getId());
const auto cacheKey = std::make_pair(imageDescription->id(), targetImageDescription->id());
if (!primariesConversionCache.contains(cacheKey)) {
const auto mat = imageDescription.getPrimaries().convertMatrix(targetImageDescription.getPrimaries()).mat();
auto conversion = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries());
const auto mat = conversion.mat();
const std::array<GLfloat, 9> glConvertMatrix = {
mat[0][0], mat[1][0], mat[2][0], //
mat[0][1], mat[1][1], mat[2][1], //
@ -1616,7 +1616,7 @@ void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const NColorManagement::SI
shader.setUniformMatrix3fv(SHADER_CONVERT_MATRIX, 1, false, primariesConversionCache[cacheKey]);
}
void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const SImageDescription& imageDescription) {
void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const PImageDescription imageDescription) {
passCMUniforms(shader, imageDescription, m_renderData.pMonitor->m_imageDescription, true, m_renderData.pMonitor->m_sdrMinLuminance, m_renderData.pMonitor->m_sdrMaxLuminance);
}
@ -1699,13 +1699,13 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
const bool isHDRSurface = m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ? m_renderData.surface->m_colorManagement->isHDR() : false;
const bool canPassHDRSurface = isHDRSurface && !m_renderData.surface->m_colorManagement->isWindowsScRGB(); // windows scRGB requires CM shader
auto imageDescription = m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ?
m_renderData.surface->m_colorManagement->imageDescription() :
(data.cmBackToSRGB ? data.cmBackToSRGBSource->m_imageDescription : SImageDescription{});
const auto imageDescription = m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ?
CImageDescription::from(m_renderData.surface->m_colorManagement->imageDescription()) :
(data.cmBackToSRGB ? data.cmBackToSRGBSource->m_imageDescription : DEFAULT_IMAGE_DESCRIPTION);
const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */
|| m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */
|| (imageDescription == m_renderData.pMonitor->m_imageDescription && !data.cmBackToSRGB) /* Source and target have the same image description */
const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */
|| m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */
|| (imageDescription->id() == m_renderData.pMonitor->m_imageDescription->id() && !data.cmBackToSRGB) /* Source and target have the same image description */
|| (((*PPASS && canPassHDRSurface) ||
(*PPASS == 1 && !isHDRSurface && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) &&
m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */;
@ -1719,8 +1719,8 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
shader->setUniformInt(SHADER_TEX_TYPE, texType);
if (data.cmBackToSRGB) {
static auto PSDREOTF = CConfigValue<Hyprlang::INT>("render:cm_sdr_eotf");
auto chosenSdrEotf = *PSDREOTF > 0 ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB;
passCMUniforms(*shader, imageDescription, NColorManagement::SImageDescription{.transferFunction = chosenSdrEotf}, true, -1, -1);
auto chosenSdrEotf = *PSDREOTF != 3 ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB;
passCMUniforms(*shader, imageDescription, CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = chosenSdrEotf}), true, -1, -1);
} else
passCMUniforms(*shader, imageDescription);
}
@ -2028,18 +2028,20 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi
useProgram(m_shaders->m_shBLURPREPARE.program);
// From FB to sRGB
const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription == SImageDescription{};
const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription->id() == DEFAULT_IMAGE_DESCRIPTION->id();
m_shaders->m_shBLURPREPARE.setUniformInt(SHADER_SKIP_CM, skipCM);
if (!skipCM) {
passCMUniforms(m_shaders->m_shBLURPREPARE, m_renderData.pMonitor->m_imageDescription, SImageDescription{});
passCMUniforms(m_shaders->m_shBLURPREPARE, m_renderData.pMonitor->m_imageDescription, DEFAULT_IMAGE_DESCRIPTION);
m_shaders->m_shBLURPREPARE.setUniformFloat(SHADER_SDR_SATURATION,
m_renderData.pMonitor->m_sdrSaturation > 0 &&
m_renderData.pMonitor->m_imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_renderData.pMonitor->m_imageDescription->value().transferFunction ==
NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_renderData.pMonitor->m_sdrSaturation :
1.0f);
m_shaders->m_shBLURPREPARE.setUniformFloat(SHADER_SDR_BRIGHTNESS,
m_renderData.pMonitor->m_sdrBrightness > 0 &&
m_renderData.pMonitor->m_imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_renderData.pMonitor->m_imageDescription->value().transferFunction ==
NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_renderData.pMonitor->m_sdrBrightness :
1.0f);
}
@ -2509,10 +2511,10 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr
useProgram(m_shaders->m_shBORDER1.program);
const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription == SImageDescription{};
const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription->id() == DEFAULT_IMAGE_DESCRIPTION->id();
m_shaders->m_shBORDER1.setUniformInt(SHADER_SKIP_CM, skipCM);
if (!skipCM)
passCMUniforms(m_shaders->m_shBORDER1, SImageDescription{});
passCMUniforms(m_shaders->m_shBORDER1, DEFAULT_IMAGE_DESCRIPTION);
m_shaders->m_shBORDER1.setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
m_shaders->m_shBORDER1.setUniform4fv(SHADER_GRADIENT, grad.m_colorsOkLabA.size() / 4, grad.m_colorsOkLabA);
@ -2593,10 +2595,10 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr
useProgram(m_shaders->m_shBORDER1.program);
const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription == SImageDescription{};
const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription->id() == DEFAULT_IMAGE_DESCRIPTION->id();
m_shaders->m_shBORDER1.setUniformInt(SHADER_SKIP_CM, skipCM);
if (!skipCM)
passCMUniforms(m_shaders->m_shBORDER1, SImageDescription{});
passCMUniforms(m_shaders->m_shBORDER1, DEFAULT_IMAGE_DESCRIPTION);
m_shaders->m_shBORDER1.setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
m_shaders->m_shBORDER1.setUniform4fv(SHADER_GRADIENT, grad1.m_colorsOkLabA.size() / 4, grad1.m_colorsOkLabA);
@ -2670,10 +2672,10 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun
blend(true);
useProgram(m_shaders->m_shSHADOW.program);
const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription == SImageDescription{};
const bool skipCM = !m_cmSupported || m_renderData.pMonitor->m_imageDescription->id() == DEFAULT_IMAGE_DESCRIPTION->id();
m_shaders->m_shSHADOW.setUniformInt(SHADER_SKIP_CM, skipCM);
if (!skipCM)
passCMUniforms(m_shaders->m_shSHADOW, SImageDescription{});
passCMUniforms(m_shaders->m_shSHADOW, DEFAULT_IMAGE_DESCRIPTION);
m_shaders->m_shSHADOW.setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
m_shaders->m_shSHADOW.setUniformFloat4(SHADER_COLOR, col.r, col.g, col.b, col.a * a);

View file

@ -403,9 +403,9 @@ class CHyprOpenGLImpl {
CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage);
CFramebuffer* blurFramebufferWithDamage(float a, CRegion* damage, CFramebuffer& source);
void passCMUniforms(SShader&, const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription,
void passCMUniforms(SShader&, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription,
bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1);
void passCMUniforms(SShader&, const NColorManagement::SImageDescription& imageDescription);
void passCMUniforms(SShader&, const NColorManagement::PImageDescription imageDescription);
void renderTexturePrimitive(SP<CTexture> tex, const CBox& box);
void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size);
void renderRectInternal(const CBox&, const CHyprColor&, const SRectRenderData& data);

View file

@ -1515,6 +1515,7 @@ static const hdr_output_metadata NO_HDR_METADATA = {.hdmi_metadata_type1 = hdr_m
static hdr_output_metadata createHDRMetadata(SImageDescription settings, SP<CMonitor> monitor) {
uint8_t eotf = 0;
switch (settings.transferFunction) {
case CM_TRANSFER_FUNCTION_GAMMA22:
case CM_TRANSFER_FUNCTION_SRGB: eotf = 0; break; // used to send primaries and luminances to AQ. ignored for now
case CM_TRANSFER_FUNCTION_ST2084_PQ: eotf = 2; break;
case CM_TRANSFER_FUNCTION_EXT_LINEAR:
@ -1527,9 +1528,11 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, S
const auto toNits = [](uint32_t value) { return sc<uint16_t>(std::round(value)); };
const auto to16Bit = [](float value) { return sc<uint16_t>(std::round(value * 50000)); };
auto colorimetry = settings.primariesNameSet || settings.primaries == SPCPRimaries{} ? getPrimaries(settings.primariesNamed) : settings.primaries;
auto colorimetry = settings.getPrimaries();
auto luminances = settings.masteringLuminances.max > 0 ? settings.masteringLuminances :
SImageDescription::SPCMasteringLuminances{.min = monitor->minLuminance(), .max = monitor->maxLuminance(10000)};
(settings.luminances != SImageDescription::SPCLuminances{} ?
SImageDescription::SPCMasteringLuminances{.min = settings.luminances.min, .max = settings.luminances.max} :
SImageDescription::SPCMasteringLuminances{.min = monitor->minLuminance(), .max = monitor->maxLuminance(10000)});
Log::logger->log(Log::TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y,
colorimetry.blue.x, colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y);
@ -1617,7 +1620,7 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
pMonitor->m_previousFSWindow.reset(); // trigger CTM update
}
Log::logger->log(Log::INFO, wantHDR ? "[CM] Updating HDR metadata from monitor" : "[CM] Restoring SDR mode");
pMonitor->m_output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->m_imageDescription, pMonitor) : NO_HDR_METADATA);
pMonitor->m_output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->m_imageDescription->value(), pMonitor) : NO_HDR_METADATA);
}
pMonitor->m_needsHDRupdate = true;
}
@ -1655,9 +1658,10 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
const auto FS_DESC = pMonitor->getFSImageDescription();
if (FS_DESC.has_value()) {
Log::logger->log(Log::INFO, "[CM] Updating fullscreen CTM");
pMonitor->m_noShaderCTM = true;
const auto mat = FS_DESC->getPrimaries().convertMatrix(pMonitor->m_imageDescription.getPrimaries()).mat();
const std::array<float, 9> CTM = {
pMonitor->m_noShaderCTM = true;
auto conversion = FS_DESC.value()->getPrimaries()->convertMatrix(pMonitor->m_imageDescription->getPrimaries());
const auto mat = conversion.mat();
const std::array<float, 9> CTM = {
mat[0][0], mat[0][1], mat[0][2], //
mat[1][0], mat[1][1], mat[1][2], //
mat[2][0], mat[2][1], mat[2][2], //

View file

@ -36,7 +36,7 @@ vec4 okLabAToSrgb(vec4 lab) {
l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292,
l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965),
l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010
), CM_TRANSFER_FUNCTION_SRGB
), CM_TRANSFER_FUNCTION_GAMMA22
), lab[3]);
}