rawTherapee/rtgui/thumbimageupdater.cc
Hombre 8b2eac9a3d Pipette and "On Preview Widgets" branch. See issue 227
The pipette part is already working quite nice but need to be finished. The widgets part needs more work...
2014-01-21 23:37:36 +01:00

321 lines
6.7 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee 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.
*
* RawTherapee 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 RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include <set>
#include "thumbimageupdater.h"
#include <gtkmm.h>
#include "guiutils.h"
#include "threadutils.h"
#ifdef _OPENMP
#include <omp.h>
#endif
#define DEBUG(format,args...)
//#define DEBUG(format,args...) printf("ThumbImageUpdate::%s: " format "\n", __FUNCTION__, ## args)
class
ThumbImageUpdater::Impl
{
public:
struct Job
{
Job(ThumbBrowserEntryBase* tbe, bool* priority, bool upgrade,
ThumbImageUpdateListener* listener):
tbe_(tbe),
/*pparams_(pparams),
height_(height), */
priority_(priority),
upgrade_(upgrade),
listener_(listener)
{}
Job():
tbe_(0),
priority_(NULL),
upgrade_(false),
listener_(0)
{}
ThumbBrowserEntryBase* tbe_;
/*rtengine::procparams::ProcParams pparams_;
int height_;*/
bool* priority_;
bool upgrade_;
ThumbImageUpdateListener* listener_;
};
typedef std::list<Job> JobList;
Impl():
active_(0),
inactive_waiting_(false)
{
int threadCount=1;
#ifdef _OPENMP
threadCount=omp_get_num_procs();
#endif
threadPool_=new Glib::ThreadPool(threadCount,0);
}
Glib::ThreadPool* threadPool_;
// Need to be a Glib::Threads::Mutex because used in a Glib::Threads::Cond object...
// This is the only exceptions in RT so far, MyMutex is used everywhere else
#ifdef WIN32
Glib::Mutex mutex_;
#else
Glib::Threads::Mutex mutex_;
#endif
JobList jobs_;
unsigned int active_;
bool inactive_waiting_;
#ifdef WIN32
Glib::Cond inactive_;
#else
Glib::Threads::Cond inactive_;
#endif
void
processNextJob()
{
Job j;
{
#ifdef WIN32
Glib::Mutex::Lock lock(mutex_);
#else
Glib::Threads::Mutex::Lock lock(mutex_);
#endif
// nothing to do; could be jobs have been removed
if ( jobs_.empty() )
{
DEBUG("processing: nothing to do (%d)",jobs_.empty());
return;
}
JobList::iterator i;
// see if any priority jobs exist
for ( i = jobs_.begin(); i != jobs_.end(); ++i)
{
if ( *(i->priority_) )
{
DEBUG("processing(priority) %s",i->tbe_->thumbnail->getFileName().c_str());
break;
}
}
// see if any none upgrade jobs exist
if ( i == jobs_.end() )
{
for ( i = jobs_.begin(); i != jobs_.end(); ++i)
{
if ( !i->upgrade_ )
{
DEBUG("processing(not-upgrade) %s",i->tbe_->thumbnail->getFileName().c_str());
break;
}
}
}
// if none, then use first
if ( i == jobs_.end() )
{
i = jobs_.begin();
DEBUG("processing(first) %s",i->tbe_->thumbnail->getFileName().c_str());
}
// copy found job
j = *i;
// remove so not run again
jobs_.erase(i);
DEBUG("%d job(s) remaining", int(jobs_.size()) );
++active_;
}
// unlock and do processing; will relock on block exit, then call listener
double scale = 1.0;
rtengine::IImage8* img = 0;
Thumbnail* thm=j.tbe_->thumbnail;
if ( j.upgrade_ )
{
if ( thm->isQuick() )
{
img = thm->upgradeThumbImage(thm->getProcParams(), j.tbe_->getPreviewHeight(), scale);
}
}
else
{
img = thm->processThumbImage(thm->getProcParams(), j.tbe_->getPreviewHeight(), scale);
}
if (img)
{
DEBUG("pushing image %s",thm->getFileName().c_str());
j.listener_->updateImage(img, scale, thm->getProcParams().crop);
}
{
#ifdef WIN32
Glib::Mutex::Lock lock(mutex_);
#else
Glib::Threads::Mutex::Lock lock(mutex_);
#endif
if ( --active_ == 0 &&
inactive_waiting_ )
{
inactive_waiting_ = false;
inactive_.signal();
}
}
}
};
ThumbImageUpdater*
ThumbImageUpdater::getInstance(void)
{
// this will not be deleted...
static ThumbImageUpdater* instance_ = 0;
if ( instance_ == 0 )
{
instance_ = new ThumbImageUpdater();
}
return instance_;
}
ThumbImageUpdater::ThumbImageUpdater():
impl_(new Impl())
{
}
void
ThumbImageUpdater::add(ThumbBrowserEntryBase* tbe, bool* priority, bool upgrade, ThumbImageUpdateListener* l)
{
// nobody listening?
if ( l == 0 )
{
return;
}
#ifdef WIN32
Glib::Mutex::Lock lock(impl_->mutex_);
#else
Glib::Threads::Mutex::Lock lock(impl_->mutex_);
#endif
// look up if an older version is in the queue
Impl::JobList::iterator i(impl_->jobs_.begin());
for ( ; i != impl_->jobs_.end(); ++i )
{
if ( i->tbe_ == tbe &&
i->listener_ == l &&
i->upgrade_ == upgrade )
{
DEBUG("updating job %s",tbe->shortname.c_str());
// we have one, update queue entry, will be picked up by thread when processed
/*i->pparams_ = params;
i->height_ = height; */
i->priority_ = priority;
return;
}
}
// create a new job and append to queue
DEBUG("queing job %s",tbe->shortname.c_str());
impl_->jobs_.push_back(Impl::Job(tbe,priority,upgrade,l));
DEBUG("adding run request %s",tbe->shortname.c_str());
impl_->threadPool_->push(sigc::mem_fun(*impl_, &ThumbImageUpdater::Impl::processNextJob));
}
void
ThumbImageUpdater::removeJobs(ThumbImageUpdateListener* listener)
{
DEBUG("removeJobs(%p)",listener);
#ifdef WIN32
Glib::Mutex::Lock lock(impl_->mutex_);
#else
Glib::Threads::Mutex::Lock lock(impl_->mutex_);
#endif
for( Impl::JobList::iterator i(impl_->jobs_.begin()); i != impl_->jobs_.end(); )
{
if (i->listener_ == listener)
{
DEBUG("erasing specific job");
Impl::JobList::iterator e(i++);
impl_->jobs_.erase(e);
}
else
{
++i;
}
}
while ( impl_->active_ != 0 )
{
// XXX this is nasty... it would be nicer if we weren't called with
// this lock held
GThreadUnLock unlock;
DEBUG("waiting for running jobs1");
impl_->inactive_waiting_ = true;
impl_->inactive_.wait(impl_->mutex_);
}
}
void
ThumbImageUpdater::removeAllJobs(void)
{
DEBUG("stop");
#ifdef WIN32
Glib::Mutex::Lock lock(impl_->mutex_);
#else
Glib::Threads::Mutex::Lock lock(impl_->mutex_);
#endif
impl_->jobs_.clear();
while ( impl_->active_ != 0 )
{
// XXX this is nasty... it would be nicer if we weren't called with
// this lock held
GThreadUnLock unlock;
DEBUG("waiting for running jobs2");
impl_->inactive_waiting_ = true;
impl_->inactive_.wait(impl_->mutex_);
}
}