]> git.sesse.net Git - casparcg/blobdiff - core/producer/text/text_producer.cpp
Merged most recent OSC changes
[casparcg] / core / producer / text / text_producer.cpp
index cd41705255ba6cf166c1331d6d1477de556f0bab..bbb798d2e78154a03e666d97de339902ba8a02ed 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <core/producer/frame_producer.h>
 #include <core/producer/color/color_producer.h>
+#include <core/producer/variable.h>
 #include <core/frame/geometry.h>
 #include <core/frame/frame.h>
 #include <core/frame/draw_frame.h>
 #include <boost/property_tree/ptree.hpp>
 #include <boost/filesystem.hpp>
 
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
 #include "utils\texture_atlas.h"
 #include "utils\texture_font.h"
 
@@ -64,87 +69,137 @@ public:
 
 namespace caspar { namespace core {
        namespace text {
-               
+
+               using namespace boost::filesystem3;
+
                std::map<std::wstring, std::wstring> fonts;
-               
+
+               std::map<std::wstring, std::wstring> enumerate_fonts()
+               {
+                       std::map<std::wstring, std::wstring> result;
+
+                       FT_Library lib;
+                       FT_Error err = FT_Init_FreeType(&lib);
+                       if(err) 
+                               return result;
+
+                       auto fonts = directory_iterator(env::system_font_folder());
+                       auto end = directory_iterator();
+                       for(; fonts != end; ++fonts)
+                       {
+                               auto file = (*fonts);
+                               if(is_regular_file(file.path()))
+                               {
+                                       FT_Face face;
+                                       err = FT_New_Face(lib, u8(file.path().native()).c_str(), 0, &face);
+                                       if(err) 
+                                               continue;
+
+                                       const char* fontname = FT_Get_Postscript_Name(face);    //this doesn't work for .fon fonts. Ignoring those for now
+                                       if(fontname != nullptr)
+                                       {
+                                               std::string fontname_str(fontname);
+                                               result.insert(std::pair<std::wstring, std::wstring>(std::wstring(fontname_str.begin(), fontname_str.end()), file.path().native()));
+                                       }
+
+                                       FT_Done_Face(face);
+                               }
+                       }
+
+                       FT_Done_FreeType(lib);
+
+                       return result;
+               }
+
                void init()
                {
-                       fonts.swap(std::move(caspar::enumerate_fonts()));
+                       fonts = enumerate_fonts();
+                       if(!fonts.empty())
+                               register_producer_factory(&create_text_producer);
                }
 
-               std::wstring find_font_file(const std::wstring& font_name)
+               text_info& find_font_file(text_info& info)
                {
+                       auto& font_name = info.font;
                        auto it = std::find_if(fonts.begin(), fonts.end(), font_comparer(font_name));
-                       if(it != fonts.end())
-                       {
-                               std::wstring filename = L"c:\\windows\\fonts\\" + (*it).second; //TODO: move font-folder setting to settings
-                               return filename;
-                       }
-       
-                       //try the default font
-                       it = std::find_if(fonts.begin(), fonts.end(), font_comparer(L"verdana"));       //TODO: move default-font to settings
-                       if(it != fonts.end())
-                       {
-                               std::wstring filename = L"c:\\windows\\fonts\\" + (*it).second; //TODO: move font-folder setting to settings
-                               return filename;
-                       }
-
-                       //fail
-                       return L"";
+                       info.font_file = (it != fonts.end()) ? (*it).second : L"";
+                       return info;
                }
        }
        
 
 struct text_producer::impl
 {
-       spl::shared_ptr<core::frame_factory>    frame_factory_;
+       monitor::subject monitor_subject_;
+       spl::shared_ptr<core::frame_factory> frame_factory_;
        constraints constraints_;
        int x_, y_, parent_width_, parent_height_;
        bool standalone_;
-       binding<std::wstring> text_;
+       variable_impl<std::wstring> text_;
        std::shared_ptr<void> text_subscription_;
+       variable_impl<double> tracking_;
+       std::shared_ptr<void> tracking_subscription_;
+       variable_impl<double> current_bearing_y_;
+       variable_impl<double> current_protrude_under_y_;
        draw_frame frame_;
        text::texture_atlas atlas_;
        text::texture_font font_;
+       bool dirty_;
 
 public:
-       explicit impl(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, const text::text_info& text_info, long parent_width, long parent_height, bool standalone) 
+       explicit impl(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, text::text_info& text_info, long parent_width, long parent_height, bool standalone) 
                : frame_factory_(frame_factory)
                , constraints_(parent_width, parent_height)
                , x_(x), y_(y), parent_width_(parent_width), parent_height_(parent_height)
                , standalone_(standalone)
                , atlas_(512,512,4)
-               , font_(atlas_, text::find_font_file(text_info.font), text_info.size, !standalone)
+               , font_(atlas_, text::find_font_file(text_info), !standalone)
+               , dirty_(false)
        {
+               //TODO: examine str to determine which unicode_blocks to load
                font_.load_glyphs(text::Basic_Latin, text_info.color);
                font_.load_glyphs(text::Latin_1_Supplement, text_info.color);
                font_.load_glyphs(text::Latin_Extended_A, text_info.color);
 
-               text_subscription_ = text_.on_change([this]()
+               tracking_.value().set(text_info.tracking);
+               text_subscription_ = text_.value().on_change([this]()
                {
-                       generate_frame(text_.get());
+                       dirty_ = true;
                });
+               tracking_subscription_ = tracking_.value().on_change([this]()
+               {
+                       dirty_ = true;
+               });
+
+               constraints_.height.depend_on(text());
+               constraints_.width.depend_on(text());
+               current_bearing_y_.as<double>().depend_on(text());
+               current_protrude_under_y_.as<double>().depend_on(text());
 
                //generate frame
-               text_.set(str);
+               text_.value().set(str);
 
                CASPAR_LOG(info) << print() << L" Initialized";
        }
 
-       void generate_frame(const std::wstring& str)
+       void generate_frame()
        {
                core::pixel_format_desc pfd(core::pixel_format::bgra);
                pfd.planes.push_back(core::pixel_format_desc::plane(static_cast<int>(atlas_.width()), static_cast<int>(atlas_.height()), static_cast<int>(atlas_.depth())));
 
                text::string_metrics metrics;
-               std::vector<float> vertex_stream(std::move(font_.create_vertex_stream(str, x_, y_, parent_width_, parent_height_, &metrics)));
+               font_.set_tracking(static_cast<int>(tracking_.value().get()));
+               auto vertex_stream = font_.create_vertex_stream(text_.value().get(), x_, y_, parent_width_, parent_height_, &metrics);
                auto frame = frame_factory_->create_frame(vertex_stream.data(), pfd);
                memcpy(frame.image_data().data(), atlas_.data(), frame.image_data().size());
                frame.set_geometry(frame_geometry(frame_geometry::quad_list, std::move(vertex_stream)));
 
                this->constraints_.width.set(metrics.width);
                this->constraints_.height.set(metrics.height);
+               current_bearing_y_.value().set(metrics.bearingY);
+               current_protrude_under_y_.value().set(metrics.protrudeUnderY);
                frame_ = core::draw_frame(std::move(frame));
+               frame_.transform().image_transform.fill_translation[1] = static_cast<double>(metrics.bearingY) / static_cast<double>(metrics.height);
        }
 
        text::string_metrics measure_string(const std::wstring& str)
@@ -156,17 +211,46 @@ public:
                        
        draw_frame receive_impl()
        {
+               if (dirty_)
+                       generate_frame();
+
                return frame_;
        }
 
        boost::unique_future<std::wstring> call(const std::vector<std::wstring>& param)
        {
                std::wstring result;
-               text_.set(param.empty() ? L"" : param[0]);
+               text_.value().set(param.empty() ? L"" : param[0]);
 
                return async(launch::deferred, [=]{return result;});
        }
 
+       variable& get_variable(const std::wstring& name)
+       {
+               if (name == L"text")
+                       return text_;
+               else if (name == L"current_bearing_y")
+                       return current_bearing_y_;
+               else if (name == L"current_protrude_under_y")
+                       return current_protrude_under_y_;
+               else if (name == L"tracking")
+                       return tracking_;
+
+               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(L"text_producer does not have a variable called " + name));
+       }
+
+       const std::vector<std::wstring>& get_variables() const
+       {
+               static std::vector<std::wstring> vars =
+                               boost::assign::list_of<std::wstring>
+                                               (L"text")
+                                               (L"tracking")
+                                               (L"current_bearing_y")
+                                               (L"current_protrude_under_y");
+
+               return vars;
+       }
+
        constraints& pixel_constraints()
        {
                return constraints_;
@@ -174,12 +258,27 @@ public:
 
        binding<std::wstring>& text()
        {
-               return text_;
+               return text_.value();
+       }
+
+       binding<double>& tracking()
+       {
+               return tracking_.value();
+       }
+
+       const binding<double>& current_bearing_y() const
+       {
+               return current_bearing_y_.value();
+       }
+
+       const binding<double>& current_protrude_under_y() const
+       {
+               return current_protrude_under_y_.value();
        }
        
        std::wstring print() const
        {
-               return L"text[" + text_.get() + L"]";
+               return L"text[" + text_.value().get() + L"]";
        }
 
        std::wstring name() const
@@ -191,28 +290,32 @@ public:
        {
                boost::property_tree::wptree info;
                info.add(L"type", L"text");
-               info.add(L"text", text_.get());
+               info.add(L"text", text_.value().get());
                return info;
        }
 };
 
-text_producer::text_producer(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, const text::text_info& text_info, long parent_width, long parent_height, bool standalone)
+text_producer::text_producer(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, text::text_info& text_info, long parent_width, long parent_height, bool standalone)
        : impl_(new impl(frame_factory, x, y, str, text_info, parent_width, parent_height, standalone))
 {}
 
 draw_frame text_producer::receive_impl() { return impl_->receive_impl(); }
 boost::unique_future<std::wstring> text_producer::call(const std::vector<std::wstring>& param) { return impl_->call(param); }
+variable& text_producer::get_variable(const std::wstring& name) { return impl_->get_variable(name); }
+const std::vector<std::wstring>& text_producer::get_variables() const { return impl_->get_variables(); }
 text::string_metrics text_producer::measure_string(const std::wstring& str) { return impl_->measure_string(str); }
 
 constraints& text_producer::pixel_constraints() { return impl_->pixel_constraints(); }
 std::wstring text_producer::print() const { return impl_->print(); }
 std::wstring text_producer::name() const { return impl_->name(); }
 boost::property_tree::wptree text_producer::info() const { return impl_->info(); }
-void text_producer::subscribe(const monitor::observable::observer_ptr& o) {}
-void text_producer::unsubscribe(const monitor::observable::observer_ptr& o) {}
+monitor::subject& text_producer::monitor_output() { return impl_->monitor_subject_; }
 binding<std::wstring>& text_producer::text() { return impl_->text(); }
+binding<double>& text_producer::tracking() { return impl_->tracking(); }
+const binding<double>& text_producer::current_bearing_y() const { return impl_->current_bearing_y(); }
+const binding<double>& text_producer::current_protrude_under_y() const { return impl_->current_protrude_under_y(); }
 
-spl::shared_ptr<text_producer> text_producer::create(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, const text::text_info& text_info, long parent_width, long parent_height, bool standalone)
+spl::shared_ptr<text_producer> text_producer::create(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, text::text_info& text_info, long parent_width, long parent_height, bool standalone)
 {
        return spl::make_shared<text_producer>(frame_factory, x, y, str, text_info, parent_width, parent_height, standalone);
 }
@@ -238,7 +341,9 @@ spl::shared_ptr<frame_producer> create_text_producer(const spl::shared_ptr<frame
        try_get_color(col_str, col_val);
        text_info.color = core::text::color<float>(col_val);
 
-       return text_producer::create(frame_factory, x, y, params.at(1), text_info, format_desc.width, format_desc.height, true);
+       bool standalone = get_param(L"STANDALONE", params, false);
+
+       return text_producer::create(frame_factory, x, y, params.at(1), text_info, format_desc.width, format_desc.height, standalone);
 }
 
-}}
\ No newline at end of file
+}}