|
Carlos Lopez |
a09598 |
/* === S Y N F I G ========================================================= */
|
|
Carlos Lopez |
a09598 |
/*! \file renderer_canvas.cpp
|
|
Carlos Lopez |
a09598 |
** \brief Template File
|
|
Carlos Lopez |
a09598 |
**
|
|
Carlos Lopez |
a09598 |
** $Id$
|
|
Carlos Lopez |
a09598 |
**
|
|
Carlos Lopez |
a09598 |
** \legal
|
|
Carlos Lopez |
a09598 |
** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
|
|
Carlos Lopez |
a09598 |
** Copyright (c) 2007, 2008 Chris Moore
|
|
Carlos Lopez |
42fd7a |
** Copyright (c) 2011 Nikita Kitaev
|
|
|
3707d8 |
** ......... ... 2018 Ivan Mahonin
|
|
Carlos Lopez |
a09598 |
**
|
|
Carlos Lopez |
a09598 |
** This package is free software; you can redistribute it and/or
|
|
Carlos Lopez |
a09598 |
** modify it under the terms of the GNU General Public License as
|
|
Carlos Lopez |
a09598 |
** published by the Free Software Foundation; either version 2 of
|
|
Carlos Lopez |
a09598 |
** the License, or (at your option) any later version.
|
|
Carlos Lopez |
a09598 |
**
|
|
Carlos Lopez |
a09598 |
** This package is distributed in the hope that it will be useful,
|
|
Carlos Lopez |
a09598 |
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Carlos Lopez |
a09598 |
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Carlos Lopez |
a09598 |
** General Public License for more details.
|
|
Carlos Lopez |
a09598 |
** \endlegal
|
|
Carlos Lopez |
a09598 |
*/
|
|
Carlos Lopez |
a09598 |
/* ========================================================================= */
|
|
Carlos Lopez |
a09598 |
|
|
Carlos Lopez |
a09598 |
/* === H E A D E R S ======================================================= */
|
|
Carlos Lopez |
a09598 |
|
|
Carlos Lopez |
a09598 |
#ifdef USING_PCH
|
|
Carlos Lopez |
a09598 |
# include "pch.h"
|
|
Carlos Lopez |
a09598 |
#else
|
|
Carlos Lopez |
a09598 |
#ifdef HAVE_CONFIG_H
|
|
Carlos Lopez |
a09598 |
# include <config.h></config.h>
|
|
Carlos Lopez |
a09598 |
#endif
|
|
Carlos Lopez |
a09598 |
|
|
|
46d84c |
#include <ctime></ctime>
|
|
|
cb8070 |
#include <cstring></cstring>
|
|
|
65aa32 |
#include <valarray></valarray>
|
|
|
46d84c |
|
|
|
46d84c |
#include <glib.h></glib.h>
|
|
|
e06541 |
#include <gdkmm general.h=""></gdkmm>
|
|
bw |
94d8a6 |
|
|
Carlos Lopez |
a09598 |
#include <etl misc=""></etl>
|
|
|
e06541 |
|
|
|
e06541 |
#include <synfig general.h=""></synfig>
|
|
|
e06541 |
#include <synfig canvas.h=""></synfig>
|
|
|
e06541 |
#include <synfig context.h=""></synfig>
|
|
|
e06541 |
#include <synfig threadpool.h=""></synfig>
|
|
|
e06541 |
#include <synfig renderer.h="" rendering=""></synfig>
|
|
|
e6766e |
#include <synfig common="" rendering="" task="" tasktransformation.h=""></synfig>
|
|
Carlos Lopez |
a09598 |
|
|
|
e06541 |
#include <gui app.h=""></gui>
|
|
|
4fb579 |
#include <gui canvasview.h=""></gui>
|
|
|
8377b5 |
#include <gui timemodel.h=""></gui>
|
|
|
e06541 |
|
|
|
e06541 |
#include "renderer_canvas.h"
|
|
Carlos Lopez |
a09598 |
|
|
|
f3d2e7 |
#include <gui localization.h=""></gui>
|
|
|
f3d2e7 |
|
|
Carlos Lopez |
a09598 |
#endif
|
|
Carlos Lopez |
a09598 |
|
|
Carlos Lopez |
a09598 |
/* === U S I N G =========================================================== */
|
|
Carlos Lopez |
a09598 |
|
|
Carlos Lopez |
a09598 |
using namespace synfig;
|
|
Carlos Lopez |
a09598 |
using namespace studio;
|
|
Carlos Lopez |
a09598 |
|
|
Carlos Lopez |
a09598 |
/* === M A C R O S ========================================================= */
|
|
Carlos Lopez |
a09598 |
|
|
|
48ca60 |
#ifndef NDEBUG
|
|
|
48ca60 |
//#define DEBUG_TILES
|
|
|
48ca60 |
#endif
|
|
|
65aa32 |
|
|
Carlos Lopez |
a09598 |
/* === G L O B A L S ======================================================= */
|
|
Carlos Lopez |
a09598 |
|
|
Carlos Lopez |
a09598 |
/* === P R O C E D U R E S ================================================= */
|
|
Carlos Lopez |
a09598 |
|
|
|
e06541 |
static int
|
|
|
e06541 |
int_floor(int x, int base)
|
|
|
b943e0 |
{ int m = x % base; return m < 0 ? x - base - m : m > 0 ? x - m : x; }
|
|
|
e06541 |
|
|
|
e06541 |
static int
|
|
|
e06541 |
int_ceil(int x, int base)
|
|
|
b943e0 |
{ int m = x % base; return m > 0 ? x + base - m : m < 0 ? x - m : x; }
|
|
|
e06541 |
|
|
|
b943e0 |
static long long
|
|
|
b943e0 |
image_rect_size(const RectInt &rect)
|
|
|
b943e0 |
{ return 4ll*rect.get_width()*rect.get_height(); }
|
|
|
46d84c |
|
|
|
b943e0 |
/* === M E T H O D S ======================================================= */
|
|
|
46d84c |
|
|
|
e06541 |
Renderer_Canvas::Renderer_Canvas():
|
|
|
b943e0 |
max_tiles_size_soft(512*1024*1024),
|
|
|
b943e0 |
max_tiles_size_hard(max_tiles_size_soft + 128*1024*1024),
|
|
|
8377b5 |
weight_future ( 1.0), // high priority
|
|
|
8377b5 |
weight_past ( 2.0), // low priority
|
|
|
8377b5 |
weight_future_extra( 16.0),
|
|
|
8377b5 |
weight_past_extra ( 32.0),
|
|
|
8377b5 |
weight_zoom_in (1024.0), // very very low priority
|
|
|
8377b5 |
weight_zoom_out (1024.0),
|
|
|
dec360 |
max_enqueued_tasks (6),
|
|
|
dec360 |
enqueued_tasks(),
|
|
|
b943e0 |
tiles_size(),
|
|
|
dec360 |
pixel_format()
|
|
|
7c6c2d |
{
|
|
luz.paz |
99f3ef |
// check endianness
|
|
|
7c6c2d |
union { int i; char c[4]; } checker = {0x01020304};
|
|
|
7c6c2d |
bool big_endian = checker.c[0] == 1;
|
|
|
7c6c2d |
|
|
|
7c6c2d |
pixel_format = big_endian
|
|
|
7c6c2d |
? (PF_A_START | PF_RGB | PF_A_PREMULT)
|
|
|
7c6c2d |
: (PF_BGR | PF_A | PF_A_PREMULT);
|
|
|
017782 |
|
|
|
017782 |
alpha_src_surface = Cairo::ImageSurface::create(
|
|
|
cb8070 |
Cairo::FORMAT_ARGB32, 1, 1);
|
|
|
017782 |
alpha_dst_surface = Cairo::ImageSurface::create(
|
|
|
cb8070 |
Cairo::FORMAT_ARGB32, 1, 1);
|
|
|
017782 |
|
|
|
cb8070 |
//! fill alpha_src_surface with white color
|
|
|
cb8070 |
//! alpha premulted - so all four channels have the same value
|
|
|
017782 |
unsigned char *data = alpha_src_surface->get_data();
|
|
|
cb8070 |
data[0] = data[1] = data[2] = data[3] = 255;
|
|
|
017782 |
alpha_src_surface->mark_dirty();
|
|
|
017782 |
alpha_src_surface->flush();
|
|
|
017782 |
|
|
|
017782 |
alpha_context = Cairo::Context::create(alpha_dst_surface);
|
|
|
7c6c2d |
}
|
|
|
e06541 |
|
|
Carlos Lopez |
a09598 |
Renderer_Canvas::~Renderer_Canvas()
|
|
|
b943e0 |
{ clear_render(); }
|
|
Carlos Lopez |
d7d632 |
|
|
|
3707d8 |
void
|
|
|
e06541 |
Renderer_Canvas::on_tile_finished_callback(bool success, Renderer_Canvas *obj, Tile::Handle tile)
|
|
|
e06541 |
{
|
|
|
b943e0 |
// This method may be called from the other threads
|
|
|
e06541 |
// Handle will protect 'tile' from deletion before this call
|
|
|
e06541 |
// This callback will called only once for each tile
|
|
|
e06541 |
// And will called before deletion of 'obj', by calling cancel_render() from destructor
|
|
|
e06541 |
obj->on_tile_finished(success, tile);
|
|
|
e06541 |
}
|
|
|
e06541 |
|
|
|
e06541 |
void
|
|
|
f2de3c |
Renderer_Canvas::on_post_tile_finished_callback(etl::handle<renderer_canvas> obj, Tile::Handle tile) {</renderer_canvas>
|
|
|
e06541 |
// this function should be called in main thread
|
|
|
e06541 |
// Handle will protect 'obj' from deletion before this call
|
|
|
46d84c |
// zero 'work_area' means that 'work_area' is destructed and here is a last Handle of 'obj'
|
|
|
46d84c |
if (obj->get_work_area())
|
|
|
f2de3c |
obj->on_post_tile_finished(tile);
|
|
|
e06541 |
}
|
|
|
e06541 |
|
|
|
b943e0 |
Cairo::RefPtr<cairo::imagesurface></cairo::imagesurface>
|
|
|
b943e0 |
Renderer_Canvas::convert(
|
|
|
e433dc |
const rendering::SurfaceResource::Handle &surface,
|
|
|
b943e0 |
int width, int height ) const
|
|
|
e06541 |
{
|
|
|
b943e0 |
// this method may be called from the other threads
|
|
|
b943e0 |
assert(width > 0 && height > 0);
|
|
Carlos Lopez |
a09598 |
|
|
|
b943e0 |
Cairo::RefPtr<cairo::imagesurface> cairo_surface =</cairo::imagesurface>
|
|
|
b943e0 |
Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height);
|
|
|
e06541 |
|
|
|
b943e0 |
bool success = false;
|
|
|
b943e0 |
|
|
|
b943e0 |
rendering::SurfaceResource::LockReadBase surface_lock(surface);
|
|
|
b943e0 |
if (surface_lock.get_resource() && surface_lock.get_resource()->is_blank()) {
|
|
|
b943e0 |
success = true;
|
|
|
b943e0 |
} else
|
|
|
b943e0 |
if (surface_lock.convert(rendering::Surface::Token::Handle(), false, true)) {
|
|
|
b943e0 |
const rendering::Surface &s = *surface_lock.get_surface();
|
|
|
b943e0 |
int w = s.get_width();
|
|
|
b943e0 |
int h = s.get_height();
|
|
|
b943e0 |
if (w == width && h == height) {
|
|
|
b943e0 |
const Color *pixels = s.get_pixels_pointer();
|
|
|
b943e0 |
std::vector<color> pixels_copy;</color>
|
|
|
b943e0 |
if (!pixels) {
|
|
|
b943e0 |
pixels_copy.resize(w*h);
|
|
|
b943e0 |
if (s.get_pixels(&pixels_copy.front()))
|
|
|
b943e0 |
pixels = &pixels_copy.front();
|
|
|
b943e0 |
}
|
|
|
b943e0 |
if (pixels) {
|
|
|
b943e0 |
// do conversion
|
|
|
b943e0 |
cairo_surface->flush();
|
|
|
d21b46 |
color_to_pixelformat(
|
|
|
d21b46 |
cairo_surface->get_data(),
|
|
|
d21b46 |
pixels,
|
|
|
d21b46 |
pixel_format,
|
|
|
a4bbdd |
0,
|
|
|
d21b46 |
cairo_surface->get_width(),
|
|
|
d21b46 |
cairo_surface->get_height(),
|
|
|
d21b46 |
cairo_surface->get_stride() );
|
|
|
b943e0 |
cairo_surface->mark_dirty();
|
|
|
b943e0 |
cairo_surface->flush();
|
|
|
b943e0 |
success = true;
|
|
|
e433dc |
} else error("Renderer_Canvas::convert: cannot access surface pixels - that really strange");
|
|
|
e433dc |
} else error("Renderer_Canvas::convert: surface with wrong size");
|
|
|
e433dc |
} else error("Renderer_Canvas::convert: surface not exists");
|
|
|
b943e0 |
|
|
|
b943e0 |
|
|
|
b943e0 |
#ifdef DEBUG_TILES
|
|
|
b943e0 |
const bool debug_tiles = true;
|
|
|
b943e0 |
#else
|
|
|
b943e0 |
const bool debug_tiles = false;
|
|
|
b943e0 |
#endif
|
|
|
b943e0 |
|
|
|
b943e0 |
// paint tile
|
|
|
b943e0 |
if (debug_tiles || !success) {
|
|
|
b943e0 |
Cairo::RefPtr<cairo::context> context = Cairo::Context::create(cairo_surface);</cairo::context>
|
|
|
b943e0 |
|
|
|
b943e0 |
if (!success) {
|
|
|
b943e0 |
// draw cross
|
|
|
b943e0 |
context->move_to(0.0, 0.0);
|
|
|
b943e0 |
context->line_to((double)width, (double)height);
|
|
|
b943e0 |
context->move_to((double)width, 0.0);
|
|
|
b943e0 |
context->line_to(0.0, (double)height);
|
|
|
b943e0 |
context->stroke();
|
|
|
e06541 |
}
|
|
|
b943e0 |
|
|
|
b943e0 |
// draw border
|
|
|
b943e0 |
context->rectangle(0, 0, width, height);
|
|
|
b943e0 |
context->stroke();
|
|
|
b943e0 |
std::valarray<double> dash(2); dash[0] = 2.0; dash[1] = 2.0;</double>
|
|
|
b943e0 |
context->set_dash(dash, 0.0);
|
|
|
b943e0 |
context->rectangle(4, 4, width-8, height-8);
|
|
|
b943e0 |
context->stroke();
|
|
|
b943e0 |
|
|
|
b943e0 |
cairo_surface->flush();
|
|
|
e06541 |
}
|
|
|
b943e0 |
return cairo_surface;
|
|
|
48ca60 |
}
|
|
|
48ca60 |
|
|
|
48ca60 |
void
|
|
|
48ca60 |
Renderer_Canvas::on_tile_finished(bool success, const Tile::Handle &tile)
|
|
|
48ca60 |
{
|
|
|
48ca60 |
// this method must be called from on_tile_finished_callback()
|
|
|
48ca60 |
// this method may be called from other threads
|
|
|
48ca60 |
|
|
|
b943e0 |
// 'tiles', 'onion_frames', 'refresh_id', and 'tiles_size' are controlled by mutex
|
|
|
48ca60 |
|
|
|
12dbc2 |
Cairo::RefPtr<cairo::imagesurface> cairo_surface;</cairo::imagesurface>
|
|
|
12dbc2 |
if (success && tile->surface)
|
|
|
12dbc2 |
cairo_surface = convert(tile->surface, tile->rect.get_width(), tile->rect.get_height());
|
|
|
12dbc2 |
|
|
|
48ca60 |
Glib::Threads::Mutex::Lock lock(mutex);
|
|
|
dec360 |
|
|
|
dec360 |
--enqueued_tasks;
|
|
|
dec360 |
|
|
|
b943e0 |
if (!tile->event && !tile->surface && !tile->cairo_surface)
|
|
|
b943e0 |
return; // tile is already removed
|
|
|
48ca60 |
|
|
|
b943e0 |
tile->event.reset();
|
|
|
12dbc2 |
tile->cairo_surface = cairo_surface;
|
|
|
b943e0 |
tile->surface.reset();
|
|
|
e06541 |
|
|
|
e06541 |
// don't create handle if ref-count is zero
|
|
|
e06541 |
// it means that object was nether had a handles and will removed with handle
|
|
|
e06541 |
// or object is already in destruction phase
|
|
|
e06541 |
if (shared_object::count())
|
|
|
f2de3c |
Glib::signal_idle().connect_once(
|
|
|
d421d7 |
sigc::bind(sigc::ptr_fun(&on_post_tile_finished_callback), etl::handle<renderer_canvas>(this), tile),</renderer_canvas>
|
|
|
d421d7 |
visible_frames.count(tile->frame_id) ? Glib::PRIORITY_DEFAULT : Glib::PRIORITY_DEFAULT_IDLE );
|
|
|
e06541 |
}
|
|
|
e06541 |
|
|
|
e06541 |
void
|
|
|
f2de3c |
Renderer_Canvas::on_post_tile_finished(const Tile::Handle &tile)
|
|
|
46d84c |
{
|
|
|
b943e0 |
// this method must be called from on_post_tile_finished_callback()
|
|
|
b943e0 |
// check if rendering is finished
|
|
|
f2de3c |
bool tile_visible = false;
|
|
|
12dbc2 |
int local_enqueued_tasks;
|
|
|
e433dc |
Time time;
|
|
|
b943e0 |
{
|
|
|
b943e0 |
Glib::Threads::Mutex::Lock lock(mutex);
|
|
|
d421d7 |
time = tile->frame_id.time;
|
|
|
f2de3c |
if (visible_frames.count(tile->frame_id))
|
|
|
f2de3c |
tile_visible = true;
|
|
|
12dbc2 |
local_enqueued_tasks = enqueued_tasks; // field should be protected by mutex
|
|
|
46d84c |
}
|
|
|
b943e0 |
|
|
|
d421d7 |
if (get_work_area()) {
|
|
|
d421d7 |
get_work_area()->signal_rendering()();
|
|
|
d421d7 |
get_work_area()->signal_rendering_tile_finished()(time);
|
|
|
d421d7 |
if (tile_visible)
|
|
|
d421d7 |
get_work_area()->queue_draw(); // enqueue_render will called while draw
|
|
|
d421d7 |
else
|
|
|
12dbc2 |
if (!local_enqueued_tasks)
|
|
|
d421d7 |
enqueue_render();
|
|
|
d421d7 |
}
|
|
|
46d84c |
}
|
|
|
46d84c |
|
|
|
46d84c |
void
|
|
|
b943e0 |
Renderer_Canvas::insert_tile(TileList &list, const Tile::Handle &tile)
|
|
|
46d84c |
{
|
|
|
b943e0 |
// this method may be called from other threads
|
|
|
b943e0 |
// mutex must be already locked
|
|
|
b943e0 |
list.push_back(tile);
|
|
|
b943e0 |
tiles_size += image_rect_size(tile->rect);
|
|
|
b943e0 |
}
|
|
|
46d84c |
|
|
|
b943e0 |
void
|
|
|
b943e0 |
Renderer_Canvas::erase_tile(TileList &list, TileList::iterator i, rendering::Task::List &events)
|
|
|
b943e0 |
{
|
|
|
b943e0 |
// this method may be called from other threads
|
|
|
b943e0 |
// mutex must be already locked
|
|
|
b943e0 |
if ((*i)->event) events.push_back((*i)->event);
|
|
|
b943e0 |
tiles_size -= image_rect_size((*i)->rect);
|
|
|
b943e0 |
(*i)->event.reset();
|
|
|
b943e0 |
(*i)->surface.reset();
|
|
|
b943e0 |
(*i)->cairo_surface.clear();
|
|
|
b943e0 |
list.erase(i);
|
|
|
b943e0 |
}
|
|
|
b943e0 |
|
|
|
b943e0 |
void
|
|
|
e433dc |
Renderer_Canvas::remove_extra_tiles(rendering::Task::List &events)
|
|
|
b943e0 |
{
|
|
|
b943e0 |
// mutex must be already locked
|
|
|
b943e0 |
|
|
|
b943e0 |
typedef std::multimap<real, tilemap::iterator=""> WeightMap;</real,>
|
|
|
b943e0 |
WeightMap sorted_frames;
|
|
|
b943e0 |
|
|
|
b943e0 |
Real current_zoom = sqrt((Real)(current_frame.width * current_frame.height));
|
|
|
b943e0 |
|
|
|
b943e0 |
// calc weight
|
|
|
b943e0 |
for(TileMap::iterator i = tiles.begin(); i != tiles.end(); ++i) {
|
|
|
b943e0 |
if (!visible_frames.count(i->first) && tiles_size > max_tiles_size_hard) {
|
|
|
b943e0 |
Real weight = 0.0;
|
|
|
b943e0 |
if (frame_duration) {
|
|
|
b943e0 |
Time dt = i->first.time - current_frame.time;
|
|
|
b943e0 |
Real df = ((double)dt)/(double)frame_duration;
|
|
|
b943e0 |
weight += df*(df > 0.0 ? weight_future : weight_past);
|
|
|
b943e0 |
}
|
|
|
b943e0 |
if (current_zoom) {
|
|
|
b943e0 |
Real zoom = sqrt((Real)(i->first.width * i->first.height));
|
|
|
b943e0 |
Real zoom_step = log(zoom/current_zoom);
|
|
|
b943e0 |
weight += zoom_step*(zoom_step > 0.0 ? weight_zoom_in : weight_zoom_out);
|
|
|
b943e0 |
}
|
|
|
b943e0 |
sorted_frames.insert( WeightMap::value_type(weight, i) );
|
|
|
46d84c |
}
|
|
|
46d84c |
}
|
|
|
46d84c |
|
|
|
b943e0 |
// remove some extra tiles to free the memory
|
|
|
b943e0 |
for(WeightMap::reverse_iterator ri = sorted_frames.rbegin(); ri != sorted_frames.rend() && tiles_size > max_tiles_size_hard; ++ri)
|
|
|
b943e0 |
for(TileList::iterator j = ri->second->second.begin(); j != ri->second->second.end() && tiles_size > max_tiles_size_hard; )
|
|
|
b943e0 |
erase_tile(ri->second->second, j++, events);
|
|
|
b943e0 |
|
|
|
b943e0 |
// remove empty entries from tiles map
|
|
|
b943e0 |
for(TileMap::iterator i = tiles.begin(); i != tiles.end(); )
|
|
|
b943e0 |
if (i->second.empty()) tiles.erase(i++); else ++i;
|
|
|
46d84c |
}
|
|
|
46d84c |
|
|
|
46d84c |
void
|
|
|
b943e0 |
Renderer_Canvas::build_onion_frames()
|
|
|
e06541 |
{
|
|
|
b943e0 |
// mutex must be already locked
|
|
Carlos Lopez |
a09598 |
|
|
|
b943e0 |
Canvas::Handle canvas = get_work_area()->get_canvas();
|
|
|
b943e0 |
int w = get_work_area()->get_w();
|
|
|
b943e0 |
int h = get_work_area()->get_h();
|
|
|
d8ad70 |
int thumb_w = get_work_area()->get_thumb_w();
|
|
|
d8ad70 |
int thumb_h = get_work_area()->get_thumb_h();
|
|
|
b943e0 |
int past = std::max(0, get_work_area()->get_onion_skins()[0]);
|
|
|
b943e0 |
int future = std::max(0, get_work_area()->get_onion_skins()[1]);
|
|
|
4fb579 |
|
|
|
4fb579 |
Time base_time;
|
|
|
4fb579 |
if (CanvasView::Handle canvas_view = get_work_area()->get_canvas_view())
|
|
|
6eb56a |
base_time = Time(canvas_view->get_time());
|
|
|
4fb579 |
|
|
|
b943e0 |
RendDesc rend_desc = canvas->rend_desc();
|
|
|
b943e0 |
float fps = rend_desc.get_frame_rate();
|
|
|
b943e0 |
|
|
|
b943e0 |
current_frame = FrameId(base_time, w, h);
|
|
|
d421d7 |
current_thumb = FrameId(base_time, thumb_w, thumb_h);
|
|
|
b943e0 |
frame_duration = Time(approximate_greater_lp(fps, 0.f) ? 1.0/(double)fps : 0.0);
|
|
|
b943e0 |
|
|
|
b943e0 |
// set onion_frames
|
|
|
b943e0 |
onion_frames.clear();
|
|
|
b943e0 |
if ( get_work_area()->get_onion_skin()
|
|
|
b943e0 |
&& frame_duration
|
|
|
b943e0 |
&& (past > 0 || future > 0) )
|
|
|
3707d8 |
{
|
|
|
b943e0 |
const Color color_past (1.f, 0.f, 0.f, 0.2f);
|
|
|
b943e0 |
const Color color_future(0.f, 1.f, 0.f, 0.2f);
|
|
|
b943e0 |
const ColorReal base_alpha = 1.f;
|
|
|
b943e0 |
const ColorReal current_alpha = 0.5f;
|
|
|
b943e0 |
// make onion levels
|
|
|
b943e0 |
for(int i = past; i > 0; --i) {
|
|
|
b943e0 |
Time time = base_time - frame_duration*i;
|
|
|
b943e0 |
ColorReal alpha = base_alpha + (ColorReal)(past - i + 1)/(ColorReal)(past + 1);
|
|
|
b943e0 |
if (time >= rend_desc.get_time_start() && time <= rend_desc.get_time_end())
|
|
|
b943e0 |
onion_frames.push_back(FrameDesc(time, w, h, alpha));
|
|
|
b943e0 |
}
|
|
|
b943e0 |
for(int i = future; i > 0; --i) {
|
|
|
b943e0 |
Time time = base_time + frame_duration*i;
|
|
|
b943e0 |
ColorReal alpha = base_alpha + (ColorReal)(future - i + 1)/(ColorReal)(future + 1);
|
|
|
b943e0 |
if (time >= rend_desc.get_time_start() && time <= rend_desc.get_time_end())
|
|
|
b943e0 |
onion_frames.push_back(FrameDesc(time, w, h, alpha));
|
|
|
e06541 |
}
|
|
|
b943e0 |
onion_frames.push_back(FrameDesc(current_frame, base_alpha + 1.f + current_alpha));
|
|
|
b943e0 |
|
|
|
b943e0 |
// normalize
|
|
|
b943e0 |
ColorReal summary = 0.f;
|
|
|
b943e0 |
for(FrameList::const_iterator i = onion_frames.begin(); i != onion_frames.end(); ++i)
|
|
|
b943e0 |
summary += i->alpha;
|
|
|
b943e0 |
ColorReal k = approximate_greater(summary, ColorReal(1.f)) ? 1.f/summary : 1.f;
|
|
|
b943e0 |
for(FrameList::iterator i = onion_frames.begin(); i != onion_frames.end(); ++i)
|
|
|
b943e0 |
i->alpha *= k;
|
|
|
b943e0 |
} else {
|
|
|
b943e0 |
onion_frames.push_back(FrameDesc(current_frame, 1.f));
|
|
|
b943e0 |
}
|
|
|
e06541 |
|
|
|
b943e0 |
// set visible_frames
|
|
|
b943e0 |
visible_frames.clear();
|
|
|
b943e0 |
for(FrameList::const_iterator i = onion_frames.begin(); i != onion_frames.end(); ++i)
|
|
|
b943e0 |
visible_frames.insert(i->id);
|
|
|
b943e0 |
}
|
|
|
e06541 |
|
|
|
b943e0 |
bool
|
|
|
d421d7 |
Renderer_Canvas::enqueue_render_frame(
|
|
|
e433dc |
const rendering::Renderer::Handle &renderer,
|
|
|
e433dc |
const Canvas::Handle &canvas,
|
|
|
e433dc |
const RectInt &window_rect,
|
|
|
d421d7 |
const FrameId &id )
|
|
|
b943e0 |
{
|
|
|
b943e0 |
// mutex must be already locked
|
|
|
e06541 |
|
|
|
b943e0 |
const int tile_grid_step = 64;
|
|
|
7c6c2d |
|
|
|
e433dc |
RendDesc rend_desc = canvas->rend_desc();
|
|
|
e433dc |
int w = id.width;
|
|
|
e433dc |
int h = id.height;
|
|
|
b943e0 |
|
|
|
b943e0 |
rend_desc.clear_flags();
|
|
|
b943e0 |
rend_desc.set_wh(w, h);
|
|
|
ebf5b9 |
rend_desc.set_render_excluded_contexts(true);
|
|
|
b943e0 |
ContextParams context_params(rend_desc.get_render_excluded_contexts());
|
|
|
b943e0 |
TileList &frame_tiles = tiles[id];
|
|
|
b943e0 |
|
|
|
b943e0 |
// create transformation matrix to flip result if needed
|
|
|
b943e0 |
bool transform = false;
|
|
|
b943e0 |
Matrix matrix;
|
|
|
b943e0 |
Vector p0 = rend_desc.get_tl();
|
|
|
b943e0 |
Vector p1 = rend_desc.get_br();
|
|
|
b943e0 |
if (p0[0] > p1[0] || p0[1] > p1[1]) {
|
|
|
b943e0 |
if (p0[0] > p1[0]) { matrix.m00 = -1.0; matrix.m20 = p0[0] + p1[0]; std::swap(p0[0], p1[0]); }
|
|
|
b943e0 |
if (p0[1] > p1[1]) { matrix.m11 = -1.0; matrix.m21 = p0[1] + p1[1]; std::swap(p0[1], p1[1]); }
|
|
|
b943e0 |
rend_desc.set_tl_br(p0, p1);
|
|
|
b943e0 |
transform = true;
|
|
|
b943e0 |
}
|
|
|
b943e0 |
|
|
|
b943e0 |
// find not actual regions
|
|
|
e433dc |
std::vector<rectint> rects;</rectint>
|
|
|
b943e0 |
rects.reserve(20);
|
|
|
b943e0 |
rects.push_back(window_rect);
|
|
|
b943e0 |
for(TileList::const_iterator j = frame_tiles.begin(); j != frame_tiles.end(); ++j)
|
|
|
b943e0 |
if (*j) etl::rects_subtract(rects, (*j)->rect);
|
|
|
e433dc |
etl::rects_merge(rects);
|
|
|
b943e0 |
|
|
|
b943e0 |
if (rects.empty()) return false;
|
|
|
b943e0 |
|
|
|
b943e0 |
// build rendering task
|
|
|
b943e0 |
canvas->set_time(id.time);
|
|
|
c006c9 |
canvas->load_resources(id.time);
|
|
|
b943e0 |
canvas->set_outline_grow(rend_desc.get_outline_grow());
|
|
|
a4bbdd |
rendering::Task::Handle task = canvas->build_rendering_task(context_params);
|
|
|
b943e0 |
|
|
|
b943e0 |
// add transformation task to flip result if needed
|
|
|
f2de3c |
if (task && transform) {
|
|
|
b943e0 |
rendering::TaskTransformationAffine::Handle t = new rendering::TaskTransformationAffine();
|
|
|
b943e0 |
t->transformation->matrix = matrix;
|
|
|
b943e0 |
t->sub_task() = task;
|
|
|
b943e0 |
task = t;
|
|
|
b943e0 |
}
|
|
|
e06541 |
|
|
|
f2de3c |
// TaskSurface assumed as valid non-trivial task by renderer
|
|
|
f2de3c |
// and TaskTransformationAffine of TaskSurface will not be optimized.
|
|
|
f2de3c |
// To avoid this construction place creation of dummy TaskSurface here.
|
|
|
f2de3c |
if (!task) task = new rendering::TaskSurface();
|
|
|
f2de3c |
|
|
|
e433dc |
for(std::vector<rectint>::iterator j = rects.begin(); j != rects.end(); ++j) {</rectint>
|
|
|
b943e0 |
// snap rect corners to tile grid
|
|
|
b943e0 |
RectInt &rect = *j;
|
|
|
b943e0 |
rect.minx = int_floor(rect.minx, tile_grid_step);
|
|
|
b943e0 |
rect.miny = int_floor(rect.miny, tile_grid_step);
|
|
|
b943e0 |
rect.maxx = int_ceil (rect.maxx, tile_grid_step);
|
|
|
b943e0 |
rect.maxy = int_ceil (rect.maxy, tile_grid_step);
|
|
|
d421d7 |
rect &= id.rect();
|
|
|
b943e0 |
|
|
|
b943e0 |
RendDesc tile_desc=rend_desc;
|
|
|
b943e0 |
tile_desc.set_subwindow(rect.minx, rect.miny, rect.get_width(), rect.get_height());
|
|
|
b943e0 |
|
|
|
b943e0 |
rendering::Task::Handle tile_task = task->clone_recursive();
|
|
|
b943e0 |
tile_task->target_surface = new rendering::SurfaceResource();
|
|
|
b943e0 |
tile_task->target_surface->create(tile_desc.get_w(), tile_desc.get_h());
|
|
|
b943e0 |
tile_task->target_rect = RectInt( VectorInt(), tile_task->target_surface->get_size() );
|
|
|
b943e0 |
tile_task->source_rect = Rect(tile_desc.get_tl(), tile_desc.get_br());
|
|
|
b943e0 |
|
|
|
b943e0 |
Tile::Handle tile = new Tile(id, *j);
|
|
|
b943e0 |
tile->surface = tile_task->target_surface;
|
|
|
b943e0 |
|
|
|
b943e0 |
tile->event = new rendering::TaskEvent();
|
|
|
b943e0 |
tile->event->signal_finished.connect( sigc::bind(
|
|
|
b943e0 |
sigc::ptr_fun(&on_tile_finished_callback), this, tile ));
|
|
|
b943e0 |
|
|
|
b943e0 |
insert_tile(frame_tiles, tile);
|
|
|
b943e0 |
|
|
|
dec360 |
++enqueued_tasks;
|
|
|
dec360 |
|
|
|
b943e0 |
// Renderer::enqueue contains the expensive 'optimization' stage, so call it async
|
|
|
b943e0 |
ThreadPool::instance.enqueue( sigc::bind(
|
|
|
b943e0 |
sigc::ptr_fun(&rendering::Renderer::enqueue_task_func),
|
|
|
b943e0 |
renderer, tile_task, tile->event, false ));
|
|
|
b943e0 |
}
|
|
|
e06541 |
|
|
|
b943e0 |
return true;
|
|
|
b943e0 |
}
|
|
Carlos Lopez |
a09598 |
|
|
|
b943e0 |
void
|
|
|
b943e0 |
Renderer_Canvas::enqueue_render()
|
|
|
b943e0 |
{
|
|
|
b943e0 |
assert(get_work_area());
|
|
|
3707d8 |
|
|
|
b943e0 |
rendering::Task::List events;
|
|
|
e06541 |
|
|
|
b943e0 |
{
|
|
|
b943e0 |
Glib::Threads::Mutex::Lock lock(mutex);
|
|
|
e06541 |
|
|
|
b943e0 |
String renderer_name = get_work_area()->get_renderer();
|
|
|
b943e0 |
RectInt window_rect = get_work_area()->get_window_rect();
|
|
|
7f1d91 |
bool bg_rendering = get_work_area()->get_background_rendering();
|
|
|
b943e0 |
Canvas::Handle canvas = get_work_area()->get_canvas();
|
|
|
73b5aa |
etl::handle<canvasview> canvas_view = get_work_area()->get_canvas_view();</canvasview>
|
|
|
73b5aa |
etl::handle<timemodel> time_model = canvas_view->time_model();</timemodel>
|
|
|
73b5aa |
bool is_playing = canvas_view->is_playing();
|
|
|
e06541 |
|
|
|
b943e0 |
build_onion_frames();
|
|
|
e06541 |
|
|
|
b943e0 |
rendering::Renderer::Handle renderer = rendering::Renderer::get_renderer(renderer_name);
|
|
|
7d6d4e |
|
|
|
7d6d4e |
int max_tasks = max_enqueued_tasks;
|
|
|
7d6d4e |
if (is_playing)
|
|
|
7d6d4e |
max_tasks = 2;
|
|
|
7d6d4e |
|
|
|
7d6d4e |
if (renderer && enqueued_tasks < max_tasks) {
|
|
|
b943e0 |
if (canvas && window_rect.is_valid()) {
|
|
|
b943e0 |
Time orig_time = canvas->get_time();
|
|
|
dec360 |
int enqueued = 0;
|
|
|
d421d7 |
|
|
|
d421d7 |
// generate rendering task for thumbnail
|
|
|
d421d7 |
// do it first to be sure that thmubnails will always fully covered by the single tile
|
|
|
d421d7 |
if (enqueue_render_frame(renderer, canvas, current_thumb.rect(), current_thumb))
|
|
|
d421d7 |
++enqueued;
|
|
|
d421d7 |
|
|
|
d421d7 |
// generate rendering tasks for visible areas
|
|
|
b943e0 |
for(FrameList::const_iterator i = onion_frames.begin(); i != onion_frames.end(); ++i)
|
|
|
d421d7 |
if (enqueue_render_frame(renderer, canvas, window_rect, i->id))
|
|
|
f2de3c |
++enqueued;
|
|
|
b943e0 |
|
|
|
b943e0 |
remove_extra_tiles(events);
|
|
|
b943e0 |
|
|
|
f2de3c |
// generate rendering tasks for future or past frames
|
|
|
55da53 |
// render only one frame in background
|
|
|
f2de3c |
int future = 0, past = 0;
|
|
|
f2de3c |
long long frame_size = image_rect_size(window_rect);
|
|
|
8377b5 |
bool time_in_repeat_range = time_model->get_time() >= time_model->get_play_bounds_lower()
|
|
|
8377b5 |
&& time_model->get_time() <= time_model->get_play_bounds_upper();
|
|
|
7d6d4e |
|
|
|
7d6d4e |
while(bg_rendering && enqueued_tasks < max_tasks && tiles_size + frame_size < max_tiles_size_soft)
|
|
|
7f1d91 |
{
|
|
|
f2de3c |
Time future_time = current_frame.time + frame_duration*future;
|
|
|
8377b5 |
bool future_exists = future_time >= time_model->get_lower()
|
|
|
8377b5 |
&& future_time <= time_model->get_upper();
|
|
|
8377b5 |
Real weight_future_current = !time_in_repeat_range
|
|
|
8377b5 |
|| ( future_time >= time_model->get_play_bounds_lower()
|
|
|
8377b5 |
&& future_time <= time_model->get_play_bounds_upper() )
|
|
|
8377b5 |
? weight_future : weight_future_extra;
|
|
|
8377b5 |
|
|
|
f2de3c |
Time past_time = current_frame.time - frame_duration*past;
|
|
|
7d6d4e |
bool past_exists = false;
|
|
|
7d6d4e |
if (!is_playing)
|
|
|
7d6d4e |
past_exists = past_time >= time_model->get_lower()
|
|
|
8377b5 |
&& past_time <= time_model->get_upper();
|
|
|
8377b5 |
Real weight_past_current = !time_in_repeat_range
|
|
|
8377b5 |
|| ( past_time >= time_model->get_play_bounds_lower()
|
|
|
8377b5 |
&& past_time <= time_model->get_play_bounds_upper() )
|
|
|
8377b5 |
? weight_past : weight_past_extra;
|
|
|
8377b5 |
|
|
|
f2de3c |
if (!future_exists && !past_exists) break;
|
|
|
8377b5 |
|
|
|
8377b5 |
bool future_priority = weight_future_current*future < weight_past_current*past;
|
|
|
f2de3c |
|
|
|
55da53 |
if (future_exists && (!past_exists || future_priority)) {
|
|
|
f2de3c |
// queue future
|
|
|
d8ad70 |
if (enqueue_render_frame(renderer, canvas, current_thumb.rect(), current_thumb.with_time(future_time)))
|
|
|
d8ad70 |
++enqueued;
|
|
|
d421d7 |
if (enqueue_render_frame(renderer, canvas, window_rect, current_frame.with_time(future_time)))
|
|
|
f2de3c |
++enqueued;
|
|
|
f2de3c |
++future;
|
|
|
f2de3c |
} else {
|
|
|
f2de3c |
// queue past
|
|
|
d8ad70 |
if (enqueue_render_frame(renderer, canvas, current_thumb.rect(), current_thumb.with_time(past_time)))
|
|
|
d8ad70 |
++enqueued;
|
|
|
d421d7 |
if (enqueue_render_frame(renderer, canvas, window_rect, current_frame.with_time(past_time)))
|
|
|
f2de3c |
++enqueued;
|
|
|
f2de3c |
++past;
|
|
|
e06541 |
}
|
|
|
e06541 |
}
|
|
|
b943e0 |
|
|
|
d421d7 |
// restore canvas time
|
|
|
73b5aa |
if (!is_playing)
|
|
|
73b5aa |
canvas->set_time(orig_time);
|
|
|
d421d7 |
|
|
|
dec360 |
if (enqueued)
|
|
|
55da53 |
get_work_area()->signal_rendering()();
|
|
|
e06541 |
}
|
|
|
e06541 |
}
|
|
|
3707d8 |
}
|
|
|
3707d8 |
|
|
|
b943e0 |
rendering::Renderer::cancel(events);
|
|
Carlos Lopez |
a09598 |
}
|
|
Carlos Lopez |
a09598 |
|
|
|
3707d8 |
void
|
|
|
3707d8 |
Renderer_Canvas::wait_render()
|
|
Carlos Lopez |
a09598 |
{
|
|
|
b943e0 |
rendering::TaskEvent::List events;
|
|
|
b943e0 |
{
|
|
|
b943e0 |
Glib::Threads::Mutex::Lock lock(mutex);
|
|
|
b943e0 |
for(FrameList::const_iterator i = onion_frames.begin(); i != onion_frames.end(); ++i) {
|
|
|
b943e0 |
TileMap::const_iterator ii = tiles.find(i->id);
|
|
|
b943e0 |
if (ii != tiles.end())
|
|
|
b943e0 |
for(TileList::const_iterator j = ii->second.begin(); j != ii->second.end(); ++j)
|
|
|
b943e0 |
if (*j && (*j)->event)
|
|
|
b943e0 |
events.push_back((*j)->event);
|
|
|
3707d8 |
}
|
|
|
3707d8 |
}
|
|
|
b943e0 |
for(rendering::TaskEvent::List::const_iterator i = events.begin(); i != events.end(); ++i)
|
|
|
b943e0 |
(*i)->wait();
|
|
Carlos Lopez |
a09598 |
}
|
|
Carlos Lopez |
a09598 |
|
|
|
b943e0 |
void
|
|
|
b943e0 |
Renderer_Canvas::clear_render()
|
|
|
e6766e |
{
|
|
|
b943e0 |
rendering::Task::List events;
|
|
|
d421d7 |
bool cleared = false;
|
|
|
b943e0 |
{
|
|
|
b943e0 |
Glib::Threads::Mutex::Lock lock(mutex);
|
|
|
d421d7 |
cleared = !tiles.empty();
|
|
|
b943e0 |
for(TileMap::iterator i = tiles.begin(); i != tiles.end(); ++i)
|
|
|
b943e0 |
while(!i->second.empty()) {
|
|
|
b943e0 |
TileList::iterator j = i->second.end(); --j;
|
|
|
b943e0 |
erase_tile(i->second, j++, events);
|
|
|
b943e0 |
}
|
|
|
b943e0 |
tiles.clear();
|
|
|
b943e0 |
}
|
|
|
b943e0 |
rendering::Renderer::cancel(events);
|
|
|
d421d7 |
if (cleared && get_work_area())
|
|
|
d421d7 |
get_work_area()->signal_rendering()();
|
|
|
b943e0 |
}
|
|
|
65aa32 |
|
|
|
b943e0 |
Renderer_Canvas::FrameStatus
|
|
|
d421d7 |
Renderer_Canvas::merge_status(FrameStatus a, FrameStatus b) {
|
|
|
d421d7 |
static FrameStatus map[FS_Count][FS_Count] = {
|
|
|
d421d7 |
// FS_None | FS_PartiallyDone | FS_InProcess | FS_Done //
|
|
|
d421d7 |
// -----------------|------------------|------------------|----------------- //
|
|
|
d421d7 |
{ FS_None , FS_PartiallyDone , FS_InProcess , FS_PartiallyDone }, // FS_None
|
|
|
d421d7 |
{ FS_PartiallyDone , FS_PartiallyDone , FS_InProcess , FS_PartiallyDone }, // FS_PartiallyDone
|
|
|
d421d7 |
{ FS_InProcess , FS_InProcess , FS_InProcess , FS_InProcess }, // FS_InProcess
|
|
|
d421d7 |
{ FS_PartiallyDone , FS_PartiallyDone , FS_InProcess , FS_Done }}; // FS_Done
|
|
|
d421d7 |
|
|
|
d421d7 |
if ((int)a < 0 || (int)a > (int)FS_Count) a = FS_None;
|
|
|
d421d7 |
if ((int)b < 0 || (int)b > (int)FS_Count) b = FS_None;
|
|
|
d421d7 |
return map[a][b];
|
|
|
d421d7 |
}
|
|
|
d421d7 |
|
|
|
d421d7 |
|
|
|
d421d7 |
Renderer_Canvas::FrameStatus
|
|
|
e433dc |
Renderer_Canvas::calc_frame_status(const FrameId &id, const RectInt &window_rect)
|
|
|
b943e0 |
{
|
|
|
b943e0 |
// mutex must be already locked
|
|
|
e6766e |
|
|
|
b943e0 |
TileMap::const_iterator i = tiles.find(id);
|
|
|
b943e0 |
if (i == tiles.end() || i->second.empty())
|
|
|
b943e0 |
return FS_None;
|
|
|
b943e0 |
|
|
|
e433dc |
std::vector<rectint> rects;</rectint>
|
|
|
b943e0 |
rects.reserve(20);
|
|
|
b943e0 |
rects.push_back(window_rect);
|
|
|
b943e0 |
for(TileList::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
|
|
|
b943e0 |
if (*j) {
|
|
|
b943e0 |
if ((*j)->event)
|
|
|
b943e0 |
return FS_InProcess;
|
|
|
b943e0 |
if ((*j)->cairo_surface)
|
|
|
b943e0 |
etl::rects_subtract(rects, (*j)->rect);
|
|
|
b943e0 |
}
|
|
|
e433dc |
etl::rects_merge(rects);
|
|
|
e6766e |
|
|
|
b943e0 |
if (rects.size() == 1 && rects.front() == window_rect)
|
|
|
b943e0 |
return FS_None;
|
|
|
b943e0 |
if (rects.empty())
|
|
|
b943e0 |
return FS_Done;
|
|
|
b943e0 |
return FS_PartiallyDone;
|
|
|
b943e0 |
}
|
|
|
017782 |
|
|
|
b943e0 |
void
|
|
|
b943e0 |
Renderer_Canvas::get_render_status(StatusMap &out_map)
|
|
|
b943e0 |
{
|
|
|
b943e0 |
Glib::Threads::Mutex::Lock lock(mutex);
|
|
|
65aa32 |
|
|
|
d421d7 |
RectInt window_rect = get_work_area()->get_window_rect();
|
|
|
65aa32 |
|
|
|
b943e0 |
out_map.clear();
|
|
|
d421d7 |
for(TileMap::const_iterator i = tiles.begin(); i != tiles.end(); ++i)
|
|
|
d421d7 |
if ( !i->second.empty()
|
|
|
d421d7 |
&& ( (i->first.width == current_thumb.width && i->first.height == current_thumb.height)
|
|
|
d421d7 |
|| (i->first.width == current_frame.width && i->first.height == current_frame.height) ))
|
|
|
d421d7 |
out_map[i->first.time] = FS_None;
|
|
|
d421d7 |
|
|
|
d421d7 |
for(StatusMap::iterator i = out_map.begin(); i != out_map.end(); ) {
|
|
|
d421d7 |
i->second = merge_status(
|
|
|
d421d7 |
calc_frame_status(current_frame.with_time(i->first), window_rect),
|
|
|
d421d7 |
calc_frame_status(current_thumb.with_time(i->first), current_thumb.rect()) );
|
|
|
d421d7 |
if (i->second == FS_None) out_map.erase(i++); else ++i;
|
|
|
e6766e |
}
|
|
|
e6766e |
}
|
|
|
e6766e |
|
|
Carlos Lopez |
a09598 |
void
|
|
Carlos Lopez |
a09598 |
Renderer_Canvas::render_vfunc(
|
|
|
60fa94 |
const Glib::RefPtr<gdk::window>& drawable,</gdk::window>
|
|
|
e06541 |
const Gdk::Rectangle& expose_area )
|
|
Carlos Lopez |
a09598 |
{
|
|
|
e06541 |
VectorInt window_offset = get_work_area()->get_windows_offset();
|
|
|
e06541 |
RectInt window_rect = get_work_area()->get_window_rect();
|
|
|
e06541 |
RectInt expose_rect = RectInt( expose_area.get_x(),
|
|
|
e06541 |
expose_area.get_y(),
|
|
|
e06541 |
expose_area.get_x() + expose_area.get_width(),
|
|
|
e06541 |
expose_area.get_y() + expose_area.get_height() );
|
|
|
3bfe1f |
expose_rect -= window_offset;
|
|
|
e06541 |
expose_rect &= window_rect;
|
|
|
e06541 |
if (!expose_rect.is_valid()) return;
|
|
Carlos Lopez |
a09598 |
|
|
|
e433dc |
// calculate world coordinates of expose_rect corners
|
|
|
e433dc |
if (!get_work_area()->get_canvas()) return;
|
|
|
e433dc |
const RendDesc &rend_desc = get_work_area()->get_canvas()->rend_desc();
|
|
|
e433dc |
int w = get_work_area()->get_w();
|
|
|
e433dc |
int h = get_work_area()->get_h();
|
|
|
e433dc |
Vector tl = rend_desc.get_tl();
|
|
|
e433dc |
Vector br = rend_desc.get_br();
|
|
|
e433dc |
if ( w <= 0 || h <= 0
|
|
|
e433dc |
|| approximate_equal(tl[0], br[0])
|
|
|
e433dc |
|| approximate_equal(tl[1], br[1]) ) return;
|
|
|
e433dc |
|
|
|
e433dc |
Real pw = (br[0] - tl[0])/(Real)w;
|
|
|
e433dc |
Real ph = (br[1] - tl[1])/(Real)h;
|
|
|
e433dc |
tl[0] += pw*(double)expose_rect.minx;
|
|
|
e433dc |
tl[1] += ph*(double)expose_rect.miny;
|
|
|
e433dc |
br[0] += pw*(double)(expose_rect.maxx - w);
|
|
|
e433dc |
br[1] += ph*(double)(expose_rect.maxy - h);
|
|
|
e433dc |
|
|
|
e433dc |
// calculate pixel coordinates of previous surface
|
|
|
e433dc |
RectInt previous_rect;
|
|
|
e433dc |
if ( previous_surface
|
|
|
e433dc |
&& previous_surface->get_width() > 0
|
|
|
e433dc |
&& previous_surface->get_height() > 0
|
|
|
e433dc |
&& approximate_not_equal(previous_tl[0], previous_br[0])
|
|
|
e433dc |
&& approximate_not_equal(previous_tl[1], previous_br[1]) )
|
|
|
e433dc |
{
|
|
|
e433dc |
previous_rect.minx = (int)round((previous_tl[0] - tl[0])/pw);
|
|
|
e433dc |
previous_rect.miny = (int)round((previous_tl[1] - tl[1])/ph);
|
|
|
e433dc |
previous_rect.maxx = (int)round((previous_br[0] - tl[0])/pw);
|
|
|
e433dc |
previous_rect.maxy = (int)round((previous_br[1] - tl[1])/ph);
|
|
|
e433dc |
previous_rect = expose_rect;
|
|
|
e433dc |
}
|
|
|
e433dc |
|
|
|
e06541 |
// enqueue rendering if not all of visible tiles are exists and actual
|
|
|
e06541 |
enqueue_render();
|
|
Carlos Lopez |
a09598 |
|
|
|
e433dc |
Cairo::RefPtr<cairo::context> canvas_context;</cairo::context>
|
|
|
e433dc |
Cairo::RefPtr<cairo::imagesurface> canvas_surface;</cairo::imagesurface>
|
|
|
e433dc |
std::vector<rectint> empty_rects;</rectint>
|
|
|
e433dc |
empty_rects.reserve(20);
|
|
|
e433dc |
empty_rects.push_back(previous_rect);
|
|
|
9f38ba |
|
|
|
e433dc |
{ // merge all tiles into single surface
|
|
|
e433dc |
Glib::Threads::Mutex::Lock lock(mutex);
|
|
|
e06541 |
|
|
|
e433dc |
if (onion_frames.empty()) return;
|
|
|
e06541 |
|
|
|
e433dc |
// create surface and context to merge tiles
|
|
|
e433dc |
canvas_surface = Cairo::ImageSurface::create(
|
|
|
017782 |
Cairo::FORMAT_ARGB32, expose_rect.get_width(), expose_rect.get_height() );
|
|
|
e433dc |
canvas_context = Cairo::Context::create(canvas_surface);
|
|
|
e433dc |
canvas_context->translate(-(double)expose_rect.minx, -(double)expose_rect.miny);
|
|
|
e433dc |
canvas_context->set_operator(Cairo::OPERATOR_SOURCE);
|
|
|
e433dc |
|
|
|
e433dc |
if ( onion_frames.size() > 1
|
|
|
e433dc |
|| !approximate_equal_lp(onion_frames.front().alpha, ColorReal(1.f)) )
|
|
|
e433dc |
{
|
|
|
e433dc |
canvas_context->set_operator(Cairo::OPERATOR_ADD);
|
|
|
e433dc |
|
|
|
e433dc |
// prepare background to tune alpha
|
|
|
e433dc |
alpha_context->set_operator(canvas_context->get_operator());
|
|
|
e433dc |
alpha_context->set_source(alpha_src_surface, 0, 0);
|
|
|
e433dc |
int alpha_offset = FLAGS(pixel_format, PF_A_START) ? 0 : 3;
|
|
|
e433dc |
unsigned char base[] = {0, 0, 0, 0};
|
|
|
cb8070 |
memcpy(alpha_dst_surface->get_data(), base, sizeof(base));
|
|
|
cb8070 |
alpha_dst_surface->mark_dirty();
|
|
|
cb8070 |
alpha_dst_surface->flush();
|
|
|
e433dc |
for(FrameList::const_iterator j = onion_frames.begin(), i = j++; j != onion_frames.end(); i = j++)
|
|
|
e433dc |
alpha_context->paint_with_alpha(i->alpha);
|
|
|
e433dc |
alpha_dst_surface->flush();
|
|
|
e433dc |
memcpy(base, alpha_dst_surface->get_data(), sizeof(base));
|
|
|
e433dc |
|
|
|
e433dc |
// tune alpha
|
|
|
e433dc |
while(true) {
|
|
|
e433dc |
memcpy(alpha_dst_surface->get_data(), base, sizeof(base));
|
|
|
e433dc |
alpha_dst_surface->mark_dirty();
|
|
|
e433dc |
alpha_dst_surface->flush();
|
|
|
e433dc |
alpha_context->paint_with_alpha(onion_frames.back().alpha);
|
|
|
e433dc |
int alpha = alpha_dst_surface->get_data()[alpha_offset];
|
|
|
e433dc |
if (alpha >= 255) break;
|
|
|
e433dc |
onion_frames.back().alpha += (ColorReal)(255 - alpha)/ColorReal(128.f);
|
|
|
e433dc |
}
|
|
|
cb8070 |
}
|
|
|
e06541 |
|
|
|
e433dc |
// draw tiles
|
|
|
e433dc |
canvas_context->save();
|
|
|
e433dc |
for(FrameList::const_iterator i = onion_frames.begin(); i != onion_frames.end(); ++i) {
|
|
|
e433dc |
TileMap::const_iterator ii = tiles.find(i->id);
|
|
|
e433dc |
if (ii == tiles.end()) continue;
|
|
|
e433dc |
for(TileList::const_iterator j = ii->second.begin(); j != ii->second.end(); ++j) {
|
|
|
e433dc |
if (!*j) continue;
|
|
|
e433dc |
if ((*j)->cairo_surface) {
|
|
|
e433dc |
etl::rects_subtract(empty_rects, (*j)->rect); // mark area as not empty
|
|
|
e433dc |
canvas_context->save();
|
|
|
e433dc |
canvas_context->rectangle((*j)->rect.minx, (*j)->rect.miny, (*j)->rect.get_width(), (*j)->rect.get_height());
|
|
|
e433dc |
canvas_context->clip();
|
|
|
e433dc |
canvas_context->set_source((*j)->cairo_surface, (*j)->rect.minx, (*j)->rect.miny);
|
|
|
e433dc |
if (canvas_surface)
|
|
|
e433dc |
canvas_context->paint_with_alpha(i->alpha);
|
|
|
e433dc |
else
|
|
|
e433dc |
canvas_context->paint();
|
|
|
e433dc |
canvas_context->restore();
|
|
|
e433dc |
}
|
|
Carlos Lopez |
a09598 |
}
|
|
Carlos Lopez |
a09598 |
}
|
|
|
e433dc |
canvas_context->restore();
|
|
|
e433dc |
canvas_surface->flush();
|
|
|
9f38ba |
}
|
|
|
e433dc |
|
|
|
e433dc |
// fill empty areas with previous surface
|
|
|
e433dc |
etl::rects_merge(empty_rects);
|
|
|
e433dc |
if (!empty_rects.empty()) {
|
|
|
e433dc |
canvas_context->save();
|
|
|
e433dc |
|
|
|
e433dc |
for(std::vector<rectint>::const_iterator i = empty_rects.begin(); i != empty_rects.end(); ++i)</rectint>
|
|
|
e433dc |
canvas_context->rectangle(i->minx, i->miny, i->get_width(), i->get_height());
|
|
|
e433dc |
canvas_context->clip();
|
|
|
e433dc |
|
|
|
e433dc |
Real previous_pw = (previous_br[0] - previous_tl[0])/(Real)previous_surface->get_width();
|
|
|
e433dc |
Real previous_ph = (previous_br[1] - previous_tl[1])/(Real)previous_surface->get_height();
|
|
|
e433dc |
canvas_context->translate(
|
|
|
e433dc |
(previous_tl[0] - rend_desc.get_tl()[0])/pw,
|
|
|
e433dc |
(previous_tl[1] - rend_desc.get_tl()[1])/ph );
|
|
|
e433dc |
canvas_context->scale(
|
|
|
e433dc |
previous_pw/pw,
|
|
|
e433dc |
previous_ph/ph );
|
|
|
e433dc |
canvas_context->set_operator(Cairo::OPERATOR_SOURCE);
|
|
|
e433dc |
canvas_context->set_source(previous_surface, 0.0, 0.0);
|
|
|
e433dc |
canvas_context->paint();
|
|
|
e433dc |
|
|
|
e433dc |
canvas_context->restore();
|
|
|
e433dc |
canvas_surface->flush();
|
|
Carlos Lopez |
a09598 |
}
|
|
|
e06541 |
|
|
|
e433dc |
// remember surface with merged tiles for the future
|
|
|
e433dc |
previous_tl = tl;
|
|
|
e433dc |
previous_br = br;
|
|
|
e433dc |
previous_surface = canvas_surface;
|
|
|
e433dc |
|
|
|
e433dc |
Cairo::RefPtr<cairo::context> context = drawable->create_cairo_context();</cairo::context>
|
|
|
e433dc |
context->save();
|
|
|
e433dc |
context->translate((double)window_offset[0], (double)window_offset[1]);
|
|
|
e433dc |
|
|
|
e433dc |
// put merged tiles to context
|
|
|
e433dc |
context->save();
|
|
|
e433dc |
context->rectangle(expose_rect.minx, expose_rect.miny, expose_rect.get_width(), expose_rect.get_height());
|
|
|
e433dc |
context->clip();
|
|
|
e433dc |
context->set_source(canvas_surface, (double)expose_rect.minx, (double)expose_rect.miny);
|
|
|
e433dc |
context->paint();
|
|
|
e433dc |
context->restore();
|
|
|
e433dc |
|
|
|
e06541 |
// draw the border around the rendered region
|
|
|
e06541 |
context->save();
|
|
|
e06541 |
context->set_line_cap(Cairo::LINE_CAP_BUTT);
|
|
|
e06541 |
context->set_line_join(Cairo::LINE_JOIN_MITER);
|
|
|
e06541 |
context->set_antialias(Cairo::ANTIALIAS_NONE);
|
|
|
e06541 |
context->set_line_width(1.0);
|
|
|
e6766e |
context->set_source_rgba(0.0, 0.0, 0.0, 1.0);
|
|
|
b943e0 |
context->rectangle(0.0, 0.0, (double)current_frame.width, (double)current_frame.height);
|
|
|
e06541 |
context->stroke();
|
|
|
e06541 |
context->restore();
|
|
|
65aa32 |
|
|
|
65aa32 |
context->restore();
|
|
Carlos Lopez |
a09598 |
}
|
|
|
d421d7 |
|
|
|
d421d7 |
Cairo::RefPtr<cairo::imagesurface></cairo::imagesurface>
|
|
|
e433dc |
Renderer_Canvas::get_thumb(const Time &time)
|
|
|
d421d7 |
{
|
|
|
d421d7 |
Glib::Threads::Mutex::Lock lock(mutex);
|
|
|
d421d7 |
TileMap::const_iterator i = tiles.find( current_thumb.with_time(time) );
|
|
|
d421d7 |
return i == tiles.end() || i->second.empty() || !*(i->second.begin())
|
|
|
d421d7 |
? Cairo::RefPtr<cairo::imagesurface>()</cairo::imagesurface>
|
|
|
d421d7 |
: (*(i->second.begin()))->cairo_surface;
|
|
|
d421d7 |
}
|