Added support for non-colored emoji fonts.

This commit is contained in:
Martín Lucas Golini
2022-03-03 21:43:36 -03:00
parent 388a66e570
commit e26d9cceff
14 changed files with 178 additions and 65 deletions

Binary file not shown.

View File

@@ -1,36 +1,42 @@
[
{
"file_patterns": ["%.php$"],
"warning_pattern": "[%a ]+:%s+(.*)%s+in%s.*on%sline%s+(%d+)",
"warning_pattern_order": { "line": 2, "col": 0, "message": 1 },
"command": "php -l $FILENAME"
"file_patterns": ["%.php$"],
"warning_pattern": "[%a ]+:%s+(.*)%s+in%s.*on%sline%s+(%d+)",
"warning_pattern_order": { "line": 2, "col": 0, "message": 1 },
"command": "php -l $FILENAME"
},
{
"file_patterns": ["%.js$", "%.ts$"],
"warning_pattern": "[^:]:(%d+):(%d+): ([^%[]+)%[([^\n]+)",
"warning_pattern_order": { "line": 1, "col": 2, "message": 3, "type": 4 },
"command": "eslint --no-ignore --format unix $FILENAME"
"file_patterns": ["%.js$", "%.ts$"],
"warning_pattern": "[^:]:(%d+):(%d+): ([^%[]+)%[([^\n]+)",
"warning_pattern_order": { "line": 1, "col": 2, "message": 3, "type": 4 },
"command": "eslint --no-ignore --format unix $FILENAME"
},
{
"file_patterns": ["%.lua$"],
"warning_pattern": "[^:]:(%d+):(%d+):[%s]?([^\n]+)",
"command": "luacheck $FILENAME --formatter=plain -g --no-max-line-length"
"file_patterns": ["%.lua$"],
"warning_pattern": "[^:]:(%d+):(%d+):[%s]?([^\n]+)",
"command": "luacheck $FILENAME --formatter=plain -g --no-max-line-length"
},
{
"file_patterns": ["%.py$"],
"warning_pattern": "[^:]:(%d+):(%d+):%s([^\n]+)",
"command": "pycodestyle --ignore=E402 $FILENAME"
"file_patterns": ["%.py$"],
"warning_pattern": "[^:]:(%d+):(%d+):%s([^\n]+)",
"command": "pycodestyle --ignore=E402 $FILENAME"
},
{
"file_patterns": ["%.sh$"],
"warning_pattern": "[^:]:(%d+):(%d+):%s?([^%s]*)([^\n]*)",
"warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 },
"command": "shellcheck -f gcc $FILENAME"
"file_patterns": ["%.sh$"],
"warning_pattern": "[^:]:(%d+):(%d+):%s?([^%s]*)([^\n]*)",
"warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 },
"command": "shellcheck -f gcc $FILENAME"
},
{
"file_patterns": ["%.sol$"],
"warning_pattern": "(%d+):(%d+)%s.(%w*)%s.([^\n]*)",
"warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 },
"command": "solhint $FILENAME"
"file_patterns": ["%.sol$"],
"warning_pattern": "(%d+):(%d+)%s.(%w*)%s.([^\n]*)",
"warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 },
"command": "solhint $FILENAME"
},
{
"file_patterns": ["%.cpp$", "%.hpp$", "%.cxx$", "%.hxx$"],
"warning_pattern": "[^:]:(%d+):(%d+):%s?([^%s]*)([^\n]*)",
"warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 },
"command": "cppcheck --language=c++ --enable=all --template=gcc $FILENAME"
}
]

View File

