-//#include "image_scroll_producer.h"\r
-//\r
-//#include "image_loader.h"\r
-//\r
-//#include "../../mixer/basic_frame.h"\r
-//#include "../../mixer/basic_frame.h"\r
-//#include "../../video_format.h"\r
-//#include "../../mixer/frame_mixer_device.h"\r
-//#include "../../configuration.h"\r
-//\r
-//#include <tbb/parallel_for.h>\r
-//#include <tbb/parallel_invoke.h>\r
-//#include <tbb/scalable_allocator.h>\r
-//\r
-//#include <boost/assign.hpp>\r
-//#include <boost/algorithm/string/case_conv.hpp>\r
-//\r
-//using namespace boost::assign;\r
-//\r
-//namespace caspar { namespace core { namespace image{\r
-//\r
-//enum direction\r
-//{\r
-// Up, Down, Left, Right\r
-//};\r
-//\r
-//struct image_scroll_producer : public frame_producer\r
-//{\r
-// static const int DEFAULT_SCROLL_SPEED = 50;\r
-//\r
-// image_scroll_producer(const std::wstring& filename, const std::vector<std::wstring>& params) \r
-// : speed_(image_scroll_producer::DEFAULT_SCROLL_SPEED), direction_(direction::Up), offset_(0), filename_(filename)\r
-// {\r
-// auto pos = filename.find_last_of(L'_');\r
-// if(pos != std::wstring::npos && pos + 1 < filename.size())\r
-// {\r
-// std::wstring speedStr = filename.substr(pos + 1);\r
-// pos = speedStr.find_first_of(L'.');\r
-// if(pos != std::wstring::npos)\r
-// {\r
-// speedStr = speedStr.substr(0, pos); \r
-// speed_ = lexical_cast_or_default<int>(speedStr, image_scroll_producer::DEFAULT_SCROLL_SPEED);\r
-// }\r
-// }\r
-//\r
-// loop_ = std::find(params.begin(), params.end(), L"LOOP") != params.end();\r
-// }\r
-//\r
-// void load_and_pad_image(const std::wstring& filename)\r
-// {\r
-// auto pBitmap = load_image(filename);\r
-//\r
-// size_t width = FreeImage_GetWidth(pBitmap.get());\r
-// size_t height = FreeImage_GetHeight(pBitmap.get());\r
-//\r
-// image_width_ = std::max(width, format_desc_.width);\r
-// image_height_ = std::max(height, format_desc_.height);\r
-//\r
-// image_ = std::shared_ptr<unsigned char>(static_cast<unsigned char*>(scalable_aligned_malloc(image_width_*image_height_*4, 16)));\r
-// std::fill_n(image_.get(), image_width_*image_height_*4, 0);\r
-//\r
-// unsigned char* pBits = FreeImage_GetBits(pBitmap.get());\r
-// \r
-// for (size_t i = 0; i < height; ++i)\r
-// std::copy_n(&pBits[i* width * 4], width * 4, &image_.get()[i * image_width_ * 4]);\r
-// }\r
-//\r
-// basic_frame do_receive()\r
-// {\r
-// auto frame = frame_factory_->create_frame(format_desc_.width, format_desc_.height);\r
-// std::fill(frame.image_data().begin(), frame.image_data().end(), 0);\r
-//\r
-// const int delta_x = direction_ == direction::Left ? speed_ : -speed_;\r
-// const int delta_y = direction_ == direction::Up ? speed_ : -speed_;\r
-//\r
-// unsigned char* frame_data = frame.image_data().begin();\r
-// unsigned char* image_data = image_.get();\r
-// \r
-// if (direction_ == direction::Up || direction_ == direction::Down)\r
-// {\r
-// tbb::parallel_for(static_cast<size_t>(0), format_desc_.height, static_cast<size_t>(1), [&](size_t i)\r
-// {\r
-// int srcRow = i + offset_;\r
-// int dstInxex = i * format_desc_.width * 4;\r
-// int srcIndex = srcRow * format_desc_.width * 4;\r
-// int size = format_desc_.width * 4;\r
-//\r
-// std::copy_n(&image_data[srcIndex], size, &frame_data[dstInxex]);\r
-// }); \r
-// \r
-// offset_ += delta_y;\r
-// }\r
-// else\r
-// {\r
-// tbb::parallel_for(static_cast<size_t>(0), format_desc_.height, static_cast<size_t>(1), [&](size_t i)\r
-// {\r
-// int correctOffset = offset_;\r
-// int dstIndex = i * format_desc_.width * 4;\r
-// int srcIndex = (i * image_width_ + correctOffset) * 4;\r
-// \r
-// int stopOffset = std::min<int>(correctOffset + format_desc_ .width, image_width_);\r
-// int size = (stopOffset - correctOffset) * 4;\r
-//\r
-// std::copy_n(&image_data[srcIndex], size, &frame_data[dstIndex]);\r
-// });\r
-//\r
-// offset_ += delta_x;\r
-// }\r
-//\r
-// return std::move(frame);\r
-// }\r
-// \r
-// safe_ptr<basic_frame> receive()\r
-// { \r
-// if(format_desc_.mode != video_mode::progressive) \r
-// {\r
-// basic_frame frame1;\r
-// basic_frame frame2;\r
-// tbb::parallel_invoke([&]{ frame1 = std::move(do_receive()); }, [&]{ frame2 = std::move(do_receive()); });\r
-// return basic_frame::interlace(std::move(frame1), std::move(frame2), format_desc_.mode);\r
-// } \r
-//\r
-// return receive(); \r
-// }\r
-// \r
-// void initialize(const safe_ptr<frame_factory>& frame_factory)\r
-// {\r
-// frame_factory_ = frame_factory;\r
-// format_desc_ = frame_factory_->get_video_format_desc();\r
-// \r
-// if(image_width_ - format_desc_.width > image_height_ - format_desc_.height)\r
-// direction_ = speed_ < 0 ? direction::Right : direction::Left;\r
-// else\r
-// direction_ = speed_ < 0 ? direction::Down : direction::Up;\r
-//\r
-// if (direction_ == direction::Down)\r
-// offset_ = image_height_ - format_desc_.height;\r
-// else if (direction_ == direction::Right)\r
-// offset_ = image_width_ - format_desc_.width;\r
-//\r
-// speed_ = static_cast<int>(abs(static_cast<double>(speed_) / format_desc_.fps));\r
-// \r
-// load_and_pad_image(filename_);\r
-// }\r
-// \r
-//\r
-// std::wstring print() const\r
-// {\r
-// return L"image_scroll_producer. filename: " + filename_;\r
-// }\r
-//\r
-// const video_format_desc& get_video_format_desc() const { return format_desc_; } \r
-// \r
-// int image_width_;\r
-// int image_height_;\r
-// int speed_;\r
-// int offset_;\r
-// direction direction_;\r
-//\r
-// tbb::atomic<bool> loop_;\r
-// std::shared_ptr<unsigned char> image_;\r
-// video_format_desc format_desc_;\r
-//\r
-// std::wstring filename_;\r
-//\r
-// safe_ptr<frame_mixer_device> frame_factory_;\r
-//};\r
-//\r
-//safe_ptr<frame_producer> create_image_scroll_producer(const std::vector<std::wstring>& params)\r
-//{\r
-// static const std::vector<std::wstring> extensions = list_of(L"spng")(L"stga")(L"sbmp")(L"sjpg")(L"sjpeg");\r
-// std::wstring filename = configuration::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
-// });\r
-//\r
-// if(ext == extensions.end())\r
-// return nullptr;\r
-//\r
-// return std::make_shared<image_scroll_producer>(filename + L"." + *ext, params);\r
-//}\r
-//\r
-//}}}
\ No newline at end of file
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Robert Nagy, ronag89@gmail.com
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#include "image_scroll_producer.h"
+
+#include "../util/image_loader.h"
+#include "../util/image_view.h"
+#include "../util/image_algorithms.h"
+
+#include <core/video_format.h>
+
+#include <core/frame/frame.h>
+#include <core/frame/draw_frame.h>
+#include <core/frame/frame_factory.h>
+#include <core/frame/frame_transform.h>
+#include <core/frame/pixel_format.h>
+#include <core/monitor/monitor.h>
+
+#include <common/env.h>
+#include <common/log.h>
+#include <common/except.h>
+#include <common/array.h>
+#include <common/tweener.h>
+
+#include <boost/assign.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/scoped_array.hpp>
+
+#include <algorithm>
+#include <array>
+
+using namespace boost::assign;
+
+namespace caspar { namespace image {
+
+struct image_scroll_producer : public core::frame_producer_base
+{
+ monitor::basic_subject event_subject_;
+
+ const std::wstring filename_;
+ std::vector<core::draw_frame> frames_;
+ core::video_format_desc format_desc_;
+ int width_;
+ int height_;
+
+ double delta_;
+ double speed_;
+
+ int start_offset_x_;
+ int start_offset_y_;
+ bool progressive_;
+
+ explicit image_scroll_producer(
+ const spl::shared_ptr<core::frame_factory>& frame_factory,
+ const core::video_format_desc& format_desc,
+ const std::wstring& filename,
+ double speed,
+ double duration,
+ int motion_blur_px = 0,
+ bool premultiply_with_alpha = false,
+ bool progressive = false)
+ : filename_(filename)
+ , delta_(0)
+ , format_desc_(format_desc)
+ , speed_(speed)
+ , start_offset_x_(0)
+ , start_offset_y_(0)
+ , progressive_(progressive)
+ {
+ auto bitmap = load_image(filename_);
+ FreeImage_FlipVertical(bitmap.get());
+
+ width_ = FreeImage_GetWidth(bitmap.get());
+ height_ = FreeImage_GetHeight(bitmap.get());
+
+ bool vertical = width_ == format_desc_.width;
+ bool horizontal = height_ == format_desc_.height;
+
+ if (!vertical && !horizontal)
+ BOOST_THROW_EXCEPTION(
+ caspar::invalid_argument() << msg_info("Neither width nor height matched the video resolution"));
+
+ if (vertical)
+ {
+ if (duration != 0.0)
+ {
+ double total_num_pixels = format_desc_.height * 2 + height_;
+
+ speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));
+
+ if (std::abs(speed_) > 1.0)
+ speed_ = std::ceil(speed_);
+ }
+
+ if (speed_ < 0.0)
+ {
+ start_offset_y_ = height_ + format_desc_.height;
+ }
+ }
+ else
+ {
+ if (duration != 0.0)
+ {
+ double total_num_pixels = format_desc_.width * 2 + width_;
+
+ speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));
+
+ if (std::abs(speed_) > 1.0)
+ speed_ = std::ceil(speed_);
+ }
+
+ if (speed_ > 0.0)
+ start_offset_x_ = format_desc_.width - (width_ % format_desc_.width);
+ else
+ start_offset_x_ = format_desc_.width - (width_ % format_desc_.width) + width_ + format_desc_.width;
+ }
+
+ auto bytes = FreeImage_GetBits(bitmap.get());
+ auto count = width_*height_*4;
+ image_view<bgra_pixel> original_view(bytes, width_, height_);
+
+ if (premultiply_with_alpha)
+ premultiply(original_view);
+
+ boost::scoped_array<uint8_t> blurred_copy;
+
+ if (motion_blur_px > 0)
+ {
+ double angle = 3.14159265 / 2; // Up
+
+ if (horizontal && speed_ < 0)
+ angle *= 2; // Left
+ else if (vertical && speed > 0)
+ angle *= 3; // Down
+ else if (horizontal && speed > 0)
+ angle = 0.0; // Right
+
+ blurred_copy.reset(new uint8_t[count]);
+ image_view<bgra_pixel> blurred_view(blurred_copy.get(), width_, height_);
+ core::tweener blur_tweener(L"easeInQuad");
+ blur(original_view, blurred_view, angle, motion_blur_px, blur_tweener);
+ bytes = blurred_copy.get();
+ bitmap.reset();
+ }
+
+ if (vertical)
+ {
+ int n = 1;
+
+ while(count > 0)
+ {
+ core::pixel_format_desc desc = core::pixel_format::bgra;
+ desc.planes.push_back(core::pixel_format_desc::plane(width_, format_desc_.height, 4));
+ auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), desc);
+
+ if(count >= frame.image_data(0).size())
+ {
+ std::copy_n(bytes + count - frame.image_data(0).size(), frame.image_data(0).size(), frame.image_data(0).begin());
+ count -= static_cast<int>(frame.image_data(0).size());
+ }
+ else
+ {
+ memset(frame.image_data(0).begin(), 0, frame.image_data(0).size());
+ std::copy_n(bytes, count, frame.image_data(0).begin() + format_desc_.size - count);
+ count = 0;
+ }
+
+ core::draw_frame draw_frame(std::move(frame));
+
+ // Set the relative position to the other image fragments
+ draw_frame.transform().image_transform.fill_translation[1] = - n++;
+
+ frames_.push_back(draw_frame);
+ }
+ }
+ else if (horizontal)
+ {
+ int i = 0;
+ while(count > 0)
+ {
+ core::pixel_format_desc desc = core::pixel_format::bgra;
+ desc.planes.push_back(core::pixel_format_desc::plane(format_desc_.width, height_, 4));
+ auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), desc);
+ if(count >= frame.image_data(0).size())
+ {
+ for(int y = 0; y < height_; ++y)
+ 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);
+
+ ++i;
+ count -= static_cast<int>(frame.image_data(0).size());
+ }
+ else
+ {
+ memset(frame.image_data(0).begin(), 0, frame.image_data(0).size());
+ auto width2 = width_ % format_desc_.width;
+ for(int y = 0; y < height_; ++y)
+ std::copy_n(bytes + i * format_desc_.width*4 + y * width_*4, width2*4, frame.image_data(0).begin() + y * format_desc_.width*4);
+
+ count = 0;
+ }
+
+ frames_.push_back(core::draw_frame(std::move(frame)));
+ }
+
+ std::reverse(frames_.begin(), frames_.end());
+
+ // Set the relative positions of the image fragments.
+ for (size_t n = 0; n < frames_.size(); ++n)
+ {
+ double translation = - (static_cast<double>(n) + 1.0);
+ frames_[n].transform().image_transform.fill_translation[0] = translation;
+ }
+ }
+
+ CASPAR_LOG(info) << print() << L" Initialized";
+ }
+
+ std::vector<core::draw_frame> get_visible()
+ {
+ std::vector<core::draw_frame> result;
+ result.reserve(frames_.size());
+
+ BOOST_FOREACH(auto& frame, frames_)
+ {
+ auto& fill_translation = frame.transform().image_transform.fill_translation;
+
+ if (width_ == format_desc_.width)
+ {
+ auto motion_offset_in_screens = (static_cast<double>(start_offset_y_) + delta_) / static_cast<double>(format_desc_.height);
+ auto vertical_offset = fill_translation[1] + motion_offset_in_screens;
+
+ if (vertical_offset < -1.0 || vertical_offset > 1.0)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ auto motion_offset_in_screens = (static_cast<double>(start_offset_x_) + delta_) / static_cast<double>(format_desc_.width);
+ auto horizontal_offset = fill_translation[0] + motion_offset_in_screens;
+
+ if (horizontal_offset < -1.0 || horizontal_offset > 1.0)
+ {
+ continue;
+ }
+ }
+
+ result.push_back(frame);
+ }
+
+ return std::move(result);
+ }
+
+ // frame_producer
+ core::draw_frame render_frame(bool allow_eof)
+ {
+ if(frames_.empty())
+ return core::draw_frame::empty();
+
+ core::draw_frame result(get_visible());
+ auto& fill_translation = result.transform().image_transform.fill_translation;
+
+ if (width_ == format_desc_.width)
+ {
+ if (static_cast<size_t>(std::abs(delta_)) >= height_ + format_desc_.height && allow_eof)
+ return core::draw_frame::empty();
+
+ fill_translation[1] =
+ static_cast<double>(start_offset_y_) / static_cast<double>(format_desc_.height)
+ + delta_ / static_cast<double>(format_desc_.height);
+ }
+ else
+ {
+ if (static_cast<size_t>(std::abs(delta_)) >= width_ + format_desc_.width && allow_eof)
+ return core::draw_frame::empty();
+
+ fill_translation[0] =
+ static_cast<double>(start_offset_x_) / static_cast<double>(format_desc_.width)
+ + (delta_) / static_cast<double>(format_desc_.width);
+ }
+
+ return result;
+ }
+
+ core::draw_frame render_frame(bool allow_eof, bool advance_delta)
+ {
+ auto result = render_frame(allow_eof);
+
+ if (advance_delta)
+ {
+ advance();
+ }
+
+ return result;
+ }
+
+ void advance()
+ {
+ delta_ += speed_;
+ }
+
+ core::draw_frame receive_impl() override
+ {
+ core::draw_frame result;
+
+ if (format_desc_.field_mode == core::field_mode::progressive || progressive_)
+ {
+ result = render_frame(true, true);
+ }
+ else
+ {
+ auto field1 = render_frame(true, true);
+ auto field2 = render_frame(true, false);
+
+ if (field1 != core::draw_frame::empty() && field2 == core::draw_frame::empty())
+ {
+ field2 = render_frame(false, true);
+ }
+ else
+ {
+ advance();
+ }
+
+ result = core::draw_frame::interlace(field1, field2, format_desc_.field_mode);
+ }
+
+ event_subject_ << monitor::event("file/path") % filename_
+ << monitor::event("delta") % delta_
+ << monitor::event("speed") % speed_;
+
+ return result;
+ }
+
+ std::wstring print() const override
+ {
+ return L"image_scroll_producer[" + filename_ + L"]";
+ }
+
+ std::wstring name() const override
+ {
+ return L"image-scroll";
+ }
+
+ boost::property_tree::wptree info() const override
+ {
+ boost::property_tree::wptree info;
+ info.add(L"type", L"image-scroll");
+ info.add(L"filename", filename_);
+ return info;
+ }
+
+ uint32_t nb_frames() const override
+ {
+ if(width_ == format_desc_.width)
+ {
+ auto length = (height_ + format_desc_.height * 2);
+ return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));
+ }
+ else
+ {
+ auto length = (width_ + format_desc_.width * 2);
+ return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));
+ }
+ }
+
+ void subscribe(const monitor::observable::observer_ptr& o) override
+ {
+ return event_subject_.subscribe(o);
+ }
+
+ void unsubscribe(const monitor::observable::observer_ptr& o) override
+ {
+ return event_subject_.unsubscribe(o);
+ }
+};
+
+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)
+{
+ 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");
+ std::wstring filename = env::media_folder() + L"\\" + params[0];
+
+ auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool
+ {
+ return boost::filesystem::is_regular_file(boost::filesystem::path(filename).replace_extension(ex));
+ });
+
+ if(ext == extensions.end())
+ return core::frame_producer::empty();
+
+ double speed = 0.0;
+ double duration = 0.0;
+ auto speed_it = std::find(params.begin(), params.end(), L"SPEED");
+ if(speed_it != params.end())
+ {
+ if(++speed_it != params.end())
+ speed = boost::lexical_cast<double>(*speed_it);
+ }
+
+ if (speed == 0)
+ {
+ auto duration_it = std::find(params.begin(), params.end(), L"DURATION");
+
+ if (duration_it != params.end() && ++duration_it != params.end())
+ {
+ duration = boost::lexical_cast<double>(*duration_it);
+ }
+ }
+
+ if(speed == 0 && duration == 0)
+ return core::frame_producer::empty();
+
+ int motion_blur_px = 0;
+ auto blur_it = std::find(params.begin(), params.end(), L"BLUR");
+ if (blur_it != params.end() && ++blur_it != params.end())
+ {
+ motion_blur_px = boost::lexical_cast<int>(*blur_it);
+ }
+
+ bool premultiply_with_alpha = std::find(params.begin(), params.end(), L"PREMULTIPLY") != params.end();
+ bool progressive = std::find(params.begin(), params.end(), L"PROGRESSIVE") != params.end();
+
+ return core::create_destroy_proxy(spl::make_shared<image_scroll_producer>(
+ frame_factory,
+ format_desc,
+ filename + *ext,
+ -speed,
+ -duration,
+ motion_blur_px,
+ premultiply_with_alpha,
+ progressive));
+}
+
+}}
\ No newline at end of file