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