From 788485d81eefcd43f0a66cf4cc664f8bde4423f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Mon, 24 Jan 2022 04:03:19 +0100 Subject: [PATCH] Redirect warnings to the info bar And speed up thumbnailing of animated images while at it. Also, fix thumbnailing SVGs with external links. --- fiv-io-benchmark.c | 17 +++-- fiv-io.c | 164 ++++++++++++++++++++++++++------------------- fiv-io.h | 18 +++-- fiv-thumbnail.c | 26 ++++--- fiv-view.c | 52 ++++++++------ 5 files changed, 168 insertions(+), 109 deletions(-) diff --git a/fiv-io-benchmark.c b/fiv-io-benchmark.c index 48c5b79..00406cd 100644 --- a/fiv-io-benchmark.c +++ b/fiv-io-benchmark.c @@ -1,7 +1,7 @@ // -// fiv-io-benchmark.c: see if we're worth the name +// fiv-io-benchmark.c: see if we suck // -// Copyright (c) 2021, Přemysl Eric Janouch +// Copyright (c) 2021 - 2022, Přemysl Eric Janouch // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted. @@ -33,9 +33,16 @@ static void one_file(const char *filename) { double since_us = timestamp(); - gchar *uri = g_filename_to_uri(filename, NULL, NULL); - cairo_surface_t *loaded_by_us = fiv_io_open(uri, NULL, FALSE, NULL); - g_free(uri); + FivIoOpenContext ctx = { + .uri = g_filename_to_uri(filename, NULL, NULL), + .screen_dpi = 96, + // Only using this array as a redirect. + .warnings = g_ptr_array_new_with_free_func(g_free), + }; + + cairo_surface_t *loaded_by_us = fiv_io_open(&ctx, NULL); + g_free((char *) ctx.uri); + g_ptr_array_free(ctx.warnings, TRUE); if (!loaded_by_us) return; diff --git a/fiv-io.c b/fiv-io.c index 84c0554..c6a22eb 100644 --- a/fiv-io.c +++ b/fiv-io.c @@ -151,6 +151,21 @@ set_error(GError **error, const char *message) g_set_error_literal(error, FIV_IO_ERROR, FIV_IO_ERROR_OPEN, message); } +static void add_warning(const FivIoOpenContext *ctx, const char *format, ...) + G_GNUC_PRINTF(2, 3); + +static void +add_warning(const FivIoOpenContext *ctx, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + if (ctx->warnings) + g_ptr_array_add(ctx->warnings, g_strdup_vprintf(format, ap)); + else + g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, format, ap); + va_end(ap); +} + static bool try_append_page(cairo_surface_t *surface, cairo_surface_t **result, cairo_surface_t **result_tail) @@ -763,10 +778,10 @@ fail: // since they depend on C++, which is undesirable. static cairo_surface_t * open_wuffs(wuffs_base__image_decoder *dec, wuffs_base__io_buffer src, - FivIoProfile profile, GError **error) + const FivIoOpenContext *ioctx, GError **error) { struct load_wuffs_frame_context ctx = { - .dec = dec, .src = &src, .target = profile}; + .dec = dec, .src = &src, .target = ioctx->screen_profile}; // TODO(p): PNG text chunks (Wuffs #58). // TODO(p): See if something could and should be done about @@ -801,21 +816,21 @@ open_wuffs(wuffs_base__image_decoder *dec, wuffs_base__io_buffer src, switch (wuffs_base__more_information__metadata__fourcc(&minfo)) { case WUFFS_BASE__FOURCC__EXIF: if (ctx.meta_exif) { - g_warning("ignoring repeated Exif"); + add_warning(ioctx, "ignoring repeated Exif"); break; } ctx.meta_exif = bytes; continue; case WUFFS_BASE__FOURCC__ICCP: if (ctx.meta_iccp) { - g_warning("ignoring repeated ICC profile"); + add_warning(ioctx, "ignoring repeated ICC profile"); break; } ctx.meta_iccp = bytes; continue; case WUFFS_BASE__FOURCC__XMP: if (ctx.meta_xmp) { - g_warning("ignoring repeated XMP"); + add_warning(ioctx, "ignoring repeated XMP"); break; } ctx.meta_xmp = bytes; @@ -918,7 +933,8 @@ open_wuffs(wuffs_base__image_decoder *dec, wuffs_base__io_buffer src, } while (load_wuffs_frame(&ctx, error)) - ; + if (ioctx->first_frame_only) + break; // Wrap the chain around, since our caller receives only one pointer. if (ctx.result) @@ -936,7 +952,7 @@ fail: static cairo_surface_t * open_wuffs_using(wuffs_base__image_decoder *(*allocate)(), - const gchar *data, gsize len, FivIoProfile profile, GError **error) + const gchar *data, gsize len, const FivIoOpenContext *ctx, GError **error) { wuffs_base__image_decoder *dec = allocate(); if (!dec) { @@ -946,7 +962,7 @@ open_wuffs_using(wuffs_base__image_decoder *(*allocate)(), cairo_surface_t *surface = open_wuffs(dec, wuffs_base__ptr_u8__reader((uint8_t *) data, len, TRUE), - profile, error); + ctx, error); free(dec); return surface; } @@ -1069,7 +1085,7 @@ load_jpeg_finalize(cairo_surface_t *surface, bool cmyk, static cairo_surface_t * open_libjpeg_turbo( - const gchar *data, gsize len, FivIoProfile profile, GError **error) + const gchar *data, gsize len, const FivIoOpenContext *ctx, GError **error) { // Note that there doesn't seem to be much of a point in using this // simplified API anymore, because JPEG-QS needs the original libjpeg API. @@ -1112,7 +1128,7 @@ open_libjpeg_turbo( cairo_image_surface_get_data(surface), width, stride, height, pixel_format, TJFLAG_ACCURATEDCT)) { if (tjGetErrorCode(dec) == TJERR_WARNING) { - g_warning("%s", tjGetErrorStr2(dec)); + add_warning(ctx, "%s", tjGetErrorStr2(dec)); } else { set_error(error, tjGetErrorStr2(dec)); cairo_surface_destroy(surface); @@ -1121,7 +1137,7 @@ open_libjpeg_turbo( } } - load_jpeg_finalize(surface, use_cmyk, profile, data, len); + load_jpeg_finalize(surface, use_cmyk, ctx->screen_profile, data, len); tjDestroy(dec); return surface; } @@ -1148,7 +1164,7 @@ libjpeg_error_exit(j_common_ptr cinfo) static cairo_surface_t * open_libjpeg_enhanced( - const gchar *data, gsize len, FivIoProfile profile, GError **error) + const gchar *data, gsize len, const FivIoOpenContext *ctx, GError **error) { cairo_surface_t *volatile surface = NULL; @@ -1208,7 +1224,7 @@ open_libjpeg_enhanced( surface_data, cinfo.output_width * cinfo.output_height); (void) jpegqs_finish_decompress(&cinfo); - load_jpeg_finalize(surface, use_cmyk, profile, data, len); + load_jpeg_finalize(surface, use_cmyk, ctx->screen_profile, data, len); jpeg_destroy_decompress(&cinfo); return surface; } @@ -1246,7 +1262,7 @@ load_libwebp_error(VP8StatusCode err) static cairo_surface_t * load_libwebp_nonanimated(WebPDecoderConfig *config, const WebPData *wd, - bool premultiply, GError **error) + const FivIoOpenContext *ctx, GError **error) { cairo_surface_t *surface = cairo_image_surface_create( config->input.has_alpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, @@ -1268,6 +1284,7 @@ load_libwebp_nonanimated(WebPDecoderConfig *config, const WebPData *wd, config->output.u.RGBA.size = config->output.u.RGBA.stride * cairo_image_surface_get_height(surface); + bool premultiply = !ctx->screen_profile; if (G_BYTE_ORDER == G_LITTLE_ENDIAN) config->output.colorspace = premultiply ? MODE_bgrA : MODE_BGRA; else @@ -1295,7 +1312,7 @@ load_libwebp_nonanimated(WebPDecoderConfig *config, const WebPData *wd, return NULL; } - g_warning("partial WebP"); + add_warning(ctx, "image file is truncated"); if (config->input.has_alpha) return surface; @@ -1355,8 +1372,10 @@ load_libwebp_frame(WebPAnimDecoder *dec, const WebPAnimInfo *info, } static cairo_surface_t * -load_libwebp_animated(const WebPData *wd, bool premultiply, GError **error) +load_libwebp_animated( + const WebPData *wd, const FivIoOpenContext *ctx, GError **error) { + bool premultiply = !ctx->screen_profile; WebPAnimDecoderOptions options = {}; WebPAnimDecoderOptionsInit(&options); options.use_threads = true; @@ -1406,8 +1425,8 @@ fail: } static cairo_surface_t * -open_libwebp(const gchar *data, gsize len, const gchar *uri, - FivIoProfile target, GError **error) +open_libwebp( + const gchar *data, gsize len, const FivIoOpenContext *ctx, GError **error) { // It is wholly zero-initialized by libwebp. WebPDecoderConfig config = {}; @@ -1426,8 +1445,8 @@ open_libwebp(const gchar *data, gsize len, const gchar *uri, } cairo_surface_t *result = config.input.has_animation - ? load_libwebp_animated(&wd, !target, error) - : load_libwebp_nonanimated(&config, &wd, !target, error); + ? load_libwebp_animated(&wd, ctx, error) + : load_libwebp_nonanimated(&config, &wd, ctx, error); if (!result) goto fail; @@ -1435,7 +1454,7 @@ open_libwebp(const gchar *data, gsize len, const gchar *uri, WebPDemuxState state = WEBP_DEMUX_PARSE_ERROR; WebPDemuxer *demux = WebPDemuxPartial(&wd, &state); if (!demux) { - g_warning("%s: %s", uri, "demux failure"); + add_warning(ctx, "demux failure while reading metadata"); goto fail; } @@ -1477,8 +1496,8 @@ open_libwebp(const gchar *data, gsize len, const gchar *uri, } WebPDemuxDelete(demux); - if (target) { - fiv_io_profile_xrgb32_page(result, target); + if (ctx->screen_profile) { + fiv_io_profile_xrgb32_page(result, ctx->screen_profile); fiv_io_premultiply_argb32_page(result); } @@ -1677,16 +1696,19 @@ load_resvg_error(int err) } static cairo_surface_t * -open_resvg(const gchar *data, gsize len, const gchar *uri, GError **error) +open_resvg( + const gchar *data, gsize len, const FivIoOpenContext *ctx, GError **error) { - GFile *file = g_file_new_for_uri(uri); + GFile *file = g_file_new_for_uri(ctx->uri); GFile *base_file = g_file_get_parent(file); g_object_unref(file); resvg_options *opt = resvg_options_create(); resvg_options_load_system_fonts(opt); resvg_options_set_resources_dir(opt, g_file_peek_path(base_file)); - // TODO(p): Acquire and set the right DPI for use. + if (ctx->screen_dpi) + resvg_options_set_dpi(opt, ctx->screen_dpi); + resvg_render_tree *tree = NULL; int err = resvg_parse_tree_from_data(data, len, opt, &tree); resvg_options_destroy(opt); @@ -1764,9 +1786,10 @@ load_librsvg_render(FivIoRenderClosure *closure, double scale) } static cairo_surface_t * -open_librsvg(const gchar *data, gsize len, const gchar *uri, GError **error) +open_librsvg( + const gchar *data, gsize len, const FivIoOpenContext *ctx, GError **error) { - GFile *base_file = g_file_new_for_uri(uri); + GFile *base_file = g_file_new_for_uri(ctx->uri); GInputStream *is = g_memory_input_stream_new_from_data(data, len, NULL); RsvgHandle *handle = rsvg_handle_new_from_stream_sync( is, base_file, RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA, NULL, error); @@ -1775,8 +1798,7 @@ open_librsvg(const gchar *data, gsize len, const gchar *uri, GError **error) if (!handle) return NULL; - // TODO(p): Acquire this from somewhere else. - rsvg_handle_set_dpi(handle, 96); + rsvg_handle_set_dpi(handle, ctx->screen_dpi); double w = 0, h = 0; #if LIBRSVG_CHECK_VERSION(2, 51, 0) @@ -2075,8 +2097,9 @@ fail: } static void -load_libheif_aux_images(const gchar *uri, struct heif_image_handle *top, - cairo_surface_t **result, cairo_surface_t **result_tail) +load_libheif_aux_images(const FivIoOpenContext *ioctx, + struct heif_image_handle *top, cairo_surface_t **result, + cairo_surface_t **result_tail) { // Include the depth image, we have no special processing for it now. int filter = LIBHEIF_AUX_IMAGE_FILTER_OMIT_ALPHA; @@ -2089,14 +2112,14 @@ load_libheif_aux_images(const gchar *uri, struct heif_image_handle *top, struct heif_error err = heif_image_handle_get_auxiliary_image_handle(top, ids[i], &handle); if (err.code != heif_error_Ok) { - g_warning("%s: %s", uri, err.message); + add_warning(ioctx, "%s", err.message); continue; } GError *e = NULL; if (!try_append_page( load_libheif_image(handle, &e), result, result_tail)) { - g_warning("%s: %s", uri, e->message); + add_warning(ioctx, "%s", e->message); g_error_free(e); } @@ -2107,8 +2130,8 @@ load_libheif_aux_images(const gchar *uri, struct heif_image_handle *top, } static cairo_surface_t * -open_libheif(const gchar *data, gsize len, const gchar *uri, - FivIoProfile profile, GError **error) +open_libheif( + const gchar *data, gsize len, const FivIoOpenContext *ioctx, GError **error) { // libheif will throw C++ exceptions on allocation failures. // The library is generally awful through and through. @@ -2129,19 +2152,19 @@ open_libheif(const gchar *data, gsize len, const gchar *uri, struct heif_image_handle *handle = NULL; err = heif_context_get_image_handle(ctx, ids[i], &handle); if (err.code != heif_error_Ok) { - g_warning("%s: %s", uri, err.message); + add_warning(ioctx, "%s", err.message); continue; } GError *e = NULL; if (!try_append_page( load_libheif_image(handle, &e), &result, &result_tail)) { - g_warning("%s: %s", uri, e->message); + add_warning(ioctx, "%s", e->message); g_error_free(e); } // TODO(p): Possibly add thumbnail images as well. - load_libheif_aux_images(uri, handle, &result, &result_tail); + load_libheif_aux_images(ioctx, handle, &result, &result_tail); heif_image_handle_release(handle); } if (!result) { @@ -2152,13 +2175,14 @@ open_libheif(const gchar *data, gsize len, const gchar *uri, g_free(ids); fail_read: heif_context_free(ctx); - return fiv_io_profile_finalize(result, profile); + return fiv_io_profile_finalize(result, ioctx->screen_profile); } #endif // HAVE_LIBHEIF -------------------------------------------------------- #ifdef HAVE_LIBTIFF //--------------------------------------------------------- struct fiv_io_tiff { + const FivIoOpenContext *ctx; unsigned char *data; gchar *error; @@ -2241,7 +2265,7 @@ fiv_io_tiff_error( if (io->error) // I'm not sure if two errors can ever come in a succession, // but make sure to log them in any case. - g_warning("tiff: %s: %s", module, message); + add_warning(io->ctx, "%s: %s", module, message); else io->error = g_strconcat(module, ": ", message, NULL); g_free(message); @@ -2335,8 +2359,8 @@ fail: } static cairo_surface_t * -open_libtiff(const gchar *data, gsize len, const gchar *uri, - FivIoProfile target, GError **error) +open_libtiff( + const gchar *data, gsize len, const FivIoOpenContext *ctx, GError **error) { // Both kinds of handlers are called, redirect everything. TIFFErrorHandler eh = TIFFSetErrorHandler(NULL); @@ -2344,13 +2368,14 @@ open_libtiff(const gchar *data, gsize len, const gchar *uri, TIFFErrorHandlerExt ehe = TIFFSetErrorHandlerExt(fiv_io_tiff_error); TIFFErrorHandlerExt whe = TIFFSetWarningHandlerExt(fiv_io_tiff_warning); struct fiv_io_tiff h = { + .ctx = ctx, .data = (unsigned char *) data, .position = 0, .len = len, }; cairo_surface_t *result = NULL, *result_tail = NULL; - TIFF *tiff = TIFFClientOpen(uri, "rm" /* Avoid mmap. */, &h, + TIFF *tiff = TIFFClientOpen(ctx->uri, "rm" /* Avoid mmap. */, &h, fiv_io_tiff_read, fiv_io_tiff_write, fiv_io_tiff_seek, fiv_io_tiff_close, fiv_io_tiff_size, NULL, NULL); if (!tiff) @@ -2385,7 +2410,7 @@ open_libtiff(const gchar *data, gsize len, const gchar *uri, GError *err = NULL; if (!try_append_page( load_libtiff_directory(tiff, &err), &result, &result_tail)) { - g_warning("%s: %s", uri, err->message); + add_warning(ctx, "%s", err->message); g_error_free(err); } } while (TIFFReadDirectory(tiff)); @@ -2408,7 +2433,7 @@ fail: // TODO(p): Colour management even for un/associated alpha channels. // Note that TIFF has a number of fields that an ICC profile can be // constructed from--it's not a good idea to blindly assume sRGB. - return fiv_io_profile_finalize(result, target); + return fiv_io_profile_finalize(result, ctx->screen_profile); } #endif // HAVE_LIBTIFF -------------------------------------------------------- @@ -2439,7 +2464,7 @@ load_gdkpixbuf_argb32_unpremultiplied(GdkPixbuf *pixbuf) static cairo_surface_t * open_gdkpixbuf( - const gchar *data, gsize len, FivIoProfile profile, GError **error) + const gchar *data, gsize len, const FivIoOpenContext *ctx, GError **error) { // gdk-pixbuf controls the playback itself, there is no reliable method of // extracting individual frames (due to loops). @@ -2449,7 +2474,8 @@ open_gdkpixbuf( if (!pixbuf) return NULL; - bool custom_argb32 = profile && gdk_pixbuf_get_has_alpha(pixbuf) && + bool custom_argb32 = ctx->screen_profile && + gdk_pixbuf_get_has_alpha(pixbuf) && gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB && gdk_pixbuf_get_n_channels(pixbuf) == 4 && gdk_pixbuf_get_bits_per_sample(pixbuf) == 8; @@ -2489,10 +2515,10 @@ open_gdkpixbuf( g_object_unref(pixbuf); if (custom_argb32) { - fiv_io_profile_xrgb32_page(surface, profile); + fiv_io_profile_xrgb32_page(surface, ctx->screen_profile); fiv_io_premultiply_argb32_page(surface); } else { - surface = fiv_io_profile_finalize(surface, profile); + surface = fiv_io_profile_finalize(surface, ctx->screen_profile); } return surface; } @@ -2517,8 +2543,7 @@ cairo_user_data_key_t fiv_io_key_page_previous; cairo_user_data_key_t fiv_io_key_render; cairo_surface_t * -fiv_io_open( - const gchar *uri, FivIoProfile profile, gboolean enhance, GError **error) +fiv_io_open(const FivIoOpenContext *ctx, GError **error) { // TODO(p): Don't always load everything into memory, test type first, // so that we can reject non-pictures early. Wuffs only needs the first @@ -2534,22 +2559,21 @@ fiv_io_open( // // gdk-pixbuf exposes its detection data through gdk_pixbuf_get_formats(). // This may also be unbounded, as per format_check(). - GFile *file = g_file_new_for_uri(uri); + GFile *file = g_file_new_for_uri(ctx->uri); gchar *data = NULL; gsize len = 0; if (!g_file_load_contents(file, NULL, &data, &len, NULL, error)) return NULL; - cairo_surface_t *surface = - fiv_io_open_from_data(data, len, uri, profile, enhance, error); + cairo_surface_t *surface = fiv_io_open_from_data(data, len, ctx, error); g_free(data); return surface; } cairo_surface_t * -fiv_io_open_from_data(const char *data, size_t len, const gchar *uri, - FivIoProfile profile, gboolean enhance, GError **error) +fiv_io_open_from_data( + const char *data, size_t len, const FivIoOpenContext *ctx, GError **error) { wuffs_base__slice_u8 prefix = wuffs_base__make_slice_u8((uint8_t *) data, len); @@ -2561,30 +2585,30 @@ fiv_io_open_from_data(const char *data, size_t len, const gchar *uri, // which is so far unsupported here. surface = open_wuffs_using( wuffs_bmp__decoder__alloc_as__wuffs_base__image_decoder, data, len, - profile, error); + ctx, error); break; case WUFFS_BASE__FOURCC__GIF: surface = open_wuffs_using( wuffs_gif__decoder__alloc_as__wuffs_base__image_decoder, data, len, - profile, error); + ctx, error); break; case WUFFS_BASE__FOURCC__PNG: surface = open_wuffs_using( wuffs_png__decoder__alloc_as__wuffs_base__image_decoder, data, len, - profile, error); + ctx, error); break; case WUFFS_BASE__FOURCC__TGA: surface = open_wuffs_using( wuffs_tga__decoder__alloc_as__wuffs_base__image_decoder, data, len, - profile, error); + ctx, error); break; case WUFFS_BASE__FOURCC__JPEG: - surface = enhance - ? open_libjpeg_enhanced(data, len, profile, error) - : open_libjpeg_turbo(data, len, profile, error); + surface = ctx->enhance + ? open_libjpeg_enhanced(data, len, ctx, error) + : open_libjpeg_turbo(data, len, ctx, error); break; case WUFFS_BASE__FOURCC__WEBP: - surface = open_libwebp(data, len, uri, profile, error); + surface = open_libwebp(data, len, ctx, error); break; default: #ifdef HAVE_LIBRAW // --------------------------------------------------------- @@ -2599,7 +2623,7 @@ fiv_io_open_from_data(const char *data, size_t len, const gchar *uri, } #endif // HAVE_LIBRAW --------------------------------------------------------- #ifdef HAVE_RESVG // ---------------------------------------------------------- - if ((surface = open_resvg(data, len, uri, error))) + if ((surface = open_resvg(data, len, ctx, error))) break; if (error) { g_debug("%s", (*error)->message); @@ -2607,7 +2631,7 @@ fiv_io_open_from_data(const char *data, size_t len, const gchar *uri, } #endif // HAVE_RESVG ---------------------------------------------------------- #ifdef HAVE_LIBRSVG // -------------------------------------------------------- - if ((surface = open_librsvg(data, len, uri, error))) + if ((surface = open_librsvg(data, len, ctx, error))) break; // XXX: It doesn't look like librsvg can return sensible errors. @@ -2625,7 +2649,7 @@ fiv_io_open_from_data(const char *data, size_t len, const gchar *uri, } #endif // HAVE_XCURSOR -------------------------------------------------------- #ifdef HAVE_LIBHEIF //--------------------------------------------------------- - if ((surface = open_libheif(data, len, uri, profile, error))) + if ((surface = open_libheif(data, len, ctx, error))) break; if (error) { g_debug("%s", (*error)->message); @@ -2634,7 +2658,7 @@ fiv_io_open_from_data(const char *data, size_t len, const gchar *uri, #endif // HAVE_LIBHEIF -------------------------------------------------------- #ifdef HAVE_LIBTIFF //--------------------------------------------------------- // This needs to be positioned after LibRaw. - if ((surface = open_libtiff(data, len, uri, profile, error))) + if ((surface = open_libtiff(data, len, ctx, error))) break; if (error) { g_debug("%s", (*error)->message); @@ -2650,7 +2674,7 @@ fiv_io_open_from_data(const char *data, size_t len, const gchar *uri, // Wuffs #71 and similar concerns make us default to it in all cases. if (!surface) { GError *err = NULL; - if ((surface = open_gdkpixbuf(data, len, profile, &err))) { + if ((surface = open_gdkpixbuf(data, len, ctx, &err))) { g_clear_error(error); } else if (err->code == GDK_PIXBUF_ERROR_UNKNOWN_TYPE) { g_error_free(err); diff --git a/fiv-io.h b/fiv-io.h index 5379303..2423618 100644 --- a/fiv-io.h +++ b/fiv-io.h @@ -74,7 +74,7 @@ extern cairo_user_data_key_t fiv_io_key_page_next; extern cairo_user_data_key_t fiv_io_key_page_previous; typedef struct _FivIoRenderClosure { - /// The rendering is allowed to fail. + /// The rendering is allowed to fail, returning NULL. cairo_surface_t *(*render)(struct _FivIoRenderClosure *, double scale); } FivIoRenderClosure; @@ -83,10 +83,18 @@ typedef struct _FivIoRenderClosure { /// The rendered image will not have this key. extern cairo_user_data_key_t fiv_io_key_render; -cairo_surface_t *fiv_io_open( - const gchar *uri, FivIoProfile profile, gboolean enhance, GError **error); -cairo_surface_t *fiv_io_open_from_data(const char *data, size_t len, - const gchar *uri, FivIoProfile profile, gboolean enhance, GError **error); +typedef struct { + const char *uri; ///< Source URI + FivIoProfile screen_profile; ///< Target colour space or NULL + int screen_dpi; ///< Target DPI + gboolean enhance; ///< Enhance JPEG (currently) + gboolean first_frame_only; ///< Only interested in the 1st frame + GPtrArray *warnings; ///< String vector for non-fatal errors +} FivIoOpenContext; + +cairo_surface_t *fiv_io_open(const FivIoOpenContext *ctx, GError **error); +cairo_surface_t *fiv_io_open_from_data( + const char *data, size_t len, const FivIoOpenContext *ctx, GError **error); // --- Filesystem -------------------------------------------------------------- diff --git a/fiv-thumbnail.c b/fiv-thumbnail.c index c6a616b..ef84079 100644 --- a/fiv-thumbnail.c +++ b/fiv-thumbnail.c @@ -252,15 +252,24 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error) return FALSE; } - // TODO(p): Add a flag to avoid loading all pages and frames. - FivIoProfile sRGB = fiv_io_profile_new_sRGB(); + FivIoOpenContext ctx = { + .uri = g_file_get_uri(target), + .screen_profile = fiv_io_profile_new_sRGB(), + .screen_dpi = 96, + .first_frame_only = TRUE, + // Only using this array as a redirect. + .warnings = g_ptr_array_new_with_free_func(g_free), + }; + gsize filesize = g_mapped_file_get_length(mf); cairo_surface_t *surface = fiv_io_open_from_data( - g_mapped_file_get_contents(mf), filesize, path, sRGB, FALSE, error); - + g_mapped_file_get_contents(mf), filesize, &ctx, error); g_mapped_file_unref(mf); - if (sRGB) - fiv_io_profile_free(sRGB); + + g_free((gchar *) ctx.uri); + g_ptr_array_free(ctx.warnings, TRUE); + if (ctx.screen_profile) + fiv_io_profile_free(ctx.screen_profile); if (!surface) return FALSE; @@ -285,7 +294,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error) } // Without a CMM, no conversion is attempted. - if (sRGB) { + if (ctx.screen_profile) { g_string_append_printf( thum, "%s%c%s%c", THUMB_COLORSPACE, 0, THUMB_COLORSPACE_SRGB, 0); } @@ -347,7 +356,8 @@ read_wide_thumbnail( if (!thumbnail_uri) return NULL; - cairo_surface_t *surface = fiv_io_open(thumbnail_uri, NULL, FALSE, error); + cairo_surface_t *surface = + fiv_io_open(&(FivIoOpenContext){.uri = thumbnail_uri}, error); g_free(thumbnail_uri); if (!surface) return NULL; diff --git a/fiv-view.c b/fiv-view.c index 26be2eb..8378a5a 100644 --- a/fiv-view.c +++ b/fiv-view.c @@ -1160,6 +1160,34 @@ fiv_view_init(FivView *self) // --- Public interface -------------------------------------------------------- +static cairo_surface_t * +open_without_swapping_in(FivView *self, const gchar *uri) +{ + FivIoOpenContext ctx = { + .uri = uri, + .screen_profile = self->enable_cms ? self->screen_cms_profile : NULL, + .screen_dpi = 96, // TODO(p): Try to retrieve it from the screen. + .enhance = self->enhance, + .warnings = g_ptr_array_new_with_free_func(g_free), + }; + + GError *error = NULL; + cairo_surface_t *surface = fiv_io_open(&ctx, &error); + if (error) { + g_ptr_array_add(ctx.warnings, g_strdup(error->message)); + g_error_free(error); + } + + g_clear_pointer(&self->messages, g_free); + if (ctx.warnings->len) { + g_ptr_array_add(ctx.warnings, NULL); + self->messages = g_strjoinv("\n", (gchar **) ctx.warnings->pdata); + } + + g_ptr_array_free(ctx.warnings, TRUE); + return surface; +} + // TODO(p): Progressive picture loading, or at least async/cancellable. gboolean fiv_view_set_uri(FivView *self, const gchar *uri) @@ -1172,16 +1200,8 @@ fiv_view_set_uri(FivView *self, const gchar *uri) G_OBJECT(self), view_properties[PROP_ENHANCE]); } - GError *error = NULL; - cairo_surface_t *surface = fiv_io_open( - uri, self->enable_cms ? self->screen_cms_profile : NULL, FALSE, &error); - - g_clear_pointer(&self->messages, g_free); + cairo_surface_t *surface = open_without_swapping_in(self, uri); g_clear_pointer(&self->image, cairo_surface_destroy); - if (error) { - self->messages = g_strdup(error->message); - g_error_free(error); - } self->frame = self->page = NULL; self->image = surface; @@ -1220,19 +1240,9 @@ frame_step(FivView *self, int step) static gboolean reload(FivView *self) { - GError *error = NULL; - cairo_surface_t *surface = fiv_io_open(self->uri, - self->enable_cms ? self->screen_cms_profile : NULL, self->enhance, - &error); - - g_clear_pointer(&self->messages, g_free); - if (error) { - self->messages = g_strdup(error->message); - g_error_free(error); - } - + cairo_surface_t *surface = open_without_swapping_in(self, self->uri); g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_MESSAGES]); - if (error) + if (!surface) return FALSE; g_clear_pointer(&self->image, cairo_surface_destroy);