From 913267724ab3d503a4c6dde4ce31ef6460749d4b Mon Sep 17 00:00:00 2001
From: Přemysl Eric Janouch
Date: Thu, 16 Sep 2021 12:35:36 +0200
Subject: Add RAW support using LibRaw
---
README.adoc | 5 ++-
fastiv-view.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
meson.build | 14 ++++---
meson_options.txt | 2 +
4 files changed, 136 insertions(+), 7 deletions(-)
create mode 100644 meson_options.txt
diff --git a/README.adoc b/README.adoc
index fe2183b..1f7df00 100644
--- a/README.adoc
+++ b/README.adoc
@@ -1,7 +1,8 @@
fastiv
======
-'fastiv' is a fast image viewer, supporting BMP, PNG, GIF and JPEG.
+'fastiv' is a fast image viewer, supporting BMP, PNG, GIF, JPEG, and optionally
+RAW pictures.
It is meant to be a viable replacement for Eye of GNOME, which is slow and likes
to break on huge pictures. Memory efficiency is specifically a non-goal.
@@ -14,7 +15,7 @@ a package with the latest development version from Archlinux's AUR.
Building and Running
--------------------
Build dependencies: Meson, pkg-config +
-Runtime dependencies: gtk+-3.0, libturbojpeg
+Runtime dependencies: gtk+-3.0, libturbojpeg, LibRaw (optional)
$ git clone --recursive https://git.janouch.name/p/fastiv.git
$ meson builddir
diff --git a/fastiv-view.c b/fastiv-view.c
index d8d71ea..a692285 100644
--- a/fastiv-view.c
+++ b/fastiv-view.c
@@ -30,8 +30,13 @@
#include "wuffs-mirror-release-c/release/c/wuffs-v0.3.c"
#include
+#include "config.h"
#include "fastiv-view.h"
+#ifdef HAVE_LIBRAW
+#include
+#endif // HAVE_LIBRAW
+
struct _FastivView {
GtkWidget parent_instance;
cairo_surface_t *surface;
@@ -330,12 +335,120 @@ open_libjpeg_turbo(const gchar *data, gsize len, GError **error)
return surface;
}
+#ifdef HAVE_LIBRAW // ---------------------------------------------------------
+
+static cairo_surface_t *
+open_libraw(const gchar *data, gsize len, GError **error)
+{
+ // https://github.com/LibRaw/LibRaw/issues/418
+ libraw_data_t *iprc = libraw_init(LIBRAW_OPIONS_NO_MEMERR_CALLBACK
+ | LIBRAW_OPIONS_NO_DATAERR_CALLBACK);
+ if (!iprc) {
+ set_error(error, "failed to obtain a LibRaw handle");
+ return NULL;
+ }
+
+#if 0
+ // TODO(p): Consider setting this--the image is still likely to be
+ // rendered suboptimally, so why not make it faster.
+ iprc->params.half_size = 1;
+#endif
+
+ // TODO(p): Check if we need to set anything for autorotation (sizes.flip).
+ iprc->params.use_camera_wb = 1;
+ iprc->params.output_color = 1; // sRGB, TODO(p): Is this used?
+ iprc->params.output_bps = 8; // This should be the default value.
+
+ int err = 0;
+ if ((err = libraw_open_buffer(iprc, (void *) data, len))) {
+ set_error(error, libraw_strerror(err));
+ libraw_close(iprc);
+ return NULL;
+ }
+
+ // TODO(p): Do we need to check iprc->idata.raw_count? Maybe for TIFFs?
+ if ((err = libraw_unpack(iprc))) {
+ set_error(error, libraw_strerror(err));
+ libraw_close(iprc);
+ return NULL;
+ }
+
+#if 0
+ // TODO(p): I'm not sure when this is necessary or useful yet.
+ if ((err = libraw_adjust_sizes_info_only(iprc))) {
+ set_error(error, libraw_strerror(err));
+ libraw_close(iprc);
+ return NULL;
+ }
+#endif
+
+ // TODO(p): Documentation says I should look at the code and do it myself.
+ if ((err = libraw_dcraw_process(iprc))) {
+ set_error(error, libraw_strerror(err));
+ libraw_close(iprc);
+ return NULL;
+ }
+
+ // FIXME: This is shittily written to iterate over the range of
+ // idata.colors, and will be naturally slow.
+ libraw_processed_image_t *image = libraw_dcraw_make_mem_image(iprc, &err);
+ if (!image) {
+ set_error(error, libraw_strerror(err));
+ libraw_close(iprc);
+ return NULL;
+ }
+
+ // This should have been transformed, and kept, respectively.
+ if (image->colors != 3 || image->bits != 8) {
+ set_error(error, "unexpected number of colours, or bit depth");
+ libraw_dcraw_clear_mem(image);
+ libraw_close(iprc);
+ return NULL;
+ }
+
+ int width = image->width, height = image->height;
+ cairo_surface_t *surface =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ if (!surface) {
+ set_error(error, "failed to allocate an image surface");
+ libraw_dcraw_clear_mem(image);
+ libraw_close(iprc);
+ return NULL;
+ }
+
+ // Starting to modify pixel data directly. Probably an unnecessary call.
+ cairo_surface_flush(surface);
+
+ uint32_t *pixels = (uint32_t *) cairo_image_surface_get_data(surface);
+ unsigned char *p = image->data;
+ for (ushort y = 0; y < image->height; y++) {
+ for (ushort x = 0; x < image->width; x++) {
+ *pixels++ = 0xff000000 | (uint32_t) p[0] << 16
+ | (uint32_t) p[1] << 8 | (uint32_t) p[2];
+ p += 3;
+ }
+ }
+
+ // Pixel data has been written, need to let Cairo know.
+ cairo_surface_mark_dirty(surface);
+
+ libraw_dcraw_clear_mem(image);
+ libraw_close(iprc);
+ return surface;
+}
+
+#endif // HAVE_LIBRAW ---------------------------------------------------------
+
// TODO(p): Progressive picture loading, or at least async/cancellable.
gboolean
fastiv_view_open(FastivView *self, const gchar *path, GError **error)
{
// TODO(p): Don't always load everything into memory, test type first,
// for which we only need the first 16 bytes right now.
+ // Though LibRaw poses an issue--we may want to try to map RAW formats
+ // to FourCC values--many of them are compliant TIFF files.
+ // We might want to employ a more generic way of magic identification,
+ // and with some luck, it could even be integrated into Wuffs.
gchar *data = NULL;
gsize len = 0;
if (!g_file_get_contents(path, &data, &len, error))
@@ -366,6 +479,15 @@ fastiv_view_open(FastivView *self, const gchar *path, GError **error)
surface = open_libjpeg_turbo(data, len, error);
break;
default:
+#ifdef HAVE_LIBRAW // ---------------------------------------------------------
+ if ((surface = open_libraw(data, len, error)))
+ break;
+
+ // TODO(p): We should try to pass actual processing errors through,
+ // notably only continue with LIBRAW_FILE_UNSUPPORTED.
+ g_clear_error(error);
+#endif // HAVE_LIBRAW ---------------------------------------------------------
+
// TODO(p): Integrate gdk-pixbuf as a fallback (optional dependency).
set_error(error, "unsupported file type");
}
diff --git a/meson.build b/meson.build
index 1295f1d..b730544 100644
--- a/meson.build
+++ b/meson.build
@@ -1,18 +1,22 @@
project('fastiv', 'c', default_options : ['c_std=gnu99'], version : '0.1.0')
+# TODO(p): consider whether libraw_r would be appropriate
+libraw = dependency('libraw', required : get_option('libraw'))
+dependencies = [
+ dependency('gtk+-3.0'),
+ dependency('libturbojpeg'),
+ libraw,
+]
+
conf = configuration_data()
conf.set_quoted('PROJECT_NAME', meson.project_name())
conf.set_quoted('PROJECT_VERSION', meson.project_version())
+conf.set('HAVE_LIBRAW', libraw.found())
configure_file(
output : 'config.h',
configuration : conf,
)
-dependencies = [
- dependency('gtk+-3.0'),
- dependency('libturbojpeg'),
-]
-
# TODO(p): we need to create and install a .desktop file
executable('fastiv', 'fastiv.c', 'fastiv-view.c',
install : true,
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..0d56b56
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,2 @@
+option('libraw', type : 'feature', value : 'auto',
+ description : 'Build with RAW support, requires LibRaw')
--
cgit v1.2.3-70-g09d2