[solang] Added a PixbufMaker with both async and sync capabilities



commit 09f0d0bbee4c94e34770285f229f586a8df27445
Author: Debarshi Ray <rishi gnu org>
Date:   Tue Dec 8 04:20:20 2009 +0200

    Added a PixbufMaker with both async and sync capabilities
    
    Modified the EnlargedRenderer to use the PixbufMaker for asynchronous
    loading of Gdk::Pixbufs.

 src/common/Makefile.am             |    2 +
 src/common/pixbuf-maker.cpp        |  236 ++++++++++++++++++++++++++++++++++++
 src/common/pixbuf-maker.h          |   88 +++++++++++++
 src/common/types.h                 |    4 +
 src/renderer/enlarged-renderer.cpp |   38 ++++--
 src/renderer/enlarged-renderer.h   |    5 +
 6 files changed, 360 insertions(+), 13 deletions(-)
---
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index aba1033..1041b33 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -38,6 +38,8 @@ libcommon_la_SOURCES = \
 	photo-search-criteria.h \
 	plugin.cpp \
 	plugin.h \
+	pixbuf-maker.cpp \
+	pixbuf-maker.h \
 	progress-dialog.cpp \
 	progress-dialog.h \
 	progress-observer.cpp \
diff --git a/src/common/pixbuf-maker.cpp b/src/common/pixbuf-maker.cpp
new file mode 100644
index 0000000..2703843
--- /dev/null
+++ b/src/common/pixbuf-maker.cpp
@@ -0,0 +1,236 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2009 Debarshi Ray <rishi gnu org>
+ *
+ * Solang 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Solang 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#include <cstdio>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <glib/gstdio.h>
+
+#include "content-type-repo.h"
+#include "photo.h"
+#include "pixbuf-maker.h"
+
+namespace Solang
+{
+
+static const guint CHUNK = 4096;
+
+PixbufMaker::PixbufMaker(bool rotate) throw() :
+    rotate_(rotate),
+    fd_(-1),
+    pixbufLoader_(),
+    signalIO_()
+{
+}
+
+PixbufMaker::~PixbufMaker() throw()
+{
+    stop_async();
+}
+
+PixbufMakerPtr
+PixbufMaker::create(bool rotate) throw()
+{
+    return PixbufMakerPtr(new PixbufMaker(rotate));
+}
+
+gint
+PixbufMaker::create_fd(const std::string & path) const
+                       throw(Glib::FileError)
+{
+    const gint fd = g_open(path.c_str(), O_RDONLY);
+
+    if (-1 == fd)
+    {
+        throw Glib::FileError(Glib::FileError::FAILED,
+                              Glib::ustring::compose(
+                                                 "%1: Failed to open",
+                                                 path));
+    }
+
+    return fd;
+}
+
+PixbufLoaderPtr
+PixbufMaker::create_pixbuf_loader(const std::string & path) const
+                                  throw(Gdk::PixbufError)
+{
+    const ContentTypeRepoPtr content_type_repo
+                                 = ContentTypeRepo::instance();
+    const Glib::ustring content_type
+        = content_type_repo->get_content_type(path);
+
+    if (false == content_type_repo->is_gdk_supported(content_type))
+    {
+        throw Gdk::PixbufError(Gdk::PixbufError::UNKNOWN_TYPE, "");
+    }
+
+    return Gdk::PixbufLoader::create(content_type, true);
+}
+
+void
+PixbufMaker::make_async(const SlotAsyncReady & slot,
+                        const PhotoPtr & photo)
+                        throw(Gdk::PixbufError, Glib::ConvertError,
+                              Glib::FileError)
+{
+    const std::string path = Glib::filename_from_utf8(
+                                 photo->get_disk_file_path());
+    fd_ = create_fd(path);
+    pixbufLoader_ = create_pixbuf_loader(path);
+
+    signalIO_
+        = Glib::signal_io().connect(
+              sigc::bind(sigc::mem_fun(*this,
+                                       &PixbufMaker::on_signal_io),
+                         slot),
+              fd_,
+              Glib::IO_IN | Glib::IO_HUP,
+              Glib::PRIORITY_DEFAULT_IDLE);
+}
+
+PixbufPtr
+PixbufMaker::make_sync(const PhotoPtr & photo)
+                       throw(Gdk::PixbufError, Glib::ConvertError,
+                             Glib::FileError)
+{
+    const std::string path = Glib::filename_from_utf8(
+                                 photo->get_disk_file_path());
+    fd_ = create_fd(path);
+    pixbufLoader_ = create_pixbuf_loader(path);
+    gssize nread;
+    guint8 buffer[CHUNK];
+
+    while (0 < (nread = read(fd_, buffer, sizeof(buffer))))
+    {
+        try
+        {
+            pixbufLoader_->write(buffer, nread);
+        }
+        catch (const Glib::FileError & e)
+        {
+            g_warning("%s", e.what().c_str());
+            break;
+        }
+        catch (const Gdk::PixbufError & e)
+        {
+            g_warning("%s", e.what().c_str());
+            break;
+        }
+    }
+
+    close(fd_);
+    fd_ = -1;
+
+    try
+    {
+        pixbufLoader_->close();
+    }
+    catch (const Glib::FileError & e)
+    {
+        g_warning("%s", e.what().c_str());
+    }
+    catch (const Gdk::PixbufError & e)
+    {
+        g_warning("%s", e.what().c_str());
+    }
+
+    PixbufPtr pixbuf = pixbufLoader_->get_pixbuf();
+    if (0 == pixbuf)
+    {
+        throw Gdk::PixbufError(Gdk::PixbufError::FAILED, "");
+    }
+
+    if (true == rotate_)
+    {
+        pixbuf = Glib::wrap(gdk_pixbuf_apply_embedded_orientation(
+                                pixbuf->gobj()), false);
+    }
+
+    return pixbuf;
+}
+
+bool
+PixbufMaker::on_signal_io(Glib::IOCondition condition,
+                          const SlotAsyncReady & slot) throw()
+{
+    guint8 buffer[CHUNK];
+    const gssize nread = read(fd_, buffer, sizeof(buffer));
+
+    try
+    {
+        if (0 == nread)
+        {
+            throw 1;
+        }
+        pixbufLoader_->write(buffer, nread);
+    }
+    catch (...)
+    {
+        stop_async();
+    }
+
+    if (-1 == fd_)
+    {
+        PixbufPtr pixbuf = pixbufLoader_->get_pixbuf();
+        if (0 != pixbuf && true == rotate_)
+        {
+            pixbuf = Glib::wrap(gdk_pixbuf_apply_embedded_orientation(
+                                    pixbuf->gobj()), false);
+        }
+
+        slot(pixbuf);
+    }
+
+    return -1 != fd_;
+}
+
+void
+PixbufMaker::stop_async() throw()
+{
+    if (false == signalIO_)
+    {
+        return;
+    }
+
+    signalIO_.disconnect();
+    close(fd_);
+    fd_ = -1;
+
+    try
+    {
+        pixbufLoader_->close();
+    }
+    catch (const Glib::FileError & e)
+    {
+        g_warning("%s", e.what().c_str());
+    }
+    catch (const Gdk::PixbufError & e)
+    {
+        g_warning("%s", e.what().c_str());
+    }
+}
+
+} // namespace Solang
diff --git a/src/common/pixbuf-maker.h b/src/common/pixbuf-maker.h
new file mode 100644
index 0000000..1e0935f
--- /dev/null
+++ b/src/common/pixbuf-maker.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2009 Debarshi Ray <rishi gnu org>
+ *
+ * Solang 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Solang 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SOLANG_PIXBUF_MAKER_H
+#define SOLANG_PIXBUF_MAKER_H
+
+#include <gdkmm.h>
+#include <glibmm.h>
+#include <sigc++/sigc++.h>
+
+#include "types.h"
+
+namespace Solang
+{
+
+class PixbufMaker
+{
+    public:
+        typedef sigc::slot<void, const PixbufPtr &> SlotAsyncReady;
+
+        ~PixbufMaker() throw();
+
+        static PixbufMakerPtr
+        create(bool rotate) throw();
+
+        void
+        make_async(const SlotAsyncReady & slot,
+                   const PhotoPtr & photo) throw(Gdk::PixbufError,
+                                                 Glib::ConvertError,
+                                                 Glib::FileError);
+
+        PixbufPtr
+        make_sync(const PhotoPtr & photo) throw(Gdk::PixbufError,
+                                                Glib::ConvertError,
+                                                Glib::FileError);
+
+        void
+        stop_async() throw();
+
+    protected:
+        PixbufMaker(bool rotate) throw();
+
+        PixbufMaker(const PixbufMaker & source) throw();
+
+        PixbufMaker &
+        operator=(const PixbufMaker & source) throw();
+
+        gint
+        create_fd(const std::string & path) const
+                  throw(Glib::FileError);
+
+        PixbufLoaderPtr
+        create_pixbuf_loader(const std::string & path) const
+                             throw(Gdk::PixbufError);
+
+        bool
+        on_signal_io(Glib::IOCondition condition,
+                     const SlotAsyncReady & slot) throw();
+
+        bool rotate_;
+
+        gint fd_;
+
+        PixbufLoaderPtr pixbufLoader_;
+
+        sigc::connection signalIO_;
+
+    private:
+};
+
+} // namespace Solang
+
+#endif // SOLANG_PIXBUF_MAKER_H
diff --git a/src/common/types.h b/src/common/types.h
index 26d40f2..7cee9d7 100644
--- a/src/common/types.h
+++ b/src/common/types.h
@@ -153,6 +153,10 @@ typedef std::tr1::shared_ptr<PhotoSearchCriteria>
 typedef std::vector<PhotoSearchCriteriaPtr> PhotoSearchCriteriaList;
 typedef std::map<Glib::ustring,PhotoSearchCriteriaPtr> DragDropCriteriaMap;
 
