]> git.sesse.net Git - casparcg/commitdiff
* Implemented FLS command (font list).
authorHelge Norberg <helge.norberg@svt.se>
Mon, 12 Oct 2015 13:36:00 +0000 (15:36 +0200)
committerHelge Norberg <helge.norberg@svt.se>
Mon, 12 Oct 2015 13:36:00 +0000 (15:36 +0200)
* Fixed some resource leaks in text_producer.
* Load font list each time a text_producer is created to discover fonts uploaded after start.

core/CMakeLists.txt
core/producer/text/text_producer.cpp
core/producer/text/text_producer.h
core/producer/text/utils/freetype_library.cpp [new file with mode: 0644]
core/producer/text/utils/freetype_library.h [new file with mode: 0644]
core/producer/text/utils/texture_font.cpp
protocol/amcp/AMCPCommandsImpl.cpp

index 17c35477878823455f3e221650ac66550d4f289a..3828f434f05d8f3c2a3632c25b884e2dde1cb94f 100644 (file)
@@ -37,6 +37,7 @@ set(SOURCES
                producer/separated/separated_producer.cpp
 
                producer/text/text_producer.cpp
+               producer/text/utils/freetype_library.cpp
                producer/text/utils/texture_atlas.cpp
                producer/text/utils/texture_font.cpp
 
@@ -99,6 +100,7 @@ set(HEADERS
                producer/scene/xml_scene_producer.h
 
                producer/text/utils/color.h
+               producer/text/utils/freetype_library.h
                producer/text/utils/string_metrics.h
                producer/text/utils/text_info.h
                producer/text/utils/texture_atlas.h
index 89756f5557b97c92f23b3259b2149129f663a29b..23076a4e5e233eda5e247b9b519f5e02865a855e 100644 (file)
@@ -57,6 +57,7 @@
 
 #include "utils/texture_atlas.h"
 #include "utils/texture_font.h"
+#include "utils/freetype_library.h"
 
 class font_comparer {
        const std::wstring& lhs;
@@ -70,59 +71,48 @@ namespace caspar { namespace core { namespace text {
 
 using namespace boost::filesystem;
 
-std::map<std::wstring, std::wstring> g_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::font_folder());
-       auto end = directory_iterator();
-       for(; fonts != end; ++fonts)
+       for(auto iter = directory_iterator(env::font_folder()), end = directory_iterator(); iter != end; ++iter)
        {
-               auto file = (*fonts);
+               auto file = (*iter);
                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
+                       auto face = get_new_face(u8(file.path().native()));
+                       const char* fontname = FT_Get_Postscript_Name(face.get());      //this doesn't work for .fon fonts. Ignoring those for now
                        if(fontname != nullptr)
                        {
                                std::string fontname_str(fontname);
                                result.insert(std::make_pair(u16(fontname_str), u16(file.path().native())));
                        }
-
-                       FT_Done_Face(face);
                }
        }
 
-       FT_Done_FreeType(lib);
-
        return result;
 }
 
+std::vector<std::pair<std::wstring, std::wstring>> list_fonts()
+{
+       auto fonts = enumerate_fonts();
+       return std::vector<std::pair<std::wstring, std::wstring>>(fonts.begin(), fonts.end());
+}
+
 void describe_text_producer(help_sink&, const help_repository&);
 spl::shared_ptr<frame_producer> create_text_producer(const frame_producer_dependencies&, const std::vector<std::wstring>&);
 
 void init(module_dependencies dependencies)
 {
-       g_fonts = enumerate_fonts();
        dependencies.producer_registry->register_producer_factory(L"Text Producer", create_text_producer, describe_text_producer);
 }
 
 text_info& find_font_file(text_info& info)
 {
        auto& font_name = info.font;
-       auto it = std::find_if(g_fonts.begin(), g_fonts.end(), font_comparer(font_name));
-       info.font_file = (it != g_fonts.end()) ? (*it).second : L"";
+       auto fonts = enumerate_fonts();
+       auto it = std::find_if(fonts.begin(), fonts.end(), font_comparer(font_name));
+       info.font_file = (it != fonts.end()) ? (*it).second : L"";
        return info;
 }
 
@@ -131,27 +121,30 @@ text_info& find_font_file(text_info& info)
 
 struct text_producer::impl
 {
-       monitor::subject monitor_subject_;
-       spl::shared_ptr<core::frame_factory> frame_factory_;
-       constraints constraints_;
-       int x_, y_, parent_width_, parent_height_;
-       bool standalone_;
-       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_                                                              { 512, 512, 4 };
-       text::texture_font font_;
-       bool dirty_                                                                                             = false;
+       monitor::subject                                                monitor_subject_;
+       spl::shared_ptr<core::frame_factory>    frame_factory_;
+       int                                                                             x_;
+       int                                                                             y_;
+       int                                                                             parent_width_;
+       int                                                                             parent_height_;
+       bool                                                                    standalone_;
+       constraints                                                             constraints_                            { parent_width_, parent_height_ };
+       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_                                          { 512, 512, 4 };
+       text::texture_font                                              font_;
+       bool                                                                    dirty_                                          = false;
 
 public:
        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)
+               , x_(x), y_(y)
+               , parent_width_(parent_width), parent_height_(parent_height)
                , standalone_(standalone)
                , font_(atlas_, text::find_font_file(text_info), !standalone)
        {
@@ -340,6 +333,7 @@ void describe_text_producer(help_sink& sink, const help_repository& repo)
        sink.para()->text(L"Examples:");
        sink.example(L">> PLAY 1-10 [TEXT] \"John Doe\" 0 0 FONT ArialMT SIZE 30 COLOR #1b698d STANDALONE 1");
        sink.example(L">> CALL 1-10 \"Jane Doe\"", L"for modifying the text while playing.");
+       sink.para()->text(L"See ")->see(L"FLS")->text(L" for listing the available fonts.");
 }
 
 spl::shared_ptr<frame_producer> create_text_producer(const frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
index 065eebf2fe1beeee3d5227488e3cee885ac40617..93c0474d1a3a33fdca8dad79cba829b20e0f4cda 100644 (file)
@@ -29,6 +29,7 @@
 #include <boost/property_tree/ptree_fwd.hpp>
 #include <string>
 #include <vector>
+#include <utility>
 
 #include "utils/color.h"
 #include "utils/string_metrics.h"
@@ -38,6 +39,7 @@ namespace caspar { namespace core {
        namespace text 
        {
                void init(module_dependencies dependencies);
+               std::vector<std::pair<std::wstring, std::wstring>> list_fonts();
        }
 
 class text_producer : public frame_producer_base
diff --git a/core/producer/text/utils/freetype_library.cpp b/core/producer/text/utils/freetype_library.cpp
new file mode 100644 (file)
index 0000000..a30d750
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#include "freetype_library.h"
+
+#include <boost/thread/tss.hpp>
+
+namespace caspar { namespace core { namespace text {
+
+spl::shared_ptr<std::remove_pointer<FT_Library>::type> get_lib_for_thread()
+{
+       typedef std::remove_pointer<FT_Library>::type non_ptr_type;
+       static boost::thread_specific_ptr<spl::shared_ptr<non_ptr_type>> libs;
+
+       auto result = libs.get();
+
+       if (!result)
+       {
+               FT_Library raw_lib;
+
+               if (FT_Init_FreeType(&raw_lib))
+                       CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info("Failed to initialize freetype"));
+
+               auto lib = spl::shared_ptr<non_ptr_type>(raw_lib, FT_Done_FreeType);
+               result = new spl::shared_ptr<non_ptr_type>(std::move(lib));
+
+               libs.reset(result);
+       }
+
+       return *result;
+}
+
+spl::shared_ptr<std::remove_pointer<FT_Face>::type> get_new_face(const std::string& font_file)
+{
+       auto lib = get_lib_for_thread();
+       FT_Face face;
+       if (FT_New_Face(lib.get(), u8(font_file).c_str(), 0, &face))
+               CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info("Failed to load font file \"" + font_file + "\""));
+
+       return spl::shared_ptr<std::remove_pointer<FT_Face>::type>(face, [lib](auto* p)
+       {
+               FT_Done_Face(p);
+       });
+}
+
+}}}
diff --git a/core/producer/text/utils/freetype_library.h b/core/producer/text/utils/freetype_library.h
new file mode 100644 (file)
index 0000000..92fa5bd
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+#include <common/except.h>
+#include <common/memory.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <string>
+#include <type_traits>
+
+namespace caspar { namespace core { namespace text {
+
+struct freetype_exception : virtual caspar_exception { };
+
+spl::shared_ptr<std::remove_pointer<FT_Library>::type> get_lib_for_thread();
+spl::shared_ptr<std::remove_pointer<FT_Face>::type> get_new_face(const std::string& font_file);
+
+}}}
index 5a81e30e31c275adf2e72b15096e659b984b614c..9c07f6c0c9a256f258cd7f046b32d9fa53622d4a 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "texture_atlas.h"
 #include "texture_font.h"
+#include "freetype_library.h"
 
 #include <map>
 #include <memory>
@@ -11,8 +12,6 @@
 
 namespace caspar { namespace core { namespace text {
 
-struct freetype_exception : virtual caspar_exception { };
-
 struct unicode_range
 {
        unicode_range() : first(0), last(0) {}
@@ -37,8 +36,7 @@ private:
                int width, height;
        };
 
-       std::shared_ptr<FT_LibraryRec_> lib_;
-       std::shared_ptr<FT_FaceRec_>    face_;
+       spl::shared_ptr<FT_FaceRec_>    face_;
        texture_atlas                                   atlas_;
        double                                                  size_;
        double                                                  tracking_;
@@ -46,22 +44,13 @@ private:
        std::map<int, glyph_info>               glyphs_;
 
 public:
-       impl(texture_atlas& atlas, const text_info& info, bool normalize_coordinates) : atlas_(atlas), size_(info.size), tracking_(info.size*info.tracking/1000.0), normalize_(normalize_coordinates)
+       impl(texture_atlas& atlas, const text_info& info, bool normalize_coordinates)
+               : face_(get_new_face(u8(info.font_file)))
+               , atlas_(atlas)
+               , size_(info.size)
+               , tracking_(info.size*info.tracking/1000.0)
+               , normalize_(normalize_coordinates)
        {
-               FT_Library lib;
-                       
-               if (FT_Init_FreeType(&lib))
-                       CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info("Failed to initialize freetype"));
-
-               lib_.reset(lib, [](FT_Library ptr) { FT_Done_FreeType(ptr); });
-
-               FT_Face face;
-                       
-               if (FT_New_Face(lib_.get(), u8(info.font_file).c_str(), 0, &face))
-                       CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info(L"Failed to load font " + info.font));
-
-               face_.reset(face, [](FT_Face ptr) { FT_Done_Face(ptr); });
-
                if (FT_Set_Char_Size(face_.get(), static_cast<FT_F26Dot6>(size_*64), 0, 72, 72))
                        CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info("Failed to set font size"));
        }
@@ -83,7 +72,6 @@ public:
 
        void load_glyphs(unicode_block block, const color<double>& col)
        {
-               FT_Error err;
                int flags = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
                unicode_range range = get_range(block);
 
@@ -93,7 +81,7 @@ public:
                        if(!glyph_index)        //ignore codes that doesn't have a glyph for now. Might want to map these to a special glyph later.
                                continue;
                        
-                       err = FT_Load_Glyph(face_.get(), glyph_index, flags);
+                       FT_Error err = FT_Load_Glyph(face_.get(), glyph_index, flags);
                        if(err) continue;       //igonore glyphs that fail to load
 
                        const FT_Bitmap& bitmap = face_->glyph->bitmap; //shorthand notation
index 0bce9354c99421268b777d5678515cac3df5f203..57ad98ad2fa57287abc99f0ab554b4d60653a1e4 100644 (file)
@@ -48,6 +48,7 @@
 #include <core/producer/transition/transition_producer.h>
 #include <core/frame/audio_channel_layout.h>
 #include <core/frame/frame_transform.h>
+#include <core/producer/text/text_producer.h>
 #include <core/producer/stage.h>
 #include <core/producer/layer.h>
 #include <core/mixer/mixer.h>
@@ -2224,6 +2225,29 @@ std::wstring cls_command(command_context& ctx)
        return boost::to_upper_copy(replyString.str());
 }
 
+void fls_describer(core::help_sink& sink, const core::help_repository& repo)
+{
+       sink.short_description(L"List all fonts.");
+       sink.syntax(L"FLS");
+       sink.para()
+               ->text(L"Lists all font files in the ")->code(L"fonts")->text(L" folder. Use the command ")
+               ->see(L"INFO PATHS")->text(L" to get the path to the ")->code(L"fonts")->text(L" folder.");
+       sink.para()->text(L"Columns in order from left to right are: Font name and font path.");
+}
+
+std::wstring fls_command(command_context& ctx)
+{
+       std::wstringstream replyString;
+       replyString << L"200 FLS OK\r\n";
+
+       for (auto& font : core::text::list_fonts())
+               replyString << L"\"" << font.first << L"\" \"" << font.second << L"\"\r\n";
+
+       replyString << L"\r\n";
+
+       return replyString.str();
+}
+
 void tls_describer(core::help_sink& sink, const core::help_repository& repo)
 {
        sink.short_description(L"List all templates.");
@@ -2845,6 +2869,7 @@ void register_commands(amcp_command_repository& repo)
 
        repo.register_command(                  L"Query Commands",              L"CINF",                                                cinf_describer,                                         cinf_command,                                   1);
        repo.register_command(                  L"Query Commands",              L"CLS",                                                 cls_describer,                                          cls_command,                                    0);
+       repo.register_command(                  L"Query Commands",              L"FLS",                                                 fls_describer,                                          fls_command,                                    0);
        repo.register_command(                  L"Query Commands",              L"TLS",                                                 tls_describer,                                          tls_command,                                    0);
        repo.register_command(                  L"Query Commands",              L"VERSION",                                             version_describer,                                      version_command,                                0);
        repo.register_command(                  L"Query Commands",              L"INFO",                                                info_describer,                                         info_command,                                   0);