use mmaped files image loader
authorVladimir Nadvornik <nadvornik@suse.cz>
Thu, 28 Aug 2008 22:26:09 +0000 (22:26 +0000)
committerVladimir Nadvornik <nadvornik@suse.cz>
Thu, 28 Aug 2008 22:26:09 +0000 (22:26 +0000)
implemented new interface for extracting raw previews
experiments with previews support in SVN version of Exiv2

src/exif.h
src/exiv2.cc
src/image-load.c
src/typedefs.h

index 2cab1b3..e9e99c6 100644 (file)
@@ -158,10 +158,8 @@ gint exif_jpeg_segment_find(guchar *data, guint size,
 gint exif_jpeg_parse_color(ExifData *exif, guchar *data, guint size);
 
 /*raw support */
-gint format_raw_img_exif_offsets_fd(gint fd, const gchar *path,
-                                   guchar *header_data, const guint header_len,
-                                   guint *image_offset, guint *exif_offset);
-
+guchar *exif_get_preview(ExifData *exif, guint *data_len);
+void exif_free_preview(guchar *buf);
 
 
 #endif
index af562b6..d8db57f 100644 (file)
@@ -30,6 +30,7 @@
 #include <fcntl.h>
 #include <sys/mman.h>
 
+#if !EXIV2_TEST_VERSION(0,17,90)
 #include <exiv2/tiffparser.hpp>
 #include <exiv2/tiffcomposite.hpp>
 #include <exiv2/tiffvisitor.hpp>
@@ -43,7 +44,9 @@
 #include <exiv2/rafimage.hpp>
 #endif
 #include <exiv2/futils.hpp>
-
+#else
+#include <exiv2/preview.hpp>
+#endif
 
 
 extern "C" {
@@ -544,7 +547,7 @@ int exif_item_set_string(ExifItem *item, const char *str)
                if (!item) return 0;
                ((Exiv2::Metadatum *)item)->setValue(std::string(str));
                return 1;
-       }
+               }
        catch (Exiv2::AnyError& e) {
                return 0;
        }
@@ -588,7 +591,7 @@ void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint c
        exif->cp_length =cp_length;
 }
 
-unsigned char *exif_get_color_profile(ExifData *exif, guint *data_len)
+guchar *exif_get_color_profile(ExifData *exif, guint *data_len)
 {
        if (exif->cp_data)
                {
@@ -601,9 +604,41 @@ unsigned char *exif_get_color_profile(ExifData *exif, guint *data_len)
        return NULL;
 }
 
+#if EXIV2_TEST_VERSION(0,17,90)
 
+guchar *exif_get_preview(ExifData *exif, guint *data_len)
+{
+       if (!exif) return NULL;
+       try {
+
+               Exiv2::PreviewImageList list(*exif->image);
+               list.read();
+
+               Exiv2::PreviewImageList::iterator pos = list.begin();
+               if (pos != list.end())
+                       {
+                       Exiv2::DataBuf buf = pos->copy();
+                       std::pair<Exiv2::byte*, long> p = buf.release();
+
+                       *data_len = p.second;
+                       return p.first;
+                       }
+               return NULL;
+       }
+       catch (Exiv2::AnyError& e) {
+               std::cout << "Caught Exiv2 exception '" << e << "'\n";
+               return NULL;
+       }
+}
+
+void exif_free_preview(guchar *buf)
+{
+       delete[] (Exiv2::byte*)buf;
+}
+#endif
 
 }
+#if !EXIV2_TEST_VERSION(0,17,90)
 
 /* This is a dirty hack to support raw file preview, bassed on
 tiffparse.cpp from Exiv2 examples */
