Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,8 @@ pkg_check_modules(
gbm
gio-2.0
re2
muparser)
muparser
lcms2)

find_package(hyprwayland-scanner 0.3.10 REQUIRED)

Expand Down
2 changes: 1 addition & 1 deletion src/Compositor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "managers/KeybindManager.hpp"
#include "managers/SessionLockManager.hpp"
#include "desktop/view/Window.hpp"
#include "protocols/types/ColorManagement.hpp"
#include "helpers/cm/ColorManagement.hpp"

#include <aquamarine/backend/Backend.hpp>
#include <aquamarine/output/Output.hpp>
Expand Down
17 changes: 17 additions & 0 deletions src/config/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ CConfigManager::CConfigManager() {
m_config->addSpecialConfigValue("monitorv2", "min_luminance", Hyprlang::FLOAT{-1.0});
m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1});
m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1});
m_config->addSpecialConfigValue("monitorv2", "icc", Hyprlang::STRING{""});

// windowrule v3
m_config->addSpecialCategory("windowrule", {.key = "name"});
Expand Down Expand Up @@ -1213,6 +1214,10 @@ std::optional<std::string> CConfigManager::handleMonitorv2(const std::string& ou
if (VAL && VAL->m_bSetByUser)
parser.rule().maxAvgLuminance = std::any_cast<Hyprlang::INT>(VAL->getValue());

VAL = m_config->getSpecialConfigValuePtr("monitorv2", "icc", output.c_str());
if (VAL && VAL->m_bSetByUser)
parser.rule().iccFile = std::any_cast<Hyprlang::STRING>(VAL->getValue());

auto newrule = parser.rule();

std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; });
Expand Down Expand Up @@ -2210,6 +2215,15 @@ bool CMonitorRuleParser::parseVRR(const std::string& value) {
return true;
}

bool CMonitorRuleParser::parseICC(const std::string& val) {
if (val.empty()) {
m_error += "invalid icc ";
return false;
}
m_rule.iccFile = val;
return true;
}

