aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2024-01-27 22:22:08 +0100
committerPřemysl Eric Janouch <p@janouch.name>2024-01-27 22:22:08 +0100
commitc6d89361a5e2a46818d67ce4a21856ddecc129a4 (patch)
tree5ca7d95e331bbf00281550da407649ad25cb0407
parent39f6be3f64a6c5c0a1ec0ee8a1d905160381b71f (diff)
downloadfiv-c6d89361a5e2a46818d67ce4a21856ddecc129a4.tar.gz
fiv-c6d89361a5e2a46818d67ce4a21856ddecc129a4.tar.xz
fiv-c6d89361a5e2a46818d67ce4a21856ddecc129a4.zip
WIP: Thread-safe colour management
-rw-r--r--fiv-io-profile.c16
-rw-r--r--fiv-io.c71
-rw-r--r--fiv-io.h13
-rw-r--r--fiv-thumbnail.c10
-rw-r--r--fiv-view.c7
-rw-r--r--tools/benchmark-raw.c4
6 files changed, 61 insertions, 60 deletions
diff --git a/fiv-io-profile.c b/fiv-io-profile.c
index 4e51848..44eb0bf 100644
--- a/fiv-io-profile.c
+++ b/fiv-io-profile.c
@@ -51,7 +51,7 @@ fiv_io_cmm_get_default()
}
FivIoProfile *
-fiv_io_profile_new(const void *data, size_t len)
+fiv_io_cmm_get_profile(FivIoCmm *self, const void *data, size_t len)
{
#ifdef HAVE_LCMS2
return cmsOpenProfileFromMemTHR(NULL, data, len);
@@ -63,7 +63,7 @@ fiv_io_profile_new(const void *data, size_t len)
}
FivIoProfile *
-fiv_io_profile_new_sRGB(void)
+fiv_io_cmm_get_profile_sRGB(FivIoCmm *self)
{
#ifdef HAVE_LCMS2
return cmsCreate_sRGBProfileTHR(NULL);
@@ -73,7 +73,7 @@ fiv_io_profile_new_sRGB(void)
}
FivIoProfile *
-fiv_io_profile_new_parametric(
+fiv_io_cmm_get_profile_parametric(FivIoCmm *self,
double gamma, double whitepoint[2], double primaries[6])
{
#ifdef HAVE_LCMS2
@@ -104,19 +104,19 @@ fiv_io_profile_new_parametric(
}
FivIoProfile *
-fiv_io_profile_new_sRGB_gamma(double gamma)
+fiv_io_cmm_get_profile_sRGB_gamma(FivIoCmm *self, double gamma)
{
- return fiv_io_profile_new_parametric(gamma,
+ return fiv_io_cmm_get_profile_parametric(self, gamma,
(double[2]){0.3127, 0.3290},
(double[6]){0.6400, 0.3300, 0.3000, 0.6000, 0.1500, 0.0600});
}
FivIoProfile *
-fiv_io_profile_new_from_bytes(GBytes *bytes)
+fiv_io_cmm_get_profile_from_bytes(FivIoCmm *self, GBytes *bytes)
{
gsize len = 0;
gconstpointer p = g_bytes_get_data(bytes, &len);
- return fiv_io_profile_new(p, len);
+ return fiv_io_cmm_get_profile(self, p, len);
}
GBytes *
@@ -269,7 +269,7 @@ fiv_io_cmm_page(FivIoCmm *self, FivIoImage *page, FivIoProfile *target,
{
FivIoProfile *source = NULL;
if (page->icc)
- source = fiv_io_profile_new_from_bytes(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)
diff --git a/fiv-io.c b/fiv-io.c
index f16a245..be15a18 100644
--- a/fiv-io.c
+++ b/fiv-io.c
@@ -379,6 +379,7 @@ struct load_wuffs_frame_context {
GBytes *meta_iccp; ///< Reference-counted ICC profile
GBytes *meta_xmp; ///< Reference-counted XMP
+ FivIoCmm *cmm; ///< CMM context, if any
FivIoProfile *target; ///< Target device profile, if any
FivIoProfile *source; ///< Source colour profile, if any
@@ -447,12 +448,12 @@ load_wuffs_frame(struct load_wuffs_frame_context *ctx, GError **error)
if (ctx->target) {
if (ctx->expand_16_float || ctx->pack_16_10) {
- fiv_io_cmm_4x16le_direct(fiv_io_cmm_get_default(),
+ fiv_io_cmm_4x16le_direct(ctx->cmm,
targetbuf, ctx->width, ctx->height, ctx->source, ctx->target);
// The first one premultiplies below, the second doesn't need to.
} else {
- fiv_io_cmm_argb32_premultiply(fiv_io_cmm_get_default(),
- image, ctx->source, ctx->target);
+ fiv_io_cmm_argb32_premultiply(
+ ctx->cmm, image, ctx->source, ctx->target);
}
}
@@ -590,7 +591,8 @@ open_wuffs(wuffs_base__image_decoder *dec, wuffs_base__io_buffer src,
const FivIoOpenContext *ioctx, GError **error)
{
struct load_wuffs_frame_context ctx = {
- .dec = dec, .src = &src, .target = ioctx->screen_profile};
+ .dec = dec, .src = &src,
+ .cmm = ioctx->cmm, .target = ioctx->screen_profile};
// TODO(p): PNG text chunks, like we do with PNG thumbnails.
// TODO(p): See if something could and should be done about
@@ -674,9 +676,11 @@ open_wuffs(wuffs_base__image_decoder *dec, wuffs_base__io_buffer src,
// TODO(p): Improve our simplistic PNG handling of: gAMA, cHRM, sRGB.
if (ctx.target) {
if (ctx.meta_iccp)
- ctx.source = fiv_io_profile_new_from_bytes(ctx.meta_iccp);
+ ctx.source = fiv_io_cmm_get_profile_from_bytes(
+ ctx.cmm, ctx.meta_iccp);
else if (isfinite(gamma) && gamma > 0)
- ctx.source = fiv_io_profile_new_sRGB_gamma(gamma);
+ ctx.source = fiv_io_cmm_get_profile_sRGB_gamma(
+ ctx.cmm, gamma);
}
// Wuffs maps tRNS to BGRA in `decoder.decode_trns?`, we should be fine.
@@ -1060,7 +1064,7 @@ parse_exif_profile_subifd(
}
static FivIoProfile *
-parse_exif_profile(const void *data, size_t len)
+parse_exif_profile(FivIoCmm *cmm, const void *data, size_t len)
{
struct tiffer T = {};
if (!tiffer_init(&T, (const uint8_t *) data, len) || !tiffer_next_ifd(&T))
@@ -1092,7 +1096,7 @@ parse_exif_profile(const void *data, size_t len)
// If sRGB is claimed, assume all parameters are standard.
if (params.colorspace == Exif_ColorSpace_sRGB)
- return fiv_io_profile_new_sRGB();
+ return fiv_io_cmm_get_profile_sRGB(cmm);
// AdobeRGB Nikon JPEGs provide all of these.
if (params.colorspace != Exif_ColorSpace_Uncalibrated ||
@@ -1101,7 +1105,7 @@ parse_exif_profile(const void *data, size_t len)
!params.have_primaries)
return NULL;
- return fiv_io_profile_new_parametric(
+ return fiv_io_cmm_get_profile_parametric(cmm,
params.gamma, params.whitepoint, params.primaries);
}
@@ -1256,19 +1260,17 @@ load_jpeg_finalize(FivIoImage *image, bool cmyk,
g_byte_array_free(meta.icc, TRUE);
FivIoProfile *source = NULL;
- if (icc_profile)
- source = fiv_io_profile_new(
+ if (icc_profile && ctx->cmm)
+ source = fiv_io_cmm_get_profile(ctx->cmm,
g_bytes_get_data(icc_profile, NULL), g_bytes_get_size(icc_profile));
- else if (image->exif)
- source = parse_exif_profile(
+ else if (image->exif && ctx->cmm)
+ source = parse_exif_profile(ctx->cmm,
g_bytes_get_data(image->exif, NULL), g_bytes_get_size(image->exif));
if (cmyk)
- fiv_io_cmm_cmyk(fiv_io_cmm_get_default(),
- image, source, ctx->screen_profile);
+ fiv_io_cmm_cmyk(ctx->cmm, image, source, ctx->screen_profile);
else
- fiv_io_cmm_any(fiv_io_cmm_get_default(),
- image, source, ctx->screen_profile);
+ fiv_io_cmm_any(ctx->cmm, image, source, ctx->screen_profile);
if (source)
fiv_io_profile_free(source);
@@ -1669,8 +1671,8 @@ open_libwebp(
WebPDemuxDelete(demux);
if (ctx->screen_profile)
- fiv_io_cmm_argb32_premultiply_page(fiv_io_cmm_get_default(),
- result, ctx->screen_profile);
+ fiv_io_cmm_argb32_premultiply_page(
+ ctx->cmm, result, ctx->screen_profile);
fail:
WebPFreeDecBuffer(&config.output);
@@ -2053,8 +2055,7 @@ open_libraw(
out:
libraw_close(iprc);
- return fiv_io_cmm_finalize(fiv_io_cmm_get_default(),
- result, ctx->screen_profile);
+ return fiv_io_cmm_finalize(ctx->cmm, result, ctx->screen_profile);
}
#endif // HAVE_LIBRAW ---------------------------------------------------------
@@ -2204,7 +2205,7 @@ load_librsvg_destroy(FivIoRenderClosure *closure)
static FivIoImage *
load_librsvg_render_internal(FivIoRenderClosureLibrsvg *self, double scale,
- FivIoProfile *target, GError **error)
+ FivIoCmm *cmm, FivIoProfile *target, GError **error)
{
RsvgRectangle viewport = {.x = 0, .y = 0,
.width = self->width * scale, .height = self->height * scale};
@@ -2231,16 +2232,15 @@ load_librsvg_render_internal(FivIoRenderClosureLibrsvg *self, double scale,
fiv_io_image_unref(image);
return NULL;
}
- return fiv_io_cmm_finalize(fiv_io_cmm_get_default(),
- image, target);
+ return fiv_io_cmm_finalize(cmm, image, target);
}
static FivIoImage *
-load_librsvg_render(
- FivIoRenderClosure *closure, FivIoProfile *target, double scale)
+load_librsvg_render(FivIoRenderClosure *closure,
+ FivIoCmm *cmm, FivIoProfile *target, double scale)
{
FivIoRenderClosureLibrsvg *self = (FivIoRenderClosureLibrsvg *) closure;
- return load_librsvg_render_internal(self, scale, target, NULL);
+ return load_librsvg_render_internal(self, scale, cmm, target, NULL);
}
static FivIoImage *
@@ -2289,8 +2289,8 @@ open_librsvg(
// librsvg rasterizes filters, so rendering to a recording Cairo surface
// has been abandoned.
- FivIoImage *image =
- load_librsvg_render_internal(closure, 1., ctx->screen_profile, error);
+ FivIoImage *image = load_librsvg_render_internal(
+ closure, 1., ctx->cmm, ctx->screen_profile, error);
if (!image) {
load_librsvg_destroy(&closure->parent);
return NULL;
@@ -2606,8 +2606,7 @@ open_libheif(
g_free(ids);
fail_read:
heif_context_free(ctx);
- return fiv_io_cmm_finalize(fiv_io_cmm_get_default(),
- result, ioctx->screen_profile);
+ return fiv_io_cmm_finalize(ioctx->cmm, result, ioctx->screen_profile);
}
#endif // HAVE_LIBHEIF --------------------------------------------------------
@@ -2838,8 +2837,7 @@ fail:
TIFFSetWarningHandlerExt(whe);
TIFFSetErrorHandler(eh);
TIFFSetWarningHandler(wh);
- return fiv_io_cmm_finalize(fiv_io_cmm_get_default(),
- result, ctx->screen_profile);
+ return fiv_io_cmm_finalize(ctx->cmm, result, ctx->screen_profile);
}
#endif // HAVE_LIBTIFF --------------------------------------------------------
@@ -2934,11 +2932,10 @@ open_gdkpixbuf(
g_object_unref(pixbuf);
if (custom_argb32)
- fiv_io_cmm_argb32_premultiply_page(fiv_io_cmm_get_default(),
- image, ctx->screen_profile);
+ fiv_io_cmm_argb32_premultiply_page(
+ ctx->cmm, image, ctx->screen_profile);
else
- image = fiv_io_cmm_finalize(fiv_io_cmm_get_default(),
- image, ctx->screen_profile);
+ image = fiv_io_cmm_finalize(ctx->cmm, image, ctx->screen_profile);
return image;
}
diff --git a/fiv-io.h b/fiv-io.h
index 26e1b93..bc88509 100644
--- a/fiv-io.h
+++ b/fiv-io.h
@@ -31,13 +31,8 @@ typedef struct _FivIoImage FivIoImage;
void fiv_io_cmm_init(void);
// TODO(p): Make it also possible to use Skia's skcms.
+// TODO(p): Profiles might want to keep references to their CMM contexts.
typedef void *FivIoProfile;
-FivIoProfile *fiv_io_profile_new(const void *data, size_t len);
-FivIoProfile *fiv_io_profile_new_from_bytes(GBytes *bytes);
-FivIoProfile *fiv_io_profile_new_sRGB(void);
-FivIoProfile *fiv_io_profile_new_sRGB_gamma(double gamma);
-FivIoProfile *fiv_io_profile_new_parametric(
- double gamma, double whitepoint[2], double primaries[6]);
GBytes *fiv_io_profile_to_bytes(FivIoProfile *profile);
void fiv_io_profile_free(FivIoProfile *self);
@@ -48,7 +43,6 @@ G_DECLARE_FINAL_TYPE(FivIoCmm, fiv_io_cmm, FIV, CMM, GObject)
FivIoCmm *fiv_io_cmm_get_default();
-/*
FivIoProfile *fiv_io_cmm_get_profile(
FivIoCmm *self, const void *data, size_t len);
FivIoProfile *fiv_io_cmm_get_profile_from_bytes(FivIoCmm *self, GBytes *bytes);
@@ -56,7 +50,6 @@ FivIoProfile *fiv_io_cmm_get_profile_sRGB(FivIoCmm *self);
FivIoProfile *fiv_io_cmm_get_profile_sRGB_gamma(FivIoCmm *self, double gamma);
FivIoProfile *fiv_io_cmm_get_profile_parametric(
FivIoCmm *self, double gamma, double whitepoint[2], double primaries[6]);
-*/
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -103,7 +96,8 @@ enum _FivIoOrientation {
// then loaders could store it in their closures.
struct _FivIoRenderClosure {
/// The rendering is allowed to fail, returning NULL.
- FivIoImage *(*render)(FivIoRenderClosure *, FivIoProfile *, double scale);
+ FivIoImage *(*render)(
+ FivIoRenderClosure *, FivIoCmm *, FivIoProfile *, double scale);
void (*destroy)(FivIoRenderClosure *);
};
@@ -174,6 +168,7 @@ cairo_surface_t *fiv_io_image_to_surface_noref(const FivIoImage *image);
typedef struct {
const char *uri; ///< Source URI
+ FivIoCmm *cmm; ///< Colour management module or NULL
FivIoProfile *screen_profile; ///< Target colour space or NULL
int screen_dpi; ///< Target DPI
gboolean enhance; ///< Enhance JPEG (currently)
diff --git a/fiv-thumbnail.c b/fiv-thumbnail.c
index 6ff1246..80f378e 100644
--- a/fiv-thumbnail.c
+++ b/fiv-thumbnail.c
@@ -137,10 +137,12 @@ might_be_a_thumbnail(const char *path_or_uri)
static FivIoImage *
render(GFile *target, GBytes *data, gboolean *color_managed, GError **error)
{
+ FivIoCmm *cmm = fiv_io_cmm_get_default();
FivIoOpenContext ctx = {
.uri = g_file_get_uri(target),
// Remember to synchronize changes with adjust_thumbnail().
- .screen_profile = fiv_io_profile_new_sRGB(),
+ .cmm = cmm,
+ .screen_profile = fiv_io_cmm_get_profile_sRGB(cmm),
.screen_dpi = 96,
.first_frame_only = TRUE,
// Only using this array as a redirect.
@@ -182,9 +184,11 @@ adjust_thumbnail(FivIoImage *thumbnail, double row_height)
FivIoRenderClosure *closure = thumbnail->render;
if (closure && orientation <= FivIoOrientation0) {
// Remember to synchronize changes with render().
- FivIoProfile *screen_profile = fiv_io_profile_new_sRGB();
+ FivIoCmm *cmm = fiv_io_cmm_get_default();
+ FivIoProfile *screen_profile = fiv_io_cmm_get_profile_sRGB(cmm);
// This API doesn't accept non-uniform scaling; prefer a vertical fit.
- FivIoImage *scaled = closure->render(closure, screen_profile, scale_y);
+ FivIoImage *scaled =
+ closure->render(closure, cmm, screen_profile, scale_y);
if (screen_profile)
fiv_io_profile_free(screen_profile);
if (scaled)
diff --git a/fiv-view.c b/fiv-view.c
index b39adab..dfc7a19 100644
--- a/fiv-view.c
+++ b/fiv-view.c
@@ -417,6 +417,7 @@ prescale_page(FivView *self)
// If it fails, the previous frame pointer may become invalid.
g_clear_pointer(&self->page_scaled, fiv_io_image_unref);
self->frame = self->page_scaled = closure->render(closure,
+ fiv_io_cmm_get_default(),
self->enable_cms ? self->screen_cms_profile : NULL, self->scale);
if (!self->page_scaled)
self->frame = self->page;
@@ -475,7 +476,8 @@ monitor_cms_profile(GdkWindow *root, int num)
if (gdk_property_get(root, gdk_atom_intern(atom, FALSE), GDK_NONE, 0,
8 << 20 /* MiB */, FALSE, &type, &format, &length, &data)) {
if (format == 8 && length > 0)
- result = fiv_io_profile_new(data, length);
+ result = fiv_io_cmm_get_profile(
+ fiv_io_cmm_get_default(), data, length);
g_free(data);
}
return result;
@@ -525,7 +527,8 @@ reload_screen_cms_profile(FivView *self, GdkWindow *window)
out:
if (!self->screen_cms_profile)
- self->screen_cms_profile = fiv_io_profile_new_sRGB();
+ self->screen_cms_profile =
+ fiv_io_cmm_get_profile_sRGB(fiv_io_cmm_get_default());
}
static void
diff --git a/tools/benchmark-raw.c b/tools/benchmark-raw.c
index e1e6ea8..c19760e 100644
--- a/tools/benchmark-raw.c
+++ b/tools/benchmark-raw.c
@@ -320,9 +320,11 @@ process_raw(jv o, const char *filename, const uint8_t *data, size_t len)
// Note that this may use the TIFF/EP shortcut code.
double since = timestamp();
GFile *file = g_file_new_for_commandline_arg(filename);
+ FivIoCmm *cmm = fiv_io_cmm_get_default();
FivIoOpenContext ctx = {
.uri = g_file_get_uri(file),
- .screen_profile = fiv_io_profile_new_sRGB(),
+ .cmm = cmm,
+ .screen_profile = fiv_io_cmm_get_profile_sRGB(cmm),
.screen_dpi = 96,
.warnings = g_ptr_array_new_with_free_func(g_free),
};