[Tizen] Move glyph cache manager into font-client-plugin-cache-handler
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-face-cache-item.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // EXTERNAL HEADERS
18 #include <dali/integration-api/debug.h>
19
20 // INTERNAL HEADERS
21 #include <dali/devel-api/adaptor-framework/environment-variable.h>
22 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
23 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
24
25 #if defined(DEBUG_ENABLED)
26 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
27 #endif
28
29 namespace Dali::TextAbstraction::Internal
30 {
31 namespace
32 {
33 const float FROM_266        = 1.0f / 64.0f;
34 const float POINTS_PER_INCH = 72.f;
35
36 /**
37  * @brief Maximum rate of bitmap glyph resize.
38  * If scale factor is bigger than this value, we will not cache resized glyph.
39  * Else, resize bitmap glyph itself and cache it.
40  */
41 constexpr float MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE = 1.5f;
42
43 /**
44  * @brief Behavior about cache the rendered glyph cache.
45  */
46 constexpr bool DEFAULT_ENABLE_CACHE_RENDERED_GLYPH = true;
47 constexpr auto ENABLE_CACHE_RENDERED_GLYPH_ENV     = "DALI_ENABLE_CACHE_RENDERED_GLYPH";
48
49 /**
50  * @brief Get whether we allow to cache rendered glyph from environment.
51  * If not settuped, default as true.
52  * @note This value fixed when we call it first time.
53  * @return True if we allow to cache rendered glyph.
54  */
55 inline bool EnableCacheRenderedGlyph()
56 {
57   using Dali::EnvironmentVariable::GetEnvironmentVariable;
58   static auto numberString = GetEnvironmentVariable(ENABLE_CACHE_RENDERED_GLYPH_ENV);
59   static auto number       = numberString ? (std::strtoul(numberString, nullptr, 10) ? true : false) : DEFAULT_ENABLE_CACHE_RENDERED_GLYPH;
60   return number;
61 }
62
63 /**
64  * @brief Policy about compress the cached rendered glyph.
65  * It will be used only if CacheRenderedGlyph is enabled
66  */
67 constexpr auto DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY =
68 #if !(defined(DALI_PROFILE_UBUNTU) || defined(ANDROID) || defined(WIN32) || defined(__APPLE__))
69   GlyphCacheManager::CompressionPolicyType::MEMORY; // If tizen target
70 #else
71   GlyphCacheManager::CompressionPolicyType::SPEED; // If not tizen target
72 #endif
73 constexpr auto RENDERED_GLYPH_COMPRESS_POLICY_ENV = "DALI_RENDERED_GLYPH_COMPRESS_POLICY";
74
75 /**
76  * @brief Get whether we allow to cache rendered glyph from environment.
77  * If not settuped, default value used, as defined above.
78  * @note This value fixed when we call it first time.
79  * @return SPEED if value start with 's' or 'S'. MEMORY if value start with 'm' or 'M'. otherwise, use default
80  */
81 inline GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy()
82 {
83   using Dali::EnvironmentVariable::GetEnvironmentVariable;
84   static auto policyString = GetEnvironmentVariable(RENDERED_GLYPH_COMPRESS_POLICY_ENV);
85
86   static auto policy = policyString ? policyString[0] == 's' || policyString[0] == 'S' ? GlyphCacheManager::CompressionPolicyType::SPEED
87                                                                                        : policyString[0] == 'm' || policyString[0] == 'M' ? GlyphCacheManager::CompressionPolicyType::MEMORY
88                                                                                                                                           : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY
89                                     : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY;
90   return policy;
91 }
92 } // namespace
93
94 FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
95                                      FT_Face            ftFace,
96                                      GlyphCacheManager* glyphCacheManager,
97                                      const FontPath&    path,
98                                      PointSize26Dot6    requestedPointSize,
99                                      FaceIndex          face,
100                                      const FontMetrics& metrics)
101 : mFreeTypeLibrary(freeTypeLibrary),
102   mFreeTypeFace(ftFace),
103   mGlyphCacheManager(glyphCacheManager),
104   mHarfBuzzProxyFont(),
105   mPath(path),
106   mRequestedPointSize(requestedPointSize),
107   mFaceIndex(face),
108   mMetrics(metrics),
109   mCharacterSet(nullptr),
110   mFixedSizeIndex(0),
111   mFixedWidthPixels(0.f),
112   mFixedHeightPixels(0.f),
113   mVectorFontId(0u),
114   mFontId(0u),
115   mIsFixedSizeBitmap(false),
116   mHasColorTables(false)
117 {
118 }
119
120 FontFaceCacheItem::FontFaceCacheItem(const FT_Library&  freeTypeLibrary,
121                                      FT_Face            ftFace,
122                                      GlyphCacheManager* glyphCacheManager,
123                                      const FontPath&    path,
124                                      PointSize26Dot6    requestedPointSize,
125                                      FaceIndex          face,
126                                      const FontMetrics& metrics,
127                                      int                fixedSizeIndex,
128                                      float              fixedWidth,
129                                      float              fixedHeight,
130                                      bool               hasColorTables)
131 : mFreeTypeLibrary(freeTypeLibrary),
132   mFreeTypeFace(ftFace),
133   mGlyphCacheManager(glyphCacheManager),
134   mHarfBuzzProxyFont(),
135   mPath(path),
136   mRequestedPointSize(requestedPointSize),
137   mFaceIndex(face),
138   mMetrics(metrics),
139   mCharacterSet(nullptr),
140   mFixedSizeIndex(fixedSizeIndex),
141   mFixedWidthPixels(fixedWidth),
142   mFixedHeightPixels(fixedHeight),
143   mVectorFontId(0u),
144   mFontId(0u),
145   mIsFixedSizeBitmap(true),
146   mHasColorTables(hasColorTables)
147 {
148 }
149
150 // Move constructor. font client plugin container may call this.
151 // Note that we make nullptr of some reference sensitive values here.
152 FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs)
153 : mFreeTypeLibrary(rhs.mFreeTypeLibrary)
154 {
155   mFreeTypeFace       = rhs.mFreeTypeFace;
156   mGlyphCacheManager  = rhs.mGlyphCacheManager;
157   mHarfBuzzProxyFont  = std::move(rhs.mHarfBuzzProxyFont);
158   mPath               = std::move(rhs.mPath);
159   mRequestedPointSize = rhs.mRequestedPointSize;
160   mFaceIndex          = rhs.mFaceIndex;
161   mMetrics            = rhs.mMetrics;
162   mCharacterSet       = rhs.mCharacterSet;
163   mFixedSizeIndex     = rhs.mFixedSizeIndex;
164   mFixedWidthPixels   = rhs.mFixedWidthPixels;
165   mFixedHeightPixels  = rhs.mFixedWidthPixels;
166   mVectorFontId       = rhs.mVectorFontId;
167   mFontId             = rhs.mFontId;
168   mIsFixedSizeBitmap  = rhs.mIsFixedSizeBitmap;
169   mHasColorTables     = rhs.mHasColorTables;
170
171   rhs.mFreeTypeFace      = nullptr;
172   rhs.mGlyphCacheManager = nullptr;
173 }
174
175 FontFaceCacheItem::~FontFaceCacheItem()
176 {
177   // delete cached glyph informations before free face.
178   if(mGlyphCacheManager)
179   {
180     mGlyphCacheManager->RemoveGlyphFromFace(mFreeTypeFace);
181   }
182
183   // delete harfbuzz proxy font before free face.
184   if(mHarfBuzzProxyFont)
185   {
186     mHarfBuzzProxyFont.reset();
187   }
188
189   // Free face.
190   if(mFreeTypeFace)
191   {
192     FT_Done_Face(mFreeTypeFace);
193   }
194 }
195
196 void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
197 {
198   metrics = mMetrics;
199
200   // Adjust the metrics if the fixed-size font should be down-scaled
201   if(mIsFixedSizeBitmap)
202   {
203     const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
204
205     if(desiredFixedSize > 0.f)
206     {
207       const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
208
209       metrics.ascender           = round(metrics.ascender * scaleFactor);
210       metrics.descender          = round(metrics.descender * scaleFactor);
211       metrics.height             = round(metrics.height * scaleFactor);
212       metrics.underlinePosition  = metrics.underlinePosition * scaleFactor;
213       metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
214     }
215   }
216 }
217
218 bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVertical, bool horizontal) const
219 {
220   bool success(true);
221
222   GlyphCacheManager::GlyphCacheData glyphData;
223   FT_Error                          error;
224
225 #ifdef FREETYPE_BITMAP_SUPPORT
226   // Check to see if we should be loading a Fixed Size bitmap?
227   if(mIsFixedSizeBitmap)
228   {
229     FT_Select_Size(mFreeTypeFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
230     mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphData, error);
231
232     if(FT_Err_Ok == error)
233     {
234       glyphInfo.width    = mFixedWidthPixels;
235       glyphInfo.height   = mFixedHeightPixels;
236       glyphInfo.advance  = mFixedWidthPixels;
237       glyphInfo.xBearing = 0.0f;
238
239       const auto& metrics = glyphData.mGlyphMetrics;
240
241       if(horizontal)
242       {
243         glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
244       }
245       else
246       {
247         glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
248       }
249
250       // Adjust the metrics if the fixed-size font should be down-scaled
251       const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
252
253       if(desiredFixedSize > 0.f)
254       {
255         const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
256         glyphInfo.width         = round(glyphInfo.width * scaleFactor);
257         glyphInfo.height        = round(glyphInfo.height * scaleFactor);
258         glyphInfo.advance       = round(glyphInfo.advance * scaleFactor);
259         glyphInfo.xBearing      = round(glyphInfo.xBearing * scaleFactor);
260         glyphInfo.yBearing      = round(glyphInfo.yBearing * scaleFactor);
261
262         glyphInfo.scaleFactor = scaleFactor;
263
264         if(scaleFactor < MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE)
265         {
266           // Resize bitmap glyph and cache it due to the performance issue.
267           // If scaleFactor is too big, cached bitmap may hold too big memory.
268           // So, we only hold small enough case.
269
270           // TODO : If dpiVertical value changed, this resize feature will be break down.
271           // Otherwise, this glyph will be resized only one times.
272           mGlyphCacheManager->ResizeBitmapGlyph(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
273         }
274       }
275     }
276     else
277     {
278       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
279       success = false;
280     }
281   }
282   else
283 #endif
284   {
285     // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
286     // i.e. with the SNum-3R font.
287     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
288     mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error);
289
290     // Keep the width of the glyph before doing the software emboldening.
291     // It will be used to calculate a scale factor to be applied to the
292     // advance as Harfbuzz doesn't apply any SW emboldening to calculate
293     // the advance of the glyph.
294
295     if(FT_Err_Ok == error)
296     {
297       const auto& metrics = glyphData.mGlyphMetrics;
298
299       glyphInfo.width  = static_cast<float>(metrics.width) * FROM_266;
300       glyphInfo.height = static_cast<float>(metrics.height) * FROM_266;
301       if(horizontal)
302       {
303         glyphInfo.xBearing += static_cast<float>(metrics.horiBearingX) * FROM_266;
304         glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
305       }
306       else
307       {
308         glyphInfo.xBearing += static_cast<float>(metrics.vertBearingX) * FROM_266;
309         glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
310       }
311
312       const bool isEmboldeningRequired = glyphInfo.isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
313       if(isEmboldeningRequired)
314       {
315         // Get dummy glyph data without embolden.
316         GlyphCacheManager::GlyphCacheData dummyData;
317         if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error))
318         {
319           // If the glyph is emboldened by software, the advance is multiplied by a
320           // scale factor to make it slightly bigger.
321           const float width = static_cast<float>(dummyData.mGlyphMetrics.width) * FROM_266;
322           if(!EqualsZero(width))
323           {
324             glyphInfo.advance *= (glyphInfo.width / width);
325           }
326         }
327       }
328
329       // Use the bounding box of the bitmap to correct the metrics.
330       // For some fonts i.e the SNum-3R the metrics need to be corrected,
331       // otherwise the glyphs 'dance' up and down depending on the
332       // font's point size.
333       FT_Glyph glyph = glyphData.mGlyph;
334
335       FT_BBox bbox;
336       FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
337
338       const float descender = glyphInfo.height - glyphInfo.yBearing;
339       glyphInfo.height      = (bbox.yMax - bbox.yMin) * FROM_266;
340       glyphInfo.yBearing    = glyphInfo.height - round(descender);
341     }
342     else
343     {
344       success = false;
345     }
346   }
347   return success;
348 }
349
350 /**
351  * @brief Create a bitmap representation of a glyph from a face font
352  *
353  * @param[in]  glyphIndex        The index of a glyph within the specified font.
354  * @param[out] data              The bitmap data.
355  * @param[in]  outlineWidth      The width of the glyph outline in pixels.
356  * @param[in]  isItalicRequired  Whether the glyph requires italic style.
357  * @param[in]  isBoldRequired    Whether the glyph requires bold style.
358  */
359 void FontFaceCacheItem::CreateBitmap(
360   GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
361 {
362   GlyphCacheManager::GlyphCacheData glyphData;
363   FT_Error                          error;
364   FT_Int32                          loadFlag;
365   // For the software italics.
366   bool isShearRequired = false;
367
368 #ifdef FREETYPE_BITMAP_SUPPORT
369   // Check to see if this is fixed size bitmap
370   if(mIsFixedSizeBitmap)
371   {
372     loadFlag = FT_LOAD_COLOR;
373   }
374   else
375 #endif
376   {
377     // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
378     // i.e. with the SNum-3R font.
379     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
380     loadFlag = FT_LOAD_NO_AUTOHINT;
381   }
382   mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, glyphData, error);
383
384   if(FT_Err_Ok == error)
385   {
386     if(isItalicRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_ITALIC))
387     {
388       // Will do the software italic.
389       isShearRequired = true;
390     }
391
392     if(!glyphData.mIsBitmap)
393     {
394       // Convert to bitmap if necessary
395       FT_Glyph glyph = glyphData.mGlyph;
396
397       DALI_ASSERT_ALWAYS(glyph->format != FT_GLYPH_FORMAT_BITMAP && "Something wrong with cashing. Some bitmap glyph cached failed.");
398
399       int  offsetX = 0, offsetY = 0;
400       bool isOutlineGlyph       = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
401       bool isStrokeGlyphSuccess = false;
402
403       // Create a bitmap for the outline
404       if(isOutlineGlyph)
405       {
406         // Retrieve the horizontal and vertical distance from the current pen position to the
407         // left and top border of the glyph bitmap for a normal glyph before applying the outline.
408         if(FT_Err_Ok == error)
409         {
410           // Copy new glyph, and keep original cached glyph.
411           error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0);
412           if(FT_Err_Ok == error)
413           {
414             FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
415
416             offsetX = bitmapGlyph->left;
417             offsetY = bitmapGlyph->top;
418
419             // Copied FT_Glyph object must be released with FT_Done_Glyph
420             FT_Done_Glyph(glyph);
421           }
422
423           // Replace as original glyph
424           glyph = glyphData.mGlyph;
425         }
426
427         // Now apply the outline
428
429         // Set up a stroker
430         FT_Stroker stroker;
431         error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
432
433         if(FT_Err_Ok == error)
434         {
435           // Copy glyph pointer for release memory.
436           FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
437           error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 0);
438
439           if(FT_Err_Ok == error)
440           {
441             FT_Stroker_Done(stroker);
442             isStrokeGlyphSuccess = true;
443           }
444           else
445           {
446             DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
447           }
448         }
449         else
450         {
451           DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
452         }
453       }
454
455       const bool ableUseCachedRenderedGlyph = EnableCacheRenderedGlyph() && !isOutlineGlyph && !isShearRequired;
456
457       // If we cache rendered glyph, and if we can use it, use cached thing first.
458       if(ableUseCachedRenderedGlyph && glyphData.mRenderedBuffer)
459       {
460         data.buffer          = glyphData.mRenderedBuffer->buffer;
461         data.width           = glyphData.mRenderedBuffer->width;
462         data.height          = glyphData.mRenderedBuffer->height;
463         data.format          = glyphData.mRenderedBuffer->format;
464         data.compressionType = glyphData.mRenderedBuffer->compressionType;
465         data.isBufferOwned   = false;
466       }
467       else
468       {
469         // Copy new glyph, and keep original cached glyph.
470         // If we already copy new glyph by stroke, just re-use that.
471         error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, isStrokeGlyphSuccess);
472         if(FT_Err_Ok == error)
473         {
474           FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
475
476           if(isOutlineGlyph)
477           {
478             // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
479             data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
480             data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
481           }
482
483           // If we can cache this bitmapGlyph, store it.
484           // Note : We will call this API once per each glyph.
485           if(ableUseCachedRenderedGlyph)
486           {
487             mGlyphCacheManager->CacheRenderedGlyphBuffer(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
488
489             GlyphCacheManager::GlyphCacheData dummyData;
490             mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, dummyData, error);
491
492             if(DALI_LIKELY(FT_Err_Ok == error && dummyData.mRenderedBuffer))
493             {
494               data.buffer          = dummyData.mRenderedBuffer->buffer;
495               data.width           = dummyData.mRenderedBuffer->width;
496               data.height          = dummyData.mRenderedBuffer->height;
497               data.format          = dummyData.mRenderedBuffer->format;
498               data.compressionType = dummyData.mRenderedBuffer->compressionType;
499               data.isBufferOwned   = false;
500             }
501             else
502             {
503               // Something problem during cache or get rendered glyph buffer.
504               // Move bitmap buffer into data.buffer
505               ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
506             }
507           }
508           else
509           {
510             // Move bitmap buffer into data.buffer
511             ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
512           }
513
514           // Copied FT_Glyph object must be released with FT_Done_Glyph
515           FT_Done_Glyph(glyph);
516         }
517         else
518         {
519           DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
520         }
521       }
522     }
523     else
524     {
525       ConvertBitmap(data, *glyphData.mBitmap, isShearRequired);
526     }
527
528     data.isColorEmoji = mIsFixedSizeBitmap;
529   }
530   else
531   {
532     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
533   }
534 }
535
536 bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
537 {
538   FT_Error error = -1;
539
540 #ifdef FREETYPE_BITMAP_SUPPORT
541   // Check to see if this is fixed size bitmap
542   if(mHasColorTables)
543   {
544     GlyphCacheManager::GlyphCacheData dummyData;
545     mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR, false, dummyData, error);
546   }
547 #endif
548   return FT_Err_Ok == error;
549 }
550
551 /**
552  * Check if the character is supported by this font
553  * @param[in] character The character to test
554  */
555 bool FontFaceCacheItem::IsCharacterSupported(Character character)
556 {
557   if(nullptr == mCharacterSet)
558   {
559     // Create again the character set.
560     // It can be null if the ResetSystemDefaults() method has been called.
561
562     FontDescription description;
563     description.path   = mPath;
564     description.family = std::move(FontFamily(mFreeTypeFace->family_name));
565     description.weight = FontWeight::NONE;
566     description.width  = FontWidth::NONE;
567     description.slant  = FontSlant::NONE;
568
569     // Note FreeType doesn't give too much info to build a proper font style.
570     if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
571     {
572       description.slant = FontSlant::ITALIC;
573     }
574     if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
575     {
576       description.weight = FontWeight::BOLD;
577     }
578
579     mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
580   }
581
582   return FcCharSetHasChar(mCharacterSet, character);
583 }
584
585 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character) const
586 {
587   return FT_Get_Char_Index(mFreeTypeFace, character);
588 }
589
590 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character, Character variantSelector) const
591 {
592   return FT_Face_GetCharVariantIndex(mFreeTypeFace, character, variantSelector);
593 }
594
595 HarfBuzzFontHandle FontFaceCacheItem::GetHarfBuzzFont(const uint32_t& horizontalDpi, const uint32_t& verticalDpi)
596 {
597   // Create new harfbuzz font only first time or DPI changed.
598   if(DALI_UNLIKELY(!mHarfBuzzProxyFont || mHarfBuzzProxyFont->mHorizontalDpi != horizontalDpi || mHarfBuzzProxyFont->mVerticalDpi != verticalDpi))
599   {
600     mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager));
601   }
602   return mHarfBuzzProxyFont->GetHarfBuzzFont();
603 }
604
605 } // namespace Dali::TextAbstraction::Internal