From a266c5fff5fdff28db3fc6c751184c92632e902a Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Wed, 24 Jul 2024 17:22:07 +0200 Subject: [PATCH] clients: Add color Add a client that creates a surface with one color using single pixel buffer. Set to that surface an image_description created with the parametric creator of the color management protocol. This client can get as params: 1. Width and height 2. Color channels R, G, B, A 3. The name of primaries 4. The name of a transfer function 5. The min, max, ref luminance levels Signed-off-by: Joan Torres --- clients/color.c | 578 ++++++++++++++++++++++++++++++++++++++++++++ clients/meson.build | 9 + 2 files changed, 587 insertions(+) create mode 100644 clients/color.c diff --git a/clients/color.c b/clients/color.c new file mode 100644 index 00000000..edb815c7 --- /dev/null +++ b/clients/color.c @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2024 SUSE Software Solutions Germany GmbH + * + * 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 +#include + +#include "color-management-v1-client-protocol.h" +#include "shared/helpers.h" +#include "shared/xalloc.h" +#include "single-pixel-buffer-v1-client-protocol.h" +#include "viewporter-client-protocol.h" +#include "window.h" + +enum image_description_status { + IMAGE_DESCRIPTION_NOT_CREATED = 0, + IMAGE_DESCRIPTION_READY, + IMAGE_DESCRIPTION_FAILED, +}; + +struct pixel_color { + uint32_t r; + uint32_t g; + uint32_t b; + uint32_t a; +}; + +struct color { + struct display *display; + struct window *window; + struct widget *parent_widget; + struct widget *widget; + + struct xx_color_manager_v4 *color_manager; + struct xx_color_management_surface_v4 *color_surface; + struct wp_single_pixel_buffer_manager_v1 *single_pixel_manager; + struct wp_viewporter *viewporter; + struct wp_viewport *viewport; + + struct pixel_color pixel_color; + + enum xx_color_manager_v4_primaries primaries; + enum xx_color_manager_v4_transfer_function transfer_function; + float min_lum; + float max_lum; + float ref_lum; + + uint32_t supported_color_features; + uint32_t supported_rendering_intents; + uint32_t supported_primaries_named; + uint32_t supported_tf_named; +}; + +struct valid_enum { + const char *name; + uint32_t value; +}; + +static bool opt_help = false; +static uint32_t opt_width = 250; +static uint32_t opt_height = 250; +static const char *opt_r = NULL; +static const char *opt_g = NULL; +static const char *opt_b = NULL; +static const char *opt_a = NULL; +static const char *opt_primaries = NULL; +static const char *opt_transfer_function = NULL; +static const char *opt_min_lum = NULL; +static const char *opt_max_lum = NULL; +static const char *opt_ref_lum = NULL; +static const struct weston_option cli_options[] = { + { WESTON_OPTION_BOOLEAN, "help", 0, &opt_help }, + { WESTON_OPTION_UNSIGNED_INTEGER, "width", 'w', &opt_width }, + { WESTON_OPTION_UNSIGNED_INTEGER, "height", 'h', &opt_height }, + { WESTON_OPTION_STRING, 0, 'R', &opt_r }, + { WESTON_OPTION_STRING, 0, 'G', &opt_g }, + { WESTON_OPTION_STRING, 0, 'B', &opt_b }, + { WESTON_OPTION_STRING, 0, 'A', &opt_a }, + { WESTON_OPTION_STRING, "primaries", 'p', &opt_primaries }, + { WESTON_OPTION_STRING, "transfer-function", 't', &opt_transfer_function }, + { WESTON_OPTION_STRING, "min-lum", 'm', &opt_min_lum }, + { WESTON_OPTION_STRING, "max-lum", 'M', &opt_max_lum }, + { WESTON_OPTION_STRING, "ref-lum", 'r', &opt_ref_lum }, +}; + +static const struct valid_enum valid_primaries[] = { + { "srgb", XX_COLOR_MANAGER_V4_PRIMARIES_SRGB }, + { "bt2020", XX_COLOR_MANAGER_V4_PRIMARIES_BT2020 }, +}; + +static const struct valid_enum valid_transfer_functions[] = { + { "srgb", XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB }, + { "pq", XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ }, + { "linear", XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR }, +}; + +static bool +validate_color(const char *c, uint32_t *dest, uint32_t fallback) +{ + char *end; + double value; + + if (!c) { + *dest = fallback; + return true; + } + + value = strtod(c, &end); + if (value < 0.0 || value > 1.0 || *end != '\0') { + fprintf(stderr, "Validating color failed, it should be between 0.0 and 1.0\n"); + return false; + } + + *dest = value * UINT32_MAX; + + return true; +} + +static bool +validate_option(const char *option, uint32_t *dest, + const struct valid_enum *valid_options, + int count, uint32_t fallback) +{ + int i; + + if (!option) { + *dest = fallback; + return true; + } + + for (i = 0; i < count; i++) { + if (strcmp(valid_options[i].name, option) == 0) { + *dest = valid_options[i].value; + return true; + } + } + + fprintf(stderr, "Validating option '%s' failed, valid options:\n", option); + for (i = 0; i < count; i++) + fprintf(stderr, "'%s' ", valid_options[i].name); + fprintf(stderr, "\n"); + + return false; +} + +static bool +validate_luminance(const char *c, float *dest, float fallback) +{ + char *end; + float value; + + if (!c) { + *dest = fallback; + return true; + } + + value = strtof(c, &end); + if (value < 0.f || value > 10000.f || *end != '\0') { + fprintf(stderr, "Validating luminance failed, it should be between 0 and 10,000\n"); + return false; + } + + *dest = value; + + return true; +} + +static bool +validate_options(struct color *color) +{ + return validate_color(opt_r, &color->pixel_color.r, 0) && + validate_color(opt_g, &color->pixel_color.g, 0) && + validate_color(opt_b, &color->pixel_color.b, 0) && + validate_color(opt_a, &color->pixel_color.a, UINT32_MAX) && + validate_option(opt_primaries, &color->primaries, + valid_primaries, + ARRAY_LENGTH(valid_primaries), + XX_COLOR_MANAGER_V4_PRIMARIES_SRGB) && + validate_option(opt_transfer_function, &color->transfer_function, + valid_transfer_functions, + ARRAY_LENGTH(valid_transfer_functions), + XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB) && + validate_luminance(opt_min_lum, &color->min_lum, -1.f) && + validate_luminance(opt_max_lum, &color->max_lum, -1.f) && + validate_luminance(opt_ref_lum, &color->ref_lum, -1.f); +} + +static void +usage(const char *program_name, int exit_code) +{ + unsigned int i; + + fprintf(stderr, "Usage: %s [OPTIONS]\n", program_name); + fprintf(stderr, " --help\n"); + fprintf(stderr, " --width or -w\n"); + fprintf(stderr, " --height or -h\n"); + fprintf(stderr, " -R (0.0 to 1.0)\n"); + fprintf(stderr, " -G (0.0 to 1.0)\n"); + fprintf(stderr, " -B (0.0 to 1.0)\n"); + fprintf(stderr, " -A (0.0 to 1.0)\n"); + fprintf(stderr, " --primaries or -p:"); + fprintf(stderr, "\n "); + for (i = 0; i < ARRAY_LENGTH(valid_primaries); i++) + fprintf(stderr, " '%s'", valid_primaries[i].name); + fprintf(stderr, "\n"); + fprintf(stderr, " --transfer-function or -t:"); + fprintf(stderr, "\n "); + for (i = 0; i < ARRAY_LENGTH(valid_transfer_functions); i++) + fprintf(stderr, " '%s'", valid_transfer_functions[i].name); + fprintf(stderr, "\n"); + fprintf(stderr, " --min-lum or -m (0.0 to 10000.0)\n"); + fprintf(stderr, " --max-lum or -M (0.0 to 10000.0)\n"); + fprintf(stderr, " --ref-lum or -r (0.0 to 10000.0)\n"); + + exit(exit_code); +} + +static void +supported_intent(void *data, struct xx_color_manager_v4 *xx_color_manager_v4, + uint32_t render_intent) +{ + struct color *color = data; + + color->supported_rendering_intents |= 1 << render_intent; +} + +static void +supported_feature(void *data, struct xx_color_manager_v4 *xx_color_manager_v4, + uint32_t feature) +{ + struct color *color = data; + + color->supported_color_features |= 1 << feature; +} + +static void +supported_tf_named(void *data, struct xx_color_manager_v4 *xx_color_manager_v4, + uint32_t tf) +{ + struct color *color = data; + + color->supported_tf_named |= 1 << tf; +} + +static void +supported_primaries_named(void *data, + struct xx_color_manager_v4 *xx_color_manager_v4, + uint32_t primaries) +{ + struct color *color = data; + + color->supported_primaries_named |= 1 << primaries; +} + +static const struct xx_color_manager_v4_listener color_manager_listener = { + supported_intent, + supported_feature, + supported_tf_named, + supported_primaries_named, +}; + +static void +global_handler(struct display *display, uint32_t name, + const char *interface, uint32_t version, void *data) +{ + struct color *color = data; + struct wl_surface *surface = widget_get_wl_surface(color->widget); + + if (strcmp(interface, xx_color_manager_v4_interface.name) == 0) { + color->color_manager = display_bind(display, name, + &xx_color_manager_v4_interface, 1); + color->color_surface = xx_color_manager_v4_get_surface(color->color_manager, + surface); + xx_color_manager_v4_add_listener(color->color_manager, + &color_manager_listener, color); + } else if (strcmp(interface, wp_single_pixel_buffer_manager_v1_interface.name) == 0) { + color->single_pixel_manager = + display_bind(display, name, + &wp_single_pixel_buffer_manager_v1_interface, 1); + } else if (strcmp(interface, wp_viewporter_interface.name) == 0) { + color->viewporter = display_bind(display, name, + &wp_viewporter_interface, 1); + color->viewport = wp_viewporter_get_viewport(color->viewporter, surface); + } +} + +static bool +check_color_requirements(struct color *color) +{ + + if (!color->color_manager) { + fprintf(stderr, "The compositor doesn't expose %s\n", + xx_color_manager_v4_interface.name); + return false; + } + if (!(color->supported_color_features & (1 << XX_COLOR_MANAGER_V4_FEATURE_PARAMETRIC))) { + fprintf(stderr, "The color manager doesn't support the parametric creator\n"); + return false; + } + if (!(color->supported_primaries_named & (1 << color->primaries))) { + fprintf(stderr, "The color manager doesn't support the primaries name\n"); + return false; + } + if (!(color->supported_tf_named & (1 << color->transfer_function))) { + fprintf(stderr, "The color manager doesn't support the transfer function\n"); + return false; + } + if (!(color->supported_rendering_intents & (1 << XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL))) { + fprintf(stderr, "The color manager doesn't support perceptual render intent\n"); + return false; + } + if (color->min_lum != -1.f || color->max_lum != -1.f || color->ref_lum != -1.f) { + if (!(color->supported_color_features & (1 << XX_COLOR_MANAGER_V4_FEATURE_SET_LUMINANCES))) { + fprintf(stderr, "The color manager doesn't support setting luminances\n"); + return false; + } + if (color->min_lum == -1.f || color->max_lum == -1.f || color->ref_lum == -1.f) { + fprintf(stderr, "To set the luminances it is required min-lum, max-lum and ref-lum\n"); + return false; + } + } + + return true; +} + +static void +color_destroy(struct color *color) +{ + if (color->color_surface) + xx_color_management_surface_v4_destroy(color->color_surface); + + if (color->color_manager) + xx_color_manager_v4_destroy(color->color_manager); + + if (color->single_pixel_manager) + wp_single_pixel_buffer_manager_v1_destroy(color->single_pixel_manager); + + if (color->viewport) + wp_viewport_destroy(color->viewport); + + if (color->viewporter) + wp_viewporter_destroy(color->viewporter); + + if (color->widget) + widget_destroy(color->widget); + + if (color->parent_widget) + widget_destroy(color->parent_widget); + + if (color->window) + window_destroy(color->window); + + if (color->display) + display_destroy(color->display); + + free(color); +} + +static void +resize_handler(struct widget *parent_widget, int32_t width, int32_t height, void *data) +{ + struct color *color = data; + struct rectangle allocation; + struct wl_surface *surface = widget_get_wl_surface(color->widget); + struct wl_subsurface *subsurface = widget_get_wl_subsurface(color->widget); + + widget_get_allocation(parent_widget, &allocation); + wl_subsurface_set_position(subsurface, allocation.x, allocation.y); + + wp_viewport_set_destination(color->viewport, width, height); + + wl_surface_commit(surface); +} + +static void +set_empty_input_region(struct color *color, struct widget *widget) +{ + struct wl_region *region; + struct wl_compositor *compositor; + struct wl_surface *surface = widget_get_wl_surface(widget); + + compositor = display_get_compositor(color->display); + region = wl_compositor_create_region(compositor); + wl_surface_set_input_region(surface, region); + wl_region_destroy(region); +} + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + wl_buffer_destroy(buffer); +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static void +set_single_pixel(struct color *color, struct widget *widget) +{ + struct wl_surface *surface = widget_get_wl_surface(widget); + struct wl_buffer *buffer = + wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(color->single_pixel_manager, + color->pixel_color.r, + color->pixel_color.g, + color->pixel_color.b, + color->pixel_color.a); + wl_buffer_add_listener(buffer, &buffer_listener, NULL); + wl_surface_attach(surface, buffer, 0, 0); +} + +static void +image_description_failed(void *data, + struct xx_image_description_v4 *xx_image_description_v4, + uint32_t cause, const char *msg) +{ + enum image_description_status *image_desc_status = data; + + fprintf(stderr, "Failed to create image description: %u - %s\n", + cause, msg); + + *image_desc_status = IMAGE_DESCRIPTION_FAILED; +} + +static void +image_description_ready(void *data, struct xx_image_description_v4 *xx_image_description_v4, + uint32_t identity) +{ + enum image_description_status *image_desc_status = data; + + *image_desc_status = IMAGE_DESCRIPTION_READY; +} + +static const struct xx_image_description_v4_listener image_description_listener = { + image_description_failed, + image_description_ready, +}; + +static struct xx_image_description_v4 * +create_image_description(struct color *color, uint32_t primaries_named, uint32_t tf_named) +{ + struct xx_image_description_creator_params_v4 *params_creator; + struct xx_image_description_v4 *image_description; + enum image_description_status image_desc_status = IMAGE_DESCRIPTION_NOT_CREATED; + int ret = 0; + + params_creator = xx_color_manager_v4_new_parametric_creator(color->color_manager); + xx_image_description_creator_params_v4_set_primaries_named(params_creator, primaries_named); + xx_image_description_creator_params_v4_set_tf_named(params_creator, tf_named); + if (color->min_lum != -1 && color->max_lum != -1 && color->ref_lum != -1) + xx_image_description_creator_params_v4_set_luminances(params_creator, + color->min_lum * 10000, + color->max_lum, + color->ref_lum); + + image_description = xx_image_description_creator_params_v4_create(params_creator); + xx_image_description_v4_add_listener(image_description, + &image_description_listener, + &image_desc_status); + + while (ret != -1 && image_desc_status == IMAGE_DESCRIPTION_NOT_CREATED) + ret = wl_display_dispatch(display_get_display(color->display)); + if (ret == -1) { + xx_image_description_v4_destroy(image_description); + fprintf(stderr, "Error when creating the image description: %s\n", strerror(errno)); + return NULL; + } + + if (image_desc_status == IMAGE_DESCRIPTION_FAILED) { + xx_image_description_v4_destroy(image_description); + return NULL; + } + + assert(image_desc_status == IMAGE_DESCRIPTION_READY); + + return image_description; +} + +static bool +set_image_description(struct color *color, struct widget *widget) +{ + struct xx_image_description_v4 *image_description; + + image_description = + create_image_description(color, + color->primaries, + color->transfer_function); + if (!image_description) + return false; + + xx_color_management_surface_v4_set_image_description( + color->color_surface, + image_description, + XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL); + + xx_image_description_v4_destroy(image_description); + + return true; +} + +int +main(int argc, char *argv[]) +{ + struct color *color; + + if (parse_options(cli_options, ARRAY_LENGTH(cli_options), &argc, argv) > 1) + usage(argv[0], EXIT_FAILURE); + + if (opt_help) + usage(argv[0], EXIT_SUCCESS); + + color = xzalloc(sizeof *color); + if (!validate_options(color)) { + color_destroy(color); + exit(EXIT_FAILURE); + } + + color->display = display_create(&argc, argv); + if (!color->display) { + color_destroy(color); + exit(EXIT_FAILURE); + } + + color->window = window_create(color->display); + color->parent_widget = window_frame_create(color->window, color); + color->widget = window_add_subsurface(color->window, color, SUBSURFACE_SYNCHRONIZED); + + display_set_user_data(color->display, color); + display_set_global_handler(color->display, global_handler); + wl_display_roundtrip(display_get_display(color->display)); + + if (!check_color_requirements(color)) { + color_destroy(color); + exit(EXIT_SUCCESS); + } + + window_unset_shadow(color->window); + window_set_title(color->window, "Color"); + window_set_appid(color->window, "org.freedesktop.weston.color"); + /* The first resize call sets the min size, + * setting 0, 0 sets a default size */ + window_schedule_resize(color->window, 0, 0); + window_schedule_resize(color->window, opt_width, opt_height); + + widget_set_resize_handler(color->parent_widget, resize_handler); + widget_set_use_cairo(color->widget, 0); + + set_empty_input_region(color, color->widget); + set_single_pixel(color, color->widget); + + if (set_image_description(color, color->widget)) + display_run(color->display); + + color_destroy(color); + + return 0; +} diff --git a/clients/meson.build b/clients/meson.build index 44a84b9d..f8641376 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -269,6 +269,15 @@ demo_clients = [ 'basename': 'cliptest', 'dep_objs': [ dep_vertex_clipping, dep_matrix_c ] }, + { + 'basename': 'color', + 'add_sources': [ + color_management_v1_client_protocol_h, + color_management_v1_protocol_c, + single_pixel_buffer_v1_client_protocol_h, + single_pixel_buffer_v1_protocol_c, + ], + }, { 'basename': 'constraints', 'add_sources': [