]> git.sesse.net Git - casparcg/commitdiff
Merged image_scroll_producer changes to 2.1
authorhellgore <hellgore@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Tue, 4 Sep 2012 13:18:15 +0000 (13:18 +0000)
committerhellgore <hellgore@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Tue, 4 Sep 2012 13:18:15 +0000 (13:18 +0000)
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches/2.1.0@3229 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

common/memory.h
modules/image/image.vcxproj
modules/image/image.vcxproj.filters
modules/image/producer/image_scroll_producer.cpp
modules/image/util/image_algorithms.h [new file with mode: 0644]
modules/image/util/image_view.h [new file with mode: 0644]

index c3235240b97ac65eaf567df39016a1db0b3c954e..5e865078ebcf6efe3fb6adc303fcb13465cdb594 100644 (file)
@@ -704,6 +704,18 @@ shared_ptr<T> make_shared(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5)
     return shared_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4), std::forward<P5>(p5)));\r
 }\r
 \r
+template<typename T, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>\r
+shared_ptr<T> make_shared(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6)\r
+{\r
+    return shared_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4), std::forward<P5>(p5), std::forward<P6>(p6)));\r
+}\r
+\r
+template<typename T, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>\r
+shared_ptr<T> make_shared(P0&& p0, P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7)\r
+{\r
+    return shared_ptr<T>(std::make_shared<T>(std::forward<P0>(p0), std::forward<P1>(p1), std::forward<P2>(p2), std::forward<P3>(p3), std::forward<P4>(p4), std::forward<P5>(p5), std::forward<P6>(p6), std::forward<P7>(p7)));\r
+}\r
+\r
 template<typename T>\r
 shared_ptr<T>::shared_ptr() \r
     : p_(make_shared<T>())\r
index 4051168ecf652c76121557b723cc60699476fe7b..44d15542f3274185ae2866252737b83e640b89a3 100644 (file)
     <ClInclude Include="image.h" />\r
     <ClInclude Include="producer\image_producer.h" />\r
     <ClInclude Include="producer\image_scroll_producer.h" />\r
+    <ClInclude Include="util\image_algorithms.h" />\r
     <ClInclude Include="util\image_loader.h" />\r
+    <ClInclude Include="util\image_view.h" />\r
   </ItemGroup>\r
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
   <ImportGroup Label="ExtensionTargets">\r
index d5c88013878359bf09ebcb9144217e393c300a5c..05665fc739f4d5f69ab3a1c064899501efa46e50 100644 (file)
     <ClInclude Include="consumer\image_consumer.h">\r
       <Filter>source\consumer</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="util\image_algorithms.h">\r