+class PixbufMaker;
+typedef std::tr1::shared_ptr<const PixbufMaker> ConstPixbufMakerPtr;
+typedef std::tr1::shared_ptr<PixbufMaker> PixbufMakerPtr;
+
 class ProgressDialog;
 typedef Glib::RefPtr<const ProgressDialog> ConstProgressDialogPtr;
 typedef Glib::RefPtr<ProgressDialog> ProgressDialogPtr;
diff --git a/src/renderer/enlarged-renderer.cpp b/src/renderer/enlarged-renderer.cpp
index 01f83e0..011e855 100644
--- a/src/renderer/enlarged-renderer.cpp
+++ b/src/renderer/enlarged-renderer.cpp
@@ -34,6 +34,7 @@
 #include "main-window.h"
 #include "photo.h"
 #include "photo-search-criteria.h"
+#include "pixbuf-maker.h"
 #include "thumbnail.h"
 #include "types.h"
 
@@ -76,6 +77,7 @@ EnlargedRenderer::EnlargedRenderer() throw() :
     imageView_(0),
     imageScrollWin_(0),
     pageNum_(-1),
+    pixbufMaker_(),
     signalMainWindowStateEvent_(),
     signalSwitchPage_()
 {
@@ -125,36 +127,46 @@ EnlargedRenderer::render(const PhotoPtr & photo) throw()
                                       "file");
     photo->set_disk_file_path(storage);
 
