From 2afda6f34d7b1f8bb06d6f8e833152da86ac5203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 4 Mar 2013 15:20:35 +0100 Subject: [PATCH] Text: split out HarfBuzz layouting into new HarfBuzzFont class. It now allows to transparently select between FreeType and HarfBuzz layouting. The new class is built only if USE_HARFBUZZ is enabled. --- src/Text/CMakeLists.txt | 13 +++-- src/Text/FreeTypeFont.cpp | 54 ------------------- src/Text/FreeTypeFont.h | 21 +++----- src/Text/HarfBuzzFont.cpp | 106 ++++++++++++++++++++++++++++++++++++++ src/Text/HarfBuzzFont.h | 75 +++++++++++++++++++++++++++ src/Text/Text.h | 5 ++ src/Text/TextRenderer.h | 5 +- 7 files changed, 204 insertions(+), 75 deletions(-) create mode 100644 src/Text/HarfBuzzFont.cpp create mode 100644 src/Text/HarfBuzzFont.h diff --git a/src/Text/CMakeLists.txt b/src/Text/CMakeLists.txt index dd747e59e..b4e0d5e96 100644 --- a/src/Text/CMakeLists.txt +++ b/src/Text/CMakeLists.txt @@ -1,11 +1,6 @@ find_package(Freetype REQUIRED) include_directories(${FREETYPE_INCLUDE_DIRS}) -if(USE_HARFBUZZ) - find_package(HarfBuzz REQUIRED) - include_directories(${HARFBUZZ_INCLUDE_DIRS}) -endif() - set(MagnumText_SRCS AbstractFont.cpp FreeTypeFont.cpp @@ -18,6 +13,14 @@ set(MagnumText_HEADERS magnumTextVisibility.h) +if(USE_HARFBUZZ) + find_package(HarfBuzz REQUIRED) + include_directories(${HARFBUZZ_INCLUDE_DIRS}) + + set(MagnumText_SRCS ${MagnumText_SRCS} HarfBuzzFont.cpp) + set(MagnumText_HEADERS ${MagnumText_HEADERS} HarfBuzzFont.h) +endif() + add_library(MagnumText SHARED ${MagnumText_SRCS}) target_link_libraries(MagnumText Magnum MagnumTextureTools ${FREETYPE_LIBRARIES}) diff --git a/src/Text/FreeTypeFont.cpp b/src/Text/FreeTypeFont.cpp index 9444f3bce..37be7d45f 100644 --- a/src/Text/FreeTypeFont.cpp +++ b/src/Text/FreeTypeFont.cpp @@ -18,9 +18,6 @@ #include #include #include FT_FREETYPE_H -#ifdef MAGNUM_USE_HARFBUZZ -#include -#endif #include #include "Extensions.h" @@ -35,21 +32,12 @@ namespace { class FreeTypeLayouter: public AbstractLayouter { public: FreeTypeLayouter(FreeTypeFont& font, const Float size, const std::string& text); - ~FreeTypeLayouter(); std::tuple renderGlyph(const Vector2& cursorPosition, const UnsignedInt i) override; private: - #ifdef MAGNUM_USE_HARFBUZZ - const FreeTypeFont& font; - hb_buffer_t* buffer; - hb_glyph_info_t* glyphInfo; - hb_glyph_position_t* glyphPositions; - #else FreeTypeFont& font; std::vector glyphs; - #endif - const Float size; }; @@ -78,11 +66,6 @@ FreeTypeFont::FreeTypeFont(FreeTypeFontRenderer& renderer, const unsigned char* void FreeTypeFont::finishConstruction() { CORRADE_INTERNAL_ASSERT_OUTPUT(FT_Set_Char_Size(_ftFont, 0, _size*64, 100, 100) == 0); - #ifdef MAGNUM_USE_HARFBUZZ - /* Create Harfbuzz font */ - _hbFont = hb_ft_font_create(_ftFont, nullptr); - #endif - #ifndef MAGNUM_TARGET_GLES MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::texture_rg); #else @@ -182,9 +165,6 @@ void FreeTypeFont::prerenderDistanceField(const std::string& characters, const V } FreeTypeFont::~FreeTypeFont() { - #ifdef MAGNUM_USE_HARFBUZZ - hb_font_destroy(_hbFont); - #endif FT_Done_Face(_ftFont); } @@ -203,20 +183,6 @@ AbstractLayouter* FreeTypeFont::layout(const Float size, const std::string& text namespace { FreeTypeLayouter::FreeTypeLayouter(FreeTypeFont& font, const Float size, const std::string& text): font(font), size(size) { - #ifdef MAGNUM_USE_HARFBUZZ - /* Prepare HarfBuzz buffer */ - buffer = hb_buffer_create(); - hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); - hb_buffer_set_script(buffer, HB_SCRIPT_LATIN); - hb_buffer_set_language(buffer, hb_language_from_string("en", 2)); - - /* Layout the text */ - hb_buffer_add_utf8(buffer, text.c_str(), -1, 0, -1); - hb_shape(font.font(), buffer, nullptr, 0); - - glyphInfo = hb_buffer_get_glyph_infos(buffer, &_glyphCount); - glyphPositions = hb_buffer_get_glyph_positions(buffer, &_glyphCount); - #else /* Get glyph codes from characters */ glyphs.reserve(text.size()+1); _glyphCount = text.size(); @@ -225,38 +191,18 @@ FreeTypeLayouter::FreeTypeLayouter(FreeTypeFont& font, const Float size, const s std::tie(codepoint, i) = Corrade::Utility::Unicode::nextChar(text, i); glyphs.push_back(FT_Get_Char_Index(font.font(), codepoint)); } - #endif -} - -FreeTypeLayouter::~FreeTypeLayouter() { - #ifdef MAGNUM_USE_HARFBUZZ - /* Destroy HarfBuzz buffer */ - hb_buffer_destroy(buffer); - #endif } std::tuple FreeTypeLayouter::renderGlyph(const Vector2& cursorPosition, const UnsignedInt i) { /* Position of the texture in the resulting glyph, texture coordinates */ Rectangle texturePosition, textureCoordinates; - #ifdef MAGNUM_USE_HARFBUZZ - std::tie(texturePosition, textureCoordinates) = font[glyphInfo[i].codepoint]; - #else std::tie(texturePosition, textureCoordinates) = font[glyphs[i]]; - #endif - #ifdef MAGNUM_USE_HARFBUZZ - /* Glyph offset and advance to next glyph in normalized coordinates */ - Vector2 offset = Vector2(glyphPositions[i].x_offset, - glyphPositions[i].y_offset)/(64*font.size()); - Vector2 advance = Vector2(glyphPositions[i].x_advance, - glyphPositions[i].y_advance)/(64*font.size()); - #else /* Load glyph */ CORRADE_INTERNAL_ASSERT_OUTPUT(FT_Load_Glyph(font.font(), glyphs[i], FT_LOAD_DEFAULT) == 0); const FT_GlyphSlot slot = font.font()->glyph; Vector2 offset = Vector2(0, 0); /** @todo really? */ Vector2 advance = Vector2(slot->advance.x, slot->advance.y)/(64*font.size()); - #endif /* Absolute quad position, composed from cursor position, glyph offset and texture position, denormalized to requested text size */ diff --git a/src/Text/FreeTypeFont.h b/src/Text/FreeTypeFont.h index 62da7c0b9..f75285844 100644 --- a/src/Text/FreeTypeFont.h +++ b/src/Text/FreeTypeFont.h @@ -31,10 +31,6 @@ struct FT_LibraryRec_; typedef FT_LibraryRec_* FT_Library; struct FT_FaceRec_; typedef FT_FaceRec_* FT_Face; -struct hb_font_t; -struct hb_buffer_t; -struct hb_glyph_info_t; -struct hb_glyph_position_t; #endif namespace Magnum { namespace Text { @@ -148,25 +144,24 @@ class MAGNUM_TEXT_EXPORT FreeTypeFont: public AbstractFont { */ const std::tuple& operator[](char32_t character) const; - /** @brief %Font handle */ - #ifdef MAGNUM_USE_HARFBUZZ - inline hb_font_t* font() { return _hbFont; } - #else + /** @brief FreeType font handle */ inline FT_Face font() { return _ftFont; } - #endif AbstractLayouter* layout(const Float size, const std::string& text) override; + #ifdef DOXYGEN_GENERATING_OUTPUT + private: + #else + protected: + #endif + FT_Face _ftFont; + private: void MAGNUM_TEXT_LOCAL finishConstruction(); void MAGNUM_TEXT_LOCAL prerenderInternal(const std::string& characters, const Vector2i& atlasSize, const Int radius, Texture2D* output); std::unordered_map> glyphs; - FT_Face _ftFont; Float _size; - #ifdef MAGNUM_USE_HARFBUZZ - hb_font_t* _hbFont; - #endif }; }} diff --git a/src/Text/HarfBuzzFont.cpp b/src/Text/HarfBuzzFont.cpp new file mode 100644 index 000000000..31b1ba1f8 --- /dev/null +++ b/src/Text/HarfBuzzFont.cpp @@ -0,0 +1,106 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum 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 Lesser General Public License version 3 for more details. +*/ + +#include "HarfBuzzFont.h" + +#include + +namespace Magnum { namespace Text { + +namespace { + +class HarfBuzzLayouter: public AbstractLayouter { + public: + HarfBuzzLayouter(HarfBuzzFont& font, const Float size, const std::string& text); + ~HarfBuzzLayouter(); + + std::tuple renderGlyph(const Vector2& cursorPosition, const UnsignedInt i) override; + + private: + const HarfBuzzFont& font; + hb_buffer_t* buffer; + hb_glyph_info_t* glyphInfo; + hb_glyph_position_t* glyphPositions; + const Float size; +}; + +} + +HarfBuzzFont::HarfBuzzFont(FreeTypeFontRenderer& renderer, const std::string& fontFile, Float size): FreeTypeFont(renderer, fontFile, size) { + finishConstruction(); +} + +HarfBuzzFont::HarfBuzzFont(FreeTypeFontRenderer& renderer, const unsigned char* data, std::size_t dataSize, Float size): FreeTypeFont(renderer, data, dataSize, size) { + finishConstruction(); +} + +void HarfBuzzFont::finishConstruction() { + /* Create Harfbuzz font */ + _hbFont = hb_ft_font_create(_ftFont, nullptr); +} + +HarfBuzzFont::~HarfBuzzFont() { + hb_font_destroy(_hbFont); +} + +AbstractLayouter* HarfBuzzFont::layout(const Float size, const std::string& text) { + return new HarfBuzzLayouter(*this, size, text); +} + +namespace { + +HarfBuzzLayouter::HarfBuzzLayouter(HarfBuzzFont& font, const Float size, const std::string& text): font(font), size(size) { + /* Prepare HarfBuzz buffer */ + buffer = hb_buffer_create(); + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); + hb_buffer_set_script(buffer, HB_SCRIPT_LATIN); + hb_buffer_set_language(buffer, hb_language_from_string("en", 2)); + + /* Layout the text */ + hb_buffer_add_utf8(buffer, text.c_str(), -1, 0, -1); + hb_shape(font.font(), buffer, nullptr, 0); + + glyphInfo = hb_buffer_get_glyph_infos(buffer, &_glyphCount); + glyphPositions = hb_buffer_get_glyph_positions(buffer, &_glyphCount); +} + +HarfBuzzLayouter::~HarfBuzzLayouter() { + /* Destroy HarfBuzz buffer */ + hb_buffer_destroy(buffer); +} + +std::tuple HarfBuzzLayouter::renderGlyph(const Vector2& cursorPosition, const UnsignedInt i) { + /* Position of the texture in the resulting glyph, texture coordinates */ + Rectangle texturePosition, textureCoordinates; + std::tie(texturePosition, textureCoordinates) = font[glyphInfo[i].codepoint]; + + /* Glyph offset and advance to next glyph in normalized coordinates */ + Vector2 offset = Vector2(glyphPositions[i].x_offset, + glyphPositions[i].y_offset)/(64*font.size()); + Vector2 advance = Vector2(glyphPositions[i].x_advance, + glyphPositions[i].y_advance)/(64*font.size()); + + /* Absolute quad position, composed from cursor position, glyph offset + and texture position, denormalized to requested text size */ + Rectangle quadPosition = Rectangle::fromSize( + (cursorPosition + offset + Vector2(texturePosition.left(), texturePosition.bottom()))*size, + texturePosition.size()*size); + + return std::make_tuple(quadPosition, textureCoordinates, advance); +} + +} + +}} diff --git a/src/Text/HarfBuzzFont.h b/src/Text/HarfBuzzFont.h new file mode 100644 index 000000000..01a264b5c --- /dev/null +++ b/src/Text/HarfBuzzFont.h @@ -0,0 +1,75 @@ +#ifndef Magnum_Text_HarfBuzzFont_h +#define Magnum_Text_HarfBuzzFont_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum 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 Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Text::HarfBuzzFont + */ + +#include "Text/FreeTypeFont.h" + +#ifndef DOXYGEN_GENERATING_OUTPUT +struct hb_font_t; +#endif + +#ifndef MAGNUM_USE_HARFBUZZ +#error Magnum is not compiled with HarfBuzz support +#endif + +namespace Magnum { namespace Text { + +/** +@brief HarfBuzz font + +Improves FreeTypeFont with [HarfBuzz](http://www.freedesktop.org/wiki/Software/HarfBuzz) +text layouting capabilities, such as kerning, ligatures etc. See FreeTypeFont +class documentation for more information about usage. +*/ +class MAGNUM_TEXT_EXPORT HarfBuzzFont: public FreeTypeFont { + public: + /** + * @brief Create font from file + * @param renderer %Font renderer + * @param fontFile %Font file + * @param size %Font size + */ + explicit HarfBuzzFont(FreeTypeFontRenderer& renderer, const std::string& fontFile, Float size); + + /** + * @brief Create font from memory + * @param renderer %Font renderer + * @param data %Font data + * @param dataSize %Font data size + * @param size %Font size + */ + explicit HarfBuzzFont(FreeTypeFontRenderer& renderer, const unsigned char* data, std::size_t dataSize, Float size); + + ~HarfBuzzFont(); + + /** @brief HarfBuzz font handle */ + inline hb_font_t* font() { return _hbFont; } + + AbstractLayouter* layout(const Float size, const std::string& text) override; + + private: + void MAGNUM_TEXT_LOCAL finishConstruction(); + + hb_font_t* _hbFont; +}; + +}} + +#endif diff --git a/src/Text/Text.h b/src/Text/Text.h index d3ba2db87..1beb1ee51 100644 --- a/src/Text/Text.h +++ b/src/Text/Text.h @@ -21,6 +21,8 @@ #include "Types.h" +#include "magnumConfigure.h" + namespace Magnum { namespace Text { class AbstractFont; @@ -28,6 +30,9 @@ class AbstractLayouter; class FreeTypeFontRenderer; class FreeTypeFont; +#ifdef MAGNUM_USE_HARFBUZZ +class HarfBuzzFont; +#endif class AbstractTextRenderer; template class TextRenderer; diff --git a/src/Text/TextRenderer.h b/src/Text/TextRenderer.h index 32d792521..0cbefe4af 100644 --- a/src/Text/TextRenderer.h +++ b/src/Text/TextRenderer.h @@ -121,9 +121,8 @@ class MAGNUM_TEXT_EXPORT AbstractTextRenderer { /** @brief %Text renderer -Lays out the text into mesh using [HarfBuzz](http://www.freedesktop.org/wiki/Software/HarfBuzz) -library. Use of ligatures, kerning etc. depends on features supported by -particular font. See also Font. +Lays out the text into mesh using given Font. Use of ligatures, kerning etc. +depends on features supported by particular font and its layouter. @section TextRenderer-usage Usage