@@ -611,7 +646,7 @@ tiffparse.cpp from Exiv2 examples */
 class RawFile {
        public:
 
-       RawFile(int fd);
+       RawFile(Exiv2::BasicIo &io);
        ~RawFile();
 
        const Exiv2::Value *find(uint16_t tag, uint16_t group);
@@ -621,15 +656,102 @@ class RawFile {
        private:
        int type;
        Exiv2::TiffComponent::AutoPtr rootDir;
-       Exiv2::byte *map_data;
+       Exiv2::BasicIo &io_;
+       const Exiv2::byte *map_data;
        size_t map_len;
        unsigned long offset;
 };
 
+typedef struct _UnmapData UnmapData;
+struct _UnmapData
+{
+       guchar *ptr;
+       guchar *map_data;
+       size_t map_len;
+};
+
+static GList *exif_unmap_list = 0;
+
+extern "C" guchar *exif_get_preview(ExifData *exif, guint *data_len)
+{
+       int success;
+       unsigned long offset;
+
+       if (!exif) return NULL;
+       const char* path = exif->image->io().path().c_str();
+
+       /* given image pathname, first do simple (and fast) file extension test */
+       if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return 0;
+
+       try {
+               struct stat st;
+               guchar *map_data;
+               size_t map_len;
+               UnmapData *ud;
+               int fd;
+               
+               RawFile rf(exif->image->io());
+               offset = rf.preview_offset();
+               DEBUG_1("%s: offset %lu", path, offset);
+               
+               fd = open(path, O_RDONLY);
+               if (fd == -1)
+                       {
+                       return 0;
+                       }
+
+               if (fstat(fd, &st) == -1)
+                       {
+                       close(fd);
+                       return 0;
+                       }
+               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 0;
+                       }
+               *data_len = map_len - offset;
+               ud = g_new(UnmapData, 1);
+               ud->ptr = map_data + offset;
+               ud->map_data = map_data;
+               ud->map_len = map_len;
+               
+               exif_unmap_list = g_list_prepend(exif_unmap_list, ud);
+               return ud->ptr;
+               
+       }
+       catch (Exiv2::AnyError& e) {
+               std::cout << "Caught Exiv2 exception '" << e << "'\n";
+       }
+       return NULL;
+
+}
+
+void exif_free_preview(guchar *buf)
+{
+       GList *work = exif_unmap_list;
+       
+       while (work)
+               {
+               UnmapData *ud = (UnmapData *)work->data;
+               if (ud->ptr == buf)
+                       {
+                       munmap(ud->map_data, ud->map_len);
+                       exif_unmap_list = g_list_remove_link(exif_unmap_list, work);
+                       g_free(ud);
+                       return;
+                       }
+               }
+       g_assert_not_reached();
+}
+
 using namespace Exiv2;
 
-RawFile::RawFile(int fd) : map_data(NULL), map_len(0), offset(0)
+RawFile::RawFile(BasicIo &io) : io_(io), map_data(NULL), map_len(0), offset(0)
 {
+/*
        struct stat st;
        if (fstat(fd, &st) == -1)
                {
@@ -641,6 +763,15 @@ RawFile::RawFile(int fd) : map_data(NULL), map_len(0), offset(0)
                {
                throw Error(14);
                }
+*/
+        if (io.open() != 0) {
+            throw Error(9, io.path(), strError());
+        }
+        
+        map_data = io.mmap();
+        map_len = io.size();
+
+
        type = Exiv2::ImageFactory::getType(map_data, map_len);
 
 #if EXIV2_TEST_VERSION(0,16,0)
@@ -723,10 +854,8 @@ RawFile::RawFile(int fd) : map_data(NULL), map_len(0), offset(0)
 
 RawFile::~RawFile(void)
 {
-       if (map_data && munmap(map_data, map_len) == -1)
-               {
-               log_printf("Failed to unmap file \n");
-               }
+       io_.munmap();
+       io_.close();
 }
 
 const Value * RawFile::find(uint16_t tag, uint16_t group)
@@ -772,41 +901,7 @@ unsigned long RawFile::preview_offset(void)
 }
 
 