void CMonitorRuleParser::setDisabled() {
m_rule.disabled = true;
}
Expand Down Expand Up @@ -2304,6 +2318,9 @@ std::optional<std::string> CConfigManager::handleMonitor(const std::string& comm
} else if (ARGS[argno] == "vrr") {
parser.parseVRR(std::string(ARGS[argno + 1]));
argno++;
} else if (ARGS[argno] == "icc") {
parser.parseICC(std::string(ARGS[argno + 1]));
argno++;
} else if (ARGS[argno] == "workspace") {
const auto& [id, name, isAutoID] = getWorkspaceIDNameFromString(std::string(ARGS[argno + 1]));

Expand Down
1 change: 1 addition & 0 deletions src/config/ConfigManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ class CMonitorRuleParser {
bool parseSDRBrightness(const std::string& value);
bool parseSDRSaturation(const std::string& value);
bool parseVRR(const std::string& value);
bool parseICC(const std::string& value);

void setDisabled();
void setMirror(const std::string& value);
Expand Down
48 changes: 28 additions & 20 deletions src/helpers/Monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -877,28 +877,39 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
m_supportsWideColor = RULE->supportsHDR;
m_supportsHDR = RULE->supportsHDR;

m_cmType = RULE->cmType;
switch (m_cmType) {
case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break;
case NCMType::CM_EDID: m_cmType = m_output->parsedEDID.chromaticityCoords.has_value() ? NCMType::CM_EDID : NCMType::CM_SRGB; break;
case NCMType::CM_HDR:
case NCMType::CM_HDR_EDID: m_cmType = supportsHDR() ? m_cmType : NCMType::CM_SRGB; break;
default: break;
}
if (RULE->iccFile.empty()) {
// only apply explicit cm settings if we have no icc file

m_cmType = RULE->cmType;
switch (m_cmType) {
case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break;
case NCMType::CM_EDID: m_cmType = m_output->parsedEDID.chromaticityCoords.has_value() ? NCMType::CM_EDID : NCMType::CM_SRGB; break;
case NCMType::CM_HDR:
case NCMType::CM_HDR_EDID: m_cmType = supportsHDR() ? m_cmType : NCMType::CM_SRGB; break;
default: break;
}

m_sdrEotf = RULE->sdrEotf;
m_sdrEotf = RULE->sdrEotf;

m_sdrMinLuminance = RULE->sdrMinLuminance;
m_sdrMaxLuminance = RULE->sdrMaxLuminance;
m_sdrMinLuminance = RULE->sdrMinLuminance;
m_sdrMaxLuminance = RULE->sdrMaxLuminance;

m_minLuminance = RULE->minLuminance;
m_maxLuminance = RULE->maxLuminance;
m_maxAvgLuminance = RULE->maxAvgLuminance;
m_minLuminance = RULE->minLuminance;
m_maxLuminance = RULE->maxLuminance;
m_maxAvgLuminance = RULE->maxAvgLuminance;

applyCMType(m_cmType, m_sdrEotf);
applyCMType(m_cmType, m_sdrEotf);

m_sdrSaturation = RULE->sdrSaturation;
m_sdrBrightness = RULE->sdrBrightness;
m_sdrSaturation = RULE->sdrSaturation;
m_sdrBrightness = RULE->sdrBrightness;
} else {
auto image = NColorManagement::SImageDescription::fromICC(RULE->iccFile);
if (!image) {
Log::logger->log(Log::ERR, "icc for {} ({}) failed: {}", m_name, RULE->iccFile, image.error());
g_pConfigManager->addParseError(std::format("failed to apply icc {} to {}: {}", RULE->iccFile, m_name, image.error()));
} else
m_imageDescription = *image;
}

Vector2D logicalSize = m_pixelSize / m_scale;
if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) {
Expand Down Expand Up @@ -2045,9 +2056,6 @@ 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)
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 ||
Expand Down
3 changes: 2 additions & 1 deletion src/helpers/Monitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include "math/Math.hpp"
#include "../desktop/reserved/ReservedArea.hpp"
#include <optional>
#include "../protocols/types/ColorManagement.hpp"
#include "cm/ColorManagement.hpp"
#include "signal/Signal.hpp"
#include "DamageRing.hpp"
#include <aquamarine/output/Output.hpp>
Expand Down Expand Up @@ -54,6 +54,7 @@ struct SMonitorRule {
float sdrSaturation = 1.0f; // SDR -> HDR
float sdrBrightness = 1.0f; // SDR -> HDR
Desktop::CReservedArea reservedArea;
std::string iccFile;

bool supportsWideColor = false; // false does nothing, true overrides EDID
bool supportsHDR = false; // false does nothing, true overrides EDID
Expand Down
48 changes: 48 additions & 0 deletions src/helpers/cm/ColorManagement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "ColorManagement.hpp"
#include <map>

namespace NColorManagement {
static uint32_t lastImageID = 0;
static std::map<uint32_t, SImageDescription> knownDescriptionIds; // expected to be small
}

using namespace NColorManagement;

const SPCPRimaries& NColorManagement::getPrimaries(ePrimaries name) {
switch (name) {
case CM_PRIMARIES_SRGB: return NColorPrimaries::BT709;
case CM_PRIMARIES_BT2020: return NColorPrimaries::BT2020;
case CM_PRIMARIES_PAL_M: return NColorPrimaries::PAL_M;
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_DCI_P3: return NColorPrimaries::DCI_P3;
case CM_PRIMARIES_DISPLAY_P3: return NColorPrimaries::DISPLAY_P3;
case CM_PRIMARIES_ADOBE_RGB: return NColorPrimaries::ADOBE_RGB;
default: return NColorPrimaries::DEFAULT_PRIMARIES;
}
}

// TODO make image descriptions immutable and always set an id

uint32_t SImageDescription::findId() const {
for (auto it = knownDescriptionIds.begin(); it != knownDescriptionIds.end(); ++it) {
if (it->second == *this)
return it->first;
}

const auto newId = ++lastImageID;
knownDescriptionIds.insert(std::make_pair(newId, *this));
return newId;
}

uint32_t SImageDescription::getId() const {
return id > 0 ? id : findId();
}

uint32_t SImageDescription::updateId() {
id = 0;
id = findId();
return id;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#include <hyprgraphics/color/Color.hpp>
#include "../../helpers/memory/Memory.hpp"

#include <filesystem>
#include <vector>
#include <expected>

#define SDR_MIN_LUMINANCE 0.2
#define SDR_MAX_LUMINANCE 80.0
#define SDR_REF_LUMINANCE 80.0
Expand All @@ -12,6 +16,8 @@
#define HDR_REF_LUMINANCE 203.0
#define HLG_MAX_LUMINANCE 1000.0

class CTexture;

namespace NColorManagement {
enum eNoShader : uint8_t {
CM_NS_DISABLE = 0,
Expand Down Expand Up @@ -132,24 +138,20 @@ namespace NColorManagement {
const SPCPRimaries& getPrimaries(ePrimaries name);

struct SImageDescription {
uint32_t id = 0; // FIXME needs id setting
static std::expected<SImageDescription, std::string> fromICC(const std::filesystem::path& file);

struct SIccFile {
int fd = -1;
uint32_t length = 0;
uint32_t offset = 0;
bool operator==(const SIccFile& i2) const {
return fd == i2.fd;
}
} icc;
//
uint32_t id = 0; // FIXME needs id setting

bool windowsScRGB = false;
std::vector<uint8_t> rawICC;

eTransferFunction transferFunction = CM_TRANSFER_FUNCTION_SRGB;
float transferFunctionPower = 1.0f;
bool windowsScRGB = false;

bool primariesNameSet = false;
ePrimaries primariesNamed = CM_PRIMARIES_SRGB;
eTransferFunction transferFunction = CM_TRANSFER_FUNCTION_SRGB;
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
Expand All @@ -175,12 +177,19 @@ namespace NColorManagement {
}
} masteringLuminances;

// LUT ramps replace destTF. These are used for ICC.
// These are INVERTED, ready for linear -> dest
struct SLUTRamps {
bool present = false;
SP<CTexture> r, g, b;
} lutRamps;

uint32_t maxCLL = 0;
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 &&
(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);
Expand Down
Loading
Loading