]> git.sesse.net Git - casparcg/blobdiff - modules/image/producer/image_scroll_producer.cpp
Merged image_scroll_producer changes to 2.1
[casparcg] / modules / image / producer / image_scroll_producer.cpp
index 47953da02f96e52ead47ca5d1c648459b7e5ba89..0a268bd73cf9c6471820588bb1832570eac6352c 100644 (file)
 * 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
 \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 <common/log.h>\r
 #include <common/except.h>\r
 #include <common/array.h>\r
+#include <common/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/scoped_array.hpp>\r
 \r
 #include <algorithm>\r
 #include <array>\r
@@ -60,30 +65,110 @@ struct image_scroll_producer : public core::frame_producer_base
        int                                                             width_;\r
        int                                                             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
+       bool                                                    progressive_;\r
        \r
-       explicit image_scroll_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, const std::wstring& filename, int speed) \r
+       explicit image_scroll_producer(\r
+               const spl::shared_ptr<core::frame_factory>& frame_factory, \r
+               const core::video_format_desc& format_desc, \r
+               const std::wstring& filename, \r
+               double speed,\r
+               double duration,\r
+               int motion_blur_px = 0,\r
+               bool premultiply_with_alpha = false,\r
+               bool progressive = false)\r
                : filename_(filename)\r
                , delta_(0)\r
                , format_desc_(format_desc)\r
                , speed_(speed)\r
+               , start_offset_x_(0)\r
+               , start_offset_y_(0)\r
+               , progressive_(progressive)\r
        {\r
-               start_offset_.assign(0.0);\r
-\r
                auto bitmap = load_image(filename_);\r
                FreeImage_FlipVertical(bitmap.get());\r
 \r
                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
+                       {\r
+                               start_offset_y_ = height_ + format_desc_.height;\r
+                       }\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
                auto 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
+                       core::tweener blur_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
                                core::pixel_format_desc desc = core::pixel_format::bgra;\r
@@ -101,18 +186,16 @@ struct image_scroll_producer : public core::frame_producer_base
                                        std::copy_n(bytes, count, frame.image_data(0).begin() + format_desc_.size - count);\r
                                        count = 0;\r
                                }\r
-                       \r
-                               frames_.push_back(core::draw_frame(std::move(frame)));\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
+                               core::draw_frame draw_frame(std::move(frame));\r
+\r
+                               // Set the relative position to the other image fragments\r
+                               draw_frame.transform().image_transform.fill_translation[1] = - n++;\r
+\r
+                               frames_.push_back(draw_frame);\r
                        }\r
                }\r
