mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-21 06:42:02 +00:00
[client] wayland: separate PQ and mastering volumes
Describe PQ pixels with the protocol-defined primary luminance range and keep mastering luminance in the target volume. Track extended target-volume support and omit unsupported metadata instead of creating an invalid image description.
This commit is contained in:
@@ -45,6 +45,9 @@ static void cmFeature(void * data, struct wp_color_manager_v1 * cm, uint32_t fea
|
|||||||
case WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES:
|
case WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES:
|
||||||
wlWm.cmHasMasteringPrimaries = true;
|
wlWm.cmHasMasteringPrimaries = true;
|
||||||
break;
|
break;
|
||||||
|
case WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME:
|
||||||
|
wlWm.cmHasExtendedTargetVolume = true;
|
||||||
|
break;
|
||||||
case WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB:
|
case WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB:
|
||||||
wlWm.cmHasWindowsSCRGB = true;
|
wlWm.cmHasWindowsSCRGB = true;
|
||||||
break;
|
break;
|
||||||
@@ -79,11 +82,12 @@ static void cmDone(void * data, struct wp_color_manager_v1 * cm)
|
|||||||
memory_order_relaxed);
|
memory_order_relaxed);
|
||||||
atomic_store_explicit(&wlWm.cmFeaturesDone, true, memory_order_release);
|
atomic_store_explicit(&wlWm.cmFeaturesDone, true, memory_order_release);
|
||||||
DEBUG_INFO("Color management features: parametric:%d luminances:%d "
|
DEBUG_INFO("Color management features: parametric:%d luminances:%d "
|
||||||
"mastering_primaries:%d st2084_pq:%d ext_linear:%d bt2020:%d srgb:%d "
|
"mastering_primaries:%d extended_target:%d st2084_pq:%d "
|
||||||
"windows_scrgb:%d perceptual:%d "
|
"ext_linear:%d bt2020:%d srgb:%d windows_scrgb:%d perceptual:%d "
|
||||||
"can_do_hdr:%d",
|
"can_do_hdr:%d",
|
||||||
wlWm.cmHasParametric, wlWm.cmHasLuminances,
|
wlWm.cmHasParametric, wlWm.cmHasLuminances,
|
||||||
wlWm.cmHasMasteringPrimaries,
|
wlWm.cmHasMasteringPrimaries,
|
||||||
|
wlWm.cmHasExtendedTargetVolume,
|
||||||
wlWm.cmHasTFSt2084PQ, wlWm.cmHasTFExtLinear,
|
wlWm.cmHasTFSt2084PQ, wlWm.cmHasTFExtLinear,
|
||||||
wlWm.cmHasPrimariesBT2020, wlWm.cmHasPrimariesSRGB,
|
wlWm.cmHasPrimariesBT2020, wlWm.cmHasPrimariesSRGB,
|
||||||
wlWm.cmHasWindowsSCRGB, wlWm.cmHasPerceptualIntent,
|
wlWm.cmHasWindowsSCRGB, wlWm.cmHasPerceptualIntent,
|
||||||
|
|||||||
@@ -312,6 +312,72 @@ EGLNativeWindowType waylandGetEGLNativeWindow(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// HDR color management support
|
// HDR color management support
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
HDR_PQ_MIN_LUMINANCE = 50,
|
||||||
|
HDR_PQ_MAX_LUMINANCE = 10000,
|
||||||
|
HDR_PQ_DEFAULT_WHITE_LEVEL = 203,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int64_t hdrTriangleEdge(int32_t ax, int32_t ay,
|
||||||
|
int32_t bx, int32_t by, int32_t px, int32_t py)
|
||||||
|
{
|
||||||
|
return (int64_t)(px - ax) * (by - ay) -
|
||||||
|
(int64_t)(py - ay) * (bx - ax);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hdrPointInBT2020(int32_t x, int32_t y)
|
||||||
|
{
|
||||||
|
// BT.2020 primaries in the KVMFR/DXGI scale of 50,000 units.
|
||||||
|
static const int32_t primary[3][2] =
|
||||||
|
{
|
||||||
|
{ 35400, 14600 },
|
||||||
|
{ 8500, 39850 },
|
||||||
|
{ 6550, 2300 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const int64_t edge0 = hdrTriangleEdge(
|
||||||
|
primary[0][0], primary[0][1], primary[1][0], primary[1][1], x, y);
|
||||||
|
const int64_t edge1 = hdrTriangleEdge(
|
||||||
|
primary[1][0], primary[1][1], primary[2][0], primary[2][1], x, y);
|
||||||
|
const int64_t edge2 = hdrTriangleEdge(
|
||||||
|
primary[2][0], primary[2][1], primary[0][0], primary[0][1], x, y);
|
||||||
|
return (edge0 >= 0 && edge1 >= 0 && edge2 >= 0) ||
|
||||||
|
(edge0 <= 0 && edge1 <= 0 && edge2 <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hdrChromaticitiesValid(const uint16_t primary[3][2],
|
||||||
|
const uint16_t whitePoint[2])
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < 3; ++i)
|
||||||
|
if (primary[i][1] == 0 || primary[i][0] > 50000 ||
|
||||||
|
primary[i][1] > 50000 ||
|
||||||
|
(uint32_t)primary[i][0] + primary[i][1] > 50000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (whitePoint[1] == 0 || whitePoint[0] > 50000 ||
|
||||||
|
whitePoint[1] > 50000 ||
|
||||||
|
(uint32_t)whitePoint[0] + whitePoint[1] > 50000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return hdrTriangleEdge(
|
||||||
|
primary[0][0], primary[0][1], primary[1][0], primary[1][1],
|
||||||
|
primary[2][0], primary[2][1]) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hdrTargetVolumeContained(const uint16_t primary[3][2],
|
||||||
|
const uint16_t whitePoint[2], uint32_t minLuminance,
|
||||||
|
uint32_t maxLuminance)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < 3; ++i)
|
||||||
|
if (!hdrPointInBT2020(primary[i][0], primary[i][1]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return hdrPointInBT2020(whitePoint[0], whitePoint[1]) &&
|
||||||
|
minLuminance >= HDR_PQ_MIN_LUMINANCE &&
|
||||||
|
maxLuminance <= HDR_PQ_MAX_LUMINANCE;
|
||||||
|
}
|
||||||
|
|
||||||
void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2],
|
void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2],
|
||||||
const uint16_t whitePoint[2], uint32_t maxDisplayLuminance,
|
const uint16_t whitePoint[2], uint32_t maxDisplayLuminance,
|
||||||
uint32_t minDisplayLuminance, uint32_t maxCLL, uint32_t maxFALL,
|
uint32_t minDisplayLuminance, uint32_t maxCLL, uint32_t maxFALL,
|
||||||
@@ -431,23 +497,32 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2],
|
|||||||
wlWm.hdrImageCreator,
|
wlWm.hdrImageCreator,
|
||||||
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ);
|
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ);
|
||||||
|
|
||||||
wlWm.hdrImageDescWhiteLevel = atomic_load(&wlWm.hdrPQWhiteLevel);
|
wlWm.hdrImageDescWhiteLevel = wlWm.cmHasLuminances ?
|
||||||
|
atomic_load(&wlWm.hdrPQWhiteLevel) : HDR_PQ_DEFAULT_WHITE_LEVEL;
|
||||||
|
|
||||||
// Set the primary colour volume luminances.
|
// The primary colour volume describes the PQ encoding itself. Mastering
|
||||||
// min_lum : 0.0001 cd/m² (source already scaled) -> pass through
|
// display luminances are target-volume metadata and are set separately.
|
||||||
// max_lum : cd/m² (used only for scRGB; ignored for PQ, where the
|
|
||||||
// compositor forces it to min_lum + 10000 cd/m²)
|
|
||||||
// reference_lum : cd/m² -> locally configured reference white
|
|
||||||
if (wlWm.cmHasLuminances)
|
if (wlWm.cmHasLuminances)
|
||||||
wp_image_description_creator_params_v1_set_luminances(
|
wp_image_description_creator_params_v1_set_luminances(
|
||||||
wlWm.hdrImageCreator,
|
wlWm.hdrImageCreator,
|
||||||
minDisplayLuminance > 0 ? minDisplayLuminance : 50,
|
HDR_PQ_MIN_LUMINANCE,
|
||||||
maxDisplayLuminance > 0 ? maxDisplayLuminance : 1000,
|
HDR_PQ_MAX_LUMINANCE,
|
||||||
wlWm.hdrImageDescWhiteLevel);
|
wlWm.hdrImageDescWhiteLevel);
|
||||||
|
|
||||||
// KVMFR uses the ST 2086/DXGI scale of 50,000 units per coordinate while
|
// KVMFR uses the ST 2086/DXGI scale of 50,000 units per coordinate while
|
||||||
// color-management-v1 uses 1,000,000 units per coordinate.
|
// color-management-v1 uses 1,000,000 units per coordinate.
|
||||||
if (hdrPQ && hdrMetadata && wlWm.cmHasMasteringPrimaries)
|
const bool validMasteringLuminance =
|
||||||
|
(uint64_t)maxDisplayLuminance * 10000 > minDisplayLuminance;
|
||||||
|
const bool validMasteringChromaticities =
|
||||||
|
hdrChromaticitiesValid(displayPrimary, whitePoint);
|
||||||
|
const bool targetVolumeContained = hdrTargetVolumeContained(
|
||||||
|
displayPrimary, whitePoint, minDisplayLuminance,
|
||||||
|
maxDisplayLuminance);
|
||||||
|
const bool canSetMasteringMetadata = hdrPQ && hdrMetadata &&
|
||||||
|
wlWm.cmHasMasteringPrimaries && validMasteringLuminance &&
|
||||||
|
validMasteringChromaticities &&
|
||||||
|
(targetVolumeContained || wlWm.cmHasExtendedTargetVolume);
|
||||||
|
if (canSetMasteringMetadata)
|
||||||
{
|
{
|
||||||
wp_image_description_creator_params_v1_set_mastering_display_primaries(
|
wp_image_description_creator_params_v1_set_mastering_display_primaries(
|
||||||
wlWm.hdrImageCreator,
|
wlWm.hdrImageCreator,
|
||||||
@@ -456,11 +531,13 @@ void waylandSetHDRImageDescription(const uint16_t displayPrimary[3][2],
|
|||||||
displayPrimary[2][0] * 20, displayPrimary[2][1] * 20,
|
displayPrimary[2][0] * 20, displayPrimary[2][1] * 20,
|
||||||
whitePoint[0] * 20, whitePoint[1] * 20);
|
whitePoint[0] * 20, whitePoint[1] * 20);
|
||||||
|
|
||||||
if ((uint64_t)maxDisplayLuminance * 10000 > minDisplayLuminance)
|
wp_image_description_creator_params_v1_set_mastering_luminance(
|
||||||
wp_image_description_creator_params_v1_set_mastering_luminance(
|
wlWm.hdrImageCreator,
|
||||||
wlWm.hdrImageCreator,
|
minDisplayLuminance, maxDisplayLuminance);
|
||||||
minDisplayLuminance, maxDisplayLuminance);
|
|
||||||
}
|
}
|
||||||
|
else if (hdrPQ && hdrMetadata && wlWm.cmHasMasteringPrimaries)
|
||||||
|
DEBUG_WARN("HDR mastering target volume is invalid or unsupported; "
|
||||||
|
"omitting mastering display metadata");
|
||||||
|
|
||||||
if (hdrPQ && hdrMetadata)
|
if (hdrPQ && hdrMetadata)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ struct WaylandDSState
|
|||||||
bool cmHasParametric;
|
bool cmHasParametric;
|
||||||
bool cmHasLuminances;
|
bool cmHasLuminances;
|
||||||
bool cmHasMasteringPrimaries;
|
bool cmHasMasteringPrimaries;
|
||||||
|
bool cmHasExtendedTargetVolume;
|
||||||
bool cmHasTFSt2084PQ;
|
bool cmHasTFSt2084PQ;
|
||||||
bool cmHasTFExtLinear;
|
bool cmHasTFExtLinear;
|
||||||
bool cmHasPrimariesBT2020;
|
bool cmHasPrimariesBT2020;
|
||||||
|
|||||||
Reference in New Issue
Block a user