aboutsummaryrefslogtreecommitdiff
path: root/fiv-io.c
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-01-24 04:03:19 +0100
committerPřemysl Eric Janouch <p@janouch.name>2022-01-24 05:48:13 +0100
commit788485d81eefcd43f0a66cf4cc664f8bde4423f5 (patch)
tree8c005e62f5ba11c8834e62334d40301515b137b4 /fiv-io.c
parent991e74b99bbf344effe456f71c544a284f7afe14 (diff)
downloadfiv-788485d81eefcd43f0a66cf4cc664f8bde4423f5.tar.gz
fiv-788485d81eefcd43f0a66cf4cc664f8bde4423f5.tar.xz
fiv-788485d81eefcd43f0a66cf4cc664f8bde4423f5.zip
Redirect warnings to the info bar
And speed up thumbnailing of animated images while at it. Also, fix thumbnailing SVGs with external links.
Diffstat (limited to 'fiv-io.c')
-rw-r--r--fiv-io.c164
1 files changed, 94 insertions, 70 deletions
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);