Skip to content
Commits on Source (10)
  • Leandro Ribeiro's avatar
    color: always set HAVE_LCMS when LittleCMS dependency is found · a2f99828
    Leandro Ribeiro authored
    
    
    Up to now, HAVE_LCMS would be set only when users would build Weston
    with the deprecated cms-static enabled. But we'll start using this in
    libweston in the next commits, independently of cms-static being enabled
    or not. So always set HAVE_LCMS when the LittleCMS dependency is found.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    a2f99828
  • Leandro Ribeiro's avatar
    shared: include missing header to weston-assert · ba156194
    Leandro Ribeiro authored
    
    
    We forgot to include stdbool to weston-assert.h, but this was not
    causing issues because callers of weston_assert_true() themselves
    include stdbool.
    
    Add stdbool to weston-assert, as this is the right thing to do.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    ba156194
  • Leandro Ribeiro's avatar
    libweston: add unique id generator · a84806a8
    Leandro Ribeiro authored
    
    
    This is preparation for the CM&HDR protocol implementation. It requires
    us to give a unique id to each color-profile, so let's do that.
    
    In this commit we introduce a generic id generator to libweston, and
    its first user: the color-profile.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    a84806a8
  • Leandro Ribeiro's avatar
    protocol: add color-management-v2.xml · 01b43e52
    Leandro Ribeiro authored
    
    
    This does not implement the protocol, it will simply run the scanner
    on the xml to generate the .c and .h files.
    
    This is a variant of the color-management protocol that still didn't
    land upstream on the wayland-protocols repository.
    
    We are using that to experiment with the protocol, but we'll
    completely drop this eventually, when the official protocol
    lands.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    01b43e52
  • Leandro Ribeiro's avatar
    color: add color properties helper · c7d33edd
    Leandro Ribeiro authored
    
    
    Similar to pixel-formats.c, but for color properties. This helper
    aggregates the same color properties from different APIs into tables,
    and introduce functions to use that.
    
    The idea is that we only use that internally in our libweston struct's,
    and pick the specific API value only when necessary.
    
    Preparation for the CM&HDR protocol extension implementation.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    c7d33edd
  • Leandro Ribeiro's avatar
    color: add support to the color-management protocol · 5706d7e5
    Leandro Ribeiro authored
    
    
    In this MR we add support to the majority of the interfaces from the
    color-management protocol.
    
    That means that we are able to advertise output's images descriptions to
    clients, preferred surface images descriptions, and so on. We also
    support clients that wants to create ICC-based images descriptions and
    set such descriptions for surfaces.
    
    We still don't support the interface to allow clients to create
    image descriptions from parameters, but that should be addressed
    in the near future.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    5706d7e5
  • Leandro Ribeiro's avatar
    libweston: expose color management support · 188a3ebd
    Leandro Ribeiro authored
    
    
    In this patch we enable the color-management protocol support, as long
    as the color-manager plugin in use supports it.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    188a3ebd
  • Leandro Ribeiro's avatar
    gitlab-ci: build and install LittleCMS version 2.16 · 39ada71c
    Leandro Ribeiro authored
    
    
    When using some features from LittleCMS in our CI, we are seeing some
    crashes on the address sanitizer. Bumping the LittleCMS version fixes
    that. So build and install a more recent version of LittleCMS on our CI.
    
    We chose version 2.16 because it introduces the function
    cmsGetToneCurveSegment(). We already make extensive use of that in our
    codebase, so it is a good idea to have that on our CI as well.
    
    Now color-curve-segments.c will start to get build on the CI, as
    HAVE_CMS_GET_TONE_CURVE_SEGMENT will be true. So we also fix a minor
    issue in which we were comparing int with uint in this file, what was
    caught after experimenting bumping the LittleCMS version.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    39ada71c
  • Leandro Ribeiro's avatar
    tests: allow to expect protocol errors from unknown objects · 67c6d396
    Leandro Ribeiro authored
    
    
    wl_display_get_protocol_error() takes a pointer to interface as
    parameter and may set it to NULL when the error comes from an unknown
    interface.
    
    That can happen when the client destroys the wl_proxy before calling
    this function. So when the wl_display.error event comes, it will refer
    to an object id that has already been marked as deleted in the
    client's object map. So the protocol error interface will be set to
    NULL and its id to 0.
    
    In our test suite, expect_protocol_error() ignores such case, and
    asserts that a interface != NULL is set. This commit fixes that, and
    now callers are able to call expect_protocol_error() when they expect
    errors from unknown objects.
    
    In the next commit we add the color-management protocol implementation
    tests, and that requires such case to work.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    67c6d396
  • Leandro Ribeiro's avatar
    tests: add tests for the color management protocol implementation · 0ade70fb
    Leandro Ribeiro authored
    
    
    Exercise the color-management protocol mechanics.
    
    This lacks a few test cases that are a bit harder to have, e.g. testing
    that a bad ICC file gets rejected. In the future we plan to add them.
    
    Signed-off-by: default avatarLeandro Ribeiro <leandro.ribeiro@collabora.com>
    0ade70fb
