]> git.lyx.org Git - lyx.git/blobdiff - src/graphics/GraphicsLoader.cpp
Work around crash on command line export
[lyx.git] / src / graphics / GraphicsLoader.cpp
index 062dc6fc13e26098912c35a400aa5bf7c090e037..498394e421f09a9a7bb9aec16f45945d4270d8e9 100644 (file)
 #include "GraphicsCacheItem.h"
 #include "GraphicsImage.h"
 #include "GraphicsParams.h"
-#include "LoaderQueue.h"
+#include "GraphicsCache.h"
 
-#include <boost/bind.hpp>
+#include "support/debug.h"
+#include "support/Timeout.h"
 
+#include "support/bind.h"
 
-using std::string;
+#include <set>
+#include <queue>
 
+using namespace std;
+using namespace lyx::support;
 
 namespace lyx {
+namespace graphics {
 
-using support::FileName;
 
-namespace graphics {
+/////////////////////////////////////////////////////////////////////
+//
+// LoaderQueue
+//
+/////////////////////////////////////////////////////////////////////
+
+class LoaderQueue {
+public:
+       /// Use this to request that the item is loaded.
+       void touch(Cache::ItemPtr const & item);
+       /// Query whether the clock is ticking.
+       bool running() const;
+       ///get the and only instance of the class
+       static LoaderQueue & get();
+private:
+       /// This class is a singleton class... use LoaderQueue::get() instead
+       LoaderQueue();
+       /// The in-progress loading queue (elements are unique here).
+       list<Cache::ItemPtr> cache_queue_;
+       /// Used to make the insertion of new elements faster.
+       set<Cache::ItemPtr> cache_set_;
+       /// Newly touched elements go here. loadNext moves them to cache_queue_
+       queue<Cache::ItemPtr> bucket_;
+       ///
+       Timeout timer;
+       ///
+       bool running_;
+
+       /** This is the 'threaded' method, that does the loading in the
+        *  background.
+        */
+       void loadNext();
+       ///
+       void startLoader();
+       ///
+       void stopLoader();
+};
+
+
+
+//static int const s_numimages_ = 5;
+static int const s_numimages_ = 10;
+static int const s_millisecs_ = 500;
+
+
+// FIXME THREAD
+LoaderQueue & LoaderQueue::get()
+{
+       static LoaderQueue singleton;
+       return singleton;
+}
+
+
+void LoaderQueue::loadNext()
+{
+       LYXERR(Debug::GRAPHICS, "LoaderQueue: "
+               << cache_queue_.size() << " items in the queue");
+       int counter = s_numimages_;
+       while (!cache_queue_.empty() && counter--) {
+               Cache::ItemPtr ptr = cache_queue_.front();
+               cache_set_.erase(ptr);
+               cache_queue_.pop_front();
+               if (ptr->status() == WaitingToLoad)
+                       ptr->startLoading();
+       }
+       if (!cache_queue_.empty())
+               startLoader();
+       else
+               stopLoader();
+}
+
+
+LoaderQueue::LoaderQueue() : timer(s_millisecs_, Timeout::ONETIME),
+                            running_(false)
+{
+       timer.timeout.connect(bind(&LoaderQueue::loadNext, this));
+}
+
+
+void LoaderQueue::startLoader()
+{
+       LYXERR(Debug::GRAPHICS, "LoaderQueue: waking up");
+       running_ = true ;
+       timer.setTimeout(s_millisecs_);
+       timer.start();
+}
+
+
+void LoaderQueue::stopLoader()
+{
+       timer.stop();
+       running_ = false ;
+       LYXERR(Debug::GRAPHICS, "LoaderQueue: I'm going to sleep");
+}
+
+
+bool LoaderQueue::running() const
+{
+       return running_ ;
+}
+
+
+void LoaderQueue::touch(Cache::ItemPtr const & item)
+{
+       if (! cache_set_.insert(item).second) {
+               list<Cache::ItemPtr>::iterator
+                       it = cache_queue_.begin();
+               list<Cache::ItemPtr>::iterator
+                       end = cache_queue_.end();
+
+               it = find(it, end, item);
+               if (it != end)
+                       cache_queue_.erase(it);
+       }
+       cache_queue_.push_front(item);
+       if (!running_)
+               startLoader();
+}
+
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// GraphicsLoader
+//
+/////////////////////////////////////////////////////////////////////
+
+typedef shared_ptr<Image> ImagePtr;
 
 class Loader::Impl : public boost::signals::trackable {
 public:
@@ -53,12 +185,21 @@ public:
         */
        Cache::ItemPtr cached_item_;
        /// We modify a local copy of the image once it is loaded.
-       Image::ImagePtr image_;
+       ImagePtr image_;
        /// This signal is emitted when the image loading status changes.
        boost::signal<void()> signal_;
        /// The connection of the signal StatusChanged  
        boost::signals::connection sc_;
 
+       double displayPixelRatio() const
+       {
+               return params_.pixel_ratio;
+       }
+       void setDisplayPixelRatio(double scale)
+       {
+               params_.pixel_ratio = scale;
+       }
+
 private:
        ///
        void statusChanged();
@@ -75,10 +216,10 @@ Loader::Loader()
 {}
 
 
-Loader::Loader(FileName const & file, DisplayType type)
+Loader::Loader(FileName const & file, bool display)
        : pimpl_(new Impl)
 {
-       reset(file, type);
+       reset(file, display);
 }
 
 
@@ -113,10 +254,10 @@ Loader & Loader::operator=(Loader const & other)
 }
 
 
-void Loader::reset(FileName const & file, DisplayType type) const
+void Loader::reset(FileName const & file, bool display) const
 {
        Params params;
-       params.display = type;
+       params.display = display;
        pimpl_->resetParams(params);
 
        pimpl_->resetFile(file);
@@ -141,15 +282,21 @@ void Loader::reset(Params const & params) const
 
 void Loader::startLoading() const
 {
-       if (pimpl_->status_ != WaitingToLoad || !pimpl_->cached_item_.get())
+       if (pimpl_->status_ != WaitingToLoad || !pimpl_->cached_item_)
                return;
        pimpl_->startLoading();
 }
 
 
+void Loader::reload() const 
+{
+       pimpl_->cached_item_->startLoading();
+}
+
+
 void Loader::startMonitoring() const
 {
-       if (!pimpl_->cached_item_.get())
+       if (!pimpl_->cached_item_)
                return;
 
        pimpl_->cached_item_->startMonitoring();
@@ -158,7 +305,7 @@ void Loader::startMonitoring() const
 
 bool Loader::monitoring() const
 {
-       if (!pimpl_->cached_item_.get())
+       if (!pimpl_->cached_item_)
                return false;
 
        return pimpl_->cached_item_->monitoring();
@@ -167,7 +314,7 @@ bool Loader::monitoring() const
 
 unsigned long Loader::checksum() const
 {
-       if (!pimpl_->cached_item_.get())
+       if (!pimpl_->cached_item_)
                return 0;
 
        return pimpl_->cached_item_->checksum();
@@ -177,7 +324,7 @@ unsigned long Loader::checksum() const
 FileName const & Loader::filename() const
 {
        static FileName const empty;
-       return pimpl_->cached_item_.get() ?
+       return pimpl_->cached_item_ ?
                pimpl_->cached_item_->filename() : empty;
 }
 
@@ -188,6 +335,18 @@ ImageStatus Loader::status() const
 }
 
 
+double Loader::displayPixelRatio() const
+{
+       return pimpl_->displayPixelRatio();
+}
+
+
+void Loader::setDisplayPixelRatio(double scale)
+{
+       pimpl_->setDisplayPixelRatio(scale);
+}
+
+
 boost::signals::connection Loader::connect(slot_type const & slot) const
 {
        return pimpl_->signal_.connect(slot);
@@ -214,7 +373,7 @@ Loader::Impl::~Impl()
 
 void Loader::Impl::resetFile(FileName const & file)
 {
-       FileName const old_file = cached_item_.get() ?
+       FileName const old_file = cached_item_ ?
                cached_item_->filename() : FileName();
 
        if (file == old_file)
@@ -230,13 +389,17 @@ void Loader::Impl::resetFile(FileName const & file)
                // signal needs to be disconnected.
                sc_.disconnect();
                cached_item_.reset();
-               Cache::get().remove(old_file);
+               if (status_ != Converting) {
+                       Cache::get().remove(old_file);
+               } else {
+                       //TODO remove cache item when it is not busy any more, see #7163
+               }
        }
 
-       status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
+       status_ = cached_item_ ? cached_item_->status() : WaitingToLoad;
        image_.reset();
 
-       if (cached_item_.get() || file.empty())
+       if (cached_item_ || file.empty())
                return;
 
        Cache & gc = Cache::get();
@@ -250,7 +413,7 @@ void Loader::Impl::resetFile(FileName const & file)
        if (continue_monitoring && !cached_item_->monitoring())
                cached_item_->startMonitoring();
 
-       sc_ = cached_item_->connect(boost::bind(&Impl::statusChanged, this));
+       sc_ = cached_item_->connect(bind(&Impl::statusChanged, this));
 }
 
 
@@ -260,14 +423,14 @@ void Loader::Impl::resetParams(Params const & params)
                return;
 
        params_ = params;
-       status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
+       status_ = cached_item_ ? cached_item_->status() : WaitingToLoad;
        image_.reset();
 }
 
 
 void Loader::Impl::statusChanged()
 {
-       status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
+       status_ = cached_item_ ? cached_item_->status() : WaitingToLoad;
        createPixmap();
        signal_();
 }
@@ -275,16 +438,35 @@ void Loader::Impl::statusChanged()
 
 void Loader::Impl::createPixmap()
 {
-       if (!cached_item_.get() ||
-           params_.display == NoDisplay || status_ != Loaded)
+       if (!params_.display || status_ != Loaded)
+               return;
+
+       if (!cached_item_) {
+               LYXERR(Debug::GRAPHICS, "pixmap not cached yet");
                return;
+       }
+
+       if (!cached_item_->image()) {
+               // There must have been a problem reading the file.
+               LYXERR(Debug::GRAPHICS, "Graphics file not loaded.");
+               return;
+       }
 
        image_.reset(cached_item_->image()->clone());
 
-       // These do nothing if there's nothing to do
-       image_->clip(params_);
-       image_->rotate(params_);
-       image_->scale(params_);
+       if (params_.pixel_ratio == 1.0) {
+               string filename = cached_item_->filename().absFileName();
+               size_t idx = filename.find_last_of('.');
+               if (idx != string::npos && idx > 3) {
+                       if (filename.substr(idx - 3, 3) == "@2x") {
+                               params_.pixel_ratio = 2.0;
+                       } else if (cached_item_->filename().extension() == "svgz") {
+                               params_.pixel_ratio = 4.0;
+                       } else if (cached_item_->filename().extension() == "svg") {
+                               params_.pixel_ratio = 4.0;
+                       }
+               }
+       }
 
        bool const success = image_->setPixmap(params_);
 
@@ -301,6 +483,12 @@ void Loader::Impl::startLoading()
        if (status_ != WaitingToLoad)
                return;
 
+       if (cached_item_->tryDisplayFormat()) {
+               status_ = Loaded;
+               createPixmap();
+               return;
+       }
+
        LoaderQueue::get().touch(cached_item_);
 }