| 1 | // Copyright (C) 2024 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "qwaylandcolormanagement_p.h" |
| 5 | #include "qwaylanddisplay_p.h" |
| 6 | |
| 7 | #include <QDebug> |
| 8 | |
| 9 | #include <unistd.h> |
| 10 | |
| 11 | QT_BEGIN_NAMESPACE |
| 12 | |
| 13 | namespace QtWaylandClient { |
| 14 | |
| 15 | ColorManager::ColorManager(struct ::wl_registry *registry, uint32_t id, int version) |
| 16 | : QtWayland::xx_color_manager_v4(registry, id, version) |
| 17 | { |
| 18 | } |
| 19 | |
| 20 | ColorManager::~ColorManager() |
| 21 | { |
| 22 | destroy(); |
| 23 | } |
| 24 | |
| 25 | void ColorManager::xx_color_manager_v4_supported_feature(uint32_t feature) |
| 26 | { |
| 27 | switch (feature) { |
| 28 | case feature_icc_v2_v4: |
| 29 | mFeatures |= Feature::ICC; |
| 30 | break; |
| 31 | case feature_parametric: |
| 32 | mFeatures |= Feature::Parametric; |
| 33 | break; |
| 34 | case feature_set_primaries: |
| 35 | mFeatures |= Feature::SetPrimaries; |
| 36 | break; |
| 37 | case feature_set_tf_power: |
| 38 | mFeatures |= Feature::PowerTransferFunction; |
| 39 | break; |
| 40 | case feature_set_luminances: |
| 41 | mFeatures |= Feature::SetLuminances; |
| 42 | break; |
| 43 | case feature_set_mastering_display_primaries: |
| 44 | mFeatures |= Feature::SetMasteringDisplayPrimaries; |
| 45 | break; |
| 46 | case feature_extended_target_volume: |
| 47 | mFeatures |= Feature::ExtendedTargetVolume; |
| 48 | break; |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | void ColorManager::xx_color_manager_v4_supported_primaries_named(uint32_t primaries) |
| 53 | { |
| 54 | mPrimaries.push_back(QtWayland::xx_color_manager_v4::primaries(primaries)); |
| 55 | } |
| 56 | |
| 57 | void ColorManager::xx_color_manager_v4_supported_tf_named(uint32_t transferFunction) |
| 58 | { |
| 59 | mTransferFunctions.push_back(QtWayland::xx_color_manager_v4::transfer_function(transferFunction)); |
| 60 | } |
| 61 | |
| 62 | ColorManager::Features ColorManager::supportedFeatures() const |
| 63 | { |
| 64 | return mFeatures; |
| 65 | } |
| 66 | |
| 67 | bool ColorManager::supportsNamedPrimary(QtWayland::xx_color_manager_v4::primaries primaries) const |
| 68 | { |
| 69 | return mPrimaries.contains(primaries); |
| 70 | } |
| 71 | |
| 72 | bool ColorManager::supportsTransferFunction(QtWayland::xx_color_manager_v4::transfer_function transferFunction) const |
| 73 | { |
| 74 | return mTransferFunctions.contains(transferFunction); |
| 75 | } |
| 76 | |
| 77 | std::unique_ptr<ImageDescription> ColorManager::createImageDescription(const QColorSpace &colorspace) |
| 78 | { |
| 79 | if (!(mFeatures & Feature::Parametric)) |
| 80 | return nullptr; |
| 81 | |
| 82 | constexpr std::array primaryMapping = { |
| 83 | std::make_pair(QColorSpace::Primaries::SRgb, primaries_srgb), |
| 84 | std::make_pair(QColorSpace::Primaries::AdobeRgb, primaries_adobe_rgb), |
| 85 | std::make_pair(QColorSpace::Primaries::DciP3D65, primaries_display_p3), |
| 86 | std::make_pair(QColorSpace::Primaries::Bt2020, primaries_bt2020), |
| 87 | }; |
| 88 | const auto primary = std::find_if(primaryMapping.begin(), primaryMapping.end(), [&colorspace](const auto &pair) { |
| 89 | return pair.first == colorspace.primaries(); |
| 90 | }); |
| 91 | if (!(supportedFeatures() & Feature::SetPrimaries) && (primary == primaryMapping.end() || !supportsNamedPrimary(primary->second))) |
| 92 | return nullptr; |
| 93 | |
| 94 | constexpr std::array tfMapping = { |
| 95 | std::make_pair(QColorSpace::TransferFunction::Linear, transfer_function_linear), |
| 96 | std::make_pair(QColorSpace::TransferFunction::SRgb, transfer_function_srgb), |
| 97 | std::make_pair(QColorSpace::TransferFunction::St2084, transfer_function_st2084_pq), |
| 98 | std::make_pair(QColorSpace::TransferFunction::Hlg, transfer_function_hlg), |
| 99 | }; |
| 100 | const auto tfIt = std::find_if(tfMapping.begin(), tfMapping.end(), [&colorspace](const auto &pair) { |
| 101 | return pair.first == colorspace.transferFunction(); |
| 102 | }); |
| 103 | auto transferFunction = tfIt == tfMapping.end() ? std::nullopt : std::make_optional(tfIt->second); |
| 104 | if (colorspace.transferFunction() == QColorSpace::TransferFunction::Gamma) { |
| 105 | if (qFuzzyCompare(colorspace.gamma(), 2.2f) && supportsTransferFunction(transfer_function_gamma22)) |
| 106 | transferFunction = transfer_function_gamma22; |
| 107 | else if (qFuzzyCompare(colorspace.gamma(), 2.8f) && supportsTransferFunction(transfer_function_gamma28)) |
| 108 | transferFunction = transfer_function_gamma28; |
| 109 | if (!transferFunction && !(mFeatures & Feature::PowerTransferFunction)) |
| 110 | return nullptr; |
| 111 | } else if (!transferFunction) { |
| 112 | return nullptr; |
| 113 | } |
| 114 | |
| 115 | auto creator = new_parametric_creator(); |
| 116 | if (primary != primaryMapping.end()) { |
| 117 | xx_image_description_creator_params_v4_set_primaries_named(creator, primary->second); |
| 118 | } else { |
| 119 | const auto primaries = colorspace.primaryPoints(); |
| 120 | xx_image_description_creator_params_v4_set_primaries(creator, |
| 121 | std::round(x: 10'000 * primaries.redPoint.x()), std::round(x: 10'000 * primaries.redPoint.y()), |
| 122 | std::round(x: 10'000 * primaries.greenPoint.x()), std::round(x: 10'000 * primaries.greenPoint.y()), |
| 123 | std::round(x: 10'000 * primaries.bluePoint.x()), std::round(x: 10'000 * primaries.bluePoint.y()), |
| 124 | std::round(x: 10'000 * primaries.whitePoint.x()), std::round(x: 10'000 * primaries.whitePoint.y()) |
| 125 | ); |
| 126 | } |
| 127 | if (transferFunction) { |
| 128 | xx_image_description_creator_params_v4_set_tf_named(creator, *transferFunction); |
| 129 | } else { |
| 130 | Q_ASSERT(colorspace.transferFunction() == QColorSpace::TransferFunction::Gamma); |
| 131 | xx_image_description_creator_params_v4_set_tf_power(creator, std::round(x: colorspace.gamma() * 10'000)); |
| 132 | } |
| 133 | return std::make_unique<ImageDescription>(xx_image_description_creator_params_v4_create(creator)); |
| 134 | } |
| 135 | |
| 136 | ImageDescriptionInfo::ImageDescriptionInfo(ImageDescription *descr) |
| 137 | : QtWayland::xx_image_description_info_v4(descr->get_information()) |
| 138 | { |
| 139 | } |
| 140 | |
| 141 | ImageDescriptionInfo::~ImageDescriptionInfo() |
| 142 | { |
| 143 | xx_image_description_info_v4_destroy(object()); |
| 144 | } |
| 145 | |
| 146 | void ImageDescriptionInfo::xx_image_description_info_v4_done() |
| 147 | { |
| 148 | Q_EMIT done(); |
| 149 | } |
| 150 | |
| 151 | void ImageDescriptionInfo::xx_image_description_info_v4_icc_file(int32_t icc, uint32_t icc_size) |
| 152 | { |
| 153 | Q_UNUSED(icc_size) |
| 154 | close(fd: icc); |
| 155 | } |
| 156 | |
| 157 | void ImageDescriptionInfo::xx_image_description_info_v4_primaries(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) |
| 158 | { |
| 159 | mContainerRed = QPointF(r_x, r_y) / 10'000.0; |
| 160 | mContainerGreen = QPointF(g_x, g_y) / 10'000.0; |
| 161 | mContainerBlue = QPointF(b_x, b_y) / 10'000.0; |
| 162 | mContainerWhite = QPointF(w_x, w_y) / 10'000.0; |
| 163 | } |
| 164 | |
| 165 | void ImageDescriptionInfo::xx_image_description_info_v4_tf_named(uint32_t transferFunction) |
| 166 | { |
| 167 | mTransferFunction = transferFunction; |
| 168 | } |
| 169 | |
| 170 | void ImageDescriptionInfo::xx_image_description_info_v4_luminances(uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) |
| 171 | { |
| 172 | mMinLuminance = min_lum / 10'000.0; |
| 173 | mMaxLuminance = max_lum; |
| 174 | mReferenceLuminance = reference_lum; |
| 175 | } |
| 176 | |
| 177 | void ImageDescriptionInfo::xx_image_description_info_v4_target_primaries(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) |
| 178 | { |
| 179 | mTargetRed = QPointF(r_x, r_y) / 10'000.0; |
| 180 | mTargetGreen = QPointF(g_x, g_y) / 10'000.0; |
| 181 | mTargetBlue = QPointF(b_x, b_y) / 10'000.0; |
| 182 | mTargetWhite = QPointF(w_x, w_y) / 10'000.0; |
| 183 | } |
| 184 | |
| 185 | void ImageDescriptionInfo::xx_image_description_info_v4_target_luminance(uint32_t min_lum, uint32_t max_lum) |
| 186 | { |
| 187 | mTargetMinLuminance = min_lum / 10'000.0; |
| 188 | mTargetMaxLuminance = max_lum; |
| 189 | } |
| 190 | |
| 191 | ImageDescription::ImageDescription(::xx_image_description_v4 *descr) |
| 192 | : QtWayland::xx_image_description_v4(descr) |
| 193 | { |
| 194 | } |
| 195 | |
| 196 | ImageDescription::~ImageDescription() |
| 197 | { |
| 198 | xx_image_description_v4_destroy(object()); |
| 199 | } |
| 200 | |
| 201 | void ImageDescription::xx_image_description_v4_failed(uint32_t cause, const QString &msg) |
| 202 | { |
| 203 | Q_UNUSED(cause); |
| 204 | qCWarning(lcQpaWayland) << "image description failed!" << msg; |
| 205 | // TODO handle this, somehow |
| 206 | // maybe fall back to the previous or preferred image description |
| 207 | } |
| 208 | |
| 209 | void ImageDescription::xx_image_description_v4_ready(uint32_t identity) |
| 210 | { |
| 211 | Q_UNUSED(identity); |
| 212 | Q_EMIT ready(); |
| 213 | } |
| 214 | |
| 215 | ColorManagementFeedback::ColorManagementFeedback(::xx_color_management_feedback_surface_v4 *obj) |
| 216 | : QtWayland::xx_color_management_feedback_surface_v4(obj) |
| 217 | , mPreferred(std::make_unique<ImageDescription>(get_preferred())) |
| 218 | { |
| 219 | } |
| 220 | |
| 221 | ColorManagementFeedback::~ColorManagementFeedback() |
| 222 | { |
| 223 | xx_color_management_feedback_surface_v4_destroy(object()); |
| 224 | } |
| 225 | |
| 226 | void ColorManagementFeedback::xx_color_management_feedback_surface_v4_preferred_changed() |
| 227 | { |
| 228 | mPreferred = std::make_unique<ImageDescription>(get_preferred()); |
| 229 | mPendingPreferredInfo = std::make_unique<ImageDescriptionInfo>(mPreferred.get()); |
| 230 | connect(mPendingPreferredInfo.get(), &ImageDescriptionInfo::done, this, &ColorManagementFeedback::preferredChanged); |
| 231 | } |
| 232 | |
| 233 | void ColorManagementFeedback::handlePreferredDone() |
| 234 | { |
| 235 | mPreferredInfo = std::move(mPendingPreferredInfo); |
| 236 | } |
| 237 | |
| 238 | ColorManagementSurface::ColorManagementSurface(::xx_color_management_surface_v4 *obj) |
| 239 | : QtWayland::xx_color_management_surface_v4(obj) |
| 240 | { |
| 241 | } |
| 242 | |
| 243 | ColorManagementSurface::~ColorManagementSurface() |
| 244 | { |
| 245 | xx_color_management_surface_v4_destroy(object()); |
| 246 | } |
| 247 | |
| 248 | void ColorManagementSurface::setImageDescription(ImageDescription *descr) |
| 249 | { |
| 250 | if (descr) |
| 251 | xx_color_management_surface_v4_set_image_description(object(), descr->object(), QtWayland::xx_color_manager_v4::render_intent::render_intent_perceptual); |
| 252 | else |
| 253 | xx_color_management_surface_v4_unset_image_description(object()); |
| 254 | } |
| 255 | |
| 256 | } |
| 257 | |
| 258 | QT_END_NAMESPACE |
| 259 | |
| 260 | #include "moc_qwaylandcolormanagement_p.cpp" |
| 261 | |