More improvements.

This commit is contained in:
Martín Lucas Golini
2024-06-13 02:20:23 -03:00
parent bb5ee4077f
commit b3cd551ff4
10 changed files with 126 additions and 63 deletions

View File

@@ -32,7 +32,7 @@ jobs:
tar xvzf premake-5.0.0-beta2-linux.tar.gz
- name: Build
run: |
./premake5 gmake2
./premake5 --with-text-shaper gmake2
cd make/linux
make all -j$(nproc) -e config=release_x86_64
- name: Unit Tests

View File

@@ -19,7 +19,7 @@ jobs:
brew install wget SDL2 premake
- name: Build
run: |
premake5 gmake2
premake5 --with-text-shaper gmake2
make -C make/macosx/ -e config=release_arm64
- name: Unit Tests
run: |

View File

@@ -28,7 +28,7 @@ jobs:
- name: Create project
shell: powershell
run: |
./premake5.exe --windows-vc-build vs2022
./premake5.exe --windows-vc-build --with-text-shaper vs2022
- name: Build
shell: cmd
run: |

View File

@@ -174,7 +174,7 @@ class EE_API FontTrueType : public Font {
Uint32 getGlyphIndex( const Uint32& codePoint ) const;
Glyph loadGlyph( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic,
Glyph loadGlyphByIndex( Uint32 codePoint, unsigned int characterSize, bool bold, bool italic,
Float outlineThickness, Page& page, const Float& maxWidth = 0.f ) const;
Rect findGlyphRect( Page& page, unsigned int width, unsigned int height ) const;
@@ -214,6 +214,7 @@ class EE_API FontTrueType : public Font {
bool mIsItalic{ false };
mutable UnorderedMap<unsigned int, unsigned int> mClosestCharacterSize;
mutable UnorderedMap<Uint32, Uint32> mCodePointIndexCache;
mutable UnorderedMap<Uint64, Float> mKerningCache;
FontHinting mHinting{ FontHinting::Full };
FontAntialiasing mAntialiasing{ FontAntialiasing::Grayscale };
FontTrueType* mFontBold{ nullptr };

View File

@@ -167,6 +167,7 @@ newoption { trigger = "thread-sanitizer", description ="Compile with ThreadSanit
newoption { trigger = "address-sanitizer", description = "Compile with AddressSanitizer." }
newoption { trigger = "time-trace", description = "Compile with time trace." }
newoption { trigger = "disable-static-build", description = "Disables eepp static build project, this is just a helper to avoid rebuilding twice eepp while developing the library." }
newoption { trigger = "with-text-shaper", description = "Enables text-shaping capabilities by relying on harfbuzz." }
newoption {
trigger = "with-backend",
description = "Select the backend to use for window and input handling.\n\t\t\tIf no backend is selected or if the selected is not installed the script will search for a backend present in the system, and will use it.",
@@ -741,8 +742,12 @@ function add_static_links()
links { "freetype-static", "libpng-static" }
end
links { "harfbuzz-static",
"SOIL2-static",
if _OPTIONS["with-text-shaper"] then
links { "harfbuzz-static" }
defines { "EE_TEXT_SHAPER_ENABLED" }
end
links { "SOIL2-static",
"libzip-static",
"jpeg-compressor-static",
"zlib-static",
@@ -1110,14 +1115,16 @@ solution "eepp"
includedirs { "src/thirdparty/freetype2/include", "src/thirdparty/libpng" }
build_base_configuration( "freetype" )
project "harfbuzz-static"
kind "StaticLib"
language "C++"
set_targetdir("libs/" .. os.get_real() .. "/thirdparty/")
defines { "HAVE_CONFIG_H" }
files { "src/thirdparty/harfbuzz/**.cc" }
includedirs { "src/thirdparty/freetype2/include", "src/thirdparty/harfbuzz" }
build_base_cpp_configuration( "harfbuzz" )
if _OPTIONS["with-text-shaper"] then
project "harfbuzz-static"
kind "StaticLib"
language "C++"
set_targetdir("libs/" .. os.get_real() .. "/thirdparty/")
defines { "HAVE_CONFIG_H" }
files { "src/thirdparty/harfbuzz/**.cc" }
includedirs { "src/thirdparty/freetype2/include", "src/thirdparty/harfbuzz" }
build_base_cpp_configuration( "harfbuzz" )
end
project "chipmunk-static"
kind "StaticLib"

View File

@@ -15,6 +15,7 @@ newoption { trigger = "thread-sanitizer", description = "Compile with ThreadSani
newoption { trigger = "address-sanitizer", description = "Compile with AddressSanitizer." }
newoption { trigger = "time-trace", description = "Compile with time tracing." }
newoption { trigger = "disable-static-build", description = "Disables eepp static build project, this is just a helper to avoid rebuilding twice eepp while developing the library." }
newoption { trigger = "with-text-shaper", description = "Enables text-shaping capabilities by relying on harfbuzz." }
newoption {
trigger = "with-backend",
description = "Select the backend to use for window and input handling.\n\t\t\tIf no backend is selected or if the selected is not installed the script will search for a backend present in the system, and will use it.",
@@ -497,8 +498,12 @@ function add_static_links()
links { "freetype-static", "libpng-static" }
end
links { "harfbuzz-static",
"SOIL2-static",
if _OPTIONS["with-text-shaper"] then
links { "harfbuzz-static" }
defines { "EE_TEXT_SHAPER_ENABLED" }
end
links { "SOIL2-static",
"chipmunk-static",
"libzip-static",
"jpeg-compressor-static",

View File

@@ -14,3 +14,4 @@
#define DR_FLAC_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define ECODE_USE_BACKWARD
#define EE_TEXT_SHAPER_ENABLED

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 13.0.1, 2024-05-10T21:21:06. -->
<!-- Written by QtCreator 13.0.2, 2024-06-13T02:20:10. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
@@ -116,7 +116,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{388e5431-b31b-42b3-b9ad-9002d279d75d}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">10</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">19</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">8</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">../../make/linux</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
@@ -193,7 +193,7 @@
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-debug-symbols gmake</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-debug-symbols --with-text-shaper gmake</value>
<value type="QString" key="ProjectExplorer.ProcessStep.Command">premake4</value>
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}../../../</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.ProcessStep</value>

View File

@@ -22,8 +22,10 @@ using namespace EE::Window;
#include <cstdlib>
#include <cstring>
#ifdef EE_TEXT_SHAPER_ENABLED
#include <harfbuzz/hb-ft.h>
#include <harfbuzz/hb.h>
#endif
namespace {
@@ -240,8 +242,9 @@ bool FontTrueType::loadFromPack( Pack* pack, std::string filePackPath ) {
bool FontTrueType::setFontFace( void* _face ) {
FT_Face face = (FT_Face)_face;
mFace = face;
#ifdef EE_TEXT_SHAPER_ENABLED
mHBFont = hb_ft_font_create( static_cast<FT_Face>( face ), NULL );
#endif
mIsMonospaceComplete = mIsMonospace = FT_IS_FIXED_WIDTH( face );
mIsColorEmojiFont = checkIsColorEmojiFont( face );
mIsEmojiFont = FT_Get_Char_Index( face, 0x1F600 ) != 0;
@@ -334,8 +337,8 @@ const Glyph& FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSiz
static_cast<FontTrueType*>( FontManager::instance()->getColorEmojiFont() );
if ( ( idx = fontEmoji->getGlyphIndex( codePoint ) ) ) {
return fontEmoji->getGlyphByIndex( idx, characterSize, bold, italic,
outlineThickness, getPage( characterSize ),
maxWidth );
0 /* outline thickness won't work here */,
getPage( characterSize ), maxWidth );
}
} else if ( !mIsEmojiFont && FontManager::instance()->getEmojiFont() != nullptr &&
FontManager::instance()->getEmojiFont()->getType() == FontType::TTF ) {
@@ -420,8 +423,8 @@ const Glyph& FontTrueType::getGlyphByIndex( Uint32 index, unsigned int character
return it->second;
} else {
// Not found: we have to load it
Glyph glyph =
loadGlyph( index, characterSize, bold, italic, outlineThickness, page, maxWidth );
Glyph glyph = loadGlyphByIndex( index, characterSize, bold, italic, outlineThickness, page,
maxWidth );
return glyphs.emplace( key, glyph ).first->second;
}
@@ -564,12 +567,15 @@ Float FontTrueType::getKerning( Uint32 first, Uint32 second, unsigned int charac
// Get the kerning vector
FT_Vector kerning;
kerning.x = kerning.y = 0;
if ( FT_HAS_KERNING( face ) )
FT_Get_Kerning( face, index1, index2, FT_KERNING_UNFITTED, &kerning );
// X advance is already in pixels for bitmap fonts
if ( !FT_IS_SCALABLE( face ) )
return static_cast<Float>( kerning.x );
if ( glyph1.font == glyph2.font ) {
if ( FT_HAS_KERNING( face ) )
FT_Get_Kerning( face, index1, index2, FT_KERNING_UNFITTED, &kerning );
// X advance is already in pixels for bitmap fonts
if ( !FT_IS_SCALABLE( face ) )
return static_cast<Float>( kerning.x );
}
// Return the X advance
return std::floor(
@@ -588,6 +594,11 @@ Float FontTrueType::getKerningFromGlyphIndex( Uint32 index1, Uint32 index2,
if ( index1 == 0 || index2 == 0 || isMonospace() )
return 0.f;
auto pair = static_cast<Uint64>( index1 ) << 32 | index2;
auto found = mKerningCache.find( pair );
if ( found != mKerningCache.end() )
return found->second;
FT_Face face = static_cast<FT_Face>( mFace );
if ( face && setCurrentSize( characterSize ) ) {
@@ -604,15 +615,20 @@ Float FontTrueType::getKerningFromGlyphIndex( Uint32 index1, Uint32 index2,
FT_Get_Kerning( face, index1, index2, FT_KERNING_UNFITTED, &kerning );
// X advance is already in pixels for bitmap fonts
if ( !FT_IS_SCALABLE( face ) )
if ( !FT_IS_SCALABLE( face ) ) {
mKerningCache.insert( { pair, static_cast<Float>( kerning.x ) } );
return static_cast<Float>( kerning.x );
}
// Return the X advance
return std::floor(
( secondLsbDelta - firstRsbDelta + static_cast<float>( kerning.x ) + 32 ) /
static_cast<float>( 1 << 6 ) );
Float val =
std::floor( ( secondLsbDelta - firstRsbDelta + static_cast<float>( kerning.x ) + 32 ) /
static_cast<float>( 1 << 6 ) );
mKerningCache.insert( { pair, val } );
return val;
} else {
// Invalid font, or no kerning
mKerningCache.insert( { pair, 0.f } );
return 0.f;
}
}
@@ -728,8 +744,10 @@ void FontTrueType::cleanup() {
mCallbacks.clear();
mNumCallBacks = 0;
#ifdef EE_TEXT_SHAPER_ENABLED
if ( mHBFont )
hb_font_destroy( (hb_font_t*)mHBFont );
#endif
// Destroy the stroker
if ( mStroker )
@@ -797,8 +815,9 @@ fontSetRenderOptions( FT_Library library, FontAntialiasing antialiasing, FontHin
return FT_RENDER_MODE_NORMAL;
}
Glyph FontTrueType::loadGlyph( Uint32 index, unsigned int characterSize, bool bold, bool /*italic*/,
Float outlineThickness, Page& page, const Float& maxWidth ) const {
Glyph FontTrueType::loadGlyphByIndex( Uint32 index, unsigned int characterSize, bool bold,
bool /*italic*/, Float outlineThickness, Page& page,
const Float& maxWidth ) const {
// The glyph to return
Glyph glyph;

View File

@@ -12,8 +12,10 @@
#include <eepp/graphics/texturefactory.hpp>
#include <limits>
#ifdef EE_TEXT_SHAPER_ENABLED
#include <harfbuzz/hb-ft.h>
#include <harfbuzz/hb.h>
#endif
namespace EE { namespace Graphics {
@@ -79,6 +81,7 @@ class TextShapeRun {
bool mIsNewLine{ false };
FontStyleConfig& mConfig;
};
} // namespace
std::string Text::styleFlagToString( const Uint32& flags ) {
@@ -218,7 +221,7 @@ static inline void drawGlyph( BatchRenderer* BR, GlyphDrawable* gd, const Vector
}
}
static inline void idrawUnderline( Font* font, Float fontSize, const Color& fontColor,
static inline void _drawUnderline( Font* font, Float fontSize, const Color& fontColor,
const Vector2f& cpos, const Uint32& style, BatchRenderer* BR,
Float outlineThickness, const Vector2f& pos, Float width,
const Color& shadowColor, const Vector2f& shadowOffset,
@@ -253,7 +256,7 @@ void Text::drawUnderline( const Vector2f& pos, Float width, Font* font, Float fo
const Color& outlineColor, const Color& shadowColor,
const Vector2f& shadowOffset ) {
BatchRenderer* BR = GlobalBatchRenderer::instance();
idrawUnderline( font, fontSize, fontColor, pos, style, BR, outlineThickness, pos, width,
_drawUnderline( font, fontSize, fontColor, pos, style, BR, outlineThickness, pos, width,
shadowColor, shadowOffset, outlineColor );
}
@@ -329,7 +332,7 @@ Sizef Text::draw( const StringType& string, const Vector2f& pos, Font* font, Flo
}
case '\n': {
if ( style & Text::Underlined ) {
idrawUnderline( font, fontSize, fontColor, cpos, style, BR, outlineThickness,
_drawUnderline( font, fontSize, fontColor, cpos, style, BR, outlineThickness,
pos, width, shadowColor, shadowOffset, outlineColor );
}
if ( style & Text::StrikeThrough ) {
@@ -378,7 +381,7 @@ Sizef Text::draw( const StringType& string, const Vector2f& pos, Font* font, Flo
}
if ( ( style & Text::Underlined ) && width != 0 ) {
idrawUnderline( font, fontSize, fontColor, cpos, style, BR, outlineThickness, pos, width,
_drawUnderline( font, fontSize, fontColor, cpos, style, BR, outlineThickness, pos, width,
shadowColor, shadowOffset, outlineColor );
}
@@ -1302,6 +1305,7 @@ void Text::ensureGeometryUpdate() {
break;
}
#ifdef EE_TEXT_SHAPER_ENABLED
if ( mFontStyleConfig.Font->getType() == FontType::TTF ) {
FontTrueType* rFont = static_cast<FontTrueType*>( mFontStyleConfig.Font );
hb_buffer_t* hbBuffer = hb_buffer_create();
@@ -1339,25 +1343,54 @@ void Text::ensureGeometryUpdate() {
hb_glyph_info_t curGlyph = glyphInfo[i];
hb_glyph_position_t curGlyphPos = glyphPos[i];
x += rFont->getKerningFromGlyphIndex(
prevGlyphIndex, curGlyph.codepoint, mFontStyleConfig.CharacterSize, bold,
reqItalic, mFontStyleConfig.OutlineThickness );
Float currentX = x + ( curGlyphPos.x_offset / 64.f );
Float currentY = y + ( curGlyphPos.y_offset / 64.f );
// Apply the outline
if ( mFontStyleConfig.OutlineThickness != 0 ) {
const Glyph& glyph = font->getGlyphByIndex(
curGlyph.codepoint, mFontStyleConfig.CharacterSize, bold, reqItalic,
mFontStyleConfig.OutlineThickness,
rFont->getPage( mFontStyleConfig.CharacterSize ), 0 );
Float left = glyph.bounds.Left;
Float top = glyph.bounds.Top;
Float right = glyph.bounds.Left + glyph.bounds.Right;
Float bottom = glyph.bounds.Top + glyph.bounds.Bottom;
// Add the outline glyph to the vertices
if ( glyph.bounds.Right > 0 && glyph.bounds.Bottom > 0 ) {
addGlyphQuad( mOutlineVertices, Vector2f( currentX, currentY ), glyph,
italic, mFontStyleConfig.OutlineThickness, centerDiffX );
}
// Update the current bounds with the outlined glyph bounds
minX = std::min( minX, x + left - italic * bottom -
mFontStyleConfig.OutlineThickness );
maxX = std::max( maxX,
x + right - italic * top - mFontStyleConfig.OutlineThickness );
minY = std::min( minY, y + top - mFontStyleConfig.OutlineThickness );
maxY = std::max( maxY, y + bottom - mFontStyleConfig.OutlineThickness );
if ( mCachedWidthNeedUpdate ) {
maxW = std::max( maxW, x + glyph.advance - italic * top -
mFontStyleConfig.OutlineThickness );
}
}
// Extract the current glyph's description
const Glyph& glyph = font->getGlyphByIndex(
curGlyph.codepoint, mFontStyleConfig.CharacterSize, bold, italic, 0,
curGlyph.codepoint, mFontStyleConfig.CharacterSize, bold, reqItalic, 0,
rFont->getPage( mFontStyleConfig.CharacterSize ), 0 );
if ( prevGlyphIndex != 0 ) {
x += rFont->getKerningFromGlyphIndex( prevGlyphIndex, curGlyph.codepoint,
mFontStyleConfig.CharacterSize, bold,
italic, 0 );
}
Float left = glyph.bounds.Left;
Float top = glyph.bounds.Top;
Float right = glyph.bounds.Left + glyph.bounds.Right;
Float bottom = glyph.bounds.Top + glyph.bounds.Bottom;
Float currentX = x + ( curGlyphPos.x_offset / 64.f );
Float currentY = y + ( curGlyphPos.y_offset / 64.f );
// Add a quad for the current character
if ( glyph.bounds.Right > 0 && glyph.bounds.Bottom > 0 ) {
addGlyphQuad( mVertices, Vector2f( currentX, currentY ), glyph, italic, 0,
@@ -1385,7 +1418,7 @@ void Text::ensureGeometryUpdate() {
}
// If we're using the underlined style, add the last line
if ( underlined ) {
if ( underlined && run.runIsNewLine() ) {
addLine( mVertices, x, y, underlineOffset, underlineThickness, 0, centerDiffX );
if ( mFontStyleConfig.OutlineThickness != 0 )
@@ -1394,7 +1427,7 @@ void Text::ensureGeometryUpdate() {
}
// If we're using the strike through style, add the last line across all characters
if ( strikeThrough ) {
if ( strikeThrough && run.runIsNewLine() ) {
addLine( mVertices, x, y, strikeThroughOffset, underlineThickness, 0, centerDiffX );
if ( mFontStyleConfig.OutlineThickness != 0 )
@@ -1428,6 +1461,7 @@ void Text::ensureGeometryUpdate() {
return;
}
#endif
for ( std::size_t i = 0; i < size; ++i ) {
Uint32 curChar = mString[i];
@@ -1514,7 +1548,7 @@ void Text::ensureGeometryUpdate() {
if ( mFontStyleConfig.OutlineThickness != 0 ) {
const Glyph& glyph =
mFontStyleConfig.Font->getGlyph( curChar, mFontStyleConfig.CharacterSize, bold,
italic, mFontStyleConfig.OutlineThickness );
reqItalic, mFontStyleConfig.OutlineThickness );
Float left = glyph.bounds.Left;
Float top = glyph.bounds.Top;
@@ -1615,9 +1649,10 @@ void Text::ensureColorUpdate() {
if ( mContainsColorEmoji ) {
auto positions = Font::emojiCodePointsPositions( mString );
for ( const auto& position : positions )
for ( const auto& position : positions ) {
setFillColor( Color( 255, 255, 255, mFontStyleConfig.FontColor.a ), position,
position );
}
}
}
}
@@ -1752,7 +1787,7 @@ void Text::setFillColor( const Color& color, Uint32 from, Uint32 to ) {
}
}
if ( rto == s ) {
if ( to == s ) {
if ( underlined ) {
lpos++;
Uint32 pos = lpos * GLi->quadVertexs();
@@ -1932,21 +1967,18 @@ void Text::addGlyphQuad( std::vector<VertexCoords>& vertices, Vector2f position,
Uint32 Text::getTotalVertices() {
bool underlined = ( mFontStyleConfig.Style & Underlined ) != 0;
bool strikeThrough = ( mFontStyleConfig.Style & StrikeThrough ) != 0;
size_t sl = mString.size();
size_t sv = sl * GLi->quadVertexs();
String::StringBaseType* c = &mString[0];
size_t sv = mString.size() * GLi->quadVertexs();
Uint32 skiped = 0;
bool lineHasChars = false;
while ( '\0' != *c ) {
for ( const auto& ch : mString ) {
lineHasChars = true;
if ( ' ' == *c || '\n' == *c || '\t' == *c || '\r' == *c ) {
if ( ' ' == ch || '\n' == ch || '\t' == ch || '\r' == ch ) {
lineHasChars = false;
skiped++;
if ( '\n' == *c ) {
if ( '\n' == ch ) {
if ( underlined )
skiped--;
@@ -1954,8 +1986,6 @@ Uint32 Text::getTotalVertices() {
skiped--;
}
}
c++;
}
if ( lineHasChars ) {