1 #include "../../StdAfx.h"
\r
3 #include "image_scroll_producer.h"
\r
5 #include "image_loader.h"
\r
7 #include "../../frame/gpu_frame.h"
\r
8 #include "../../frame/gpu_composite_frame.h"
\r
9 #include "../../frame/frame_format.h"
\r
10 #include "../../frame/frame_factory.h"
\r
11 #include "../../server.h"
\r
12 #include "../../../common/utility/find_file.h"
\r
13 #include "../../../common/utility/memory.h"
\r
15 #include <tbb/parallel_for.h>
\r
16 #include <tbb/parallel_invoke.h>
\r
17 #include <tbb/scalable_allocator.h>
\r
19 #include <boost/assign.hpp>
\r
20 #include <boost/algorithm/string/case_conv.hpp>
\r
22 using namespace boost::assign;
\r
24 namespace caspar { namespace core { namespace image{
\r
28 Up, Down, Left, Right
\r
31 struct image_scroll_producer : public frame_producer
\r
33 static const int DEFAULT_SCROLL_SPEED = 50;
\r
35 image_scroll_producer(const std::wstring& filename, const std::vector<std::wstring>& params, const frame_format_desc& format_desc)
\r
36 : format_desc_(format_desc), speed_(image_scroll_producer::DEFAULT_SCROLL_SPEED), direction_(direction::Up), offset_(0)
\r
38 load_and_pad_image(filename);
\r
40 auto pos = filename.find_last_of(L'_');
\r
41 if(pos != std::wstring::npos && pos + 1 < filename.size())
\r
43 std::wstring speedStr = filename.substr(pos + 1);
\r
44 pos = speedStr.find_first_of(L'.');
\r
45 if(pos != std::wstring::npos)
\r
47 speedStr = speedStr.substr(0, pos);
\r
48 speed_ = common::lexical_cast_or_default<int>(speedStr, image_scroll_producer::DEFAULT_SCROLL_SPEED);
\r
52 loop_ = std::find(params.begin(), params.end(), L"LOOP") != params.end();
\r
54 if(image_width_ - format_desc.width > image_height_ - format_desc_.height)
\r
55 direction_ = speed_ < 0 ? direction::Right : direction::Left;
\r
57 direction_ = speed_ < 0 ? direction::Down : direction::Up;
\r
59 if (direction_ == direction::Down)
\r
60 offset_ = image_height_ - format_desc_.height;
\r
61 else if (direction_ == direction::Right)
\r
62 offset_ = image_width_ - format_desc_.width;
\r
64 speed_ = static_cast<int>(abs(static_cast<double>(speed_) / format_desc.fps));
\r
67 ~image_scroll_producer()
\r
70 factory_->release_frames(this);
\r
73 void load_and_pad_image(const std::wstring& filename)
\r
75 auto pBitmap = load_image(filename);
\r
77 size_t width = FreeImage_GetWidth(pBitmap.get());
\r
78 size_t height = FreeImage_GetHeight(pBitmap.get());
\r
80 image_width_ = std::max(width, format_desc_.width);
\r
81 image_height_ = std::max(height, format_desc_.height);
\r
83 image_ = std::shared_ptr<unsigned char>(static_cast<unsigned char*>(scalable_aligned_malloc(image_width_*image_height_*4, 16)));
\r
84 common::clear(image_.get(), image_width_*image_height_*4);
\r
86 unsigned char* pBits = FreeImage_GetBits(pBitmap.get());
\r
88 for (size_t i = 0; i < height; ++i)
\r
89 common::aligned_parallel_memcpy(&image_.get()[i * image_width_ * 4], &pBits[i* width * 4], width * 4);
\r
92 gpu_frame_ptr render_frame()
\r
94 gpu_frame_ptr frame = factory_->create_frame(format_desc_, this);
\r
95 common::clear(frame->data(), frame->size());
\r
97 const int delta_x = direction_ == direction::Left ? speed_ : -speed_;
\r
98 const int delta_y = direction_ == direction::Up ? speed_ : -speed_;
\r
100 unsigned char* frame_data = frame->data();
\r
101 unsigned char* image_data = image_.get();
\r
103 if (direction_ == direction::Up || direction_ == direction::Down)
\r
105 tbb::parallel_for(static_cast<size_t>(0), format_desc_.height, static_cast<size_t>(1), [&](size_t i)
\r
107 int srcRow = i + offset_;
\r
108 int dstInxex = i * format_desc_.width * 4;
\r
109 int srcIndex = srcRow * format_desc_.width * 4;
\r
110 int size = format_desc_.width * 4;
\r
112 memcpy(&frame_data[dstInxex], &image_data[srcIndex], size);
\r
115 offset_ += delta_y;
\r
119 tbb::parallel_for(static_cast<size_t>(0), format_desc_.height, static_cast<size_t>(1), [&](size_t i)
\r
121 int correctOffset = offset_;
\r
122 int dstIndex = i * format_desc_.width * 4;
\r
123 int srcIndex = (i * image_width_ + correctOffset) * 4;
\r
125 int stopOffset = std::min<int>(correctOffset + format_desc_ .width, image_width_);
\r
126 int size = (stopOffset - correctOffset) * 4;
\r
128 memcpy(&frame_data[dstIndex], &image_data[srcIndex], size);
\r
131 offset_ += delta_x;
\r
137 gpu_frame_ptr get_frame()
\r
139 if(format_desc_.mode != video_mode::progressive)
\r
141 gpu_frame_ptr frame1;
\r
142 gpu_frame_ptr frame2;
\r
143 tbb::parallel_invoke([&]{ frame1 = render_frame(); }, [&]{ frame2 = render_frame(); });
\r
144 return gpu_composite_frame::interlace(frame1, frame2, format_desc_.mode);
\r
147 return render_frame();
\r
150 void initialize(const frame_factory_ptr& factory)
\r
152 factory_ = factory;
\r
155 const frame_format_desc& get_frame_format_desc() const { return format_desc_; }
\r
161 direction direction_;
\r
163 tbb::atomic<bool> loop_;
\r
164 std::shared_ptr<unsigned char> image_;
\r
165 frame_format_desc format_desc_;
\r
167 frame_factory_ptr factory_;
\r
170 frame_producer_ptr create_image_scroll_producer(const std::vector<std::wstring>& params, const frame_format_desc& format_desc)
\r
172 std::wstring filename = params[0];
\r
173 auto result_filename = common::find_file(server::media_folder() + filename, list_of(L"spng")(L"stga")(L"sbmp")(L"sjpg")(L"sjpeg"));
\r
174 if(!result_filename.empty())
\r
175 return std::make_shared<image_scroll_producer>(result_filename, params, format_desc);
\r