......@@ -43,7 +43,7 @@
variables:
FDO_UPSTREAM_REPO: wayland/weston
FDO_REPO_SUFFIX: "$BUILD_OS-$FDO_DISTRIBUTION_VERSION/$BUILD_ARCH"
FDO_DISTRIBUTION_TAG: '2024-01-19-wayland-protocols-1.33-7'
FDO_DISTRIBUTION_TAG: '2024-02-14-00-lcms2'
include:
......
......@@ -201,3 +201,12 @@ meson build --wrap-mode=nofallback
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf libdisplay-info
# Build and install lcms2, which we use to support color-management.
git clone --branch master https://github.com/mm2/Little-CMS.git lcms2
cd lcms2
git checkout -b snapshot lcms2.16
meson build --wrap-mode=nofallback
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf lcms2
......@@ -66,7 +66,6 @@ apt-get -y --no-install-recommends install \
libjack-jackd2-dev \
libjpeg-dev \
libjpeg-dev \
liblcms2-dev \
libmtdev-dev \
libpam0g-dev \
libpango1.0-dev \
......
......@@ -103,8 +103,6 @@ if get_option('deprecated-color-management-static')
error('cms-static requires lcms2 which was not found. Or, you can use \'-Ddeprecated-color-management-static=false\'.')
endif
config_h.set('HAVE_LCMS', '1')
plugin_lcms = shared_library(
'cms-static',
srcs_lcms,
......
......@@ -410,6 +410,12 @@ struct weston_head {
/** Current content protection status */
enum weston_hdcp_protection current_protection;
/* xx_color_manager_v1.get_color_management_output
*
* When a client uses this request, we add the wl_resource we create to
* this list. */
struct wl_list cm_output_resource_list;
};
/** Output properties derived from its color characteristics and profile
......@@ -1438,6 +1444,8 @@ struct weston_compositor {
uint32_t capabilities; /* combination of enum weston_capability */
struct weston_color_manager *color_manager;
struct weston_idalloc *color_profile_id_generator;
struct weston_renderer *renderer;
const struct pixel_format_info *read_format;
......@@ -1802,6 +1810,11 @@ struct weston_surface_state {
/* weston_protected_surface.enforced/relaxed */
enum weston_surface_protection_mode protection_mode;
/* color_management_surface_v1_interface.set_image_description or
* color_management_surface_v1_interface.unset_image_description */
struct weston_color_profile *color_profile;
const struct weston_render_intent_info *render_intent;
};
struct weston_surface_activation_data {
......@@ -1947,6 +1960,16 @@ struct weston_surface {
enum weston_surface_protection_mode protection_mode;
struct weston_tearing_control *tear_control;
struct weston_color_profile *color_profile;
struct weston_color_profile *preferred_color_profile;
const struct weston_render_intent_info *render_intent;
/* xx_color_manager_v1.get_color_management_surface
*
* When a client uses this request, we add the wl_resource we create to
* this list. */
struct wl_list cm_surface_resource_list;
};
struct weston_subsurface {
......@@ -2188,6 +2211,11 @@ weston_compositor_set_default_pointer_grab(struct weston_compositor *compositor,
struct weston_surface *
weston_surface_create(struct weston_compositor *compositor);
void
weston_surface_set_color_profile(struct weston_surface *surface,
struct weston_color_profile *cprof,
const struct weston_render_intent_info *intent_info);
struct weston_view *
weston_view_create(struct weston_surface *surface);
......
......@@ -301,7 +301,7 @@ are_segments_equal(const cmsCurveSegment *seg_A, const cmsCurveSegment *seg_B)
/* Parametric curve. Determine the number of params that we should
* compare. */
for (i = 0; i < ARRAY_LENGTH(types); i++) {
if (types[i] == abs(seg_A->Type)) {
if (types[i] == (uint32_t)abs(seg_A->Type)) {
n_params = types_n_params[i];
break;
}
......
......@@ -32,8 +32,10 @@
#include "color.h"
#include "color-lcms.h"
#include "color-management.h"
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "shared/weston-assert.h"
const char *
cmlcms_category_name(enum cmlcms_category cat)
......@@ -50,18 +52,27 @@ cmlcms_category_name(enum cmlcms_category cat)
return category_names[cat] ?: "[undocumented category value]";
}
static cmsUInt32Number
static const struct weston_render_intent_info *
cmlcms_get_render_intent(enum cmlcms_category cat,
struct weston_surface *surface,
struct weston_output *output)
{
/*
* TODO: Take into account client provided content profile,
* output profile, and the category of the wanted color
* transformation.
*/
cmsUInt32Number intent = INTENT_RELATIVE_COLORIMETRIC;
return intent;
struct weston_color_manager *cm = output->compositor->color_manager;
/* TODO: take into account the cmlcms_category. */
/* Use default render intent. TODO: default should be
* WESTON_RENDER_INTENT_PERCEPTUAL. That requires tweaking the tests. */
if (!surface || !surface->render_intent) {
weston_assert_true(output->compositor, (cm->supported_rendering_intents >>
WESTON_RENDER_INTENT_RELATIVE) & 1);
return weston_render_intent_info_from(output->compositor,
WESTON_RENDER_INTENT_RELATIVE);
}
weston_assert_ptr(surface->compositor, surface->color_profile);
return surface->render_intent;
}
static struct cmlcms_color_profile *
......@@ -95,10 +106,10 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base,
struct cmlcms_color_transform_search_param param = {
.category = CMLCMS_CATEGORY_INPUT_TO_BLEND,
.input_profile = get_cprof_or_stock_sRGB(cm, NULL /* TODO: surface->color_profile */),
.input_profile = get_cprof_or_stock_sRGB(cm, surface->color_profile),
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
};
param.intent_output = cmlcms_get_render_intent(param.category,
param.render_intent = cmlcms_get_render_intent(param.category,
surface, output);
xform = cmlcms_color_transform_get(cm, &param);
......@@ -133,7 +144,7 @@ cmlcms_get_blend_to_output_color_transform(struct weston_color_manager_lcms *cm,
.input_profile = NULL,
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
};
param.intent_output = cmlcms_get_render_intent(param.category,
param.render_intent = cmlcms_get_render_intent(param.category,
NULL, output);
xform = cmlcms_color_transform_get(cm, &param);
......@@ -158,7 +169,7 @@ cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager_lcms *cm,
.input_profile = cm->sRGB_profile,
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
};
param.intent_output = cmlcms_get_render_intent(param.category,
param.render_intent = cmlcms_get_render_intent(param.category,
NULL, output);
/*
......@@ -191,7 +202,7 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager_lcms *cm,
.input_profile = cm->sRGB_profile,
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
};
param.intent_output = cmlcms_get_render_intent(param.category,
param.render_intent = cmlcms_get_render_intent(param.category,
NULL, output);
xform = cmlcms_color_transform_get(cm, &param);
......@@ -368,10 +379,30 @@ cmlcms_destroy(struct weston_color_manager *cm_base)
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
if (cm->sRGB_profile) {
assert(cm->sRGB_profile->base.ref_count == 1);
/* TODO: when we fix the ugly bug described below, we should
* change this assert to == 1. */
weston_assert_true(cm->base.compositor,
cm->sRGB_profile->base.ref_count >= 1);
unref_cprof(cm->sRGB_profile);
}
/* TODO: this is an ugly hack. Remove it when we stop leaking surfaces
* when shutting down Weston with client surfaces alive. */
if (!wl_list_empty(&cm->color_profile_list)) {
struct cmlcms_color_profile *cprof, *tmp;
weston_log("BUG: When Weston is shutting down with client surfaces alive, it may\n" \
"leak them. This is a bug that needs to be fixed. At this point (in which\n" \
"we are destroying the color manager), we expect all the objects referencing\n" \
"color profiles to be already gone and, consequently, the color profiles\n" \
"themselves should have been already destroyed. But because of this other\n" \
"bug, this didn't happen, and now we destroy the color profiles and leave\n" \
"dangling pointers around.");
wl_list_for_each_safe(cprof, tmp, &cm->color_profile_list, link)
cmlcms_color_profile_destroy(cprof);
}
assert(wl_list_empty(&cm->color_transform_list));
assert(wl_list_empty(&cm->color_profile_list));
......@@ -439,16 +470,30 @@ weston_color_manager_create(struct weston_compositor *compositor)
cm->base.name = "work-in-progress";
cm->base.compositor = compositor;
cm->base.supports_client_protocol = true;
cm->base.init = cmlcms_init;
cm->base.destroy = cmlcms_destroy;
cm->base.destroy_color_profile = cmlcms_destroy_color_profile;
cm->base.get_stock_sRGB_color_profile = cmlcms_get_stock_sRGB_color_profile;
cm->base.get_color_profile_from_icc = cmlcms_get_color_profile_from_icc;
cm->base.send_image_desc_info = cmlcms_send_image_desc_info;
cm->base.destroy_color_transform = cmlcms_destroy_color_transform;
cm->base.get_surface_color_transform = cmlcms_get_surface_color_transform;
cm->base.create_output_color_outcome = cmlcms_create_output_color_outcome;
/* We support the CM&HDR protocol extension when using LittleCMS. */
if (weston_compositor_enable_color_management_protocol(compositor) < 0)
goto err;
/* We still do not support creating parametric color profiles. */
cm->base.supported_color_features = (1 << WESTON_COLOR_FEATURE_ICC);
/* We support all rendering intents. */
cm->base.supported_rendering_intents = (1 << WESTON_RENDER_INTENT_PERCEPTUAL) |
(1 << WESTON_RENDER_INTENT_SATURATION) |
(1 << WESTON_RENDER_INTENT_ABSOLUTE) |
(1 << WESTON_RENDER_INTENT_RELATIVE) |
(1 << WESTON_RENDER_INTENT_RELATIVE_BPC);
wl_list_init(&cm->color_transform_list);
wl_list_init(&cm->color_profile_list);
......
......@@ -33,6 +33,7 @@
#include "color.h"
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
struct weston_color_manager_lcms {
struct weston_color_manager base;
......@@ -65,6 +66,9 @@ struct cmlcms_color_profile {
cmsHPROFILE profile;
struct cmlcms_md5_sum md5sum;
/* Only for profiles created from an ICC file. */
struct ro_anonymous_file *prof_rofile;
/** The curves to decode an electrical signal
*
* For ICC profiles, if the profile type is matrix-shaper, then eotf
......@@ -138,6 +142,10 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm,
struct weston_color_profile **cprof_out,
char **errmsg);
bool
cmlcms_send_image_desc_info(struct cm_image_desc_info *cm_image_desc_info,
struct weston_color_profile *cprof_base);
void
cmlcms_destroy_color_profile(struct weston_color_profile *cprof_base);
......@@ -146,7 +154,7 @@ struct cmlcms_color_transform_search_param {
enum cmlcms_category category;
struct cmlcms_color_profile *input_profile;
struct cmlcms_color_profile *output_profile;
cmsUInt32Number intent_output; /* selected intent from output profile */
const struct weston_render_intent_info *render_intent;
};
struct cmlcms_color_transform {
......
......@@ -26,16 +26,17 @@
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <libweston/libweston.h>
#include "color.h"
#include "color-lcms.h"
#include "color-management.h"
#include "shared/helpers.h"
#include "shared/string-helpers.h"
#include "shared/xalloc.h"
#include "shared/weston-assert.h"
struct xyz_arr_flt {
float v[3];
......@@ -350,6 +351,10 @@ cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof)
cmsFreeToneCurveTriple(cprof->output_inv_eotf_vcgt);
cmsCloseProfile(cprof->profile);
/* Only profiles created from ICC files have these. */
if (cprof->prof_rofile)
os_ro_anonymous_file_destroy(cprof->prof_rofile);
weston_log_scope_printf(cm->profiles_scope, "Destroyed color profile %p. " \
"Description: %s\n", cprof, cprof->base.description);
......@@ -464,7 +469,7 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm_base,
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
cmsHPROFILE profile;
struct cmlcms_md5_sum md5sum;
struct cmlcms_color_profile *cprof;
struct cmlcms_color_profile *cprof = NULL;
char *desc = NULL;
if (!icc_data || icc_len < 1) {
......@@ -506,15 +511,85 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm_base,
if (!cprof)
goto err_close;
cprof->prof_rofile = os_ro_anonymous_file_create(icc_len, icc_data);
if (!cprof->prof_rofile)
goto err_close;
*cprof_out = &cprof->base;
return true;
err_close:
if (cprof)
cmlcms_color_profile_destroy(cprof);
free(desc);
cmsCloseProfile(profile);
return false;
}
bool
cmlcms_send_image_desc_info(struct cm_image_desc_info *cm_image_desc_info,
struct weston_color_profile *cprof_base)
{
struct weston_color_manager_lcms *cm = get_cmlcms(cprof_base->cm);
struct weston_compositor *compositor = cm->base.compositor;
struct cmlcms_color_profile *cprof = get_cprof(cprof_base);
const struct weston_color_primaries_info *primaries_info;
const struct weston_color_tf_info *tf_info;
int32_t fd;
uint32_t len;
if (cprof->prof_rofile) {
/* ICC-based color profile, so just send the ICC file fd. If we
* get an error (negative fd), the helper will send the proper
* error to the client. */
fd = os_ro_anonymous_file_get_fd(cprof->prof_rofile,
RO_ANONYMOUS_FILE_MAPMODE_PRIVATE);
if (fd < 0) {
weston_cm_send_icc_file(cm_image_desc_info, -1, 0);
return false;
}
len = os_ro_anonymous_file_size(cprof->prof_rofile);
weston_assert_uint32_gt(compositor, len, 0);
weston_cm_send_icc_file(cm_image_desc_info, fd, len);
os_ro_anonymous_file_put_fd(fd);
} else {
/* TODO: we still don't support parametric color profiles that
* are not the stock one. This should change when we start
* advertising parametric image description support in our
* color-management protocol implementation. */
if (cprof != cm->sRGB_profile)
weston_assert_not_reached(compositor, "we don't support parametric " \
"cprof's that are not the stock sRGB one");
/* Stock sRGB color profile. TODO: when we add support for
* parametric color profiles, the stock sRGB will be crafted
* using parameters, instead of cmsCreate_sRGBProfileTHR()
* (which we currently use). So we'll get the parameters
* directly from it, instead of hardcoding as we are doing here.
* We don't get the parameters from the stock sRGB color profile
* because it is not trivial to retrieve that from LittleCMS. */
/* Send the H.273 ColourPrimaries code point that matches the
* Rec709 primaries and the D65 white point. */
primaries_info = weston_color_primaries_info_from(compositor,
WESTON_PRIMARIES_CICP_SRGB);
weston_cm_send_primaries_named(cm_image_desc_info, primaries_info);
/* These are the Rec709 primaries and D65 white point. */
weston_cm_send_primaries(cm_image_desc_info,
&primaries_info->color_gamut);
/* sRGB transfer function. */
tf_info = weston_color_tf_info_from(compositor, WESTON_TF_GAMMA22);
weston_cm_send_tf_named(cm_image_desc_info, tf_info);
}
return true;
}
void
cmlcms_destroy_color_profile(struct weston_color_profile *cprof_base)
{
......
......@@ -33,6 +33,7 @@
#include "color.h"
#include "color-curve-segments.h"
#include "color-lcms.h"
#include "color-properties.h"
#include "shared/helpers.h"
#include "shared/string-helpers.h"
#include "shared/xalloc.h"
......@@ -884,6 +885,7 @@ xform_realize_chain(struct cmlcms_color_transform *xform)
cmsHPROFILE chain[5];
unsigned chain_len = 0;
cmsHPROFILE extra = NULL;
cmsUInt32Number dwFlags;
chain[chain_len++] = xform->search_key.input_profile->profile;
chain[chain_len++] = output_profile->profile;
......@@ -920,13 +922,14 @@ xform_realize_chain(struct cmlcms_color_transform *xform)
assert(xform->status == CMLCMS_TRANSFORM_FAILED);
/* transform_factory() is invoked by this call. */
dwFlags = xform->search_key.render_intent->bps ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0;
xform->cmap_3dlut = cmsCreateMultiprofileTransformTHR(xform->lcms_ctx,
chain,
chain_len,
TYPE_RGB_FLT,
TYPE_RGB_FLT,
xform->search_key.intent_output,
0);
xform->search_key.render_intent->lcms_intent,
dwFlags);
cmsCloseProfile(extra);
if (!xform->cmap_3dlut)
......@@ -969,11 +972,11 @@ cmlcms_color_transform_search_param_string(const struct cmlcms_color_transform_s
str_printf(&str, " catergory: %s\n" \
" input profile: %s\n" \
" output profile: %s\n" \
" selected intent from output profile: %u\n",
" selected intent from output profile: %s\n",
cmlcms_category_name(search_key->category),
input_prof_desc,
output_prof_desc,
search_key->intent_output);
search_key->render_intent->desc);
abort_oom_if_null(str);
......@@ -1059,7 +1062,7 @@ transform_matches_params(const struct cmlcms_color_transform *xform,
if (xform->search_key.category != param->category)
return false;
if (xform->search_key.intent_output != param->intent_output ||
if (xform->search_key.render_intent != param->render_intent ||
xform->search_key.output_profile != param->output_profile ||
xform->search_key.input_profile != param->input_profile)
return false;
......
......@@ -16,6 +16,7 @@ config_h.set10('HAVE_CMS_GET_TONE_CURVE_SEGMENT',
has_function_cmsGetToneCurveSegment)
srcs_color_lcms = [
color_management_v1_server_protocol_h,
'color-lcms.c',
'color-profile.c',
'color-transform.c',
......@@ -28,6 +29,7 @@ deps_color_lcms = [
dep_libm,
dep_libweston_private,
dep_lcms2,
dep_libshared,
]
plugin_color_lcms = shared_library(
......
This diff is collapsed.
/*
* Copyright 2023 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef WESTON_COLOR_MANAGEMENT_H
#define WESTON_COLOR_MANAGEMENT_H
#include "color-properties.h"
int
weston_compositor_enable_color_management_protocol(struct weston_compositor *compositor);
void
weston_output_send_image_description_changed(struct weston_output *output);
void
weston_surface_send_preferred_image_description_changed(struct weston_surface *surface);
void
weston_cm_send_icc_file(struct cm_image_desc_info *cm_image_desc_info,
int32_t fd, uint32_t len);
void
weston_cm_send_primaries_named(struct cm_image_desc_info *cm_image_desc_info,
const struct weston_color_primaries_info *primaries_info);
void
weston_cm_send_primaries(struct cm_image_desc_info *cm_image_desc_info,
const struct weston_color_gamut *color_gamut);
void
weston_cm_send_tf_named(struct cm_image_desc_info *cm_image_desc_info,
const struct weston_color_tf_info *tf_info);
#endif /* WESTON_COLOR_MANAGEMENT_H */
......@@ -30,6 +30,7 @@
#include "color.h"
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "shared/weston-assert.h"
struct cmnoop_color_profile {
struct weston_color_profile base;
......@@ -146,12 +147,18 @@ cmnoop_get_surface_color_transform(struct weston_color_manager *cm_base,
struct weston_output *output,
struct weston_surface_color_transform *surf_xform)
{
struct weston_compositor *compositor = output->compositor;
struct weston_color_manager_noop *cmnoop = get_cmnoop(cm_base);
/* TODO: Assert that, if the surface has a cprof, it is the stock one */
/* If surface has a cprof, it has to be the stock one. */
if (surface->color_profile)
weston_assert_ptr_eq(compositor, get_cprof(surface->color_profile),
cmnoop->stock_cprof);
assert(output->color_profile &&
get_cprof(output->color_profile) == cmnoop->stock_cprof);
/* The output must have a cprof, and it has to be the stock one. */
weston_assert_ptr(compositor, output->color_profile);
weston_assert_ptr_eq(compositor, get_cprof(output->color_profile),
cmnoop->stock_cprof);
if (!check_output_eotf_mode(output))
return false;
......@@ -236,15 +243,19 @@ weston_color_manager_noop_create(struct weston_compositor *compositor)
cm->base.name = "no-op";
cm->base.compositor = compositor;
cm->base.supports_client_protocol = false;
cm->base.init = cmnoop_init;
cm->base.destroy = cmnoop_destroy;
cm->base.destroy_color_profile = cmnoop_destroy_color_profile;
cm->base.get_stock_sRGB_color_profile = cmnoop_get_stock_sRGB_color_profile;
cm->base.get_color_profile_from_icc = cmnoop_get_color_profile_from_icc;
cm->base.send_image_desc_info = NULL;
cm->base.destroy_color_transform = cmnoop_destroy_color_transform;
cm->base.get_surface_color_transform = cmnoop_get_surface_color_transform;
cm->base.create_output_color_outcome = cmnoop_create_output_color_outcome;
/* We don't support anything related to the CM&HDR protocol extension */
cm->base.supported_color_features = 0;
cm->base.supported_rendering_intents = 0;
return &cm->base;
}
/*
* Copyright 2023 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#ifdef HAVE_LCMS
#include <lcms2.h>
#define LCMS_INTENT(x) .lcms_intent = (x)
#else
/* invalid value */
#define LCMS_INTENT(x) .lcms_intent = 0xffffffff
#endif
#include <libweston/libweston.h>
#include <color-properties.h>
#include "shared/helpers.h"
#include "shared/weston-assert.h"
#include "color-management-v1-server-protocol.h"
static const struct weston_color_feature_info color_feature_info_table[] = {
{
.feature = WESTON_COLOR_FEATURE_ICC,
.desc = "Allow clients to use the new_icc_creator request " \
"from the CM&HDR protocol extension",
.protocol_feature = XX_COLOR_MANAGER_V2_FEATURE_ICC_V2_V4,
},
{
.feature = WESTON_COLOR_FEATURE_PARAMETRIC,
.desc = "Allow clients to use the new_parametric_creator " \
"request from the CM&HDR protocol extension",
.protocol_feature = XX_COLOR_MANAGER_V2_FEATURE_PARAMETRIC,
},
{
.feature = WESTON_COLOR_FEATURE_SET_PRIMARIES,
.desc = "Allow clients to use the parametric set_primaries " \
"request from the CM&HDR protocol extension",
.protocol_feature = XX_COLOR_MANAGER_V2_FEATURE_SET_PRIMARIES,
},
{
.feature = WESTON_COLOR_FEATURE_SET_TF_POWER,
.desc = "Allow clients to use the parametric set_tf_power " \
"request from the CM&HDR protocol extension",
.protocol_feature = XX_COLOR_MANAGER_V2_FEATURE_SET_TF_POWER,
},
{
.feature = WESTON_COLOR_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES,
.desc = "Allow clients to use the parametric " \
"set_mastering_display_primaries request from the " \
"CM&HDR protocol extension",
.protocol_feature = XX_COLOR_MANAGER_V2_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES,
},
{
.feature = WESTON_COLOR_FEATURE_EXTENDED_TARGET_VOLUME,
.desc = "Allow clients to specify (through the CM&HDR protocol " \
"extension) target color volumes that extend outside of the" \
"primary color volume. This can only be supported when feature " \
"WESTON_COLOR_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES " \
"is supported",
.protocol_feature = XX_COLOR_MANAGER_V2_FEATURE_EXTENDED_TARGET_VOLUME,
},
};
static const struct weston_render_intent_info render_intent_info_table[] = {
{
.intent = WESTON_RENDER_INTENT_PERCEPTUAL,
.desc = "Perceptual",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_PERCEPTUAL,
LCMS_INTENT(INTENT_PERCEPTUAL),
.bps = false,
},
{
.intent = WESTON_RENDER_INTENT_RELATIVE,
.desc = "Media-relative colorimetric",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_RELATIVE,
LCMS_INTENT(INTENT_RELATIVE_COLORIMETRIC),
.bps = false,
},
{
.intent = WESTON_RENDER_INTENT_SATURATION,
.desc = "Saturation",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_SATURATION,
LCMS_INTENT(INTENT_SATURATION),
.bps = false,
},
{
.intent = WESTON_RENDER_INTENT_ABSOLUTE,
.desc = "ICC-absolute colorimetric",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_ABSOLUTE,
LCMS_INTENT(INTENT_ABSOLUTE_COLORIMETRIC),
.bps = false,
},
{
.intent = WESTON_RENDER_INTENT_RELATIVE_BPC,
.desc = "Media-relative colorimetric + black point compensation",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_RELATIVE_BPC,
LCMS_INTENT(INTENT_RELATIVE_COLORIMETRIC),
.bps = true,
},
};
static const struct weston_color_primaries_info color_primaries_info_table[] = {
{
.primaries = WESTON_PRIMARIES_CICP_SRGB,
.desc = "Color primaries for the sRGB color space as defined by " \
"the BT.709 standard",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_SRGB,
.color_gamut = {
.primary = { { 0.64, 0.33 }, /* RGB order */
{ 0.30, 0.60 },
{ 0.15, 0.06 },
},
.white_point = { 0.3127, 0.3290 },
},
},
{
.primaries = WESTON_PRIMARIES_CICP_PAL_M,
.desc = "Color primaries for PAL-M as defined by the BT.470 standard",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_PAL_M,
.color_gamut = {
.primary = { { 0.67, 0.33 }, /* RGB order */
{ 0.21, 0.71 },
{ 0.14, 0.08 },
},
.white_point = { 0.3101, 0.3162 },
},
},
{
.primaries = WESTON_PRIMARIES_CICP_PAL,
.desc = "Color primaries for PAL as defined by the BT.601 standard",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_PAL,
.color_gamut = {
.primary = { { 0.64, 0.33 }, /* RGB order */
{ 0.29, 0.60 },
{ 0.15, 0.06 },
},
.white_point = { 0.3127, 0.3290 },
},
},
{
.primaries = WESTON_PRIMARIES_CICP_NTSC,
.desc = "Color primaries for NTSC as defined by the BT.601 standard",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_NTSC,
.color_gamut = {
.primary = { { 0.630, 0.340 }, /* RGB order */
{ 0.310, 0.595 },
{ 0.155, 0.070 },
},
.white_point = { 0.3127, 0.3290 },
},
},
{
.primaries = WESTON_PRIMARIES_CICP_GENERIC_FILM,
.desc = "Generic film with color filters using Illuminant C",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_GENERIC_FILM,
.color_gamut = {
.primary = { { 0.681, 0.319 }, /* RGB order */
{ 0.243, 0.692 },
{ 0.145, 0.049 },
},
.white_point = { 0.3101, 0.3162 },
},
},
{
.primaries = WESTON_PRIMARIES_CICP_BT2020,
.desc = "Color primaries as defined by the BT.2020 and BT.2100 " \
"standard",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_BT2020,
.color_gamut = {
.primary = { { 0.708, 0.292 }, /* RGB order */
{ 0.170, 0.797 },
{ 0.131, 0.046 },
},
.white_point = { 0.3127, 0.3290 },
},
},
{
.primaries = WESTON_PRIMARIES_CICP_CIE1931_XYZ,
.desc = "Color primaries of the full CIE 1931 XYZ color space",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_CIE1931_XYZ,
.color_gamut = {
.primary = { { 1.0, 0.0 }, /* RGB order */
{ 0.0, 1.0 },
{ 0.0, 0.0 },
},
.white_point = { 0.3333, 0.3333 },
},
},
{
.primaries = WESTON_PRIMARIES_CICP_DCI_P3,
.desc = "Color primaries of the DCI P3 color space as defined by " \
"the SMPTE RP 431 standard",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_DCI_P3,
.color_gamut = {
.primary = { { 0.680, 0.320 }, /* RGB order */
{ 0.265, 0.690 },
{ 0.150, 0.060 },
},
.white_point = { 0.314, 0.351 },
},
},
{
.primaries = WESTON_PRIMARIES_CICP_DISPLAY_P3,
.desc = "Color primaries of Display P3 variant of the DCI-P3 color " \
"space as defined by the SMPTE EG 432 standard",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_DISPLAY_P3,
.color_gamut = {
.primary = { { 0.680, 0.320 }, /* RGB order */
{ 0.265, 0.690 },
{ 0.150, 0.060 },
},
.white_point = { 0.3127, 0.3290 },
},
},
{
.primaries = WESTON_PRIMARIES_ADOBE_RGB,
.desc = "Color primaries of the Adobe RGB color space as defined " \
"by the ISO 12640 standard",
.protocol_primaries = XX_COLOR_MANAGER_V2_PRIMARIES_ADOBE_RGB,
.color_gamut = {
.primary = { { 0.64, 0.33 }, /* RGB order */
{ 0.21, 0.71 },
{ 0.15, 0.06 },
},
.white_point = { 0.3127, 0.3290 },
},
},
};
static const struct weston_color_tf_info color_tf_info_table[] = {
{
.tf = WESTON_TF_LINEAR,
.desc = "Linear transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_LINEAR,
},
{
.tf = WESTON_TF_GAMMA22,
.desc = "Assumed display gamma 2.2 transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_GAMMA22,
},
{
.tf = WESTON_TF_GAMMA28,
.desc = "Assumed display gamma 2.8 transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_GAMMA28,
},
{
.tf = WESTON_TF_SRGB,
.desc = "sRGB piece-wise transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_SRGB,
},
{
.tf = WESTON_TF_EXT_SRGB,
.desc = "Extended sRGB piece-wise transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_EXT_SRGB,
},
{
.tf = WESTON_TF_BT709,
.desc = "BT.709 transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_BT709,
},
{
.tf = WESTON_TF_BT1361,
.desc = "BT.1361 extended transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_BT1361,
},
{
.tf = WESTON_TF_ST240,
.desc = "SMPTE ST 240 transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_ST240,
},
{
.tf = WESTON_TF_ST428,
.desc = "SMPTE ST 428 transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_ST428,
},
{
.tf = WESTON_TF_ST2084_PQ,
.desc = "Perceptual quantizer transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_ST2084_PQ,
},
{
.tf = WESTON_TF_LOG_100,
.desc = "Logarithmic 100:1 transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_LOG_100,
},
{
.tf = WESTON_TF_LOG_316,
.desc = "Logarithmic (100*Sqrt(10) : 1) transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_LOG_316,
},
{
.tf = WESTON_TF_XVYCC,
.desc = "IEC 61966-2-4 transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_XVYCC,
},
{
.tf = WESTON_TF_HLG,
.desc = "Hybrid log-gamma transfer function",
.protocol_tf = XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_HLG,
},
};
WL_EXPORT const struct weston_color_feature_info *
weston_color_feature_info_from(struct weston_compositor *compositor,
enum weston_color_feature feature)
{
unsigned int i;
for (i = 0; i < ARRAY_LENGTH(color_feature_info_table); i++)
if (color_feature_info_table[i].feature == feature)
return &color_feature_info_table[i];
weston_assert_not_reached(compositor, "unknown color feature");
}
WL_EXPORT const struct weston_render_intent_info *
weston_render_intent_info_from(struct weston_compositor *compositor,
enum weston_render_intent intent)
{
unsigned int i;
for (i = 0; i < ARRAY_LENGTH(render_intent_info_table); i++)
if (render_intent_info_table[i].intent == intent)
return &render_intent_info_table[i];
weston_assert_not_reached(compositor, "unknown render intent");
}
WL_EXPORT const struct weston_render_intent_info *
weston_render_intent_info_from_protocol(struct weston_compositor *compositor,
uint32_t protocol_intent)
{
unsigned int i;
for (i = 0; i < ARRAY_LENGTH(render_intent_info_table); i++)
if (render_intent_info_table[i].protocol_intent == protocol_intent)
return &render_intent_info_table[i];
return NULL;
}
WL_EXPORT const struct weston_color_primaries_info *
weston_color_primaries_info_from(struct weston_compositor *compositor,
enum weston_color_primaries primaries)
{
unsigned int i;
for (i = 0; i < ARRAY_LENGTH(color_primaries_info_table); i++)
if (color_primaries_info_table[i].primaries == primaries)
return &color_primaries_info_table[i];
weston_assert_not_reached(compositor, "unknown primaries");
}
WL_EXPORT const struct weston_color_tf_info *
weston_color_tf_info_from(struct weston_compositor *compositor,
enum weston_transfer_function tf)
{
unsigned int i;
for (i = 0; i < ARRAY_LENGTH(color_tf_info_table); i++)
if (color_tf_info_table[i].tf == tf)
return &color_tf_info_table[i];
weston_assert_not_reached(compositor, "unknown tf");
}
/*
* Copyright 2023 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef WESTON_COLOR_CHARACTERISTICS_H
#define WESTON_COLOR_CHARACTERISTICS_H
#include <stdint.h>
#include <stdbool.h>
#include <libweston/libweston.h>
struct weston_compositor;
/**
* Color features.
*/
enum weston_color_feature {
WESTON_COLOR_FEATURE_ICC = 0,
WESTON_COLOR_FEATURE_PARAMETRIC,
WESTON_COLOR_FEATURE_SET_PRIMARIES,
WESTON_COLOR_FEATURE_SET_TF_POWER,
WESTON_COLOR_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES,
WESTON_COLOR_FEATURE_EXTENDED_TARGET_VOLUME,
};
/**
* Rendering intents.
*/
enum weston_render_intent {
WESTON_RENDER_INTENT_PERCEPTUAL = 0,
WESTON_RENDER_INTENT_RELATIVE,
WESTON_RENDER_INTENT_SATURATION,
WESTON_RENDER_INTENT_ABSOLUTE,
WESTON_RENDER_INTENT_RELATIVE_BPC,
};
/**
* Color primaries.
*/
enum weston_color_primaries {
WESTON_PRIMARIES_CICP_SRGB = 0,
WESTON_PRIMARIES_CICP_PAL_M,
WESTON_PRIMARIES_CICP_PAL,
WESTON_PRIMARIES_CICP_NTSC,
WESTON_PRIMARIES_CICP_GENERIC_FILM,
WESTON_PRIMARIES_CICP_BT2020,
WESTON_PRIMARIES_CICP_CIE1931_XYZ,
WESTON_PRIMARIES_CICP_DCI_P3,
WESTON_PRIMARIES_CICP_DISPLAY_P3,
WESTON_PRIMARIES_ADOBE_RGB,
};
/**
* Transfer functions.
*/
enum weston_transfer_function {
WESTON_TF_LINEAR = 0,
WESTON_TF_GAMMA22,
WESTON_TF_GAMMA28,
WESTON_TF_SRGB,
WESTON_TF_EXT_SRGB,
WESTON_TF_BT709,
WESTON_TF_BT1361,
WESTON_TF_ST240,
WESTON_TF_ST428,
WESTON_TF_ST2084_PQ,
WESTON_TF_LOG_100,
WESTON_TF_LOG_316,
WESTON_TF_XVYCC,
WESTON_TF_HLG,
};
struct weston_color_feature_info {
/** Our internal representation for the features. */
enum weston_color_feature feature;
/** String describing the feature. */
const char *desc;
/** CM&HDR protocol extension value representing the feature. */
uint32_t protocol_feature;
};
struct weston_render_intent_info {
/** Our internal representation of the intents. */
enum weston_render_intent intent;
/* String describing the intent. */
const char *desc;
/** CM&HDR protocol extension value representing the intent. */
uint32_t protocol_intent;
/** LittleCMS representation. */
uint32_t lcms_intent;
/** Black-point compensation? */
bool bps;
};
struct weston_color_gamut {
struct weston_CIExy primary[3]; /* RGB order */
struct weston_CIExy white_point;
};
struct weston_color_primaries_info {
/** Our internal representation for the primaries. */
enum weston_color_primaries primaries;
/** Raw values for the primaries. */
struct weston_color_gamut color_gamut;
/** String describing the primaries. */
const char *desc;
/** CM&HDR protocol extension value representing the primaries. */
uint32_t protocol_primaries;
};
struct weston_color_tf_info {
/** Our internal representation for the tf. */
enum weston_transfer_function tf;
/** String describing the tf. */
const char *desc;
/** CM&HDR protocol extension value representing the tf. */
uint32_t protocol_tf;
};
const struct weston_color_feature_info *
weston_color_feature_info_from(struct weston_compositor *compositor,
enum weston_color_feature feature);
const struct weston_render_intent_info *
weston_render_intent_info_from(struct weston_compositor *compositor,
enum weston_render_intent intent);
const struct weston_render_intent_info *
weston_render_intent_info_from_protocol(struct weston_compositor *compositor,
uint32_t protocol_intent);
const struct weston_color_primaries_info *
weston_color_primaries_info_from(struct weston_compositor *compositor,
enum weston_color_primaries primaries);
const struct weston_color_tf_info *
weston_color_tf_info_from(struct weston_compositor *compositor,
enum weston_transfer_function tf);
#endif /* WESTON_COLOR_CHARACTERISTICS_H */
......@@ -37,6 +37,7 @@
#include <string.h>
#include "color.h"
#include "id-number-allocator.h"
#include "libweston-internal.h"
#include <libweston/weston-log.h>
#include "shared/xalloc.h"
......@@ -73,6 +74,9 @@ weston_color_profile_unref(struct weston_color_profile *cprof)
if (--cprof->ref_count > 0)
return;
weston_idalloc_put_id(cprof->cm->compositor->color_profile_id_generator,
cprof->id);
cprof->cm->destroy_color_profile(cprof);
}
......@@ -109,6 +113,7 @@ weston_color_profile_init(struct weston_color_profile *cprof,
{
cprof->cm = cm;
cprof->ref_count = 1;
cprof->id = weston_idalloc_get_id(cm->compositor->color_profile_id_generator);
}
/**
......
......@@ -40,6 +40,9 @@ struct weston_color_profile {
struct weston_color_manager *cm;
int ref_count;
char *description;
/* Unique id to be used by the CM&HDR protocol extension. */
uint32_t id;
};
/** Type or formula for a curve */
......@@ -240,6 +243,8 @@ struct weston_surface_color_transform {
bool identity_pipeline;
};
struct cm_image_desc_info;
struct weston_color_manager {
/** Identifies this CMS component */
const char *name;
......@@ -247,8 +252,21 @@ struct weston_color_manager {
/** This compositor instance */
struct weston_compositor *compositor;
/** Supports the Wayland CM&HDR protocol extension? */
bool supports_client_protocol;
/**
* Supported color features from Wayland CM&HDR protocol extension.
*
* If v (v being enum weston_color_feature v) is a supported color
* feature, the bit v of this will be set to 1.
*/
uint32_t supported_color_features;
/**
* Supported rendering intents from Wayland CM&HDR protocol extension.
*
* If v (v being enum weston_render_intent v) is a supported rendering
* intent, the bit v of this will be set to 1.
*/
uint32_t supported_rendering_intents;
/** Initialize color manager */
bool
......@@ -295,6 +313,23 @@ struct weston_color_manager {
struct weston_color_profile **cprof_out,
char **errmsg);
/** Send image description to clients.
*
* \param cm_image_desc_info The image description info object
* \param cprof_base The color profile that backs the image description
* \return True on success, false on failure
*
* This should be used only by the CM&HDR protocol extension
* implementation.
*
* The color manager implementing this function should use the helpers
* from color-management.c (weston_cm_send_primaries(), etc) to send the
* information to clients.
*/
bool
(*send_image_desc_info)(struct cm_image_desc_info *cm_image_desc_info,
struct weston_color_profile *cprof_base);
/** Destroy a color transform after refcount fell to zero */
void
(*destroy_color_transform)(struct weston_color_transform *xform);
......
......@@ -72,6 +72,7 @@
#include "shared/string-helpers.h"
#include "shared/timespec-util.h"
#include "shared/xalloc.h"
#include "shared/weston-assert.h"
#include "tearing-control-v1-server-protocol.h"
#include "git-version.h"
#include <libweston/version.h>
......@@ -80,6 +81,8 @@
#include "backend.h"
#include "libweston-internal.h"
#include "color.h"
#include "color-management.h"
#include "id-number-allocator.h"
#include "output-capture.h"
#include "pixman-renderer.h"
#include "renderer-gl/gl-renderer.h"
......@@ -778,6 +781,9 @@ weston_surface_state_init(struct weston_surface *surface,
state->desired_protection = WESTON_HDCP_DISABLE;
state->protection_mode = WESTON_SURFACE_PROTECTION_MODE_RELAXED;
state->color_profile = NULL;
state->render_intent = NULL;
}
static void
......@@ -801,6 +807,10 @@ weston_surface_state_fini(struct weston_surface_state *state)
fd_clear(&state->acquire_fence_fd);
weston_buffer_release_reference(&state->buffer_release_ref, NULL);
weston_color_profile_unref(state->color_profile);
state->color_profile = NULL;
state->render_intent = NULL;
}
static void
......@@ -818,6 +828,55 @@ weston_surface_state_set_buffer(struct weston_surface_state *state,
&state->buffer_destroy_listener);
}
static void
weston_surface_update_preferred_color_profile(struct weston_surface *surface)
{
struct weston_compositor *compositor = surface->compositor;
struct weston_color_manager *cm = compositor->color_manager;
struct weston_color_profile *old, *new;
old = surface->preferred_color_profile;
if (surface->output) {
/* The surface preferred color profile is the same color profile
* of its primary output. */
new = weston_color_profile_ref(surface->output->color_profile);
} else if (!wl_list_empty(&compositor->output_list)) {
/* Surface is still unmapped, with no primary output. To map the
* surface, clients need to draw, and in order to do that they
* should ask the preferred color profile for the surface (at
* least for color-aware clients). So in order to maximize the
* changes of the first frame being correct, we arbitrarily pick
* an output and use its color profile as the preferred. The
* most common scenario is a system with a single monitor (and
* output), so when the surface gets mapped this output will
* become the surface primary one, and the preferred color
* profile will stay the same. */
struct weston_output *output;
output = wl_container_of(surface->compositor->output_list.next,
output, link);
new = weston_color_profile_ref(output->color_profile);
} else {
/* Unmapped surface and no outputs available, so let's pick
* stock sRGB color profile. */
new = cm->get_stock_sRGB_color_profile(cm);
}
/* Nothing to do. */
if (new == old) {
weston_color_profile_unref(new);
return;
}
weston_color_profile_unref(old);
/* Update the preferred color profile and notify color-aware clients
* that the surface preferred image description changed. Part of the
* CM&HDR protocol extension implementation. */
surface->preferred_color_profile = new;
weston_surface_send_preferred_image_description_changed(surface);
}
WL_EXPORT struct weston_surface *
weston_surface_create(struct weston_compositor *compositor)
{
......@@ -866,6 +925,17 @@ weston_surface_create(struct weston_compositor *compositor)
surface->current_protection = WESTON_HDCP_DISABLE;
surface->protection_mode = WESTON_SURFACE_PROTECTION_MODE_RELAXED;
wl_list_init(&surface->cm_surface_resource_list);
/* The surfaces start with no color profile and render intent. It's up
* to the color manager what to do with that. Later, clients are able to
* define these values using the CM&HDR protocol extension. */
surface->color_profile = NULL;
surface->render_intent = NULL;
/* Also part of the CM&HDR protocol extension implementation. */
weston_surface_update_preferred_color_profile(surface);
return surface;
}
......@@ -1128,6 +1198,46 @@ weston_surface_send_enter_leave(struct weston_surface *surface,
}
}
/** Set the color profile and render intent of a surface.
*
* \param surface The surface to update
* \param cprof The new color profile, or NULL
* \param render_intent The render intent info object, or NULL
*
* It is forbidden to pass a valid cprof and a NULL render intent, and
* vice-versa. But both NULL is valid.
*/
void
weston_surface_set_color_profile(struct weston_surface *surface,
struct weston_color_profile *cprof,
const struct weston_render_intent_info *render_intent)
{
struct weston_color_manager *cm = surface->compositor->color_manager;
struct weston_paint_node *pnode;
/* Nothing to do. */
if (surface->color_profile == cprof &&
surface->render_intent == render_intent)
return;
if (!!cprof ^ !!render_intent)
weston_assert_not_reached(cm->compositor,
"received valid cprof and NULL render intent, " \
"or vice versa; invalid for this function");
/* Remove outdated cached color transformations */
wl_list_for_each(pnode, &surface->paint_node_list, surface_link) {
weston_surface_color_transform_fini(&pnode->surf_xform);
pnode->surf_xform_valid = false;
}
/* Caller gave us a color profile and render intent (or NULL for both,
* which is also valid), so update the surface with them. */
weston_color_profile_unref(surface->color_profile);
surface->color_profile = weston_color_profile_ref(cprof);
surface->render_intent = render_intent;
}
static void
weston_surface_compute_protection(struct protected_surface *psurface)
{
......@@ -1377,6 +1487,11 @@ weston_surface_assign_output(struct weston_surface *es)
es->output = new_output;
weston_surface_update_output_mask(es, mask);
/* Surface primary output may have changed, and that may change the
* surface preferred color profile. Part of the CM&HDR protocol
* extension implementation. */
weston_surface_update_preferred_color_profile(es);
}
/** Recalculate which output(s) the view is displayed on
......@@ -2486,6 +2601,7 @@ weston_surface_unref(struct weston_surface *surface)
struct wl_resource *cb, *next;
struct weston_view *ev, *nv;
struct weston_pointer_constraint *constraint, *next_constraint;
struct wl_resource *cm_surface_res, *cm_surface_res_tmp;
struct weston_paint_node *pnode, *pntmp;
if (!surface)
......@@ -2538,6 +2654,16 @@ weston_surface_unref(struct weston_surface *surface)
if (surface->tear_control)
surface->tear_control->surface = NULL;
weston_color_profile_unref(surface->color_profile);
weston_color_profile_unref(surface->preferred_color_profile);
wl_resource_for_each_safe(cm_surface_res, cm_surface_res_tmp,
&surface->cm_surface_resource_list) {
wl_list_remove(wl_resource_get_link(cm_surface_res));
wl_list_init(wl_resource_get_link(cm_surface_res));
wl_resource_set_user_data(cm_surface_res, NULL);
}
free(surface);
}
......@@ -4548,6 +4674,11 @@ weston_surface_commit_state(struct weston_surface *surface,
/* weston_protected_surface.set_type */
weston_surface_set_desired_protection(surface, state->desired_protection);
/* color_management_surface_v1_interface.set_image_description or
* color_management_surface_v1_interface.unset_image_description */
weston_surface_set_color_profile(surface, state->color_profile,
state->render_intent);
wl_signal_emit(&surface->commit_signal, surface);
/* Surface is now quiescent */
......@@ -4877,6 +5008,10 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub)
&surface->pending.damage_buffer);
pixman_region32_clear(&surface->pending.damage_buffer);
sub->cached.render_intent = surface->pending.render_intent;
weston_color_profile_unref(sub->cached.color_profile);
sub->cached.color_profile =
weston_color_profile_ref(surface->pending.color_profile);
if (surface->pending.status & WESTON_SURFACE_DIRTY_BUFFER) {
weston_surface_state_set_buffer(&sub->cached,
......@@ -6013,6 +6148,12 @@ weston_head_remove_global(struct weston_head *head)
wl_resource_set_destructor(resource, NULL);
}
wl_list_init(&head->xdg_output_resource_list);
wl_resource_for_each_safe(resource, tmp, &head->cm_output_resource_list) {
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
wl_resource_set_user_data(resource, NULL);
}
}
/** Get the backing object of wl_output
......@@ -6058,6 +6199,7 @@ weston_head_init(struct weston_head *head, const char *name)
wl_list_init(&head->output_link);
wl_list_init(&head->resource_list);
wl_list_init(&head->xdg_output_resource_list);
wl_list_init(&head->cm_output_resource_list);
head->name = xstrdup(name);
head->supported_eotf_mask = WESTON_EOTF_MODE_SDR;
head->current_protection = WESTON_HDCP_DISABLE;
......@@ -7429,18 +7571,24 @@ WL_EXPORT bool
weston_output_set_color_profile(struct weston_output *output,
struct weston_color_profile *cprof)
{
struct weston_color_manager *cm = output->compositor->color_manager;
struct weston_color_profile *old;
struct weston_compositor *compositor = output->compositor;
struct weston_color_manager *cm = compositor->color_manager;
struct weston_color_profile *old, *new;
struct weston_paint_node *pnode;
struct weston_view *view;
old = output->color_profile;
new = cprof ? weston_color_profile_ref(cprof) :
cm->get_stock_sRGB_color_profile(cm);
if (!cprof) {
output->color_profile = cm->get_stock_sRGB_color_profile(cm);
} else {
output->color_profile = weston_color_profile_ref(cprof);
/* Nothing to do. */
if (new == old) {
weston_color_profile_unref(new);
return true;
}
output->color_profile = new;
if (output->enabled) {
if (!weston_output_set_color_outcome(output)) {
/* Failed, roll back */
......@@ -7454,10 +7602,22 @@ weston_output_set_color_profile(struct weston_output *output,
weston_surface_color_transform_fini(&pnode->surf_xform);
pnode->surf_xform_valid = false;
}
/* The preferred color profile of a surface is its primary
* output color profile. For each surface that has this output
* as primary, we may need to update their preferred color
* profile. Part of the CM&HDR protocol extension
* implementation. */
wl_list_for_each(view, &compositor->view_list, link)
weston_surface_update_preferred_color_profile(view->surface);
}
weston_color_profile_unref(old);
/* Output color profile has changed, so we need to notify clients about
* that. Part of the CM&HDR protocol extension implementation. */
weston_output_send_image_description_changed(output);
return true;
}
......@@ -9171,6 +9331,11 @@ weston_compositor_shutdown(struct weston_compositor *ec)
ec->color_manager = NULL;
}
/* Already destroyed color manager, now we can safely destroy the color
* profile id generator. */
weston_idalloc_destroy(ec->color_profile_id_generator);
ec->color_profile_id_generator = NULL;
if (ec->renderer)
ec->renderer->destroy(ec);
......@@ -9284,6 +9449,9 @@ weston_compositor_backends_loaded(struct weston_compositor *compositor)
if (!compositor->color_manager)
return -1;
/* Create id generator before initing the color manager. */
compositor->color_profile_id_generator = weston_idalloc_create(compositor);
if (!compositor->color_manager->init(compositor->color_manager))
return -1;
......
/*
* Copyright 2024 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "id-number-allocator.h"
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "shared/weston-assert.h"
struct weston_idalloc {
struct weston_compositor *compositor;
/* Each value on this array is a bucket of size 32. Bit is 0 if the id
* is available, 1 otherwise. */
uint32_t *buckets;
uint32_t num_buckets;
uint32_t lowest_free_bucket;
};
/**
* Creates a unique id allocator
*
* \param compositor The compositor
* \return The unique id allocator
*/
WESTON_EXPORT_FOR_TESTS struct weston_idalloc *
weston_idalloc_create(struct weston_compositor *compositor)
{
struct weston_idalloc *idalloc;
idalloc = xzalloc(sizeof(*idalloc));
idalloc->compositor = compositor;
/* Start with 2 buckets. If necessary we increase that on demand. */
idalloc->num_buckets = 2;
idalloc->buckets = xzalloc(idalloc->num_buckets * sizeof(*idalloc->buckets));
/* Let's reserve id 0 for errors. So start with id 0 already taken. Set
* the first bit of the first bucket to 1. */
idalloc->buckets[idalloc->lowest_free_bucket] = 1;
return idalloc;
}
/**
* Destroys a unique id allocator
*
* \param idalloc The unique id allocator to destroy
*/
WESTON_EXPORT_FOR_TESTS void
weston_idalloc_destroy(struct weston_idalloc *idalloc)
{
/* Sanity check: id 0 should still be taken. */
weston_assert_true(idalloc->compositor, idalloc->buckets[0] & 1);
free(idalloc->buckets);
free(idalloc);
}
static void
update_lowest_free_bucket(struct weston_idalloc *idalloc)
{
uint32_t old_lowest_free_bucket = idalloc->lowest_free_bucket;
uint32_t *bucket;
unsigned int i;
for (i = old_lowest_free_bucket; i < idalloc->num_buckets; i++) {
bucket = &idalloc->buckets[i];
/* Skip full bucket */
if (*bucket == 0xffffffff)
continue;
idalloc->lowest_free_bucket = i;
return;
}
/* We didn't find any free bucket, so we need to add more buckets. The
* first one (from the new added) will be the lowest free. */
idalloc->lowest_free_bucket = idalloc->num_buckets;
idalloc->num_buckets *= 2;
idalloc->buckets = xrealloc(idalloc->buckets,
idalloc->num_buckets * sizeof(*idalloc->buckets));
}
/**
* Gets an id from unique id allocator
*
* \param idalloc The unique id allocator
* \return The unique id
*/
WESTON_EXPORT_FOR_TESTS uint32_t
weston_idalloc_get_id(struct weston_idalloc *idalloc)
{
uint32_t *bucket = &idalloc->buckets[idalloc->lowest_free_bucket];
unsigned int i;
uint32_t id;
/* Sanity check: lowest free bucket should not be full. */
weston_assert_uint32_neq(idalloc->compositor, *bucket, 0xffffffff);
for (i = 0; i < 32; i++) {
/* Id already used, skip it. */
if ((*bucket >> i) & 1)
continue;
/* Found free id, take it and set it to 1 on the bucket. */
*bucket |= 1 << i;
id = (32 * idalloc->lowest_free_bucket) + i;
/* Bucket may become full... */
if (*bucket == 0xffffffff)
update_lowest_free_bucket(idalloc);
return id;
}
/* We need to find an available id. */
weston_assert_not_reached(idalloc->compositor,
"should be able to allocate unique id");
}
/**
* Releases a id back to unique id allocator
*
* When an id from the unique id allocator will not be used anymore, users
* should call this function so that this id can be advertised again by the id
* allocator.
*
* \param idalloc The unique id allocator
* \param id The id to release
*/
WESTON_EXPORT_FOR_TESTS void
weston_idalloc_put_id(struct weston_idalloc *idalloc, uint32_t id)
{
uint32_t bucket_index = id / 32;
uint32_t id_index_on_bucket = id % 32;
uint32_t *bucket;
/* Shouldn't try to release index 0, we never advertise this id to anyone. */
weston_assert_uint32_neq(idalloc->compositor, id, 0);
/* Bucket index should be lower than num_buckets. */
weston_assert_uint32_lt(idalloc->compositor,
bucket_index, idalloc->num_buckets);
bucket = &idalloc->buckets[bucket_index];
/* Shouldn't try to release a free index. */
weston_assert_true(idalloc->compositor,
(*bucket >> id_index_on_bucket) & 1);
/* We now have an available index id on this bucket, so it may become
* the lowest bucket. */
if (bucket_index < idalloc->lowest_free_bucket)
idalloc->lowest_free_bucket = bucket_index;
/* Zero the bit on the bucket. */
*bucket &= ~(1 << id_index_on_bucket);
}