+      <Filter>source\util</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="util\image_view.h">\r
+      <Filter>source\util</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
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
diff --git a/modules/image/util/image_algorithms.h b/modules/image/util/image_algorithms.h
new file mode 100644 (file)
index 0000000..b7b38d2
--- /dev/null
@@ -0,0 +1,228 @@
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\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
+*\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: Helge Norberg, helge.norberg@svt.se\r
+*/\r
+\r
+#pragma once\r
+\r
+#include <common/tweener.h>\r
+\r
+#include <cmath>\r
+#include <boost/foreach.hpp>\r
+\r
+namespace caspar { namespace image {\r
+\r
+/**\r
+ * Helper for calculating the color of a pixel given any number of of other\r
+ * pixels (each with their own weight).\r
+ */\r
+class rgba_weighting\r
+{\r
+       int r, g, b, a;\r
+       int total_weight;\r
+public:\r
+       rgba_weighting()\r
+               : r(0), g(0), b(0), a(0), total_weight(0)\r
+       {\r
+       }\r
+\r
+       template<class RGBAPixel>\r
+       inline void add_pixel(const RGBAPixel& pixel, uint8_t weight)\r
+       {\r
+               r += pixel.r() * weight;\r
+               g += pixel.g() * weight;\r
+               b += pixel.b() * weight;\r
+               a += pixel.a() * weight;\r
+\r
+               total_weight += weight;\r
+       }\r
+\r
+       template<class RGBAPixel>\r
+       inline void store_result(RGBAPixel& pixel)\r
+       {\r
+               pixel.r() = static_cast<uint8_t>(r / total_weight);\r
+               pixel.g() = static_cast<uint8_t>(g / total_weight);\r
+               pixel.b() = static_cast<uint8_t>(b / total_weight);\r
+               pixel.a() = static_cast<uint8_t>(a / total_weight);\r
+       }\r
+};\r
+\r
+template<class T>\r
+std::vector<T> get_tweened_values(const core::tweener& tweener, size_t num_values, T from, T to)\r
+{\r
+       std::vector<T> result;\r
+       result.reserve(num_values);\r
+\r
+       double start = static_cast<double>(from);\r
+       double delta = static_cast<double>(to - from);\r
+       double duration = static_cast<double>(num_values);\r
+\r
+       for (double t = 0; t < duration; ++t)\r
+       {\r
+               result.push_back(static_cast<T>(tweener(t, start, delta, duration - 1.0)));\r
+       }\r
+\r
+       return std::move(result);\r
+}\r
+\r
+/**\r
+ * Blur a source image and store the blurred result in a destination image.\r
+ * <p>\r
+ * The blur is done by weighting each relative pixel from a destination pixel\r
+ * position using a vector of relative x-y pairs. The further away a related\r
+ * pixel is the less weight it gets. A tweener is used to calculate the actual\r
+ * weights of each related pixel.\r
+ *\r
+ * @param src                      The source view. Has to model the ImageView\r
+ *                                 concept and have a pixel type modelling the\r
+ *                                 RGBAPixel concept.\r
+ * @param dst                      The destination view. Has to model the\r
+ *                                 ImageView concept and have a pixel type\r
+ *                                 modelling the RGBAPixel concept.\r
+ * @param motion_trail_coordinates The relative x-y positions to weight in for\r
+ *                                 each pixel.\r
+ * @param tweener                  The tweener to use for calculating the\r
+ *                                 weights of each relative position in the\r
+ *                                 motion trail.\r
+ */\r
+template<class SrcView, class DstView>\r
+void blur(\r
+       const SrcView& src,\r
+       DstView& dst,\r
+       const std::vector<std::pair<int, int>> motion_trail_coordinates, \r
+       const core::tweener& tweener)\r
+{\r
+       auto blur_px = motion_trail_coordinates.size();\r
+       auto tweened_weights_y = get_tweened_values<uint8_t>(tweener, blur_px + 2, 255, 0);\r
+       tweened_weights_y.pop_back();\r
+       tweened_weights_y.erase(tweened_weights_y.begin());\r
+\r
+       auto src_end = src.end();\r
+       auto dst_iter = dst.begin();\r
+\r
+       for (auto src_iter = src.begin(); src_iter != src_end; ++src_iter, ++dst_iter)\r
+       {\r
+               rgba_weighting w;\r
+\r
+               for (int i = 0; i < blur_px; ++i)\r
+               {\r
+                       auto& coordinate = motion_trail_coordinates[i];\r
+                       auto other_pixel = src.relative(src_iter, coordinate.first, coordinate.second);\r
+\r
+                       if (other_pixel == nullptr)\r
+                               break;\r
+\r
+                       w.add_pixel(*other_pixel, tweened_weights_y[i]);\r
+               }\r
+\r
+               w.add_pixel(*src_iter, 255);\r
+               w.store_result(*dst_iter);\r
+       }\r
+}\r
+\r
+/**\r
+ * Calculate relative x-y coordinates of a straight line with a given angle and\r
+ * a given number of points.\r
+ *\r
+ * @param num_pixels    The number of pixels/points to create.\r
+ * @param angle_radians The angle of the line in radians.\r
+ *\r
+ * @return the x-y pairs.\r
+ */\r
+std::vector<std::pair<int, int>> get_line_points(int num_pixels, double angle_radians)\r
+{\r
+       std::vector<std::pair<int, int>> line_points;\r
+       line_points.reserve(num_pixels);\r
+\r
+       double delta_x = std::cos(angle_radians);\r
+       double delta_y = -std::sin(angle_radians); // In memory is revered\r
+       double max_delta = std::max(std::abs(delta_x), std::abs(delta_y));\r
+       double amplification = 1.0 / max_delta;\r
+       delta_x *= amplification;\r
+       delta_y *= amplification;\r
+\r
+       for (int i = 1; i <= num_pixels; ++i)\r
+               line_points.push_back(std::make_pair(\r
+                       static_cast<int>(std::floor(delta_x * static_cast<double>(i) + 0.5)), \r
+                       static_cast<int>(std::floor(delta_y * static_cast<double>(i) + 0.5))));\r
+\r
+       return std::move(line_points);\r
+}\r
+\r
+/**\r
+ * Directionally blur a source image modelling the ImageView concept and store\r
+ * the blurred image to a destination image also modelling the ImageView\r
+ * concept.\r
+ * <p>\r
+ * The pixel type of the views must model the RGBAPixel concept.\r
+ *\r
+ * @param src           The source image view. Has to model the ImageView\r
+ *                      concept and have a pixel type that models RGBAPixel.\r
+ * @param dst           The destiation image view. Has to model the ImageView\r
+ *                      concept and have a pixel type that models RGBAPixel.\r
+ * @param angle_radians The angle in radians to directionally blur the image.\r
+ * @param blur_px       The number of pixels of the blur.\r
+ * @param tweener       The tweener to use to create a pixel weighting curve\r
+ *                      with.\r
+ */\r
+template<class SrcView, class DstView>\r
+void blur(\r
+       const SrcView& src,\r
+       DstView& dst,\r
+       double angle_radians,\r
+       int blur_px, \r
+       const core::tweener& tweener)\r
+{\r
+       auto motion_trail = get_line_points(blur_px, angle_radians);\r
+\r
+       blur(src, dst, motion_trail, tweener);\r
+}\r
+\r
+/**\r
+ * Premultiply with alpha for each pixel in an ImageView. The modifications is\r
+ * done in place. The pixel type of the ImageView must model the RGBAPixel\r
+ * concept.\r
+ *\r
+ * @param view_to_modify The image view to premultiply in place. Has to model\r
+ *                       the ImageView concept and have a pixel type that\r
+ *                       models RGBAPixel.\r
+ */\r
+template<class SrcDstView>\r
+void premultiply(SrcDstView& view_to_modify)\r
+{\r
+       std::for_each(view_to_modify.begin(), view_to_modify.end(), [&](SrcDstView::pixel_type& pixel)\r
+       {\r
+               int alpha = static_cast<int>(pixel.a());\r
+\r
+               if (alpha != 255) // Performance optimization\r
+               {\r
+                       // We don't event try to premultiply 0 since it will be unaffected.\r
+                       if (pixel.r())\r
+                               pixel.r() = static_cast<uint8_t>(static_cast<int>(pixel.r()) * alpha / 255);\r
+\r
+                       if (pixel.g())\r
+                               pixel.g() = static_cast<uint8_t>(static_cast<int>(pixel.g()) * alpha / 255);\r
+\r
+                       if (pixel.b())\r
+                               pixel.b() = static_cast<uint8_t>(static_cast<int>(pixel.b()) * alpha / 255);\r
+               }\r
+       });\r
+}\r
+\r
+}}\r
diff --git a/modules/image/util/image_view.h b/modules/image/util/image_view.h
new file mode 100644 (file)
index 0000000..422ab3c
--- /dev/null
@@ -0,0 +1,277 @@
+/*\r
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
+*\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
+*\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: Helge Norberg, helge.norberg@svt.se\r
+*/\r
+\r
+#pragma once\r
+\r
+#include <boost/iterator/filter_iterator.hpp>\r
+\r
+namespace caspar { namespace image {\r
+\r
+/**\r
+ * A POD pixel with a compatible memory layout as a 8bit BGRA pixel (32bits in\r
+ * total).\r
+ * <p>\r
+ * Models the PackedPixel concept used by for example image_view. Also models\r
+ * the RGBAPixel concept which does not care about the order between RGBA but\r
+ * only requires that all 4 channel has accessors.\r
+ */\r
+class bgra_pixel\r
+{\r
+       uint8_t b_;\r
+       uint8_t g_;\r
+       uint8_t r_;\r
+       uint8_t a_;\r
+public:\r
+       bgra_pixel(uint8_t b = 0, uint8_t g = 0, uint8_t r = 0, uint8_t a = 0) : b_(b), g_(g), r_(r), a_(a) {}\r
+       inline const uint8_t& b() const { return b_; }\r
+       inline uint8_t& b() { return b_; }\r
+       inline const uint8_t& g() const { return g_; }\r
+       inline uint8_t& g() { return g_; }\r
+       inline const uint8_t& r() const { return r_; }\r
+       inline uint8_t& r() { return r_; }\r
+       inline const uint8_t& a() const { return a_; }\r
+       inline uint8_t& a() { return a_; }\r
+};\r
+\r
+template<class PackedPixel> class image_sub_view;\r
+\r
+/**\r
+ * An image view abstracting raw packed pixel data\r
+ * <p>\r
+ * This is only a view, it does not own the data.\r
+ * <p>\r
+ * Models the the ImageView concept.\r
+ */\r
+template<class PackedPixel>\r
+class image_view\r
+{\r
+public:\r
+       typedef PackedPixel pixel_type;\r
+\r
+       image_view(void* raw_start, int width, int height)\r
+               : begin_(static_cast<PackedPixel*>(raw_start))\r
+               , end_(begin_ + (width * height))\r
+               , width_(width)\r
+               , height_(height)\r
+       {\r
+       }\r
+\r
+       PackedPixel* begin()\r
+       {\r
+               return begin_;\r
+       }\r
+\r
+       const PackedPixel* begin() const\r
+       {\r
+               return begin_;\r
+       }\r
+\r
+       PackedPixel* end()\r
+       {\r
+               return end_;\r
+       }\r
+\r
+       const PackedPixel* end() const\r
+       {\r
+               return end_;\r
+       }\r
+\r
+       template<class PackedPixelIter>\r
+       inline PackedPixel* relative(PackedPixelIter to, int delta_x, int delta_y)\r
+       {\r
+               auto pixel_distance = delta_x + width_ * delta_y;\r
+               PackedPixel* to_address = &(*to);\r
+               auto result = to_address + pixel_distance;\r
+\r
+               if (result < begin_ || result >= end_)\r
+                       return nullptr;\r
+               else\r
+                       return result;\r
+       }\r
+\r
+       template<class PackedPixelIter>\r
+       inline const PackedPixel* relative(PackedPixelIter to, int delta_x, int delta_y) const\r
+       {\r
+               //auto x_distance\r
+               auto pixel_distance = delta_x + width_ * delta_y;\r
+               const PackedPixel* to_address = &(*to);\r
+               auto result = to_address + pixel_distance;\r
+\r
+               /*if (delta_x != 0)\r
+               {\r
+                       auto actual_delta_y = result % width_\r
+               }*/\r
+\r
+               if (result < begin_ || result >= end_)\r
+                       return nullptr;\r
+               else\r
+                       return result;\r
+       }\r
+\r
+       int width() const\r
+       {\r
+               return width_;\r
+       }\r
+\r
+       int height() const\r
+       {\r
+               return height_;\r
+       }\r
+\r
+       image_sub_view<PackedPixel> subview(int x, int y, int width, int height)\r
+       {\r
+               return image_sub_view<PackedPixel>(*this, x, y, width, height);\r
+       }\r
+\r
+       const image_sub_view<PackedPixel> subview(int x, int y, int width, int height) const\r
+       {\r
+               return image_sub_view<PackedPixel>(*this, x, y, width, height);\r
+       }\r
+private:\r
+       PackedPixel* begin_;\r
+       PackedPixel* end_;\r
+       int width_;\r
+       int height_;\r
+};\r
+\r
+template<class PackedPixel>\r
+class is_within_view\r
+{\r
+public:\r
+       is_within_view(const PackedPixel* begin, int width, int stride)\r
+               : begin_(begin)\r
+               , width_(width)\r
+               , stride_(stride)\r
+               , no_check_(width == stride)\r
+       {\r
+       }\r
+\r
+       inline bool operator()(const PackedPixel& pixel) const\r
+       {\r
+               if (no_check_)\r
+                       return true;\r
+\r
+               const PackedPixel* position = &pixel;\r
+               int distance_from_row_start = (position - begin_) % stride_;\r
+\r
+               return distance_from_row_start < width_;\r
+       }\r
+private:\r
+       const PackedPixel* begin_;\r
+       int width_;\r
+       int stride_;\r
+       bool no_check_;\r
+};\r
+\r
+template <class PackedPixel>\r
+struct image_stride_iterator : public boost::filter_iterator<is_within_view<PackedPixel>, PackedPixel*>\r
+{\r
+       image_stride_iterator(PackedPixel* begin, PackedPixel* end, int width, int stride)\r
+               : boost::filter_iterator<is_within_view<PackedPixel>, PackedPixel*>::filter_iterator(\r
+                       is_within_view<PackedPixel>(begin, width, stride), begin, end)\r
+       {\r
+       }\r
+};\r
+\r
+/**\r
+ * A sub view created from an image_view.\r
+ * <p>\r
+ * This also models the ImageView concept.\r
+ */\r
+template<class PackedPixel>\r
+class image_sub_view\r
+{\r
+public:\r
+       typedef PackedPixel pixel_type;\r
+\r
+       image_sub_view(image_view<PackedPixel>& root_view, int x, int y, int width, int height)\r
+               : root_view_(root_view)\r
+               , relative_to_root_x_(x)\r
+               , relative_to_root_y_(y)\r
+               , width_(width)\r
+               , height_(height)\r
+               , raw_begin_(root_view.relative(root_view.begin(), x, y))\r
+               , raw_end_(root_view.relative(raw_begin_, width - 1, height_ - 1) + 1)\r
+       {\r
+       }\r
+\r
+       image_stride_iterator<PackedPixel> begin()\r
+       {\r
+               return image_stride_iterator<PackedPixel>(raw_begin_, raw_end_, width_, root_view_.width());\r
+       }\r
+\r
+       image_stride_iterator<const PackedPixel> begin() const\r
+       {\r
+               return image_stride_iterator<const PackedPixel>(raw_begin_, raw_end_, width_, root_view_.width());\r
+       }\r
+\r
+       image_stride_iterator<PackedPixel> end()\r
+       {\r
+               return image_stride_iterator<PackedPixel>(raw_end_, raw_end_, width_, root_view_.width());\r
+       }\r
+\r
+       image_stride_iterator<const PackedPixel> end() const\r
+       {\r
+               return image_stride_iterator<const PackedPixel>(raw_end_, raw_end_, width_, root_view_.width());\r
+       }\r
+\r
+       template<class PackedPixelIter>\r
+       PackedPixel* relative(PackedPixelIter to, int delta_x, int delta_y)\r
+       {\r
+               return root_view_.relative(to, delta_x, delta_y);\r
+       }\r
+\r
+       template<class PackedPixelIter>\r
+       const PackedPixel* relative(PackedPixelIter to, int delta_x, int delta_y) const\r
+       {\r
+               return root_view_.relative(to, delta_x, delta_y);\r
+       }\r
+\r
+       int width() const\r
+       {\r
+               return width_;\r
+       }\r
+\r
+       int height() const\r
+       {\r
+               return height_;\r
+       }\r
+\r
+       image_sub_view<PackedPixel> subview(int x, int y, int width, int height)\r
+       {\r
+               return root_view_.subview(relative_to_root_x_ + x, relative_to_root_y_ + y, width, height);\r
+       }\r
+\r
+       const image_sub_view<PackedPixel> subview(int x, int y, int width, int height) const\r
+       {\r
+               return root_view_.subview(relative_to_root_x_ + x, relative_to_root_y_ + y, width, height);\r
+       }\r
+private:\r
+       image_view<PackedPixel> root_view_;\r
+       int relative_to_root_x_;\r
+       int relative_to_root_y_;\r
+       int width_;\r
+       int height_;\r
+       PackedPixel* raw_begin_;\r
+       PackedPixel* raw_end_;\r
+};\r
+\r
+}}\r