weston/tests/color-management-test.c
Loïc Molinari 343adb2acd tests: Speed up runtime using immediate repaint on capture
The test suite is throttled by the headless backend repaint
timer. This commit uses the headless refresh rate option to speed up
runtime by using the immediate repaint-only-on-capture mode by
default. Tests which don't support that mode yet override the refresh
value to use the highest rate possible.

Fixes #682

Signed-off-by: Loïc Molinari <loic.molinari@collabora.com>
2024-02-22 14:26:32 +00:00

983 lines
31 KiB
C

/*
* 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"
#include "color-properties.h"
#include "weston-test-client-helper.h"
#include "weston-test-fixture-compositor.h"
#include "shared/xalloc.h"
#include "lcms_util.h"
#include "color-management-v1-client-protocol.h"
#include <fcntl.h>
#include <sys/stat.h>
static char srgb_icc_profile_path[500] = "\0";
enum image_descr_info_event {
IMAGE_DESCR_INFO_EVENT_ICC_FD = 1,
IMAGE_DESCR_INFO_EVENT_PRIMARIES_NAMED,
IMAGE_DESCR_INFO_EVENT_PRIMARIES,
IMAGE_DESCR_INFO_EVENT_TF_NAMED,
IMAGE_DESCR_INFO_EVENT_TF_POWER_EXP,
IMAGE_DESCR_INFO_EVENT_TARGET_PRIMARIES,
IMAGE_DESCR_INFO_EVENT_TARGET_MAXCLL,
IMAGE_DESCR_INFO_EVENT_TARGET_MAXFALL,
IMAGE_DESCR_INFO_EVENT_TARGET_LUMINANCE,
};
const struct lcms_pipeline pipeline_sRGB = {
.color_space = "sRGB",
.prim_output = {
.Red = { 0.640, 0.330, 1.0 },
.Green = { 0.300, 0.600, 1.0 },
.Blue = { 0.150, 0.060, 1.0 }
},
.pre_fn = TRANSFER_FN_SRGB_EOTF,
.mat = LCMSMAT3(1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0),
.mat2XYZ = LCMSMAT3(0.436037, 0.385124, 0.143039,
0.222482, 0.716913, 0.060605,
0.013922, 0.097078, 0.713899),
.post_fn = TRANSFER_FN_SRGB_EOTF_INVERSE
};
struct image_description {
struct xx_image_description_v2 *xx_image_descr;
bool ready;
/* color_manager::image_descr_list */
struct wl_list link;
/* For ICC-based image descriptions. */
int32_t icc_fd;
uint32_t icc_size;
/* For parametric images descriptions. */
enum xx_color_manager_v2_primaries primaries_named;
struct weston_color_gamut primaries;
enum xx_color_manager_v2_transfer_function tf_named;
float tf_power;
struct weston_color_gamut target_primaries;
float target_min_lum, target_max_lum;
float target_max_cll;
float target_max_fall;
};
struct image_description_info {
struct xx_image_description_info_v2 *xx_image_description_info;
struct image_description *image_descr;
/* Bitfield that holds what events the compositor has sent us through
* the image_descr_info object. For each event image_descr_info_event v
* received, the bit v of this bitfield will be set to 1. */
uint32_t events_received;
};
struct color_manager {
struct xx_color_manager_v2 *manager;
struct xx_color_management_output_v2 *output;
struct xx_color_management_surface_v2 *surface;
struct wl_list image_descr_list; /* image_description::link */
/* Bitfield that holds what color features are supported. If enum
* supported_color_feature v is supported, bit v will be set to 1. */
uint32_t supported_features;
/* Bitfield that holds what rendering intents are supported. If enum
* supported_render_intent v is supported, bit v will be set to 1. */
uint32_t supported_rendering_intents;
};
static struct image_description *
image_description_create(void)
{
struct image_description *image_descr = xzalloc(sizeof(*image_descr));
return image_descr;
}
static void
image_description_destroy(struct image_description *image_descr)
{
wl_list_remove(&image_descr->link);
xx_image_description_v2_destroy(image_descr->xx_image_descr);
free(image_descr);
}
static void
image_descr_ready(void *data, struct xx_image_description_v2 *xx_image_description_v2,
uint32_t identity)
{
struct image_description *image_descr = data;
image_descr->ready = true;
}
static void
image_descr_failed(void *data, struct xx_image_description_v2 *xx_image_description_v2,
uint32_t cause, const char *msg)
{
testlog("Failed to create image description:\n" \
" cause: %u, msg: %s\n", cause, msg);
}
static const struct xx_image_description_v2_listener
image_descr_iface = {
.ready = image_descr_ready,
.failed = image_descr_failed,
};
static void
image_descr_info_received(struct image_description_info *image_descr_info,
enum image_descr_info_event ev)
{
/* TODO: replace this assert with weston_assert_uint32_mask_bit_is_clear
* when we start using weston-assert in the test suite. */
assert(!((image_descr_info->events_received >> ev) & 1));
image_descr_info->events_received |= (1 << ev);
}
static void
image_descr_info_primaries(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2,
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)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
image_descr_info_received(image_descr_info,
IMAGE_DESCR_INFO_EVENT_PRIMARIES);
image_descr->primaries.primary[0].x = r_x / 10000.0;
image_descr->primaries.primary[0].y = r_y / 10000.0;
image_descr->primaries.primary[1].x = g_x / 10000.0;
image_descr->primaries.primary[1].y = g_y / 10000.0;
image_descr->primaries.primary[2].x = b_x / 10000.0;
image_descr->primaries.primary[2].y = b_y / 10000.0;
image_descr->primaries.white_point.x = w_x / 10000.0;
image_descr->primaries.white_point.y = w_y / 10000.0;
}
static void
image_descr_info_primaries_named(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2,
uint32_t primaries)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
image_descr_info_received(image_descr_info,
IMAGE_DESCR_INFO_EVENT_PRIMARIES_NAMED);
image_descr->primaries_named = primaries;
}
static void
image_descr_info_tf_named(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2,
uint32_t tf)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
image_descr_info_received(image_descr_info,
IMAGE_DESCR_INFO_EVENT_TF_NAMED);
image_descr->tf_named = tf;
}
static void
image_descr_info_tf_power(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2,
uint32_t tf_power)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
image_descr_info_received(image_descr_info,
IMAGE_DESCR_INFO_EVENT_TF_POWER_EXP);
image_descr->tf_power = tf_power / 10000.0;
}
static void
image_descr_info_target_primaries(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2,
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)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
image_descr_info_received(image_descr_info,
IMAGE_DESCR_INFO_EVENT_TARGET_PRIMARIES);
image_descr->target_primaries.primary[0].x = r_x / 10000.0;
image_descr->target_primaries.primary[0].y = r_y / 10000.0;
image_descr->target_primaries.primary[1].x = g_x / 10000.0;
image_descr->target_primaries.primary[1].y = g_y / 10000.0;
image_descr->target_primaries.primary[2].x = b_x / 10000.0;
image_descr->target_primaries.primary[2].y = b_y / 10000.0;
image_descr->target_primaries.white_point.x = w_x / 10000.0;
image_descr->target_primaries.white_point.y = w_y / 10000.0;
}
static void
image_descr_info_target_luminance(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2,
uint32_t min_lum, uint32_t max_lum)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
image_descr_info_received(image_descr_info,
IMAGE_DESCR_INFO_EVENT_TARGET_LUMINANCE);
image_descr->target_min_lum = min_lum / 10000.0;
image_descr->target_max_lum = max_lum;
}
static void
image_descr_info_target_max_cll(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2,
uint32_t maxCLL)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
image_descr_info_received(image_descr_info,
IMAGE_DESCR_INFO_EVENT_TARGET_MAXCLL);
image_descr->target_max_cll = maxCLL;
}
static void
image_descr_info_target_max_fall(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2,
uint32_t maxFALL)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
image_descr_info_received(image_descr_info,
IMAGE_DESCR_INFO_EVENT_TARGET_MAXFALL);
image_descr->target_max_fall = maxFALL;
}
static void
image_descr_info_icc_file_event(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2,
int32_t icc_fd, uint32_t icc_size)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
image_descr_info_received(image_descr_info,
IMAGE_DESCR_INFO_EVENT_ICC_FD);
image_descr->icc_fd = icc_fd;
image_descr->icc_size = icc_size;
}
static bool
are_events_received_valid(struct image_description_info *image_descr_info)
{
uint32_t events_received = image_descr_info->events_received;
struct image_description *image_descr = image_descr_info->image_descr;
/* ICC-based image description... */
if ((events_received >> IMAGE_DESCR_INFO_EVENT_ICC_FD) & 1) {
/* ...so we shouldn't have receive any other events. */
if ((1 << IMAGE_DESCR_INFO_EVENT_ICC_FD) == events_received)
return true;
testlog(" Error: ICC image description but also received " \
"parametric events\n");
return false;
}
/* Non-ICC based image description, let's make sure that the received
* parameters make sense. */
bool received_primaries, received_primaries_named;
bool received_tf_power, received_tf_named;
/* Should have received the primaries somewhow. */
received_primaries_named = (events_received >>
IMAGE_DESCR_INFO_EVENT_PRIMARIES_NAMED) & 1;
received_primaries = (events_received >>
IMAGE_DESCR_INFO_EVENT_PRIMARIES) & 1;
if (!(received_primaries_named || received_primaries)) {
testlog(" Error: parametric image description but no " \
"primaries received\n");
return false;
}
/* Should have received tf somehow. */
received_tf_named = (events_received >>
IMAGE_DESCR_INFO_EVENT_TF_NAMED) & 1;
received_tf_power = (events_received >>
IMAGE_DESCR_INFO_EVENT_TF_POWER_EXP) & 1;
if (!(received_tf_named || received_tf_power)) {
testlog(" Error: parametric image description but no " \
" tf received\n");
return false;
}
/* If we received tf named and exp power, they must match. */
if (received_tf_named && received_tf_power) {
if (image_descr->tf_named != XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_GAMMA22 &&
image_descr->tf_named != XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_GAMMA28) {
testlog(" Error: parametric image description tf " \
"named is not pure power-law, but still received " \
"tf power event\n");
return false;
} else if (image_descr->tf_named == XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_GAMMA22 &&
image_descr->tf_power != 2.2f) {
testlog(" Error: parametric image description tf named " \
"is pure power-law 2.2, but tf power received is %f\n",
image_descr->tf_power);
return false;
} else if (image_descr->tf_named == XX_COLOR_MANAGER_V2_TRANSFER_FUNCTION_GAMMA28 &&
image_descr->tf_power != 2.8f) {
testlog(" Error: parametric image description tf named " \
"is pure power-law 2.8, but tf power received is %f\n",
image_descr->tf_power);
return false;
}
}
/* TODO: when target primaries, luminance, maxcll and maxfall are
* allowed? */
return true;
}
static void
image_descr_info_done(void *data,
struct xx_image_description_info_v2 *xx_image_description_info_v2)
{
struct image_description_info *image_descr_info = data;
struct image_description *image_descr = image_descr_info->image_descr;
testlog("Image description info %p done:\n", xx_image_description_info_v2);
assert(are_events_received_valid(image_descr_info));
/* ICC based image description */
if ((image_descr_info->events_received >> IMAGE_DESCR_INFO_EVENT_ICC_FD) & 1) {
testlog(" ICC file: fd %d, icc size %u.\n",
image_descr->icc_fd, image_descr->icc_size);
close(image_descr->icc_fd);
return;
}
if ((image_descr_info->events_received >> IMAGE_DESCR_INFO_EVENT_PRIMARIES_NAMED) & 1)
testlog(" Primaries named: %u\n", image_descr->primaries_named);
if ((image_descr_info->events_received >> IMAGE_DESCR_INFO_EVENT_PRIMARIES) & 1)
testlog(" Primary primaries:\n" \
" red (x, y) = (%.4f, %.4f)\n" \
" green (x, y) = (%.4f, %.4f)\n" \
" blue (x, y) = (%.4f, %.4f)\n" \
" white point (x, y) = (%.4f, %.4f)\n",
image_descr->primaries.primary[0].x,
image_descr->primaries.primary[0].y,
image_descr->primaries.primary[1].x,
image_descr->primaries.primary[1].y,
image_descr->primaries.primary[2].x,
image_descr->primaries.primary[2].y,
image_descr->primaries.white_point.x,
image_descr->primaries.white_point.y);
if ((image_descr_info->events_received >> IMAGE_DESCR_INFO_EVENT_TF_NAMED) & 1)
testlog(" Transfer characteristics named: %u\n", image_descr->tf_named);
if ((image_descr_info->events_received >> IMAGE_DESCR_INFO_EVENT_TF_POWER_EXP) & 1)
testlog(" EOTF is a pure power-law curve of exp %.4f\n",
image_descr->tf_power);
if ((image_descr_info->events_received >> IMAGE_DESCR_INFO_EVENT_TARGET_PRIMARIES) & 1)
testlog(" Target primaries:\n" \
" red (x, y) = (%.4f, %.4f)\n" \
" green (x, y) = (%.4f, %.4f)\n" \
" blue (x, y) = (%.4f, %.4f)\n" \
" white point (x, y) = (%.4f, %.4f)\n",
image_descr->target_primaries.primary[0].x,
image_descr->target_primaries.primary[0].y,
image_descr->target_primaries.primary[1].x,
image_descr->target_primaries.primary[1].y,
image_descr->target_primaries.primary[2].x,
image_descr->target_primaries.primary[2].y,
image_descr->target_primaries.white_point.x,
image_descr->target_primaries.white_point.y);
if ((image_descr_info->events_received >> IMAGE_DESCR_INFO_EVENT_TARGET_LUMINANCE) & 1)
testlog(" Target luminance: min: %.4f, max %.4f\n",
image_descr->target_min_lum, image_descr->target_max_lum);
if ((image_descr_info->events_received >> IMAGE_DESCR_INFO_EVENT_TARGET_MAXCLL) & 1)
testlog(" Target maxCLL: %.4f\n", image_descr->target_max_cll);
if ((image_descr_info->events_received >> IMAGE_DESCR_INFO_EVENT_TARGET_MAXFALL) & 1)
testlog(" Target maxFALL: %.4f\n", image_descr->target_max_fall);
}
static const struct xx_image_description_info_v2_listener
image_descr_info_iface = {
.primaries = image_descr_info_primaries,
.primaries_named = image_descr_info_primaries_named,
.tf_named = image_descr_info_tf_named,
.tf_power = image_descr_info_tf_power,
.target_primaries = image_descr_info_target_primaries,
.target_luminance = image_descr_info_target_luminance,
.target_max_cll = image_descr_info_target_max_cll,
.target_max_fall = image_descr_info_target_max_fall,
.icc_file = image_descr_info_icc_file_event,
.done = image_descr_info_done,
};
static void
cm_supported_intent(void *data, struct xx_color_manager_v2 *xx_color_manager_v2,
uint32_t render_intent)
{
struct color_manager *cm = data;
cm->supported_rendering_intents |= (1 << render_intent);
}
static void
cm_supported_feature(void *data, struct xx_color_manager_v2 *xx_color_manager_v2,
uint32_t feature)
{
struct color_manager *cm = data;
cm->supported_features |= (1 << feature);
}
static void
cm_supported_tf_named(void *data, struct xx_color_manager_v2 *xx_color_manager_v2,
uint32_t tf_code)
{
/* only used to create image descriptions using parameters, which is
* still unsupported by Weston. */
}
static void
cm_supported_primaries_named(void *data, struct xx_color_manager_v2 *xx_color_manager_v2,
uint32_t primaries_code)
{
/* only used to create image descriptions using parameters, which is
* still unsupported by Weston. */
}
static const struct xx_color_manager_v2_listener
cm_iface = {
.supported_intent = cm_supported_intent,
.supported_feature = cm_supported_feature,
.supported_tf_named = cm_supported_tf_named,
.supported_primaries_named = cm_supported_primaries_named,
};
static void
color_manager_init(struct color_manager *cm, struct client *client)
{
memset(cm, 0, sizeof(*cm));
wl_list_init(&cm->image_descr_list);
cm->manager = bind_to_singleton_global(client,
&xx_color_manager_v2_interface,
1);
xx_color_manager_v2_add_listener(cm->manager, &cm_iface, cm);
cm->output = xx_color_manager_v2_get_output(cm->manager,
client->output->wl_output);
cm->surface = xx_color_manager_v2_get_surface(cm->manager,
client->surface->wl_surface);
client_roundtrip(client);
/* For now, Weston only supports the ICC image description creator. All
* the parametric parts of the protocol are still unsupported. */
assert(cm->supported_features == (1 << XX_COLOR_MANAGER_V2_FEATURE_ICC_V2_V4));
/* Weston supports all rendering intents. */
assert(cm->supported_rendering_intents == ((1 << XX_COLOR_MANAGER_V2_RENDER_INTENT_PERCEPTUAL) |
(1 << XX_COLOR_MANAGER_V2_RENDER_INTENT_RELATIVE) |
(1 << XX_COLOR_MANAGER_V2_RENDER_INTENT_SATURATION) |
(1 << XX_COLOR_MANAGER_V2_RENDER_INTENT_ABSOLUTE) |
(1 << XX_COLOR_MANAGER_V2_RENDER_INTENT_RELATIVE_BPC)));
}
static void
color_manager_fini(struct color_manager *cm)
{
struct image_description *image_descr, *tmp;
wl_list_for_each_safe(image_descr, tmp, &cm->image_descr_list, link)
image_description_destroy(image_descr);
xx_color_management_output_v2_destroy(cm->output);
xx_color_management_surface_v2_destroy(cm->surface);
xx_color_manager_v2_destroy(cm->manager);
}
static struct image_description *
get_output_image_description(struct color_manager *cm)
{
struct image_description *image_descr = image_description_create();
image_descr->xx_image_descr =
xx_color_management_output_v2_get_image_description(cm->output);
xx_image_description_v2_add_listener(image_descr->xx_image_descr,
&image_descr_iface, image_descr);
wl_list_insert(&cm->image_descr_list, &image_descr->link);
return image_descr;
}
static struct image_description *
get_surface_preferred_image_description(struct color_manager *cm)
{
struct image_description *image_descr = image_description_create();
image_descr->xx_image_descr =
xx_color_management_surface_v2_get_preferred(cm->surface);
xx_image_description_v2_add_listener(image_descr->xx_image_descr,
&image_descr_iface, image_descr);
wl_list_insert(&cm->image_descr_list, &image_descr->link);
return image_descr;
}
static struct image_description *
create_icc_based_image_description(struct color_manager *cm,
struct xx_image_description_creator_icc_v2 *image_descr_creator_icc,
const char *icc_path)
{
struct image_description *image_descr = image_description_create();
int32_t icc_fd;
struct stat st;
icc_fd = open(icc_path, 'r');
assert(icc_fd >= 0);
assert(fstat(icc_fd, &st) == 0);
xx_image_description_creator_icc_v2_set_icc_file(image_descr_creator_icc,
icc_fd, 0, st.st_size);
image_descr->xx_image_descr =
xx_image_description_creator_icc_v2_create(image_descr_creator_icc);
xx_image_description_v2_add_listener(image_descr->xx_image_descr,
&image_descr_iface, image_descr);
wl_list_insert(&cm->image_descr_list, &image_descr->link);
close(icc_fd);
return image_descr;
}
static void
build_sRGB_icc_profile(const char *filename)
{
cmsHPROFILE profile;
double vcgt_exponents[COLOR_CHAN_NUM] = { 0.0 };
bool saved;
profile = build_lcms_matrix_shaper_profile_output(NULL, &pipeline_sRGB,
vcgt_exponents);
assert(profile);
saved = cmsSaveProfileToFile(profile, filename);
assert(saved);
cmsCloseProfile(profile);
}
static enum test_result_code
fixture_setup(struct weston_test_harness *harness)
{
struct compositor_setup setup;
compositor_setup_defaults(&setup);
setup.renderer = WESTON_RENDERER_GL;
setup.shell = SHELL_TEST_DESKTOP;
setup.refresh = HIGHEST_OUTPUT_REFRESH;
/* Create the sRGB ICC profile. We do that only once for this test
* program. */
if (strlen(srgb_icc_profile_path) == 0) {
char *tmp;
tmp = output_filename_for_test_program(THIS_TEST_NAME,
NULL, "icm");
assert(strlen(tmp) < ARRAY_LENGTH(srgb_icc_profile_path));
strcpy(srgb_icc_profile_path, tmp);
free(tmp);
build_sRGB_icc_profile(srgb_icc_profile_path);
}
weston_ini_setup(&setup,
cfgln("[core]"),
cfgln("color-management=true"));
return weston_test_harness_execute_as_client(harness, &setup);
}
DECLARE_FIXTURE_SETUP(fixture_setup);
TEST(smoke_test)
{
struct client *client;
struct color_manager cm;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
color_manager_fini(&cm);
client_destroy(client);
}
static void
image_descr_info_destroy(struct image_description_info *image_descr_info)
{
xx_image_description_info_v2_destroy(image_descr_info->xx_image_description_info);
free(image_descr_info);
}
static struct image_description_info *
image_descr_get_information(struct image_description *image_descr)
{
struct image_description_info *image_descr_info;
image_descr_info = xzalloc(sizeof(*image_descr_info));
image_descr_info->image_descr = image_descr;
image_descr_info->xx_image_description_info =
xx_image_description_v2_get_information(image_descr->xx_image_descr);
xx_image_description_info_v2_add_listener(image_descr_info->xx_image_description_info,
&image_descr_info_iface,
image_descr_info);
return image_descr_info;
}
TEST(output_get_image_description)
{
struct client *client;
struct color_manager cm;
struct image_description *image_descr;
struct image_description_info *image_descr_info;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
/* Get image description from output */
image_descr = get_output_image_description(&cm);
client_roundtrip(client);
assert(image_descr->ready);
/* Get output image description information */
image_descr_info = image_descr_get_information(image_descr);
client_roundtrip(client);
image_descr_info_destroy(image_descr_info);
color_manager_fini(&cm);
client_destroy(client);
}
TEST(surface_get_preferred_image_description)
{
struct client *client;
struct color_manager cm;
struct image_description *image_descr;
struct image_description_info *image_descr_info;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
/* Get preferred image description from surface */
image_descr = get_surface_preferred_image_description(&cm);
client_roundtrip(client);
assert(image_descr->ready);
/* Get surface image description information */
image_descr_info = image_descr_get_information(image_descr);
client_roundtrip(client);
image_descr_info_destroy(image_descr_info);
color_manager_fini(&cm);
client_destroy(client);
}
TEST(create_parametric_image_description_creator_object)
{
struct client *client;
struct color_manager cm;
struct xx_image_description_creator_params_v2 *param_creator;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
/* Parametric image description creator is still unsupported */
param_creator = xx_color_manager_v2_new_parametric_creator(cm.manager);
expect_protocol_error(client, &xx_color_manager_v2_interface,
XX_COLOR_MANAGER_V2_ERROR_UNSUPPORTED_FEATURE);
xx_image_description_creator_params_v2_destroy(param_creator);
color_manager_fini(&cm);
client_destroy(client);
}
TEST(create_image_description_before_setting_icc_file)
{
struct client *client;
struct color_manager cm;
struct xx_image_description_creator_icc_v2 *image_descr_creator_icc;
struct xx_image_description_v2 *image_desc;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
image_descr_creator_icc =
xx_color_manager_v2_new_icc_creator(cm.manager);
/* Try creating image description based on ICC profile but without
* setting the ICC file, what should fail.
*
* We expect a protocol error from unknown object, because the
* image_descr_creator_icc wl_proxy will get destroyed with the create
* call below. It is a destructor request. */
image_desc = xx_image_description_creator_icc_v2_create(image_descr_creator_icc);
expect_protocol_error(client, NULL,
XX_IMAGE_DESCRIPTION_CREATOR_ICC_V2_ERROR_INCOMPLETE_SET);
xx_image_description_v2_destroy(image_desc);
color_manager_fini(&cm);
client_destroy(client);
}
TEST(set_unreadable_icc_fd)
{
struct client *client;
struct color_manager cm;
struct xx_image_description_creator_icc_v2 *image_descr_creator_icc;
int32_t icc_fd;
struct stat st;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
image_descr_creator_icc =
xx_color_manager_v2_new_icc_creator(cm.manager);
/* The file is being open with WRITE, not READ permission. So the
* compositor should complain. */
icc_fd = open(srgb_icc_profile_path, O_WRONLY);
assert(icc_fd >= 0);
assert(fstat(icc_fd, &st) == 0);
/* Try setting the bad ICC file fd, it should fail. */
xx_image_description_creator_icc_v2_set_icc_file(image_descr_creator_icc,
icc_fd, 0, st.st_size);
expect_protocol_error(client, &xx_image_description_creator_icc_v2_interface,
XX_IMAGE_DESCRIPTION_CREATOR_ICC_V2_ERROR_BAD_FD);
close(icc_fd);
xx_image_description_creator_icc_v2_destroy(image_descr_creator_icc);
color_manager_fini(&cm);
client_destroy(client);
}
TEST(set_bad_icc_size_zero)
{
struct client *client;
struct color_manager cm;
struct xx_image_description_creator_icc_v2 *image_descr_creator_icc;
int32_t icc_fd;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
image_descr_creator_icc =
xx_color_manager_v2_new_icc_creator(cm.manager);
icc_fd = open(srgb_icc_profile_path, 'r');
assert(icc_fd >= 0);
/* Try setting ICC file with a bad size, it should fail. */
xx_image_description_creator_icc_v2_set_icc_file(image_descr_creator_icc,
icc_fd, 0, 0);
expect_protocol_error(client, &xx_image_description_creator_icc_v2_interface,
XX_IMAGE_DESCRIPTION_CREATOR_ICC_V2_ERROR_BAD_SIZE);
close(icc_fd);
xx_image_description_creator_icc_v2_destroy(image_descr_creator_icc);
color_manager_fini(&cm);
client_destroy(client);
}
TEST(set_bad_icc_non_seekable)
{
struct client *client;
struct color_manager cm;
struct xx_image_description_creator_icc_v2 *image_descr_creator_icc;
int32_t fds[2];
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
image_descr_creator_icc =
xx_color_manager_v2_new_icc_creator(cm.manager);
/* We need a non-seekable file, and pipes are non-seekable. */
assert(pipe(fds) >= 0);
/* Pretend that it has a valid size of 1024 bytes. That still should
* fail because the fd is non-seekable. */
xx_image_description_creator_icc_v2_set_icc_file(image_descr_creator_icc,
fds[0], 0, 1024);
expect_protocol_error(client, &xx_image_description_creator_icc_v2_interface,
XX_IMAGE_DESCRIPTION_CREATOR_ICC_V2_ERROR_BAD_FD);
close(fds[0]);
close(fds[1]);
xx_image_description_creator_icc_v2_destroy(image_descr_creator_icc);
color_manager_fini(&cm);
client_destroy(client);
}
TEST(set_icc_twice)
{
struct client *client;
struct color_manager cm;
struct xx_image_description_creator_icc_v2 *image_descr_creator_icc;
int32_t icc_fd;
struct stat st;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
image_descr_creator_icc =
xx_color_manager_v2_new_icc_creator(cm.manager);
icc_fd = open(srgb_icc_profile_path, 'r');
assert(icc_fd >= 0);
assert(fstat(icc_fd, &st) == 0);
xx_image_description_creator_icc_v2_set_icc_file(image_descr_creator_icc,
icc_fd, 0, st.st_size);
client_roundtrip(client);
/* Set the ICC again, what should fail. */
xx_image_description_creator_icc_v2_set_icc_file(image_descr_creator_icc,
icc_fd, 0, st.st_size);
expect_protocol_error(client, &xx_image_description_creator_icc_v2_interface,
XX_IMAGE_DESCRIPTION_CREATOR_ICC_V2_ERROR_ALREADY_SET);
close(icc_fd);
xx_image_description_creator_icc_v2_destroy(image_descr_creator_icc);
color_manager_fini(&cm);
client_destroy(client);
}
TEST(create_icc_image_description_no_info)
{
struct client *client;
struct color_manager cm;
struct xx_image_description_creator_icc_v2 *image_descr_creator_icc;
struct image_description *image_descr;
struct image_description_info *image_descr_info;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
image_descr_creator_icc =
xx_color_manager_v2_new_icc_creator(cm.manager);
/* Create image description based on ICC profile */
image_descr = create_icc_based_image_description(&cm, image_descr_creator_icc,
srgb_icc_profile_path);
client_roundtrip(client);
/* Get image description information, and that should fail. Images
* descriptions that we create do not accept this request. */
image_descr_info = image_descr_get_information(image_descr);
expect_protocol_error(client, &xx_image_description_v2_interface,
XX_IMAGE_DESCRIPTION_V2_ERROR_NO_INFORMATION);
image_descr_info_destroy(image_descr_info);
color_manager_fini(&cm);
client_destroy(client);
}
TEST(set_surface_image_description)
{
struct client *client;
struct color_manager cm;
struct xx_image_description_creator_icc_v2 *image_descr_creator_icc;
struct image_description *image_descr;
client = create_client_and_test_surface(100, 100, 100, 100);
color_manager_init(&cm, client);
image_descr_creator_icc =
xx_color_manager_v2_new_icc_creator(cm.manager);
/* Create image description based on ICC profile */
image_descr = create_icc_based_image_description(&cm, image_descr_creator_icc,
srgb_icc_profile_path);
client_roundtrip(client);
/* Set surface image description */
xx_color_management_surface_v2_set_image_description(cm.surface,
image_descr->xx_image_descr,
XX_COLOR_MANAGER_V2_RENDER_INTENT_PERCEPTUAL);
client_roundtrip(client);
color_manager_fini(&cm);
client_destroy(client);
}