Part fix #891: Application crashes while viewing CR3 thumbnails
authorColin Clark <colin.clark@cclark.uk>
Mon, 10 May 2021 11:03:22 +0000 (12:03 +0100)
committerColin Clark <colin.clark@cclark.uk>
Mon, 10 May 2021 11:03:22 +0000 (12:03 +0100)
https://github.com/BestImageViewer/geeqie/issues/891

Use LibRaw to extract preview images from .cr3 files (or any raw files
not recognized by exiv2).

README.md
configure.ac
doxygen.conf
src/Makefile.am
src/image-load.c
src/image-load.h
src/image_load_libraw.c [new file with mode: 0644]
src/image_load_libraw.h [new file with mode: 0644]
web/geeqie-install-debian.sh

index 7f06060..4ef910c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -266,6 +266,9 @@ And either the ChangeLog file or [Geeqie ChangeLog](http://geeqie.org/cgi-bin/gi
     libopenjp2
         For displaying JP2 images
 
+    libraw 0.20
+        For displaying CR3 images
+
     yelp-tools
         For creating the Help files
 
index ba7dca7..799128b 100644 (file)
@@ -368,6 +368,31 @@ AM_CONDITIONAL(HAVE_TIFF, [test "x$HAVE_TIFF" = xyes])
 AC_SUBST(TIFF_CFLAGS)
 AC_SUBST(TIFF_LIBS)
 
+#  libraw support
+# ----------------------------------------------------------------------
+
+AC_ARG_ENABLE([raw],
+  AC_HELP_STRING([--disable-raw], [disable LibRaw support]),
+    [libraw=$enableval], [libraw=auto])
+
+if test "x${libraw}" != "xno"; then
+  PKG_CHECK_MODULES(RAW, [libraw >= 0.20],
+    [
+      HAVE_RAW=yes
+      AC_DEFINE(HAVE_RAW, 1, [define to enable libraw support])
+    ],
+    [
+      HAVE_RAW=no
+      AC_MSG_WARN([$RAW_PKG_ERRORS])
+    ])
+else
+  HAVE_RAW=disabled
+fi
+
+AM_CONDITIONAL(HAVE_RAW, [test "x$HAVE_RAW" = xyes])
+AC_SUBST(RAW_CFLAGS)
+AC_SUBST(RAW_LIBS)
+
 #  libffmpegthumbnailer support
 # ----------------------------------------------------------------------
 
@@ -784,7 +809,7 @@ Flags:
   Gtk:           $GTK_CFLAGS
   Glib:          $GLIB_CFLAGS
   Thread:        $GTHREAD_LIBS
-  Others:       $JPEG_LIBS $TIFF_LIBS $LCMS_LIBS $EXIV2_LIBS $CLUTTER_LIBS $CLUTTER_GTK_LIBS $LIBCHAMPLAIN_LIBS $LIBCHAMPLAIN_GTK_LIBS $LUA_LIBS
+  Others:       $JPEG_LIBS $TIFF_LIBS $LCMS_LIBS $EXIV2_LIBS $CLUTTER_LIBS $CLUTTER_GTK_LIBS $LIBCHAMPLAIN_LIBS $LIBCHAMPLAIN_GTK_LIBS $LUA_LIBS $RAW_LIBS
 
 Localization:
   NLS support:   $USE_NLS
@@ -810,6 +835,7 @@ Support:
   WebP:                 $HAVE_WEBP
   DjVu:                 $HAVE_DJVU
   J2K:          $HAVE_J2K
+  LibRaw:        $HAVE_RAW
 
 Documentation:
   Doxygen:       $DX_DOXYGEN
index e4d2b2e..d952f4b 100644 (file)
@@ -2111,6 +2111,7 @@ PREDEFINED             = HAVE_CLUTTER=1 \
                          HAVE_LIRC=1 \
                          HAVE_LUA=1 \
                          HAVE_PDF=1 \
+                         HAVE_RAW=1 \
                          HAVE_TIFF=1 \
                          HAVE_WEBP=1
 
index 64e4d61..bff64e1 100644 (file)
@@ -205,6 +205,8 @@ geeqie_SOURCES = \
        image_load_psd.h\
        image_load_j2k.c\
        image_load_j2k.h\
+       image_load_libraw.c\
+       image_load_libraw.h\
        image_load_svgz.c\
        image_load_svgz.h\
        image_load_ffmpegthumbnailer.c\
@@ -299,7 +301,7 @@ geeqie_SOURCES = \
        zonedetect.c    \
        zonedetect.h
 
-geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(JPEG_LIBS) $(TIFF_LIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS) $(LUA_LIBS) $(CLUTTER_LIBS) $(CLUTTER_GTK_LIBS) $(FFMPEGTHUMBNAILER_LIBS) $(PDF_LIBS) $(HEIF_LIBS) $(WEBP_LIBS) $(DJVU_LIBS) $(J2K_LIBS)
+geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(JPEG_LIBS) $(TIFF_LIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS) $(LUA_LIBS) $(CLUTTER_LIBS) $(CLUTTER_GTK_LIBS) $(FFMPEGTHUMBNAILER_LIBS) $(PDF_LIBS) $(HEIF_LIBS) $(WEBP_LIBS) $(DJVU_LIBS) $(J2K_LIBS) $(RAW_LIBS)
 
 EXTRA_DIST = \
        $(extra_SLIK)
index 4f722e4..0ddf32c 100644 (file)
@@ -34,6 +34,7 @@
 #include "image_load_collection.h"
 #include "image_load_webp.h"
 #include "image_load_j2k.h"
+#include "image_load_libraw.h"
 #include "image_load_svgz.h"
 
 #include "exif.h"
@@ -109,6 +110,7 @@ static void image_loader_init(GTypeInstance *instance, gpointer g_class)
        il->idle_read_loop_count = IMAGE_LOADER_IDLE_READ_LOOP_COUNT_DEFAULT;
        il->read_buffer_size = IMAGE_LOADER_READ_BUFFER_SIZE_DEFAULT;
        il->mapped_file = NULL;
+       il->preview = IMAGE_LOADER_PREVIEW_NONE;
 
        il->requested_width = 0;
        il->requested_height = 0;
@@ -682,6 +684,7 @@ static void image_loader_setup_loader(ImageLoader *il)
                DEBUG_1("Using custom jpeg loader");
                image_loader_backend_set_jpeg(&il->backend);
                }
