]> git.sesse.net Git - casparcg/blob - modules/image/producer/image_scroll_producer.cpp
* Compiled ffmpeg for Linux as similary to Zeranoe Windows build as possible.
[casparcg] / modules / image / producer / image_scroll_producer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 * Author: Helge Norberg, helge.norberg@svt.se
21 */
22
23 #include "image_scroll_producer.h"
24
25 #include "../util/image_loader.h"
26 #include "../util/image_view.h"
27 #include "../util/image_algorithms.h"
28
29 #include <core/video_format.h>
30
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>
37
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>
45
46 #include <boost/filesystem.hpp>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/property_tree/ptree.hpp>
49 #include <boost/scoped_array.hpp>
50
51 #include <algorithm>
52 #include <array>
53
54 namespace caspar { namespace image {
55                 
56 struct image_scroll_producer : public core::frame_producer_base
57 {       
58         core::monitor::subject                  monitor_subject_;
59
60         const std::wstring                              filename_;
61         std::vector<core::draw_frame>   frames_;
62         core::video_format_desc                 format_desc_;
63         int                                                             width_;
64         int                                                             height_;
65         core::constraints                               constraints_;
66
67         double                                                  delta_                          = 0.0;
68         double                                                  speed_;
69
70         int                                                             start_offset_x_         = 0;
71         int                                                             start_offset_y_         = 0;
72         bool                                                    progressive_;
73         
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,
78                         double speed,
79                         double duration,
80                         int motion_blur_px = 0,
81                         bool premultiply_with_alpha = false,
82                         bool progressive = false)
83                 : filename_(filename)
84                 , format_desc_(format_desc)
85                 , speed_(speed)
86                 , progressive_(progressive)
87         {
88                 auto bitmap = load_image(filename_);
89                 FreeImage_FlipVertical(bitmap.get());
90
91                 width_  = FreeImage_GetWidth(bitmap.get());
92                 height_ = FreeImage_GetHeight(bitmap.get());
93                 constraints_.width.set(width_);
94                 constraints_.height.set(height_);
95
96                 bool vertical = width_ == format_desc_.width;
97                 bool horizontal = height_ == format_desc_.height;
98
99                 if (!vertical && !horizontal)
100                         BOOST_THROW_EXCEPTION(
101                                 caspar::invalid_argument() << msg_info("Neither width nor height matched the video resolution"));
102
103                 if (vertical)
104                 {
105                         if (duration != 0.0)
106                         {
107                                 double total_num_pixels = format_desc_.height * 2 + height_;
108
109                                 speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));
110
111                                 if (std::abs(speed_) > 1.0)
112                                         speed_ = std::ceil(speed_);
113                         }
114
115                         if (speed_ < 0.0)
116                         {
117                                 start_offset_y_ = height_ + format_desc_.height;
118                         }
119                 }
120                 else
121                 {
122                         if (duration != 0.0)
123                         {
124                                 double total_num_pixels = format_desc_.width * 2 + width_;
125
126                                 speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));
127
128                                 if (std::abs(speed_) > 1.0)
129                                         speed_ = std::ceil(speed_);
130                         }
131
132                         if (speed_ > 0.0)
133                                 start_offset_x_ = format_desc_.width - (width_ % format_desc_.width);
134                         else
135                                 start_offset_x_ = format_desc_.width - (width_ % format_desc_.width) + width_ + format_desc_.width;
136                 }
137
138                 auto bytes = FreeImage_GetBits(bitmap.get());
139                 auto count = width_*height_*4;
140                 image_view<bgra_pixel> original_view(bytes, width_, height_);
141
142                 if (premultiply_with_alpha)
143                         premultiply(original_view);
144
145                 boost::scoped_array<uint8_t> blurred_copy;
146
147                 if (motion_blur_px > 0)
148                 {
149                         double angle = 3.14159265 / 2; // Up
150
151                         if (horizontal && speed_ < 0)
152                                 angle *= 2; // Left
153                         else if (vertical && speed > 0)
154                                 angle *= 3; // Down
155                         else if (horizontal && speed  > 0)
156                                 angle = 0.0; // Right
157
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();
163                         bitmap.reset();
164                 }
165
166                 if (vertical)
167                 {
168                         int n = 1;
169
170                         while(count > 0)
171                         {
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);
175
176                                 if(count >= frame.image_data(0).size())
177                                 {       
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());
180                                 }
181                                 else
182                                 {
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);
185                                         count = 0;
186                                 }
187
188                                 core::draw_frame draw_frame(std::move(frame));
189
190                                 // Set the relative position to the other image fragments
191                                 draw_frame.transform().image_transform.fill_translation[1] = - n++;
192
193                                 frames_.push_back(draw_frame);
194                         }
195                 }
196                 else if (horizontal)
197                 {
198                         int i = 0;
199                         while(count > 0)
200                         {
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())
205                                 {       
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);
208                                         
209                                         ++i;
210                                         count -= static_cast<int>(frame.image_data(0).size());
211                                 }
212                                 else
213                                 {
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);
218
219                                         count = 0;
220                                 }
221                         
222                                 frames_.push_back(core::draw_frame(std::move(frame)));
223                         }
224
225                         std::reverse(frames_.begin(), frames_.end());
226
227                         // Set the relative positions of the image fragments.
228                         for (size_t n = 0; n < frames_.size(); ++n)
229                         {
230                                 double translation = - (static_cast<double>(n) + 1.0);
231                                 frames_[n].transform().image_transform.fill_translation[0] = translation;
232                         }
233                 }
234
235                 CASPAR_LOG(info) << print() << L" Initialized";
236         }
237
238         std::vector<core::draw_frame> get_visible()
239         {
240                 std::vector<core::draw_frame> result;
241                 result.reserve(frames_.size());
242
243                 for (auto& frame : frames_)
244                 {
245                         auto& fill_translation = frame.transform().image_transform.fill_translation;
246
247                         if (width_ == format_desc_.width)
248                         {
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;
251
252                                 if (vertical_offset < -1.0 || vertical_offset > 1.0)
253                                 {
254                                         continue;
255                                 }
256                         }
257                         else
258                         {
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;
261
262                                 if (horizontal_offset < -1.0 || horizontal_offset > 1.0)
263                                 {
264                                         continue;
265                                 }
266                         }
267
268                         result.push_back(frame);
269                 }
270
271                 return std::move(result);
272         }
273         
274         // frame_producer
275         core::draw_frame render_frame(bool allow_eof)
276         {
277                 if(frames_.empty())
278                         return core::draw_frame::empty();
279                 
280                 core::draw_frame result(get_visible());
281                 auto& fill_translation = result.transform().image_transform.fill_translation;
282
283                 if (width_ == format_desc_.width)
284                 {
285                         if (static_cast<size_t>(std::abs(delta_)) >= height_ + format_desc_.height && allow_eof)
286                                 return core::draw_frame::empty();
287
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);
291                 }
292                 else
293                 {
294                         if (static_cast<size_t>(std::abs(delta_)) >= width_ + format_desc_.width && allow_eof)
295                                 return core::draw_frame::empty();
296
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);
300                 }
301
302                 return result;
303         }
304
305         core::draw_frame render_frame(bool allow_eof, bool advance_delta)
306         {
307                 auto result = render_frame(allow_eof);
308
309                 if (advance_delta)
310                 {
311                         advance();
312                 }
313
314                 return result;
315         }
316
317         void advance()
318         {
319                 delta_ += speed_;
320         }
321
322         core::draw_frame receive_impl() override
323         {
324                 core::draw_frame result;
325
326                 if (format_desc_.field_mode == core::field_mode::progressive || progressive_)
327                 {
328                         result = render_frame(true, true);
329                 }
330                 else
331                 {
332                         auto field1 = render_frame(true, true);
333                         auto field2 = render_frame(true, false);
334
335                         if (field1 != core::draw_frame::empty() && field2 == core::draw_frame::empty())
336                         {
337                                 field2 = render_frame(false, true);
338                         }
339                         else
340                         {
341                                 advance();
342                         }
343
344                         result = core::draw_frame::interlace(field1, field2, format_desc_.field_mode);
345                 }
346                 
347                 monitor_subject_ << core::monitor::message("/file/path") % filename_
348                                                  << core::monitor::message("/delta") % delta_ 
349                                                  << core::monitor::message("/speed") % speed_;
350
351                 return result;
352         }
353
354         core::constraints& pixel_constraints() override
355         {
356                 return constraints_;
357         }
358                                 
359         std::wstring print() const override
360         {
361                 return L"image_scroll_producer[" + filename_ + L"]";
362         }
363
364         std::wstring name() const override
365         {
366                 return L"image-scroll";
367         }
368
369         boost::property_tree::wptree info() const override
370         {
371                 boost::property_tree::wptree info;
372                 info.add(L"type", L"image-scroll");
373                 info.add(L"filename", filename_);
374                 return info;
375         }
376
377         uint32_t nb_frames() const override
378         {
379                 if(width_ == format_desc_.width)
380                 {
381                         auto length = (height_ + format_desc_.height * 2);
382                         return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));
383                 }
384                 else
385                 {
386                         auto length = (width_ + format_desc_.width * 2);
387                         return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));
388                 }
389         }
390
391         core::monitor::subject& monitor_output()
392         {
393                 return monitor_subject_;
394         }
395 };
396
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)
398 {
399         static const auto extensions = {
400                 L".png",
401                 L".tga",
402                 L".bmp",
403                 L".jpg",
404                 L".jpeg",
405                 L".gif",
406                 L".tiff",
407                 L".tif",
408                 L".jp2",
409                 L".jpx",
410                 L".j2k",
411                 L".j2c"
412         };
413         std::wstring filename = env::media_folder() + params[0];
414         
415         auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool
416         {
417                 auto file = caspar::find_case_insensitive(boost::filesystem::path(filename).replace_extension(ex).wstring());
418
419                 return static_cast<bool>(file);
420         });
421
422         if(ext == extensions.end())
423                 return core::frame_producer::empty();
424         
425         double duration = 0.0;
426         double speed = get_param(L"SPEED", params, 0.0);
427
428         if (speed == 0)
429                 duration = get_param(L"DURATION", params, 0.0);
430
431         if(speed == 0 && duration == 0)
432                 return core::frame_producer::empty();
433
434         int motion_blur_px = get_param(L"BLUR", params, 0);
435
436         bool premultiply_with_alpha = contains_param(L"PREMULTIPLY", params);
437         bool progressive = contains_param(L"PROGRESSIVE", params);
438
439         return core::create_destroy_proxy(spl::make_shared<image_scroll_producer>(
440                         frame_factory,
441                         format_desc,
442                         *caspar::find_case_insensitive(filename + *ext),
443                         -speed,
444                         -duration,
445                         motion_blur_px,
446                         premultiply_with_alpha,
447                         progressive));
448 }
449
450 }}