[RFC PATCH 2/6] Attach input profiles and build corresponding LUTs

Niels Ole Salscheider niels_ole at salscheider-online.de
Sun Mar 30 04:36:33 PDT 2014


This implements the functionality to attach a profile to a surface
in weston. An LUT is built from the profile that can be used to
transform colors from the input color space to the blending color
space.

This patch uses the sRGB color space as blending space because it
uses 8 bit LUTs for now and I want to avoid additional banding. In
the long term we should use a linear blending space though to get
correct results.

Signed-off-by: Niels Ole Salscheider <niels_ole at salscheider-online.de>
---
 Makefile.am      |   3 +
 src/compositor.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/compositor.h |  20 ++++++
 3 files changed, 207 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index ec0a30b..c62ab0d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -42,6 +42,9 @@ weston_CPPFLAGS = $(AM_CPPFLAGS) -DIN_WESTON
 weston_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) $(LIBUNWIND_CFLAGS)
 weston_LDADD = $(COMPOSITOR_LIBS) $(LIBUNWIND_LIBS) \
 	$(DLOPEN_LIBS) -lm libshared.la
+if HAVE_LCMS
+weston_LDADD += $(LCMS_LIBS)
+endif
 
 weston_SOURCES =					\
 	src/git-version.h				\
diff --git a/src/compositor.c b/src/compositor.c
index 7c29d51..6adc2a0 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -54,6 +54,7 @@
 
 #include "compositor.h"
 #include "scaler-server-protocol.h"
+#include "colorcorrection-server-protocol.h"
 #include "../shared/os-compatibility.h"
 #include "git-version.h"
 #include "version.h"
@@ -384,6 +385,58 @@ weston_view_create(struct weston_surface *surface)
 	return view;
 }
 
+#ifdef HAVE_LCMS
+static void
+weston_free_clut(struct weston_clut *clut)
+{
+	clut->points = 0;
+	if (clut->data)
+		free(clut->data);
+	clut->data = NULL;
+}
+
+static void
+weston_build_clut(struct weston_clut *clut, cmsHPROFILE input_profile,
+			  cmsHPROFILE output_profile)
+{
+	cmsHTRANSFORM xform;
+	unsigned r, g, b;
+
+	weston_free_clut(clut);
+
+	if (input_profile == output_profile)
+		return;
+	if (input_profile == NULL)
+		return;
+	if (output_profile == NULL)
+		return;
+
+	clut->points = 64;
+
+	xform = cmsCreateTransform(input_profile, TYPE_RGB_8, output_profile,
+				   TYPE_RGB_8, INTENT_PERCEPTUAL, 0);
+
+	clut->data = malloc(clut->points * clut->points * clut->points *
+			    3 * sizeof(char));
+
+	for (b = 0; b < clut->points; b++)
+		for (g = 0; g < clut->points; g++)
+			for (r = 0; r < clut->points; r++) {
+				char in[3];
+				unsigned entry = 3 * (r + clut->points *
+						 (g + clut->points * b));
+				const float step = 255.0 / (clut->points - 1);
+				in[0] = (char) (r * step + 0.5);
+				in[1] = (char) (g * step + 0.5);
+				in[2] = (char) (b * step + 0.5);
+				cmsDoTransform(xform, in,
+					       &clut->data[entry], 1);
+			}
+
+	cmsDeleteTransform(xform);
+}
+#endif
+
 WL_EXPORT struct weston_surface *
 weston_surface_create(struct weston_compositor *compositor)
 {
@@ -427,6 +480,13 @@ weston_surface_create(struct weston_compositor *compositor)
 	wl_list_init(&surface->subsurface_list);
 	wl_list_init(&surface->subsurface_list_pending);
 
+#ifdef HAVE_LCMS
+	surface->color_profile_mode = WL_COLORCORRECTION_MODE_SRGB;
+	surface->color_profile = compositor->srgb_profile;
+	weston_build_clut(&surface->clut, surface->color_profile,
+			  compositor->blending_profile);
+#endif
+
 	return surface;
 }
 
@@ -1376,6 +1436,16 @@ weston_surface_destroy(struct weston_surface *surface)
 	pixman_region32_fini(&surface->opaque);
 	pixman_region32_fini(&surface->input);
 
+#ifdef HAVE_LCMS
+	weston_free_clut(&surface->clut);
+
+	if (surface->color_profile &&
+	    surface->color_profile_mode == WL_COLORCORRECTION_MODE_PROFILE) {
+		cmsCloseProfile(surface->color_profile);
+		surface->color_profile = 0;
+	}
+#endif
+
 	wl_list_for_each_safe(cb, next, &surface->frame_callback_list, link)
 		wl_resource_destroy(cb->resource);
 
@@ -3465,6 +3535,85 @@ bind_scaler(struct wl_client *client,
 }
 
 static void
