X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Ftext%2Ftext-abstraction%2Ffont-client-plugin-impl.cpp;h=60deca8b84cc26a3c1e0ea981636ef913e8c7013;hb=9d6d9f21547fbe420c839e361590eab7f079d04c;hp=6b1e49153ff96752200415db119144646a125ab6;hpb=3feeffa0c34180dae652427ff38e9cdb5160f701;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/dali/internal/text/text-abstraction/font-client-plugin-impl.cpp b/dali/internal/text/text-abstraction/font-client-plugin-impl.cpp index 6b1e491..60deca8 100644 --- a/dali/internal/text/text-abstraction/font-client-plugin-impl.cpp +++ b/dali/internal/text/text-abstraction/font-client-plugin-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,20 +20,23 @@ // INTERNAL INCLUDES #include -#include -#include + +#include #include #include -#include -#include #include +#include +#include +#include +#include // EXTERNAL INCLUDES #include +#include +#include namespace { - #if defined(DEBUG_ENABLED) Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT"); #endif @@ -41,14 +44,13 @@ Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New /** * Conversion from Fractional26.6 to float */ -const float FROM_266 = 1.0f / 64.0f; +const float FROM_266 = 1.0f / 64.0f; const float POINTS_PER_INCH = 72.f; -const std::string FONT_FORMAT( "TrueType" ); -const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" ); -const int DEFAULT_FONT_WIDTH = 100; // normal -const int DEFAULT_FONT_WEIGHT = 80; // normal -const int DEFAULT_FONT_SLANT = 0; // normal +const std::string DEFAULT_FONT_FAMILY_NAME("Tizen"); +const int DEFAULT_FONT_WIDTH = 100; // normal +const int DEFAULT_FONT_WEIGHT = 80; // normal +const int DEFAULT_FONT_SLANT = 0; // normal const uint32_t ELLIPSIS_CHARACTER = 0x2026; @@ -64,8 +66,8 @@ const uint32_t ELLIPSIS_CHARACTER = 0x2026; // EXPANDED 125 // EXTRA_EXPANDED 150 // ULTRA_EXPANDED 200 -const int FONT_WIDTH_TYPE_TO_INT[] = { -1, 50, 63, 75, 87, 100, 113, 125, 150, 200 }; -const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int ); +const int FONT_WIDTH_TYPE_TO_INT[] = {-1, 50, 63, 75, 87, 100, 113, 125, 150, 200}; +const unsigned int NUM_FONT_WIDTH_TYPE = sizeof(FONT_WIDTH_TYPE_TO_INT) / sizeof(int); // NONE -1 --> DEFAULT_FONT_WEIGHT (NORMAL) will be used. // THIN 0 @@ -79,29 +81,27 @@ const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / size // BOLD 200 // ULTRA_BOLD, EXTRA_BOLD 205 // BLACK, HEAVY, EXTRA_BLACK 210 -const int FONT_WEIGHT_TYPE_TO_INT[] = { -1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 }; -const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int ); +const int FONT_WEIGHT_TYPE_TO_INT[] = {-1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210}; +const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof(FONT_WEIGHT_TYPE_TO_INT) / sizeof(int); // NONE -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used. // NORMAL, ROMAN 0 // ITALIC 100 // OBLIQUE 110 -const int FONT_SLANT_TYPE_TO_INT[] = { -1, 0, 100, 110 }; -const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int ); +const int FONT_SLANT_TYPE_TO_INT[] = {-1, 0, 100, 110}; +const unsigned int NUM_FONT_SLANT_TYPE = sizeof(FONT_SLANT_TYPE_TO_INT) / sizeof(int); } // namespace using Dali::Vector; +using namespace std; namespace Dali { - namespace TextAbstraction { - namespace Internal { - /** * @brief Returns the FontWidth's enum index for the given width value. * @@ -109,9 +109,9 @@ namespace Internal * * @return The FontWidth's enum index. */ -FontWidth::Type IntToWidthType( int width ) +FontWidth::Type IntToWidthType(int width) { - return static_cast( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) ); + return static_cast(ValueToIndex(width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u)); } /** @@ -121,9 +121,9 @@ FontWidth::Type IntToWidthType( int width ) * * @return The FontWeight's enum index. */ -FontWeight::Type IntToWeightType( int weight ) +FontWeight::Type IntToWeightType(int weight) { - return static_cast( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) ); + return static_cast(ValueToIndex(weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u)); } /** @@ -133,134 +133,310 @@ FontWeight::Type IntToWeightType( int weight ) * * @return The FontSlant's enum index. */ -FontSlant::Type IntToSlantType( int slant ) +FontSlant::Type IntToSlantType(int slant) +{ + return static_cast(ValueToIndex(slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u)); +} + +/** + * @brief Free the resources allocated by the FcCharSet objects. + * + * @param[in] characterSets The vector of character sets. + */ +void DestroyCharacterSets(CharacterSetList& characterSets) +{ + for(auto& item : characterSets) + { + if(item) + { + FcCharSetDestroy(item); + } + } +} + +/** + * @brief Check if @p ftFace and @p requestedPointSize produces block that fit into atlas block + * + * @param[in/out] ftFace Face type object. + * @param[in] horizontalDpi The horizontal dpi. + * @param[in] verticalDpi The vertical dpi. + * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas + * @param[in] requestedPointSize The requested point-size. + * @return whether the ftFace's block can fit into atlas + */ +bool IsFitIntoAtlas(FT_Face& ftFace, int& error, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, const uint32_t& requestedPointSize) +{ + bool isFit = false; + + error = FT_Set_Char_Size(ftFace, + 0, + requestedPointSize, + horizontalDpi, + verticalDpi); + + if( error == FT_Err_Ok) + { + //Check width and height of block for requestedPointSize + //If the width or height is greater than the maximum-size then decrement by one unit of point-size. + if( static_cast(ftFace->size->metrics.height) * FROM_266 <= maxSizeFitInAtlas.height + && (static_cast(ftFace->size->metrics.ascender)-static_cast(ftFace->size->metrics.descender))* FROM_266 <= maxSizeFitInAtlas.width) + { + isFit = true; + } + } + + return isFit; +} + +/** + * @brief Search on proper @p requestedPointSize that produces block that fit into atlas block considering on @p ftFace, @p horizontalDpi, and @p verticalDpi + * + * @param[in/out] ftFace Face type object. + * @param[in] horizontalDpi The horizontal dpi. + * @param[in] verticalDpi The vertical dpi. + * @param[in] maxSizeFitInAtlas The maximum size of block to fit into atlas + * @param[in/out] requestedPointSize The requested point-size. + * @return FreeType error code. 0 means success when requesting the nominal size (in points). + */ +int SearchOnProperPointSize(FT_Face& ftFace, const unsigned int& horizontalDpi, const unsigned int& verticalDpi, const Size& maxSizeFitInAtlas, uint32_t& requestedPointSize) +{ + //To improve performance of sequential search. This code is applying Exponential search then followed by Binary search. + const uint32_t& pointSizePerOneUnit = TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE; + bool canFitInAtlas; + int error; // FreeType error code. + + canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize); + if(FT_Err_Ok != error) + { + return error; + } + + if(!canFitInAtlas) + { + //Exponential search + uint32_t exponentialDecrement = 1; + + while(!canFitInAtlas && requestedPointSize > pointSizePerOneUnit*exponentialDecrement) + { + requestedPointSize-=(pointSizePerOneUnit*exponentialDecrement); + canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize); + if(FT_Err_Ok != error) + { + return error; + } + + exponentialDecrement*=2; + } + + //Binary search + uint32_t minPointSize; + uint32_t maxPointSize; + + if(canFitInAtlas) + { + exponentialDecrement/=2; + minPointSize = requestedPointSize; + maxPointSize = requestedPointSize + (pointSizePerOneUnit*exponentialDecrement); + } + else + { + minPointSize = 0; + maxPointSize = requestedPointSize; + } + + while(minPointSize < maxPointSize) + { + requestedPointSize = ((maxPointSize/pointSizePerOneUnit - minPointSize/pointSizePerOneUnit)/2) * pointSizePerOneUnit + minPointSize; + canFitInAtlas = IsFitIntoAtlas(ftFace, error, horizontalDpi, verticalDpi, maxSizeFitInAtlas, requestedPointSize); + if(FT_Err_Ok != error) + { + return error; + } + + if(canFitInAtlas) + { + if(minPointSize == requestedPointSize) + { + //Found targeted point-size + return error; + } + + minPointSize = requestedPointSize; + } + else + { + maxPointSize = requestedPointSize; + } + } + } + + return error; +} + + +FontClient::Plugin::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets) +: fontDescription{std::move(font)}, + fallbackFonts{fallbackFonts}, + characterSets{characterSets} { - return static_cast( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) ); } - FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( const FontDescription& font, FontList* fallbackFonts, CharacterSetList* characterSets ) -: fontDescription( font ), - fallbackFonts( fallbackFonts ), - characterSets( characterSets ) +FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription, + FontDescriptionId index) +: fontDescription{fontDescription}, + index{index} { } -FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription, - FontDescriptionId index ) -: fontDescription( fontDescription ), - index( index ) +FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription, + FontDescriptionId index) +: fontDescription{std::move(fontDescription)}, + index{index} { } -FontClient::Plugin::FontIdCacheItem::FontIdCacheItem( FontDescriptionId validatedFontId, - PointSize26Dot6 requestedPointSize, - FontId fontId ) -: validatedFontId( validatedFontId ), - requestedPointSize( requestedPointSize ), - fontId( fontId ) +FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem(FontDescriptionId validatedFontId, + PointSize26Dot6 requestedPointSize, + FontId fontId) +: validatedFontId(validatedFontId), + requestedPointSize(requestedPointSize), + fontId(fontId) { } -FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace, - const FontPath& path, - PointSize26Dot6 requestedPointSize, - FaceIndex face, - const FontMetrics& metrics ) -: mFreeTypeFace( ftFace ), - mPath( path ), - mRequestedPointSize( requestedPointSize ), - mFaceIndex( face ), - mMetrics( metrics ), - mCharacterSet( NULL ), - mFixedWidthPixels( 0.0f ), - mFixedHeightPixels( 0.0f ), - mVectorFontId( 0 ), - mIsFixedSizeBitmap( false ), - mHasColorTables( false ) +FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace, + const FontPath& path, + PointSize26Dot6 requestedPointSize, + FaceIndex face, + const FontMetrics& metrics) +: mFreeTypeFace(ftFace), + mPath(path), + mRequestedPointSize(requestedPointSize), + mFaceIndex(face), + mMetrics(metrics), + mCharacterSet(nullptr), + mFixedSizeIndex(0), + mFixedWidthPixels(0.f), + mFixedHeightPixels(0.f), + mVectorFontId(0u), + mFontId(0u), + mIsFixedSizeBitmap(false), + mHasColorTables(false) { } -FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace, - const FontPath& path, - PointSize26Dot6 requestedPointSize, - FaceIndex face, - const FontMetrics& metrics, - float fixedWidth, - float fixedHeight, - bool hasColorTables ) -: mFreeTypeFace( ftFace ), - mPath( path ), - mRequestedPointSize( requestedPointSize ), - mFaceIndex( face ), - mMetrics( metrics ), - mCharacterSet( NULL ), - mFixedWidthPixels( fixedWidth ), - mFixedHeightPixels( fixedHeight ), - mVectorFontId( 0 ), - mIsFixedSizeBitmap( true ), - mHasColorTables( hasColorTables ) +FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem(FT_Face ftFace, + const FontPath& path, + PointSize26Dot6 requestedPointSize, + FaceIndex face, + const FontMetrics& metrics, + int fixedSizeIndex, + float fixedWidth, + float fixedHeight, + bool hasColorTables) +: mFreeTypeFace(ftFace), + mPath(path), + mRequestedPointSize(requestedPointSize), + mFaceIndex(face), + mMetrics(metrics), + mCharacterSet(nullptr), + mFixedSizeIndex(fixedSizeIndex), + mFixedWidthPixels(fixedWidth), + mFixedHeightPixels(fixedHeight), + mVectorFontId(0u), + mFontId(0u), + mIsFixedSizeBitmap(true), + mHasColorTables(hasColorTables) { } -FontClient::Plugin::Plugin( unsigned int horizontalDpi, - unsigned int verticalDpi ) -: mFreeTypeLibrary( NULL ), - mDpiHorizontal( horizontalDpi ), - mDpiVertical( verticalDpi ), +FontClient::Plugin::Plugin(unsigned int horizontalDpi, + unsigned int verticalDpi) +: mFreeTypeLibrary(nullptr), + mDpiHorizontal(horizontalDpi), + mDpiVertical(verticalDpi), mDefaultFontDescription(), mSystemFonts(), mDefaultFonts(), - mFontCache(), + mFontIdCache(), + mFontFaceCache(), mValidatedFontCache(), - mFontDescriptionCache( 1u ), + mFontDescriptionCache(), mCharacterSetCache(), - mFontIdCache(), - mVectorFontCache( NULL ), + mFontDescriptionSizeCache(), + mVectorFontCache(nullptr), mEllipsisCache(), - mDefaultFontDescriptionCached( false ) -{ - mCharacterSetCache.Resize( 1u ); + mEmbeddedItemCache(), + mDefaultFontDescriptionCached(false), + mIsAtlasLimitationEnabled(TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED), + mCurrentMaximumBlockSizeFitInAtlas(TextAbstraction::FontClient::MAX_SIZE_FIT_IN_ATLAS) - int error = FT_Init_FreeType( &mFreeTypeLibrary ); - if( FT_Err_Ok != error ) +{ + int error = FT_Init_FreeType(&mFreeTypeLibrary); + if(FT_Err_Ok != error) { - DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Init error: %d\n", error ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Init error: %d\n", error); } #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING - mVectorFontCache = new VectorFontCache( mFreeTypeLibrary ); + mVectorFontCache = new VectorFontCache(mFreeTypeLibrary); #endif } FontClient::Plugin::~Plugin() { - for( std::vector::iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end(); - it != endIt; - ++it ) - { - FallbackCacheItem& item = *it; + ClearFallbackCache(mFallbackCache); - if( item.fallbackFonts ) - { - delete item.fallbackFonts; - delete item.characterSets; - item.fallbackFonts = NULL; - item.characterSets = NULL; - } - } + // Free the resources allocated by the FcCharSet objects. + DestroyCharacterSets(mDefaultFontCharacterSets); + DestroyCharacterSets(mCharacterSetCache); + ClearCharacterSetFromFontFaceCache(); #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING delete mVectorFontCache; #endif + FT_Done_FreeType(mFreeTypeLibrary); +} + +void FontClient::Plugin::ClearCache() +{ + mDefaultFontDescription = FontDescription(); + + mSystemFonts.clear(); + mDefaultFonts.clear(); + + DestroyCharacterSets(mDefaultFontCharacterSets); + mDefaultFontCharacterSets.Clear(); + + ClearFallbackCache(mFallbackCache); + mFallbackCache.clear(); - FT_Done_FreeType( mFreeTypeLibrary ); + mFontIdCache.Clear(); + + ClearCharacterSetFromFontFaceCache(); + mFontFaceCache.clear(); + + mValidatedFontCache.clear(); + mFontDescriptionCache.clear(); + + DestroyCharacterSets(mCharacterSetCache); + mCharacterSetCache.Clear(); + + mFontDescriptionSizeCache.clear(); + + mEllipsisCache.Clear(); + mPixelBufferCache.clear(); + mEmbeddedItemCache.Clear(); + mBitmapFontCache.clear(); + + mDefaultFontDescriptionCached = false; } -void FontClient::Plugin::SetDpi( unsigned int horizontalDpi, - unsigned int verticalDpi ) +void FontClient::Plugin::SetDpi(unsigned int horizontalDpi, + unsigned int verticalDpi) { mDpiHorizontal = horizontalDpi; - mDpiVertical = verticalDpi; + mDpiVertical = verticalDpi; } void FontClient::Plugin::ResetSystemDefaults() @@ -268,172 +444,176 @@ void FontClient::Plugin::ResetSystemDefaults() mDefaultFontDescriptionCached = false; } -void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList ) +void FontClient::Plugin::SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]); fontList.clear(); - FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); + FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy. FcResult result = FcResultMatch; // Match the pattern. - FcFontSet* fontSet = FcFontSort( NULL /* use default configure */, - fontFamilyPattern, - false /* don't trim */, - NULL, - &result ); + FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */, + fontFamilyPattern, + false /* don't trim */, + nullptr, + &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy. - if( NULL != fontSet ) + if(nullptr != fontSet) { - DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont ); + DALI_LOG_INFO(gLogFilter, Debug::General, " number of fonts found : [%d]\n", fontSet->nfont); // Reserve some space to avoid reallocations. - fontList.reserve( fontSet->nfont ); + fontList.reserve(fontSet->nfont); - for( int i = 0u; i < fontSet->nfont; ++i ) + for(int i = 0u; i < fontSet->nfont; ++i) { FcPattern* fontPattern = fontSet->fonts[i]; FontPath path; // Skip fonts with no path - if( GetFcString( fontPattern, FC_FILE, path ) ) + if(GetFcString(fontPattern, FC_FILE, path)) { - FcCharSet* characterSet = NULL; - FcPatternGetCharSet( fontPattern, FC_CHARSET, 0u, &characterSet ); + // Retrieve the character set. Need to call FcCharSetDestroy to free the resources. + FcCharSet* characterSet = nullptr; + FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet); + + // Increase the reference counter of the character set. + characterSetList.PushBack(FcCharSetCopy(characterSet)); - characterSetList.PushBack( characterSet ); - fontList.push_back( FontDescription() ); + fontList.push_back(FontDescription()); FontDescription& newFontDescription = fontList.back(); - newFontDescription.path = path; + newFontDescription.path = std::move(path); - int width = 0; + int width = 0; int weight = 0; - int slant = 0; - GetFcString( fontPattern, FC_FAMILY, newFontDescription.family ); - GetFcInt( fontPattern, FC_WIDTH, width ); - GetFcInt( fontPattern, FC_WEIGHT, weight ); - GetFcInt( fontPattern, FC_SLANT, slant ); - newFontDescription.width = IntToWidthType( width ); - newFontDescription.weight = IntToWeightType( weight ); - newFontDescription.slant = IntToSlantType( slant ); - - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", newFontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", newFontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[newFontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[newFontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant] ); + int slant = 0; + GetFcString(fontPattern, FC_FAMILY, newFontDescription.family); + GetFcInt(fontPattern, FC_WIDTH, width); + GetFcInt(fontPattern, FC_WEIGHT, weight); + GetFcInt(fontPattern, FC_SLANT, slant); + newFontDescription.width = IntToWidthType(width); + newFontDescription.weight = IntToWeightType(weight); + newFontDescription.slant = IntToSlantType(slant); + + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", newFontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", newFontDescription.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[newFontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[newFontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant]); } } - FcFontSetDestroy( fontSet ); + // Destroys the font set created by FcFontSort. + FcFontSetDestroy(fontSet); } else { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " No fonts found.\n" ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " No fonts found.\n"); } - FcPatternDestroy( fontFamilyPattern ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n" ); + // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern. + FcPatternDestroy(fontFamilyPattern); + + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n"); } -void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts ) +void FontClient::Plugin::GetDefaultFonts(FontList& defaultFonts) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n"); - if( mDefaultFonts.empty() ) + if(mDefaultFonts.empty()) { FontDescription fontDescription; - fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font - fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH ); - fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT ); - fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT ); - SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets ); + fontDescription.family = DEFAULT_FONT_FAMILY_NAME; // todo This could be set to the Platform font + fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH); + fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT); + fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT); + SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets); } defaultFonts = mDefaultFonts; - DALI_LOG_INFO( gLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size() ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " number of default fonts : [%d]\n", mDefaultFonts.size()); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n"); } -void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription ) +void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n"); - if( !mDefaultFontDescriptionCached ) + if(!mDefaultFontDescriptionCached) { // Clear any font config stored info in the caches. + + // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects. + DestroyCharacterSets(mDefaultFontCharacterSets); + DestroyCharacterSets(mCharacterSetCache); mDefaultFontCharacterSets.Clear(); mCharacterSetCache.Clear(); - for( std::vector::iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end(); it != endIt; ++it ) + for(auto& item : mFallbackCache) { - FallbackCacheItem& item = *it; + // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects. + DestroyCharacterSets(*item.characterSets); - item.characterSets->Clear(); + delete item.characterSets; + item.characterSets = nullptr; } - for( std::vector::iterator it = mFontCache.begin(), endIt = mFontCache.end(); it != endIt; ++it ) - { - FontFaceCacheItem& item = *it; - - // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont() - item.mCharacterSet = NULL; - } + // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont() + ClearCharacterSetFromFontFaceCache(); // FcInitBringUptoDate did not seem to reload config file as was still getting old default font. FcInitReinitialize(); - FcPattern* matchPattern = FcPatternCreate(); + FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy. - if( matchPattern ) + if(nullptr != matchPattern) { - FcConfigSubstitute( NULL, matchPattern, FcMatchPattern ); - FcDefaultSubstitute( matchPattern ); + FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern); + FcDefaultSubstitute(matchPattern); - FcCharSet* characterSet = NULL; - MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription, &characterSet ); - FcPatternDestroy( matchPattern ); + FcCharSet* characterSet = nullptr; + MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet); + // Decrease the reference counter of the character set as it's not stored. + FcCharSetDestroy(characterSet); + + // Destroys the pattern created. + FcPatternDestroy(matchPattern); } // Create again the character sets as they are not valid after FcInitReinitialize() - for( FontList::const_iterator it = mDefaultFonts.begin(), endIt = mDefaultFonts.end(); it != endIt; ++it ) + for(const auto& description : mDefaultFonts) { - const FontDescription& description = *it; - - mDefaultFontCharacterSets.PushBack( CreateCharacterSetFromDescription( description ) ); + mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description))); } - for( FontList::const_iterator it = mFontDescriptionCache.begin(), endIt = mFontDescriptionCache.end(); it != endIt; ++it ) + for(const auto& description : mFontDescriptionCache) { - const FontDescription& description = *it; - - mCharacterSetCache.PushBack( CreateCharacterSetFromDescription( description ) ); + mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description))); } - for( std::vector::iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end(); it != endIt; ++it ) + for(auto& item : mFallbackCache) { - FallbackCacheItem& item = *it; - - if( NULL != item.fallbackFonts ) + if(nullptr != item.fallbackFonts) { - if( NULL == item.characterSets ) + if(nullptr == item.characterSets) { item.characterSets = new CharacterSetList; } - for( FontList::const_iterator flIt = item.fallbackFonts->begin(), flEndIt = item.fallbackFonts->end(); flIt != flEndIt; ++flIt ) + for(const auto& description : *(item.fallbackFonts)) { - const FontDescription& description = *flIt; - item.characterSets->PushBack( CreateCharacterSetFromDescription( description ) ); + item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description))); } } } @@ -447,92 +627,127 @@ void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fon fontDescription.weight = mDefaultFontDescription.weight; fontDescription.slant = mDefaultFontDescription.slant; - DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n"); } -void FontClient::Plugin::GetSystemFonts( FontList& systemFonts ) +void FontClient::Plugin::GetSystemFonts(FontList& systemFonts) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n"); - if( mSystemFonts.empty() ) + if(mSystemFonts.empty()) { InitSystemFonts(); } systemFonts = mSystemFonts; - DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size() ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " number of system fonts : [%d]\n", mSystemFonts.size()); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n"); } -void FontClient::Plugin::GetDescription( FontId id, - FontDescription& fontDescription ) const +void FontClient::Plugin::GetDescription(FontId id, + FontDescription& fontDescription) const { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n"); - DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id); + const FontId index = id - 1u; - for( std::vector::const_iterator it = mFontIdCache.begin(), - endIt = mFontIdCache.end(); - it != endIt; - ++it ) + if((id > 0u) && (index < mFontIdCache.Count())) { - const FontIdCacheItem& item = *it; - - if( item.fontId == id ) + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; + switch(fontIdCacheItem.type) { - fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId ); - - DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n"); - return; + case FontDescription::FACE_FONT: + { + for(const auto& item : mFontDescriptionSizeCache) + { + if(item.fontId == fontIdCacheItem.id) + { + fontDescription = *(mFontDescriptionCache.begin() + item.validatedFontId - 1u); + + DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n"); + return; + } + } + break; + } + case FontDescription::BITMAP_FONT: + { + fontDescription.type = FontDescription::BITMAP_FONT; + fontDescription.family = mBitmapFontCache[fontIdCacheItem.id].font.name; + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + fontDescription.type = FontDescription::INVALID; + fontDescription.family.clear(); + } } } - DALI_LOG_INFO( gLogFilter, Debug::General, " No description found for the font ID %d\n", id ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " No description found for the font ID %d\n", id); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n"); } -PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id ) +PointSize26Dot6 FontClient::Plugin::GetPointSize(FontId id) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n"); - DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id); const FontId index = id - 1u; - if( ( id > 0u ) && - ( index < mFontCache.size() ) ) + if((id > 0u) && + (index < mFontIdCache.Count())) { - DALI_LOG_INFO( gLogFilter, Debug::General, " point size : %d\n", ( *( mFontCache.begin() + index ) ).mRequestedPointSize ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n"); - return ( *( mFontCache.begin() + index ) ).mRequestedPointSize; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; + + switch(fontIdCacheItem.type) + { + case FontDescription::FACE_FONT: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " point size : %d\n", (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n"); + return (*(mFontFaceCache.begin() + fontIdCacheItem.id)).mRequestedPointSize; + } + case FontDescription::BITMAP_FONT: + { + return TextAbstraction::FontClient::DEFAULT_POINT_SIZE; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + } + } } else { - DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font ID %d\n", id ); + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid font ID %d\n", id); } - DALI_LOG_INFO( gLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n"); return TextAbstraction::FontClient::DEFAULT_POINT_SIZE; } -bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character character ) +bool FontClient::Plugin::IsCharacterSupportedByFont(FontId fontId, Character character) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n"); - DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId); + DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", character); - if( ( fontId < 1u ) || ( fontId > mFontCache.size() ) ) + if((fontId < 1u) || (fontId > mFontIdCache.Count())) { - DALI_LOG_INFO( gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n",mFontCache.size()); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid font id. Number of items in the cache: %d\n", mFontIdCache.Count()); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n"); return false; } @@ -540,155 +755,184 @@ bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character ch bool isSupported = false; - FontFaceCacheItem& cacheItem = mFontCache[fontId]; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[fontId]; - if( NULL == cacheItem.mCharacterSet ) + switch(fontIdCacheItem.type) { - // Create again the character set. - // It can be null if the ResetSystemDefaults() method has been called. + case FontDescription::FACE_FONT: + { + if(fontIdCacheItem.id < mFontFaceCache.size()) + { + FontFaceCacheItem& cacheItem = mFontFaceCache[fontIdCacheItem.id]; + + if(nullptr == cacheItem.mCharacterSet) + { + // Create again the character set. + // It can be null if the ResetSystemDefaults() method has been called. + + FontDescription description; + description.path = cacheItem.mPath; + description.family = std::move(FontFamily(cacheItem.mFreeTypeFace->family_name)); + description.weight = FontWeight::NONE; + description.width = FontWidth::NONE; + description.slant = FontSlant::NONE; + + // Note FreeType doesn't give too much info to build a proper font style. + if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC) + { + description.slant = FontSlant::ITALIC; + } + if(cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD) + { + description.weight = FontWeight::BOLD; + } - FontDescription description; - description.path = cacheItem.mPath; - description.family = FontFamily( cacheItem.mFreeTypeFace->family_name ); - description.weight = FontWeight::NONE; - description.width = FontWidth::NONE; - description.slant = FontSlant::NONE; + cacheItem.mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description)); + } - // Note FreeType doesn't give too much info to build a proper font style. - if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC ) + isSupported = FcCharSetHasChar(cacheItem.mCharacterSet, character); + } + break; + } + case FontDescription::BITMAP_FONT: { - description.slant = FontSlant::ITALIC; + const BitmapFont& bitmapFont = mBitmapFontCache[fontIdCacheItem.id].font; + + for(const auto& glyph : bitmapFont.glyphs) + { + if(glyph.utf32 == character) + { + isSupported = true; + break; + } + } + break; } - if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD ) + default: { - description.weight = FontWeight::BOLD; + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); } - - cacheItem.mCharacterSet = CreateCharacterSetFromDescription( description ); } - isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character ); - - DALI_LOG_INFO( gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false") ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " is supported : %s\n", (isSupported ? "true" : "false")); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n"); return isSupported; } -FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList, - const CharacterSetList& characterSetList, - Character character, - PointSize26Dot6 requestedPointSize, - bool preferColor ) +FontId FontClient::Plugin::FindFontForCharacter(const FontList& fontList, + const CharacterSetList& characterSetList, + Character character, + PointSize26Dot6 requestedPointSize, + bool preferColor) { - DALI_ASSERT_DEBUG( ( fontList.size() == characterSetList.Count() ) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets." ); + DALI_ASSERT_DEBUG((fontList.size() == characterSetList.Count()) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets."); - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", character ); - DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); - DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", character); + DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize); + DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false")); - FontId fontId = 0u; - bool foundColor = false; + FontId fontId = 0u; + bool foundColor = false; - DALI_LOG_INFO( gLogFilter, Debug::General, " number of fonts : %d\n", fontList.size() ); + DALI_LOG_INFO(gLogFilter, Debug::General, " number of fonts : %d\n", fontList.size()); // Traverse the list of fonts. // Check for each font if supports the character. - for( unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index ) + for(unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index) { - const FontDescription& description = fontList[index]; + const FontDescription& description = fontList[index]; const FcCharSet* const characterSet = characterSetList[index]; - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", description.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant] ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", description.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant]); bool foundInRanges = false; - if( NULL != characterSet ) + if(nullptr != characterSet) { - foundInRanges = FcCharSetHasChar( characterSet, character ); + foundInRanges = FcCharSetHasChar(characterSet, character); } - if( foundInRanges ) + if(foundInRanges) { - fontId = GetFontId( description, - requestedPointSize, - 0u ); + fontId = GetFontId(description, + requestedPointSize, + 0u); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " font id : %d\n", fontId ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " font id : %d\n", fontId); - if( preferColor ) + if(preferColor) { - if( ( fontId > 0 ) && - ( fontId - 1u < mFontCache.size() ) ) + if((fontId > 0) && + (fontId - 1u < mFontIdCache.Count())) { - const FontFaceCacheItem& item = mFontCache[fontId - 1u]; + const FontFaceCacheItem& item = mFontFaceCache[mFontIdCache[fontId - 1u].id]; foundColor = item.mHasColorTables; } - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " foundColor : %s\n", ( foundColor ? "true" : "false" ) ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " foundColor : %s\n", (foundColor ? "true" : "false")); } // Keep going unless we prefer a different (color) font. - if( !preferColor || foundColor ) + if(!preferColor || foundColor) { break; } } } - DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n"); return fontId; } -FontId FontClient::Plugin::FindDefaultFont( Character charcode, - PointSize26Dot6 requestedPointSize, - bool preferColor ) +FontId FontClient::Plugin::FindDefaultFont(Character charcode, + PointSize26Dot6 requestedPointSize, + bool preferColor) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode ); - DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); - DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", charcode); + DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize); + DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false")); FontId fontId(0); // Create the list of default fonts if it has not been created. - if( mDefaultFonts.empty() ) + if(mDefaultFonts.empty()) { FontDescription fontDescription; fontDescription.family = DEFAULT_FONT_FAMILY_NAME; - fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH ); - fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT ); - fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT ); + fontDescription.width = IntToWidthType(DEFAULT_FONT_WIDTH); + fontDescription.weight = IntToWeightType(DEFAULT_FONT_WEIGHT); + fontDescription.slant = IntToSlantType(DEFAULT_FONT_SLANT); - SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets ); + SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets); } - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size() ); - + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of default fonts : %d\n", mDefaultFonts.size()); // Traverse the list of default fonts. // Check for each default font if supports the character. - fontId = FindFontForCharacter( mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor ); + fontId = FindFontForCharacter(mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor); - DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n"); return fontId; } -FontId FontClient::Plugin::FindFallbackFont( Character charcode, - const FontDescription& preferredFontDescription, - PointSize26Dot6 requestedPointSize, - bool preferColor ) +FontId FontClient::Plugin::FindFallbackFont(Character charcode, + const FontDescription& preferredFontDescription, + PointSize26Dot6 requestedPointSize, + bool preferColor) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " character : %p\n", charcode ); - DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); - DALI_LOG_INFO( gLogFilter, Debug::General, " preferColor : %s\n", ( preferColor ? "true" : "false" ) ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " character : %p\n", charcode); + DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize); + DALI_LOG_INFO(gLogFilter, Debug::General, " preferColor : %s\n", (preferColor ? "true" : "false")); // The font id to be returned. FontId fontId = 0u; @@ -697,94 +941,112 @@ FontId FontClient::Plugin::FindFallbackFont( Character charcode, // Fill the font description with the preferred font description and complete with the defaults. fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family; - fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight ); - fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width ); - fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant ); + fontDescription.weight = ((FontWeight::NONE == preferredFontDescription.weight) ? IntToWeightType(DEFAULT_FONT_WEIGHT) : preferredFontDescription.weight); + fontDescription.width = ((FontWidth::NONE == preferredFontDescription.width) ? IntToWidthType(DEFAULT_FONT_WIDTH) : preferredFontDescription.width); + fontDescription.slant = ((FontSlant::NONE == preferredFontDescription.slant) ? IntToSlantType(DEFAULT_FONT_SLANT) : preferredFontDescription.slant); - DALI_LOG_INFO( gLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant] ); + DALI_LOG_INFO(gLogFilter, Debug::General, " preferredFontDescription --> fontDescription\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant]); // Check first if the font's description has been queried before. - FontList* fontList = NULL; - CharacterSetList* characterSetList = NULL; + FontList* fontList = nullptr; + CharacterSetList* characterSetList = nullptr; - if( !FindFallbackFontList( fontDescription, fontList, characterSetList ) ) + if(!FindFallbackFontList(fontDescription, fontList, characterSetList)) { - fontList = new FontList; + fontList = new FontList; characterSetList = new CharacterSetList; - SetFontList( fontDescription, *fontList, *characterSetList ); + SetFontList(fontDescription, *fontList, *characterSetList); +#ifdef __APPLE__ + FontDescription appleColorEmoji; + appleColorEmoji.family = "Apple Color Emoji"; + appleColorEmoji.width = fontDescription.width; + appleColorEmoji.weight = fontDescription.weight; + appleColorEmoji.slant = fontDescription.slant; + FontList emojiFontList; + CharacterSetList emojiCharSetList; + SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList); + + std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList)); + emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End()); + *fontList = std::move(emojiFontList); + *characterSetList = std::move(emojiCharSetList); +#endif // Add the font-list to the cache. - mFallbackCache.push_back( FallbackCacheItem( fontDescription, fontList, characterSetList ) ); + mFallbackCache.push_back(std::move(FallbackCacheItem(std::move(fontDescription), fontList, characterSetList))); } - if( fontList && characterSetList ) + if(fontList && characterSetList) { - fontId = FindFontForCharacter( *fontList, *characterSetList, charcode, requestedPointSize, preferColor ); + fontId = FindFontForCharacter(*fontList, *characterSetList, charcode, requestedPointSize, preferColor); } - DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n"); return fontId; } -FontId FontClient::Plugin::GetFontId( const FontPath& path, - PointSize26Dot6 requestedPointSize, - FaceIndex faceIndex, - bool cacheDescription ) +FontId FontClient::Plugin::GetFontId(const FontPath& path, + PointSize26Dot6 requestedPointSize, + FaceIndex faceIndex, + bool cacheDescription) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize); - FontId id( 0 ); + FontId id = 0u; - if( NULL != mFreeTypeLibrary ) + if(nullptr != mFreeTypeLibrary) { - FontId foundId(0); - if( FindFont( path, requestedPointSize, faceIndex, foundId ) ) + FontId foundId = 0u; + if(FindFont(path, requestedPointSize, faceIndex, foundId)) { id = foundId; } else { - id = CreateFont( path, requestedPointSize, faceIndex, cacheDescription ); + id = CreateFont(path, requestedPointSize, faceIndex, cacheDescription); } } - DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n"); return id; } -FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription, - PointSize26Dot6 requestedPointSize, - FaceIndex faceIndex ) +FontId FontClient::Plugin::GetFontId(const FontDescription& fontDescription, + PointSize26Dot6 requestedPointSize, + FaceIndex faceIndex) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); - DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]); + DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize); // This method uses three vectors which caches: + // * The bitmap font cache // * Pairs of non validated font descriptions and an index to a vector with paths to font file names. // * The path to font file names. // * The font ids of pairs 'font point size, index to the vector with paths to font file names'. - // 1) Checks in the cache if the font's description has been validated before. + // 1) Checks if the font description matches with a previously loaded bitmap font. + // Returns if a font is found. + // 2) Checks in the cache if the font's description has been validated before. // If it was it gets an index to the vector with paths to font file names. Otherwise, // retrieves using font config a path to a font file name which matches with the // font's description. The path is stored in the cache. // - // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to + // 3) Checks in the cache if the pair 'font point size, index to the vector with paths to // font file names' exists. If exists, it gets the font id. If it doesn't it calls // the GetFontId() method with the path to the font file name and the point size to // get the font id. @@ -792,285 +1054,481 @@ FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription, // The font id to be returned. FontId fontId = 0u; - // Check first if the font's description have been validated before. + // Check first if the font description matches with a previously loaded bitmap font. + if(FindBitmapFont(fontDescription.family, fontId)) + { + return fontId; + } + + // Check if the font's description have been validated before. FontDescriptionId validatedFontId = 0u; - if( !FindValidatedFont( fontDescription, - validatedFontId ) ) + if(!FindValidatedFont(fontDescription, + validatedFontId)) { // Use font config to validate the font's description. - ValidateFont( fontDescription, - validatedFontId ); + ValidateFont(fontDescription, + validatedFontId); } + FontId fontFaceId = 0u; // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache. - if( !FindFont( validatedFontId, requestedPointSize, fontId ) ) + if(!FindFont(validatedFontId, requestedPointSize, fontFaceId)) { // Retrieve the font file name path. - const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId ); + const FontDescription& description = *(mFontDescriptionCache.begin() + validatedFontId - 1u); // Retrieve the font id. Do not cache the description as it has been already cached. - fontId = GetFontId( description.path, - requestedPointSize, - faceIndex, - false ); + fontId = GetFontId(description.path, + requestedPointSize, + faceIndex, + false); - mFontCache[fontId-1u].mCharacterSet = mCharacterSetCache[validatedFontId]; + fontFaceId = mFontIdCache[fontId - 1u].id; + mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(mCharacterSetCache[validatedFontId - 1u]); // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries. - mFontIdCache.push_back( FontIdCacheItem( validatedFontId, - requestedPointSize, - fontId ) ); + mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId, + requestedPointSize, + fontFaceId)); + } + else + { + fontId = mFontFaceCache[fontFaceId].mFontId + 1u; } - DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", fontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", fontId); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n"); return fontId; } -void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription, - FontDescriptionId& validatedFontId ) +FontId FontClient::Plugin::GetFontId(const BitmapFont& bitmapFont) +{ + for(const auto& item : mBitmapFontCache) + { + if(bitmapFont.name == item.font.name) + { + return item.id + 1u; + } + } + + BitmapFontCacheItem bitmapFontCacheItem; + bitmapFontCacheItem.font = bitmapFont; + bitmapFontCacheItem.id = mFontIdCache.Count(); + + // Resize the vector with the pixel buffers. + bitmapFontCacheItem.pixelBuffers.resize(bitmapFont.glyphs.size()); + + // Traverse all the glyphs and load the pixel buffer of those with ascender and descender equal to zero. + unsigned int index = 0u; + for(auto& glyph : bitmapFontCacheItem.font.glyphs) + { + Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index]; + + if(EqualsZero(glyph.ascender) && EqualsZero(glyph.descender)) + { + // Load the glyph. + pixelBuffer = LoadImageFromFile(glyph.url); + + if(pixelBuffer) + { + glyph.ascender = static_cast(pixelBuffer.GetHeight()); + } + } + + bitmapFontCacheItem.font.ascender = std::max(glyph.ascender, bitmapFontCacheItem.font.ascender); + bitmapFontCacheItem.font.descender = std::min(glyph.descender, bitmapFontCacheItem.font.descender); + + ++index; + } + + FontIdCacheItem fontIdCacheItem; + fontIdCacheItem.type = FontDescription::BITMAP_FONT; + fontIdCacheItem.id = mBitmapFontCache.size(); + + mBitmapFontCache.push_back(std::move(bitmapFontCacheItem)); + mFontIdCache.PushBack(fontIdCacheItem); + + return bitmapFontCacheItem.id + 1u; +} + +void FontClient::Plugin::ValidateFont(const FontDescription& fontDescription, + FontDescriptionId& validatedFontId) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]); // Create a font pattern. - FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); + FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); FontDescription description; - FcCharSet* characterSet = NULL; - bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description, &characterSet ); - FcPatternDestroy( fontFamilyPattern ); + FcCharSet* characterSet = nullptr; + bool matched = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet); + FcPatternDestroy(fontFamilyPattern); - if( matched && ( NULL != characterSet ) ) + if(matched && (nullptr != characterSet)) { + // Add the path to the cache. + description.type = FontDescription::FACE_FONT; + mFontDescriptionCache.push_back(description); + // Set the index to the vector of paths to font file names. validatedFontId = mFontDescriptionCache.size(); - DALI_LOG_INFO( gLogFilter, Debug::General, " matched description; family : [%s]\n", description.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant] ); - DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId ); + DALI_LOG_INFO(gLogFilter, Debug::General, " matched description; family : [%s]\n", description.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", description.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[description.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[description.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[description.slant]); + DALI_LOG_INFO(gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId); - // Add the path to the cache. - mFontDescriptionCache.push_back( description ); - mCharacterSetCache.PushBack( characterSet ); + // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern. + mCharacterSetCache.PushBack(characterSet); // Cache the index and the matched font's description. - FontDescriptionCacheItem item( description, - validatedFontId ); + FontDescriptionCacheItem item(description, + validatedFontId); - mValidatedFontCache.push_back( item ); + mValidatedFontCache.push_back(std::move(item)); - if( ( fontDescription.family != description.family ) || - ( fontDescription.width != description.width ) || - ( fontDescription.weight != description.weight ) || - ( fontDescription.slant != description.slant ) ) + if((fontDescription.family != description.family) || + (fontDescription.width != description.width) || + (fontDescription.weight != description.weight) || + (fontDescription.slant != description.slant)) { // Cache the given font's description if it's different than the matched. - FontDescriptionCacheItem item( fontDescription, - validatedFontId ); + FontDescriptionCacheItem item(fontDescription, + validatedFontId); - mValidatedFontCache.push_back( item ); + mValidatedFontCache.push_back(std::move(item)); } } else { - DALI_LOG_INFO( gLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str() ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font validation failed for font [%s]\n", fontDescription.family.c_str()); } - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n"); } -void FontClient::Plugin::GetFontMetrics( FontId fontId, - FontMetrics& metrics ) +void FontClient::Plugin::GetFontMetrics(FontId fontId, + FontMetrics& metrics) { - if( ( fontId > 0 ) && - ( fontId - 1u < mFontCache.size() ) ) - { - const FontFaceCacheItem& font = mFontCache[fontId-1]; + const FontId index = fontId - 1u; - metrics = font.mMetrics; + if((fontId > 0) && + (index < mFontIdCache.Count())) + { + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; - // Adjust the metrics if the fixed-size font should be down-scaled - if( font.mIsFixedSizeBitmap ) + switch(fontIdCacheItem.type) { - const float desiredFixedSize = static_cast( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical; + case FontDescription::FACE_FONT: + { + const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id]; + + metrics = font.mMetrics; - if( desiredFixedSize > 0.f ) + // Adjust the metrics if the fixed-size font should be down-scaled + if(font.mIsFixedSizeBitmap) + { + const float desiredFixedSize = static_cast(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical; + + if(desiredFixedSize > 0.f) + { + const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels; + + metrics.ascender = metrics.ascender * scaleFactor; + metrics.descender = metrics.descender * scaleFactor; + metrics.height = metrics.height * scaleFactor; + metrics.underlinePosition = metrics.underlinePosition * scaleFactor; + metrics.underlineThickness = metrics.underlineThickness * scaleFactor; + } + } + break; + } + case FontDescription::BITMAP_FONT: { - const float scaleFactor = desiredFixedSize / static_cast( font.mFixedHeightPixels ); + const BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id]; - metrics.ascender = floorf( metrics.ascender * scaleFactor ); - metrics.descender = floorf( metrics.descender * scaleFactor ); - metrics.height = floorf( metrics.height * scaleFactor ); - metrics.underlinePosition = floorf( metrics.underlinePosition * scaleFactor ); - metrics.underlineThickness = floorf( metrics.underlineThickness * scaleFactor ); + metrics.ascender = bitmapFontCacheItem.font.ascender; + metrics.descender = bitmapFontCacheItem.font.descender; + metrics.height = metrics.ascender - metrics.descender; + metrics.underlinePosition = bitmapFontCacheItem.font.underlinePosition; + metrics.underlineThickness = bitmapFontCacheItem.font.underlineThickness; + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); } } } else { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId); } } -GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId, - Character charcode ) +GlyphIndex FontClient::Plugin::GetGlyphIndex(FontId fontId, + Character charcode) { - GlyphIndex index = 0u; + GlyphIndex glyphIndex = 0u; + const FontId index = fontId - 1u; - if( ( fontId > 0u ) && - ( fontId - 1u < mFontCache.size() ) ) + if((fontId > 0u) && + (index < mFontIdCache.Count())) { - FT_Face ftFace = mFontCache[fontId-1u].mFreeTypeFace; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; - index = FT_Get_Char_Index( ftFace, charcode ); + if(FontDescription::FACE_FONT == fontIdCacheItem.type) + { + FT_Face ftFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace; + + glyphIndex = FT_Get_Char_Index(ftFace, charcode); + } } - return index; + return glyphIndex; } -bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array, - uint32_t size, - GlyphType type, - bool horizontal ) +bool FontClient::Plugin::GetGlyphMetrics(GlyphInfo* array, + uint32_t size, + GlyphType type, + bool horizontal) { - if( VECTOR_GLYPH == type ) + if(VECTOR_GLYPH == type) { - return GetVectorMetrics( array, size, horizontal ); + return GetVectorMetrics(array, size, horizontal); } - return GetBitmapMetrics( array, size, horizontal ); + return GetBitmapMetrics(array, size, horizontal); } -bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array, - uint32_t size, - bool horizontal ) +bool FontClient::Plugin::GetBitmapMetrics(GlyphInfo* array, + uint32_t size, + bool horizontal) { - bool success( true ); + bool success(true); - for( unsigned int i=0; i 0 && - fontId-1 < mFontCache.size() ) + if((glyph.fontId > 0u) && + (index < mFontIdCache.Count())) { - const FontFaceCacheItem& font = mFontCache[fontId-1]; - - FT_Face ftFace = font.mFreeTypeFace; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; -#ifdef FREETYPE_BITMAP_SUPPORT - // Check to see if we should be loading a Fixed Size bitmap? - if ( font.mIsFixedSizeBitmap ) + switch(fontIdCacheItem.type) { - int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR ); - if ( FT_Err_Ok == error ) + case FontDescription::FACE_FONT: { - glyph.width = font.mFixedWidthPixels; - glyph.height = font.mFixedHeightPixels; - glyph.advance = font.mFixedWidthPixels; - glyph.xBearing = 0.0f; - glyph.yBearing = font.mFixedHeightPixels; + const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id]; - // Adjust the metrics if the fixed-size font should be down-scaled - const float desiredFixedSize = static_cast( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical; + FT_Face ftFace = font.mFreeTypeFace; - if( desiredFixedSize > 0.f ) +#ifdef FREETYPE_BITMAP_SUPPORT + // Check to see if we should be loading a Fixed Size bitmap? + if(font.mIsFixedSizeBitmap) { - const float scaleFactor = desiredFixedSize / static_cast( font.mFixedHeightPixels ); + FT_Select_Size(ftFace, font.mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again. + int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR); + if(FT_Err_Ok == error) + { + glyph.width = font.mFixedWidthPixels; + glyph.height = font.mFixedHeightPixels; + glyph.advance = font.mFixedWidthPixels; + glyph.xBearing = 0.0f; + glyph.yBearing = font.mFixedHeightPixels; - glyph.width = floorf( glyph.width * scaleFactor ); - glyph.height = floorf( glyph.height * scaleFactor ); - glyph.advance = floorf( glyph.advance * scaleFactor ); - glyph.xBearing = floorf( glyph.xBearing * scaleFactor ); - glyph.yBearing = floorf( glyph.yBearing * scaleFactor ); + // Adjust the metrics if the fixed-size font should be down-scaled + const float desiredFixedSize = static_cast(font.mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * mDpiVertical; - glyph.scaleFactor = scaleFactor; - } - } - else - { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error ); - success = false; - } - } - else -#endif - { - int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_DEFAULT ); + if(desiredFixedSize > 0.f) + { + const float scaleFactor = desiredFixedSize / font.mFixedHeightPixels; - if( FT_Err_Ok == error ) - { - glyph.width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266; - glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ; - if( horizontal ) - { - glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266; - glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266; + glyph.width = glyph.width * scaleFactor; + glyph.height = glyph.height * scaleFactor; + glyph.advance = glyph.advance * scaleFactor; + glyph.xBearing = glyph.xBearing * scaleFactor; + glyph.yBearing = glyph.yBearing * scaleFactor; + + glyph.scaleFactor = scaleFactor; + } + } + else + { + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error); + success = false; + } } else +#endif { - glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266; - glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266; - } - } - else - { - success = false; - } - } - } - else - { - success = false; - } - } - - return success; -} + // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap. + // i.e. with the SNum-3R font. + // @todo: add an option to use the FT_LOAD_DEFAULT if required? + int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT); + + // Keep the width of the glyph before doing the software emboldening. + // It will be used to calculate a scale factor to be applied to the + // advance as Harfbuzz doesn't apply any SW emboldening to calculate + // the advance of the glyph. + const float width = static_cast(ftFace->glyph->metrics.width) * FROM_266; + + if(FT_Err_Ok == error) + { + const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD); + if(isEmboldeningRequired) + { + // Does the software bold. + FT_GlyphSlot_Embolden(ftFace->glyph); + } -bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array, - uint32_t size, - bool horizontal ) + glyph.width = static_cast(ftFace->glyph->metrics.width) * FROM_266; + glyph.height = static_cast(ftFace->glyph->metrics.height) * FROM_266; + if(horizontal) + { + glyph.xBearing += static_cast(ftFace->glyph->metrics.horiBearingX) * FROM_266; + glyph.yBearing += static_cast(ftFace->glyph->metrics.horiBearingY) * FROM_266; + } + else + { + glyph.xBearing += static_cast(ftFace->glyph->metrics.vertBearingX) * FROM_266; + glyph.yBearing += static_cast(ftFace->glyph->metrics.vertBearingY) * FROM_266; + } + + if(isEmboldeningRequired && !Dali::EqualsZero(width)) + { + // If the glyph is emboldened by software, the advance is multiplied by a + // scale factor to make it slightly bigger. + glyph.advance *= (glyph.width / width); + } + + // Use the bounding box of the bitmap to correct the metrics. + // For some fonts i.e the SNum-3R the metrics need to be corrected, + // otherwise the glyphs 'dance' up and down depending on the + // font's point size. + + FT_Glyph ftGlyph; + error = FT_Get_Glyph(ftFace->glyph, &ftGlyph); + + FT_BBox bbox; + FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox); + + const float descender = glyph.height - glyph.yBearing; + glyph.height = (bbox.yMax - bbox.yMin) * FROM_266; + glyph.yBearing = glyph.height - round(descender); + + // Created FT_Glyph object must be released with FT_Done_Glyph + FT_Done_Glyph(ftGlyph); + } + else + { + success = false; + } + } + break; + } + case FontDescription::BITMAP_FONT: + { + BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id]; + + unsigned int index = 0u; + for(auto& item : bitmapFontCacheItem.font.glyphs) + { + if(item.utf32 == glyph.index) + { + Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index]; + if(!pixelBuffer) + { + pixelBuffer = LoadImageFromFile(item.url); + } + + glyph.width = static_cast(pixelBuffer.GetWidth()); + glyph.height = static_cast(pixelBuffer.GetHeight()); + glyph.xBearing = 0.f; + glyph.yBearing = glyph.height + item.descender; + glyph.advance = glyph.width; + glyph.scaleFactor = 1.f; + break; + } + ++index; + } + + success = true; + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + } + } + } + else + { + // Check if it's an embedded image. + if((0u == glyph.fontId) && (0u != glyph.index) && (glyph.index <= mEmbeddedItemCache.Count())) + { + const EmbeddedItem& item = mEmbeddedItemCache[glyph.index - 1u]; + + glyph.width = static_cast(item.width); + glyph.height = static_cast(item.height); + glyph.xBearing = 0.f; + glyph.yBearing = glyph.height; + glyph.advance = glyph.width; + glyph.scaleFactor = 1.f; + } + else + { + success = false; + } + } + } + + return success; +} + +bool FontClient::Plugin::GetVectorMetrics(GlyphInfo* array, + uint32_t size, + bool horizontal) { #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING - bool success( true ); + bool success(true); - for( unsigned int i=0; i 0 && - fontId-1 < mFontCache.size() ) + if((fontId > 0u) && + (fontId - 1u) < mFontIdCache.Count()) { - FontFaceCacheItem& font = mFontCache[fontId-1]; + FontFaceCacheItem& font = mFontFaceCache[mFontIdCache[fontId - 1u].id]; - if( ! font.mVectorFontId ) + if(!font.mVectorFontId) { - font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath ); + font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath); } - mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] ); + mVectorFontCache->GetGlyphMetrics(font.mVectorFontId, array[i]); // Vector metrics are in EMs, convert to pixels - const float scale = ( static_cast( font.mRequestedPointSize ) * FROM_266 ) * static_cast( mDpiVertical ) / POINTS_PER_INCH; - array[i].width *= scale; - array[i].height *= scale; + const float scale = (static_cast(font.mRequestedPointSize) * FROM_266) * static_cast(mDpiVertical) / POINTS_PER_INCH; + array[i].width *= scale; + array[i].height *= scale; array[i].xBearing *= scale; array[i].yBearing *= scale; - array[i].advance *= scale; + array[i].advance *= scale; } else { @@ -1084,387 +1542,716 @@ bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array, #endif } -void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth ) +void FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth) { - if( ( fontId > 0 ) && - ( fontId - 1u < mFontCache.size() ) ) + const FontId index = fontId - 1u; + + if((fontId > 0u) && + (index < mFontIdCache.Count())) { - FT_Face ftFace = mFontCache[fontId - 1u].mFreeTypeFace; + data.isColorBitmap = false; + data.isColorEmoji = false; - FT_Error error; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; -#ifdef FREETYPE_BITMAP_SUPPORT - // Check to see if this is fixed size bitmap - if ( mFontCache[fontId - 1u].mIsFixedSizeBitmap ) + switch(fontIdCacheItem.type) { - error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR ); - } - else -#endif - { - error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT ); - } - if( FT_Err_Ok == error ) - { - FT_Glyph glyph; - error = FT_Get_Glyph( ftFace->glyph, &glyph ); - - // Convert to bitmap if necessary - if ( FT_Err_Ok == error ) + case FontDescription::FACE_FONT: { - if( glyph->format != FT_GLYPH_FORMAT_BITMAP ) + // For the software italics. + bool isShearRequired = false; + + const FontFaceCacheItem& fontFaceCacheItem = mFontFaceCache[fontIdCacheItem.id]; + FT_Face ftFace = fontFaceCacheItem.mFreeTypeFace; + + FT_Error error; + +#ifdef FREETYPE_BITMAP_SUPPORT + // Check to see if this is fixed size bitmap + if(fontFaceCacheItem.mIsFixedSizeBitmap) + { + error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR); + } + else +#endif { - // Check whether we should create a bitmap for the outline - if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 ) + // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap. + // i.e. with the SNum-3R font. + // @todo: add an option to use the FT_LOAD_DEFAULT if required? + error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT); + } + if(FT_Err_Ok == error) + { + if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD)) { - // Set up a stroker - FT_Stroker stroker; - error = FT_Stroker_New(mFreeTypeLibrary, &stroker ); + // Does the software bold. + FT_GlyphSlot_Embolden(ftFace->glyph); + } - if ( FT_Err_Ok == error ) + if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)) + { + // Will do the software italic. + isShearRequired = true; + } + + FT_Glyph glyph; + error = FT_Get_Glyph(ftFace->glyph, &glyph); + + // Convert to bitmap if necessary + if(FT_Err_Ok == error) + { + if(glyph->format != FT_GLYPH_FORMAT_BITMAP) { - FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 ); - error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 ); + int offsetX = 0, offsetY = 0; + bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0); - if ( FT_Err_Ok == error ) + // Create a bitmap for the outline + if(isOutlineGlyph) { - FT_Stroker_Done( stroker ); + // Retrieve the horizontal and vertical distance from the current pen position to the + // left and top border of the glyph bitmap for a normal glyph before applying the outline. + if(FT_Err_Ok == error) + { + FT_Glyph normalGlyph; + error = FT_Get_Glyph(ftFace->glyph, &normalGlyph); + + error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1); + if(FT_Err_Ok == error) + { + FT_BitmapGlyph bitmapGlyph = reinterpret_cast(normalGlyph); + + offsetX = bitmapGlyph->left; + offsetY = bitmapGlyph->top; + } + + // Created FT_Glyph object must be released with FT_Done_Glyph + FT_Done_Glyph(normalGlyph); + } + + // Now apply the outline + + // Set up a stroker + FT_Stroker stroker; + error = FT_Stroker_New(mFreeTypeLibrary, &stroker); + + if(FT_Err_Ok == error) + { + FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); + error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1); + + if(FT_Err_Ok == error) + { + FT_Stroker_Done(stroker); + } + else + { + DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error); + } + } + else + { + DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error); + } + } + + error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); + if(FT_Err_Ok == error) + { + FT_BitmapGlyph bitmapGlyph = reinterpret_cast(glyph); + + if(isOutlineGlyph) + { + // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph + data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth; + data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth; + } + + ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired); } else { - DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error); } } else { - DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error ); + ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired); } - } - error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 ); - if ( FT_Err_Ok == error ) - { - FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph ); - ConvertBitmap( data, bitmapGlyph->bitmap ); - } - else - { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error ); + data.isColorEmoji = fontFaceCacheItem.mIsFixedSizeBitmap; + + // Created FT_Glyph object must be released with FT_Done_Glyph + FT_Done_Glyph(glyph); } } else { - ConvertBitmap( data, ftFace->glyph->bitmap ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error); } + break; + } + case FontDescription::BITMAP_FONT: + { + BitmapFontCacheItem& bitmapFontCacheItem = mBitmapFontCache[fontIdCacheItem.id]; + + unsigned int index = 0u; + for(auto& item : bitmapFontCacheItem.font.glyphs) + { + if(item.utf32 == glyphIndex) + { + Devel::PixelBuffer& pixelBuffer = bitmapFontCacheItem.pixelBuffers[index]; + if(!pixelBuffer) + { + pixelBuffer = LoadImageFromFile(item.url); + } + + data.width = pixelBuffer.GetWidth(); + data.height = pixelBuffer.GetHeight(); + + data.isColorBitmap = bitmapFontCacheItem.font.isColorFont; - // Created FT_Glyph object must be released with FT_Done_Glyph - FT_Done_Glyph( glyph ); + ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer()); + + // Sets the pixel format. + data.format = pixelBuffer.GetPixelFormat(); + break; + } + ++index; + } + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); } } - else + } + else + { + if((0u != glyphIndex) && (glyphIndex <= mEmbeddedItemCache.Count())) { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error ); + // It's an embedded item. + const EmbeddedItem& item = mEmbeddedItemCache[glyphIndex - 1u]; + + data.width = item.width; + data.height = item.height; + if(0u != item.pixelBufferId) + { + Devel::PixelBuffer pixelBuffer = mPixelBufferCache[item.pixelBufferId - 1u].pixelBuffer; + if(pixelBuffer) + { + ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer()); + + // Sets the pixel format. + data.format = pixelBuffer.GetPixelFormat(); + } + } + else + { + // Creates the output buffer + const unsigned int bufferSize = data.width * data.height * 4u; + data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[]. + + memset(data.buffer, 0u, bufferSize); + + // Just creates a void buffer. Doesn't matter what pixel format is set as is the application code the responsible of filling it. + } } } } -PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth ) +PixelData FontClient::Plugin::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth) { TextAbstraction::FontClient::GlyphBufferData data; - CreateBitmap( fontId, glyphIndex, data, outlineWidth ); + CreateBitmap(fontId, glyphIndex, false, false, data, outlineWidth); - return PixelData::New( data.buffer, - data.width * data.height * Pixel::GetBytesPerPixel( data.format ), - data.width, - data.height, - data.format, - PixelData::DELETE_ARRAY ); + return PixelData::New(data.buffer, + data.width * data.height * Pixel::GetBytesPerPixel(data.format), + data.width, + data.height, + data.format, + PixelData::DELETE_ARRAY); } -void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight ) +void FontClient::Plugin::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight) { - blob = NULL; + blob = nullptr; blobLength = 0; #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING - if( fontId > 0 && - fontId-1 < mFontCache.size() ) + if((fontId > 0u) && + (fontId - 1u < mFontIdCache.Count())) { - FontFaceCacheItem& font = mFontCache[fontId-1]; + const FontId fontFaceId = mFontIdCache[fontId - 1u].id; + FontFaceCacheItem& font = mFontFaceCache[fontFaceId]; - if( ! font.mVectorFontId ) + if(!font.mVectorFontId) { - font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath ); + font.mVectorFontId = mVectorFontCache->GetFontId(font.mPath); } - mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight ); + mVectorFontCache->GetVectorBlob(font.mVectorFontId, fontFaceId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight); } #endif } -const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize ) +const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize %d.\n", requestedPointSize); // First look into the cache if there is an ellipsis glyph for the requested point size. - for( Vector::ConstIterator it = mEllipsisCache.Begin(), - endIt = mEllipsisCache.End(); - it != endIt; - ++it ) + for(const auto& item : mEllipsisCache) { - const EllipsisItem& item = *it; - - if( fabsf( item.requestedPointSize - requestedPointSize ) < Math::MACHINE_EPSILON_1000 ) + if(item.requestedPointSize == requestedPointSize) { // Use the glyph in the cache. - DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index ); - DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index); + DALI_LOG_INFO(gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n"); return item.glyph; } } // No glyph has been found. Create one. - mEllipsisCache.PushBack( EllipsisItem() ); - EllipsisItem& item = *( mEllipsisCache.End() - 1u ); + mEllipsisCache.PushBack(EllipsisItem()); + EllipsisItem& item = *(mEllipsisCache.End() - 1u); item.requestedPointSize = requestedPointSize; // Find a font for the ellipsis glyph. - item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER, - requestedPointSize, - false ); + item.glyph.fontId = FindDefaultFont(ELLIPSIS_CHARACTER, + requestedPointSize, + false); // Set the character index to access the glyph inside the font. - item.glyph.index = FT_Get_Char_Index( mFontCache[item.glyph.fontId-1].mFreeTypeFace, - ELLIPSIS_CHARACTER ); + item.glyph.index = FT_Get_Char_Index(mFontFaceCache[mFontIdCache[item.glyph.fontId - 1u].id].mFreeTypeFace, + ELLIPSIS_CHARACTER); - GetBitmapMetrics( &item.glyph, 1u, true ); + GetBitmapMetrics(&item.glyph, 1u, true); - DALI_LOG_INFO( gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index ); - DALI_LOG_INFO( gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " glyph id %d found in the cache.\n", item.glyph.index); + DALI_LOG_INFO(gLogFilter, Debug::General, " font %d.\n", item.glyph.fontId); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n"); return item.glyph; } -bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex ) +bool FontClient::Plugin::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex) { FT_Error error = -1; -#ifdef FREETYPE_BITMAP_SUPPORT - if( ( fontId > 0 ) && - ( fontId - 1u < mFontCache.size() ) ) + const FontId index = fontId - 1u; + + if((fontId > 0u) && + (index < mFontIdCache.Count())) { - const FontFaceCacheItem& item = mFontCache[fontId - 1u]; - FT_Face ftFace = item.mFreeTypeFace; + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; - // Check to see if this is fixed size bitmap - if( item.mHasColorTables ) + switch(fontIdCacheItem.type) { - error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR ); + case FontDescription::FACE_FONT: + { +#ifdef FREETYPE_BITMAP_SUPPORT + const FontFaceCacheItem& item = mFontFaceCache[fontIdCacheItem.id]; + FT_Face ftFace = item.mFreeTypeFace; + + // Check to see if this is fixed size bitmap + if(item.mHasColorTables) + { + error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR); + } +#endif + break; + } + case FontDescription::BITMAP_FONT: + { + error = FT_Err_Ok; // Will return true; + break; + } + default: + { + DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n"); + } } } -#endif return FT_Err_Ok == error; } +FT_FaceRec_* FontClient::Plugin::GetFreetypeFace(FontId fontId) +{ + FT_Face fontFace = nullptr; + + const FontId index = fontId - 1u; + if((fontId > 0u) && + (index < mFontIdCache.Count())) + { + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; + + if(FontDescription::FACE_FONT == fontIdCacheItem.type) + { + fontFace = mFontFaceCache[fontIdCacheItem.id].mFreeTypeFace; + } + } + return fontFace; +} + +FontDescription::Type FontClient::Plugin::GetFontType(FontId fontId) +{ + const FontId index = fontId - 1u; + if((fontId > 0u) && + (index < mFontIdCache.Count())) + { + return mFontIdCache[index].type; + } + return FontDescription::INVALID; +} + +bool FontClient::Plugin::AddCustomFontDirectory(const FontPath& path) +{ + // nullptr as first parameter means the current configuration is used. + return FcConfigAppFontAddDir(nullptr, reinterpret_cast(path.c_str())); +} + +GlyphIndex FontClient::Plugin::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat) +{ + EmbeddedItem embeddedItem; + + embeddedItem.pixelBufferId = 0u; + embeddedItem.width = description.width; + embeddedItem.height = description.height; + + pixelFormat = Pixel::A8; + + if(!description.url.empty()) + { + // Check if the url is in the cache. + PixelBufferId index = 0u; + + for(const auto& cacheItem : mPixelBufferCache) + { + ++index; + if(cacheItem.url == description.url) + { + // The url is in the pixel buffer cache. + // Set the index +1 to the vector. + embeddedItem.pixelBufferId = index; + break; + } + } + + Devel::PixelBuffer pixelBuffer; + if(0u == embeddedItem.pixelBufferId) + { + // The pixel buffer is not in the cache. Create one and cache it. + + // Load the image from the url. + pixelBuffer = LoadImageFromFile(description.url); + + // Create the cache item. + PixelBufferCacheItem pixelBufferCacheItem; + pixelBufferCacheItem.pixelBuffer = pixelBuffer; + pixelBufferCacheItem.url = description.url; + + // Store the cache item in the cache. + mPixelBufferCache.push_back(std::move(pixelBufferCacheItem)); + + // Set the pixel buffer id to the embedded item. + embeddedItem.pixelBufferId = mPixelBufferCache.size(); + } + else + { + // Retrieve the pixel buffer from the cache to set the pixel format. + pixelBuffer = mPixelBufferCache[embeddedItem.pixelBufferId - 1u].pixelBuffer; + } + + if(pixelBuffer) + { + // Set the size of the embedded item if it has not been set. + if(0u == embeddedItem.width) + { + embeddedItem.width = static_cast(pixelBuffer.GetWidth()); + } + + if(0u == embeddedItem.height) + { + embeddedItem.height = static_cast(pixelBuffer.GetHeight()); + } + + // Set the pixel format. + pixelFormat = pixelBuffer.GetPixelFormat(); + } + } + + // Find if the same embeddedItem has already been created. + unsigned int index = 0u; + for(const auto& item : mEmbeddedItemCache) + { + ++index; + if((item.pixelBufferId == embeddedItem.pixelBufferId) && + (item.width == embeddedItem.width) && + (item.height == embeddedItem.height)) + { + return index; + } + } + + // Cache the embedded item. + mEmbeddedItemCache.PushBack(embeddedItem); + + return mEmbeddedItemCache.Count(); +} +//SHS + +void FontClient::Plugin::EnableAtlasLimitation(bool enabled) +{ + mIsAtlasLimitationEnabled = enabled; +} + +bool FontClient::Plugin::IsAtlasLimitationEnabled() const +{ + return mIsAtlasLimitationEnabled; +} + +Size FontClient::Plugin::GetMaximumTextAtlasSize() const +{ + return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE; +} + +Size FontClient::Plugin::GetDefaultTextAtlasSize() const +{ + return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE; +} + +Size FontClient::Plugin::GetCurrentMaximumBlockSizeFitInAtlas() const +{ + return mCurrentMaximumBlockSizeFitInAtlas; +} + +bool FontClient::Plugin::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas) +{ + bool isChanged = false; + const Size& maxTextAtlasSize = TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE; + const uint16_t& padding = TextAbstraction::FontClient::PADDING_TEXT_ATLAS_BLOCK; + + if(currentMaximumBlockSizeFitInAtlas.width <= maxTextAtlasSize.width - padding + && currentMaximumBlockSizeFitInAtlas.height <= maxTextAtlasSize.height - padding) + { + mCurrentMaximumBlockSizeFitInAtlas = currentMaximumBlockSizeFitInAtlas; + isChanged = true; + } + + return isChanged; +} + +uint32_t FontClient::Plugin::GetNumberOfPointsPerOneUnitOfPointSize() const +{ + return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;; +} + + void FontClient::Plugin::InitSystemFonts() { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n"); - FcFontSet* fontSet = GetFcFontSet(); + FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy. - if( fontSet ) + if(fontSet) { - DALI_LOG_INFO( gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont ); + DALI_LOG_INFO(gLogFilter, Debug::General, " number of system fonts : %d\n", fontSet->nfont); // Reserve some space to avoid reallocations. - mSystemFonts.reserve( fontSet->nfont ); + mSystemFonts.reserve(fontSet->nfont); - for( int i = 0u; i < fontSet->nfont; ++i ) + for(int i = 0u; i < fontSet->nfont; ++i) { FcPattern* fontPattern = fontSet->fonts[i]; FontPath path; // Skip fonts with no path - if( GetFcString( fontPattern, FC_FILE, path ) ) + if(GetFcString(fontPattern, FC_FILE, path)) { - mSystemFonts.push_back( FontDescription() ); + mSystemFonts.push_back(FontDescription()); FontDescription& fontDescription = mSystemFonts.back(); - fontDescription.path = path; + fontDescription.path = std::move(path); - int width = 0; + int width = 0; int weight = 0; - int slant = 0; - GetFcString( fontPattern, FC_FAMILY, fontDescription.family ); - GetFcInt( fontPattern, FC_WIDTH, width ); - GetFcInt( fontPattern, FC_WEIGHT, weight ); - GetFcInt( fontPattern, FC_SLANT, slant ); - fontDescription.width = IntToWidthType( width ); - fontDescription.weight = IntToWeightType( weight ); - fontDescription.slant = IntToSlantType( slant ); - - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); + int slant = 0; + GetFcString(fontPattern, FC_FAMILY, fontDescription.family); + GetFcInt(fontPattern, FC_WIDTH, width); + GetFcInt(fontPattern, FC_WEIGHT, weight); + GetFcInt(fontPattern, FC_SLANT, slant); + fontDescription.width = IntToWidthType(width); + fontDescription.weight = IntToWeightType(weight); + fontDescription.slant = IntToSlantType(slant); + + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " description; family : [%s]\n", fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]); } } - FcFontSetDestroy( fontSet ); + // Destroys the font set created. + FcFontSetDestroy(fontSet); } - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n"); } -bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet ) +bool FontClient::Plugin::MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n"); - FcResult result = FcResultMatch; - FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result ); + FcResult result = FcResultMatch; + FcPattern* match = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy. - const bool matched = NULL != match; - DALI_LOG_INFO( gLogFilter, Debug::General, " pattern matched : %s\n", ( matched ? "true" : "false" ) ); + const bool matched = nullptr != match; + DALI_LOG_INFO(gLogFilter, Debug::General, " pattern matched : %s\n", (matched ? "true" : "false")); - if( matched ) + if(matched) { - int width = 0; + int width = 0; int weight = 0; - int slant = 0; - GetFcString( match, FC_FILE, fontDescription.path ); - GetFcString( match, FC_FAMILY, fontDescription.family ); - GetFcInt( match, FC_WIDTH, width ); - GetFcInt( match, FC_WEIGHT, weight ); - GetFcInt( match, FC_SLANT, slant ); - fontDescription.width = IntToWidthType( width ); - fontDescription.weight = IntToWeightType( weight ); - fontDescription.slant = IntToSlantType( slant ); - - // Cache the character ranges. - FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet ); + int slant = 0; + GetFcString(match, FC_FILE, fontDescription.path); + GetFcString(match, FC_FAMILY, fontDescription.family); + GetFcInt(match, FC_WIDTH, width); + GetFcInt(match, FC_WEIGHT, weight); + GetFcInt(match, FC_SLANT, slant); + fontDescription.width = IntToWidthType(width); + fontDescription.weight = IntToWeightType(weight); + fontDescription.slant = IntToSlantType(slant); + + // Retrieve the character set and increase the reference counter. + FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet); + *characterSet = FcCharSetCopy(*characterSet); // destroyed the matched pattern - FcPatternDestroy( match ); + FcPatternDestroy(match); - DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); + DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]); } - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n"); return matched; } -FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const +FcPattern* FontClient::Plugin::CreateFontFamilyPattern(const FontDescription& fontDescription) const { // create the cached font family lookup pattern // a pattern holds a set of names, each name refers to a property of the font - FcPattern* fontFamilyPattern = FcPatternCreate(); + FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy. - if( !fontFamilyPattern ) + if(!fontFamilyPattern) { - return NULL; + return nullptr; } // add a property to the pattern for the font family - FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast( fontDescription.family.c_str() ) ); + FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast(fontDescription.family.c_str())); + + // add a property to the pattern for local setting. + const char* locale = setlocale(LC_MESSAGES, nullptr); + if(locale != nullptr) + { + FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast(locale)); + } int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width]; - if( width < 0 ) + if(width < 0) { // Use default. width = DEFAULT_FONT_WIDTH; } int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight]; - if( weight < 0 ) + if(weight < 0) { // Use default. weight = DEFAULT_FONT_WEIGHT; } int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant]; - if( slant < 0 ) + if(slant < 0) { // Use default. slant = DEFAULT_FONT_SLANT; } - FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width ); - FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight ); - FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant ); - - // Add a property of the pattern, to say we want to match TrueType fonts - FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast( FONT_FORMAT.c_str() ) ); + FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width); + FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight); + FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant); // modify the config, with the mFontFamilyPatterm - FcConfigSubstitute( NULL /* use default configure */, fontFamilyPattern, FcMatchPattern ); + FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern); // provide default values for unspecified properties in the font pattern // e.g. patterns without a specified style or weight are set to Medium - FcDefaultSubstitute( fontFamilyPattern ); + FcDefaultSubstitute(fontFamilyPattern); return fontFamilyPattern; } _FcFontSet* FontClient::Plugin::GetFcFontSet() const { + FcFontSet* fontset = nullptr; + // create a new pattern. // a pattern holds a set of names, each name refers to a property of the font FcPattern* pattern = FcPatternCreate(); - FcFontSet* fontset = NULL; - - // create an object set used to define which properties are to be returned in the patterns from FcFontList. - FcObjectSet* objectSet = FcObjectSetCreate(); - - if( objectSet ) + if(nullptr != pattern) { - // build an object set from a list of property names - FcObjectSetAdd( objectSet, FC_FILE ); - FcObjectSetAdd( objectSet, FC_FAMILY ); - FcObjectSetAdd( objectSet, FC_WIDTH ); - FcObjectSetAdd( objectSet, FC_WEIGHT ); - FcObjectSetAdd( objectSet, FC_SLANT ); + // create an object set used to define which properties are to be returned in the patterns from FcFontList. + FcObjectSet* objectSet = FcObjectSetCreate(); - // get a list of fonts - // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns - fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet ); + if(nullptr != objectSet) + { + // build an object set from a list of property names + FcObjectSetAdd(objectSet, FC_FILE); + FcObjectSetAdd(objectSet, FC_FAMILY); + FcObjectSetAdd(objectSet, FC_WIDTH); + FcObjectSetAdd(objectSet, FC_WEIGHT); + FcObjectSetAdd(objectSet, FC_SLANT); + + // get a list of fonts + // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns + fontset = FcFontList(nullptr /* the default configuration is checked to be up to date, and used */, pattern, objectSet); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy. + + // clear up the object set + FcObjectSetDestroy(objectSet); + } - // clear up the object set - FcObjectSetDestroy( objectSet ); - } - // clear up the pattern - if( pattern ) - { - FcPatternDestroy( pattern ); + // clear up the pattern + FcPatternDestroy(pattern); } return fontset; } -bool FontClient::Plugin::GetFcString( const FcPattern* const pattern, - const char* const n, - std::string& string ) +bool FontClient::Plugin::GetFcString(const FcPattern* const pattern, + const char* const n, + std::string& string) { - FcChar8* file = NULL; - const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file ); + FcChar8* file = nullptr; + const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file); - if( FcResultMatch == retVal ) + if(FcResultMatch == retVal) { // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*. - string.assign( reinterpret_cast( file ) ); + string.assign(reinterpret_cast(file)); return true; } @@ -1472,11 +2259,11 @@ bool FontClient::Plugin::GetFcString( const FcPattern* const pattern, return false; } -bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal ) +bool FontClient::Plugin::GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal) { - const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal ); + const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal); - if( FcResultMatch == retVal ) + if(FcResultMatch == retVal) { return true; } @@ -1484,146 +2271,290 @@ bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* return false; } -FontId FontClient::Plugin::CreateFont( const FontPath& path, - PointSize26Dot6 requestedPointSize, - FaceIndex faceIndex, - bool cacheDescription ) +FontId FontClient::Plugin::CreateFont(const FontPath& path, + PointSize26Dot6 requestedPointSize, + FaceIndex faceIndex, + bool cacheDescription) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize); FontId id = 0u; // Create & cache new font face FT_Face ftFace; - int error = FT_New_Face( mFreeTypeLibrary, - path.c_str(), - 0, - &ftFace ); + int error = FT_New_Face(mFreeTypeLibrary, + path.c_str(), + 0, + &ftFace); - if( FT_Err_Ok == error ) + if(FT_Err_Ok == error) { // Check if a font is scalable. - const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) ); - const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes ); - const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) ); + const bool isScalable = (0 != (ftFace->face_flags & FT_FACE_FLAG_SCALABLE)); + const bool hasFixedSizedBitmaps = (0 != (ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES)) && (0 != ftFace->num_fixed_sizes); + const bool hasColorTables = (0 != (ftFace->face_flags & FT_FACE_FLAG_COLOR)); + FontId fontFaceId = 0u; - DALI_LOG_INFO( gLogFilter, Debug::General, " isScalable : [%s]\n", ( isScalable ? "true" : "false" ) ); - DALI_LOG_INFO( gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) ); - DALI_LOG_INFO( gLogFilter, Debug::General, " hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) ); + DALI_LOG_INFO(gLogFilter, Debug::General, " isScalable : [%s]\n", (isScalable ? "true" : "false")); + DALI_LOG_INFO(gLogFilter, Debug::General, " hasFixedSizedBitmaps : [%s]\n", (hasFixedSizedBitmaps ? "true" : "false")); + DALI_LOG_INFO(gLogFilter, Debug::General, " hasColorTables : [%s]\n", (hasColorTables ? "true" : "false")); // Check to see if the font contains fixed sizes? - if( !isScalable && hasFixedSizedBitmaps ) + if(!isScalable && hasFixedSizedBitmaps) { PointSize26Dot6 actualPointSize = 0u; - int fixedSizeIndex = 0; - for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex ) + int fixedSizeIndex = 0; + for(; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex) { const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size; - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " size index : %d, size : %d\n", fixedSizeIndex, fixedSize); - if( fixedSize >= requestedPointSize ) + if(fixedSize >= requestedPointSize) { actualPointSize = fixedSize; break; } } - if( 0u == actualPointSize ) + if(0u == actualPointSize) { // The requested point size is bigger than the bigest fixed size. - fixedSizeIndex = ftFace->num_fixed_sizes - 1; + fixedSizeIndex = ftFace->num_fixed_sizes - 1; actualPointSize = ftFace->available_sizes[fixedSizeIndex].size; } - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize); // Tell Freetype to use this size - error = FT_Select_Size( ftFace, fixedSizeIndex ); - if ( FT_Err_Ok != error ) + error = FT_Select_Size(ftFace, fixedSizeIndex); + if(FT_Err_Ok != error) { - DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error); } else { - float fixedWidth = static_cast< float >( ftFace->available_sizes[ fixedSizeIndex ].width ); - float fixedHeight = static_cast< float >( ftFace->available_sizes[ fixedSizeIndex ].height ); + const float fixedWidth = static_cast(ftFace->available_sizes[fixedSizeIndex].width); + const float fixedHeight = static_cast(ftFace->available_sizes[fixedSizeIndex].height); // Indicate that the font is a fixed sized bitmap - FontMetrics metrics( fixedHeight, // The ascender in pixels. - 0.0f, - fixedHeight, // The height in pixels. - 0.0f, - 0.0f ); - - mFontCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedWidth, fixedHeight, hasColorTables ) ); - id = mFontCache.size(); + FontMetrics metrics(fixedHeight, // The ascender in pixels. + 0.0f, + fixedHeight, // The height in pixels. + 0.0f, + 0.0f); + + // Create the FreeType font face item to cache. + FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables); + + // Set the index to the font's id cache. + fontFaceCacheItem.mFontId = mFontIdCache.Count(); + + // Create the font id item to cache. + FontIdCacheItem fontIdCacheItem; + fontIdCacheItem.type = FontDescription::FACE_FONT; + + // Set the index to the FreeType font face cache. + fontIdCacheItem.id = mFontFaceCache.size(); + fontFaceId = fontIdCacheItem.id + 1u; + + // Cache the items. + mFontFaceCache.push_back(fontFaceCacheItem); + mFontIdCache.PushBack(fontIdCacheItem); + + // Set the font id to be returned. + id = mFontIdCache.Count(); } } else { - error = FT_Set_Char_Size( ftFace, - 0, - requestedPointSize, - mDpiHorizontal, - mDpiVertical ); - - if( FT_Err_Ok == error ) + if(mIsAtlasLimitationEnabled) + { + //There is limitation on block size to fit in predefined atlas size. + //If the block size cannot fit into atlas size, then the system cannot draw block. + //This is workaround to avoid issue in advance + //Decrementing point-size until arriving to maximum allowed block size. + auto requestedPointSizeBackup= requestedPointSize; + const Size& maxSizeFitInAtlas = GetCurrentMaximumBlockSizeFitInAtlas(); + error = SearchOnProperPointSize(ftFace, mDpiHorizontal, mDpiVertical, maxSizeFitInAtlas, requestedPointSize); + + if(requestedPointSize != requestedPointSizeBackup) + { + DALI_LOG_WARNING(" The requested-point-size : %d, is reduced to point-size : %d\n", requestedPointSizeBackup, requestedPointSize); + } + } + else { + error = FT_Set_Char_Size(ftFace, + 0, + requestedPointSize, + mDpiHorizontal, + mDpiVertical); + } + if(FT_Err_Ok == error) + { FT_Size_Metrics& ftMetrics = ftFace->size->metrics; - FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266, - static_cast< float >( ftMetrics.descender ) * FROM_266, - static_cast< float >( ftMetrics.height ) * FROM_266, - static_cast< float >( ftFace->underline_position ) * FROM_266, - static_cast< float >( ftFace->underline_thickness ) * FROM_266 ); + FontMetrics metrics(static_cast(ftMetrics.ascender) * FROM_266, + static_cast(ftMetrics.descender) * FROM_266, + static_cast(ftMetrics.height) * FROM_266, + static_cast(ftFace->underline_position) * FROM_266, + static_cast(ftFace->underline_thickness) * FROM_266); + + // Create the FreeType font face item to cache. + FontFaceCacheItem fontFaceCacheItem(ftFace, path, requestedPointSize, faceIndex, metrics); + + // Set the index to the font's id cache. + fontFaceCacheItem.mFontId = mFontIdCache.Count(); + + // Create the font id item to cache. + FontIdCacheItem fontIdCacheItem; + fontIdCacheItem.type = FontDescription::FACE_FONT; + + // Set the index to the FreeType font face cache. + fontIdCacheItem.id = mFontFaceCache.size(); + fontFaceId = fontIdCacheItem.id + 1u; + + // Cache the items. + mFontFaceCache.push_back(fontFaceCacheItem); + mFontIdCache.PushBack(fontIdCacheItem); - mFontCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics ) ); - id = mFontCache.size(); + // Set the font id to be returned. + id = mFontIdCache.Count(); } else { - DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize ); + DALI_LOG_INFO(gLogFilter, Debug::General, " FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize); } } - if( 0u != id ) + if(0u != fontFaceId) { - if( cacheDescription ) + if(cacheDescription) { - CacheFontPath( ftFace, id, requestedPointSize, path ); + CacheFontPath(ftFace, fontFaceId, requestedPointSize, path); } } } else { - DALI_LOG_INFO( gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str() ); + DALI_LOG_INFO(gLogFilter, Debug::General, " FreeType New_Face error: %d for [%s]\n", error, path.c_str()); } - DALI_LOG_INFO( gLogFilter, Debug::General, " font id : %d\n", id ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font id : %d\n", id); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n"); return id; } -void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap ) +void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer) { - if( srcBitmap.width*srcBitmap.rows > 0 ) + // Set the input dimensions. + const ImageDimensions inputDimensions(srcWidth, srcHeight); + + // Set the output dimensions. + // If the output dimension is not given, the input dimension is set + // and won't be downscaling. + data.width = (data.width == 0) ? srcWidth : data.width; + data.height = (data.height == 0) ? srcHeight : data.height; + const ImageDimensions desiredDimensions(data.width, data.height); + + // Creates the output buffer + const unsigned int bufferSize = data.width * data.height * 4u; + data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[]. + + if(inputDimensions == desiredDimensions) + { + // There isn't downscaling. + memcpy(data.buffer, srcBuffer, bufferSize); + } + else + { + Dali::Internal::Platform::LanczosSample4BPP(srcBuffer, + inputDimensions, + data.buffer, + desiredDimensions); + } +} + +void FontClient::Plugin::ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap, bool isShearRequired) +{ + if(srcBitmap.width * srcBitmap.rows > 0) { - switch( srcBitmap.pixel_mode ) + switch(srcBitmap.pixel_mode) { case FT_PIXEL_MODE_GRAY: { - if( srcBitmap.pitch == static_cast( srcBitmap.width ) ) + if(srcBitmap.pitch == static_cast(srcBitmap.width)) { - const unsigned int bufferSize = srcBitmap.width * srcBitmap.rows; - data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[]. - data.width = srcBitmap.width; - data.height = srcBitmap.rows; - data.format = Pixel::L8; - memcpy( data.buffer, srcBitmap.buffer, bufferSize ); + uint8_t* pixelsIn = srcBitmap.buffer; + unsigned int width = srcBitmap.width; + unsigned height = srcBitmap.rows; + + std::unique_ptr pixelsOutPtr(nullptr, free); + + if(isShearRequired) + { + /** + * Glyphs' bitmaps with no slant retrieved from FreeType: + * __________ ____ + * |XXXXXXXX| |XX| + * | XX | |XX| + * | XX | |XX| + * | XX | |XX| + * | XX | |XX| + * | XX | |XX| + * ---------- ---- + * + * Expected glyphs' bitmaps with italic slant: + * ____________ ______ + * | XXXXXXXX| | XX| + * | XX | | XX| + * | XX | | XX | + * | XX | | XX | + * | XX | |XX | + * | XX | |XX | + * ------------ ------ + * + * Glyphs' bitmaps with software italic slant retrieved from FreeType: + * __________ ______ + * |XXXXXXXX| | XX| + * | XX | | XX| + * | XX | | XX | + * | XX | | XX | + * | XX | |XX | + * | XX | |XX | + * ---------- ------ + * + * This difference in some bitmaps' width causes an overlap of some glyphs. This is the reason why a shear operation is done here instead of relying on the experimental FT_GlyphSlot_Oblique() implementation. + */ + unsigned int widthOut = 0u; + unsigned int heightOut = 0u; + uint8_t* pixelsOut = nullptr; + + Dali::Internal::Platform::HorizontalShear(pixelsIn, + width, + height, + 1u, + -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE, + pixelsOut, + widthOut, + heightOut); + + width = widthOut; + height = heightOut; + pixelsIn = pixelsOut; + pixelsOutPtr.reset(pixelsOut); + } + + const unsigned int bufferSize = width * height; + data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[]. + data.width = width; + data.height = height; + data.format = Pixel::L8; // Sets the pixel format. + memcpy(data.buffer, pixelsIn, bufferSize); } break; } @@ -1631,34 +2562,11 @@ void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBuffer #ifdef FREETYPE_BITMAP_SUPPORT case FT_PIXEL_MODE_BGRA: { - if( srcBitmap.pitch == static_cast( srcBitmap.width << 2u ) ) + if(srcBitmap.pitch == static_cast(srcBitmap.width << 2u)) { - // Set the input dimensions. - const ImageDimensions inputDimensions( srcBitmap.width, srcBitmap.rows ); + ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer); - // Set the output dimensions. - // If the output dimension is not given, the input dimension is set - // and won't be downscaling. - data.width = ( data.width == 0 ) ? srcBitmap.width : data.width; - data.height = ( data.height == 0 ) ? srcBitmap.rows : data.height; - const ImageDimensions desiredDimensions( data.width, data.height ); - - // Creates the output buffer - const unsigned int bufferSize = data.width * data.height * 4u; - data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[]. - - if( inputDimensions == desiredDimensions ) - { - // There isn't downscaling. - memcpy( data.buffer, srcBitmap.buffer, bufferSize ); - } - else - { - Dali::Internal::Platform::LanczosSample4BPP( srcBitmap.buffer, - inputDimensions, - data.buffer, - desiredDimensions ); - } + // Sets the pixel format. data.format = Pixel::BGRA8888; } break; @@ -1666,174 +2574,171 @@ void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBuffer #endif default: { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n"); break; } } } } -bool FontClient::Plugin::FindFont( const FontPath& path, - PointSize26Dot6 requestedPointSize, - FaceIndex faceIndex, - FontId& fontId ) const +bool FontClient::Plugin::FindFont(const FontPath& path, + PointSize26Dot6 requestedPointSize, + FaceIndex faceIndex, + FontId& fontId) const { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " path : [%s]\n", path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontCache.size() ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " path : [%s]\n", path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of fonts in the cache : %d\n", mFontFaceCache.size()); fontId = 0u; - for( std::vector::const_iterator it = mFontCache.begin(), - endIt = mFontCache.end(); - it != endIt; - ++it, ++fontId ) + for(const auto& cacheItem : mFontFaceCache) { - const FontFaceCacheItem& cacheItem = *it; - - if( cacheItem.mRequestedPointSize == requestedPointSize && - cacheItem.mFaceIndex == faceIndex && - cacheItem.mPath == path ) + if(cacheItem.mRequestedPointSize == requestedPointSize && + cacheItem.mFaceIndex == faceIndex && + cacheItem.mPath == path) { - ++fontId; + fontId = cacheItem.mFontId + 1u; - DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font found, id : %d\n", fontId); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n"); return true; } } - DALI_LOG_INFO( gLogFilter, Debug::General, " font not found\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font not found\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n"); return false; } -bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription, - FontDescriptionId& validatedFontId ) +bool FontClient::Plugin::FindValidatedFont(const FontDescription& fontDescription, + FontDescriptionId& validatedFontId) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size() ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of validated fonts in the cache : %d\n", mValidatedFontCache.size()); validatedFontId = 0u; - for( std::vector::const_iterator it = mValidatedFontCache.begin(), - endIt = mValidatedFontCache.end(); - it != endIt; - ++it ) + for(const auto& item : mValidatedFontCache) { - const FontDescriptionCacheItem& item = *it; - - if( !fontDescription.family.empty() && - ( fontDescription.family == item.fontDescription.family ) && - ( fontDescription.width == item.fontDescription.width ) && - ( fontDescription.weight == item.fontDescription.weight ) && - ( fontDescription.slant == item.fontDescription.slant ) ) + if(!fontDescription.family.empty() && + (fontDescription.family == item.fontDescription.family) && + (fontDescription.width == item.fontDescription.width) && + (fontDescription.weight == item.fontDescription.weight) && + (fontDescription.slant == item.fontDescription.slant)) { validatedFontId = item.index; - DALI_LOG_INFO( gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " validated font found, id : %d\n", validatedFontId); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n"); return true; } } - DALI_LOG_INFO( gLogFilter, Debug::General, " validated font not found\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " validated font not found\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n"); return false; } -bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription, - FontList*& fontList, - CharacterSetList*& characterSetList ) +bool FontClient::Plugin::FindFallbackFontList(const FontDescription& fontDescription, + FontList*& fontList, + CharacterSetList*& characterSetList) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str() ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size() ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " description; family : [%s]\n", fontDescription.family.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " path : [%s]\n", fontDescription.path.c_str()); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " width : [%s]\n", FontWidth::Name[fontDescription.width]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " weight : [%s]\n", FontWeight::Name[fontDescription.weight]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " slant : [%s]\n\n", FontSlant::Name[fontDescription.slant]); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of fallback font lists in the cache : %d\n", mFallbackCache.size()); - fontList = NULL; + fontList = nullptr; - for( std::vector::const_iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end(); - it != endIt; - ++it ) + for(const auto& item : mFallbackCache) { - const FallbackCacheItem& item = *it; - - if( !fontDescription.family.empty() && - ( fontDescription.family == item.fontDescription.family ) && - ( fontDescription.width == item.fontDescription.width ) && - ( fontDescription.weight == item.fontDescription.weight ) && - ( fontDescription.slant == item.fontDescription.slant ) ) + if(!fontDescription.family.empty() && + (fontDescription.family == item.fontDescription.family) && + (fontDescription.width == item.fontDescription.width) && + (fontDescription.weight == item.fontDescription.weight) && + (fontDescription.slant == item.fontDescription.slant)) { - fontList = item.fallbackFonts; + fontList = item.fallbackFonts; characterSetList = item.characterSets; - DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list found.\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " fallback font list found.\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n"); return true; } } - DALI_LOG_INFO( gLogFilter, Debug::General, " fallback font list not found.\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " fallback font list not found.\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n"); return false; } -bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId, - PointSize26Dot6 requestedPointSize, - FontId& fontId ) +bool FontClient::Plugin::FindFont(FontDescriptionId validatedFontId, + PointSize26Dot6 requestedPointSize, + FontId& fontId) { - DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize ); + DALI_LOG_INFO(gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, " validatedFontId : %d\n", validatedFontId); + DALI_LOG_INFO(gLogFilter, Debug::General, " requestedPointSize : %d\n", requestedPointSize); fontId = 0u; - for( std::vector::const_iterator it = mFontIdCache.begin(), - endIt = mFontIdCache.end(); - it != endIt; - ++it ) + for(const auto& item : mFontDescriptionSizeCache) { - const FontIdCacheItem& item = *it; - - if( ( validatedFontId == item.validatedFontId ) && - ( requestedPointSize == item.requestedPointSize ) ) + if((validatedFontId == item.validatedFontId) && + (requestedPointSize == item.requestedPointSize)) { fontId = item.fontId; - DALI_LOG_INFO( gLogFilter, Debug::General, " font found, id : %d\n", fontId ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font found, id : %d\n", fontId); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n"); return true; } } - DALI_LOG_INFO( gLogFilter, Debug::General, " font not found.\n" ); - DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, " font not found.\n"); + DALI_LOG_INFO(gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n"); return false; } -bool FontClient::Plugin::IsScalable( const FontPath& path ) +bool FontClient::Plugin::FindBitmapFont(const FontFamily& bitmapFont, FontId& fontId) const +{ + fontId = 0u; + + for(const auto& item : mBitmapFontCache) + { + if(bitmapFont == item.font.name) + { + fontId = item.id + 1u; + return true; + } + } + + return false; +} + +bool FontClient::Plugin::IsScalable(const FontPath& path) { bool isScalable = false; FT_Face ftFace; - int error = FT_New_Face( mFreeTypeLibrary, - path.c_str(), - 0, - &ftFace ); - if( FT_Err_Ok != error ) + int error = FT_New_Face(mFreeTypeLibrary, + path.c_str(), + 0, + &ftFace); + if(FT_Err_Ok != error) { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str()); } else { @@ -1843,160 +2748,222 @@ bool FontClient::Plugin::IsScalable( const FontPath& path ) return isScalable; } -bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription ) +bool FontClient::Plugin::IsScalable(const FontDescription& fontDescription) { // Create a font pattern. - FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); + FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy. FcResult result = FcResultMatch; // match the pattern - FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result ); - bool isScalable = false; + FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy. + bool isScalable = false; - if( match ) + if(match) { // Get the path to the font file name. FontPath path; - GetFcString( match, FC_FILE, path ); - isScalable = IsScalable( path ); + GetFcString(match, FC_FILE, path); + isScalable = IsScalable(path); } else { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str()); } - FcPatternDestroy( fontFamilyPattern ); - FcPatternDestroy( match ); + + // Destroys the created patterns. + FcPatternDestroy(match); + FcPatternDestroy(fontFamilyPattern); + return isScalable; } -void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes ) +void FontClient::Plugin::GetFixedSizes(const FontPath& path, Vector& sizes) { // Empty the caller container sizes.Clear(); FT_Face ftFace; - int error = FT_New_Face( mFreeTypeLibrary, - path.c_str(), - 0, - &ftFace ); - if( FT_Err_Ok != error ) + int error = FT_New_Face(mFreeTypeLibrary, + path.c_str(), + 0, + &ftFace); + if(FT_Err_Ok != error) { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str()); } // Fetch the number of fixed sizes available - if ( ftFace->num_fixed_sizes && ftFace->available_sizes ) + if(ftFace->num_fixed_sizes && ftFace->available_sizes) { - for ( int i = 0; i < ftFace->num_fixed_sizes; ++i ) + for(int i = 0; i < ftFace->num_fixed_sizes; ++i) { - sizes.PushBack( ftFace->available_sizes[ i ].size ); + sizes.PushBack(ftFace->available_sizes[i].size); } } } -void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription, - Vector< PointSize26Dot6 >& sizes ) +void FontClient::Plugin::GetFixedSizes(const FontDescription& fontDescription, + Vector& sizes) { // Create a font pattern. - FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); + FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy. FcResult result = FcResultMatch; // match the pattern - FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result ); + FcPattern* match = FcFontMatch(nullptr /* use default configure */, fontFamilyPattern, &result); // Creates a font pattern that needs to be destroyed by calling FcPatternDestroy. - if( match ) + if(match) { // Get the path to the font file name. FontPath path; - GetFcString( match, FC_FILE, path ); - GetFixedSizes( path, sizes ); + GetFcString(match, FC_FILE, path); + GetFixedSizes(path, sizes); } else { - DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() ); + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str()); } - FcPatternDestroy( match ); - FcPatternDestroy( fontFamilyPattern ); + + // Destroys the created patterns. + FcPatternDestroy(match); + FcPatternDestroy(fontFamilyPattern); } -void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path ) +bool FontClient::Plugin::HasItalicStyle(FontId fontId) const +{ + bool hasItalicStyle = false; + + const FontId index = fontId - 1u; + + if((fontId > 0) && + (index < mFontIdCache.Count())) + { + const FontIdCacheItem& fontIdCacheItem = mFontIdCache[index]; + + if(FontDescription::FACE_FONT == fontIdCacheItem.type) + { + const FontFaceCacheItem& font = mFontFaceCache[fontIdCacheItem.id]; + + hasItalicStyle = 0u != (font.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC); + } + } + else + { + DALI_LOG_INFO(gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId); + } + + return hasItalicStyle; +} + +void FontClient::Plugin::CacheFontPath(FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize, const FontPath& path) { FontDescription description; - description.path = path; - description.family = FontFamily( ftFace->family_name ); + description.path = path; + description.family = std::move(FontFamily(ftFace->family_name)); description.weight = FontWeight::NONE; - description.width = FontWidth::NONE; - description.slant = FontSlant::NONE; + description.width = FontWidth::NONE; + description.slant = FontSlant::NONE; // Note FreeType doesn't give too much info to build a proper font style. - if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) + if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC) { description.slant = FontSlant::ITALIC; } - if( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) + if(ftFace->style_flags & FT_STYLE_FLAG_BOLD) { description.weight = FontWeight::BOLD; } FontDescriptionId validatedFontId = 0u; - if( !FindValidatedFont( description, - validatedFontId ) ) + if(!FindValidatedFont(description, + validatedFontId)) { - // Set the index to the vector of paths to font file names. - validatedFontId = mFontDescriptionCache.size(); - - FcPattern* pattern = CreateFontFamilyPattern( description ); + FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy. - FcResult result = FcResultMatch; - FcPattern* match = FcFontMatch( NULL, pattern, &result ); + FcResult result = FcResultMatch; + FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy. - FcCharSet* characterSet = NULL; - FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet ); + FcCharSet* characterSet = nullptr; + FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet); - FcPatternDestroy( match ); - FcPatternDestroy( pattern ); + const FontId fontFaceId = id - 1u; + mFontFaceCache[fontFaceId].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter. - mFontCache[id-1u].mCharacterSet = characterSet; + // Destroys the created patterns. + FcPatternDestroy(match); + FcPatternDestroy(pattern); // Add the path to the cache. - mFontDescriptionCache.push_back( description ); - mCharacterSetCache.PushBack( characterSet ); + description.type = FontDescription::FACE_FONT; + mFontDescriptionCache.push_back(description); - // Cache the index and the font's description. - FontDescriptionCacheItem item( description, - validatedFontId ); + // Set the index to the vector of paths to font file names. + validatedFontId = mFontDescriptionCache.size(); + + // Increase the reference counter and add the character set to the cache. + mCharacterSetCache.PushBack(FcCharSetCopy(characterSet)); - mValidatedFontCache.push_back( item ); + // Cache the index and the font's description. + mValidatedFontCache.push_back(std::move(FontDescriptionCacheItem(std::move(description), + validatedFontId))); // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries. - mFontIdCache.push_back( FontIdCacheItem( validatedFontId, - requestedPointSize, - id ) ); + mFontDescriptionSizeCache.push_back(FontDescriptionSizeCacheItem(validatedFontId, + requestedPointSize, + fontFaceId)); } } -FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description ) const +FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription(const FontDescription& description) { - FcCharSet* characterSet = NULL; + FcCharSet* characterSet = nullptr; - FcPattern* pattern = CreateFontFamilyPattern( description ); + FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy. - if( NULL != pattern ) + if(nullptr != pattern) { - FcResult result = FcResultMatch; - FcPattern* match = FcFontMatch( NULL, pattern, &result ); + FcResult result = FcResultMatch; + FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy. - FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet ); + FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet); - FcPatternDestroy( match ); - FcPatternDestroy( pattern ); + // Destroys the created patterns. + FcPatternDestroy(match); + FcPatternDestroy(pattern); } return characterSet; } +void FontClient::Plugin::ClearFallbackCache(std::vector& fallbackCache) +{ + for(auto& item : fallbackCache) + { + if(nullptr != item.fallbackFonts) + { + delete item.fallbackFonts; + } + + if(nullptr != item.characterSets) + { + // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector. + DestroyCharacterSets(*item.characterSets); + delete item.characterSets; + } + } +} + +void FontClient::Plugin::ClearCharacterSetFromFontFaceCache() +{ + for(auto& item : mFontFaceCache) + { + FcCharSetDestroy(item.mCharacterSet); + item.mCharacterSet = nullptr; + } +} + } // namespace Internal } // namespace TextAbstraction