@@ -61,7 +61,7 @@ class EE_API Font {
virtual ~Font();
/** @return The current font height */
virtual Uint32 getFontHeight( const Uint32& characterSize ) = 0;
virtual Uint32 getFontHeight( const Uint32& characterSize ) const = 0;
/** @return The type of the instance of the font */
const FontType& getType() const;

View File

@@ -42,7 +42,7 @@ class EE_API FontBMFont : public Font {
Float getLineSpacing( unsigned int characterSize ) const;
Uint32 getFontHeight( const Uint32& characterSize );
Uint32 getFontHeight( const Uint32& characterSize ) const;
Float getUnderlinePosition( unsigned int characterSize ) const;

View File

@@ -23,12 +23,17 @@ class EE_API FontManager : public ResourceManager<Font> {
/** @brief Adds a new font to the manager */
Graphics::Font* add( Graphics::Font* Font );
void setColorEmojiFont( Graphics::Font* font ) { mColorEmojiFont = font; }
Font* getColorEmojiFont() const;
Font* getColorEmojiFont() const { return mColorEmojiFont; }
void setColorEmojiFont( Graphics::Font* font );
Font* getEmojiFont() const;
void setEmojiFont( Font* newEmojiFont );
protected:
Font* mColorEmojiFont{ nullptr };
Font* mEmojiFont{ nullptr };
FontManager();
};

View File

@@ -46,7 +46,7 @@ class EE_API FontSprite : public Font {
Float getLineSpacing( unsigned int characterSize ) const;
Uint32 getFontHeight( const Uint32& characterSize );
Uint32 getFontHeight( const Uint32& characterSize ) const;
Float getUnderlinePosition( unsigned int characterSize ) const;

View File

@@ -40,7 +40,7 @@ class EE_API FontTrueType : public Font {
Float getLineSpacing( unsigned int characterSize ) const;
Uint32 getFontHeight( const Uint32& characterSize );
Uint32 getFontHeight( const Uint32& characterSize ) const;
Float getUnderlinePosition( unsigned int characterSize ) const;
@@ -62,6 +62,8 @@ class EE_API FontTrueType : public Font {
bool isMonospace() const;
bool isEmojiFont() const { return mIsEmojiFont; }
protected:
explicit FontTrueType( const std::string& FontName );
@@ -118,6 +120,7 @@ class EE_API FontTrueType : public Font {
mPixelBuffer; ///< Pixel buffer holding a glyph's pixels before being written to the texture
bool mBoldAdvanceSameAsRegular;
bool mIsColorEmojiFont{ false };
bool mIsEmojiFont{ false };
mutable std::map<unsigned int, unsigned int> mClosestCharacterSize;
Uint64 getCharIndexKey( Uint32 codePoint, bool bold, Float outlineThickness ) const;

View File

@@ -231,7 +231,7 @@ Float FontBMFont::getLineSpacing( unsigned int characterSize ) const {
return ( (Float)characterSize / mFontSize ) * mFontSize;
}
Uint32 FontBMFont::getFontHeight( const Uint32& characterSize ) {
Uint32 FontBMFont::getFontHeight( const Uint32& characterSize ) const {
return ( Uint32 )( (Float)characterSize / mFontSize ) * mFontSize;
}

View File

@@ -13,4 +13,20 @@ Graphics::Font* FontManager::add( Graphics::Font* Font ) {
return ResourceManager<Graphics::Font>::add( Font );
}
void FontManager::setColorEmojiFont( Font* font ) {
mColorEmojiFont = font;
}
Graphics::Font* FontManager::getColorEmojiFont() const {
return mColorEmojiFont;
}
Graphics::Font* FontManager::getEmojiFont() const {
return mEmojiFont;
}
void FontManager::setEmojiFont( Graphics::Font* newEmojiFont ) {
mEmojiFont = newEmojiFont;
}
}} // namespace EE::Graphics

View File

@@ -222,7 +222,7 @@ Float FontSprite::getLineSpacing( unsigned int characterSize ) const {
return ( (Float)characterSize / mFontSize ) * mFontSize;
}
Uint32 FontSprite::getFontHeight( const Uint32& characterSize ) {
Uint32 FontSprite::getFontHeight( const Uint32& characterSize ) const {
return ( Uint32 )( (Float)characterSize / mFontSize ) * mFontSize;
}

View File

@@ -120,10 +120,14 @@ bool FontTrueType::loadFromFile( const std::string& filename ) {
mFace = face;
mIsColorEmojiFont = checkIsColorEmojiFont( static_cast<FT_Face>( mFace ) );
mIsEmojiFont = FT_Get_Char_Index( static_cast<FT_Face>( mFace ), 0x1F600 ) != 0;
if ( mIsColorEmojiFont && FontManager::instance()->getColorEmojiFont() == nullptr )
FontManager::instance()->setColorEmojiFont( this );
if ( mIsEmojiFont && FontManager::instance()->getEmojiFont() == nullptr )
FontManager::instance()->setEmojiFont( this );
FT_Stroker stroker = nullptr;
if ( !mIsColorEmojiFont ) {
// Load the stroker that will be used to outline the font
@@ -188,10 +192,14 @@ bool FontTrueType::loadFromMemory( const void* data, std::size_t sizeInBytes, bo
mFace = face;
mIsColorEmojiFont = checkIsColorEmojiFont( static_cast<FT_Face>( mFace ) );
mIsEmojiFont = FT_Get_Char_Index( static_cast<FT_Face>( mFace ), 0x1F600 ) != 0;
if ( mIsColorEmojiFont && FontManager::instance()->getColorEmojiFont() == nullptr )
FontManager::instance()->setColorEmojiFont( this );
if ( mIsEmojiFont && FontManager::instance()->getEmojiFont() == nullptr )
FontManager::instance()->setEmojiFont( this );
// Load the stroker that will be used to outline the font
FT_Stroker stroker = nullptr;
if ( !mIsColorEmojiFont ) {
@@ -264,11 +272,15 @@ bool FontTrueType::loadFromStream( IOStream& stream ) {
mFace = face;
mIsColorEmojiFont = checkIsColorEmojiFont( static_cast<FT_Face>( mFace ) );
mIsEmojiFont = FT_Get_Char_Index( static_cast<FT_Face>( mFace ), 0x1F600 ) != 0;
FT_Stroker stroker = nullptr;
if ( mIsColorEmojiFont && FontManager::instance()->getColorEmojiFont() == nullptr )
FontManager::instance()->setColorEmojiFont( this );
if ( mIsEmojiFont && FontManager::instance()->getEmojiFont() == nullptr )
FontManager::instance()->setEmojiFont( this );
if ( !mIsColorEmojiFont ) {
// Load the stroker that will be used to outline the font
if ( FT_Stroker_New( static_cast<FT_Library>( mLibrary ), &stroker ) != 0 ) {
@@ -324,7 +336,8 @@ Uint64 FontTrueType::getCharIndexKey( Uint32 codePoint, bool bold, Float outline
Uint32 charIndex = FT_Get_Char_Index( static_cast<FT_Face>( mFace ), codePoint );
Uint64 key = combine( outlineThickness, bold, charIndex );
if ( charIndex == 0 && !mIsColorEmojiFont && Font::isEmojiCodePoint( codePoint ) ) {
if ( charIndex == 0 && Font::isEmojiCodePoint( codePoint ) && !mIsColorEmojiFont &&
!mIsEmojiFont ) {
if ( FontManager::instance()->getColorEmojiFont() != nullptr &&
FontManager::instance()->getColorEmojiFont()->getType() == FontType::TTF ) {
FontTrueType* fontEmoji =
@@ -332,6 +345,13 @@ Uint64 FontTrueType::getCharIndexKey( Uint32 codePoint, bool bold, Float outline
key =
combine( outlineThickness, bold,
FT_Get_Char_Index( static_cast<FT_Face>( fontEmoji->mFace ), codePoint ) );
} else if ( FontManager::instance()->getEmojiFont() != nullptr &&
FontManager::instance()->getEmojiFont()->getType() == FontType::TTF ) {
FontTrueType* fontEmoji =
static_cast<FontTrueType*>( FontManager::instance()->getEmojiFont() );
key =
combine( outlineThickness, bold,
FT_Get_Char_Index( static_cast<FT_Face>( fontEmoji->mFace ), codePoint ) );
}
}
@@ -422,7 +442,7 @@ Float FontTrueType::getLineSpacing( unsigned int characterSize ) const {
#define FT_FLOOR( X ) ( ( X & -64 ) / 64 )
#define FT_CEIL( X ) ( ( ( X + 63 ) & -64 ) / 64 )
Uint32 FontTrueType::getFontHeight( const Uint32& characterSize ) {
Uint32 FontTrueType::getFontHeight( const Uint32& characterSize ) const {
FT_Face face = static_cast<FT_Face>( mFace );
if ( face && setCurrentSize( characterSize ) ) {
@@ -545,25 +565,39 @@ void FontTrueType::cleanup() {
}
Glyph FontTrueType::loadGlyph( Uint32 codePoint, unsigned int characterSize, bool bold,
Float outlineThickness, Page& page, const Float& forceSize ) const {
Float outlineThickness, Page& page, const Float& maxWidth ) const {
// The glyph to return
Glyph glyph;
if ( !mIsColorEmojiFont && Font::isEmojiCodePoint( codePoint ) ) {
if ( FontManager::instance()->getColorEmojiFont() != nullptr &&
if ( Font::isEmojiCodePoint( codePoint ) && !mIsColorEmojiFont && !mIsEmojiFont ) {
if ( !mIsColorEmojiFont && FontManager::instance()->getColorEmojiFont() != nullptr &&
FontManager::instance()->getColorEmojiFont()->getType() == FontType::TTF ) {
Float forcedSize = 0.f;
Float maxWidth = 0.f;
if ( isMonospace() ) {
Glyph monospaceGlyph = getGlyph( ' ', characterSize, bold, outlineThickness );
forcedSize = monospaceGlyph.advance;
maxWidth = monospaceGlyph.advance;
}
FontTrueType* fontEmoji =
static_cast<FontTrueType*>( FontManager::instance()->getColorEmojiFont() );
return fontEmoji->loadGlyph( codePoint, characterSize, bold, outlineThickness, page,
forcedSize );
maxWidth );
} else if ( !mIsEmojiFont && FontManager::instance()->getEmojiFont() != nullptr &&
FontManager::instance()->getEmojiFont()->getType() == FontType::TTF ) {
Float maxWidth = 0.f;
if ( isMonospace() ) {
Glyph monospaceGlyph = getGlyph( ' ', characterSize, bold, outlineThickness );
maxWidth = monospaceGlyph.advance;
}
FontTrueType* fontEmoji =
static_cast<FontTrueType*>( FontManager::instance()->getEmojiFont() );
return fontEmoji->loadGlyph( codePoint, characterSize, bold, outlineThickness, page,
maxWidth );
}
}
@@ -640,8 +674,8 @@ Glyph FontTrueType::loadGlyph( Uint32 codePoint, unsigned int characterSize, boo
glyph.advance =
static_cast<Float>( face->glyph->metrics.horiAdvance ) / static_cast<Float>( 1 << 6 );
if ( forceSize > 0.f )
glyph.advance = forceSize;
if ( maxWidth > 0.f )
glyph.advance = maxWidth;
if ( bold && !mBoldAdvanceSameAsRegular )
glyph.advance += static_cast<Float>( weight ) / static_cast<Float>( 1 << 6 );
@@ -654,22 +688,25 @@ Glyph FontTrueType::loadGlyph( Uint32 codePoint, unsigned int characterSize, boo
// pollute them with pixels from neighbors
const int padding = 2;
Float scale = mIsColorEmojiFont
? (Float)( forceSize > 0.f ? forceSize : characterSize ) / (Float)height
: 1.f;
Float scale = 1.f;
if ( mIsColorEmojiFont || mIsEmojiFont )
scale = eemin( 1.f, (Float)( maxWidth > 0.f ? maxWidth : characterSize ) /
(Float)( maxWidth > 0.f ? width : height ) );
int destWidth = width;
int destHeight = height;
if ( mIsColorEmojiFont ) {
if ( maxWidth <= 0.f )
glyph.advance *= scale;
if ( scale >= 1.f ) {
destWidth *= scale;
destHeight *= scale;
if ( forceSize <= 0.f )
glyph.advance *= scale;
width += 2 * padding;
height += 2 * padding;
}
width += 2 * padding;
height += 2 * padding;
destWidth += 2 * padding;
destHeight += 2 * padding;
@@ -725,7 +762,7 @@ Glyph FontTrueType::loadGlyph( Uint32 codePoint, unsigned int characterSize, boo
for ( size_t y = 0; y < bitmap.rows; ++y ) {
for ( size_t x = 0; x < bitmap.width; ++x ) {
Color col = source.getPixel( x, y );
dest.setPixel( x + padding, y + padding, Color( col.b, col.g, col.r, col.a ) );
dest.setPixel( x, y, Color( col.b, col.g, col.r, col.a ) );
}
}
@@ -741,14 +778,38 @@ Glyph FontTrueType::loadGlyph( Uint32 codePoint, unsigned int characterSize, boo
destHeight = dest.getHeight() + 2 * padding;
}
} else {
// Pixels are 8 bits gray levels
for ( int y = padding; y < height - padding; ++y ) {
for ( int x = padding; x < width - padding; ++x ) {
// The color channels remain white, just fill the alpha channel
std::size_t index = x + y * width;
mPixelBuffer[index * 4 + 3] = pixels[x - padding];
if ( scale < 1.f ) {
// Pixels are 8 bits gray levels
for ( int y = 0; y < height; ++y ) {
for ( int x = 0; x < width; ++x ) {
// The color channels remain white, just fill the alpha channel
std::size_t index = x + y * width;
mPixelBuffer[index * 4 + 3] = pixels[x];
}
pixels += bitmap.pitch;
}
Image dest( &mPixelBuffer[0], bitmap.width, bitmap.rows, 4 );
dest.avoidFreeImage( true );
dest.scale( scale );
dest.avoidFreeImage( true );
pixelPtr = dest.getPixels();
glyph.bounds.Left = glyph.bounds.Left * scale;
glyph.bounds.Right *= scale;
glyph.bounds.Top = glyph.bounds.Top * scale;
glyph.bounds.Bottom *= scale;
destWidth = dest.getWidth() + 2 * padding;
destHeight = dest.getHeight() + 2 * padding;
} else {
// Pixels are 8 bits gray levels
for ( int y = padding; y < height - padding; ++y ) {
for ( int x = padding; x < width - padding; ++x ) {
// The color channels remain white, just fill the alpha channel
std::size_t index = x + y * width;
mPixelBuffer[index * 4 + 3] = pixels[x - padding];
}
pixels += bitmap.pitch;
}
pixels += bitmap.pitch;
}
}
@@ -761,7 +822,7 @@ Glyph FontTrueType::loadGlyph( Uint32 codePoint, unsigned int characterSize, boo
unsigned int w = glyph.textureRect.Right;
unsigned int h = glyph.textureRect.Bottom;
if ( bitmap.pixel_mode == FT_PIXEL_MODE_BGRA && scale < 1.f ) {
if ( scale < 1.f ) {
w = destWidth - 2 * padding;
h = destHeight - 2 * padding;
x += padding;

View File

@@ -1,6 +1,7 @@
#include <algorithm>
#include <cmath>
#include <eepp/graphics/fontmanager.hpp>
#include <eepp/graphics/fonttruetype.hpp>
#include <eepp/graphics/globalbatchrenderer.hpp>
#include <eepp/graphics/pixeldensity.hpp>
#include <eepp/graphics/primitives.hpp>
@@ -161,8 +162,13 @@ void Text::setString( const String& string ) {
mColorsNeedUpdate = true;
mGeometryNeedUpdate = true;
mCachedWidthNeedUpdate = true;
if ( FontManager::instance()->getColorEmojiFont() != nullptr )
mContainsColorEmoji = Font::containsEmojiCodePoint( string );
if ( FontManager::instance()->getColorEmojiFont() != nullptr ) {
if ( mFont->getType() == FontType::TTF ) {
FontTrueType* fontTrueType = static_cast<FontTrueType*>( mFont );
if ( fontTrueType->isColorEmojiFont() || !fontTrueType->isEmojiFont() )
mContainsColorEmoji = Font::containsEmojiCodePoint( string );
}
}
}
}

View File

@@ -4,6 +4,7 @@ EE::Window::Window* win = NULL;
FontTrueType* fontTest;
FontTrueType* fontTest2;
FontTrueType* fontEmoji;
FontTrueType* fontEmojiColor;
FontBMFont* fontBMFont;
FontSprite* fontSprite;
Text text;
@@ -12,6 +13,7 @@ Text text3;
Text text4;
Text text5;
Text text6;
Text text7;
void mainLoop() {
// Clear the screen buffer
@@ -30,6 +32,8 @@ void mainLoop() {
text2.draw( ( win->getWidth() - text2.getTextWidth() ) * 0.5f, 300 );
text7.draw( ( win->getWidth() - text7.getTextWidth() ) * 0.5f, 400 );
// Text rotated and scaled
text2.draw( ( win->getWidth() - text2.getTextWidth() ) * 0.5f, 430, Vector2f( 1.1f, 1.1f ),
12.5f );
@@ -75,8 +79,11 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
fontTest2 = FontTrueType::New( "NotoSans-Regular" );
fontTest2->loadFromFile( "assets/fonts/NotoSans-Regular.ttf" );
fontEmoji = FontTrueType::New( "NotoColorEmoji" );
fontEmoji->loadFromFile( "assets/fonts/NotoColorEmoji.ttf" );
fontEmoji= FontTrueType::New( "NotoEmoji-Regular" );
fontEmoji->loadFromFile( "assets/fonts/NotoEmoji-Regular.ttf" );
fontEmojiColor = FontTrueType::New( "NotoColorEmoji" );
fontEmojiColor->loadFromFile( "assets/fonts/NotoColorEmoji.ttf" );
fontBMFont = FontBMFont::New( "bmfont" );
fontBMFont->loadFromFile( "assets/fonts/bmfont.fnt" );
@@ -106,24 +113,31 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
text3.setFont( fontTest );
text3.setString( text2.getString() );
text3.setOutlineThickness( 2 );
text3.setFontSize( 24 );
text3.setFillColor( Color( 255, 255, 255, 255 ) );
text3.setOutlineThickness( 2 );
text3.setOutlineColor( Color( 0, 0, 0, 255 ) );
text4.setFont( fontBMFont );
text4.setString( "Lorem ipsum dolor sit amet, consectetur adipisicing elit." );
text4.setString( text2.getString() );
text4.setFontSize( 45 );
text4.setFillColor( Color::Black );
text5.setFont( fontSprite );
text5.setString( "Lorem ipsum dolor sit amet, consectetur adipisicing elit." );
text5.setString( text2.getString() );
text5.setFontSize( 38 );
text6.setFont( fontEmoji );
text6.setFont( fontEmojiColor );
text6.setFontSize( 64 );
text6.setString( "👽 😀 💩 😃 👻" );
text7.setFont( fontEmoji );
text7.setFontSize( 32 );
text7.setString( "👽 😀 💩 😃 👻" );
text7.setFillColor( Color::Gray );
text7.setOutlineThickness( 2 );
text7.setOutlineColor( Color( 0, 0, 0, 255 ) );
// Application loop
win->runMainLoop( &mainLoop );
}

View File

@@ -2068,6 +2068,8 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
if ( mFontMono )
mFontMono->setBoldAdvanceSameAsRegular( true );
loadFont( "NotoEmoji-Regular", "assets/fonts/NotoEmoji-Regular.ttf" );
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
loadFont( "NotoColorEmoji", "assets/fonts/NotoColorEmoji.ttf" );
#endif