]> git.sesse.net Git - casparcg/blob - modules/image/producer/image_scroll_producer.cpp
Draft of interaction with producers and scene_producer
[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
44 #include <boost/assign.hpp>
45 #include <boost/filesystem.hpp>
46 #include <boost/foreach.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 using namespace boost::assign;
55
56 namespace caspar { namespace image {
57                 
58 struct image_scroll_producer : public core::frame_producer_base
59 {       
60         monitor::basic_subject                  event_subject_;
61
62         const std::wstring                              filename_;
63         std::vector<core::draw_frame>   frames_;
64         core::video_format_desc                 format_desc_;
65         int                                                             width_;
66         int                                                             height_;
67         core::constraints                               constraints_;
68
69         double                                                  delta_;
70         double                                                  speed_;
71
72         int                                                             start_offset_x_;
73         int                                                             start_offset_y_;
74         bool                                                    progressive_;
75         
76         explicit image_scroll_producer(
77                 const spl::shared_ptr<core::frame_factory>& frame_factory, 
78                 const core::video_format_desc& format_desc, 
79                 const std::wstring& filename, 
80                 double speed,
81                 double duration,
82                 int motion_blur_px = 0,
83                 bool premultiply_with_alpha = false,
84                 bool progressive = false)
85                 : filename_(filename)
86                 , delta_(0)
87                 , format_desc_(format_desc)
88                 , speed_(speed)
89                 , start_offset_x_(0)
90                 , start_offset_y_(0)
91                 , progressive_(progressive)
92         {
93                 auto bitmap = load_image(filename_);
94                 FreeImage_FlipVertical(bitmap.get());
95
96                 width_  = FreeImage_GetWidth(bitmap.get());
97                 height_ = FreeImage_GetHeight(bitmap.get());
98                 constraints_.width.set(width_);
99                 constraints_.height.set(height_);
100
101                 bool vertical = width_ == format_desc_.width;
102                 bool horizontal = height_ == format_desc_.height;
103
104                 if (!vertical && !horizontal)
105                         BOOST_THROW_EXCEPTION(
106                                 caspar::invalid_argument() << msg_info("Neither width nor height matched the video resolution"));
107
108                 if (vertical)
109                 {
110                         if (duration != 0.0)
111                         {
112                                 double total_num_pixels = format_desc_.height * 2 + height_;
113
114                                 speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));
115
116                                 if (std::abs(speed_) > 1.0)
117                                         speed_ = std::ceil(speed_);
118                         }
119
120                         if (speed_ < 0.0)
121                         {
122                                 start_offset_y_ = height_ + format_desc_.height;
123                         }
124                 }
125                 else
126                 {
127                         if (duration != 0.0)
128                         {
129                                 double total_num_pixels = format_desc_.width * 2 + width_;
130
131                                 speed_ = total_num_pixels / (duration * format_desc_.fps * static_cast<double>(format_desc_.field_count));
132
133                                 if (std::abs(speed_) > 1.0)
134                                         speed_ = std::ceil(speed_);
135                         }
136
137                         if (speed_ > 0.0)
138                                 start_offset_x_ = format_desc_.width - (width_ % format_desc_.width);
139                         else
140                                 start_offset_x_ = format_desc_.width - (width_ % format_desc_.width) + width_ + format_desc_.width;
141                 }
142
143                 auto bytes = FreeImage_GetBits(bitmap.get());
144                 auto count = width_*height_*4;
145                 image_view<bgra_pixel> original_view(bytes, width_, height_);
146
147                 if (premultiply_with_alpha)
148                         premultiply(original_view);
149
150                 boost::scoped_array<uint8_t> blurred_copy;
151
152                 if (motion_blur_px > 0)
153                 {
154                         double angle = 3.14159265 / 2; // Up
155
156                         if (horizontal && speed_ < 0)
157                                 angle *= 2; // Left
158                         else if (vertical && speed > 0)
159                                 angle *= 3; // Down
160                         else if (horizontal && speed  > 0)
161                                 angle = 0.0; // Right
162
163                         blurred_copy.reset(new uint8_t[count]);
164                         image_view<bgra_pixel> blurred_view(blurred_copy.get(), width_, height_);
165                         core::tweener blur_tweener(L"easeInQuad");
166                         blur(original_view, blurred_view, angle, motion_blur_px, blur_tweener);
167                         bytes = blurred_copy.get();
168                         bitmap.reset();
169                 }
170
171                 if (vertical)
172                 {
173                         int n = 1;
174
175                         while(count > 0)
176                         {
177                                 core::pixel_format_desc desc = core::pixel_format::bgra;
178                                 desc.planes.push_back(core::pixel_format_desc::plane(width_, format_desc_.height, 4));
179                                 auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), desc);
180
181                                 if(count >= frame.image_data(0).size())
182                                 {       
183                                         std::copy_n(bytes + count - frame.image_data(0).size(), frame.image_data(0).size(), frame.image_data(0).begin());
184                                         count -= static_cast<int>(frame.image_data(0).size());
185                                 }
186                                 else
187                                 {
188                                         memset(frame.image_data(0).begin(), 0, frame.image_data(0).size());     
189                                         std::copy_n(bytes, count, frame.image_data(0).begin() + format_desc_.size - count);
190                                         count = 0;
191                                 }
192
193                                 core::draw_frame draw_frame(std::move(frame));
194
195                                 // Set the relative position to the other image fragments
196                                 draw_frame.transform().image_transform.fill_translation[1] = - n++;
197
198                                 frames_.push_back(draw_frame);
199                         }
200                 }
201                 else if (horizontal)
202                 {
203                         int i = 0;
204                         while(count > 0)
205                         {
206                                 core::pixel_format_desc desc = core::pixel_format::bgra;
207                                 desc.planes.push_back(core::pixel_format_desc::plane(format_desc_.width, height_, 4));
208                                 auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), desc);
209                                 if(count >= frame.image_data(0).size())
210                                 {       
211                                         for(int y = 0; y < height_; ++y)
212                                                 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);
213                                         
214                                         ++i;
215                                         count -= static_cast<int>(frame.image_data(0).size());
216                                 }
217                                 else
218                                 {
219                                         memset(frame.image_data(0).begin(), 0, frame.image_data(0).size());     
220                                         auto width2 = width_ % format_desc_.width;
221                                         for(int y = 0; y < height_; ++y)
222                                                 std::copy_n(bytes + i * format_desc_.width*4 + y * width_*4, width2*4, frame.image_data(0).begin() + y * format_desc_.width*4);
223
224                                         count = 0;
225                                 }
226                         
227                                 frames_.push_back(core::draw_frame(std::move(frame)));
228                         }
229
230                         std::reverse(frames_.begin(), frames_.end());
231
232                         // Set the relative positions of the image fragments.
233                         for (size_t n = 0; n < frames_.size(); ++n)
234                         {
235                                 double translation = - (static_cast<double>(n) + 1.0);
236                                 frames_[n].transform().image_transform.fill_translation[0] = translation;
237                         }
238                 }
239
240                 CASPAR_LOG(info) << print() << L" Initialized";
241         }
242
243         std::vector<core::draw_frame> get_visible()
244         {
245                 std::vector<core::draw_frame> result;
246                 result.reserve(frames_.size());
247
248                 BOOST_FOREACH(auto& frame, frames_)
249                 {
250                         auto& fill_translation = frame.transform().image_transform.fill_translation;
251
252                         if (width_ == format_desc_.width)
253                         {
254                                 auto motion_offset_in_screens = (static_cast<double>(start_offset_y_) + delta_) / static_cast<double>(format_desc_.height);
255                                 auto vertical_offset = fill_translation[1] + motion_offset_in_screens;
256
257                                 if (vertical_offset < -1.0 || vertical_offset > 1.0)
258                                 {
259                                         continue;
260                                 }
261                         }
262                         else
263                         {
264                                 auto motion_offset_in_screens = (static_cast<double>(start_offset_x_) + delta_) / static_cast<double>(format_desc_.width);
265                                 auto horizontal_offset = fill_translation[0] + motion_offset_in_screens;
266
267                                 if (horizontal_offset < -1.0 || horizontal_offset > 1.0)
268                                 {
269                                         continue;
270                                 }
271                         }
272
273                         result.push_back(frame);
274                 }
275
276                 return std::move(result);
277         }
278         
279         // frame_producer
280         core::draw_frame render_frame(bool allow_eof)
281         {
282                 if(frames_.empty())
283                         return core::draw_frame::empty();
284                 
285                 core::draw_frame result(get_visible());
286                 auto& fill_translation = result.transform().image_transform.fill_translation;
287
288                 if (width_ == format_desc_.width)
289                 {
290                         if (static_cast<size_t>(std::abs(delta_)) >= height_ + format_desc_.height && allow_eof)
291                                 return core::draw_frame::empty();
292
293                         fill_translation[1] = 
294                                 static_cast<double>(start_offset_y_) / static_cast<double>(format_desc_.height)
295                                 + delta_ / static_cast<double>(format_desc_.height);
296                 }
297                 else
298                 {
299                         if (static_cast<size_t>(std::abs(delta_)) >= width_ + format_desc_.width && allow_eof)
300                                 return core::draw_frame::empty();
301
302                         fill_translation[0] = 
303                                 static_cast<double>(start_offset_x_) / static_cast<double>(format_desc_.width)
304                                 + (delta_) / static_cast<double>(format_desc_.width);
305                 }
306
307                 return result;
308         }
309
310         core::draw_frame render_frame(bool allow_eof, bool advance_delta)
311         {
312                 auto result = render_frame(allow_eof);
313
314                 if (advance_delta)
315                 {
316                         advance();
317                 }
318
319                 return result;
320         }
321
322         void advance()
323         {
324                 delta_ += speed_;
325         }
326
327         core::draw_frame receive_impl() override
328         {
329                 core::draw_frame result;
330
331                 if (format_desc_.field_mode == core::field_mode::progressive || progressive_)
332                 {
333                         result = render_frame(true, true);
334                 }
335                 else
336                 {
337                         auto field1 = render_frame(true, true);
338                         auto field2 = render_frame(true, false);
339
340                         if (field1 != core::draw_frame::empty() && field2 == core::draw_frame::empty())
341                         {
342                                 field2 = render_frame(false, true);
343                         }
344                         else
345                         {
346                                 advance();
347                         }
348
349                         result = core::draw_frame::interlace(field1, field2, format_desc_.field_mode);
350                 }
351                 
352                 event_subject_ << monitor::event("file/path") % filename_
353                                            << monitor::event("delta") % delta_ 
354                                            << monitor::event("speed") % speed_;
355
356                 return result;
357         }
358
359         core::constraints& pixel_constraints() override
360         {
361                 return constraints_;
362         }
363                                 
364         std::wstring print() const override
365         {
366                 return L"image_scroll_producer[" + filename_ + L"]";
367         }
368
369         std::wstring name() const override
370         {
371                 return L"image-scroll";
372         }
373
374         boost::property_tree::wptree info() const override
375         {
376                 boost::property_tree::wptree info;
377                 info.add(L"type", L"image-scroll");
378                 info.add(L"filename", filename_);
379                 return info;
380         }
381
382         uint32_t nb_frames() const override
383         {
384                 if(width_ == format_desc_.width)
385                 {
386                         auto length = (height_ + format_desc_.height * 2);
387                         return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));
388                 }
389                 else
390                 {
391                         auto length = (width_ + format_desc_.width * 2);
392                         return static_cast<uint32_t>(length / std::abs(speed_));// + length % std::abs(delta_));
393                 }
394         }
395
396         void subscribe(const monitor::observable::observer_ptr& o) override                                                                                                                     
397         {
398                 return event_subject_.subscribe(o);
399         }
400
401         void unsubscribe(const monitor::observable::observer_ptr& o) override           
402         {
403                 return event_subject_.unsubscribe(o);
404         }
405 };
406
407 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)
408 {
409         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");
410         std::wstring filename = env::media_folder() + L"\\" + params[0];
411         
412         auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool
413                 {                                       
414                         return boost::filesystem::is_regular_file(boost::filesystem::path(filename).replace_extension(ex));
415                 });
416
417         if(ext == extensions.end())
418                 return core::frame_producer::empty();
419         
420         double speed = 0.0;
421         double duration = 0.0;
422         auto speed_it = std::find(params.begin(), params.end(), L"SPEED");
423         if(speed_it != params.end())
424         {
425                 if(++speed_it != params.end())
426                         speed = boost::lexical_cast<double>(*speed_it);
427         }
428
429         if (speed == 0)
430         {
431                 auto duration_it = std::find(params.begin(), params.end(), L"DURATION");
432
433                 if (duration_it != params.end() && ++duration_it != params.end())
434                 {
435                         duration = boost::lexical_cast<double>(*duration_it);
436                 }
437         }
438
439         if(speed == 0 && duration == 0)
440                 return core::frame_producer::empty();
441
442         int motion_blur_px = 0;
443         auto blur_it = std::find(params.begin(), params.end(), L"BLUR");
444         if (blur_it != params.end() && ++blur_it != params.end())
445         {
446                 motion_blur_px = boost::lexical_cast<int>(*blur_it);
447         }
448
449         bool premultiply_with_alpha = std::find(params.begin(), params.end(), L"PREMULTIPLY") != params.end();
450         bool progressive = std::find(params.begin(), params.end(), L"PROGRESSIVE") != params.end();
451
452         return core::create_destroy_proxy(spl::make_shared<image_scroll_producer>(
453                 frame_factory, 
454                 format_desc, 
455                 filename + *ext, 
456                 -speed, 
457                 -duration, 
458                 motion_blur_px, 
459                 premultiply_with_alpha,
460                 progressive));
461 }
462
463 }}