} \r
\r
if(codec_context_->codec_id == CODEC_ID_DVVIDEO && frame_factory_->get_video_format_desc().mode == video_mode::upper)\r
- write->get_image_transform().set_image_translation(0.0f, 1.0/static_cast<double>(height_));\r
+ write->get_image_transform().set_fill_translation(0.0f, 1.0/static_cast<double>(height_));\r
\r
return write;\r
}\r
return make_safe<image_producer>(filename + L"." + *ext);\r
}\r
\r
-std::wstring get_image_version()\r
+std::wstring get_fill_version()\r
{\r
return widen(std::string(FreeImage_GetVersion()));\r
}\r
\r
safe_ptr<frame_producer> create_image_producer(const std::vector<std::wstring>& params);\r
\r
-std::wstring get_image_version();\r
+std::wstring get_fill_version();\r
\r
}}}
\ No newline at end of file
}\r
else if(info_.type == transition::slide)\r
{\r
- d_frame1->get_image_transform().set_image_translation((-1.0+alpha-half_alpha_step)*dir, 0.0); \r
- d_frame2->get_image_transform().set_image_translation((-1.0+alpha)*dir, 0.0); \r
+ d_frame1->get_image_transform().set_fill_translation((-1.0+alpha-half_alpha_step)*dir, 0.0); \r
+ d_frame2->get_image_transform().set_fill_translation((-1.0+alpha)*dir, 0.0); \r
}\r
else if(info_.type == transition::push)\r
{\r
- d_frame1->get_image_transform().set_image_translation((-1.0+alpha-half_alpha_step)*dir, 0.0);\r
- d_frame2->get_image_transform().set_image_translation((-1.0+alpha)*dir, 0.0);\r
+ d_frame1->get_image_transform().set_fill_translation((-1.0+alpha-half_alpha_step)*dir, 0.0);\r
+ d_frame2->get_image_transform().set_fill_translation((-1.0+alpha)*dir, 0.0);\r
\r
- s_frame1->get_image_transform().set_image_translation((0.0+alpha-half_alpha_step)*dir, 0.0); \r
- s_frame2->get_image_transform().set_image_translation((0.0+alpha)*dir, 0.0); \r
+ s_frame1->get_image_transform().set_fill_translation((0.0+alpha-half_alpha_step)*dir, 0.0); \r
+ s_frame2->get_image_transform().set_fill_translation((0.0+alpha)*dir, 0.0); \r
}\r
else if(info_.type == transition::wipe) \r
{\r
- d_frame1->get_image_transform().set_mask_scale(alpha-half_alpha_step, 1.0); \r
- d_frame2->get_image_transform().set_mask_scale(alpha, 1.0); \r
+ d_frame1->get_image_transform().set_key_scale(alpha-half_alpha_step, 1.0); \r
+ d_frame2->get_image_transform().set_key_scale(alpha, 1.0); \r
}\r
\r
return draw_frame(draw_frame::interlace(s_frame1, s_frame2, format_desc_.mode), draw_frame::interlace(d_frame1, d_frame2, format_desc_.mode));\r
\r
void draw_frame::set_layer_index(int index) { impl_->index_ = index; }\r
int draw_frame::get_layer_index() const { return impl_->index_; }\r
-\r
+std::vector<safe_ptr<draw_frame>> draw_frame::get_children(){return impl_->frames_;}\r
+ \r
}}
\ No newline at end of file
\r
void set_layer_index(int index);\r
int get_layer_index() const;\r
- \r
+\r
+ std::vector<safe_ptr<draw_frame>> get_children();\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
template<typename T>\r
struct animated_value\r
{\r
- virtual T fetch() = 0;\r
- virtual T fetch_and_tick(bool& done) = 0;\r
- T fetch_and_tick()\r
- {\r
- bool dummy;\r
- return fetch_and_tick(dummy);\r
- }\r
+ virtual T fetch() const = 0;\r
+ virtual T fetch_and_tick(int num_tick = 1) = 0;\r
+ virtual bool is_finished() const = 0;\r
\r
virtual safe_ptr<animated_value<T>> source() = 0;\r
virtual safe_ptr<animated_value<T>> dest() = 0;\r
basic_animated_value(){}\r
basic_animated_value(const T& current) : current_(current){}\r
\r
- virtual T fetch(){return current_;}\r
- virtual T fetch_and_tick(bool& done)\r
- {\r
- done = false;\r
- return current_;\r
- }\r
+ virtual T fetch() const { return current_;}\r
+ virtual T fetch_and_tick(int num_tick = 1){return current_;}\r
+ virtual bool is_finished() const {return false;}\r
\r
virtual safe_ptr<animated_value<T>> source() {return make_safe<basic_animated_value<T>>(current_);}\r
virtual safe_ptr<animated_value<T>> dest() {return make_safe<basic_animated_value<T>>(current_);}\r
, duration_(duration)\r
, time_(0){}\r
\r
- virtual T fetch()\r
+ virtual T fetch() const\r
{\r
return lerp(source_->fetch(), dest_->fetch(), duration_ < 1 ? 1.0f : static_cast<float>(time_)/static_cast<float>(duration_));\r
}\r
- virtual T fetch_and_tick(bool& done)\r
+\r
+ virtual T fetch_and_tick(int num_tick = 1)\r
{\r
- done = time_ >= duration_;\r
+ auto result = lerp(source_->fetch_and_tick(), dest_->fetch_and_tick(), duration_ < 1 ? 1.0f : (static_cast<float>(time_)/static_cast<float>(duration_)));\r
\r
- bool src_done = false;\r
- auto src = source_->fetch_and_tick(src_done);\r
- if(src_done)\r
+ if(source_->is_finished())\r
source_ = source_->dest();\r
- \r
- bool dst_done = false;\r
- auto dst = dest_->fetch_and_tick(dst_done);\r
- if(dst_done)\r
+\r
+ if(dest_->is_finished())\r
dest_ = dest_->dest();\r
\r
- time_ = std::min(time_+1, duration_);\r
- return lerp(src, dst, duration_ < 1 ? 1.0f : (static_cast<float>(time_)/static_cast<float>(duration_)));\r
+ time_ = std::min(time_+num_tick, duration_);\r
+\r
+ return result;\r
}\r
\r
+ virtual bool is_finished() const\r
+ {\r
+ return time_ >= duration_;\r
+ }\r
+ \r
virtual safe_ptr<animated_value<T>> source() {return source_;}\r
virtual safe_ptr<animated_value<T>> dest() {return dest_;}\r
};\r
\r
output_func output_;\r
\r
- std::unordered_map<int, image_transform> image_transforms_;\r
- std::unordered_map<int, audio_transform> audio_transforms_;\r
+ std::unordered_map<int, std::shared_ptr<animated_value<image_transform>>> image_transforms_;\r
+ std::unordered_map<int, std::shared_ptr<animated_value<audio_transform>>> audio_transforms_;\r
\r
- image_transform root_image_transform_;\r
+ safe_ptr<animated_value<image_transform>> root_image_transform_;\r
safe_ptr<animated_value<audio_transform>> root_audio_transform_;\r
\r
executor executor_;\r
, image_mixer_(format_desc)\r
, output_(output)\r
, executor_(print())\r
+ , root_image_transform_(basic_animated_value<image_transform>())\r
, root_audio_transform_(basic_animated_value<audio_transform>())\r
{\r
graph_->guide("frame-time", 0.5f); \r
auto image = image_mixer_.begin_pass();\r
BOOST_FOREACH(auto& frame, frames)\r
{\r
- image_mixer_.begin(root_image_transform_*image_transforms_[frame->get_layer_index()]);\r
+ image_mixer_.begin(fetch_and_tick(*root_image_transform_)*fetch_and_tick(*image_transforms_[frame->get_layer_index()]));\r
frame->process_image(image_mixer_);\r
image_mixer_.end();\r
}\r
auto audio = audio_mixer_.begin_pass();\r
BOOST_FOREACH(auto& frame, frames)\r
{\r
- audio_mixer_.begin(root_audio_transform_->fetch_and_tick()*audio_transforms_[frame->get_layer_index()]);\r
+ audio_mixer_.begin(fetch_and_tick(*root_audio_transform_)*fetch_and_tick(*audio_transforms_[frame->get_layer_index()]));\r
frame->process_audio(audio_mixer_);\r
audio_mixer_.end();\r
}\r
});\r
graph_->set("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
}\r
- \r
+\r
+ audio_transform fetch_and_tick(animated_value<audio_transform>& transform)\r
+ {\r
+ int num_tick = format_desc_.mode == video_mode::progressive ? 1 : 2;\r
+ return transform.fetch_and_tick(num_tick);\r
+ }\r
+ \r
+ image_transform fetch_and_tick(animated_value<image_transform>& transform)\r
+ {\r
+ int num_tick = format_desc_.mode == video_mode::progressive ? 1 : 2;\r
+ return transform.fetch_and_tick();\r
+ }\r
+\r
safe_ptr<write_frame> create_frame(const pixel_format_desc& desc)\r
{\r
return make_safe<write_frame>(desc, image_mixer_.create_buffers(desc));\r
\r
image_transform get_image_transform(int index)\r
{\r
- return executor_.invoke([&]{return image_transforms_[index];});\r
+ return executor_.invoke([&]{return image_transforms_[index]->fetch();});\r
}\r
\r
audio_transform get_audio_transform(int index)\r
{\r
- return executor_.invoke([&]{return audio_transforms_[index];});\r
+ return executor_.invoke([&]{return audio_transforms_[index]->fetch();});\r
}\r
\r
void set_image_transform(const image_transform& transform, int mix_duration)\r
{\r
return executor_.invoke([&]\r
{\r
- root_image_transform_ = root_image_transform_;\r
+ root_image_transform_ = make_safe<nested_animated_value<image_transform>>(root_image_transform_, make_safe<basic_animated_value<image_transform>>(transform), mix_duration); \r
});\r
}\r
\r
{\r
return executor_.invoke([&]\r
{\r
- root_audio_transform_ = make_safe<nested_animated_value<audio_transform>>(root_audio_transform_, make_safe<basic_animated_value<audio_transform>>(transform), mix_duration);\r
+ root_audio_transform_ = make_safe<nested_animated_value<audio_transform>>(root_audio_transform_, make_safe<basic_animated_value<audio_transform>>(transform), mix_duration); \r
});\r
}\r
\r
{\r
return executor_.invoke([&]\r
{\r
- image_transforms_[index] = transform;\r
+ auto tmp = safe_ptr<animated_value<image_transform>>(image_transforms_[index]);\r
+ image_transforms_[index] = std::make_shared<nested_animated_value<image_transform>>(tmp, make_safe<basic_animated_value<image_transform>>(transform), mix_duration);\r
});\r
}\r
\r
{\r
return executor_.invoke([&]\r
{\r
- audio_transforms_[index] = transform;\r
+ auto tmp = safe_ptr<animated_value<audio_transform>>(audio_transforms_[index]);\r
+ audio_transforms_[index] = std::make_shared<nested_animated_value<audio_transform>>(tmp, make_safe<basic_animated_value<audio_transform>>(transform), mix_duration);\r
});\r
}\r
\r
device_buffers[n]->bind();\r
}\r
\r
- auto m_p = transform.get_mask_translation();\r
- auto m_s = transform.get_mask_scale();\r
+ auto m_p = transform.get_key_translation();\r
+ auto m_s = transform.get_key_scale();\r
double w = static_cast<double>(format_desc_.width);\r
double h = static_cast<double>(format_desc_.height);\r
\r
GL(glEnable(GL_SCISSOR_TEST));\r
GL(glScissor(static_cast<size_t>(m_p[0]*w), static_cast<size_t>(m_p[1]*h), static_cast<size_t>(m_s[0]*w), static_cast<size_t>(m_s[1]*h)));\r
\r
- auto f_p = transform.get_image_translation();\r
- auto f_s = transform.get_image_scale();\r
+ auto f_p = transform.get_fill_translation();\r
+ auto f_s = transform.get_fill_scale();\r
\r
glBegin(GL_QUADS);\r
glTexCoord2d(0.0, 0.0); glVertex2d( f_p[0] *2.0-1.0, f_p[1] *2.0-1.0);\r
\r
#include "image_transform.h"\r
\r
+#include <common/utility/assert.h>\r
+\r
namespace caspar { namespace core {\r
\r
image_transform::image_transform() \r
return gain_;\r
}\r
\r
-void image_transform::set_image_translation(double x, double y)\r
+void image_transform::set_fill_translation(double x, double y)\r
{\r
fill_translation_[0] = x;\r
fill_translation_[1] = y;\r
}\r
\r
-void image_transform::set_image_scale(double x, double y)\r
+void image_transform::set_fill_scale(double x, double y)\r
{\r
fill_scale_[0] = x;\r
fill_scale_[1] = y; \r
}\r
\r
-std::array<double, 2> image_transform::get_image_translation() const\r
+std::array<double, 2> image_transform::get_fill_translation() const\r
{\r
return fill_translation_;\r
}\r
\r
-std::array<double, 2> image_transform::get_image_scale() const\r
+std::array<double, 2> image_transform::get_fill_scale() const\r
{\r
return fill_scale_;\r
}\r
\r
-void image_transform::set_mask_translation(double x, double y)\r
+void image_transform::set_key_translation(double x, double y)\r
{\r
key_translation_[0] = x;\r
key_translation_[1] = y;\r
}\r
\r
-void image_transform::set_mask_scale(double x, double y)\r
+void image_transform::set_key_scale(double x, double y)\r
{\r
key_scale_[0] = x;\r
key_scale_[1] = y; \r
}\r
\r
-std::array<double, 2> image_transform::get_mask_translation() const\r
+std::array<double, 2> image_transform::get_key_translation() const\r
{\r
return key_translation_;\r
}\r
\r
-std::array<double, 2> image_transform::get_mask_scale() const\r
+std::array<double, 2> image_transform::get_key_scale() const\r
{\r
return key_scale_;\r
}\r
return image_transform(*this) *= other;\r
}\r
\r
+template<typename T>\r
+T mix(const T& lhs, const T& rhs, float alpha)\r
+{\r
+ return (1.0f - alpha) * lhs + alpha * rhs;\r
+}\r
+\r
+image_transform lerp(const image_transform& lhs, const image_transform& rhs, float alpha)\r
+{\r
+ CASPAR_ASSERT(lhs.get_mode() == rhs.get_mode() || lhs.get_mode() == video_mode::invalid || rhs.get_mode() == video_mode::invalid);\r
+\r
+ image_transform result; \r
+ result.set_mode(rhs.get_mode() != video_mode::invalid ? rhs.get_mode() : lhs.get_mode());\r
+ result.set_gain(mix(lhs.get_gain(), rhs.get_gain(), alpha));\r
+ result.set_opacity(mix(lhs.get_opacity(), rhs.get_opacity(), alpha));\r
+ result.set_fill_translation(mix(lhs.get_fill_translation()[0], rhs.get_fill_translation()[0], alpha), mix(lhs.get_fill_translation()[1], rhs.get_fill_translation()[1], alpha));\r
+ result.set_fill_scale(mix(lhs.get_fill_scale()[0], rhs.get_fill_scale()[0], alpha), mix(lhs.get_fill_scale()[1], rhs.get_fill_scale()[1], alpha));\r
+ result.set_key_translation(mix(lhs.get_key_translation()[0], rhs.get_key_translation()[0], alpha), mix(lhs.get_key_translation()[1], rhs.get_key_translation()[1], alpha));\r
+ result.set_key_scale(mix(lhs.get_key_scale()[0], rhs.get_key_scale()[0], alpha), mix(lhs.get_key_scale()[1], rhs.get_key_scale()[1], alpha));\r
+ \r
+ return result;\r
+}\r
+\r
}}
\ No newline at end of file
void set_gain(double value);\r
double get_gain() const;\r
\r
- void set_image_translation(double x, double y);\r
- std::array<double, 2> get_image_translation() const;\r
+ void set_fill_translation(double x, double y);\r
+ std::array<double, 2> get_fill_translation() const;\r
\r
- void set_image_scale(double x, double y);\r
- std::array<double, 2> get_image_scale() const;\r
+ void set_fill_scale(double x, double y);\r
+ std::array<double, 2> get_fill_scale() const;\r
\r
- void set_mask_translation(double x, double y);\r
- std::array<double, 2> get_mask_translation() const;\r
+ void set_key_translation(double x, double y);\r
+ std::array<double, 2> get_key_translation() const;\r
\r
- void set_mask_scale(double x, double y);\r
- std::array<double, 2> get_mask_scale() const;\r
+ void set_key_scale(double x, double y);\r
+ std::array<double, 2> get_key_scale() const;\r
\r
void set_mode(video_mode::type mode);\r
video_mode::type get_mode() const;\r
video_mode::type mode_;\r
};\r
\r
+image_transform lerp(const image_transform& lhs, const image_transform& rhs, float alpha);\r
+\r
}}
\ No newline at end of file
else\r
GetChannel()->mixer().set_image_transform(GetLayerIndex(), transform, duration);\r
}\r
- else if(_parameters[1] == L"FIX_RECT")\r
+ else if(_parameters[1] == L"FILL_RECT")\r
{\r
int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[5], 0) : 0;\r
double x = boost::lexical_cast<double>(_parameters.at(2));\r
double x_s = boost::lexical_cast<double>(_parameters.at(4));\r
double y_s = boost::lexical_cast<double>(_parameters.at(5));\r
auto transform = GetChannel()->mixer().get_image_transform(GetLayerIndex());\r
- transform.set_image_translation(x, y);\r
- transform.set_image_scale(x_s, y_s);\r
- transform.set_mask_translation(x, y);\r
- transform.set_mask_scale(x_s, y_s);\r
+ transform.set_fill_translation(x, y);\r
+ transform.set_fill_scale(x_s, y_s);\r
+ transform.set_key_translation(x, y);\r
+ transform.set_key_scale(x_s, y_s);\r
\r
int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
if(layer != std::numeric_limits<int>::min())\r
else\r
GetChannel()->mixer().set_image_transform(transform, duration);\r
}\r
- else if(_parameters[1] == L"CLIP_RECT")\r
+ else if(_parameters[1] == L"KEY_RECT")\r
{\r
int duration = _parameters.size() > 6 ? lexical_cast_or_default(_parameters[5], 0) : 0;\r
double x = boost::lexical_cast<double>(_parameters.at(2));\r
double x_s = boost::lexical_cast<double>(_parameters.at(4));\r
double y_s = boost::lexical_cast<double>(_parameters.at(5));\r
auto transform = GetChannel()->mixer().get_image_transform(GetLayerIndex());\r
- transform.set_image_translation(0.0, 0.0);\r
- transform.set_image_scale(1.0, 1.0);\r
- transform.set_mask_translation(x, y);\r
- transform.set_mask_scale(x_s, y_s);\r
+ transform.set_fill_translation(0.0, 0.0);\r
+ transform.set_fill_scale(1.0, 1.0);\r
+ transform.set_key_translation(x, y);\r
+ transform.set_key_scale(x_s, y_s);\r
\r
int layer = GetLayerIndex(std::numeric_limits<int>::min());\r
if(layer != std::numeric_limits<int>::min())\r
{\r
int index = x+y*n;\r
auto transform = GetChannel()->mixer().get_image_transform(index); \r
- transform.set_image_translation(x*delta, y*delta);\r
- transform.set_image_scale(delta, delta); \r
- transform.set_mask_translation(x*delta, y*delta);\r
- transform.set_mask_scale(delta, delta);\r
+ transform.set_fill_translation(x*delta, y*delta);\r
+ transform.set_fill_scale(delta, delta); \r
+ transform.set_key_translation(x*delta, y*delta);\r
+ transform.set_key_scale(delta, delta);\r
GetChannel()->mixer().set_image_transform(index, transform, 0);\r
}\r
}\r
CASPAR_LOG(info) << L"Starting CasparCG Video and Graphics Playout Server " << env::version();\r
CASPAR_LOG(info) << L"Flash " << flash::get_flash_version();\r
CASPAR_LOG(info) << L"Flash-Template-Host " << flash::get_cg_version();\r
- CASPAR_LOG(info) << L"FreeImage " << image::get_image_version();\r
+ CASPAR_LOG(info) << L"FreeImage " << image::get_fill_version();\r
\r
std::wstring decklink_devices;\r
BOOST_FOREACH(auto& device, get_decklink_device_list())\r