File indexing completed on 2024-05-12 05:32:33

0001 /*
0002     SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "xx_colormanagement_v2.h"
0007 #include "display.h"
0008 #include "surface.h"
0009 #include "surface_p.h"
0010 #include "utils/resource.h"
0011 #include "wayland/output.h"
0012 
0013 namespace KWin
0014 {
0015 
0016 XXColorManagerV2::XXColorManagerV2(Display *display, QObject *parent)
0017     : QObject(parent)
0018     , QtWaylandServer::xx_color_manager_v2(*display, 1)
0019 {
0020 }
0021 
0022 void XXColorManagerV2::xx_color_manager_v2_bind_resource(Resource *resource)
0023 {
0024     send_supported_feature(resource->handle, feature::feature_parametric);
0025     send_supported_feature(resource->handle, feature::feature_extended_target_volume);
0026     send_supported_feature(resource->handle, feature::feature_set_mastering_display_primaries);
0027     send_supported_feature(resource->handle, feature::feature_set_primaries);
0028 
0029     send_supported_primaries_named(resource->handle, primaries::primaries_srgb);
0030     send_supported_primaries_named(resource->handle, primaries::primaries_bt2020);
0031 
0032     send_supported_tf_named(resource->handle, transfer_function::transfer_function_bt709);
0033     send_supported_tf_named(resource->handle, transfer_function::transfer_function_gamma22);
0034     send_supported_tf_named(resource->handle, transfer_function::transfer_function_srgb);
0035     send_supported_tf_named(resource->handle, transfer_function::transfer_function_st2084_pq);
0036     // TODO scRGB?
0037 
0038     send_supported_intent(resource->handle, render_intent::render_intent_perceptual);
0039     send_supported_intent(resource->handle, render_intent::render_intent_relative);
0040     // TODO implement the other rendering intents
0041 }
0042 
0043 void XXColorManagerV2::xx_color_manager_v2_destroy(Resource *resource)
0044 {
0045     wl_resource_destroy(resource->handle);
0046 }
0047 
0048 void XXColorManagerV2::xx_color_manager_v2_get_output(Resource *resource, uint32_t id, struct ::wl_resource *output)
0049 {
0050     new XXColorManagementOutputV2(resource->client(), id, resource->version(), OutputInterface::get(output)->handle());
0051 }
0052 
0053 void XXColorManagerV2::xx_color_manager_v2_get_surface(Resource *resource, uint32_t id, struct ::wl_resource *surface)
0054 {
0055     const auto surf = SurfaceInterface::get(surface);
0056     const auto priv = SurfaceInterfacePrivate::get(surf);
0057     if (priv->frogColorManagement || priv->xxColorSurface) {
0058         wl_resource_post_error(resource->handle, 0, "there's already a color management surface for this wl_surface");
0059         return;
0060     }
0061     priv->xxColorSurface = new XXColorSurfaceV2(resource->client(), id, resource->version(), surf);
0062 }
0063 
0064 void XXColorManagerV2::xx_color_manager_v2_new_icc_creator(Resource *resource, uint32_t obj)
0065 {
0066     wl_resource_post_error(resource->handle, error::error_unsupported_feature, "ICC profiles are not supported");
0067 }
0068 
0069 void XXColorManagerV2::xx_color_manager_v2_new_parametric_creator(Resource *resource, uint32_t obj)
0070 {
0071     new XXColorParametricCreatorV2(resource->client(), obj, resource->version());
0072 }
0073 
0074 XXColorSurfaceV2::XXColorSurfaceV2(wl_client *client, uint32_t id, uint32_t version, SurfaceInterface *surface)
0075     : QtWaylandServer::xx_color_management_surface_v2(client, id, version)
0076     , m_surface(surface)
0077     , m_preferred(SurfaceInterfacePrivate::get(surface)->preferredColorDescription.value_or(ColorDescription::sRGB))
0078 {
0079 }
0080 
0081 XXColorSurfaceV2::~XXColorSurfaceV2()
0082 {
0083     if (m_surface) {
0084         const auto priv = SurfaceInterfacePrivate::get(m_surface);
0085         priv->pending->colorDescription = ColorDescription::sRGB;
0086         priv->pending->colorDescriptionIsSet = true;
0087         priv->xxColorSurface = nullptr;
0088     }
0089 }
0090 
0091 void XXColorSurfaceV2::setPreferredColorDescription(const ColorDescription &descr)
0092 {
0093     if (m_preferred != descr) {
0094         m_preferred = descr;
0095         send_preferred_changed(resource()->handle);
0096     }
0097 }
0098 
0099 void XXColorSurfaceV2::xx_color_management_surface_v2_destroy_resource(Resource *resource)
0100 {
0101     delete this;
0102 }
0103 
0104 void XXColorSurfaceV2::xx_color_management_surface_v2_destroy(Resource *resource)
0105 {
0106     wl_resource_destroy(resource->handle);
0107 }
0108 
0109 void XXColorSurfaceV2::xx_color_management_surface_v2_set_image_description(Resource *resource, struct ::wl_resource *image_description, uint32_t render_intent)
0110 {
0111     if (!m_surface) {
0112         return;
0113     }
0114     const auto priv = SurfaceInterfacePrivate::get(m_surface);
0115     priv->pending->colorDescription = XXImageDescriptionV2::get(image_description)->description();
0116     priv->pending->colorDescriptionIsSet = true;
0117     // TODO render_intent
0118 }
0119 
0120 void XXColorSurfaceV2::xx_color_management_surface_v2_unset_image_description(Resource *resource)
0121 {
0122     if (!m_surface) {
0123         return;
0124     }
0125     const auto priv = SurfaceInterfacePrivate::get(m_surface);
0126     priv->pending->colorDescription = ColorDescription::sRGB;
0127     priv->pending->colorDescriptionIsSet = true;
0128 }
0129 
0130 void XXColorSurfaceV2::xx_color_management_surface_v2_get_preferred(Resource *resource, uint32_t image_description)
0131 {
0132     new XXImageDescriptionV2(resource->client(), image_description, resource->version(), m_preferred);
0133 }
0134 
0135 XXColorParametricCreatorV2::XXColorParametricCreatorV2(wl_client *client, uint32_t id, uint32_t version)
0136     : QtWaylandServer::xx_image_description_creator_params_v2(client, id, version)
0137 {
0138 }
0139 
0140 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_destroy_resource(Resource *resource)
0141 {
0142     delete this;
0143 }
0144 
0145 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_create(Resource *resource, uint32_t image_description)
0146 {
0147     if (!m_colorimetry || !m_transferFunction) {
0148         wl_resource_post_error(resource->handle, error::error_incomplete_set, "colorimetry or transfer function missing");
0149         return;
0150     }
0151     if (m_transferFunction != NamedTransferFunction::PerceptualQuantizer && (m_maxFrameAverageBrightness || m_maxPeakBrightness)) {
0152         wl_resource_post_error(resource->handle, error::error_inconsistent_set, "max_cll and max_fall must only be set with the PQ transfer function");
0153         return;
0154     }
0155     new XXImageDescriptionV2(resource->client(), image_description, resource->version(), ColorDescription(*m_colorimetry, *m_transferFunction, 100, 0, m_maxFrameAverageBrightness.value_or(100), m_maxPeakBrightness.value_or(100)));
0156     wl_resource_destroy(resource->handle);
0157 }
0158 
0159 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_set_tf_named(Resource *resource, uint32_t tf)
0160 {
0161     if (m_transferFunction) {
0162         wl_resource_post_error(resource->handle, error::error_already_set, "transfer function is already set");
0163         return;
0164     }
0165     switch (tf) {
0166     case XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_SRGB:
0167     case XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_BT709:
0168     case XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_GAMMA22:
0169         m_transferFunction = NamedTransferFunction::gamma22;
0170         return;
0171     case XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_ST2084_PQ:
0172         m_transferFunction = NamedTransferFunction::PerceptualQuantizer;
0173         return;
0174     default:
0175         // TODO add more transfer functions
0176         wl_resource_post_error(resource->handle, error::error_invalid_tf, "unsupported named transfer function");
0177     }
0178 }
0179 
0180 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_set_tf_power(Resource *resource, uint32_t eexp)
0181 {
0182     wl_resource_post_error(resource->handle, error::error_invalid_tf, "power transfer functions are not supported");
0183 }
0184 
0185 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_set_primaries_named(Resource *resource, uint32_t primaries)
0186 {
0187     if (m_colorimetry) {
0188         wl_resource_post_error(resource->handle, error::error_already_set, "primaries are already set");
0189         return;
0190     }
0191     switch (primaries) {
0192     case XX_COLOR_MANAGER_V2_PRIMARIES_SRGB:
0193         m_colorimetry = Colorimetry::fromName(NamedColorimetry::BT709);
0194         return;
0195     case XX_COLOR_MANAGER_V2_PRIMARIES_BT2020:
0196         m_colorimetry = Colorimetry::fromName(NamedColorimetry::BT2020);
0197         return;
0198     default:
0199         // TODO add more named primaries
0200         wl_resource_post_error(resource->handle, error::error_invalid_primaries, "unsupported named primaries");
0201     }
0202 }
0203 
0204 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_set_primaries(Resource *resource, 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)
0205 {
0206     if (m_colorimetry) {
0207         wl_resource_post_error(resource->handle, error::error_already_set, "primaries are already set");
0208         return;
0209     }
0210     if (w_x == 0 || w_y == 0) {
0211         wl_resource_post_error(resource->handle, error::error_invalid_primaries, "whitepoint must not be zero");
0212         return;
0213     }
0214     m_colorimetry = Colorimetry{
0215         QVector2D(r_x / 10'000.0, r_y / 10'000.0),
0216         QVector2D(g_x / 10'000.0, g_y / 10'000.0),
0217         QVector2D(b_x / 10'000.0, b_y / 10'000.0),
0218         QVector2D(w_x / 10'000.0, w_y / 10'000.0)};
0219 }
0220 
0221 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_set_mastering_display_primaries(Resource *resource, 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)
0222 {
0223     // ignored (at least for now)
0224 }
0225 
0226 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_set_mastering_luminance(Resource *resource, uint32_t min_lum, uint32_t max_lum)
0227 {
0228     // ignored (at least for now)
0229 }
0230 
0231 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_set_max_cll(Resource *resource, uint32_t max_cll)
0232 {
0233     m_maxPeakBrightness = max_cll;
0234 }
0235 
0236 void XXColorParametricCreatorV2::xx_image_description_creator_params_v2_set_max_fall(Resource *resource, uint32_t max_fall)
0237 {
0238     m_maxFrameAverageBrightness = max_fall;
0239 }
0240 
0241 XXImageDescriptionV2::XXImageDescriptionV2(wl_client *client, uint32_t id, uint32_t version, const ColorDescription &color)
0242     : QtWaylandServer::xx_image_description_v2(client, id, version)
0243     , m_description(color)
0244 {
0245     // there's no need to track image description identities, as our descriptions are very lightweight
0246     static uint32_t s_identity = 1;
0247     send_ready(resource()->handle, s_identity++);
0248 }
0249 
0250 void XXImageDescriptionV2::xx_image_description_v2_destroy_resource(Resource *resource)
0251 {
0252     delete this;
0253 }
0254 
0255 void XXImageDescriptionV2::xx_image_description_v2_destroy(Resource *resource)
0256 {
0257     wl_resource_destroy(resource->handle);
0258 }
0259 
0260 static uint32_t kwinTFtoProtoTF(NamedTransferFunction tf)
0261 {
0262     switch (tf) {
0263     case NamedTransferFunction::sRGB:
0264         return xx_color_manager_v2_transfer_function::XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_SRGB;
0265     case NamedTransferFunction::linear:
0266         return xx_color_manager_v2_transfer_function::XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_LINEAR;
0267     case NamedTransferFunction::PerceptualQuantizer:
0268         return xx_color_manager_v2_transfer_function::XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_ST2084_PQ;
0269     case NamedTransferFunction::scRGB:
0270         return xx_color_manager_v2_transfer_function::XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_LINEAR;
0271     case NamedTransferFunction::gamma22:
0272         return xx_color_manager_v2_transfer_function::XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_GAMMA22;
0273     }
0274     Q_UNREACHABLE();
0275 }
0276 
0277 void XXImageDescriptionV2::xx_image_description_v2_get_information(Resource *qtResource, uint32_t information)
0278 {
0279     auto resource = wl_resource_create(qtResource->client(), &xx_image_description_info_v2_interface, qtResource->version(), information);
0280     const auto c = m_description.colorimetry();
0281     const auto round = [](float f) {
0282         return std::clamp(std::round(f * 10'000.0), 0.0, 1.0);
0283     };
0284     xx_image_description_info_v2_send_primaries(resource,
0285                                                 round(c.red().x()), round(c.red().y()),
0286                                                 round(c.green().x()), round(c.green().y()),
0287                                                 round(c.blue().x()), round(c.blue().y()),
0288                                                 round(c.white().x()), round(c.white().y()));
0289     xx_image_description_info_v2_send_tf_named(resource, kwinTFtoProtoTF(m_description.transferFunction()));
0290     xx_image_description_info_v2_send_done(resource);
0291     wl_resource_destroy(resource);
0292 }
0293 
0294 const ColorDescription &XXImageDescriptionV2::description() const
0295 {
0296     return m_description;
0297 }
0298 
0299 XXImageDescriptionV2 *XXImageDescriptionV2::get(wl_resource *resource)
0300 {
0301     if (auto resourceContainer = Resource::fromResource(resource)) {
0302         return static_cast<XXImageDescriptionV2 *>(resourceContainer->object());
0303     } else {
0304         return nullptr;
0305     }
0306 }
0307 
0308 XXColorManagementOutputV2::XXColorManagementOutputV2(wl_client *client, uint32_t id, uint32_t version, Output *output)
0309     : QtWaylandServer::xx_color_management_output_v2(client, id, version)
0310     , m_output(output)
0311     , m_colorDescription(output->colorDescription())
0312 {
0313     connect(output, &Output::colorDescriptionChanged, this, &XXColorManagementOutputV2::colorDescriptionChanged);
0314 }
0315 
0316 void XXColorManagementOutputV2::xx_color_management_output_v2_destroy_resource(Resource *resource)
0317 {
0318     delete this;
0319 }
0320 
0321 void XXColorManagementOutputV2::xx_color_management_output_v2_destroy(Resource *resource)
0322 {
0323     wl_resource_destroy(resource->handle);
0324 }
0325 
0326 void XXColorManagementOutputV2::xx_color_management_output_v2_get_image_description(Resource *resource, uint32_t image_description)
0327 {
0328     new XXImageDescriptionV2(resource->client(), image_description, resource->version(), m_colorDescription);
0329 }
0330 
0331 void XXColorManagementOutputV2::colorDescriptionChanged()
0332 {
0333     m_colorDescription = m_output->colorDescription();
0334     send_image_description_changed();
0335 }
0336 
0337 }
0338 
0339 #include "moc_xx_colormanagement_v2.cpp"