-               else\r
+               else if (horizontal)\r
                {\r
                        int i = 0;\r
                        while(count > 0)\r
@@ -143,57 +226,131 @@ struct image_scroll_producer : public core::frame_producer_base
 \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
-                               auto offset = format_desc_.width - (width_ % format_desc_.width);\r
-                               start_offset_[0] = offset * 0.5/static_cast<double>(format_desc_.width);\r
+                               double translation = - (static_cast<double>(n) + 1.0);\r
+                               frames_[n].transform().image_transform.fill_translation[0] = translation;\r
+                       }\r
+               }\r
+\r
+               CASPAR_LOG(info) << print() << L" Initialized";\r
+       }\r
+\r
+       std::vector<core::draw_frame> get_visible()\r
+       {\r
+               std::vector<core::draw_frame> result;\r
+               result.reserve(frames_.size());\r
+\r
+               BOOST_FOREACH(auto& frame, frames_)\r
+               {\r
+                       auto& fill_translation = frame.transform().image_transform.fill_translation;\r
+\r
+                       if (width_ == format_desc_.width)\r
+                       {\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
-               CASPAR_LOG(info) << print() << L" Initialized";\r
+               return std::move(result);\r
        }\r
        \r
        // frame_producer\r
-\r
-       core::draw_frame receive_impl() override\r
-       {               \r
-               delta_ += speed_;\r
-\r
+       core::draw_frame render_frame(bool allow_eof)\r
+       {\r
                if(frames_.empty())\r
-                       return core::draw_frame::late();\r
+                       return core::draw_frame::empty();\r
                \r
-               if(height_ > format_desc_.height)\r
+               core::draw_frame result(get_visible());\r
+               auto& fill_translation = result.transform().image_transform.fill_translation;\r
+\r
+               if (width_ == format_desc_.width)\r
                {\r
-                       if(static_cast<int>(std::abs(delta_)) >= height_ - format_desc_.height)\r
-                               return last_frame();\r
+                       if (static_cast<size_t>(std::abs(delta_)) >= height_ + format_desc_.height && allow_eof)\r
+                               return core::draw_frame::empty();\r
 \r
-                       for(int n = 0; n < frames_.size(); ++n)\r
-                       {\r
-                               frames_[n].transform().image_transform.fill_translation[0] = start_offset_[0];\r
-                               frames_[n].transform().image_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 && allow_eof)\r
+                               return core::draw_frame::empty();\r
+\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
+       core::draw_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
+       core::draw_frame receive_impl() override\r
+       {\r
+               core::draw_frame result;\r
+\r
+               if (format_desc_.field_mode == core::field_mode::progressive || progressive_)\r
+               {\r
+                       result = render_frame(true, true);\r
                }\r
                else\r
                {\r
-                       if(static_cast<int>(std::abs(delta_)) >= width_ - format_desc_.width)\r
-                               return last_frame();\r
+                       auto field1 = render_frame(true, true);\r
+                       auto field2 = render_frame(true, false);\r
 \r
-                       for(int n = 0; n < frames_.size(); ++n)\r
+                       if (field1 != core::draw_frame::empty() && field2 == core::draw_frame::empty())\r
                        {\r
-                               frames_[n].transform().image_transform.fill_translation[0] = start_offset_[0] - (n+1) + delta_ * 0.5/static_cast<double>(format_desc_.width);                           \r
-                               frames_[n].transform().image_transform.fill_translation[1] = start_offset_[1];\r
+                               field2 = render_frame(false, true);\r
                        }\r
+                       else\r
+                       {\r
+                               advance();\r
+                       }\r
+\r
+                       result = core::draw_frame::interlace(field1, field2, format_desc_.field_mode);\r
                }\r
                \r
                event_subject_ << monitor::event("file/path") % filename_\r
                                           << monitor::event("delta") % delta_ \r
                                           << monitor::event("speed") % speed_;\r
 \r
-               return core::draw_frame(frames_);\r
+               return result;\r
        }\r
                                \r
        std::wstring print() const override\r
@@ -216,16 +373,15 @@ struct image_scroll_producer : public core::frame_producer_base
 \r
        uint32_t nb_frames() const override\r
        {\r
-               if(height_ > format_desc_.height)\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
@@ -242,29 +398,58 @@ struct image_scroll_producer : public core::frame_producer_base
 \r
 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)\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
+       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
        \r
        auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool\r
                {                                       \r
-                       return boost::filesystem::is_regular_file(boost::filesystem::wpath(filename).replace_extension(ex));\r
+                       return boost::filesystem::is_regular_file(boost::filesystem::path(filename).replace_extension(ex));\r
                });\r
 \r
        if(ext == extensions.end())\r
                return core::frame_producer::empty();\r
        \r
-       int 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 spl::make_shared<image_scroll_producer>(frame_factory, format_desc, filename + L"." + *ext, speed);\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
+       bool progressive = std::find(params.begin(), params.end(), L"PROGRESSIVE") != params.end();\r
+\r
+       return core::create_destroy_proxy(spl::make_shared<image_scroll_producer>(\r
+               frame_factory, \r
+               format_desc, \r
+               filename + *ext, \r
+               -speed, \r
+               -duration, \r
+               motion_blur_px, \r
+               premultiply_with_alpha,\r
+               progressive));\r
 }\r
 \r
 }}
\ No newline at end of file