From e5b1a1861cb0e69411b4ebbfaadb235b9bd8a001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Sun, 26 Dec 2021 01:24:09 +0100 Subject: [PATCH] Avoid double CM in saved WebPs --- README.adoc | 4 ++-- fiv-io.c | 42 ++++++++++++++++++++++++++++++++++-------- fiv-io.h | 2 +- fiv-view.c | 17 +++++++++++++---- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/README.adoc b/README.adoc index b364e4f..f133222 100644 --- a/README.adoc +++ b/README.adoc @@ -5,8 +5,8 @@ fastiv raw photos, HEIC, AVIF, WebP, SVG, X11 cursors and TIFF, or whatever gdk-pixbuf loads. -Its development status can be summarized as '`beta`'. E.g., colour management -is a bit incomplete, and certain GIFs animate wrong. +Its development status can be summarized as '`beta`'. +E.g., certain GIFs animate wrong. Non-goals --------- diff --git a/fiv-io.c b/fiv-io.c index c08d121..2ab97f1 100644 --- a/fiv-io.c +++ b/fiv-io.c @@ -206,6 +206,24 @@ fiv_io_profile_new_from_bytes(GBytes *bytes) return fiv_io_profile_new(p, len); } +static GBytes * +fiv_io_profile_to_bytes(FivIoProfile profile) +{ +#ifdef HAVE_LCMS2 + cmsUInt32Number len = 0; + (void) cmsSaveProfileToMem(profile, NULL, &len); + gchar *data = g_malloc0(len); + if (!cmsSaveProfileToMem(profile, data, &len)) { + g_free(data); + return NULL; + } + return g_bytes_new_take(data, len); +#else + (void) profile; + return NULL; +#endif +} + void fiv_io_profile_free(FivIoProfile self) { @@ -2490,10 +2508,8 @@ encode_webp_animation(WebPMux *mux, cairo_surface_t *page) } static gboolean -transfer_metadata(WebPMux *mux, const char *fourcc, cairo_surface_t *page, - const cairo_user_data_key_t *kind) +set_metadata(WebPMux *mux, const char *fourcc, GBytes *data) { - GBytes *data = cairo_surface_get_user_data(page, kind); if (!data) return TRUE; @@ -2504,8 +2520,8 @@ transfer_metadata(WebPMux *mux, const char *fourcc, cairo_surface_t *page, } gboolean -fiv_io_save(cairo_surface_t *page, cairo_surface_t *frame, const gchar *path, - GError **error) +fiv_io_save(cairo_surface_t *page, cairo_surface_t *frame, FivIoProfile target, + const gchar *path, GError **error) { g_return_val_if_fail(page != NULL, FALSE); g_return_val_if_fail(path != NULL, FALSE); @@ -2519,9 +2535,16 @@ fiv_io_save(cairo_surface_t *page, cairo_surface_t *frame, const gchar *path, else ok = encode_webp_animation(mux, page); - ok = ok && transfer_metadata(mux, "EXIF", page, &fiv_io_key_exif); - ok = ok && transfer_metadata(mux, "ICCP", page, &fiv_io_key_icc); - ok = ok && transfer_metadata(mux, "XMP ", page, &fiv_io_key_xmp); + ok = ok && set_metadata(mux, "EXIF", + cairo_surface_get_user_data(page, &fiv_io_key_exif)); + ok = ok && set_metadata(mux, "ICCP", + cairo_surface_get_user_data(page, &fiv_io_key_icc)); + ok = ok && set_metadata(mux, "XMP ", + cairo_surface_get_user_data(page, &fiv_io_key_xmp)); + + GBytes *iccp = NULL; + if (ok && target && (iccp = fiv_io_profile_to_bytes(target))) + ok = set_metadata(mux, "ICCP", iccp); WebPData assembled = {}; WebPDataInit(&assembled); @@ -2531,6 +2554,9 @@ fiv_io_save(cairo_surface_t *page, cairo_surface_t *frame, const gchar *path, ok = g_file_set_contents( path, (const gchar *) assembled.bytes, assembled.size, error); + if (iccp) + g_bytes_unref(iccp); + WebPMuxDelete(mux); WebPDataClear(&assembled); return ok; diff --git a/fiv-io.h b/fiv-io.h index f2cfecd..69e3c90 100644 --- a/fiv-io.h +++ b/fiv-io.h @@ -79,7 +79,7 @@ int fiv_io_filecmp(GFile *f1, GFile *f2); /// Requires libwebp. /// If no exact frame is specified, this potentially creates an animation. gboolean fiv_io_save(cairo_surface_t *page, cairo_surface_t *frame, - const gchar *path, GError **error); + FivIoProfile target, const gchar *path, GError **error); // --- Metadata ---------------------------------------------------------------- diff --git a/fiv-view.c b/fiv-view.c index 3f6357f..d03e9ee 100644 --- a/fiv-view.c +++ b/fiv-view.c @@ -779,9 +779,18 @@ print(FivView *self) } static gboolean -save_as(FivView *self, gboolean frame) +save_as(FivView *self, cairo_surface_t *frame) { GtkWindow *window = get_toplevel(GTK_WIDGET(self)); + FivIoProfile target = NULL; + if (self->enable_cms && (target = self->screen_cms_profile)) { + GtkWidget *dialog = gtk_message_dialog_new(window, GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, "%s", + "Color management overrides attached color profiles."); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + } + GtkWidget *dialog = gtk_file_chooser_dialog_new( frame ? "Save frame as" : "Save page as", window, GTK_FILE_CHOOSER_ACTION_SAVE, @@ -834,7 +843,7 @@ save_as(FivView *self, gboolean frame) GError *error = NULL; #ifdef HAVE_LIBWEBP if (gtk_file_chooser_get_filter(chooser) == webp_filter) - fiv_io_save(self->page, frame ? self->frame : NULL, path, &error); + fiv_io_save(self->page, frame, target, path, &error); else #endif // HAVE_LIBWEBP fiv_io_save_metadata(self->page, path, &error); @@ -1027,7 +1036,7 @@ fiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event) case GDK_KEY_s: return command(self, FIV_VIEW_COMMAND_SAVE_PAGE); case GDK_KEY_S: - return save_as(self, TRUE); + return save_as(self, self->frame); } } if (state == GDK_MOD1_MASK) { @@ -1320,7 +1329,7 @@ fiv_view_command(FivView *self, FivViewCommand command) break; case FIV_VIEW_COMMAND_PRINT: print(self); break; case FIV_VIEW_COMMAND_SAVE_PAGE: - save_as(self, FALSE); + save_as(self, NULL); break; case FIV_VIEW_COMMAND_INFO: info(self);