+colorcorrection_set_profile(struct wl_client *client,
+			    struct wl_resource *colorcorrection,
+			    struct wl_resource *surface_resource,
+			    uint32_t mode,
+			    struct wl_array *data)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(surface_resource);
+
+#ifdef HAVE_LCMS
+	struct weston_compositor *compositor =
+		wl_resource_get_user_data(colorcorrection);
+
+	if (surface->color_profile &&
+	    surface->color_profile_mode == WL_COLORCORRECTION_MODE_PROFILE) {
+		cmsCloseProfile(surface->color_profile);
+		surface->color_profile = 0;
+	}
+#endif
+
+	surface->color_profile_mode = mode;
+	switch (mode) {
+	case WL_COLORCORRECTION_MODE_SRGB:
+#ifdef HAVE_LCMS
+		surface->color_profile = compositor->srgb_profile;
+		weston_build_clut(&surface->clut, surface->color_profile,
+				  compositor->blending_profile);
+#endif
+		break;
+	case WL_COLORCORRECTION_MODE_BLENDING_SPACE:
+	case WL_COLORCORRECTION_MODE_UNCORRECTED:
+#ifdef HAVE_LCMS
+		weston_free_clut(&surface->clut);
+#endif
+		break;
+	case WL_COLORCORRECTION_MODE_PROFILE:
+#ifdef HAVE_LCMS
+		surface->color_profile = cmsOpenProfileFromMem(data->data,
+							       data->size);
+		if (!surface->color_profile) {
+			wl_resource_post_error(colorcorrection,
+				WL_COLORCORRECTION_ERROR_INVALID_PROFILE,
+				"the passed ICC profile is not valid");
+		return;
+		}
+		weston_build_clut(&surface->clut, surface->color_profile,
+				  compositor->blending_profile);
+#endif
+		break;
+	default:
+		wl_resource_post_error(colorcorrection,
+			WL_COLORCORRECTION_ERROR_INVALID_MODE,
+			"the passed mode is not a valid mode");
+		return;
+	}
+}
+
+static const struct wl_colorcorrection_interface colorcorrection_interface = {
+	colorcorrection_set_profile
+};
+
+static void
+bind_colorcorrection(struct wl_client *client,
+		     void *data, uint32_t version, uint32_t id)
+{
+	struct wl_resource *resource;
+
+	resource = wl_resource_create(client, &wl_colorcorrection_interface,
+				      1, id);
+	if (resource == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_resource_set_implementation(resource, &colorcorrection_interface,
+				       data, NULL);
+}
+
+static void
 compositor_bind(struct wl_client *client,
 		void *data, uint32_t version, uint32_t id)
 {
@@ -3558,6 +3707,10 @@ weston_compositor_init(struct weston_compositor *ec,
 			      ec, bind_scaler))
 		return -1;
 
+	if (!wl_global_create(display, &wl_colorcorrection_interface, 1,
+			      ec, bind_colorcorrection))
+		return -1;
+
 	wl_list_init(&ec->view_list);
 	wl_list_init(&ec->plane_list);
 	wl_list_init(&ec->layer_list);
@@ -4021,6 +4174,12 @@ int main(int argc, char *argv[])
 	int32_t version = 0;
 	struct weston_config *config;
 	struct weston_config_section *section;
+#ifdef HAVE_LCMS
+	struct weston_view *view;
+	struct wl_array profile_data;
+	char *ptr;
+	uint32_t size;
+#endif
 
 	const struct weston_option core_options[] = {
 		{ WESTON_OPTION_STRING, "backend", 'B', &option_backend },
@@ -4110,6 +4269,27 @@ int main(int argc, char *argv[])
 	ec->idle_time = idle_time;
 	ec->default_pointer_grab = NULL;
 
+#ifdef HAVE_LCMS
+	ec->srgb_profile = cmsCreate_sRGBProfile();
+	/* TODO: We should really use a linear blending space. But this might
+	 * cause additional banding since we only use an 8 bit LUT for now. */
+	ec->blending_profile = ec->srgb_profile;
+
+	cmsSaveProfileToMem(ec->blending_profile, NULL, &size);
+	if (size) {
+		wl_array_init(&profile_data);
+		ptr = wl_array_add(&profile_data, size);
+		cmsSaveProfileToMem(ec->blending_profile, ptr, &size);
+
+		wl_list_for_each(view, &ec->view_list, link) {
+			wl_colorcorrection_send_blending_space(
+				view->surface->resource, &profile_data);
+		}
+
+		wl_array_release(&profile_data);
+	}
+#endif
+
 	setenv("WAYLAND_DISPLAY", socket_name, 1);
 
 	if (option_shell)
@@ -4167,6 +4347,10 @@ int main(int argc, char *argv[])
 	ec->destroy(ec);
 	wl_display_destroy(display);
 
+#ifdef HAVE_LCMS
+	cmsCloseProfile(ec->srgb_profile);
+#endif
+
 	weston_log_file_close();
 
 	return ret;
diff --git a/src/compositor.h b/src/compositor.h
index 8fb8afb..10d461f 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -39,6 +39,10 @@ extern "C" {
 #include "config-parser.h"
 #include "zalloc.h"
 
+#ifdef HAVE_LCMS
+#include <lcms2.h>
+#endif
+
 #ifndef MIN
 #define MIN(x,y) (((x) < (y)) ? (x) : (y))
 #endif
@@ -172,6 +176,11 @@ enum weston_mode_switch_op {
 	WESTON_MODE_SWITCH_RESTORE_NATIVE
 };
 
+struct weston_clut {
+	unsigned points;
+	char *data;
+};
+
 struct weston_output {
 	uint32_t id;
 	char *name;
@@ -633,6 +642,11 @@ struct weston_compositor {
 
 	/* Raw keyboard processing (no libxkbcommon initialization or handling) */
 	int use_xkbcommon;
+
+#ifdef HAVE_LCMS
+	cmsHPROFILE srgb_profile;
+	cmsHPROFILE blending_profile;
+#endif
 };
 
 struct weston_buffer {
@@ -859,6 +873,12 @@ struct weston_surface {
 	struct weston_buffer_viewport buffer_viewport;
 	int keep_buffer; /* bool for backends to prevent early release */
 
+	struct weston_clut clut;
+	uint32_t color_profile_mode;
+#ifdef HAVE_LCMS
+	cmsHPROFILE color_profile;
+#endif
+
 	/* wl_viewport resource for this surface */
 	struct wl_resource *viewport_resource;
 
-- 
1.9.1



More information about the wayland-devel mailing list