* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
*\r
* Author: Robert Nagy, ronag89@gmail.com\r
+* Author: Helge Norberg, helge.norberg@svt.se\r
*/\r
\r
#include "image_scroll_producer.h"\r
\r
#include "../util/image_loader.h"\r
+#include "../util/image_view.h"\r
+#include "../util/image_algorithms.h"\r
\r
#include <core/video_format.h>\r
\r
#include <common/log.h>\r
#include <common/except.h>\r
#include <common/array.h>\r
+#include <common/tweener.h>\r
\r
#include <boost/assign.hpp>\r
#include <boost/filesystem.hpp>\r
#include <boost/foreach.hpp>\r
#include <boost/lexical_cast.hpp>\r
#include <boost/property_tree/ptree.hpp>\r
+#include <boost/scoped_array.hpp>\r
\r
#include <algorithm>\r
#include <array>\r
int width_;\r
int height_;\r
\r
- int delta_;\r
- int speed_;\r
+ double delta_;\r
+ double speed_;\r
\r
- std::array<double, 2> start_offset_;\r
+ int start_offset_x_;\r
+ int start_offset_y_;\r
+ bool progressive_;\r
\r
- explicit image_scroll_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, const std::wstring& filename, int speed) \r
+ explicit image_scroll_producer(\r
+ const spl::shared_ptr<core::frame_factory>& frame_factory, \r
+ const core::video_format_desc& format_desc, \r
+ const std::wstring& filename, \r
+ double speed,\r
+ double duration,\r
+ int motion_blur_px = 0,\r
+ bool premultiply_with_alpha = false,\r
+ bool progressive = false)\r
: filename_(filename)\r
, delta_(0)\r
, format_desc_(format_desc)\r
, speed_(speed)\r
+ , start_offset_x_(0)\r
+ , start_offset_y_(0)\r
+ , progressive_(progressive)\r
{\r
- start_offset_.assign(0.0);\r
-\r
auto bitmap = load_image(filename_);\r
FreeImage_FlipVertical(bitmap.get());\r
\r
width_ = FreeImage_GetWidth(bitmap.get());\r
height_ = FreeImage_GetHeight(bitmap.get());\r
\r
+ bool vertical = width_ == format_desc_.width;\r
+ bool horizontal = height_ == format_desc_.height;\r
+\r
+ if (!vertical && !horizontal)\r
+ BOOST_THROW_EXCEPTION(\r
+ caspar::invalid_argument() << msg_info("Neither width nor height matched the video resolution"));\r
+\r
+ if (vertical)\r
+ {\r
+ if (duration != 0.0)\r
+ {\r
+ double total_num_pixels = format_desc_.height * 2 + height_;\r
+\r
+ speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));\r
+\r
+ if (std::abs(speed_) > 1.0)\r
+ speed_ = std::ceil(speed_);\r
+ }\r
+\r
+ if (speed_ < 0.0)\r
+ {\r
+ start_offset_y_ = height_ + format_desc_.height;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (duration != 0.0)\r
+ {\r
+ double total_num_pixels = format_desc_.width * 2 + width_;\r
+\r
+ speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));\r
+\r
+ if (std::abs(speed_) > 1.0)\r
+ speed_ = std::ceil(speed_);\r
+ }\r
+\r
+ if (speed_ > 0.0)\r
+ start_offset_x_ = format_desc_.width - (width_ % format_desc_.width);\r
+ else\r
+ start_offset_x_ = format_desc_.width - (width_ % format_desc_.width) + width_ + format_desc_.width;\r
+ }\r
+\r
auto bytes = FreeImage_GetBits(bitmap.get());\r
auto count = width_*height_*4;\r
+ image_view<bgra_pixel> original_view(bytes, width_, height_);\r
+\r
+ if (premultiply_with_alpha)\r
+ premultiply(original_view);\r
+\r
+ boost::scoped_array<uint8_t> blurred_copy;\r
+\r
+ if (motion_blur_px > 0)\r
+ {\r
+ double angle = 3.14159265 / 2; // Up\r
+\r
+ if (horizontal && speed_ < 0)\r
+ angle *= 2; // Left\r
+ else if (vertical && speed > 0)\r
+ angle *= 3; // Down\r
+ else if (horizontal && speed > 0)\r
+ angle = 0.0; // Right\r
+\r
+ blurred_copy.reset(new uint8_t[count]);\r
+ image_view<bgra_pixel> blurred_view(blurred_copy.get(), width_, height_);\r
+ core::tweener blur_tweener(L"easeInQuad");\r
+ blur(original_view, blurred_view, angle, motion_blur_px, blur_tweener);\r
+ bytes = blurred_copy.get();\r
+ bitmap.reset();\r
+ }\r
\r
- if(height_ > format_desc_.height)\r
+ if (vertical)\r
{\r
+ int n = 1;\r
+\r
while(count > 0)\r
{\r
core::pixel_format_desc desc = core::pixel_format::bgra;\r
std::copy_n(bytes, count, frame.image_data(0).begin() + format_desc_.size - count);\r
count = 0;\r
}\r
- \r
- frames_.push_back(core::draw_frame(std::move(frame)));\r
- }\r
- \r
- if(speed_ < 0.0)\r
- {\r
- auto offset = format_desc_.height - (height_ % format_desc_.height);\r
- auto offset2 = offset * 0.5/static_cast<double>(format_desc_.height);\r
- start_offset_[1] = (std::ceil(static_cast<double>(height_) / static_cast<double>(format_desc_.height)) + 1.0) * 0.5 - offset2;// - 1.5;\r
+\r
+ core::draw_frame draw_frame(std::move(frame));\r
+\r
+ // Set the relative position to the other image fragments\r
+ draw_frame.transform().image_transform.fill_translation[1] = - n++;\r
+\r
+ frames_.push_back(draw_frame);\r
}\r
}\r
- else\r
+ else if (horizontal)\r
{\r
int i = 0;\r
while(count > 0)\r
\r
std::reverse(frames_.begin(), frames_.end());\r
\r
- if(speed_ > 0.0)\r
+ // Set the relative positions of the image fragments.\r
+ for (size_t n = 0; n < frames_.size(); ++n)\r
{\r
- auto offset = format_desc_.width - (width_ % format_desc_.width);\r
- start_offset_[0] = offset * 0.5/static_cast<double>(format_desc_.width);\r
+ double translation = - (static_cast<double>(n) + 1.0);\r
+ frames_[n].transform().image_transform.fill_translation[0] = translation;\r
+ }\r
+ }\r
+\r
+ CASPAR_LOG(info) << print() << L" Initialized";\r
+ }\r
+\r
+ std::vector<core::draw_frame> get_visible()\r
+ {\r
+ std::vector<core::draw_frame> result;\r
+ result.reserve(frames_.size());\r
+\r
+ BOOST_FOREACH(auto& frame, frames_)\r
+ {\r
+ auto& fill_translation = frame.transform().image_transform.fill_translation;\r
+\r
+ if (width_ == format_desc_.width)\r
+ {\r
+ auto motion_offset_in_screens = (static_cast<double>(start_offset_y_) + delta_) / static_cast<double>(format_desc_.height);\r
+ auto vertical_offset = fill_translation[1] + motion_offset_in_screens;\r
+\r
+ if (vertical_offset < -1.0 || vertical_offset > 1.0)\r
+ {\r
+ continue;\r
+ }\r
}\r
else\r
{\r
- start_offset_[0] = (std::ceil(static_cast<double>(width_) / static_cast<double>(format_desc_.width)) + 1.0) * 0.5;// - 1.5;\r
+ auto motion_offset_in_screens = (static_cast<double>(start_offset_x_) + delta_) / static_cast<double>(format_desc_.width);\r
+ auto horizontal_offset = fill_translation[0] + motion_offset_in_screens;\r
+\r
+ if (horizontal_offset < -1.0 || horizontal_offset > 1.0)\r
+ {\r
+ continue;\r
+ }\r
}\r
+\r
+ result.push_back(frame);\r
}\r
\r
- CASPAR_LOG(info) << print() << L" Initialized";\r
+ return std::move(result);\r
}\r
\r
// frame_producer\r
-\r
- core::draw_frame receive_impl() override\r
- { \r
- delta_ += speed_;\r
-\r
+ core::draw_frame render_frame(bool allow_eof)\r
+ {\r
if(frames_.empty())\r
- return core::draw_frame::late();\r
+ return core::draw_frame::empty();\r
\r
- if(height_ > format_desc_.height)\r
+ core::draw_frame result(get_visible());\r
+ auto& fill_translation = result.transform().image_transform.fill_translation;\r
+\r
+ if (width_ == format_desc_.width)\r
{\r
- if(static_cast<int>(std::abs(delta_)) >= height_ - format_desc_.height)\r
- return last_frame();\r
+ if (static_cast<size_t>(std::abs(delta_)) >= height_ + format_desc_.height && allow_eof)\r
+ return core::draw_frame::empty();\r
\r
- for(int n = 0; n < frames_.size(); ++n)\r
- {\r
- frames_[n].transform().image_transform.fill_translation[0] = start_offset_[0];\r
- frames_[n].transform().image_transform.fill_translation[1] = start_offset_[1] - (n+1) + delta_ * 0.5/static_cast<double>(format_desc_.height);\r
- }\r
+ fill_translation[1] = \r
+ static_cast<double>(start_offset_y_) / static_cast<double>(format_desc_.height)\r
+ + delta_ / static_cast<double>(format_desc_.height);\r
+ }\r
+ else\r
+ {\r
+ if (static_cast<size_t>(std::abs(delta_)) >= width_ + format_desc_.width && allow_eof)\r
+ return core::draw_frame::empty();\r
+\r
+ fill_translation[0] = \r
+ static_cast<double>(start_offset_x_) / static_cast<double>(format_desc_.width)\r
+ + (delta_) / static_cast<double>(format_desc_.width);\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ core::draw_frame render_frame(bool allow_eof, bool advance_delta)\r
+ {\r
+ auto result = render_frame(allow_eof);\r
+\r
+ if (advance_delta)\r
+ {\r
+ advance();\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ void advance()\r
+ {\r
+ delta_ += speed_;\r
+ }\r
+\r
+ core::draw_frame receive_impl() override\r
+ {\r
+ core::draw_frame result;\r
+\r
+ if (format_desc_.field_mode == core::field_mode::progressive || progressive_)\r
+ {\r
+ result = render_frame(true, true);\r
}\r
else\r
{\r
- if(static_cast<int>(std::abs(delta_)) >= width_ - format_desc_.width)\r
- return last_frame();\r
+ auto field1 = render_frame(true, true);\r
+ auto field2 = render_frame(true, false);\r
\r
- for(int n = 0; n < frames_.size(); ++n)\r
+ if (field1 != core::draw_frame::empty() && field2 == core::draw_frame::empty())\r
{\r
- frames_[n].transform().image_transform.fill_translation[0] = start_offset_[0] - (n+1) + delta_ * 0.5/static_cast<double>(format_desc_.width); \r
- frames_[n].transform().image_transform.fill_translation[1] = start_offset_[1];\r
+ field2 = render_frame(false, true);\r
}\r
+ else\r
+ {\r
+ advance();\r
+ }\r
+\r
+ result = core::draw_frame::interlace(field1, field2, format_desc_.field_mode);\r
}\r
\r
event_subject_ << monitor::event("file/path") % filename_\r
<< monitor::event("delta") % delta_ \r
<< monitor::event("speed") % speed_;\r
\r
- return core::draw_frame(frames_);\r
+ return result;\r
}\r
\r
std::wstring print() const override\r
\r
uint32_t nb_frames() const override\r
{\r
- if(height_ > format_desc_.height)\r
+ if(width_ == format_desc_.width)\r
{\r
- auto length = (height_ - format_desc_.height);\r
- return length/std::abs(speed_);// + length % std::abs(delta_));\r
+ auto length = (height_ + format_desc_.height * 2);\r
+ return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));\r
}\r
else\r
{\r
- auto length = (width_ - format_desc_.width);\r
- auto result = length/std::abs(speed_);// + length % std::abs(delta_));\r
- return result;\r
+ auto length = (width_ + format_desc_.width * 2);\r
+ return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));\r
}\r
}\r
\r
\r
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)\r
{\r
- static const std::vector<std::wstring> extensions = list_of(L"png")(L"tga")(L"bmp")(L"jpg")(L"jpeg")(L"gif")(L"tiff")(L"tif")(L"jp2")(L"jpx")(L"j2k")(L"j2c");\r
+ static const std::vector<std::wstring> extensions = list_of(L".png")(L".tga")(L".bmp")(L".jpg")(L".jpeg")(L".gif")(L".tiff")(L".tif")(L".jp2")(L".jpx")(L".j2k")(L".j2c");\r
std::wstring filename = env::media_folder() + L"\\" + params[0];\r
\r
auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool\r
{ \r
- return boost::filesystem::is_regular_file(boost::filesystem::wpath(filename).replace_extension(ex));\r
+ return boost::filesystem::is_regular_file(boost::filesystem::path(filename).replace_extension(ex));\r
});\r
\r
if(ext == extensions.end())\r
return core::frame_producer::empty();\r
\r
- int speed = 0;\r
+ double speed = 0.0;\r
+ double duration = 0.0;\r
auto speed_it = std::find(params.begin(), params.end(), L"SPEED");\r
if(speed_it != params.end())\r
{\r
if(++speed_it != params.end())\r
- speed = boost::lexical_cast<int>(*speed_it);\r
+ speed = boost::lexical_cast<double>(*speed_it);\r
+ }\r
+\r
+ if (speed == 0)\r
+ {\r
+ auto duration_it = std::find(params.begin(), params.end(), L"DURATION");\r
+\r
+ if (duration_it != params.end() && ++duration_it != params.end())\r
+ {\r
+ duration = boost::lexical_cast<double>(*duration_it);\r
+ }\r
}\r
\r
- if(speed == 0)\r
+ if(speed == 0 && duration == 0)\r
return core::frame_producer::empty();\r
\r
- return spl::make_shared<image_scroll_producer>(frame_factory, format_desc, filename + L"." + *ext, speed);\r
+ int motion_blur_px = 0;\r
+ auto blur_it = std::find(params.begin(), params.end(), L"BLUR");\r
+ if (blur_it != params.end() && ++blur_it != params.end())\r
+ {\r
+ motion_blur_px = boost::lexical_cast<int>(*blur_it);\r
+ }\r
+\r
+ bool premultiply_with_alpha = std::find(params.begin(), params.end(), L"PREMULTIPLY") != params.end();\r
+ bool progressive = std::find(params.begin(), params.end(), L"PROGRESSIVE") != params.end();\r
+\r
+ return core::create_destroy_proxy(spl::make_shared<image_scroll_producer>(\r
+ frame_factory, \r
+ format_desc, \r
+ filename + *ext, \r
+ -speed, \r
+ -duration, \r
+ motion_blur_px, \r
+ premultiply_with_alpha,\r
+ progressive));\r
}\r
\r
}}
\ No newline at end of file