2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>
\r
4 * This file is part of CasparCG.
\r
6 * CasparCG is free software: you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation, either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * CasparCG is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
\r
20 #include "../../stdafx.h"
\r
22 #include "transition_producer.h"
\r
24 #include "../../frame/frame_format.h"
\r
26 #include "../../../common/image/image.h"
\r
27 #include "../../frame/system_frame.h"
\r
28 #include "../../frame/audio_chunk.h"
\r
29 #include "../../renderer/render_device.h"
\r
31 #include <boost/range/algorithm/copy.hpp>
\r
35 class empty_producer : public frame_producer
\r
38 explicit empty_producer(const frame_format_desc& format_desc)
\r
39 : format_desc_(format_desc), frame_(clear_frame(std::make_shared<system_frame>(format_desc_.size)))
\r
42 frame_ptr get_frame() { return frame_; }
\r
43 const frame_format_desc& get_frame_format_desc() const { return format_desc_; }
\r
45 frame_format_desc format_desc_;
\r
49 struct transition_producer::implementation : boost::noncopyable
\r
51 implementation(const frame_producer_ptr& dest, const transition_info& info, const frame_format_desc& format_desc)
\r
52 : current_frame_(0), info_(info), border_color_(0), format_desc_(format_desc),
\r
53 empty_(std::make_shared<empty_producer>(format_desc)), source_(empty_), dest_(dest)
\r
56 BOOST_THROW_EXCEPTION(null_argument() << arg_name_info("dest"));
\r
59 frame_producer_ptr get_following_producer() const
\r
64 void set_leading_producer(const frame_producer_ptr& producer)
\r
66 source_ = producer != nullptr ? producer : empty_;
\r
69 frame_ptr get_frame()
\r
71 if(++current_frame_ >= info_.duration)
\r
74 return compose(get_producer_frame(dest_), get_producer_frame(source_));
\r
77 frame_ptr get_producer_frame(frame_producer_ptr& producer)
\r
79 assert(producer != nullptr);
\r
84 frame = producer->get_frame();
\r
88 CASPAR_LOG_CURRENT_EXCEPTION();
\r
89 CASPAR_LOG(warning) << "Removed renderer from transition.";
\r
92 if(frame == nullptr && producer->get_following_producer() != nullptr)
\r
94 auto following = producer->get_following_producer();
\r
95 following->set_leading_producer(producer);
\r
96 producer = following;
\r
97 return get_producer_frame(producer);
\r
102 frame_ptr compose(const frame_ptr& dest_frame, const frame_ptr& src_frame)
\r
104 frame_ptr result_frame = dest_frame;
\r
105 if(src_frame != nullptr && dest_frame != nullptr)
\r
107 result_frame = std::make_shared<system_frame>(format_desc_.size);
\r
108 tbb::parallel_invoke(
\r
111 GenerateFrame(result_frame->data(), src_frame->data(), dest_frame->data());
\r
115 float delta = static_cast<float>(current_frame_)/static_cast<float>(info_.duration);
\r
116 set_frame_volume(dest_frame, delta*100.0f);
\r
117 set_frame_volume(src_frame, (1.0f-delta)*100.0f);
\r
119 boost::range::copy(src_frame->audio_data(), std::back_inserter(result_frame->audio_data()));
\r
120 boost::range::copy(dest_frame->audio_data(), std::back_inserter(result_frame->audio_data()));;
\r
123 return result_frame;
\r
126 void GenerateFrame(unsigned char* pResultData, const unsigned char* pSourceData, const unsigned char* pDestData)
\r
128 if(info_.type == transition_type::cut)
\r
130 common::image::copy(pResultData, pSourceData, format_desc_.size);
\r
134 if(current_frame_ >= info_.duration)
\r
136 common::image::copy(pResultData, pDestData, format_desc_.size);
\r
140 if(info_.type == transition_type::mix)
\r
142 common::image::lerp(pResultData, pSourceData, pDestData, 1.0f-static_cast<float>(current_frame_)/static_cast<float>(info_.duration), format_desc_.size);
\r
146 size_t totalWidth = format_desc_.width + info_.border_width;
\r
148 float fStep = totalWidth / static_cast<float>(info_.duration);
\r
149 float fOffset = fStep * static_cast<float>(current_frame_);
\r
151 size_t halfStep = static_cast<size_t>(fStep/2.0);
\r
152 size_t offset = static_cast<size_t>(fOffset+0.5f);
\r
154 //read source to buffer
\r
155 for(size_t row = 0, even = 0; row < format_desc_.height; ++row, even ^= 1)
\r
157 size_t fieldCorrectedOffset = offset + (halfStep*even);
\r
158 if(fieldCorrectedOffset < format_desc_.width)
\r
160 if(info_.direction != transition_direction::from_left)
\r
162 if(info_.type == transition_type::push)
\r
163 memcpy(&(pResultData[4*row*format_desc_.width]), &(pSourceData[4*(row*format_desc_.width+fieldCorrectedOffset)]), (format_desc_.width-fieldCorrectedOffset)*4);
\r
164 else //Slide | Wipe
\r
165 memcpy(&(pResultData[4*row*format_desc_.width]), &(pSourceData[4*row*format_desc_.width]), (format_desc_.width-fieldCorrectedOffset)*4);
\r
167 else // if (direction == LEFT)
\r
169 if(info_.type == transition_type::push)
\r
170 memcpy(&(pResultData[4*(row*format_desc_.width+fieldCorrectedOffset)]), &(pSourceData[4*(row*format_desc_.width)]), (format_desc_.width-fieldCorrectedOffset)*4);
\r
171 else //slide eller wipe
\r
172 memcpy(&(pResultData[4*(row*format_desc_.width+fieldCorrectedOffset)]), &(pSourceData[4*(row*format_desc_.width+fieldCorrectedOffset)]), (format_desc_.width-fieldCorrectedOffset)*4);
\r
177 //write border to buffer
\r
178 if(info_.border_width > 0)
\r
180 for(size_t row = 0, even = 0; row < format_desc_.height; ++row, even ^= 1)
\r
182 size_t fieldCorrectedOffset = offset + (halfStep*even);
\r
183 size_t length = info_.border_width;
\r
186 if(info_.direction != transition_direction::from_left)
\r
188 if(fieldCorrectedOffset > format_desc_.width)
\r
190 length -= fieldCorrectedOffset-format_desc_.width;
\r
191 start += fieldCorrectedOffset-format_desc_.width;
\r
192 fieldCorrectedOffset = format_desc_.width;
\r
194 else if(fieldCorrectedOffset < length)
\r
196 length = fieldCorrectedOffset;
\r
199 for(size_t i = 0; i < length; ++i)
\r
200 memcpy(&(pResultData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset+i)]), &border_color_, 4);
\r
203 else // if (direction == LEFT)
\r
205 if(fieldCorrectedOffset > format_desc_.width)
\r
207 length -= fieldCorrectedOffset-format_desc_.width;
\r
209 fieldCorrectedOffset -= info_.border_width-length;
\r
211 else if(fieldCorrectedOffset < length)
\r
213 length = fieldCorrectedOffset;
\r
214 start = info_.border_width-fieldCorrectedOffset;
\r
217 for(size_t i = 0; i < length; ++i)
\r
218 memcpy(&(pResultData[4*(row*format_desc_.width+fieldCorrectedOffset-length+i)]), &border_color_, 4);
\r
224 //read dest to buffer
\r
225 offset -= info_.border_width;
\r
228 for(size_t row = 0, even = 0; row < format_desc_.height; ++row, even ^= 1)
\r
230 int fieldCorrectedOffset = offset + (halfStep*even);
\r
232 if(info_.direction != transition_direction::from_left)
\r
234 if(info_.type == transition_type::wipe)
\r
235 memcpy(&(pResultData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset)]), &(pDestData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset)]), fieldCorrectedOffset*4);
\r
237 memcpy(&(pResultData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset)]), &(pDestData[4*row*format_desc_.width]), fieldCorrectedOffset*4);
\r
239 else // if (direction == LEFT)
\r
241 if(info_.type == transition_type::wipe)
\r
242 memcpy(&(pResultData[4*(row*format_desc_.width)]), &(pDestData[4*(row*format_desc_.width)]), fieldCorrectedOffset*4);
\r
244 memcpy(&(pResultData[4*(row*format_desc_.width)]), &(pDestData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset)]), fieldCorrectedOffset*4);
\r
250 const frame_format_desc format_desc_;
\r
252 frame_producer_ptr empty_;
\r
253 frame_producer_ptr source_;
\r
254 frame_producer_ptr dest_;
\r
256 unsigned short current_frame_;
\r
258 const transition_info info_;
\r
259 const unsigned long border_color_;
\r
262 transition_producer::transition_producer(const frame_producer_ptr& dest, const transition_info& info, const frame_format_desc& format_desc)
\r
263 : impl_(new implementation(dest, info, format_desc)){}
\r
264 frame_ptr transition_producer::get_frame(){return impl_->get_frame();}
\r
265 frame_producer_ptr transition_producer::get_following_producer() const{return impl_->get_following_producer();}
\r
266 void transition_producer::set_leading_producer(const frame_producer_ptr& producer) { impl_->set_leading_producer(producer); }
\r
267 const frame_format_desc& transition_producer::get_frame_format_desc() const { return impl_->format_desc_; }
\r