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