X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fgraphics%2FGraphicsLoader.cpp;h=987a973139088f5a6e763d7fc1e0297fb9957640;hb=425c190d623daeb6d05bce1aa2244b548225305a;hp=14df775328cc957ddff49b0005132015d3fb3d1e;hpb=897436efbb9bd641b61467d185a2dfae9839e575;p=lyx.git diff --git a/src/graphics/GraphicsLoader.cpp b/src/graphics/GraphicsLoader.cpp index 14df775328..987a973139 100644 --- a/src/graphics/GraphicsLoader.cpp +++ b/src/graphics/GraphicsLoader.cpp @@ -15,24 +15,159 @@ #include "GraphicsCacheItem.h" #include "GraphicsImage.h" #include "GraphicsParams.h" -#include "LoaderQueue.h" +#include "GraphicsCache.h" -#include +#include "support/debug.h" +#include "support/lassert.h" +#include "support/Timeout.h" +#include "support/bind.h" -using std::string; +#include +#include +#include +using namespace std; +using namespace lyx::support; namespace lyx { -using support::FileName; - namespace graphics { -class Loader::Impl : public boost::signals::trackable { + +///////////////////////////////////////////////////////////////////// +// +// 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_queue_; + /// Used to make the insertion of new elements faster. + set cache_set_; + /// Newly touched elements go here. loadNext moves them to cache_queue_ + queue bucket_; /// - Impl(); + 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; + + +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::iterator + it = cache_queue_.begin(); + list::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 std::shared_ptr ImagePtr; + +class Loader::Impl : public boost::signals2::trackable { + friend class Loader; +public: + /// + Impl(FileName const & doc_file); /// ~Impl(); /// @@ -46,6 +181,8 @@ public: /// Params const & params() const { return params_; } + /// + FileName doc_file_; /// The loading status of the image. ImageStatus status_; /** Must store a copy of the cached item to ensure that it is not @@ -53,9 +190,20 @@ 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 signal_; + boost::signals2::signal signal_; + /// The connection of the signal StatusChanged + boost::signals2::connection sc_; + + double displayPixelRatio() const + { + return params_.pixel_ratio; + } + void setDisplayPixelRatio(double scale) + { + params_.pixel_ratio = scale; + } private: /// @@ -68,27 +216,35 @@ private: }; -Loader::Loader() - : pimpl_(new Impl) +Loader::Loader(FileName const & doc_file) + : pimpl_(new Impl(doc_file)) {} -Loader::Loader(FileName const & file, DisplayType type) - : pimpl_(new Impl) +Loader::Loader(FileName const & doc_file, FileName const & file, bool display) + : pimpl_(new Impl(doc_file)) { - reset(file, type); + reset(file, display); } -Loader::Loader(FileName const & file, Params const & params) - : pimpl_(new Impl) +Loader::Loader(FileName const & doc_file, FileName const & file, Params const & params) + : pimpl_(new Impl(doc_file)) { reset(file, params); } +Loader::Loader(FileName const & doc_file, Loader const & other) + : pimpl_(new Impl(doc_file)) +{ + Params const & params = other.pimpl_->params(); + reset(params.filename, params); +} + + Loader::Loader(Loader const & other) - : pimpl_(new Impl) + : pimpl_(new Impl(other.pimpl_->doc_file_)) { Params const & params = other.pimpl_->params(); reset(params.filename, params); @@ -96,12 +252,17 @@ Loader::Loader(Loader const & other) Loader::~Loader() -{} +{ + delete pimpl_; +} Loader & Loader::operator=(Loader const & other) { + LASSERT(false, /**/); if (this != &other) { + delete pimpl_; + pimpl_ = new Impl(other.pimpl_->doc_file_); Params const & params = other.pimpl_->params(); reset(params.filename, params); } @@ -109,10 +270,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); @@ -137,15 +298,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(); @@ -154,7 +321,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(); @@ -163,7 +330,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(); @@ -173,7 +340,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; } @@ -184,7 +351,19 @@ ImageStatus Loader::status() const } -boost::signals::connection Loader::connect(slot_type const & slot) const +double Loader::displayPixelRatio() const +{ + return pimpl_->displayPixelRatio(); +} + + +void Loader::setDisplayPixelRatio(double scale) +{ + pimpl_->setDisplayPixelRatio(scale); +} + + +boost::signals2::connection Loader::connect(slot_type const & slot) const { return pimpl_->signal_.connect(slot); } @@ -196,8 +375,8 @@ Image const * Loader::image() const } -Loader::Impl::Impl() - : status_(WaitingToLoad) +Loader::Impl::Impl(FileName const & doc_file) + : doc_file_(doc_file), status_(WaitingToLoad) { } @@ -210,7 +389,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) @@ -222,19 +401,26 @@ void Loader::Impl::resetFile(FileName const & file) if (!old_file.empty()) { continue_monitoring = cached_item_->monitoring(); + // cached_item_ is going to be reset, so the connected + // 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(); if (!gc.inCache(file)) - gc.add(file); + gc.add(file, doc_file_); // We /must/ make a local copy of this. cached_item_ = gc.item(file); @@ -243,7 +429,7 @@ void Loader::Impl::resetFile(FileName const & file) if (continue_monitoring && !cached_item_->monitoring()) cached_item_->startMonitoring(); - cached_item_->connect(boost::bind(&Impl::statusChanged, this)); + sc_ = cached_item_->connect(bind(&Impl::statusChanged, this)); } @@ -253,14 +439,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_(); } @@ -268,16 +454,31 @@ 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; + } + } + } bool const success = image_->setPixmap(params_); @@ -294,6 +495,12 @@ void Loader::Impl::startLoading() if (status_ != WaitingToLoad) return; + if (cached_item_->tryDisplayFormat()) { + status_ = Loaded; + createPixmap(); + return; + } + LoaderQueue::get().touch(cached_item_); }