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:
parent
991e74b99b
commit
788485d81e
@ -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
164
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);
|
||||
|
18
fiv-io.h
18
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 --------------------------------------------------------------
|
||||
|
||||
|
@ -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;
|
||||
|
52
fiv-view.c
52
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);
|
||||
|
Loading…
Reference in New Issue
Block a user