From 501dc123ada348f18cac7ed05c812b5e633bd2ef Mon Sep 17 00:00:00 2001
From: Přemysl Eric Janouch
Date: Sun, 28 Jan 2024 01:13:15 +0100
Subject: WIP: Thread-safe colour management
---
fiv-io-cmm.c | 163 +++++++++++++++++++++++++++++------------------------------
1 file changed, 79 insertions(+), 84 deletions(-)
(limited to 'fiv-io-cmm.c')
diff --git a/fiv-io-cmm.c b/fiv-io-cmm.c
index f2d38f9..b131acf 100644
--- a/fiv-io-cmm.c
+++ b/fiv-io-cmm.c
@@ -31,6 +31,55 @@
#include
#endif // HAVE_LCMS2_FAST_FLOAT
+// --- CMM-independent transforms ----------------------------------------------
+
+// CAIRO_STRIDE_ALIGNMENT is 4 bytes, so there will be no padding with
+// ARGB/BGRA/XRGB/BGRX.
+static void
+trivial_cmyk_to_host_byte_order_argb(unsigned char *p, int len)
+{
+ // This CMYK handling has been seen in gdk-pixbuf/JPEG, GIMP/JPEG, skcms.
+ // It will typically produce horribly oversaturated results.
+ // Assume that all YCCK/CMYK JPEG files use inverted CMYK, as Photoshop
+ // does, see https://bugzilla.gnome.org/show_bug.cgi?id=618096
+ while (len--) {
+ int c = p[0], m = p[1], y = p[2], k = p[3];
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ p[0] = k * y / 255;
+ p[1] = k * m / 255;
+ p[2] = k * c / 255;
+ p[3] = 255;
+#else
+ p[3] = k * y / 255;
+ p[2] = k * m / 255;
+ p[1] = k * c / 255;
+ p[0] = 255;
+#endif
+ p += 4;
+ }
+}
+
+// From libwebp, verified to exactly match [x * a / 255].
+#define PREMULTIPLY8(a, x) (((uint32_t) (x) * (uint32_t) (a) * 32897U) >> 23)
+
+void
+fiv_io_premultiply_argb32(FivIoImage *image)
+{
+ if (image->format != CAIRO_FORMAT_ARGB32)
+ return;
+
+ for (uint32_t y = 0; y < image->height; y++) {
+ uint32_t *dstp = (uint32_t *) (image->data + image->stride * y);
+ for (uint32_t x = 0; x < image->width; x++) {
+ uint32_t argb = dstp[x], a = argb >> 24;
+ dstp[x] = a << 24 |
+ PREMULTIPLY8(a, 0xFF & (argb >> 16)) << 16 |
+ PREMULTIPLY8(a, 0xFF & (argb >> 8)) << 8 |
+ PREMULTIPLY8(a, 0xFF & argb);
+ }
+ }
+}
+
// --- Profiles ----------------------------------------------------------------
#ifdef HAVE_LCMS2
@@ -107,8 +156,6 @@ fiv_io_cmm_class_init(FivIoCmmClass *klass)
static void
fiv_io_cmm_init(FivIoCmm *self)
{
- (void) self;
-
self->context = cmsCreateContext(NULL, self);
#ifdef HAVE_LCMS2_FAST_FLOAT
if (cmsPluginTHR(self->context, cmsFastFloatExtensions()))
@@ -119,7 +166,7 @@ fiv_io_cmm_init(FivIoCmm *self)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FivIoCmm *
-fiv_io_cmm_get_default()
+fiv_io_cmm_get_default(void)
{
static gsize initialization_value = 0;
static FivIoCmm *default_ = NULL;
@@ -134,8 +181,7 @@ fiv_io_cmm_get_default()
FivIoProfile *
fiv_io_cmm_get_profile(FivIoCmm *self, const void *data, size_t len)
{
- if (!self)
- self = fiv_io_cmm_get_default();
+ g_return_val_if_fail(self != NULL, NULL);
return fiv_io_profile_new(self,
cmsOpenProfileFromMemTHR(self->context, data, len));
@@ -144,8 +190,7 @@ fiv_io_cmm_get_profile(FivIoCmm *self, const void *data, size_t len)
FivIoProfile *
fiv_io_cmm_get_profile_sRGB(FivIoCmm *self)
{
- if (!self)
- self = fiv_io_cmm_get_default();
+ g_return_val_if_fail(self != NULL, NULL);
return fiv_io_profile_new(self,
cmsCreate_sRGBProfileTHR(self->context));
@@ -155,8 +200,7 @@ FivIoProfile *
fiv_io_cmm_get_profile_parametric(FivIoCmm *self,
double gamma, double whitepoint[2], double primaries[6])
{
- if (!self)
- self = fiv_io_cmm_get_default();
+ g_return_val_if_fail(self != NULL, NULL);
const cmsCIExyY cmsWP = {whitepoint[0], whitepoint[1], 1.0};
const cmsCIExyYTRIPLE cmsP = {
@@ -202,6 +246,7 @@ fiv_io_cmm_get_profile_parametric(FivIoCmm *, double, double[2], double[6])
}
#endif // ! HAVE_LCMS2
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FivIoProfile *
fiv_io_cmm_get_profile_sRGB_gamma(FivIoCmm *self, double gamma)
@@ -220,33 +265,6 @@ fiv_io_cmm_get_profile_from_bytes(FivIoCmm *self, GBytes *bytes)
}
// --- Image loading -----------------------------------------------------------
-
-// CAIRO_STRIDE_ALIGNMENT is 4 bytes, so there will be no padding with
-// ARGB/BGRA/XRGB/BGRX.
-static void
-trivial_cmyk_to_host_byte_order_argb(unsigned char *p, int len)
-{
- // This CMYK handling has been seen in gdk-pixbuf/JPEG, GIMP/JPEG, skcms.
- // It will typically produce horribly oversaturated results.
- // Assume that all YCCK/CMYK JPEG files use inverted CMYK, as Photoshop
- // does, see https://bugzilla.gnome.org/show_bug.cgi?id=618096
- while (len--) {
- int c = p[0], m = p[1], y = p[2], k = p[3];
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
- p[0] = k * y / 255;
- p[1] = k * m / 255;
- p[2] = k * c / 255;
- p[3] = 255;
-#else
- p[3] = k * y / 255;
- p[2] = k * m / 255;
- p[1] = k * c / 255;
- p[0] = 255;
-#endif
- p += 4;
- }
-}
-
#ifdef HAVE_LCMS2
// TODO(p): In general, try to use CAIRO_FORMAT_RGB30 or CAIRO_FORMAT_RGBA128F.
@@ -259,8 +277,7 @@ void
fiv_io_cmm_cmyk(FivIoCmm *self,
FivIoImage *image, FivIoProfile *source, FivIoProfile *target)
{
- if (!self)
- self = fiv_io_cmm_get_default();
+ g_return_if_fail(target == NULL || self != NULL);
cmsHTRANSFORM transform = NULL;
if (source && target) {
@@ -283,8 +300,7 @@ fiv_io_cmm_rgb_direct(FivIoCmm *self, unsigned char *data, int w, int h,
FivIoProfile *source, FivIoProfile *target,
uint32_t source_format, uint32_t target_format)
{
- if (!self)
- self = fiv_io_cmm_get_default();
+ g_return_val_if_fail(target == NULL || self != NULL, false);
// TODO(p): We should make this optional.
FivIoProfile *src_fallback = NULL;
@@ -344,44 +360,6 @@ fiv_io_cmm_4x16le_direct(
#endif // ! HAVE_LCMS2
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-void
-fiv_io_cmm_page(FivIoCmm *self, FivIoImage *page, FivIoProfile *target,
- void (*frame_cb) (FivIoCmm *, FivIoImage *, FivIoProfile *, FivIoProfile *))
-{
- FivIoProfile *source = NULL;
- if (page->icc)
- source = fiv_io_cmm_get_profile_from_bytes(self, page->icc);
-
- // TODO(p): All animations need to be composited in a linear colour space.
- for (FivIoImage *frame = page; frame != NULL; frame = frame->frame_next)
- frame_cb(self, frame, source, target);
-
- if (source)
- fiv_io_profile_free(source);
-}
-
-// From libwebp, verified to exactly match [x * a / 255].
-#define PREMULTIPLY8(a, x) (((uint32_t) (x) * (uint32_t) (a) * 32897U) >> 23)
-
-void
-fiv_io_premultiply_argb32(FivIoImage *image)
-{
- if (image->format != CAIRO_FORMAT_ARGB32)
- return;
-
- for (uint32_t y = 0; y < image->height; y++) {
- uint32_t *dstp = (uint32_t *) (image->data + image->stride * y);
- for (uint32_t x = 0; x < image->width; x++) {
- uint32_t argb = dstp[x], a = argb >> 24;
- dstp[x] = a << 24 |
- PREMULTIPLY8(a, 0xFF & (argb >> 16)) << 16 |
- PREMULTIPLY8(a, 0xFF & (argb >> 8)) << 8 |
- PREMULTIPLY8(a, 0xFF & argb);
- }
- }
-}
-
#if defined HAVE_LCMS2 && LCMS_VERSION >= 2130
#define FIV_IO_PROFILE_ARGB32_PREMUL \
@@ -404,12 +382,11 @@ void
fiv_io_cmm_argb32_premultiply(FivIoCmm *self,
FivIoImage *image, FivIoProfile *source, FivIoProfile *target)
{
- if (!self)
- self = fiv_io_cmm_get_default();
+ g_return_if_fail(target == NULL || self != NULL);
if (image->format != CAIRO_FORMAT_ARGB32) {
fiv_io_cmm_xrgb32(self, image, source, target);
- } else if (self->broken_premul) {
+ } else if (!target || self->broken_premul) {
fiv_io_cmm_xrgb32(self, image, source, target);
fiv_io_premultiply_argb32(image);
} else if (!fiv_io_cmm_rgb_direct(self, image->data,
@@ -422,8 +399,11 @@ fiv_io_cmm_argb32_premultiply(FivIoCmm *self,
#else // ! HAVE_LCMS2 || LCMS_VERSION < 2130
-// TODO(p): Unpremultiply, transform, repremultiply. Or require lcms2>=2.13.
-#define fiv_io_cmm_argb32(self, image, source, target)
+static void
+fiv_io_cmm_argb32(FivIoCmm *, FivIoImage *, FivIoProfile *, FivIoProfile *)
+{
+ // TODO(p): Unpremultiply, transform, repremultiply. Or require lcms2>=2.13.
+}
void
fiv_io_cmm_argb32_premultiply(FivIoCmm *self,
@@ -434,9 +414,24 @@ fiv_io_cmm_argb32_premultiply(FivIoCmm *self,
}
#endif // ! HAVE_LCMS2 || LCMS_VERSION < 2130
-
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void
+fiv_io_cmm_page(FivIoCmm *self, FivIoImage *page, FivIoProfile *target,
+ void (*frame_cb) (FivIoCmm *, FivIoImage *, FivIoProfile *, FivIoProfile *))
+{
+ FivIoProfile *source = NULL;
+ if (page->icc)
+ source = fiv_io_cmm_get_profile_from_bytes(self, page->icc);
+
+ // TODO(p): All animations need to be composited in a linear colour space.
+ for (FivIoImage *frame = page; frame != NULL; frame = frame->frame_next)
+ frame_cb(self, frame, source, target);
+
+ if (source)
+ fiv_io_profile_free(source);
+}
+
void
fiv_io_cmm_any(FivIoCmm *self,
FivIoImage *image, FivIoProfile *source, FivIoProfile *target)
--
cgit v1.2.3-70-g09d2