]> git.sesse.net Git - casparcg/blobdiff - modules/image/producer/image_scroll_producer.cpp
set svn:eol-style native on .h and .cpp files
[casparcg] / modules / image / producer / image_scroll_producer.cpp
index 0a268bd73cf9c6471820588bb1832570eac6352c..097451d30165a89233f00f249b1b5350bf2116ab 100644 (file)
-/*\r
-* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
-*\r
-* This file is part of CasparCG (www.casparcg.com).\r
-*\r
-* CasparCG is free software: you can redistribute it and/or modify\r
-* it under the terms of the GNU General Public License as published by\r
-* the Free Software Foundation, either version 3 of the License, or\r
-* (at your option) any later version.\r
-*\r
-* CasparCG is distributed in the hope that it will be useful,\r
-* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-* GNU General Public License for more details.\r
-*\r
-* You should have received a copy of the GNU General Public License\r
-* 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 <core/frame/frame.h>\r
-#include <core/frame/draw_frame.h>\r
-#include <core/frame/frame_factory.h>\r
-#include <core/frame/frame_transform.h>\r
-#include <core/frame/pixel_format.h>\r
-#include <core/monitor/monitor.h>\r
-\r
-#include <common/env.h>\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
-\r
-using namespace boost::assign;\r
-\r
-namespace caspar { namespace image {\r
-               \r
-struct image_scroll_producer : public core::frame_producer_base\r
-{      \r
-       monitor::basic_subject                  event_subject_;\r
-\r
-       const std::wstring                              filename_;\r
-       std::vector<core::draw_frame>   frames_;\r
-       core::video_format_desc                 format_desc_;\r
-       int                                                             width_;\r
-       int                                                             height_;\r
-\r
-       double                                                  delta_;\r
-       double                                                  speed_;\r
-\r
-       int                                                             start_offset_x_;\r
-       int                                                             start_offset_y_;\r
-       bool                                                    progressive_;\r
-       \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
-               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 (vertical)\r
-               {\r
-                       int n = 1;\r
-\r
-                       while(count > 0)\r
-                       {\r
-                               core::pixel_format_desc desc = core::pixel_format::bgra;\r
-                               desc.planes.push_back(core::pixel_format_desc::plane(width_, format_desc_.height, 4));\r
-                               auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), desc);\r
-\r
-                               if(count >= frame.image_data(0).size())\r
-                               {       \r
-                                       std::copy_n(bytes + count - frame.image_data(0).size(), frame.image_data(0).size(), frame.image_data(0).begin());\r
-                                       count -= static_cast<int>(frame.image_data(0).size());\r
-                               }\r
-                               else\r
-                               {\r
-                                       memset(frame.image_data(0).begin(), 0, frame.image_data(0).size());     \r
-                                       std::copy_n(bytes, count, frame.image_data(0).begin() + format_desc_.size - count);\r
-                                       count = 0;\r
-                               }\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 if (horizontal)\r
-               {\r
-                       int i = 0;\r
-                       while(count > 0)\r
-                       {\r
-                               core::pixel_format_desc desc = core::pixel_format::bgra;\r
-                               desc.planes.push_back(core::pixel_format_desc::plane(format_desc_.width, height_, 4));\r
-                               auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), desc);\r
-                               if(count >= frame.image_data(0).size())\r
-                               {       \r
-                                       for(int y = 0; y < height_; ++y)\r
-                                               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);\r
-                                       \r
-                                       ++i;\r
-                                       count -= static_cast<int>(frame.image_data(0).size());\r
-                               }\r
-                               else\r
-                               {\r
-                                       memset(frame.image_data(0).begin(), 0, frame.image_data(0).size());     \r
-                                       auto width2 = width_ % format_desc_.width;\r
-                                       for(int y = 0; y < height_; ++y)\r
-                                               std::copy_n(bytes + i * format_desc_.width*4 + y * width_*4, width2*4, frame.image_data(0).begin() + y * format_desc_.width*4);\r
-\r
-                                       count = 0;\r
-                               }\r
-                       \r
-                               frames_.push_back(core::draw_frame(std::move(frame)));\r
-                       }\r
-\r
-                       std::reverse(frames_.begin(), frames_.end());\r
-\r
-                       // Set the relative positions of the image fragments.\r
-                       for (size_t n = 0; n < frames_.size(); ++n)\r
-                       {\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
-                               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
-               return std::move(result);\r
-       }\r
-       \r
-       // frame_producer\r
-       core::draw_frame render_frame(bool allow_eof)\r
-       {\r
-               if(frames_.empty())\r
-                       return core::draw_frame::empty();\r
-               \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<size_t>(std::abs(delta_)) >= height_ + format_desc_.height && allow_eof)\r
-                               return core::draw_frame::empty();\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
-                       auto field1 = render_frame(true, true);\r
-                       auto field2 = render_frame(true, false);\r
-\r
-                       if (field1 != core::draw_frame::empty() && field2 == core::draw_frame::empty())\r
-                       {\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 result;\r
-       }\r
-                               \r
-       std::wstring print() const override\r
-       {\r
-               return L"image_scroll_producer[" + filename_ + L"]";\r
-       }\r
-\r
-       std::wstring name() const override\r
-       {\r
-               return L"image-scroll";\r
-       }\r
-\r
-       boost::property_tree::wptree info() const override\r
-       {\r
-               boost::property_tree::wptree info;\r
-               info.add(L"type", L"image-scroll");\r
-               info.add(L"filename", filename_);\r
-               return info;\r
-       }\r
-\r
-       uint32_t nb_frames() const override\r
-       {\r
-               if(width_ == format_desc_.width)\r
-               {\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 * 2);\r
-                       return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));\r
-               }\r
-       }\r
-\r
-       void subscribe(const monitor::observable::observer_ptr& o) override                                                                                                                     \r
-       {\r
-               return event_subject_.subscribe(o);\r
-       }\r
-\r
-       void unsubscribe(const monitor::observable::observer_ptr& o) override           \r
-       {\r
-               return event_subject_.unsubscribe(o);\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
-       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::path(filename).replace_extension(ex));\r
-               });\r
-\r
-       if(ext == extensions.end())\r
-               return core::frame_producer::empty();\r
-       \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<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 && duration == 0)\r
-               return core::frame_producer::empty();\r
-\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
+/*
+* 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