2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Robert Nagy, ronag89@gmail.com
20 * Author: Helge Norberg, helge.norberg@svt.se
23 #include "image_scroll_producer.h"
25 #include "../util/image_loader.h"
26 #include "../util/image_view.h"
27 #include "../util/image_algorithms.h"
29 #include <core/video_format.h>
31 #include <core/frame/frame.h>
32 #include <core/frame/draw_frame.h>
33 #include <core/frame/frame_factory.h>
34 #include <core/frame/frame_transform.h>
35 #include <core/frame/pixel_format.h>
36 #include <core/monitor/monitor.h>
38 #include <common/env.h>
39 #include <common/log.h>
40 #include <common/except.h>
41 #include <common/array.h>
42 #include <common/tweener.h>
43 #include <common/param.h>
44 #include <common/os/filesystem.h>
46 #include <boost/filesystem.hpp>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/property_tree/ptree.hpp>
49 #include <boost/scoped_array.hpp>
54 namespace caspar { namespace image {
56 struct image_scroll_producer : public core::frame_producer_base
58 core::monitor::subject monitor_subject_;
60 const std::wstring filename_;
61 std::vector<core::draw_frame> frames_;
62 core::video_format_desc format_desc_;
65 core::constraints constraints_;
70 int start_offset_x_ = 0;
71 int start_offset_y_ = 0;
74 explicit image_scroll_producer(
75 const spl::shared_ptr<core::frame_factory>& frame_factory,
76 const core::video_format_desc& format_desc,
77 const std::wstring& filename,
80 int motion_blur_px = 0,
81 bool premultiply_with_alpha = false,
82 bool progressive = false)
84 , format_desc_(format_desc)
86 , progressive_(progressive)
88 auto bitmap = load_image(filename_);
89 FreeImage_FlipVertical(bitmap.get());
91 width_ = FreeImage_GetWidth(bitmap.get());
92 height_ = FreeImage_GetHeight(bitmap.get());
93 constraints_.width.set(width_);
94 constraints_.height.set(height_);
96 bool vertical = width_ == format_desc_.width;
97 bool horizontal = height_ == format_desc_.height;
99 if (!vertical && !horizontal)
100 BOOST_THROW_EXCEPTION(
101 caspar::invalid_argument() << msg_info("Neither width nor height matched the video resolution"));
107 double total_num_pixels = format_desc_.height * 2 + height_;
109 speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));
111 if (std::abs(speed_) > 1.0)
112 speed_ = std::ceil(speed_);
117 start_offset_y_ = height_ + format_desc_.height;
124 double total_num_pixels = format_desc_.width * 2 + width_;
126 speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));
128 if (std::abs(speed_) > 1.0)
129 speed_ = std::ceil(speed_);
133 start_offset_x_ = format_desc_.width - (width_ % format_desc_.width);
135 start_offset_x_ = format_desc_.width - (width_ % format_desc_.width) + width_ + format_desc_.width;
138 auto bytes = FreeImage_GetBits(bitmap.get());
139 auto count = width_*height_*4;
140 image_view<bgra_pixel> original_view(bytes, width_, height_);
142 if (premultiply_with_alpha)
143 premultiply(original_view);
145 boost::scoped_array<uint8_t> blurred_copy;
147 if (motion_blur_px > 0)
149 double angle = 3.14159265 / 2; // Up
151 if (horizontal && speed_ < 0)
153 else if (vertical && speed > 0)
155 else if (horizontal && speed > 0)
156 angle = 0.0; // Right
158 blurred_copy.reset(new uint8_t[count]);
159 image_view<bgra_pixel> blurred_view(blurred_copy.get(), width_, height_);
160 caspar::tweener blur_tweener(L"easeInQuad");
161 blur(original_view, blurred_view, angle, motion_blur_px, blur_tweener);
162 bytes = blurred_copy.get();
172 core::pixel_format_desc desc = core::pixel_format::bgra;
173 desc.planes.push_back(core::pixel_format_desc::plane(width_, format_desc_.height, 4));
174 auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), desc);
176 if(count >= frame.image_data(0).size())
178 std::copy_n(bytes + count - frame.image_data(0).size(), frame.image_data(0).size(), frame.image_data(0).begin());
179 count -= static_cast<int>(frame.image_data(0).size());
183 memset(frame.image_data(0).begin(), 0, frame.image_data(0).size());
184 std::copy_n(bytes, count, frame.image_data(0).begin() + format_desc_.size - count);
188 core::draw_frame draw_frame(std::move(frame));
190 // Set the relative position to the other image fragments
191 draw_frame.transform().image_transform.fill_translation[1] = - n++;
193 frames_.push_back(draw_frame);
201 core::pixel_format_desc desc = core::pixel_format::bgra;
202 desc.planes.push_back(core::pixel_format_desc::plane(format_desc_.width, height_, 4));
203 auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), desc);
204 if(count >= frame.image_data(0).size())
206 for(int y = 0; y < height_; ++y)
207 std::copy_n(bytes + i * format_desc_.width*4 + y * width_*4, format_desc_.width*4, frame.image_data(0).begin() + y * format_desc_.width*4);
210 count -= static_cast<int>(frame.image_data(0).size());
214 memset(frame.image_data(0).begin(), 0, frame.image_data(0).size());
215 auto width2 = width_ % format_desc_.width;
216 for(int y = 0; y < height_; ++y)
217 std::copy_n(bytes + i * format_desc_.width*4 + y * width_*4, width2*4, frame.image_data(0).begin() + y * format_desc_.width*4);
222 frames_.push_back(core::draw_frame(std::move(frame)));
225 std::reverse(frames_.begin(), frames_.end());
227 // Set the relative positions of the image fragments.
228 for (size_t n = 0; n < frames_.size(); ++n)
230 double translation = - (static_cast<double>(n) + 1.0);
231 frames_[n].transform().image_transform.fill_translation[0] = translation;
235 CASPAR_LOG(info) << print() << L" Initialized";
238 std::vector<core::draw_frame> get_visible()
240 std::vector<core::draw_frame> result;
241 result.reserve(frames_.size());
243 for (auto& frame : frames_)
245 auto& fill_translation = frame.transform().image_transform.fill_translation;
247 if (width_ == format_desc_.width)
249 auto motion_offset_in_screens = (static_cast<double>(start_offset_y_) + delta_) / static_cast<double>(format_desc_.height);
250 auto vertical_offset = fill_translation[1] + motion_offset_in_screens;
252 if (vertical_offset < -1.0 || vertical_offset > 1.0)
259 auto motion_offset_in_screens = (static_cast<double>(start_offset_x_) + delta_) / static_cast<double>(format_desc_.width);
260 auto horizontal_offset = fill_translation[0] + motion_offset_in_screens;
262 if (horizontal_offset < -1.0 || horizontal_offset > 1.0)
268 result.push_back(frame);
271 return std::move(result);
275 core::draw_frame render_frame(bool allow_eof)
278 return core::draw_frame::empty();
280 core::draw_frame result(get_visible());
281 auto& fill_translation = result.transform().image_transform.fill_translation;
283 if (width_ == format_desc_.width)
285 if (static_cast<size_t>(std::abs(delta_)) >= height_ + format_desc_.height && allow_eof)
286 return core::draw_frame::empty();
288 fill_translation[1] =
289 static_cast<double>(start_offset_y_) / static_cast<double>(format_desc_.height)
290 + delta_ / static_cast<double>(format_desc_.height);
294 if (static_cast<size_t>(std::abs(delta_)) >= width_ + format_desc_.width && allow_eof)
295 return core::draw_frame::empty();
297 fill_translation[0] =
298 static_cast<double>(start_offset_x_) / static_cast<double>(format_desc_.width)
299 + (delta_) / static_cast<double>(format_desc_.width);
305 core::draw_frame render_frame(bool allow_eof, bool advance_delta)
307 auto result = render_frame(allow_eof);
322 core::draw_frame receive_impl() override
324 core::draw_frame result;
326 if (format_desc_.field_mode == core::field_mode::progressive || progressive_)
328 result = render_frame(true, true);
332 auto field1 = render_frame(true, true);
333 auto field2 = render_frame(true, false);
335 if (field1 != core::draw_frame::empty() && field2 == core::draw_frame::empty())
337 field2 = render_frame(false, true);
344 result = core::draw_frame::interlace(field1, field2, format_desc_.field_mode);
347 monitor_subject_ << core::monitor::message("/file/path") % filename_
348 << core::monitor::message("/delta") % delta_
349 << core::monitor::message("/speed") % speed_;
354 core::constraints& pixel_constraints() override
359 std::wstring print() const override
361 return L"image_scroll_producer[" + filename_ + L"]";
364 std::wstring name() const override
366 return L"image-scroll";
369 boost::property_tree::wptree info() const override
371 boost::property_tree::wptree info;
372 info.add(L"type", L"image-scroll");
373 info.add(L"filename", filename_);
377 uint32_t nb_frames() const override
379 if(width_ == format_desc_.width)
381 auto length = (height_ + format_desc_.height * 2);
382 return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));
386 auto length = (width_ + format_desc_.width * 2);
387 return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));
391 core::monitor::subject& monitor_output()
393 return monitor_subject_;
397 spl::shared_ptr<core::frame_producer> create_scroll_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, const std::vector<std::wstring>& params)
399 static const auto extensions = {
413 std::wstring filename = env::media_folder() + params[0];
415 auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool
417 auto file = caspar::find_case_insensitive(boost::filesystem::path(filename).replace_extension(ex).wstring());
419 return static_cast<bool>(file);
422 if(ext == extensions.end())
423 return core::frame_producer::empty();
425 double duration = 0.0;
426 double speed = get_param(L"SPEED", params, 0.0);
429 duration = get_param(L"DURATION", params, 0.0);
431 if(speed == 0 && duration == 0)
432 return core::frame_producer::empty();
434 int motion_blur_px = get_param(L"BLUR", params, 0);
436 bool premultiply_with_alpha = contains_param(L"PREMULTIPLY", params);
437 bool progressive = contains_param(L"PROGRESSIVE", params);
439 return core::create_destroy_proxy(spl::make_shared<image_scroll_producer>(
442 *caspar::find_case_insensitive(filename + *ext),
446 premultiply_with_alpha,