-extern "C" gint format_raw_img_exif_offsets_fd(int fd, const gchar *path,
-                                   unsigned char *header_data, const guint header_len,
-                                   guint *image_offset, guint *exif_offset)
-{
-       int success;
-       unsigned long offset;
-
-       /* given image pathname, first do simple (and fast) file extension test */
-       if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return 0;
-
-       try {
-               RawFile rf(fd);
-               offset = rf.preview_offset();
-               DEBUG_1("%s: offset %lu", path, offset);
-       }
-       catch (Exiv2::AnyError& e) {
-               std::cout << "Caught Exiv2 exception '" << e << "'\n";
-               return 0;
-       }
-
-       if (image_offset && offset > 0)
-               {
-               *image_offset = offset;
-               if ((unsigned long) lseek(fd, *image_offset, SEEK_SET) != *image_offset)
-                       {
-                       log_printf("Failed to seek to embedded image\n");
-
-                       *image_offset = 0;
-                       if (*exif_offset) *exif_offset = 0;
-                       success = FALSE;
-                       }
-               }
-
-       return offset > 0;
-}
+#endif
 
 
 #endif
index 0e3bb16..2dbf97a 100644 (file)
@@ -19,6 +19,7 @@
 #include "ui_fileops.h"
 
 #include <fcntl.h>
+#include <sys/mman.h>
 
 
 static const gchar *image_loader_path(ImageLoader *il)
@@ -155,10 +156,17 @@ static void image_loader_stop(ImageLoader *il)
                il->loader = NULL;
                }
 
-       if (il->load_fd != -1)
+       if (il->mapped_file)
                {
-               close(il->load_fd);
-               il->load_fd = -1;
+               if (il->preview)
+                       {
+                       exif_free_preview(il->mapped_file);
+                       }
+               else
+                       {
+                       munmap(il->mapped_file, il->bytes_total);
+                       }
+               il->mapped_file = NULL;
                }
 
        il->done = TRUE;
