Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/render/color_lcms2.c (2.9K)
1 #include <assert.h>
2 #include <lcms2.h>
3 #include <stdlib.h>
4 #include <wlr/util/log.h>
5 #include <wlr/render/color.h>
6 #include "render/color.h"
7
8 struct wlr_color_transform_lcms2 {
9 struct wlr_color_transform base;
10
11 cmsContext ctx;
12 cmsHTRANSFORM lcms;
13 };
14
15 static const cmsCIExyY srgb_whitepoint = { 0.3127, 0.3291, 1 };
16
17 static const cmsCIExyYTRIPLE srgb_primaries = {
18 .Red = { 0.64, 0.33, 1 },
19 .Green = { 0.3, 0.6, 1 },
20 .Blue = { 0.15, 0.06, 1},
21 };
22
23 static void handle_lcms_error(cmsContext ctx, cmsUInt32Number code, const char *text) {
24 wlr_log(WLR_ERROR, "[lcms] %s", text);
25 }
26
27 struct wlr_color_transform *wlr_color_transform_init_linear_to_icc(
28 const void *data, size_t size) {
29 struct wlr_color_transform_lcms2 *tx = NULL;
30
31 cmsContext ctx = cmsCreateContext(NULL, NULL);
32 if (ctx == NULL) {
33 wlr_log(WLR_ERROR, "cmsCreateContext failed");
34 return NULL;
35 }
36
37 cmsSetLogErrorHandlerTHR(ctx, handle_lcms_error);
38
39 cmsHPROFILE icc_profile = cmsOpenProfileFromMemTHR(ctx, data, size);
40 if (icc_profile == NULL) {
41 wlr_log(WLR_ERROR, "cmsOpenProfileFromMemTHR failed");
42 goto error_ctx;
43 }
44
45 if (cmsGetDeviceClass(icc_profile) != cmsSigDisplayClass) {
46 wlr_log(WLR_ERROR, "ICC profile must have the Display device class");
47 goto error_icc_profile;
48 }
49
50 cmsToneCurve *linear_tone_curve = cmsBuildGamma(ctx, 1);
51 if (linear_tone_curve == NULL) {
52 wlr_log(WLR_ERROR, "cmsBuildGamma failed");
53 goto error_icc_profile;
54 }
55
56 cmsToneCurve *linear_tf[] = {
57 linear_tone_curve,
58 linear_tone_curve,
59 linear_tone_curve,
60 };
61 cmsHPROFILE srgb_profile = cmsCreateRGBProfileTHR(ctx, &srgb_whitepoint,
62 &srgb_primaries, linear_tf);
63 cmsFreeToneCurve(linear_tone_curve);
64 if (srgb_profile == NULL) {
65 wlr_log(WLR_ERROR, "cmsCreateRGBProfileTHR failed");
66 goto error_icc_profile;
67 }
68
69 cmsHTRANSFORM lcms_tr = cmsCreateTransformTHR(ctx,
70 srgb_profile, TYPE_RGB_FLT, icc_profile, TYPE_RGB_FLT,
71 INTENT_RELATIVE_COLORIMETRIC, 0);
72 cmsCloseProfile(srgb_profile);
73 cmsCloseProfile(icc_profile);
74 if (lcms_tr == NULL) {
75 wlr_log(WLR_ERROR, "cmsCreateTransformTHR failed");
76 goto error_ctx;
77 }
78
79 tx = calloc(1, sizeof(*tx));
80 if (!tx) {
81 cmsDeleteTransform(lcms_tr);
82 goto error_ctx;
83 }
84 wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_LCMS2);
85
86 tx->ctx = ctx;
87 tx->lcms = lcms_tr;
88
89 return &tx->base;
90
91 error_icc_profile:
92 cmsCloseProfile(icc_profile);
93 error_ctx:
94 cmsDeleteContext(ctx);
95 return NULL;
96 }
97
98 void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr) {
99 cmsDeleteTransform(tr->lcms);
100 cmsDeleteContext(tr->ctx);
101 }
102
103 struct wlr_color_transform_lcms2 *color_transform_lcms2_from_base(
104 struct wlr_color_transform *tr) {
105 assert(tr->type == COLOR_TRANSFORM_LCMS2);
106 struct wlr_color_transform_lcms2 *lcms2 = wl_container_of(tr, lcms2, base);
107 return lcms2;
108 }
109
110 void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr,
111 float out[static 3], const float in[static 3]) {
112 cmsDoTransform(tr->lcms, in, out, 1);
113 }