X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=text%2Fdali%2Finternal%2Ftext-abstraction%2Ffont-client-plugin-impl.cpp;h=970f0ffe8edd2d4a35e8f3f0fcaebb1e7e3afea8;hb=refs%2Fchanges%2F03%2F130603%2F2;hp=4d6b5c4486986aefb384ac7bb04df90a310c5f02;hpb=2e993edc5579440c248875eed2c968089e0b279b;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/text/dali/internal/text-abstraction/font-client-plugin-impl.cpp b/text/dali/internal/text-abstraction/font-client-plugin-impl.cpp index 4d6b5c4..970f0ff 100644 --- a/text/dali/internal/text-abstraction/font-client-plugin-impl.cpp +++ b/text/dali/internal/text-abstraction/font-client-plugin-impl.cpp @@ -19,24 +19,81 @@ #include // INTERNAL INCLUDES +#include +#include #include -#include #include +#include +#include +#include +#include // EXTERNAL INCLUDES #include +namespace +{ + +#if defined(DEBUG_ENABLED) +Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT"); +#endif + /** * Conversion from Fractional26.6 to float */ -namespace -{ 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 std::string DEFAULT_FONT_STYLE( "Regular" ); -} +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; + +const bool FONT_FIXED_SIZE_BITMAP( true ); + +// http://www.freedesktop.org/software/fontconfig/fontconfig-user.html + +// NONE -1 --> DEFAULT_FONT_WIDTH (NORMAL) will be used. +// ULTRA_CONDENSED 50 +// EXTRA_CONDENSED 63 +// CONDENSED 75 +// SEMI_CONDENSED 87 +// NORMAL 100 +// SEMI_EXPANDED 113 +// 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 ); + +// NONE -1 --> DEFAULT_FONT_WEIGHT (NORMAL) will be used. +// THIN 0 +// ULTRA_LIGHT, EXTRA_LIGHT 40 +// LIGHT 50 +// DEMI_LIGHT, SEMI_LIGHT 55 +// BOOK 75 +// NORMAL, REGULAR 80 +// MEDIUM 100 +// DEMI_BOLD, SEMI_BOLD 180 +// 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 ); + +// 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 ); + +} // namespace + +using Dali::Vector; namespace Dali { @@ -47,55 +104,146 @@ namespace TextAbstraction namespace Internal { -FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontFamily& fontFamily, - const FontStyle& fontStyle, +/** + * @brief Returns the FontWidth's enum index for the given width value. + * + * @param[in] width The width value. + * + * @return The FontWidth's enum index. + */ +FontWidth::Type IntToWidthType( int width ) +{ + return static_cast( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) ); +} + +/** + * @brief Returns the FontWeight's enum index for the given weight value. + * + * @param[in] weight The weight value. + * + * @return The FontWeight's enum index. + */ +FontWeight::Type IntToWeightType( int weight ) +{ + return static_cast( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) ); +} + +/** + * @brief Returns the FontSlant's enum index for the given slant value. + * + * @param[in] slant The slant value. + * + * @return The FontSlant's enum index. + */ +FontSlant::Type IntToSlantType( int slant ) +{ + return static_cast( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) ); +} + +FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( const FontDescription& font, FontList* list ) +: fontDescription( font ), + fallbackFonts( list ) +{ +} + +FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription, FontDescriptionId index ) -: fontFamily( fontFamily ), - fontStyle( fontStyle ), +: fontDescription( fontDescription ), index( index ) -{} +{ +} FontClient::Plugin::FontIdCacheItem::FontIdCacheItem( FontDescriptionId validatedFontId, - PointSize26Dot6 pointSize, + PointSize26Dot6 requestedPointSize, FontId fontId ) : validatedFontId( validatedFontId ), - pointSize( pointSize ), + requestedPointSize( requestedPointSize ), fontId( fontId ) -{} +{ +} -FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace, - const FontPath& path, - PointSize26Dot6 pointSize, - FaceIndex face, - const FontMetrics& metrics ) +FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace, + const FontPath& path, + PointSize26Dot6 requestedPointSize, + FaceIndex face, + const FontMetrics& metrics ) : mFreeTypeFace( ftFace ), mPath( path ), - mPointSize( pointSize ), + mRequestedPointSize( requestedPointSize ), mFaceIndex( face ), - mMetrics( metrics ) -{} + mMetrics( metrics ), + mFixedWidthPixels( 0.0f ), + mFixedHeightPixels( 0.0f ), + mVectorFontId( 0 ), + mIsFixedSizeBitmap( false ) +{ +} + +FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace, + const FontPath& path, + PointSize26Dot6 requestedPointSize, + FaceIndex face, + const FontMetrics& metrics, + float fixedWidth, + float fixedHeight ) +: mFreeTypeFace( ftFace ), + mPath( path ), + mRequestedPointSize( requestedPointSize ), + mFaceIndex( face ), + mMetrics( metrics ), + mFixedWidthPixels( fixedWidth ), + mFixedHeightPixels( fixedHeight ), + mVectorFontId( 0 ), + mIsFixedSizeBitmap( true ) +{ +} FontClient::Plugin::Plugin( unsigned int horizontalDpi, unsigned int verticalDpi ) : mFreeTypeLibrary( NULL ), mDpiHorizontal( horizontalDpi ), mDpiVertical( verticalDpi ), + mDefaultFontDescription(), mSystemFonts(), mDefaultFonts(), mFontCache(), mValidatedFontCache(), mFontDescriptionCache( 1u ), - mFontIdCache() + mFontIdCache(), + mVectorFontCache( NULL ), + mEllipsisCache(), + mDefaultFontDescriptionCached( false ) { int error = FT_Init_FreeType( &mFreeTypeLibrary ); if( FT_Err_Ok != error ) { DALI_LOG_ERROR( "FreeType Init error: %d\n", error ); } + +#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING + mVectorFontCache = new VectorFontCache( mFreeTypeLibrary ); +#endif } FontClient::Plugin::~Plugin() { + for( std::vector::iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end(); + it != endIt; + ++it ) + { + FallbackCacheItem& item = *it; + + if( item.fallbackFonts ) + { + delete item.fallbackFonts; + item.fallbackFonts = NULL; + } + } + +#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING + delete mVectorFontCache; +#endif + FT_Done_FreeType( mFreeTypeLibrary ); } @@ -106,13 +254,18 @@ void FontClient::Plugin::SetDpi( unsigned int horizontalDpi, mDpiVertical = verticalDpi; } -void FontClient::Plugin::SetDefaultFontFamily( const FontFamily& fontFamilyName, - const FontStyle& fontStyle ) +void FontClient::Plugin::ResetSystemDefaults() +{ + mDefaultFontDescriptionCached = false; +} + +void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList ) { - mDefaultFonts.clear(); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::SetFontList family(%s)\n", fontDescription.family.c_str() ); + + fontList.clear(); - FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontFamilyName, - fontStyle ); + FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); FcResult result = FcResultMatch; @@ -126,7 +279,7 @@ void FontClient::Plugin::SetDefaultFontFamily( const FontFamily& fontFamilyName, if( NULL != fontSet ) { // Reserve some space to avoid reallocations. - mDefaultFonts.reserve( fontSet->nfont ); + fontList.reserve( fontSet->nfont ); for( int i = 0u; i < fontSet->nfont; ++i ) { @@ -137,13 +290,21 @@ void FontClient::Plugin::SetDefaultFontFamily( const FontFamily& fontFamilyName, // Skip fonts with no path if( GetFcString( fontPattern, FC_FILE, path ) ) { - mDefaultFonts.push_back( FontDescription() ); - FontDescription& fontDescription = mDefaultFonts.back(); - - fontDescription.path = path; - - GetFcString( fontPattern, FC_FAMILY, fontDescription.family ); - GetFcString( fontPattern, FC_STYLE, fontDescription.style ); + fontList.push_back( FontDescription() ); + FontDescription& newFontDescription = fontList.back(); + + newFontDescription.path = path; + + 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 ); } } @@ -155,17 +316,54 @@ void FontClient::Plugin::SetDefaultFontFamily( const FontFamily& fontFamilyName, void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts ) { + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultFonts mDefaultFonts(%s)\n", ( mDefaultFonts.empty()?"empty":"valid" ) ); + if( mDefaultFonts.empty() ) { - SetDefaultFontFamily( DEFAULT_FONT_FAMILY_NAME, - DEFAULT_FONT_STYLE ); + 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 ); } defaultFonts = mDefaultFonts; } +void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription ) +{ + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetDefaultPlatformFontDescription\n"); + + if( !mDefaultFontDescriptionCached ) + { + FcInitReinitialize(); // FcInitBringUptoDate did not seem to reload config file as was still getting old default font. + + FcPattern* matchPattern = FcPatternCreate(); + + if( matchPattern ) + { + FcConfigSubstitute( NULL, matchPattern, FcMatchPattern ); + FcDefaultSubstitute( matchPattern ); + + MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription ); + FcPatternDestroy( matchPattern ); + } + + mDefaultFontDescriptionCached = true; + } + + fontDescription.path = mDefaultFontDescription.path; + fontDescription.family = mDefaultFontDescription.family; + fontDescription.width = mDefaultFontDescription.width; + fontDescription.weight = mDefaultFontDescription.weight; + fontDescription.slant = mDefaultFontDescription.slant; +} + void FontClient::Plugin::GetSystemFonts( FontList& systemFonts ) { + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetSystemFonts\n"); + if( mSystemFonts.empty() ) { InitSystemFonts(); @@ -201,7 +399,7 @@ PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id ) if( id > 0u && index < mFontCache.size() ) { - return ( *( mFontCache.begin() + index ) ).mPointSize; + return ( *( mFontCache.begin() + index ) ).mRequestedPointSize; } else { @@ -211,28 +409,25 @@ PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id ) return TextAbstraction::FontClient::DEFAULT_POINT_SIZE; } -FontId FontClient::Plugin::FindDefaultFont( Character charcode, - PointSize26Dot6 pointSize ) +FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList, + Character charcode, + PointSize26Dot6 requestedPointSize, + bool preferColor ) { - // Create the list of default fonts if it has not been created. - if( mDefaultFonts.empty() ) - { - SetDefaultFontFamily( DEFAULT_FONT_FAMILY_NAME, - DEFAULT_FONT_STYLE ); - } + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFontForCharacter\n"); - // Traverse the list of default fonts. - // Check for each default font if supports the character. + FontId fontId = 0u; + bool foundColor = false; - for( FontList::const_iterator it = mDefaultFonts.begin(), - endIt = mDefaultFonts.end(); + // Traverse the list of fonts. + // Check for each font if supports the character. + for( FontList::const_iterator it = fontList.begin(), endIt = fontList.end(); it != endIt; ++it ) { const FontDescription& description = *it; - FcPattern* pattern = CreateFontFamilyPattern( description.family, - description.style ); + FcPattern* pattern = CreateFontFamilyPattern( description ); FcResult result = FcResultMatch; FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result ); @@ -242,138 +437,280 @@ FontId FontClient::Plugin::FindDefaultFont( Character charcode, if( FcCharSetHasChar( charSet, charcode ) ) { - return GetFontId( description.family, - description.style, - pointSize, - 0u ); + Vector< PointSize26Dot6 > fixedSizes; + GetFixedSizes( description, + fixedSizes ); + + PointSize26Dot6 actualPointSize = requestedPointSize; + + const Vector< PointSize26Dot6 >::SizeType count = fixedSizes.Count(); + + if( 0 != count ) + { + // If the font is not scalable, pick the largest size <= requestedPointSize + actualPointSize = fixedSizes[0]; + for( unsigned int i=1; i actualPointSize ) + { + actualPointSize = fixedSizes[i]; + } + } + } + + fontId = GetFontId( description, + requestedPointSize, + actualPointSize, + 0u ); + + if( preferColor ) + { + foundColor = IsColorGlyph( fontId, GetGlyphIndex( fontId, charcode ) ); + } + + // Keep going unless we prefer a different (color) font. + if( !preferColor || foundColor ) + { + FcPatternDestroy( match ); + FcPatternDestroy( pattern ); + break; + } } + + FcPatternDestroy( match ); + FcPatternDestroy( pattern ); } - return 0u; + return fontId; +} + +FontId FontClient::Plugin::FindDefaultFont( Character charcode, + PointSize26Dot6 requestedPointSize, + bool preferColor ) +{ + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindDefaultFont DefaultFontsList(%s)\n", (mDefaultFonts.empty()?"empty":"created") ); + + FontId fontId(0); + + // Create the list of default fonts if it has not been created. + 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 ); + + SetFontList( fontDescription, mDefaultFonts ); + } + + // Traverse the list of default fonts. + // Check for each default font if supports the character. + fontId = FindFontForCharacter( mDefaultFonts, charcode, requestedPointSize, preferColor ); + + return fontId; +} + +FontId FontClient::Plugin::FindFallbackFont( Character charcode, + const FontDescription& preferredFontDescription, + PointSize26Dot6 requestedPointSize, + bool preferColor ) +{ + // The font id to be returned. + FontId fontId = 0u; + + FontDescription fontDescription; + + // 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 ); + + // Check first if the font's description has been queried before. + FontList* fontList( NULL ); + + if( !FindFallbackFontList( fontDescription, fontList ) ) + { + fontList = new FontList; + SetFontList( fontDescription, *fontList ); + + // Add the font-list to the cache. + mFallbackCache.push_back( FallbackCacheItem( fontDescription, fontList ) ); + } + + if( fontList ) + { + fontId = FindFontForCharacter( *fontList, charcode, requestedPointSize, preferColor ); + } + + return fontId; } FontId FontClient::Plugin::GetFontId( const FontPath& path, - PointSize26Dot6 pointSize, + PointSize26Dot6 requestedPointSize, + PointSize26Dot6 actualPointSize, FaceIndex faceIndex, bool cacheDescription ) { + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId fontPatch:%s\n", path.c_str() ); + FontId id( 0 ); if( NULL != mFreeTypeLibrary ) { FontId foundId(0); - if( FindFont( path, pointSize, faceIndex, foundId ) ) + if( FindFont( path, requestedPointSize, faceIndex, foundId ) ) { id = foundId; } else { - id = CreateFont( path, pointSize, faceIndex, cacheDescription ); + id = CreateFont( path, requestedPointSize, actualPointSize, faceIndex, cacheDescription ); } } return id; } -FontId FontClient::Plugin::GetFontId( const FontFamily& fontFamily, - const FontStyle& fontStyle, - PointSize26Dot6 pointSize, +FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription, + PointSize26Dot6 requestedPointSize, + PointSize26Dot6 actualPointSize, FaceIndex faceIndex ) { + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId font family(%s)\n", fontDescription.family.c_str() ); + // This method uses three vectors which caches: - // * Pairs of non validated 'fontFamily, fontStyle' and an index to a vector with paths to font file names. + // * 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 pair 'fontFamily, fontStyle' has been validated before. + // 1) 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 pair - // 'fontFamily, fontStyle'. The path is stored in the chache. + // 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 - // fon file names' exists. If exists, it gets the font id. If it doesn't it calls + // 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. // The font id to be returned. FontId fontId = 0u; - // Check first if the pair font family and style have been validated before. + // Check first if the font's description have been validated before. FontDescriptionId validatedFontId = 0u; - if( !FindValidatedFont( fontFamily, - fontStyle, + if( !FindValidatedFont( fontDescription, validatedFontId ) ) { - // Use font config to validate the font family name and font style. - - // Create a font pattern. - FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontFamily, - fontStyle ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::GetFontId Validating Font\n"); - FcResult result = FcResultMatch; - - // match the pattern - FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result ); - - if( match ) - { - // Get the path to the font file name. - FontDescription description; - GetFcString( match, FC_FILE, description.path ); - GetFcString( match, FC_FAMILY, description.family ); - GetFcString( match, FC_STYLE, description.style ); - - // Set the index to the vector of paths to font file names. - validatedFontId = mFontDescriptionCache.size(); - - // Add the path to the cache. - mFontDescriptionCache.push_back( description ); - - // Cache the index and the pair font family name, font style. - FontDescriptionCacheItem item( fontFamily, fontStyle, validatedFontId ); - mValidatedFontCache.push_back( item ); - - // destroyed the matched pattern - FcPatternDestroy( match ); - } - else - { - DALI_LOG_ERROR( "FontClient::Plugin::GetFontId failed for font %s %s\n", fontFamily.c_str(), fontStyle.c_str() ); - } - - // destroy the pattern - FcPatternDestroy( fontFamilyPattern ); + // Use font config to validate the font's description. + ValidateFont( fontDescription, + validatedFontId ); } - // Check if exists a pair 'validatedFontId, pointSize' in the cache. - if( !FindFont( validatedFontId, pointSize, fontId ) ) + // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache. + if( !FindFont( validatedFontId, requestedPointSize, fontId ) ) { // Retrieve the font file name path. const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId ); // Retrieve the font id. Do not cache the description as it has been already cached. fontId = GetFontId( description.path, - pointSize, + requestedPointSize, + actualPointSize, faceIndex, false ); - // Cache the pair 'validatedFontId, pointSize' to improve the following queries. + // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries. mFontIdCache.push_back( FontIdCacheItem( validatedFontId, - pointSize, + requestedPointSize, fontId ) ); } return fontId; } +void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription, + FontDescriptionId& validatedFontId ) +{ + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont Validating Font family(%s) \n", fontDescription.family.c_str() ); + + // Create a font pattern. + FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); + + FontDescription description; + + bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description ); + FcPatternDestroy( fontFamilyPattern ); + + if( matched ) + { + // Set the index to the vector of paths to font file names. + validatedFontId = mFontDescriptionCache.size(); + + // Add the path to the cache. + mFontDescriptionCache.push_back( description ); + + // Cache the index and the matched font's description. + FontDescriptionCacheItem item( description, + validatedFontId ); + + mValidatedFontCache.push_back( item ); + + 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 ); + + mValidatedFontCache.push_back( item ); + } + } + else + { + DALI_LOG_ERROR( "FontClient::Plugin::ValidateFont failed for font %s %d %d %d\n", + fontDescription.family.c_str(), + fontDescription.width, + fontDescription.weight, + fontDescription.slant ); + } + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::ValidateFont validatedFontId(%u) font family(%s)\n", validatedFontId, fontDescription.family.c_str() ); +} + void FontClient::Plugin::GetFontMetrics( FontId fontId, FontMetrics& metrics ) { - if( fontId > 0 && - fontId-1 < mFontCache.size() ) + if( ( fontId > 0 ) && + ( fontId - 1u < mFontCache.size() ) ) { - metrics = mFontCache[fontId-1].mMetrics; + const FontFaceCacheItem& font = mFontCache[fontId-1]; + + metrics = font.mMetrics; + + // 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 / static_cast( font.mFixedHeightPixels ); + + 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 ); + } + } } else { @@ -399,41 +736,95 @@ GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId, bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array, uint32_t size, + GlyphType type, bool horizontal ) { + if( VECTOR_GLYPH == type ) + { + return GetVectorMetrics( array, size, horizontal ); + } + + return GetBitmapMetrics( array, size, horizontal ); +} + +bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array, + uint32_t size, + bool horizontal ) +{ bool success( true ); for( unsigned int i=0; i 0 && fontId-1 < mFontCache.size() ) { - FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace; + const FontFaceCacheItem& font = mFontCache[fontId-1]; - int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_DEFAULT ); + FT_Face ftFace = font.mFreeTypeFace; - if( FT_Err_Ok == error ) +#ifdef FREETYPE_BITMAP_SUPPORT + // Check to see if we should be loading a Fixed Size bitmap? + if ( font.mIsFixedSizeBitmap ) { - array[i].width = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266; - array[i].height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ; - if( horizontal ) + int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR ); + if ( FT_Err_Ok == error ) { - array[i].xBearing = static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266; - array[i].yBearing = static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266; - array[i].advance = static_cast< float >( ftFace->glyph->metrics.horiAdvance ) * FROM_266; + glyph.width = font.mFixedWidthPixels; + glyph.height = font.mFixedHeightPixels; + glyph.advance = font.mFixedWidthPixels; + glyph.xBearing = 0.0f; + glyph.yBearing = font.mFixedHeightPixels; + + // 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; + + if( desiredFixedSize > 0.f ) + { + const float scaleFactor = desiredFixedSize / static_cast( 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 ); + + glyph.scaleFactor = scaleFactor; + } } else { - array[i].xBearing = static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266; - array[i].yBearing = static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266; - array[i].advance = static_cast< float >( ftFace->glyph->metrics.vertAdvance ) * FROM_266; + DALI_LOG_ERROR( "FreeType Bitmap Load_Glyph error %d\n", error ); + success = false; } } else +#endif { - success = false; + int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_DEFAULT ); + + 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; + } + else + { + 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 @@ -445,17 +836,69 @@ bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array, return success; } -BitmapImage FontClient::Plugin::CreateBitmap( FontId fontId, - GlyphIndex glyphIndex ) +bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array, + uint32_t size, + bool horizontal ) { - BitmapImage bitmap; +#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING + bool success( true ); - if( fontId > 0 && - fontId-1 < mFontCache.size() ) + for( unsigned int i=0; i 0 && + fontId-1 < mFontCache.size() ) + { + FontFaceCacheItem& font = mFontCache[fontId-1]; + + if( ! font.mVectorFontId ) + { + font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath ); + } + + 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; + array[i].xBearing *= scale; + array[i].yBearing *= scale; + array[i].advance *= scale; + } + else + { + success = false; + } + } - FT_Error error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT ); + return success; +#else + return false; +#endif +} + +void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data ) +{ + if( ( fontId > 0 ) && + ( fontId - 1u < mFontCache.size() ) ) + { + FT_Face ftFace = mFontCache[fontId - 1u].mFreeTypeFace; + + FT_Error error; + +#ifdef FREETYPE_BITMAP_SUPPORT + // Check to see if this is fixed size bitmap + if ( mFontCache[fontId - 1u].mIsFixedSizeBitmap ) + { + 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; @@ -467,38 +910,131 @@ BitmapImage FontClient::Plugin::CreateBitmap( FontId fontId, if( glyph->format != FT_GLYPH_FORMAT_BITMAP ) { error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 ); + if ( FT_Err_Ok == error ) + { + FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph; + ConvertBitmap( data, bitmapGlyph->bitmap ); + } + else + { + DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error ); + } } else { - DALI_LOG_ERROR( "FT_Glyph_To_Bitmap Failed with error: %d\n", error ); + ConvertBitmap( data, ftFace->glyph->bitmap ); } - } - else - { - DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error ); - } - if( FT_Err_Ok == error ) - { - // Access the underlying bitmap data - FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph; - ConvertBitmap( bitmap, bitmapGlyph->bitmap ); + // Created FT_Glyph object must be released with FT_Done_Glyph + FT_Done_Glyph( glyph ); } - - // Created FT_Glyph object must be released with FT_Done_Glyph - FT_Done_Glyph( glyph ); } else { DALI_LOG_ERROR( "FT_Load_Glyph Failed with error: %d\n", error ); } } +} + +PixelData FontClient::Plugin::CreateBitmap( FontId fontId, + GlyphIndex glyphIndex ) +{ + TextAbstraction::FontClient::GlyphBufferData data; + + CreateBitmap( fontId, glyphIndex, data ); + + 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 ) +{ + blob = NULL; + blobLength = 0; + +#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING + if( fontId > 0 && + fontId-1 < mFontCache.size() ) + { + FontFaceCacheItem& font = mFontCache[fontId-1]; + + if( ! font.mVectorFontId ) + { + font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath ); + } + + mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight ); + } +#endif +} + +const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 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 ) + { + const EllipsisItem& item = *it; + + if( fabsf( item.requestedPointSize - requestedPointSize ) < Math::MACHINE_EPSILON_1000 ) + { + // Use the glyph in the cache. + return item.glyph; + } + } + + // No glyph has been found. Create one. + 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 ); + + // 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 ); + + GetBitmapMetrics( &item.glyph, 1u, true ); + + return item.glyph; +} + +bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex ) +{ + FT_Error error = -1; + +#ifdef FREETYPE_BITMAP_SUPPORT + if( ( fontId > 0 ) && + ( fontId - 1u < mFontCache.size() ) ) + { + const FontFaceCacheItem& item = mFontCache[fontId - 1u]; + FT_Face ftFace = item.mFreeTypeFace; + + // Check to see if this is fixed size bitmap + if( item.mIsFixedSizeBitmap ) + { + error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR ); + } + } +#endif - return bitmap; + return FT_Err_Ok == error; } void FontClient::Plugin::InitSystemFonts() { + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::InitSystemFonts \n"); + FcFontSet* fontSet = GetFcFontSet(); if( fontSet ) @@ -520,8 +1056,18 @@ void FontClient::Plugin::InitSystemFonts() fontDescription.path = path; + int width = 0; + int weight = 0; + int slant = 0; GetFcString( fontPattern, FC_FAMILY, fontDescription.family ); - GetFcString( fontPattern, FC_STYLE, fontDescription.style ); + 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, "FontClient::Plugin::InitSystemFonts font family(%s)\n", fontDescription.family.c_str() ); + } } @@ -529,18 +1075,74 @@ void FontClient::Plugin::InitSystemFonts() } } -FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontFamily& fontFamily, - const FontStyle& fontStyle ) +bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription ) +{ + FcResult result = FcResultMatch; + FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result ); + + bool ret = false; + + if( match ) + { + int width = 0; + int weight = 0; + int slant = 0; + GetFcString( match, FC_FILE, fontDescription.path ); + GetFcString( match, FC_FAMILY, fontDescription.family ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::MatchFontDescriptionToPattern matched:%s \n", fontDescription.family.c_str()); + 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 ); + + // destroyed the matched pattern + FcPatternDestroy( match ); + ret = true; + } + return ret; +} + + +FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) { // 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(); - // add a property to the pattern for the font family - FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast( fontFamily.c_str() ) ); + if( !fontFamilyPattern ) + { + return NULL; + } // add a property to the pattern for the font family - FcPatternAddString( fontFamilyPattern, FC_STYLE, reinterpret_cast( fontStyle.c_str() ) ); + FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast( fontDescription.family.c_str() ) ); + + int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width]; + if( width < 0 ) + { + // Use default. + width = DEFAULT_FONT_WIDTH; + } + + int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight]; + if( weight < 0 ) + { + // Use default. + weight = DEFAULT_FONT_WEIGHT; + } + + int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant]; + 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() ) ); @@ -567,7 +1169,9 @@ _FcFontSet* FontClient::Plugin::GetFcFontSet() const // build an object set from a list of property names FcObjectSetAdd( objectSet, FC_FILE ); FcObjectSetAdd( objectSet, FC_FAMILY ); - FcObjectSetAdd( objectSet, FC_STYLE ); + 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 @@ -605,8 +1209,21 @@ bool FontClient::Plugin::GetFcString( const FcPattern* const pattern, return false; } +bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal ) +{ + const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal ); + + if( FcResultMatch == retVal ) + { + return true; + } + + return false; +} + FontId FontClient::Plugin::CreateFont( const FontPath& path, - PointSize26Dot6 pointSize, + PointSize26Dot6 requestedPointSize, + PointSize26Dot6 actualPointSize, FaceIndex faceIndex, bool cacheDescription ) { @@ -621,37 +1238,89 @@ FontId FontClient::Plugin::CreateFont( const FontPath& path, if( FT_Err_Ok == error ) { - error = FT_Set_Char_Size( ftFace, - 0, - pointSize, - mDpiHorizontal, - mDpiVertical ); + // Check to see if the font contains fixed sizes? + if ( ftFace->num_fixed_sizes && ftFace->available_sizes ) + { + // Ensure this size is available + for ( int i = 0; i < ftFace->num_fixed_sizes; ++i ) + { + if ( static_cast( actualPointSize ) == ftFace->available_sizes[ i ].size ) + { + // Tell Freetype to use this size + error = FT_Select_Size( ftFace, i ); + if ( FT_Err_Ok != error ) + { + DALI_LOG_ERROR( "FreeType Select_Size error: %d\n", error ); + } + else + { + float fixedWidth = static_cast< float >( ftFace->available_sizes[ i ].width ); + float fixedHeight = static_cast< float >( ftFace->available_sizes[ i ].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 ) ); + id = mFontCache.size(); + + if( cacheDescription ) + { + CacheFontPath( ftFace, id, requestedPointSize, path ); + } + + return id; + } + } + } - if( FT_Err_Ok == error ) + // Can't find this size + std::stringstream sizes; + for ( int i = 0; i < ftFace->num_fixed_sizes; ++i ) + { + if ( i ) + { + sizes << ", "; + } + sizes << ftFace->available_sizes[ i ].size; + } + DALI_LOG_ERROR( "FreeType Font: %s, does not contain Bitmaps of size: %d. Available sizes are: %s\n", + path.c_str(), actualPointSize, sizes.str().c_str() ); + } + else { - id = mFontCache.size() + 1; + error = FT_Set_Char_Size( ftFace, + 0, + actualPointSize, + mDpiHorizontal, + mDpiVertical ); - FT_Size_Metrics& ftMetrics = ftFace->size->metrics; + if( FT_Err_Ok == error ) + { - FontMetrics metrics( static_cast< float >( ftMetrics.ascender ) * FROM_266, - static_cast< float >( ftMetrics.descender ) * FROM_266, - static_cast< float >( ftMetrics.height ) * FROM_266 ); + FT_Size_Metrics& ftMetrics = ftFace->size->metrics; - mFontCache.push_back( CacheItem( ftFace, path, pointSize, faceIndex, 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 ); - if( cacheDescription ) - { - FontDescription description; - description.path = path; - description.family = FontFamily( ftFace->family_name ); - description.style = FontStyle( ftFace->style_name ); + mFontCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics ) ); + id = mFontCache.size(); - mFontDescriptionCache.push_back( description ); + if( cacheDescription ) + { + CacheFontPath( ftFace, id, requestedPointSize, path ); + } + } + else + { + DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", error, actualPointSize ); } - } - else - { - DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", pointSize ); } } else @@ -662,39 +1331,85 @@ FontId FontClient::Plugin::CreateFont( const FontPath& path, return id; } -void FontClient::Plugin::ConvertBitmap( BitmapImage& destBitmap, - FT_Bitmap srcBitmap ) +void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap ) { if( srcBitmap.width*srcBitmap.rows > 0 ) { - // TODO - Support all pixel modes - if( FT_PIXEL_MODE_GRAY == srcBitmap.pixel_mode ) + switch( srcBitmap.pixel_mode ) { - if( srcBitmap.pitch == static_cast< int >( srcBitmap.width ) ) + case FT_PIXEL_MODE_GRAY: { - destBitmap = BitmapImage::New( srcBitmap.width, srcBitmap.rows, Pixel::L8 ); + if( srcBitmap.pitch == static_cast( srcBitmap.width ) ) + { + const unsigned int bufferSize = srcBitmap.width * srcBitmap.rows; + data.buffer = new unsigned char[bufferSize]; + data.width = srcBitmap.width; + data.height = srcBitmap.rows; + data.format = Pixel::L8; + memcpy( data.buffer, srcBitmap.buffer, bufferSize ); + } + break; + } - PixelBuffer* destBuffer = destBitmap.GetBuffer(); - memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows ); +#ifdef FREETYPE_BITMAP_SUPPORT + case FT_PIXEL_MODE_BGRA: + { + if( srcBitmap.pitch == static_cast( srcBitmap.width << 2u ) ) + { + // Set the input dimensions. + const ImageDimensions inputDimensions( srcBitmap.width, srcBitmap.rows ); + + // 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 ); + } + data.format = Pixel::BGRA8888; + } + break; + } +#endif + default: + { + DALI_LOG_ERROR( "FontClient Unable to create Bitmap of this PixelType\n" ); + break; } } } } bool FontClient::Plugin::FindFont( const FontPath& path, - PointSize26Dot6 pointSize, + PointSize26Dot6 requestedPointSize, FaceIndex faceIndex, FontId& fontId ) const { fontId = 0u; - for( std::vector::const_iterator it = mFontCache.begin(), + for( std::vector::const_iterator it = mFontCache.begin(), endIt = mFontCache.end(); it != endIt; ++it, ++fontId ) { - const CacheItem& cacheItem = *it; + const FontFaceCacheItem& cacheItem = *it; - if( cacheItem.mPointSize == pointSize && + if( cacheItem.mRequestedPointSize == requestedPointSize && cacheItem.mFaceIndex == faceIndex && cacheItem.mPath == path ) { @@ -706,10 +1421,11 @@ bool FontClient::Plugin::FindFont( const FontPath& path, return false; } -bool FontClient::Plugin::FindValidatedFont( const FontFamily& fontFamily, - const FontStyle& fontStyle, +bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription, FontDescriptionId& validatedFontId ) { + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont fontDescription family(%s)\n", fontDescription.family.c_str() ); + validatedFontId = 0u; for( std::vector::const_iterator it = mValidatedFontCache.begin(), @@ -719,20 +1435,59 @@ bool FontClient::Plugin::FindValidatedFont( const FontFamily& fontFamily, { const FontDescriptionCacheItem& item = *it; - if( ( fontFamily == item.fontFamily ) && - ( fontStyle == item.fontStyle ) ) + 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::Verbose, "FontClient::Plugin::FindValidatedFont validated font family(%s) font id (%u) \n", fontDescription.family.c_str(), validatedFontId ); + return true; } } + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindValidatedFont NOT VALIDATED return false\n" ); + + return false; +} + +bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription, + FontList*& fontList ) +{ + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList fontDescription family(%s)\n", fontDescription.family.c_str() ); + + fontList = NULL; + + for( std::vector::const_iterator it = mFallbackCache.begin(), endIt = mFallbackCache.end(); + it != endIt; + ++it ) + { + 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 ) ) + { + fontList = item.fallbackFonts; + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList font family(%s) font-list (%p) \n", fontDescription.family.c_str(), fontList ); + + return true; + } + } + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "FontClient::Plugin::FindFallbackFontList NOT FOUND return false\n" ); + return false; } bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId, - PointSize26Dot6 pointSize, + PointSize26Dot6 requestedPointSize, FontId& fontId ) { fontId = 0u; @@ -745,7 +1500,7 @@ bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId, const FontIdCacheItem& item = *it; if( ( validatedFontId == item.validatedFontId ) && - ( pointSize == item.pointSize ) ) + ( requestedPointSize == item.requestedPointSize ) ) { fontId = item.fontId; return true; @@ -755,6 +1510,148 @@ bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId, return false; } +bool FontClient::Plugin::IsScalable( const FontPath& path ) +{ + FT_Face ftFace; + int error = FT_New_Face( mFreeTypeLibrary, + path.c_str(), + 0, + &ftFace ); + if( FT_Err_Ok != error ) + { + DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() ); + } + return ( ftFace->num_fixed_sizes == 0 ); +} + +bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription ) +{ + // Create a font pattern. + FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); + + FcResult result = FcResultMatch; + + // match the pattern + FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result ); + bool isScalable = true; + + if( match ) + { + // Get the path to the font file name. + FontPath path; + GetFcString( match, FC_FILE, path ); + isScalable = IsScalable( path ); + } + else + { + DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n", + fontDescription.family.c_str(), + fontDescription.width, + fontDescription.weight, + fontDescription.slant ); + } + FcPatternDestroy( fontFamilyPattern ); + FcPatternDestroy( match ); + return isScalable; +} + +void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& 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 ) + { + DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() ); + } + + // Fetch the number of fixed sizes available + if ( ftFace->num_fixed_sizes && ftFace->available_sizes ) + { + for ( int i = 0; i < ftFace->num_fixed_sizes; ++i ) + { + sizes.PushBack( ftFace->available_sizes[ i ].size ); + } + } +} + +void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription, + Vector< PointSize26Dot6 >& sizes ) +{ + // Create a font pattern. + FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription ); + + FcResult result = FcResultMatch; + + // match the pattern + FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result ); + + if( match ) + { + // Get the path to the font file name. + FontPath path; + GetFcString( match, FC_FILE, path ); + GetFixedSizes( path, sizes ); + } + else + { + DALI_LOG_ERROR( "FreeType Cannot check font: %s %d %d %d\n", + fontDescription.family.c_str(), + fontDescription.width, + fontDescription.weight, + fontDescription.slant ); + } + FcPatternDestroy( match ); + FcPatternDestroy( fontFamilyPattern ); +} + +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.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( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) + { + description.slant = FontSlant::ITALIC; + } + if( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) + { + description.weight = FontWeight::BOLD; + } + + FontDescriptionId validatedFontId = 0u; + if( !FindValidatedFont( description, + validatedFontId ) ) + { + // Set the index to the vector of paths to font file names. + validatedFontId = mFontDescriptionCache.size(); + + // Add the path to the cache. + mFontDescriptionCache.push_back( description ); + + // Cache the index and the font's description. + FontDescriptionCacheItem item( description, + validatedFontId ); + + mValidatedFontCache.push_back( item ); + + // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries. + mFontIdCache.push_back( FontIdCacheItem( validatedFontId, + requestedPointSize, + id ) ); + } +} + } // namespace Internal } // namespace TextAbstraction