@@ -208,7 +216,7 @@ static gint image_loader_idle_cb(gpointer data)
        c = il->idle_read_loop_count ? il->idle_read_loop_count : 1;
        while (c > 0)
                {
-               b = read(il->load_fd, il->read_buffer, il->read_buffer_size);
+               b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
 
                if (b == 0)
                        {
@@ -216,7 +224,7 @@ static gint image_loader_idle_cb(gpointer data)
                        return FALSE;
                        }
 
-               if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->read_buffer, b, NULL)))
+               if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL)))
                        {
                        image_loader_error(il);
                        return FALSE;
@@ -238,19 +246,10 @@ static gint image_loader_idle_cb(gpointer data)
 static gint image_loader_begin(ImageLoader *il)
 {
        gint b;
-       guint offset = 0;
 
        if (!il->loader || il->pixbuf) return FALSE;
 
-       b = read(il->load_fd, il->read_buffer, il->read_buffer_size);
-
-       if (b > 0 &&
-           format_raw_img_exif_offsets_fd(il->load_fd, image_loader_path(il), il->read_buffer, b, &offset, NULL))
-               {
-               DEBUG_1("Raw file %s contains embedded image", image_loader_path(il));
-
-               b = read(il->load_fd, il->read_buffer, il->read_buffer_size);
-               }
+       b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
 
        if (b < 1)
                {
@@ -258,19 +257,19 @@ static gint image_loader_begin(ImageLoader *il)
                return FALSE;
                }
 
-       if (!gdk_pixbuf_loader_write(il->loader, il->read_buffer, b, NULL))
+       if (!gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL))
                {
                image_loader_stop(il);
                return FALSE;
                }
 
-       il->bytes_read += b + offset;
+       il->bytes_read += b;
 
        /* read until size is known */
        while (il->loader && !gdk_pixbuf_loader_get_pixbuf(il->loader) && b > 0)
                {
-               b = read(il->load_fd, il->read_buffer, il->read_buffer_size);
-               if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->read_buffer, b, NULL)))
+               b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read);
+               if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, NULL)))
                        {
                        image_loader_stop(il);
                        return FALSE;
@@ -307,18 +306,56 @@ static gint image_loader_setup(ImageLoader *il)
        struct stat st;
        gchar *pathl;
 
-       if (!il || il->load_fd != -1 || il->loader) return FALSE;
+       if (!il || il->loader || il->mapped_file) return FALSE;
 
-       pathl = path_from_utf8(image_loader_path(il));
-       il->load_fd = open(pathl, O_RDONLY | O_NONBLOCK);
-       g_free(pathl);
-       if (il->load_fd == -1) return FALSE;
+       il->mapped_file = NULL;
 
-       if (fstat(il->load_fd, &st) == 0)
+       if (il->fd)
                {
-               il->bytes_total = st.st_size;
+               ExifData *exif = exif_read_fd(il->fd);
+
+               il->mapped_file = exif_get_preview(exif, &il->bytes_total);
+               
+               if (il->mapped_file)
+                       {
+                       il->preview = TRUE;
+                       DEBUG_1("Raw file %s contains embedded image", image_loader_path(il));
+                       }
+               exif_free_fd(il->fd, exif);
                }
 
+       
+       if (!il->mapped_file)
+               {
+               /* normal file */
+               gint load_fd;
+       
+               pathl = path_from_utf8(image_loader_path(il));
+               load_fd = open(pathl, O_RDONLY | O_NONBLOCK);
+               g_free(pathl);
+               if (load_fd == -1) return FALSE;
+
+               if (fstat(load_fd, &st) == 0)
+                       {
+                       il->bytes_total = st.st_size;
+                       }
+               else
+                       {
+                       close(load_fd);
+                       return FALSE;
+                       }
+               
+               il->mapped_file = mmap(0, il->bytes_total, PROT_READ|PROT_WRITE, MAP_PRIVATE, load_fd, 0);
+               close(load_fd);
+               if (il->mapped_file == MAP_FAILED)
+                       {
+                       il->mapped_file = 0;
+                       return FALSE;
+                       }
+               il->preview = FALSE;
+               }
+
+
        il->loader = gdk_pixbuf_loader_new();
        g_signal_connect(G_OBJECT(il->loader), "area_updated",
                         G_CALLBACK(image_loader_area_updated_cb), il);
@@ -346,7 +383,6 @@ static ImageLoader *image_loader_new_real(FileData *fd, const gchar *path)
        il->idle_priority = G_PRIORITY_DEFAULT_IDLE;
        il->done = FALSE;
        il->loader = NULL;
-       il->load_fd = -1;
 
        il->bytes_read = 0;
        il->bytes_total = 0;
@@ -355,7 +391,7 @@ static ImageLoader *image_loader_new_real(FileData *fd, const gchar *path)
 
        il->idle_read_loop_count = options->image.idle_read_loop_count;
        il->read_buffer_size = options->image.read_buffer_size;
-       il->read_buffer = g_new(guchar, il->read_buffer_size);
+       il->mapped_file = NULL;
 
        il->requested_width = 0;
        il->requested_height = 0;
@@ -383,7 +419,6 @@ void image_loader_free(ImageLoader *il)
        if (il->pixbuf) gdk_pixbuf_unref(il->pixbuf);
        if (il->fd) file_data_unref(il->fd);
        if (il->path) g_free(il->path);
-       if (il->read_buffer) g_free(il->read_buffer);
        DEBUG_1("freeing image loader %p bytes_read=%d", il, il->bytes_read);
        g_free(il);
 }
index 9cda6aa..90b80ef 100644 (file)
@@ -216,6 +216,8 @@ struct _ImageLoader
        gint bytes_read;
        gint bytes_total;
 
+       gint preview;
+
        gint requested_width;
        gint requested_height;
        gint shrunk;
@@ -225,7 +227,6 @@ struct _ImageLoader
        gint idle_priority;
 
        GdkPixbufLoader *loader;
-       gint load_fd;
 
        void (*func_area_ready)(ImageLoader *, guint x, guint y, guint w, guint h, gpointer);
        void (*func_error)(ImageLoader *, gpointer);
@@ -239,7 +240,7 @@ struct _ImageLoader
 
        gint idle_done_id;
 
-       guchar *read_buffer;
+       guchar *mapped_file;
        gint read_buffer_size;
        gint idle_read_loop_count;
 };