+#ifndef HAVE_RAW
        else
        if (il->bytes_total >= 11 &&
                (memcmp(il->mapped_file + 4, "ftypcrx", 7) == 0) &&
@@ -690,6 +693,7 @@ static void image_loader_setup_loader(ImageLoader *il)
                DEBUG_1("Using custom cr3 loader");
                image_loader_backend_set_cr3(&il->backend);
                }
+#endif
        else
 #endif
 #ifdef HAVE_TIFF
@@ -954,13 +958,37 @@ static gboolean image_loader_setup_source(ImageLoader *il)
                ExifData *exif = exif_read_fd(il->fd);
 
                if (options->thumbnails.use_exif)
+                       {
                        il->mapped_file = exif_get_preview(exif, (guint *)&il->bytes_total, il->requested_width, il->requested_height);
+
+                       if (il->mapped_file)
+                               {
+                               il->preview = IMAGE_LOADER_PREVIEW_EXIF;
+                               }
+                       }
                else
+                       {
                        il->mapped_file = exif_get_preview(exif, (guint *)&il->bytes_total, 0, 0); /* get the largest available preview image or NULL for normal images*/
 
+                       if (il->mapped_file)
+                               {
+                               il->preview = IMAGE_LOADER_PREVIEW_EXIF;
+                               }
+                       }
+
+               /* If exiv2 does not find a thumbnail, try libraw (which may be slower) */
+               if (!il->mapped_file)
+                       {
+                       il->mapped_file = libraw_get_preview(il, (guint *)&il->bytes_total);
+
+                       if (il->mapped_file)
+                               {
+                               il->preview = IMAGE_LOADER_PREVIEW_LIBRAW;
+                               }
+                       }
+
                if (il->mapped_file)
                        {
-                       il->preview = TRUE;
                        DEBUG_1("Usable reduced size (preview) image loaded from file %s", il->fd->path);
                        }
                exif_free_fd(il->fd, exif);
@@ -994,7 +1022,7 @@ static gboolean image_loader_setup_source(ImageLoader *il)
                        il->mapped_file = 0;
                        return FALSE;
                        }
-               il->preview = FALSE;
+               il->preview = IMAGE_LOADER_PREVIEW_NONE;
                }
 
        return TRUE;