-    PixbufPtr pixbuf;
-    std::string path;
-
-    try
+    if (0 == pixbufMaker_)
     {
-        path = Glib::filename_from_utf8(photo->get_disk_file_path());
+        pixbufMaker_ = PixbufMaker::create(true);
     }
-    catch (const Glib::ConvertError & e)
+    else
     {
-        g_warning("%s", e.what().c_str());
-        return;
+        pixbufMaker_->stop_async();
     }
 
     try
     {
-        pixbuf = Gdk::Pixbuf::create_from_file(path);
+        pixbufMaker_->make_async(
+            sigc::mem_fun(
+                *this,
+                &EnlargedRenderer::on_pixbuf_maker_async_ready),
+            photo);
+
+        MainWindow & main_window = application_->get_main_window();
+        main_window.set_busy(true);
+    }
+    catch (const Glib::ConvertError & e)
+    {
+        g_warning("%s", e.what().c_str());
     }
     catch (const Glib::FileError & e)
     {
         g_warning("%s", e.what().c_str());
-        return;
     }
     catch (const Gdk::PixbufError & e)
     {
         g_warning("%s", e.what().c_str());
-        return;
     }
+}
 
-    pixbuf = Glib::wrap(gdk_pixbuf_apply_embedded_orientation(
-                            pixbuf->gobj()), false);
+void
+EnlargedRenderer::on_pixbuf_maker_async_ready(
+                      const PixbufPtr & pixbuf) throw()
+{
+    MainWindow & main_window = application_->get_main_window();
+    main_window.set_busy(false);
 
     if (0 == imageView_)
     {
diff --git a/src/renderer/enlarged-renderer.h b/src/renderer/enlarged-renderer.h
index 6c6dc81..e53af0d 100644
--- a/src/renderer/enlarged-renderer.h
+++ b/src/renderer/enlarged-renderer.h
@@ -110,6 +110,9 @@ class EnlargedRenderer :
                                    throw();
 
         void
+        on_pixbuf_maker_async_ready(const PixbufPtr & pixbuf) throw();
+
+        void
         on_switch_page(GtkNotebookPage * notebook_page, guint page_num)
                        throw();
 
@@ -140,6 +143,8 @@ class EnlargedRenderer :
 
         gint pageNum_;
 
+        PixbufMakerPtr pixbufMaker_;
+
         sigc::connection signalMainWindowStateEvent_;
 
         sigc::connection signalListStoreChangeEnd_;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]