From caca14036c0b26df6eef4cdaef9cb7811545d52b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Sat, 11 Dec 2021 18:25:25 +0100 Subject: [PATCH] Add preliminary direct support for WebP --- README.adoc | 5 ++-- fastiv-io.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++ meson.build | 5 ++++ meson_options.txt | 2 ++ 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 33691d8..ceab31c 100644 --- a/README.adoc +++ b/README.adoc @@ -2,7 +2,8 @@ fastiv ====== 'fastiv' is a fast image viewer, supporting BMP, PNG, GIF, JPEG, and optionally -raw photos, HEIC, AVIF, SVG, X11 cursors and TIFF, or whatever gdk-pixbuf loads. +raw photos, HEIC, AVIF, WebP, SVG, X11 cursors and TIFF, or whatever gdk-pixbuf +loads. It still has some road to go, but it's already become quite usable, and it has received basic polishing. @@ -25,7 +26,7 @@ Building and Running Build dependencies: Meson, pkg-config + Runtime dependencies: gtk+-3.0, glib>=2.64, pixman-1, shared-mime-info, spng>=0.7.0, libturbojpeg + -Optional dependencies: LibRaw, librsvg-2.0, xcursor, libheif, libtiff, +Optional dependencies: LibRaw, librsvg-2.0, xcursor, libwebp, libheif, libtiff, gdk-pixbuf-2.0 $ git clone --recursive https://git.janouch.name/p/fastiv.git diff --git a/fastiv-io.c b/fastiv-io.c index 87e26b3..e637d1d 100644 --- a/fastiv-io.c +++ b/fastiv-io.c @@ -33,6 +33,9 @@ #ifdef HAVE_XCURSOR #include #endif // HAVE_XCURSOR +#ifdef HAVE_LIBWEBP +#include +#endif // HAVE_LIBWEBP #ifdef HAVE_LIBHEIF #include #endif // HAVE_LIBHEIF @@ -1110,6 +1113,67 @@ open_xcursor(const gchar *data, gsize len, GError **error) } #endif // HAVE_XCURSOR -------------------------------------------------------- +#ifdef HAVE_LIBWEBP //--------------------------------------------------------- + +static cairo_surface_t * +open_libwebp(const gchar *data, gsize len, GError **error) +{ + // It is wholly zero-initialized by libwebp. + WebPDecoderConfig config = {}; + if (!WebPInitDecoderConfig(&config)) { + set_error(error, "libwebp version mismatch"); + return NULL; + } + + // TODO(p): Differentiate between a bad WebP, and not a WebP. + VP8StatusCode err = 0; + if ((err = WebPGetFeatures((const uint8_t *) data, len, &config.input))) { + set_error(error, "WebP decoding error"); + return NULL; + } + + cairo_surface_t *result = NULL; + + cairo_surface_t *surface = cairo_image_surface_create( + config.input.has_alpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, + config.input.width, config.input.height); + cairo_status_t surface_status = cairo_surface_status(surface); + if (surface_status != CAIRO_STATUS_SUCCESS) { + set_error(error, cairo_status_to_string(surface_status)); + goto fail; + } + + config.options.use_threads = true; + + config.output.width = config.input.width; + config.output.height = config.input.height; + config.output.is_external_memory = true; + config.output.u.RGBA.rgba = cairo_image_surface_get_data(surface); + config.output.u.RGBA.stride = cairo_image_surface_get_stride(surface); + config.output.u.RGBA.size = + config.output.u.RGBA.stride * cairo_image_surface_get_height(surface); + if (G_BYTE_ORDER == G_LITTLE_ENDIAN) + config.output.colorspace = MODE_bgrA; + else + config.output.colorspace = MODE_Argb; + + if ((err = WebPDecode((const uint8_t *) data, len, &config))) { + set_error(error, "WebP decoding error"); + goto fail; + } + + // TODO(p): Extract metadata, support animations (has_animation). + result = surface; + +fail: + if (!result) + cairo_surface_destroy(surface); + + WebPFreeDecBuffer(&config.output); + return result; +} + +#endif // HAVE_LIBWEBP -------------------------------------------------------- #ifdef HAVE_LIBHEIF //--------------------------------------------------------- static cairo_surface_t * @@ -1683,6 +1747,14 @@ fastiv_io_open_from_data(const char *data, size_t len, const gchar *path, g_clear_error(error); } #endif // HAVE_XCURSOR -------------------------------------------------------- +#ifdef HAVE_LIBWEBP //--------------------------------------------------------- + if ((surface = open_libwebp(data, len, error))) + break; + if (error) { + g_debug("%s", (*error)->message); + g_clear_error(error); + } +#endif // HAVE_LIBWEBP -------------------------------------------------------- #ifdef HAVE_LIBHEIF //--------------------------------------------------------- if ((surface = open_libheif(data, len, path, error))) break; diff --git a/meson.build b/meson.build index bbd0cc5..5538323 100644 --- a/meson.build +++ b/meson.build @@ -17,6 +17,8 @@ endif libraw = dependency('libraw', required : get_option('libraw')) librsvg = dependency('librsvg-2.0', required : get_option('librsvg')) xcursor = dependency('xcursor', required : get_option('xcursor')) +libwebp = dependency('libwebp', required : get_option('libwebp')) +libwebpdecoder = dependency('libwebpdecoder', required : get_option('libwebp')) libheif = dependency('libheif', required : get_option('libheif')) libtiff = dependency('libtiff-4', required : get_option('libtiff')) gdkpixbuf = dependency('gdk-pixbuf-2.0', required : get_option('gdk-pixbuf')) @@ -29,6 +31,8 @@ dependencies = [ libraw, librsvg, xcursor, + libwebp, + libwebpdecoder, libheif, libtiff, gdkpixbuf, @@ -41,6 +45,7 @@ conf.set_quoted('PROJECT_VERSION', meson.project_version()) conf.set('HAVE_LIBRAW', libraw.found()) conf.set('HAVE_LIBRSVG', librsvg.found()) conf.set('HAVE_XCURSOR', xcursor.found()) +conf.set('HAVE_LIBWEBP', libwebp.found()) conf.set('HAVE_LIBHEIF', libheif.found()) conf.set('HAVE_LIBTIFF', libtiff.found()) conf.set('HAVE_GDKPIXBUF', gdkpixbuf.found()) diff --git a/meson_options.txt b/meson_options.txt index f428622..3edd0e1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -4,6 +4,8 @@ option('librsvg', type : 'feature', value : 'auto', description : 'Build with SVG support, requires librsvg') option('xcursor', type : 'feature', value : 'auto', description : 'Build with Xcursor support, requires libXcursor') +option('libwebp', type : 'feature', value : 'auto', + description : 'Build with WEBP support, requires libwebp') option('libheif', type : 'feature', value : 'auto', description : 'Build with HEIF/AVIF support, requires libheif') option('libtiff', type : 'feature', value : 'auto',