]> git.sesse.net Git - casparcg/blobdiff - modules/image/producer/image_scroll_producer.cpp
Modified the image_scroll_producer
[casparcg] / modules / image / producer / image_scroll_producer.cpp
index 6e888e59fbee8769761c63b296eea2137bb2b406..37dbf115971f3e7695805a2f6f250ca7158ba448 100644 (file)
@@ -1,28 +1,30 @@
 /*\r
-* copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
 *\r
-*  This file is part of CasparCG.\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
+* 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
+* 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
-// TODO: Refactor.\r
-// TODO: Looping.\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/mixer/write_frame.h>\r
 \r
 #include <common/env.h>\r
+#include <common/log/log.h>\r
 #include <common/memory/memclr.h>\r
 #include <common/exception/exceptions.h>\r
+#include <common/utility/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/gil/gil_all.hpp>\r
 \r
 #include <algorithm>\r
 #include <array>\r
+#include <boost/math/special_functions/round.hpp>\r
+#include <boost/scoped_array.hpp>\r
 \r
 using namespace boost::assign;\r
 \r
-namespace caspar {\r
-               \r
+namespace caspar { namespace image {\r
+\r
 struct image_scroll_producer : public core::frame_producer\r
 {      \r
        const std::wstring                                                      filename_;\r
@@ -55,21 +63,29 @@ struct image_scroll_producer : public core::frame_producer
        size_t                                                                          width_;\r
        size_t                                                                          height_;\r
 \r
-       int                                                                                     delta_;\r
-       int                                                                                     speed_;\r
+       double                                                                          delta_;\r
+       double                                                                          speed_;\r
 \r
-       std::array<double, 2>                                           start_offset_;\r
+       int                                                                                     start_offset_x_;\r
+       int                                                                                     start_offset_y_;\r
 \r
        safe_ptr<core::basic_frame>                                     last_frame_;\r
        \r
-       explicit image_scroll_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::wstring& filename, int speed) \r
+       explicit image_scroll_producer(\r
+               const safe_ptr<core::frame_factory>& frame_factory, \r
+               const std::wstring& filename, \r
+               double speed,\r
+               double duration,\r
+               int motion_blur_px = 0,\r
+               bool premultiply_with_alpha = false) \r
                : filename_(filename)\r
                , delta_(0)\r
                , format_desc_(frame_factory->get_video_format_desc())\r
                , speed_(speed)\r
                , last_frame_(core::basic_frame::empty())\r
        {\r
-               start_offset_.assign(0.0);\r
+               start_offset_x_ = 0;\r
+               start_offset_y_ = 0;\r
 \r
                auto bitmap = load_image(filename_);\r
                FreeImage_FlipVertical(bitmap.get());\r
@@ -77,14 +93,85 @@ struct image_scroll_producer : public core::frame_producer
                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
+                               start_offset_y_ = height_ + format_desc_.height;\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
                int 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
+                       tweener_t blur_tweener = get_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(height_ > format_desc_.height)\r
+               if (vertical)\r
                {\r
+                       int n = 1;\r
+\r
                        while(count > 0)\r
                        {\r
-                               auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), width_, format_desc_.height);\r
+                               core::pixel_format_desc desc;\r
+                               desc.pix_fmt = 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().size())\r
                                {       \r
                                        std::copy_n(bytes + count - frame->image_data().size(), frame->image_data().size(), frame->image_data().begin());\r
@@ -96,24 +183,24 @@ struct image_scroll_producer : public core::frame_producer
                                        std::copy_n(bytes, count, frame->image_data().begin() + format_desc_.size - count);\r
                                        count = 0;\r
                                }\r
-                       \r
+\r
                                frame->commit();\r
                                frames_.push_back(frame);\r
+\r
+                               // Set the relative position to the other image fragments\r
+                               frame->get_frame_transform().fill_translation[1] = - n++;\r
                        }\r
-                       \r
-                       if(speed_ < 0.0)\r
-                       {\r
-                               auto offset = format_desc_.height - (height_ % format_desc_.height);\r
-                               auto offset2 = offset * 0.5/static_cast<double>(format_desc_.height);\r
-                               start_offset_[1] = (std::ceil(static_cast<double>(height_) / static_cast<double>(format_desc_.height)) + 1.0) * 0.5 - offset2;// - 1.5;\r
-                       }\r
+\r
                }\r
-               else\r
+               else if (horizontal)\r
                {\r
                        int i = 0;\r
                        while(count > 0)\r
                        {\r
-                               auto frame = frame_factory->create_frame(reinterpret_cast<void*>(rand()), format_desc_.width, height_);\r
+                               core::pixel_format_desc desc;\r
+                               desc.pix_fmt = 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().size())\r
                                {       \r
                                        for(size_t y = 0; y < height_; ++y)\r
@@ -138,80 +225,162 @@ struct image_scroll_producer : public core::frame_producer
 \r
                        std::reverse(frames_.begin(), frames_.end());\r
 \r
-                       if(speed_ > 0.0)\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]->get_frame_transform().fill_translation[0] = translation;\r
+                       }\r
+               }\r
+\r
+               CASPAR_LOG(info) << print() << L" Initialized";\r
+       }\r
+\r
+       std::vector<safe_ptr<core::basic_frame>> get_visible()\r
+       {\r
+               std::vector<safe_ptr<core::basic_frame>> result;\r
+               result.reserve(frames_.size());\r
+\r
+               BOOST_FOREACH(auto& frame, frames_)\r
+               {\r
+                       auto& fill_translation = frame->get_frame_transform().fill_translation;\r
+\r
+                       if (width_ == format_desc_.width)\r
                        {\r
-                               auto offset = format_desc_.width - (width_ % format_desc_.width);\r
-                               start_offset_[0] = offset * 0.5/static_cast<double>(format_desc_.width);\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
-                               start_offset_[0] = (std::ceil(static_cast<double>(width_) / static_cast<double>(format_desc_.width)) + 1.0) * 0.5;// - 1.5;\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
 \r
-       virtual safe_ptr<core::basic_frame> receive(int)\r
-       {               \r
-               delta_ += speed_;\r
-\r
+       safe_ptr<core::basic_frame> render_frame(bool allow_eof)\r
+       {\r
                if(frames_.empty())\r
                        return core::basic_frame::eof();\r
                \r
-               if(height_ > format_desc_.height)\r
+               auto result = make_safe<core::basic_frame>(get_visible());\r
+               auto& fill_translation = result->get_frame_transform().fill_translation;\r
+\r
+               if (width_ == format_desc_.width)\r
                {\r
-                       if(static_cast<size_t>(std::abs(delta_)) >= height_ - format_desc_.height)\r
+                       if (static_cast<size_t>(std::abs(delta_)) >= height_ + format_desc_.height && allow_eof)\r
                                return core::basic_frame::eof();\r
 \r
-                       for(size_t n = 0; n < frames_.size(); ++n)\r
-                       {\r
-                               frames_[n]->get_frame_transform().fill_translation[0] = start_offset_[0];\r
-                               frames_[n]->get_frame_transform().fill_translation[1] = start_offset_[1] - (n+1) + delta_ * 0.5/static_cast<double>(format_desc_.height);\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)\r
+                       if (static_cast<size_t>(std::abs(delta_)) >= width_ + format_desc_.width && allow_eof)\r
                                return core::basic_frame::eof();\r
 \r
-                       for(size_t n = 0; n < frames_.size(); ++n)\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
+       safe_ptr<core::basic_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
+       virtual safe_ptr<core::basic_frame> receive(int) override\r
+       {\r
+               if (format_desc_.field_mode == core::field_mode::progressive)\r
+               {\r
+                       return last_frame_ = 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::basic_frame::eof() && field2 == core::basic_frame::eof())\r
                        {\r
-                               frames_[n]->get_frame_transform().fill_translation[0] = start_offset_[0] - (n+1) + delta_ * 0.5/static_cast<double>(format_desc_.width);                                \r
-                               frames_[n]->get_frame_transform().fill_translation[1] = start_offset_[1];\r
+                               field2 = render_frame(false, true);\r
                        }\r
-               }\r
+                       else\r
+                       {\r
+                               advance();\r
+                       }\r
+\r
+                       last_frame_ = field2;\r
 \r
-               return last_frame_ = core::basic_frame(frames_);\r
+                       return core::basic_frame::interlace(field1, field2, format_desc_.field_mode);\r
+               }\r
        }\r
 \r
-       virtual safe_ptr<core::basic_frame> last_frame() const\r
+       virtual safe_ptr<core::basic_frame> last_frame() const override\r
        {\r
                return last_frame_;\r
        }\r
                \r
-       virtual std::wstring print() const\r
+       virtual std::wstring print() const override\r
        {\r
                return L"image_scroll_producer[" + filename_ + L"]";\r
        }\r
 \r
-       virtual int64_t nb_frames() const \r
+       virtual boost::property_tree::wptree info() const override\r
        {\r
-               if(height_ > format_desc_.height)\r
+               boost::property_tree::wptree info;\r
+               info.add(L"type", L"image-scroll-producer");\r
+               info.add(L"filename", filename_);\r
+               return info;\r
+       }\r
+\r
+       virtual uint32_t nb_frames() const override\r
+       {\r
+               if(width_ == format_desc_.width)\r
                {\r
-                       auto length = (height_ - format_desc_.height);\r
-                       return length/std::abs(speed_);// + length % std::abs(delta_));\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);\r
-                       auto result = length/std::abs(speed_);// + length % std::abs(delta_));\r
-                       return result;\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
 \r
-safe_ptr<core::frame_producer> create_image_scroll_producer(const safe_ptr<core::frame_factory>& frame_factory, const std::vector<std::wstring>& params)\r
+safe_ptr<core::frame_producer> create_scroll_producer(const safe_ptr<core::frame_factory>& frame_factory, 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
@@ -224,19 +393,44 @@ safe_ptr<core::frame_producer> create_image_scroll_producer(const safe_ptr<core:
        if(ext == extensions.end())\r
                return core::frame_producer::empty();\r
        \r
-       size_t speed = 0;\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<int>(*speed_it);\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)\r
+       if(speed == 0 && duration == 0)\r
                return core::frame_producer::empty();\r
 \r
-       return make_safe<image_scroll_producer>(frame_factory, filename + L"." + *ext, speed);\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
 \r
+       return create_producer_print_proxy(make_safe<image_scroll_producer>(\r
+               frame_factory, \r
+               filename + L"." + *ext, \r
+               -speed, \r
+               -duration, \r
+               motion_blur_px, \r
+               premultiply_with_alpha));\r
+}\r
 \r
-}
\ No newline at end of file
+}}
\ No newline at end of file