[graphics/krita] /: Create lager model for HDR metadata, use colorimetry::xy for volume.

Wolthera van Hövell tot Westerflier <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit b533c4d976f371d0fcf4aa0aea38fe5b0fa9894f by Wolthera van Hövell tot Westerflier.
Committed on 11/08/2026 at 09:33.
Pushed by woltherav into branch 'master'.

Create lager model for HDR metadata, use colorimetry::xy for volume.

M  +2    -0    libs/image/CMakeLists.txt
A  +24   -0    libs/image/kis_hdr_metadata.cpp     [License: GPL(v2.0+)]
M  +13   -17   libs/image/kis_hdr_metadata.h
A  +222  -0    libs/image/kis_hdr_metadata_models.cpp     [License: GPL(v2.0+)]
A  +83   -0    libs/image/kis_hdr_metadata_models.h     [License: GPL(v2.0+)]
M  +21   -20   libs/ui/animation/KisHDRMetadataOptions.cpp
M  +37   -106  libs/ui/dialogs/kis_dlg_image_properties.cc
M  +55   -0    libs/widgets/KisWidgetConnectionUtils.cpp
M  +2    -0    libs/widgets/KisWidgetConnectionUtils.h
M  +23   -8    plugins/impex/libkra/kis_kra_loader.cpp
M  +4    -4    plugins/impex/libkra/kis_kra_saver.cpp

https://invent.kde.org/graphics/krita/-/commit/b533c4d976f371d0fcf4aa0aea38fe5b0fa9894f

diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt
index a9361ee5f5b..e8a37ac3e44 100644
--- a/libs/image/CMakeLists.txt
+++ b/libs/image/CMakeLists.txt
@@ -362,6 +362,8 @@ set(kritaimage_LIB_SRCS
 
    kis_convex_hull.cpp
    KisSpatialContainer.cpp
+   kis_hdr_metadata.cpp
+   kis_hdr_metadata_models.cpp
 )
 
 set(einspline_SRCS
diff --git a/libs/image/kis_hdr_metadata.cpp b/libs/image/kis_hdr_metadata.cpp
new file mode 100644
index 00000000000..c1b68b01c7a
--- /dev/null
+++ b/libs/image/kis_hdr_metadata.cpp
@@ -0,0 +1,24 @@
+/*
+ *  SPDX-FileCopyrightText: 2026 Wolthera van Hövell tot Westerflier <[email protected]>
+ *
+ *  SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <kis_hdr_metadata.h>
+#include <KisStaticInitializer.h>
+
+KIS_DECLARE_STATIC_INITIALIZER {
+    qRegisterMetaType<KisRelativeContentLightLevelInformation::CalculationType>();
+}
+
+bool KisRelativeContentLightLevelInformation::operator==(const KisRelativeContentLightLevelInformation &other) const {
+    return qFuzzyCompare(maxContentLightLevel, other.maxContentLightLevel)
+    && qFuzzyCompare(maxFrameAverageLightLevel, other.maxFrameAverageLightLevel)
+        && (type == other.type);
+}
+
+bool KisColorVolumeInformation::operator==(const KisColorVolumeInformation &other) const {
+    return qFuzzyCompare(maxLuminance, other.maxLuminance)
+    && qFuzzyCompare(minLuminance, other.minLuminance)
+        && white == other.white && red == other.red
+                                             && green == other.green && blue == other.blue;
+}
diff --git a/libs/image/kis_hdr_metadata.h b/libs/image/kis_hdr_metadata.h
index ad22c30e65e..af45987f2b6 100644
--- a/libs/image/kis_hdr_metadata.h
+++ b/libs/image/kis_hdr_metadata.h
@@ -7,14 +7,17 @@
 #define KIS_HDR_METADATA_H
 
 #include <QPointF>
+#include <KoColorimetryUtils.h>
 #include <boost/operators.hpp>
+
+#include "kritaimage_export.h"
 /**
  * KisRelativeContentLightLevelInformation is a struct that represents
  * HDR10 metadata. It is similar to the same values in MasteringInfo in
  * KisSurfaceColorimetry.h, though typically relative to the diffuse white.
  */
 
-struct KisRelativeContentLightLevelInformation: public boost::equality_comparable<KisRelativeContentLightLevelInformation> {
+struct KRITAIMAGE_EXPORT KisRelativeContentLightLevelInformation: public boost::equality_comparable<KisRelativeContentLightLevelInformation> {
     /**
      * MaxContentLightLevel or MaxCLL is the brightest pixel in the frame sequence.
      */
@@ -34,11 +37,7 @@ struct KisRelativeContentLightLevelInformation: public boost::equality_comparabl
 
     CalculationType type = XYZLuminance;
 
-    bool operator==(const KisRelativeContentLightLevelInformation & other) const {
-        return qFuzzyCompare(maxContentLightLevel, other.maxContentLightLevel)
-            && qFuzzyCompare(maxFrameAverageLightLevel, other.maxFrameAverageLightLevel)
-            && (type == other.type);
-    };
+    bool operator==(const KisRelativeContentLightLevelInformation & other) const;;
 };
 
 /**
@@ -46,21 +45,18 @@ struct KisRelativeContentLightLevelInformation: public boost::equality_comparabl
  * is a struct that represents the 'mastering' display. It's primary purpose
  * is to provide extra information for gamutmapping.
  */
-struct KisColorVolumeInformation: public boost::equality_comparable<KisColorVolumeInformation> {
-    QPointF white; ///< xyY location of the whitepoint.
-    QPointF red; ///< xyY location of the red colorant.
-    QPointF green; ///< xyY location of the green colorant.
-    QPointF blue; ///< xyY location of the blue colorant.
+struct KRITAIMAGE_EXPORT KisColorVolumeInformation: public boost::equality_comparable<KisColorVolumeInformation> {
+    KoColorimetryUtils::xy white; ///< xyY location of the whitepoint.
+    KoColorimetryUtils::xy red; ///< xyY location of the red colorant.
+    KoColorimetryUtils::xy green; ///< xyY location of the green colorant.
+    KoColorimetryUtils::xy blue; ///< xyY location of the blue colorant.
 
     double maxLuminance = 0.0; ///< Maximum screen brightness in cd/m²
     double minLuminance = 0.0; ///< Minimum screen brightness in cd/m²
 
-    bool operator==(const KisColorVolumeInformation & other) const {
-        return qFuzzyCompare(maxLuminance, other.maxLuminance)
-        && qFuzzyCompare(minLuminance, other.minLuminance)
-            && white == other.white && red == other.red
-            && green == other.green && blue == other.blue;
-    };
+    bool operator==(const KisColorVolumeInformation & other) const;;
 };
 
+Q_DECLARE_METATYPE(KisRelativeContentLightLevelInformation::CalculationType);
+
 #endif // KIS_HDR_METADATA_H
diff --git a/libs/image/kis_hdr_metadata_models.cpp b/libs/image/kis_hdr_metadata_models.cpp
new file mode 100644
index 00000000000..d2995762898
--- /dev/null
+++ b/libs/image/kis_hdr_metadata_models.cpp
@@ -0,0 +1,222 @@
+/*
+ *  SPDX-FileCopyrightText: 2026 Wolthera van Hövell tot Westerflier <[email protected]>
+ *
+ *  SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "kis_hdr_metadata_models.h"
+#include <KLocalizedString>
+#include <lager/constant.hpp>
+#include <KisZug.h>
+#include <KisLager.h>
+namespace {
+
+ComboBoxState calcTypeComboBoxState(KisRelativeContentLightLevelInformation::CalculationType type, bool enabled)
+{
+    QStringList values;
+    QStringList toolTips;
+
+    values << i18nc("CLLI calculation type", "XYZ Luminance");
+    values << i18nc("CLLI calculation type", "Rec 2020 Per Component");
+    values << i18nc("CLLI calculation type", "RGB Per Component");
+
+    toolTips << i18nc("@info:tooltip", "Calculate the brightness in nits against XYZ luminance.");
+    toolTips << i18nc("@info:tooltip", "Calculate the brightness in nits by testing the components in linear rec 2020.");
+    toolTips << i18nc("@info:tooltip", "Calculate the brightness in nits by testing the components in linear RGB, if possible, falls back to using XYZ luminance.");
+
+    return {values, static_cast<int>(type), enabled, toolTips};
+}
+
+auto multiplyByReferenceWhite = [](double referenceWhite){
+    return lager::lenses::getset(
+        [referenceWhite] (const double &value) -> double {
+            return value * referenceWhite;
+        },
+        [referenceWhite] (double value, const double &newValue) {
+            value = referenceWhite > 0? newValue/referenceWhite: 0.0;
+            return value;
+        }
+        );
+};
+
+auto colorimetryXcoord = []() {
+    return lager::lenses::getset(
+        [] (const KoColorimetryUtils::xy &value) -> double {
+            return value.x;
+        },
+        [] (KoColorimetryUtils::xy value, const double &val) {
+            value.x = val;
+            return value;
+        }
+    );
+};
+
+auto colorimetryYcoord = []() {
+    return lager::lenses::getset(
+        [] (const KoColorimetryUtils::xy &value) -> double {
+            return value.y;
+        },
+        [] (KoColorimetryUtils::xy value, const double &val) {
+            value.y = val;
+            return value;
+        }
+        );
+};
+
+}
+
+using KisWidgetConnectionUtils::ToSpinBoxState;
+using KisWidgetConnectionUtils::ToControlState;
+
+KisHDRMetadataModel::KisHDRMetadataModel(QObject *parent)
+    : QObject{parent}
+    , clliData(lager::make_state(KisRelativeContentLightLevelInformation(), lager::automatic_tag{}))
+    , cviData(lager::make_state(KisColorVolumeInformation(), lager::automatic_tag{}))
+    , referenceWhiteData(lager::make_state(203.0, lager::automatic_tag{}))
+    , imageProfileRelative(lager::make_state(false, lager::automatic_tag{}))
+    , referenceWhiteEnabledData(lager::make_state(false, lager::automatic_tag{}))
+    , clliEnabledData(lager::make_state(false, lager::automatic_tag{}))
+    , cviEnabledData(lager::make_state(false, lager::automatic_tag{}))
+    , LAGER_QT(refWhiteEnabled){lager::with(referenceWhiteEnabledData)}
+    , LAGER_QT(refWhiteEnabledState){lager::with(LAGER_QT(refWhiteEnabled), lager::make_constant(true)).map(ToControlState{})}
+    , LAGER_QT(referenceWhite) {referenceWhiteData}
+    , LAGER_QT(clliEnabled){lager::with(clliEnabledData)}
+    , LAGER_QT(clliEnabledState){lager::with(LAGER_QT(clliEnabled), lager::make_constant(true)).map(ToControlState{})}
+    , LAGER_QT(maxContentLightLevel) {clliData[&KisRelativeContentLightLevelInformation::maxContentLightLevel].zoom(multiplyByReferenceWhite(LAGER_QT(referenceWhite).get()))}
+    , LAGER_QT(maxContentLightLevelState) {lager::with(LAGER_QT(maxContentLightLevel),
+                                                      lager::make_constant(0.0),
+                                                      lager::make_constant(100000.0),
+                                                      LAGER_QT(clliEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(maxFrameAverageLightLevel) {clliData[&KisRelativeContentLightLevelInformation::maxFrameAverageLightLevel].zoom(multiplyByReferenceWhite(LAGER_QT(referenceWhite).get()))}
+    , LAGER_QT(maxFrameAverageLightLevelState) {lager::with(LAGER_QT(maxFrameAverageLightLevel),
+                                                      lager::make_constant(0.0),
+                                                      lager::make_constant(100000.0),
+                                                      LAGER_QT(clliEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(clliCalculationType) {clliData[&KisRelativeContentLightLevelInformation::type]}
+    , LAGER_QT(clliCalculationTypeState) {lager::with(LAGER_QT(clliCalculationType), LAGER_QT(clliEnabled)).map(&calcTypeComboBoxState)}
+    , LAGER_QT(cviEnabled){lager::with(cviEnabledData)}
+    , LAGER_QT(cviEnabledState){lager::with(LAGER_QT(cviEnabled), lager::make_constant(true)).map(ToControlState{})}
+    , LAGER_QT(cviWhiteX){cviData[&KisColorVolumeInformation::white].zoom(colorimetryXcoord())}
+    , LAGER_QT(cviWhiteY){cviData[&KisColorVolumeInformation::white].zoom(colorimetryYcoord())}
+    , LAGER_QT(cviWhiteXState){lager::with(LAGER_QT(cviWhiteX),
+                                           lager::make_constant(0.0),
+                                           lager::make_constant(1.0),
+                                           LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(cviWhiteYState){lager::with(LAGER_QT(cviWhiteY),
+                                           lager::make_constant(0.0),
+                                           lager::make_constant(1.0),
+                                           LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(cviRedX){cviData[&KisColorVolumeInformation::red].zoom(colorimetryXcoord())}
+    , LAGER_QT(cviRedY){cviData[&KisColorVolumeInformation::red].zoom(colorimetryYcoord())}
+    , LAGER_QT(cviRedXState){lager::with(LAGER_QT(cviRedX),
+                                         lager::make_constant(0.0),
+                                         lager::make_constant(1.0),
+                                         LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(cviRedYState){lager::with(LAGER_QT(cviRedY),
+                                         lager::make_constant(0.0),
+                                         lager::make_constant(1.0),
+                                         LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(cviGreenX){cviData[&KisColorVolumeInformation::green].zoom(colorimetryXcoord())}
+    , LAGER_QT(cviGreenY){cviData[&KisColorVolumeInformation::green].zoom(colorimetryYcoord())}
+    , LAGER_QT(cviGreenXState){lager::with(LAGER_QT(cviGreenX),
+                                           lager::make_constant(0.0),
+                                           lager::make_constant(1.0),
+                                           LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(cviGreenYState){lager::with(LAGER_QT(cviGreenY),
+                                           lager::make_constant(0.0),
+                                           lager::make_constant(1.0),
+                                           LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(cviBlueX){cviData[&KisColorVolumeInformation::blue].zoom(colorimetryXcoord())}
+    , LAGER_QT(cviBlueY){cviData[&KisColorVolumeInformation::blue].zoom(colorimetryYcoord())}
+    , LAGER_QT(cviBlueXState){lager::with(LAGER_QT(cviBlueX),
+                                          lager::make_constant(0.0),
+                                          lager::make_constant(1.0),
+                                          LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(cviBlueYState){lager::with(LAGER_QT(cviBlueY),
+                                          lager::make_constant(0.0),
+                                          lager::make_constant(1.0),
+                                          LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(cviMaxLuminance){cviData[&KisColorVolumeInformation::maxLuminance]}
+    , LAGER_QT(cviMaxLuminanceState){lager::with(LAGER_QT(cviMaxLuminance),
+                                                 lager::make_constant(0.0),
+                                                 lager::make_constant(10000.0),
+                                                 LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+    , LAGER_QT(cviMinLuminance){cviData[&KisColorVolumeInformation::minLuminance]}
+    , LAGER_QT(cviMinLuminanceState){lager::with(LAGER_QT(cviMinLuminance),
+                lager::make_constant(0.0),
+                lager::make_constant(10000.0),
+                LAGER_QT(cviEnabled)).map(ToSpinBoxState{})}
+
+{
+
+}
+
+KisHDRMetadataModel::~KisHDRMetadataModel() {
+
+}
+
+void KisHDRMetadataModel::setClli(const std::optional<KisRelativeContentLightLevelInformation> &clli)
+{
+    if (clli) {
+        clliEnabledData.set(true);
+        clliData.set(*clli);
+    } else {
+        clliEnabledData.set(false);
+    }
+}
+
+std::optional<KisRelativeContentLightLevelInformation> KisHDRMetadataModel::clli() const
+{
+    const bool enabled = clliEnabledData.get();
+    if (enabled) {
+        return std::make_optional(clliData.get());
+    }
+    return std::nullopt;
+}
+
+void KisHDRMetadataModel::setCvi(const std::optional<KisColorVolumeInformation> &cvi)
+{
+    if (cvi) {
+        cviEnabledData.set(true);
+        cviData.set(*cvi);
+    } else {
+        cviEnabledData.set(false);
+    }
+}
+
+std::optional<KisColorVolumeInformation> KisHDRMetadataModel::cvi() const
+{
+    const bool enabled = cviEnabledData.get();
+    if (enabled) {
+        return std::make_optional(cviData.get());
+    }
+    return std::nullopt;
+}
+
+void KisHDRMetadataModel::setRefWhite(const std::optional<double> &refWhite)
+{
+    if (refWhite) {
+        referenceWhiteEnabledData.set(true);
+        referenceWhiteData.set(*refWhite);
+    } else {
+        referenceWhiteEnabledData.set(false);
+    }
+}
+
+std::optional<double> KisHDRMetadataModel::refWhite() const
+{
+    const bool enabled = referenceWhiteEnabledData.get();
+    if (enabled) {
+        return std::make_optional(referenceWhiteData.get());
+    }
+    return std::nullopt;
+}
+
+void KisHDRMetadataModel::setImageProfileRelative(const std::optional<double> &refWhite)
+{
+    if (refWhite) {
+        imageProfileRelative.set(false);
+        referenceWhiteData.set(*refWhite);
+    } else {
+        imageProfileRelative.set(true);
+    }
+}
diff --git a/libs/image/kis_hdr_metadata_models.h b/libs/image/kis_hdr_metadata_models.h
new file mode 100644
index 00000000000..5661c5a5c10
--- /dev/null
+++ b/libs/image/kis_hdr_metadata_models.h
@@ -0,0 +1,83 @@
+/*
+ *  SPDX-FileCopyrightText: 2026 Wolthera van Hövell tot Westerflier <[email protected]>
+ *
+ *  SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef KIS_HDR_METADATA_MODELS_H
+#define KIS_HDR_METADATA_MODELS_H
+
+#include <QObject>
+
+#include <lager/cursor.hpp>
+#include <lager/extra/qt.hpp>
+#include <lager/state.hpp>
+
+#include <KisWidgetConnectionUtils.h>
+#include <kis_hdr_metadata.h>
+
+#include "kritaimage_export.h"
+
+class KRITAIMAGE_EXPORT KisHDRMetadataModel : public QObject
+{
+    Q_OBJECT
+public:
+    explicit KisHDRMetadataModel(QObject *parent = nullptr);
+    ~KisHDRMetadataModel();
+
+    lager::cursor<KisRelativeContentLightLevelInformation> clliData;
+    lager::cursor<KisColorVolumeInformation> cviData;
+    lager::cursor<double> referenceWhiteData;
+    lager::cursor<bool> imageProfileRelative;
+    lager::cursor<bool> referenceWhiteEnabledData;
+    lager::cursor<bool> clliEnabledData;
+    lager::cursor<bool> cviEnabledData;
+
+    LAGER_QT_CURSOR(bool, refWhiteEnabled);
+    LAGER_QT_READER(CheckBoxState, refWhiteEnabledState);
+    LAGER_QT_CURSOR(double, referenceWhite);
+
+    LAGER_QT_CURSOR(bool, clliEnabled);
+    LAGER_QT_READER(CheckBoxState, clliEnabledState);
+    LAGER_QT_CURSOR(double, maxContentLightLevel);
+    LAGER_QT_READER(DoubleSpinBoxState, maxContentLightLevelState);
+    LAGER_QT_CURSOR(double, maxFrameAverageLightLevel);
+    LAGER_QT_READER(DoubleSpinBoxState, maxFrameAverageLightLevelState);
+    LAGER_QT_CURSOR(KisRelativeContentLightLevelInformation::CalculationType, clliCalculationType);
+    LAGER_QT_READER(ComboBoxState, clliCalculationTypeState);
+
+    LAGER_QT_CURSOR(bool, cviEnabled);
+    LAGER_QT_READER(CheckBoxState, cviEnabledState);
+    LAGER_QT_CURSOR(double, cviWhiteX);
+    LAGER_QT_CURSOR(double, cviWhiteY);
+    LAGER_QT_READER(DoubleSpinBoxState, cviWhiteXState);
+    LAGER_QT_READER(DoubleSpinBoxState, cviWhiteYState);
+    LAGER_QT_CURSOR(double, cviRedX);
+    LAGER_QT_CURSOR(double, cviRedY);
+    LAGER_QT_READER(DoubleSpinBoxState, cviRedXState);
+    LAGER_QT_READER(DoubleSpinBoxState, cviRedYState);
+    LAGER_QT_CURSOR(double, cviGreenX);
+    LAGER_QT_CURSOR(double, cviGreenY);
+    LAGER_QT_READER(DoubleSpinBoxState, cviGreenXState);
+    LAGER_QT_READER(DoubleSpinBoxState, cviGreenYState);
+    LAGER_QT_CURSOR(double, cviBlueX);
+    LAGER_QT_CURSOR(double, cviBlueY);
+    LAGER_QT_READER(DoubleSpinBoxState, cviBlueXState);
+    LAGER_QT_READER(DoubleSpinBoxState, cviBlueYState);
+
+    LAGER_QT_CURSOR(double, cviMaxLuminance);
+    LAGER_QT_READER(DoubleSpinBoxState, cviMaxLuminanceState);
+    LAGER_QT_CURSOR(double, cviMinLuminance);
+    LAGER_QT_READER(DoubleSpinBoxState, cviMinLuminanceState);
+
+
+    void setClli(const std::optional<KisRelativeContentLightLevelInformation> &clli);
+    std::optional<KisRelativeContentLightLevelInformation> clli() const;
+    void setCvi(const std::optional<KisColorVolumeInformation> &cvi);
+    std::optional<KisColorVolumeInformation> cvi() const;
+    void setRefWhite(const std::optional<double> &refWhite);
+    std::optional<double> refWhite() const;
+    void setImageProfileRelative(const std::optional<double> &refWhite);
+
+};
+
+#endif // KIS_HDR_METADATA_MODELS_H
diff --git a/libs/ui/animation/KisHDRMetadataOptions.cpp b/libs/ui/animation/KisHDRMetadataOptions.cpp
index a3fd7fe53a6..fd629b2fa4a 100644
--- a/libs/ui/animation/KisHDRMetadataOptions.cpp
+++ b/libs/ui/animation/KisHDRMetadataOptions.cpp
@@ -20,17 +20,17 @@ KisPropertiesConfigurationSP KisHDRMetadataOptions::toProperties() const
 
     config->setProperty("predefinedMasterDisplayId", predefinedMasterDisplayId);
 
-    config->setProperty("redX", cvi.red.x());
-    config->setProperty("redY", cvi.red.y());
+    config->setProperty("redX", cvi.red.x);
+    config->setProperty("redY", cvi.red.y);
 
-    config->setProperty("greenX", cvi.green.x());
-    config->setProperty("greenY", cvi.green.y());
+    config->setProperty("greenX", cvi.green.x);
+    config->setProperty("greenY", cvi.green.y);
 
-    config->setProperty("blueX", cvi.blue.x());
-    config->setProperty("blueY", cvi.blue.y());
+    config->setProperty("blueX", cvi.blue.x);
+    config->setProperty("blueY", cvi.blue.y);
 
-    config->setProperty("whiteX", cvi.white.x());
-    config->setProperty("whiteY", cvi.white.y());
+    config->setProperty("whiteX", cvi.white.x);
+    config->setProperty("whiteY", cvi.white.y);
 
     config->setProperty("minLuminance", cvi.maxLuminance);
     config->setProperty("maxLuminance", cvi.minLuminance);
@@ -45,17 +45,17 @@ void KisHDRMetadataOptions::fromProperties(KisPropertiesConfigurationSP config)
 {
     predefinedMasterDisplayId = config->getPropertyLazy("predefinedMasterDisplayId", predefinedMasterDisplayId);
 
-    cvi.red.setX(config->getPropertyLazy("redX", cvi.red.x()));
-    cvi.red.setY(config->getPropertyLazy("redX", cvi.red.y()));
+    cvi.red.x = config->getPropertyLazy("redX", cvi.red.x);
+    cvi.red.y = config->getPropertyLazy("redX", cvi.red.y);
 
-    cvi.green.setX(config->getPropertyLazy("greenX", cvi.green.x()));
-    cvi.green.setY(config->getPropertyLazy("greenY", cvi.green.y()));
+    cvi.green.x = (config->getPropertyLazy("greenX", cvi.green.x));
+    cvi.green.y = (config->getPropertyLazy("greenY", cvi.green.y));
 
-    cvi.blue.setX(config->getPropertyLazy("blueX", cvi.blue.x()));
-    cvi.blue.setY(config->getPropertyLazy("blueY", cvi.blue.y()));
+    cvi.blue.x = (config->getPropertyLazy("blueX", cvi.blue.x));
+    cvi.blue.y = (config->getPropertyLazy("blueY", cvi.blue.y));
 
-    cvi.white.setX(config->getPropertyLazy("whiteX", cvi.white.x()));
-    cvi.white.setY(config->getPropertyLazy("whiteY", cvi.white.y()));
+    cvi.white.x = (config->getPropertyLazy("whiteX", cvi.white.x));
+    cvi.white.y = (config->getPropertyLazy("whiteY", cvi.white.y));
 
     cvi.minLuminance = config->getPropertyLazy("minLuminance", cvi.minLuminance);
     cvi.maxLuminance = config->getPropertyLazy("maxLuminance", cvi.maxLuminance);
@@ -73,10 +73,11 @@ QString KisHDRMetadataOptions::generateFFMpegOptions() const
             QString("-x265-params ");
 
     if (cvi != KisColorVolumeInformation()) {
-        x265Params += QString("master-display=G(%3,%4)B(%5,%6)R(%1,%2)WP(%7,%8)L(%9,%10):")            .arg(cprim(cvi.red.x())).arg(cprim(cvi.red.y()))
-            .arg(cprim(cvi.green.x())).arg(cprim(cvi.green.y()))
-            .arg(cprim(cvi.blue.x())).arg(cprim(cvi.blue.y()))
-            .arg(cprim(cvi.white.x())).arg(cprim(cvi.white.y()))
+        x265Params += QString("master-display=G(%3,%4)B(%5,%6)R(%1,%2)WP(%7,%8)L(%9,%10):")
+            .arg(cprim(cvi.red.x)).arg(cprim(cvi.red.y))
+            .arg(cprim(cvi.green.x)).arg(cprim(cvi.green.y))
+            .arg(cprim(cvi.blue.x)).arg(cprim(cvi.blue.y))
+            .arg(cprim(cvi.white.x)).arg(cprim(cvi.white.y))
             .arg(lum(cvi.maxLuminance)).arg(lum(cvi.minLuminance));
     }
     if (clli != KisRelativeContentLightLevelInformation()) {
diff --git a/libs/ui/dialogs/kis_dlg_image_properties.cc b/libs/ui/dialogs/kis_dlg_image_properties.cc
index 76657799144..3d2e2e5f05d 100644
--- a/libs/ui/dialogs/kis_dlg_image_properties.cc
+++ b/libs/ui/dialogs/kis_dlg_image_properties.cc
@@ -20,6 +20,7 @@
 #include <kis_signal_compressor.h>
 #include <kis_image_config.h>
 #include "kis_hdr_metadata.h"
+#include <kis_hdr_metadata_models.h>
 #include "kis_layer_utils.h"
 #include <kis_display_color_converter.h>
 #include <KisWidgetConnectionUtils.h>
@@ -53,6 +54,7 @@ struct KisDlgImageProperties::Private {
     KisSignalCompressor compressor ;
     KisDisplayColorConverter *colorConverter;
     KisSignalCompressor colorVolumeCompressor;
+    KisHDRMetadataModel hdrMetadataModel;
 };
 
 KisDlgImageProperties::KisDlgImageProperties(KisImageWSP image, KisDisplayColorConverter *colorConverter, QWidget *parent, const char *name)
@@ -125,8 +127,9 @@ KisDlgImageProperties::KisDlgImageProperties(KisImageWSP image, KisDisplayColorC
             this,
             &KisDlgImageProperties::setProofingConfigToImage);
 
-    updateHDRLightLevels();
-    updateHDRColorVolume();
+    d->hdrMetadataModel.setClli(d->image->relativeContentLightLevelInformation());
+    d->hdrMetadataModel.setCvi(d->image->colorVolumeInformation());
+    d->hdrMetadataModel.setRefWhite(d->image->diffuseWhiteLightLevel());
     connect(m_page->btnCalculateClli, &QPushButton::clicked, this, &KisDlgImageProperties::slotCalculateLightLevels);
     connect(d->image, &KisImage::sigContentLightLevelInformationChanged, this, &KisDlgImageProperties::updateHDRLightLevels);
     connect(d->image, &KisImage::sigDiffuseWhiteLightLevelChanged, this, &KisDlgImageProperties::updateHDRDiffuseWhite);
@@ -135,37 +138,30 @@ KisDlgImageProperties::KisDlgImageProperties(KisImageWSP image, KisDisplayColorC
     m_page->cmbDiffuseWhite->addItem(i18n("80 cd/m²"), 80.0);
     m_page->cmbDiffuseWhite->addItem(i18n("203 cd/m²"), 203.0);
 
-    connect(m_page->gbxDiffuseWhite, &QGroupBox::clicked, this, &KisDlgImageProperties::setHDRDiffuseWhiteOnImage);
-    connect(m_page->cmbDiffuseWhite, SIGNAL(activated(int)), this, SLOT(setHDRDiffuseWhiteOnImage()));
-
-    connect(m_page->gbxContentLightLevel, &QGroupBox::clicked, this, &KisDlgImageProperties::setHDRLightLevelsOnImage);
-    connect(m_page->spnMaxCll, &QDoubleSpinBox::valueChanged, this, &KisDlgImageProperties::setHDRLightLevelsOnImage);
-    connect(m_page->spnMaxFall, &QDoubleSpinBox::valueChanged, this, &KisDlgImageProperties::setHDRLightLevelsOnImage);
-
     m_page->cmbColorVolumePresets->addItem(i18n("Rec. 2100 PQ"), "p2100-pq");
     m_page->cmbColorVolumePresets->addItem(i18n("DCI-P3 D65"), "dci-p3-d65");
 
-    m_page->cmbLumiCalcType->addItem(i18n("XYZ Luminance"), KisRelativeContentLightLevelInformation::XYZLuminance);
-    m_page->cmbLumiCalcType->addItem(i18n("Rec 2020 Per Component"), KisRelativeContentLightLevelInformation::Rec2020Component);
-    m_page->cmbLumiCalcType->addItem(i18n("RGB Per Component"), KisRelativeContentLightLevelInformation::RGBComponent);
 
-    m_page->cmbLumiCalcType->setItemData(0, i18nc("@tooltip", "Calculate the brightness in nits against XYZ luminance."), Qt::ToolTipRole);
-    m_page->cmbLumiCalcType->setItemData(1, i18nc("@tooltip", "Calculate the brightness in nits by testing the components in linear rec 2020."), Qt::ToolTipRole);
-    m_page->cmbLumiCalcType->setItemData(2, i18nc("@tooltip", "Calculate the brightness in nits by testing the components in linear RGB, if possible, falls back to using XYZ luminance."), Qt::ToolTipRole);
+    connect(m_page->gbxDiffuseWhite, &QGroupBox::clicked, this, &KisDlgImageProperties::setHDRDiffuseWhiteOnImage);
+    connect(m_page->cmbDiffuseWhite, SIGNAL(activated(int)), this, SLOT(setHDRDiffuseWhiteOnImage()));
 
+    KisWidgetConnectionUtils::connectControlState( m_page->gbxContentLightLevel,  &d->hdrMetadataModel, "clliEnabledState", "clliEnabled");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnMaxCll,  &d->hdrMetadataModel, "maxContentLightLevelState", "maxContentLightLevel");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnMaxFall,  &d->hdrMetadataModel, "maxFrameAverageLightLevelState", "maxFrameAverageLightLevel");
+    KisWidgetConnectionUtils::connectControlState( m_page->cmbLumiCalcType,  &d->hdrMetadataModel, "clliCalculationTypeState", "clliCalculationType");
+
+    KisWidgetConnectionUtils::connectControlState( m_page->gbxColorVolume, &d->hdrMetadataModel, "cviEnabledState", "cviEnabled");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnWhiteX, &d->hdrMetadataModel, "cviWhiteXState", "cviWhiteX");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnWhiteY, &d->hdrMetadataModel, "cviWhiteYState", "cviWhiteY");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnRedX, &d->hdrMetadataModel, "cviRedXState", "cviRedX");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnRedY, &d->hdrMetadataModel, "cviRedYState", "cviRedY");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnGreenX, &d->hdrMetadataModel, "cviGreenXState", "cviGreenX");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnGreenY, &d->hdrMetadataModel, "cviGreenYState", "cviGreenY");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnBlueX, &d->hdrMetadataModel, "cviBlueXState", "cviBlueX");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnBlueY, &d->hdrMetadataModel, "cviBlueYState", "cviBlueY");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnMinLuminance, &d->hdrMetadataModel, "cviMinLuminanceState", "cviMinLuminance");
+    KisWidgetConnectionUtils::connectControlState( m_page->spnMaxLuminance, &d->hdrMetadataModel, "cviMaxLuminanceState", "cviMaxLuminance");
     connect(m_page->cmbColorVolumePresets, SIGNAL(activated(int)), this, SLOT(changeColorVolumePreset()));
-    connect(m_page->gbxColorVolume, &QGroupBox::clicked, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnWhiteX, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnWhiteY, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnRedX, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnRedY, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnGreenX, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnGreenY, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnBlueX, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnBlueX, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnMinLuminance, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(m_page->spnMaxLuminance, &QDoubleSpinBox::valueChanged, &d->colorVolumeCompressor, &KisSignalCompressor::start);
-    connect(&d->colorVolumeCompressor, &KisSignalCompressor::timeout, this, &KisDlgImageProperties::setHDRColorVolumeOnImage);
 
     const bool hdr = (d->image->colorSpace()->hasHighDynamicRange()
                       || d->image->colorSpace()->profile()->getTransferCharacteristics() == TRC_ITU_R_BT_2100_0_PQ);
@@ -218,6 +214,9 @@ int KisDlgImageProperties::exec()
         } else {
             d->image->setProofingConfiguration(nullptr);
         }
+        setHDRDiffuseWhiteOnImage();
+        setHDRLightLevelsOnImage();
+        setHDRColorVolumeOnImage();
     } else {
         d->image->setProofingConfiguration(d->originalProofingConfig);
     }
@@ -261,71 +260,23 @@ void KisDlgImageProperties::updateDisplayConfigInfo()
 
 void KisDlgImageProperties::updateHDRDiffuseWhite()
 {
-    if (d->image->diffuseWhiteLightLevel()) {
-        m_page->gbxDiffuseWhite->setChecked(true);
-        m_page->cmbDiffuseWhite->setCurrentIndex(m_page->cmbDiffuseWhite->findData(*d->image->diffuseWhiteLightLevel()));
-        if (d->image->colorSpace()->profile()->hdrReferenceWhite()) {
-            m_page->gbxDiffuseWhite->setEnabled(false);
-            m_page->cmbDiffuseWhite->setEnabled(false);
-        }
-
-    } else {
-        m_page->gbxDiffuseWhite->setChecked(false);
-        m_page->cmbDiffuseWhite->setCurrentIndex(0);
-    }
+    d->hdrMetadataModel.setRefWhite(d->image->diffuseWhiteLightLevel());
 }
 
 void KisDlgImageProperties::updateHDRLightLevels()
 {
-    if (d->image->relativeContentLightLevelInformation()) {
-        m_page->gbxContentLightLevel->setChecked(true);
-        double diffuseWhite = d->image->diffuseWhiteLightLevel()? *d->image->diffuseWhiteLightLevel(): 80.0;
-        KisRelativeContentLightLevelInformation clli = *d->image->relativeContentLightLevelInformation();
-        m_page->spnMaxCll->setValue(clli.maxContentLightLevel*diffuseWhite);
-        m_page->spnMaxFall->setValue(clli.maxFrameAverageLightLevel*diffuseWhite);
-        m_page->cmbLumiCalcType->setCurrentIndex(clli.type);
-    } else {
-        m_page->gbxContentLightLevel->setChecked(false);
-    }
+    d->hdrMetadataModel.setClli(d->image->relativeContentLightLevelInformation());
 }
 
 void KisDlgImageProperties::updateHDRColorVolume()
 {
-    if (d->image->colorVolumeInformation()) {
-        m_page->gbxColorVolume->setChecked(true);
-        KisColorVolumeInformation cvi = *d->image->colorVolumeInformation();
-        m_page->spnWhiteX->setValue(cvi.white.x());
-        m_page->spnWhiteY->setValue(cvi.white.y());
-
-        m_page->spnRedX->setValue(cvi.red.x());
-        m_page->spnRedY->setValue(cvi.red.y());
-
-        m_page->spnGreenX->setValue(cvi.green.x());
-        m_page->spnGreenY->setValue(cvi.green.y());
-
-        m_page->spnBlueX->setValue(cvi.blue.x());
-        m_page->spnBlueY->setValue(cvi.blue.y());
-
-        m_page->spnMaxLuminance->setValue(cvi.maxLuminance);
-        m_page->spnMinLuminance->setValue(cvi.minLuminance);
-
-    } else {
-        m_page->gbxColorVolume->setChecked(false);
-    }
+    d->hdrMetadataModel.setCvi(d->image->colorVolumeInformation());
 }
 
 void KisDlgImageProperties::setHDRLightLevelsOnImage()
 {
-    std::optional<KisRelativeContentLightLevelInformation> optClli = std::nullopt;
-
-    if (m_page->gbxContentLightLevel->isChecked()) {
-        double diffuseWhite = d->image->diffuseWhiteLightLevel().value_or(80.0);
-        KisRelativeContentLightLevelInformation clli;
-        clli.maxContentLightLevel = m_page->spnMaxCll->value() / diffuseWhite;
-        clli.maxFrameAverageLightLevel = m_page->spnMaxFall->value() / diffuseWhite;
-        clli.type = KisRelativeContentLightLevelInformation::CalculationType(m_page->cmbLumiCalcType->currentData().toInt());
-        optClli = std::make_optional(clli);
-    }
+    std::optional<KisRelativeContentLightLevelInformation> optClli = d->hdrMetadataModel.clli();
+
     if (d->image->relativeContentLightLevelInformation() != optClli) {
         KisProcessingApplicator::runSingleCommandStroke(d->image,
             new KisChangeImageHdrContentLightLevelCommand(d->image, optClli));
@@ -334,10 +285,7 @@ void KisDlgImageProperties::setHDRLightLevelsOnImage()
 
 void KisDlgImageProperties::setHDRDiffuseWhiteOnImage()
 {
-    std::optional<double> dw = std::nullopt;
-    if (m_page->gbxDiffuseWhite->isChecked()) {
-        dw = std::make_optional(m_page->cmbDiffuseWhite->currentData().toDouble());
-    }
+    std::optional<double> dw = d->hdrMetadataModel.refWhite();
     if (d->image->diffuseWhiteLightLevel() != dw) {
         KUndo2Command *cmd = new KisChangeImageHdrDiffuseWhiteCommand(d->image, dw);
         d->image->undoAdapter()->addCommand(cmd);
@@ -346,22 +294,7 @@ void KisDlgImageProperties::setHDRDiffuseWhiteOnImage()
 
 void KisDlgImageProperties::setHDRColorVolumeOnImage()
 {
-    std::optional<KisColorVolumeInformation> optCvi = std::nullopt;
-    if (m_page->gbxColorVolume->isChecked()) {
-        KisColorVolumeInformation cvi;
-
-        cvi.white = QPointF(m_page->spnWhiteX->value(), m_page->spnWhiteY->value());
-
-        cvi.red = QPointF(m_page->spnRedX->value(), m_page->spnRedY->value());
-        cvi.green = QPointF(m_page->spnGreenX->value(), m_page->spnGreenY->value());
-        cvi.blue = QPointF(m_page->spnBlueX->value(), m_page->spnBlueY->value());
-
-        cvi.maxLuminance = m_page->spnMaxLuminance->value();
-        cvi.minLuminance = m_page->spnMinLuminance->value();
-
-        optCvi = std::make_optional(cvi);
-
-    }
+    std::optional<KisColorVolumeInformation> optCvi = d->hdrMetadataModel.cvi();
     if (d->image->colorVolumeInformation() != optCvi) {
         KUndo2Command *cmd = new KisChangeImageHdrColorVolumeCommand(d->image, optCvi);
         d->image->undoAdapter()->addCommand(cmd);
@@ -411,7 +344,7 @@ void KisDlgImageProperties::changeColorVolumePreset()
 
 void KisDlgImageProperties::slotCalculateLightLevels()
 {
-    KisRelativeContentLightLevelInformation::CalculationType type = KisRelativeContentLightLevelInformation::CalculationType(m_page->cmbLumiCalcType->currentData().toInt());
+    KisRelativeContentLightLevelInformation::CalculationType type = d->hdrMetadataModel.clliCalculationType();
     KisSharedPtr<KisContentLightLevelProcessingVistor> visitor =
             new KisContentLightLevelProcessingVistor(type, d->image->bounds());
 
@@ -426,12 +359,10 @@ void KisDlgImageProperties::slotCalculateLightLevels()
     // has completed its execution
     QSharedPointer<FunctionToSignalProxy> completionSignalProxy(new FunctionToSignalProxy);
     connect(completionSignalProxy.get(), &FunctionToSignalProxy::timeout, this,
-        [visitor, image = d->image]() {
+        [visitor, model = &d->hdrMetadataModel, image = d->image]() {
             auto info = visitor->contentLightLevelInformation();
-            if (info != image->relativeContentLightLevelInformation()) {
-                KisProcessingApplicator::runSingleCommandStroke(image,
-                                                                new KisChangeImageHdrContentLightLevelCommand(image, info));
-            }
+
+            model->setClli(info);
         });
 
     // add the command **after** the signal has been set up; this
diff --git a/libs/widgets/KisWidgetConnectionUtils.cpp b/libs/widgets/KisWidgetConnectionUtils.cpp
index 0a2c16b5103..28d4c8036f9 100644
--- a/libs/widgets/KisWidgetConnectionUtils.cpp
+++ b/libs/widgets/KisWidgetConnectionUtils.cpp
@@ -17,6 +17,7 @@
 #include <QLineEdit>
 #include <QSlider>
 #include <QCheckBox>
+#include <QGroupBox>
 #include "kis_debug.h"
 #include "kis_spacing_selection_widget.h"
 #include "kis_multipliers_double_slider_spinbox.h"
@@ -63,6 +64,29 @@ private:
     QAbstractButton *m_button;
 };
 
+class ConnectGroupBoxStateHelper : public QObject
+{
+    Q_OBJECT
+public:
+
+    ConnectGroupBoxStateHelper(QGroupBox *parent)
+        : QObject(parent),
+        m_button(parent)
+    {
+    }
+public Q_SLOTS:
+    void updateState(const CheckBoxState &state) {
+        QSignalBlocker b(m_button);
+        m_button->setEnabled(state.enabled);
+        m_button->setChecked(state.value);
+
+               // TODO: verify if the two properties are equal or the control is disabled
+    }
+
+private:
+    QGroupBox *m_button;
+};
+
 class ConnectComboBoxStateHelper : public QObject
 {
     Q_OBJECT
@@ -494,6 +518,37 @@ void connectControlState(QAbstractButton *button, QObject *source, const char *r
     }
 }
 
+void connectControlState(QGroupBox *button, QObject *source, const char *readStatePropertyName, const char *writePropertyName)
+{
+    const QMetaObject* meta = source->metaObject();
+    QMetaProperty readStateProp = meta->property(meta->indexOfProperty(readStatePropertyName));
+    SANITY_CHECK_PROPERTY_EXISTS(readStateProp);
+
+    KIS_SAFE_ASSERT_RECOVER_RETURN(readStateProp.hasNotifySignal());
+
+    QMetaMethod signal = readStateProp.notifySignal();
+
+    KIS_SAFE_ASSERT_RECOVER_RETURN(signal.parameterCount() >= 1);
+    KIS_SAFE_ASSERT_RECOVER_RETURN(signal.parameterType(0) == QMetaType::type("CheckBoxState"));
+
+    ConnectGroupBoxStateHelper *helper = new ConnectGroupBoxStateHelper(button);
+
+    const QMetaObject* dstMeta = helper->metaObject();
+
+    QMetaMethod updateSlot = dstMeta->method(
+        dstMeta->indexOfSlot("updateState(CheckBoxState)"));
+    QObject::connect(source, signal, helper, updateSlot);
+
+    helper->updateState(readStateProp.read(source).value<CheckBoxState>());
+
+    QMetaProperty writeProp = meta->property(meta->indexOfProperty(writePropertyName));
+    SANITY_CHECK_PROPERTY_EXISTS_AND_WRITABLE(writeProp);
+    if (writeProp.isWritable()) {
+        button->connect(button, &QGroupBox::toggled,
+                        source, [writeProp, source] (bool value) { writeProp.write(source, value); });
+    }
+}
+
 
 void connectControlState(QComboBox *button, QObject *source, const char *readStatePropertyName, const char *writePropertyName)
 {
diff --git a/libs/widgets/KisWidgetConnectionUtils.h b/libs/widgets/KisWidgetConnectionUtils.h
index 0060140d511..b2113a75717 100644
--- a/libs/widgets/KisWidgetConnectionUtils.h
+++ b/libs/widgets/KisWidgetConnectionUtils.h
@@ -21,6 +21,7 @@ class QDoubleSpinBox;
 class QObject;
 class QSlider;
 class QCheckBox;
+class QGroupBox;
 class KisSpacingSelectionWidget;
 class KisAngleSelector;
 class KisColorButton;
@@ -148,6 +149,7 @@ void KRITAWIDGETS_EXPORT connectControl(QButtonGroup *button, QObject *source, c
 void KRITAWIDGETS_EXPORT connectControl(QComboBox *button, QObject *source, const char *property);
 void KRITAWIDGETS_EXPORT connectControl(QComboBox *button, QObject *source, const char *property);
 void KRITAWIDGETS_EXPORT connectControlState(QAbstractButton *button, QObject *source, const char *readStateProperty, const char *writeProperty);
+void KRITAWIDGETS_EXPORT connectControlState(QGroupBox *button, QObject *source, const char *readStateProperty, const char *writeProperty);
 void KRITAWIDGETS_EXPORT connectControlState(QButtonGroup *group, QObject *source, const char *readStateProperty, const char *writeProperty);
 void KRITAWIDGETS_EXPORT connectControlState(QComboBox *button, QObject *source, const char *readStateProperty, const char *writeProperty);
 void KRITAWIDGETS_EXPORT connectControl(KisSpacingSelectionWidget *widget, QObject *source, const char *property);
diff --git a/plugins/impex/libkra/kis_kra_loader.cpp b/plugins/impex/libkra/kis_kra_loader.cpp
index 43a73fa111c..43a90255737 100644
--- a/plugins/impex/libkra/kis_kra_loader.cpp
+++ b/plugins/impex/libkra/kis_kra_loader.cpp
@@ -1559,8 +1559,8 @@ void KisKraLoader::loadHDRMetadata(const QDomElement &elem, KisImageSP image)
         }
         if (child.tagName() == CONTENTLIGHTLEVEL) {
             KisRelativeContentLightLevelInformation clli;
-            clli.maxContentLightLevel = child.attribute(MAXCLL, "0.0").toDouble();
-            clli.maxFrameAverageLightLevel = child.attribute(MAXFALL, "0.0").toDouble();
+            clli.maxContentLightLevel = KisDomUtils::toDouble(child.attribute(MAXCLL, "0.0"));
+            clli.maxFrameAverageLightLevel = KisDomUtils::toDouble(child.attribute(MAXFALL, "0.0"));
             const QString type = child.attribute(CLLI_CALCTYPE);
             if (type == CLLI_CALC_RGB_XYZ_FALLBACK) {
                 clli.type = KisRelativeContentLightLevelInformation::RGBComponent;
@@ -1574,17 +1574,32 @@ void KisKraLoader::loadHDRMetadata(const QDomElement &elem, KisImageSP image)
         }
         if (child.tagName() == COLORVOLUME) {
             KisColorVolumeInformation cv;
-            cv.maxLuminance = child.attribute(COLORVOLUMEMAXLUMINANCE, "0.0").toDouble();
-            cv.minLuminance = child.attribute(COLORVOLUMEMINLUMINANCE, "0.0").toDouble();
+            //TODO: Kisdomutils!!!
+            cv.maxLuminance = KisDomUtils::toDouble(child.attribute(COLORVOLUMEMAXLUMINANCE, "0.0"));
+            cv.minLuminance = KisDomUtils::toDouble(child.attribute(COLORVOLUMEMINLUMINANCE, "0.0"));
 
             QDomElement red = child.firstChildElement(COLORVOLUMERED);
             QDomElement green = child.firstChildElement(COLORVOLUMEGREEN);
             QDomElement blue = child.firstChildElement(COLORVOLUMEBLUE);
             QDomElement white = child.firstChildElement(COLORVOLUMEWHITE);
-            KisDomUtils::loadValue(red, &cv.red);
-            KisDomUtils::loadValue(green, &cv.green);
-            KisDomUtils::loadValue(blue, &cv.blue);
-            KisDomUtils::loadValue(white, &cv.white);
+
+            QPointF temp;
+            if (KisDomUtils::loadValue(red, &temp)) {
+                cv.red.x = temp.x();
+                cv.red.y = temp.y();
+            }
+            if (KisDomUtils::loadValue(green, &temp)) {
+                cv.green.x = temp.x();
+                cv.green.y = temp.y();
+            }
+            if (KisDomUtils::loadValue(blue, &temp)) {
+                cv.blue.x = temp.x();
+                cv.blue.y = temp.y();
+            }
+            if (KisDomUtils::loadValue(white, &temp)) {
+                cv.white.x = temp.x();
+                cv.white.y = temp.y();
+            }
             std::optional<KisColorVolumeInformation> cvi = std::make_optional(cv);
             image->setColorVolumeInformation(cvi);
         }
diff --git a/plugins/impex/libkra/kis_kra_saver.cpp b/plugins/impex/libkra/kis_kra_saver.cpp
index eccd099e00a..98d8bb45d65 100644
--- a/plugins/impex/libkra/kis_kra_saver.cpp
+++ b/plugins/impex/libkra/kis_kra_saver.cpp
@@ -732,10 +732,10 @@ void KisKraSaver::saveHDRMetadata(QDomDocument &doc, QDomElement &element, KisIm
         cvi.setAttribute(COLORVOLUMEMAXLUMINANCE, KisDomUtils::toString(cv.maxLuminance));
         cvi.setAttribute(COLORVOLUMEMINLUMINANCE, KisDomUtils::toString(cv.minLuminance));
 
-        KisDomUtils::saveValue(&cvi, COLORVOLUMERED, cv.red);
-        KisDomUtils::saveValue(&cvi, COLORVOLUMEGREEN, cv.green);
-        KisDomUtils::saveValue(&cvi, COLORVOLUMEBLUE, cv.blue);
-        KisDomUtils::saveValue(&cvi, COLORVOLUMEWHITE, cv.white);
+        KisDomUtils::saveValue(&cvi, COLORVOLUMERED, cv.red.asVector().toPointF());
+        KisDomUtils::saveValue(&cvi, COLORVOLUMEGREEN, cv.green.asVector().toPointF());
+        KisDomUtils::saveValue(&cvi, COLORVOLUMEBLUE, cv.blue.asVector().toPointF());
+        KisDomUtils::saveValue(&cvi, COLORVOLUMEWHITE, cv.white.asVector().toPointF());
 
         hdr.appendChild(cvi);
     }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.