Redirect warnings to the info bar

And speed up thumbnailing of animated images while at it.

Also, fix thumbnailing SVGs with external links.
This commit is contained in:
Přemysl Eric Janouch 2022-01-24 04:03:19 +01:00
parent 991e74b99b
commit 788485d81e
Signed by: p
GPG Key ID: A0420B94F92B9493
5 changed files with 168 additions and 109 deletions

View File

@ -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 <p@janouch.name>
// Copyright (c) 2021 - 2022, Přemysl Eric Janouch <p@janouch.name>
//
// 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;

164
fiv-io.c
View File

@ -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);

View File

@ -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 --------------------------------------------------------------

View File

@ -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;

View File

@ -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);