@@ -1006,10 +1034,14 @@ static void image_loader_stop_source(ImageLoader *il)
 
        if (il->mapped_file)
                {
-               if (il->preview)
+               if (il->preview == IMAGE_LOADER_PREVIEW_EXIF)
                        {
                        exif_free_preview(il->mapped_file);
                        }
+               else if (il->preview == IMAGE_LOADER_PREVIEW_LIBRAW)
+                       {
+                       libraw_free_preview(il->mapped_file);
+                       }
                else
                        {
                        munmap(il->mapped_file, il->bytes_total);
index 176a790..4c00583 100644 (file)
@@ -58,6 +58,12 @@ struct _ImageLoaderBackend
        ImageLoaderBackendFuncGetPageTotal get_page_total;
 };
 
+typedef enum {
+       IMAGE_LOADER_PREVIEW_NONE = 0,
+       IMAGE_LOADER_PREVIEW_EXIF = 1,
+       IMAGE_LOADER_PREVIEW_LIBRAW = 2
+} ImageLoaderPreview;
+
 
 //typedef struct _ImageLoader ImageLoader;
 typedef struct _ImageLoaderClass ImageLoaderClass;
@@ -74,7 +80,7 @@ struct _ImageLoader
        gsize bytes_read;
        gsize bytes_total;
 
-       gboolean preview;
+       ImageLoaderPreview preview;
 
        gint requested_width;
        gint requested_height;
diff --git a/src/image_load_libraw.c b/src/image_load_libraw.c
new file mode 100644 (file)
index 0000000..bed4efc
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2021 The Geeqie Team
+ *
+ * Authors: Colin Clark
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * @file
+ * This uses libraw to extract a thumbnail from a raw image. The exiv2 library
+ * does not (yet) extract thumbnails from .cr3 images.
+ * LibRaw seems to be slower than exiv2, so let exiv2 have priority.
+ */
+
+#include "main.h"
+
+#include "filefilter.h"
+#include "image-load.h"
+#include "image_load_libraw.h"
+
+#ifdef HAVE_RAW
+
+#include <libraw/libraw.h>
+#include <sys/mman.h>
+
+typedef struct _UnmapData UnmapData;
+struct _UnmapData
+{
+       guchar *ptr;
+       guchar *map_data;
+       size_t map_len;
+       libraw_data_t *lrdt;
+};
+
+static GList *libraw_unmap_list = 0;
+
+void libraw_free_preview(guchar *buf)
+{
+       GList *work = libraw_unmap_list;
+
+       while (work)
+               {
+               UnmapData *ud = (UnmapData *)work->data;
+               if (ud->ptr == buf)
+                       {
+                       munmap(ud->map_data, ud->map_len);
+                       libraw_close(ud->lrdt);
+                       libraw_unmap_list = g_list_remove_link(libraw_unmap_list, work);
+                       g_free(ud);
+                       return;
+                       }
+               work = work->next;
+               }
+       g_assert_not_reached();
+}
+
+guchar *libraw_get_preview(ImageLoader *il, guint *data_len)
+{
+       libraw_data_t *lrdt;
+       int ret;
+       UnmapData *ud;
+       struct stat st;
+       guchar *map_data;
+       size_t map_len;
+       int fd;
+
+       if (!filter_file_class(il->fd->path, FORMAT_CLASS_RAWIMAGE)) return NULL;
+
+       fd = open(il->fd->path, O_RDONLY);
+       if (fd == -1)
+               {
+               return NULL;
+               }
+
+       if (fstat(fd, &st) == -1)
+               {
+               close(fd);
+               return NULL;
+               }
+
+       map_len = st.st_size;
+       map_data = (guchar *) mmap(0, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
+       close(fd);
+
+       if (map_data == MAP_FAILED)
+               {
+               return NULL;
+               }
+
+       lrdt = libraw_init(0);
+       if (!lrdt)
+               {
+               log_printf("Warning: Cannot create libraw handle\n");
+               return NULL;
+               }
+
+       ret = libraw_open_buffer(lrdt, (void *)map_data, map_len);
+       if (ret == LIBRAW_SUCCESS)
+               {
+               ret = libraw_unpack_thumb(lrdt);
+               if (ret == LIBRAW_SUCCESS)
+                       {
+                       il->mapped_file = (guchar *)lrdt->thumbnail.thumb;
+                       *data_len = lrdt->thumbnail.tlength;
+
+                       ud = g_new(UnmapData, 1);
+                       ud->ptr =(guchar *)lrdt->thumbnail.thumb;
+                       ud->map_data = map_data;
+                       ud->map_len = lrdt->thumbnail.tlength;
+                       ud->lrdt = lrdt;
+
+                       libraw_unmap_list = g_list_prepend(libraw_unmap_list, ud);
+
+                       return (guchar *)lrdt->thumbnail.thumb;
+                       }
+               }
+
+       libraw_close(lrdt);
+
+       return NULL;
+}
+
+#else /* !define HAVE_RAW */
+
+void libraw_free_preview(guchar *buf)
+{
+}
+
+guchar *libraw_get_preview(ImageLoader *il, guint *data_len)
+{
+       return NULL;
+}
+
+#endif
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
diff --git a/src/image_load_libraw.h b/src/image_load_libraw.h
new file mode 100644 (file)
index 0000000..30e5578
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Geeqie Team
+ *
+ * Author: Colin Clark
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef IMAGE_LOAD_RAW_H
+#define IMAGE_LOAD_RAW_H
+
+guchar *libraw_get_preview(ImageLoader *il, guint *data_len);
+void libraw_free_preview(guchar *buf);
+
+#endif
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index a606422..9a16278 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/bash
-version="2019-12-30"
+version="2021-05-10"
 description=$'
 Geeqie is an image viewer.
 This script will download, compile, and install Geeqie on Debian-based systems.
@@ -69,6 +69,8 @@ optional_array=(
 "libdjvulibre-dev"
 "libopenjp2 (for JP2 images)"
 "libopenjp2-7-dev"
+"libraw (for CR3 images)"
+"libraw-dev"
 )
 
 # Optional for GTK3 only