/* === S Y N F I G ========================================================= */
/*! \file synfig/rendering/task.cpp
** \brief Task
**
** $Id$
**
** \legal
** ......... ... 2015-2018 Ivan Mahonin
**
** This package 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 2 of
** the License, or (at your option) any later version.
**
** This package 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.
** \endlegal
*/
/* ========================================================================= */
/* === H E A D E R S ======================================================= */
#ifdef USING_PCH
# include "pch.h"
#else
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <synfig/general.h>
#include "task.h"
#include "renderer.h"
#endif
using namespace synfig;
using namespace rendering;
/* === M A C R O S ========================================================= */
/* === G L O B A L S ======================================================= */
/* === P R O C E D U R E S ================================================= */
/* === M E T H O D S ======================================================= */
synfig::Token Mode::mode_token;
synfig::Token Task::token;
const Task::Handle Task::blank;
Task::Token TaskSurface::token(
DescSpecial<TaskSurface>("Surface") );
Task::Token TaskLockSurface::token(
DescSpecial<TaskLockSurface>("LoskSurface") );
Task::Token TaskList::token(
DescSpecial<TaskList>("List") );
Task::Token TaskEvent::token(
DescSpecial<TaskEvent>("Event") );
// Task
void Task::Token::unprepare_vfunc()
{ alternatives_.clear(); }
void
Task::Token::prepare_vfunc()
{
assert( is_abstract()
? !mode && !abstract_task && !convert
: mode && abstract_task && abstract_task->is_abstract() && convert );
if (!is_abstract() && abstract_task->is_abstract()) {
assert(!abstract_task->alternatives_.count(mode));
abstract_task->cast_const().alternatives_[mode] = Handle(*this);
}
}
Task::RunParams::RunParams(const Renderer::Handle &renderer):
rendererHolder(renderer), renderer(renderer.get()) { }
Task::Task():
bounds_calculated(false),
bounds(Rect::infinite()),
source_rect(Rect::infinite()),
target_rect(RectInt::zero())
{ }
Task::~Task()
{ }
void
Task::assign_target(const Task &other) {
source_rect = other.source_rect;
target_rect = other.target_rect;
target_surface = other.target_surface;
}
void
Task::assign(const Task &other) {
assign_target(other);
sub_tasks = other.sub_tasks;
renderer_data = other.renderer_data; // TODO: remove renderer_data from task
}
Task&
Task::operator=(const Task &other)
{ assign(other); return *this; }
bool
Task::can_convert_to(ModeToken::Handle mode) const
{
if (!mode) return false;
Token::Handle token = get_token();
if (!token->is_abstract())
token = token->abstract_task;
return token->alternatives().count(mode);
}
ModeToken::Handle
Task::get_mode() const
{ return get_token()->mode; }
Task::Handle
Task::convert_to(ModeToken::Handle mode) const
{
if (!mode)
return Task::Handle();
Token::Handle token = get_token();
if (!token->is_abstract())
return Task::Handle();
Token::Map::const_iterator i = token->alternatives().find(mode);
if (i == token->alternatives().end())
return Task::Handle();
return Task::Handle(i->second->convert(*this));
}
Task::Handle
Task::convert_to_any() const
{
Token::Handle token = get_token();
if (!token->is_abstract())
return Task::Handle();
Task::Handle task;
for(Token::Map::const_iterator i = token->alternatives().begin(); i != token->alternatives().end(); ++i) {
task = i->second->convert(*this);
if (task) return task;
}
return Task::Handle();
}
Task::Handle
Task::clone() const {
Task *t = get_token()->clone(*this);
assert(t);
return Task::Handle(t);
}
Task::Handle
Task::clone_recursive() const
{
Task::Handle task = clone();
if (task)
for(List::iterator i = task->sub_tasks.begin(); i != task->sub_tasks.end(); ++i)
if (*i) (*i) = (*i)->clone_recursive();
return task;
}
Vector
Task::get_pixels_per_unit() const
{
if (!is_valid_coords())
return Vector();
return Vector(
(Real)target_rect.get_width()/source_rect.get_width(),
(Real)target_rect.get_height()/source_rect.get_height() );
}
Vector
Task::get_units_per_pixel() const
{
if (!is_valid_coords())
return Vector();
return Vector(
source_rect.get_width()/(Real)target_rect.get_width(),
source_rect.get_height()/(Real)target_rect.get_height() );
}
void
Task::trunc_to_zero()
{
source_rect = Rect::zero();
target_rect = RectInt::zero();
}
void
Task::trunc_source_rect(const Rect &rect)
{
if (!rect.is_valid() || !is_valid_coords())
{ trunc_to_zero(); return; }
Rect sr = source_rect & rect;
if (!sr.is_valid())
{ trunc_to_zero(); return; }
Vector ppu = get_pixels_per_unit();
RectInt tr = target_rect;
tr.minx += (int)floor(ppu[0]*(sr.minx - source_rect.minx));
tr.miny += (int)floor(ppu[1]*(sr.miny - source_rect.miny));
tr.maxx -= (int)floor(ppu[0]*(source_rect.maxx - sr.maxx));
tr.maxy -= (int)floor(ppu[1]*(source_rect.maxy - sr.maxy));
trunc_target_rect(tr);
}
void
Task::trunc_target_rect(const RectInt &rect)
{
if (!rect.is_valid() || !is_valid_coords())
{ trunc_to_zero(); return; }
RectInt tr = target_rect & rect;
if (!tr.is_valid())
{ trunc_to_zero(); return; }
Vector upp = get_units_per_pixel();
source_rect.minx += upp[0]*(tr.minx - target_rect.minx);
source_rect.miny += upp[1]*(tr.miny - target_rect.miny);
source_rect.maxx -= upp[0]*(target_rect.maxx - tr.maxx);
source_rect.maxy -= upp[1]*(target_rect.maxy - tr.maxy);
target_rect = tr;
}
void
Task::trunc_by_bounds()
{ trunc_source_rect(get_bounds()); }
void
Task::move_target_rect(const VectorInt &offset)
{ if (is_valid_coords()) target_rect += offset; }
void
Task::set_target_origin(const VectorInt &origin)
{ if (is_valid_coords()) move_target_rect(origin - target_rect.get_min()); }
Rect
Task::calc_bounds() const
{ return Rect::infinite(); }
void
Task::set_coords(const Rect &source_rect, const VectorInt &target_size)
{
if (this->source_rect.is_full_infinite()) {
this->source_rect = source_rect;
this->target_rect = RectInt(VectorInt(), target_size);
if (!is_valid_coords())
trunc_to_zero();
} else {
trunc_source_rect(source_rect);
}
if (!target_surface)
target_surface = new SurfaceResource();
// allocate surface by incoming target_size without truncation,
// it's significant for transformation antialiasing
if (!target_surface->is_exists())
target_surface->create(target_rect.maxx, target_rect.maxy);
trunc_by_bounds();
set_coords_sub_tasks();
}
bool
Task::allow_run_before(Task &other) const {
if (!is_valid() || !other.is_valid())
return true;
if (!get_allow_multithreading() && !other.get_allow_multithreading())
return false;
if (target_surface == other.target_surface)
if ( !get_mode_allow_simultaneous_write()
|| !other.get_mode_allow_simultaneous_write()
|| get_target_token() != other.get_target_token()
|| etl::intersect(target_rect, other.target_rect) )
return false;
for(Task::List::const_iterator i = sub_tasks.begin(); i != sub_tasks.end(); ++i)
if ( *i && (*i)->is_valid()
&& (*i)->target_surface == other.target_surface
&& etl::intersect((*i)->target_rect, other.target_rect) ) return false;
return true;
}
void
Task::set_coords_zero()
{ set_coords(Rect::zero(), VectorInt::zero()); }
void
Task::touch_coords()
{ set_coords(Rect(source_rect), target_rect.get_size()); }
void
Task::set_coords_sub_tasks()
{
// by default set the same coords for all childs
for(List::iterator i = sub_tasks.begin(); i != sub_tasks.end(); ++i)
if (*i) (*i)->set_coords(source_rect, target_rect.get_size());
}
bool
Task::run(RunParams & /* params */) const
{ return false; }
// TaskList
VectorInt
TaskList::calc_target_offset(const Task &a, const Task &b)
{
Vector ppuA = a.get_pixels_per_unit();
Vector ppuB = b.get_pixels_per_unit();
if (ppuA != ppuB)
warning( "Different pixel-per-unit value while calculation of target offset. a: %s, b: %s",
a.get_token()->name.c_str(),
b.get_token()->name.c_str() );
Vector offset = (b.source_rect.get_min() - a.source_rect.get_min()).multiply_coords(a.get_pixels_per_unit());
return b.target_rect.get_min() - a.target_rect.get_min() - VectorInt((int)round(offset[0]), (int)round(offset[1]));
}
// TaskLockSurface
void
TaskLockSurface::set_surface(const SurfaceResource::Handle &surface)
{
unlock();
target_surface = surface;
source_rect = Rect(0.0, 0.0, 1.0, 1.0);
target_rect = RectInt(
VectorInt::zero(),
target_surface ? target_surface->get_size() : VectorInt::zero() );
lock();
}
TaskLockSurface&
TaskLockSurface::operator=(const TaskLockSurface& other) {
*(TaskSurface*)this = other;
if (other.lock_ && other.lock_->resource != other.target_surface)
lock();
return *this;
}
void
TaskLockSurface::lock() {
if (lock_ && lock_->resource != target_surface)
unlock();
if (target_surface)
lock_ = new SurfaceResource::LockReadBase(target_surface);
}
void
TaskLockSurface::unlock() {
if (lock_) {
delete lock_;
lock_ = NULL;
}
}
// TaskEvent
TaskEvent&
TaskEvent::operator=(const TaskEvent &other)
{
*(Task*)(this) = other;
done = (int)other.done;
cancelled = (int)other.cancelled;
signal_finished = other.signal_finished;
return *this;
}
bool
TaskEvent::is_done() const
{
std::lock_guard<std::mutex> lock(mutex);
return done;
}
bool
TaskEvent::is_cancelled() const
{
std::lock_guard<std::mutex> lock(mutex);
return cancelled;
}
bool
TaskEvent::is_finished() const
{
std::lock_guard<std::mutex> lock(mutex);
return done || cancelled;
}
void
TaskEvent::finish(bool success)
{
{
std::lock_guard<std::mutex> lock(mutex);
if (done || cancelled) return;
(success ? done : cancelled) = true;
}
signal_finished(success);
cond.notify_one();
}
void
TaskEvent::wait()
{
std::unique_lock<std::mutex> lock(mutex);
if (done || cancelled) return;
cond.wait(lock);
}
bool
TaskEvent::run(RunParams & /* params */) const
{
const_cast<TaskEvent*>(this)->finish(true);
return true;
}
/* === E N T R Y P O I N T ================================================= */