[Tizen] Add trace log to check text performance
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-client-plugin-cache-handler.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
18 // CLASS HEADER
19 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-cache-handler.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/trace.h>
24 #include <fontconfig/fontconfig.h>
25
26 // INTERNAL INCLUDES
27 #include <dali/devel-api/adaptor-framework/environment-variable.h>
28 #include <dali/devel-api/adaptor-framework/image-loading.h>
29 #include <dali/internal/text/text-abstraction/font-client-impl.h>
30 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
31 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
32
33 #if defined(DEBUG_ENABLED)
34 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
35
36 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)                                                                            \
37   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix "  description; family : [%s]\n", fontDescription.family.c_str()); \
38   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose,                                                                            \
39                 "                 path : [%s]\n"                                                                                 \
40                 "                width : [%s]\n"                                                                                 \
41                 "               weight : [%s]\n"                                                                                 \
42                 "                slant : [%s]\n\n",                                                                              \
43                 fontDescription.path.c_str(),                                                                                    \
44                 FontWidth::Name[fontDescription.width],                                                                          \
45                 FontWeight::Name[fontDescription.weight],                                                                        \
46                 FontSlant::Name[fontDescription.slant])
47
48 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
49   DALI_LOG_INFO(gFontClientLogFilter, Debug::General,               \
50                 "           character : %p\n"                       \
51                 "  requestedPointSize : %d\n"                       \
52                 "         preferColor : %s\n",                      \
53                 charcode,                                           \
54                 requestedPointSize,                                 \
55                 (preferColor ? "true" : "false"))
56
57 #else
58
59 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
60 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
61
62 #endif
63
64 namespace
65 {
66
67 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_FONT_PERFORMANCE_MARKER, false);
68
69 /**
70  * @brief Maximum size of glyph cache per each font face.
71  */
72 constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX         = 128;
73 constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
74
75 constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
76
77 /**
78  * @brief Get maximum size of glyph cache size from environment.
79  * If not settuped, default as 128.
80  * @note This value fixed when we call it first time.
81  * @return The max size of glyph cache.
82  */
83 inline size_t GetMaxNumberOfGlyphCache()
84 {
85   using Dali::EnvironmentVariable::GetEnvironmentVariable;
86   static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV);
87   static auto number       = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX;
88   return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number;
89 }
90
91 } // namespace
92
93 namespace Dali::TextAbstraction::Internal
94 {
95 namespace
96 {
97 /**
98  * @brief Free the resources allocated by the FcCharSet objects.
99  *
100  * @param[in] characterSets The vector of character sets.
101  */
102 void DestroyCharacterSets(CharacterSetList& characterSets)
103 {
104   for(auto& item : characterSets)
105   {
106     if(item)
107     {
108       FcCharSetDestroy(item);
109     }
110   }
111 }
112
113 /**
114  * @brief Retrieves the fonts present in the platform.
115  *
116  * @note Need to call FcFontSetDestroy to free the allocated resources.
117  *
118  * @return A font fonfig data structure with the platform's fonts.
119  */
120 _FcFontSet* GetFcFontSet()
121 {
122   FcFontSet* fontset = nullptr;
123
124   // create a new pattern.
125   // a pattern holds a set of names, each name refers to a property of the font
126   FcPattern* pattern = FcPatternCreate();
127
128   if(nullptr != pattern)
129   {
130     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
131     FcObjectSet* objectSet = FcObjectSetCreate();
132
133     if(nullptr != objectSet)
134     {
135       // build an object set from a list of property names
136       FcObjectSetAdd(objectSet, FC_FILE);
137       FcObjectSetAdd(objectSet, FC_FAMILY);
138       FcObjectSetAdd(objectSet, FC_WIDTH);
139       FcObjectSetAdd(objectSet, FC_WEIGHT);
140       FcObjectSetAdd(objectSet, FC_SLANT);
141
142       // get a list of fonts
143       // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
144       fontset = FcFontList(nullptr /* the default configuration is checked to be up to date, and used */, pattern, objectSet); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
145
146       // clear up the object set
147       FcObjectSetDestroy(objectSet);
148     }
149
150     // clear up the pattern
151     FcPatternDestroy(pattern);
152   }
153
154   return fontset;
155 }
156
157 /**
158  * @brief Helper for GetDefaultFonts etc.
159  *
160  * @note CharacterSetList is a vector of FcCharSet that are reference counted. It's needed to call FcCharSetDestroy to decrease the reference counter.
161  *
162  * @param[in] fontDescription A font description.
163  * @param[out] fontList A list of the fonts which are a close match for fontDescription.
164  * @param[out] characterSetList A list of character sets which are a close match for fontDescription.
165  */
166 void SetFontList(const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList)
167 {
168   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
169   fontList.clear();
170
171   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
172
173   FcResult result = FcResultMatch;
174
175   // Match the pattern.
176   FcFontSet* fontSet = FcFontSort(nullptr /* use default configure */,
177                                   fontFamilyPattern,
178                                   false /* don't trim */,
179                                   nullptr,
180                                   &result); // FcFontSort creates a font set that needs to be destroyed by calling FcFontSetDestroy.
181
182   if(nullptr != fontSet)
183   {
184     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of fonts found : [%d]\n", fontSet->nfont);
185     // Reserve some space to avoid reallocations.
186     fontList.reserve(fontSet->nfont);
187
188     for(int i = 0u; i < fontSet->nfont; ++i)
189     {
190       FcPattern* fontPattern = fontSet->fonts[i];
191
192       FontPath path;
193
194       // Skip fonts with no path
195       if(GetFcString(fontPattern, FC_FILE, path))
196       {
197         // Retrieve the character set. Need to call FcCharSetDestroy to free the resources.
198         FcCharSet* characterSet = nullptr;
199         FcPatternGetCharSet(fontPattern, FC_CHARSET, 0u, &characterSet);
200
201         // Increase the reference counter of the character set.
202         characterSetList.PushBack(FcCharSetCopy(characterSet));
203
204         fontList.push_back(FontDescription());
205         FontDescription& newFontDescription = fontList.back();
206
207         newFontDescription.path = std::move(path);
208
209         int width  = 0;
210         int weight = 0;
211         int slant  = 0;
212         GetFcString(fontPattern, FC_FAMILY, newFontDescription.family);
213         GetFcInt(fontPattern, FC_WIDTH, width);
214         GetFcInt(fontPattern, FC_WEIGHT, weight);
215         GetFcInt(fontPattern, FC_SLANT, slant);
216         newFontDescription.width  = IntToWidthType(width);
217         newFontDescription.weight = IntToWeightType(weight);
218         newFontDescription.slant  = IntToSlantType(slant);
219
220         FONT_LOG_DESCRIPTION(newFontDescription, "new font");
221       }
222     }
223
224     // Destroys the font set created by FcFontSort.
225     FcFontSetDestroy(fontSet);
226   }
227   else
228   {
229     DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  No fonts found.\n");
230   }
231
232   // Destroys the pattern created by FcPatternCreate in CreateFontFamilyPattern.
233   FcPatternDestroy(fontFamilyPattern);
234 }
235
236 } // namespace
237
238 FontClient::Plugin::CacheHandler::FallbackCacheItem::FallbackCacheItem(FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets)
239 : fontDescription{std::move(font)},
240   fallbackFonts{fallbackFonts},
241   characterSets{characterSets}
242 {
243 }
244
245 FontClient::Plugin::CacheHandler::FontDescriptionCacheItem::FontDescriptionCacheItem(const FontDescription& fontDescription,
246                                                                                      FontDescriptionId      index)
247 : fontDescription{fontDescription},
248   index{index}
249 {
250 }
251
252 FontClient::Plugin::CacheHandler::FontDescriptionCacheItem::FontDescriptionCacheItem(FontDescription&& fontDescription,
253                                                                                      FontDescriptionId index)
254 : fontDescription{std::move(fontDescription)},
255   index{index}
256 {
257 }
258
259 FontClient::Plugin::CacheHandler::FontDescriptionSizeCacheKey::FontDescriptionSizeCacheKey(FontDescriptionId fontDescriptionId,
260                                                                                            PointSize26Dot6   requestedPointSize)
261 : fontDescriptionId(fontDescriptionId),
262   requestedPointSize(requestedPointSize)
263 {
264 }
265
266 // CacheHandler
267 FontClient::Plugin::CacheHandler::CacheHandler()
268 : mDefaultFontDescription(),
269   mSystemFonts(),
270   mDefaultFonts(),
271   mFontIdCache(),
272   mFontFaceCache(),
273   mValidatedFontCache(),
274   mFontDescriptionCache(),
275   mCharacterSetCache(),
276   mFontDescriptionSizeCache(),
277   mEllipsisCache(),
278   mEmbeddedItemCache(),
279   mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())),
280   mLatestFoundFontDescription(),
281   mLatestFoundFontDescriptionId(0u),
282   mLatestFoundCacheKey(0, 0),
283   mLatestFoundCacheIndex(0u),
284   mDefaultFontDescriptionCached(false)
285 {
286 }
287
288 FontClient::Plugin::CacheHandler::~CacheHandler()
289 {
290   ClearCache();
291 }
292
293 void FontClient::Plugin::CacheHandler::ClearCache()
294 {
295   // delete cached glyph informations before clear mFontFaceCache.
296   mGlyphCacheManager->ClearCache();
297
298   mDefaultFontDescription = FontDescription();
299
300   mSystemFonts.clear();
301   mDefaultFonts.clear();
302
303   DestroyCharacterSets(mDefaultFontCharacterSets);
304   mDefaultFontCharacterSets.Clear();
305
306   ClearFallbackCache();
307   mFallbackCache.clear();
308
309   mFontIdCache.clear();
310
311   ClearCharacterSetFromFontFaceCache();
312   mFontFaceCache.clear();
313
314   mValidatedFontCache.clear();
315   mFontDescriptionCache.clear();
316
317   DestroyCharacterSets(mCharacterSetCache);
318   mCharacterSetCache.Clear();
319
320   mFontDescriptionSizeCache.clear();
321   mFontDescriptionSizeCache.rehash(0); // Note : unordered_map.clear() didn't deallocate memory
322
323   mEllipsisCache.clear();
324   mPixelBufferCache.clear();
325   mEmbeddedItemCache.clear();
326   mBitmapFontCache.clear();
327
328   mLatestFoundFontDescription.family.clear();
329   mLatestFoundCacheKey = FontDescriptionSizeCacheKey(0, 0);
330
331   mDefaultFontDescriptionCached = false;
332 }
333
334 void FontClient::Plugin::CacheHandler::ResetSystemDefaults()
335 {
336   mDefaultFontDescriptionCached = false;
337 }
338
339 // Clear cache area
340
341 void FontClient::Plugin::CacheHandler::ClearFallbackCache()
342 {
343   for(auto& item : mFallbackCache)
344   {
345     if(nullptr != item.fallbackFonts)
346     {
347       delete item.fallbackFonts;
348     }
349
350     if(nullptr != item.characterSets)
351     {
352       // Free the resources allocated by the FcCharSet objects in the 'characterSets' vector.
353       DestroyCharacterSets(*item.characterSets);
354       delete item.characterSets;
355     }
356   }
357 }
358
359 void FontClient::Plugin::CacheHandler::ClearCharacterSetFromFontFaceCache()
360 {
361   for(auto& item : mFontFaceCache)
362   {
363     FcCharSetDestroy(item.mCharacterSet);
364     item.mCharacterSet = nullptr;
365   }
366 }
367
368 void FontClient::Plugin::CacheHandler::ClearCharacterSet()
369 {
370   // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
371   DestroyCharacterSets(mDefaultFontCharacterSets);
372   DestroyCharacterSets(mCharacterSetCache);
373   mDefaultFontCharacterSets.Clear();
374   mCharacterSetCache.Clear();
375
376   for(auto& item : mFallbackCache)
377   {
378     // Decrease the reference counter and eventually free the resources allocated by FcCharSet objects.
379     DestroyCharacterSets(*item.characterSets);
380
381     delete item.characterSets;
382     item.characterSets = nullptr;
383   }
384
385   // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
386   ClearCharacterSetFromFontFaceCache();
387 }
388
389 void FontClient::Plugin::CacheHandler::CreateCharacterSet()
390 {
391   for(const auto& description : mDefaultFonts)
392   {
393     mDefaultFontCharacterSets.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
394   }
395
396   for(const auto& description : mFontDescriptionCache)
397   {
398     mCharacterSetCache.PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
399   }
400
401   for(auto& item : mFallbackCache)
402   {
403     if(nullptr != item.fallbackFonts)
404     {
405       if(nullptr == item.characterSets)
406       {
407         item.characterSets = new CharacterSetList;
408       }
409
410       for(const auto& description : *(item.fallbackFonts))
411       {
412         item.characterSets->PushBack(FcCharSetCopy(CreateCharacterSetFromDescription(description)));
413       }
414     }
415   }
416 }
417
418 // System / Default
419
420 void FontClient::Plugin::CacheHandler::InitSystemFonts()
421 {
422   if(mSystemFonts.empty())
423   {
424     FcFontSet* fontSet = GetFcFontSet(); // Creates a FcFontSet that needs to be destroyed by calling FcFontSetDestroy.
425
426     if(fontSet)
427     {
428       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont);
429
430       // Reserve some space to avoid reallocations.
431       mSystemFonts.reserve(fontSet->nfont);
432
433       for(int i = 0u; i < fontSet->nfont; ++i)
434       {
435         FcPattern* fontPattern = fontSet->fonts[i];
436
437         FontPath path;
438
439         // Skip fonts with no path
440         if(GetFcString(fontPattern, FC_FILE, path))
441         {
442           mSystemFonts.push_back(FontDescription());
443           FontDescription& fontDescription = mSystemFonts.back();
444
445           fontDescription.path = std::move(path);
446
447           int width  = 0;
448           int weight = 0;
449           int slant  = 0;
450           GetFcString(fontPattern, FC_FAMILY, fontDescription.family);
451           GetFcInt(fontPattern, FC_WIDTH, width);
452           GetFcInt(fontPattern, FC_WEIGHT, weight);
453           GetFcInt(fontPattern, FC_SLANT, slant);
454           fontDescription.width  = IntToWidthType(width);
455           fontDescription.weight = IntToWeightType(weight);
456           fontDescription.slant  = IntToSlantType(slant);
457
458           FONT_LOG_DESCRIPTION(fontDescription, "system fonts");
459         }
460       }
461
462       // Destroys the font set created.
463       FcFontSetDestroy(fontSet);
464     }
465   }
466 }
467
468 void FontClient::Plugin::CacheHandler::InitDefaultFonts()
469 {
470   if(mDefaultFonts.empty())
471   {
472     FontDescription fontDescription;
473     fontDescription.family = DefaultFontFamily(); // todo This could be set to the Platform font
474     fontDescription.width  = DefaultFontWidth();
475     fontDescription.weight = DefaultFontWeight();
476     fontDescription.slant  = DefaultFontSlant();
477     SetFontList(fontDescription, mDefaultFonts, mDefaultFontCharacterSets);
478   }
479 }
480
481 void FontClient::Plugin::CacheHandler::InitDefaultFontDescription()
482 {
483   if(!mDefaultFontDescriptionCached)
484   {
485     // Clear any font config stored info in the caches.
486     ClearCharacterSet();
487
488     // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
489     FcInitReinitialize();
490
491     FcPattern* matchPattern = FcPatternCreate(); // Creates a pattern that needs to be destroyed by calling FcPatternDestroy.
492
493     if(nullptr != matchPattern)
494     {
495       FcConfigSubstitute(nullptr, matchPattern, FcMatchPattern);
496       FcDefaultSubstitute(matchPattern);
497
498       FcCharSet* characterSet = nullptr;
499       bool       matched      = MatchFontDescriptionToPattern(matchPattern, mDefaultFontDescription, &characterSet);
500
501       // Caching the default font description
502       if(matched)
503       {
504         // Copy default font description info.
505         // Due to the type changed, we need to make some temperal font description.
506         FontDescription tempFontDescription = mDefaultFontDescription;
507
508         // Add the path to the cache.
509         tempFontDescription.type = FontDescription::FACE_FONT;
510         mFontDescriptionCache.push_back(tempFontDescription);
511
512         // Set the index to the vector of paths to font file names.
513         const FontDescriptionId fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
514
515         FONT_LOG_DESCRIPTION(tempFontDescription, "default platform font");
516         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font fontDescriptionId : %d\n", fontDescriptionId);
517
518         // Cache the index and the matched font's description.
519         CacheValidateFont(std::move(tempFontDescription), fontDescriptionId);
520       }
521       else
522       {
523         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  default font validation failed for font [%s]\n", mDefaultFontDescription.family.c_str());
524       }
525
526       // Decrease the reference counter of the character set as it's not stored.
527       // Note. the cached default font description will increase reference counter by
528       // mFontDescriptionCache in CreateCharacterSet(). So we can decrease reference counter here.
529       FcCharSetDestroy(characterSet);
530
531       // Destroys the pattern created.
532       FcPatternDestroy(matchPattern);
533     }
534
535     // Create again the character sets as they are not valid after FcInitReinitialize()
536     CreateCharacterSet();
537
538     mDefaultFontDescriptionCached = true;
539   }
540 }
541
542 // Validate
543
544 bool FontClient::Plugin::CacheHandler::FindValidatedFont(const FontDescription& fontDescription,
545                                                          FontDescriptionId&     fontDescriptionId)
546 {
547   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
548   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %zu\n", mValidatedFontCache.size());
549
550   fontDescriptionId = 0u;
551
552   // Fast cut if inputed family is empty.
553   if(DALI_UNLIKELY(fontDescription.family.empty()))
554   {
555     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description not found / fontDescription.family is empty!\n");
556     return false;
557   }
558
559   // Heuristic optimize code : Compare with latest found item.
560   if((fontDescription.width == mLatestFoundFontDescription.width) &&
561      (fontDescription.weight == mLatestFoundFontDescription.weight) &&
562      (fontDescription.slant == mLatestFoundFontDescription.slant) &&
563      (fontDescription.family == mLatestFoundFontDescription.family))
564   {
565     fontDescriptionId = mLatestFoundFontDescriptionId;
566
567     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description same as latest, id : %d\n", fontDescriptionId);
568     return true;
569   }
570
571   for(const auto& item : mValidatedFontCache)
572   {
573     if((fontDescription.width == item.fontDescription.width) &&
574        (fontDescription.weight == item.fontDescription.weight) &&
575        (fontDescription.slant == item.fontDescription.slant) &&
576        (fontDescription.family == item.fontDescription.family))
577     {
578       fontDescriptionId = item.index;
579
580       mLatestFoundFontDescription   = fontDescription;
581       mLatestFoundFontDescriptionId = fontDescriptionId;
582
583       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description found, id : %d\n", fontDescriptionId);
584       return true;
585     }
586   }
587
588   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  validated font description not found\n");
589   return false;
590 }
591
592 void FontClient::Plugin::CacheHandler::ValidateFont(const FontDescription& fontDescription,
593                                                     FontDescriptionId&     fontDescriptionId)
594 {
595   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
596   FONT_LOG_DESCRIPTION(fontDescription, "");
597
598   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_VALIDATE_FONT");
599
600   // Create a font pattern.
601   FcPattern* fontFamilyPattern = CreateFontFamilyPattern(fontDescription);
602
603   FontDescription description;
604
605   FcCharSet* characterSet = nullptr;
606   bool       matched      = MatchFontDescriptionToPattern(fontFamilyPattern, description, &characterSet);
607   FcPatternDestroy(fontFamilyPattern);
608
609   if(matched && (nullptr != characterSet))
610   {
611     #if defined(TRACE_ENABLED)
612     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
613     {
614       DALI_LOG_DEBUG_INFO("DALI_TEXT_VALIDATE_FONT : FcFontMatch : %s, style : %s, %s, %s\n", fontDescription.family.c_str(), FontWidth::Name[fontDescription.width], FontWeight::Name[fontDescription.weight], FontSlant::Name[fontDescription.slant]);
615     }
616     #endif
617
618     // Add the path to the cache.
619     description.type = FontDescription::FACE_FONT;
620     mFontDescriptionCache.push_back(description);
621
622     // Set the index to the vector of paths to font file names.
623     fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
624
625     FONT_LOG_DESCRIPTION(description, "matched");
626     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fontDescriptionId : %d\n", fontDescriptionId);
627
628     // The reference counter of the character set has already been increased in MatchFontDescriptionToPattern.
629     mCharacterSetCache.PushBack(characterSet);
630
631     if((fontDescription.family != description.family) ||
632        (fontDescription.width != description.width) ||
633        (fontDescription.weight != description.weight) ||
634        (fontDescription.slant != description.slant))
635     {
636       // Cache the given font's description if it's different than the matched.
637       CacheValidateFont(std::move(FontDescription(fontDescription)), fontDescriptionId);
638     }
639
640     // Cache the index and the matched font's description.
641     CacheValidateFont(std::move(description), fontDescriptionId);
642   }
643   else
644   {
645     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str());
646   }
647 }
648
649 void FontClient::Plugin::CacheHandler::CacheValidateFont(FontDescription&& fontDescription,
650                                                          FontDescriptionId validatedFontId)
651 {
652   mValidatedFontCache.emplace_back(std::move(FontDescriptionCacheItem(fontDescription, validatedFontId)));
653 }
654
655 // Fallback
656
657 bool FontClient::Plugin::CacheHandler::FindFallbackFontList(const FontDescription& fontDescription,
658                                                             FontList*&             fontList,
659                                                             CharacterSetList*&     characterSetList) const
660 {
661   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
662   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %zu\n", mFallbackCache.size());
663
664   fontList = nullptr;
665
666   for(const auto& item : mFallbackCache)
667   {
668     if(!fontDescription.family.empty() &&
669        (fontDescription.family == item.fontDescription.family) &&
670        (fontDescription.width == item.fontDescription.width) &&
671        (fontDescription.weight == item.fontDescription.weight) &&
672        (fontDescription.slant == item.fontDescription.slant))
673     {
674       fontList         = item.fallbackFonts;
675       characterSetList = item.characterSets;
676
677       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list found.\n");
678       return true;
679     }
680   }
681
682   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  fallback font list not found.\n");
683   return false;
684 }
685
686 void FontClient::Plugin::CacheHandler::CacheFallbackFontList(FontDescription&&  fontDescription,
687                                                              FontList*&         fontList,
688                                                              CharacterSetList*& characterSetList)
689 {
690   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
691
692   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_FALLBACK_FONTLIST");
693
694   #if defined(TRACE_ENABLED)
695   if(gTraceFilter && gTraceFilter->IsTraceEnabled())
696   {
697     DALI_LOG_DEBUG_INFO("DALI_TEXT_FALLBACK_FONTLIST : FcFontSort : %s\n", fontDescription.family.c_str());
698   }
699   #endif
700
701   fontList         = new FontList;
702   characterSetList = new CharacterSetList;
703
704   SetFontList(fontDescription, *fontList, *characterSetList);
705 #ifdef __APPLE__
706   FontDescription appleColorEmoji;
707   appleColorEmoji.family = "Apple Color Emoji";
708   appleColorEmoji.width  = fontDescription.width;
709   appleColorEmoji.weight = fontDescription.weight;
710   appleColorEmoji.slant  = fontDescription.slant;
711   FontList         emojiFontList;
712   CharacterSetList emojiCharSetList;
713   SetFontList(appleColorEmoji, emojiFontList, emojiCharSetList);
714
715   std::move(fontList->begin(), fontList->end(), std::back_inserter(emojiFontList));
716   emojiCharSetList.Insert(emojiCharSetList.End(), characterSetList->Begin(), characterSetList->End());
717   *fontList         = std::move(emojiFontList);
718   *characterSetList = std::move(emojiCharSetList);
719 #endif
720
721   // Add the font-list to the cache.
722   mFallbackCache.push_back(std::move(CacheHandler::FallbackCacheItem(std::move(fontDescription), fontList, characterSetList)));
723 }
724
725 // Font / FontFace
726
727 bool FontClient::Plugin::CacheHandler::FindFontByPath(const FontPath& path,
728                                                       PointSize26Dot6 requestedPointSize,
729                                                       FaceIndex       faceIndex,
730                                                       FontId&         fontId) const
731 {
732   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
733   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "                path : [%s]\n", path.c_str());
734   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
735   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "  number of fonts in the cache : %zu\n", mFontFaceCache.size());
736
737   fontId = 0u;
738   for(const auto& cacheItem : mFontFaceCache)
739   {
740     if(cacheItem.mRequestedPointSize == requestedPointSize &&
741        cacheItem.mFaceIndex == faceIndex &&
742        cacheItem.mPath == path)
743     {
744       fontId = cacheItem.mFontId + 1u;
745
746       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, id : %d\n", fontId);
747       return true;
748     }
749   }
750
751   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found\n");
752   return false;
753 }
754
755 bool FontClient::Plugin::CacheHandler::FindFont(FontDescriptionId fontDescriptionId,
756                                                 PointSize26Dot6   requestedPointSize,
757                                                 FontCacheIndex&   fontCacheIndex)
758 {
759   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
760   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "   fontDescriptionId : %d\n", fontDescriptionId);
761   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize);
762
763   fontCacheIndex = 0u;
764
765   const FontDescriptionSizeCacheKey key(fontDescriptionId, requestedPointSize);
766
767   // Heuristic optimize code : Compare with latest found item.
768   if(key == mLatestFoundCacheKey)
769   {
770     fontCacheIndex = mLatestFoundCacheIndex;
771
772     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font same as latest, index of font cache : %d\n", fontCacheIndex);
773     return true;
774   }
775
776   const auto& iter = mFontDescriptionSizeCache.find(key);
777   if(iter != mFontDescriptionSizeCache.cend())
778   {
779     fontCacheIndex = iter->second;
780
781     mLatestFoundCacheKey   = key;
782     mLatestFoundCacheIndex = fontCacheIndex;
783
784     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font found, index of font cache : %d\n", fontCacheIndex);
785     return true;
786   }
787
788   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  font not found.\n");
789   return false;
790 }
791
792 void FontClient::Plugin::CacheHandler::CacheFontDescriptionSize(FontDescriptionId fontDescriptionId, PointSize26Dot6 requestedPointSize, FontCacheIndex fontCacheIndex)
793 {
794   mFontDescriptionSizeCache.emplace(FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
795 }
796
797 void FontClient::Plugin::CacheHandler::CacheFontPath(FT_Face ftFace, FontId fontId, PointSize26Dot6 requestedPointSize, const FontPath& path)
798 {
799   FontDescription description;
800   description.path   = path;
801   description.family = std::move(FontFamily(ftFace->family_name));
802   description.weight = FontWeight::NONE;
803   description.width  = FontWidth::NONE;
804   description.slant  = FontSlant::NONE;
805
806   // Note FreeType doesn't give too much info to build a proper font style.
807   if(ftFace->style_flags & FT_STYLE_FLAG_ITALIC)
808   {
809     description.slant = FontSlant::ITALIC;
810   }
811   if(ftFace->style_flags & FT_STYLE_FLAG_BOLD)
812   {
813     description.weight = FontWeight::BOLD;
814   }
815
816   FontDescriptionId fontDescriptionId = 0u;
817   if(!FindValidatedFont(description, fontDescriptionId))
818   {
819     // TODO : Due to the FontClient pattern match process, we cannot pass dali-toolkit UTC.
820     // Can't we use ValidateFont here?
821     /*
822     // Use font config to validate the font's description.
823     ValidateFont(description, fontDescriptionId);
824
825     const FontCacheIndex fontCacheIndex          = mFontIdCache[fontId - 1u].index;
826     mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(mCharacterSetCache[fontDescriptionId - 1u]); // Increases the reference counter.
827
828     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
829     mFontDescriptionSizeCache.emplace(CacheHandler::FontDescriptionSizeCacheKey(fontDescriptionId, requestedPointSize), fontCacheIndex);
830     */
831
832     FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
833
834     FcResult   result = FcResultMatch;
835     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
836
837     FcCharSet* characterSet = nullptr;
838     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
839
840     const FontCacheIndex fontCacheIndex          = mFontIdCache[fontId - 1u].index;
841     mFontFaceCache[fontCacheIndex].mCharacterSet = FcCharSetCopy(characterSet); // Increases the reference counter.
842
843     // Destroys the created patterns.
844     FcPatternDestroy(match);
845     FcPatternDestroy(pattern);
846
847     // Add the path to the cache.
848     description.type = FontDescription::FACE_FONT;
849     mFontDescriptionCache.push_back(description);
850
851     // Set the index to the vector of paths to font file names.
852     fontDescriptionId = static_cast<FontDescriptionId>(mFontDescriptionCache.size());
853
854     // Increase the reference counter and add the character set to the cache.
855     mCharacterSetCache.PushBack(FcCharSetCopy(characterSet));
856
857     // Cache the index and the font's description.
858     CacheValidateFont(std::move(description), fontDescriptionId);
859
860     // Cache the pair 'fontDescriptionId, requestedPointSize' to improve the following queries.
861     CacheFontDescriptionSize(fontDescriptionId, requestedPointSize, fontCacheIndex);
862   }
863 }
864
865 FontId FontClient::Plugin::CacheHandler::CacheFontFaceCacheItem(FontFaceCacheItem&& fontFaceCacheItem)
866 {
867   // Set the index to the font's id cache.
868   fontFaceCacheItem.mFontId = static_cast<FontId>(mFontIdCache.size());
869
870   // Create the font id item to cache.
871   FontIdCacheItem fontIdCacheItem;
872   fontIdCacheItem.type = FontDescription::FACE_FONT;
873
874   // Set the index to the FreeType font face cache.
875   fontIdCacheItem.index = static_cast<FontCacheIndex>(mFontFaceCache.size());
876
877   // Cache the items.
878   mFontFaceCache.emplace_back(std::move(fontFaceCacheItem));
879   mFontIdCache.emplace_back(std::move(fontIdCacheItem));
880
881   // Set the font id to be returned.
882   FontId fontId = static_cast<FontId>(mFontIdCache.size());
883
884   return fontId;
885 }
886
887 // Ellipsis
888
889 bool FontClient::Plugin::CacheHandler::FindEllipsis(PointSize26Dot6 requestedPointSize, EllipsisCacheIndex& ellipsisCacheIndex) const
890 {
891   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
892   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize);
893
894   ellipsisCacheIndex = 0u;
895
896   // First look into the cache if there is an ellipsis glyph for the requested point size.
897   for(const auto& item : mEllipsisCache)
898   {
899     if(item.requestedPointSize == requestedPointSize)
900     {
901       // Use the glyph in the cache.
902       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index);
903       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId);
904       ellipsisCacheIndex = item.index;
905       return true;
906     }
907   }
908   return false;
909 }
910
911 FontClient::Plugin::CacheHandler::EllipsisCacheIndex FontClient::Plugin::CacheHandler::CacheEllipsis(EllipsisItem&& ellipsisItem)
912 {
913   EllipsisCacheIndex ellipsisCacheIndex = static_cast<EllipsisCacheIndex>(mEllipsisCache.size());
914
915   mEllipsisCache.emplace_back(std::move(ellipsisItem));
916
917   return ellipsisCacheIndex;
918 }
919
920 // Bitmap font
921
922 bool FontClient::Plugin::CacheHandler::FindBitmapFont(const FontFamily& bitmapFontFamily, FontId& fontId) const
923 {
924   fontId = 0u;
925
926   for(const auto& item : mBitmapFontCache)
927   {
928     if(bitmapFontFamily == item.font.name)
929     {
930       fontId = item.id + 1u;
931       return true;
932     }
933   }
934
935   return false;
936 }
937
938 FontId FontClient::Plugin::CacheHandler::CacheBitmapFontCacheItem(BitmapFontCacheItem&& bitmapFontCacheItem)
939 {
940   // Set the index to the font's id cache.
941   bitmapFontCacheItem.id = static_cast<FontId>(mFontIdCache.size());
942
943   // Create the font id item to cache.
944   CacheHandler::FontIdCacheItem fontIdCacheItem;
945   fontIdCacheItem.type = FontDescription::BITMAP_FONT;
946
947   // Set the index to the Bitmap font face cache.
948   fontIdCacheItem.index = static_cast<FontCacheIndex>(mBitmapFontCache.size());
949
950   // Cache the items.
951   mBitmapFontCache.emplace_back(std::move(bitmapFontCacheItem));
952   mFontIdCache.emplace_back(std::move(fontIdCacheItem));
953
954   // Set the font id to be returned.
955   FontId fontId = static_cast<FontId>(mFontIdCache.size());
956
957   return fontId;
958 }
959
960 // Embedded
961
962 bool FontClient::Plugin::CacheHandler::FindEmbeddedPixelBufferId(const std::string& url, PixelBufferId& pixelBufferId) const
963 {
964   pixelBufferId = 0u;
965
966   for(const auto& cacheItem : mPixelBufferCache)
967   {
968     if(cacheItem.url == url)
969     {
970       // The url is in the pixel buffer cache.
971       pixelBufferId = cacheItem.id;
972       return true;
973     }
974   }
975
976   return false;
977 }
978
979 PixelBufferId FontClient::Plugin::CacheHandler::CacheEmbeddedPixelBuffer(const std::string& url)
980 {
981   PixelBufferId pixelBufferId = 0u;
982
983   // Load the image from the url.
984   Devel::PixelBuffer pixelBuffer = LoadImageFromFile(url);
985   if(pixelBuffer)
986   {
987     // Create the cache item.
988     PixelBufferCacheItem pixelBufferCacheItem;
989     pixelBufferCacheItem.pixelBuffer = pixelBuffer;
990     pixelBufferCacheItem.url         = url;
991     pixelBufferCacheItem.id          = static_cast<PixelBufferId>(mPixelBufferCache.size() + 1u);
992
993     // Store the cache item in the cache.
994     mPixelBufferCache.emplace_back(std::move(pixelBufferCacheItem));
995
996     // Set the pixel buffer id to be returned.
997     pixelBufferId = static_cast<PixelBufferId>(mPixelBufferCache.size());
998   }
999   return pixelBufferId;
1000 }
1001
1002 bool FontClient::Plugin::CacheHandler::FindEmbeddedItem(PixelBufferId pixelBufferId, uint32_t width, uint32_t height, GlyphIndex& index) const
1003 {
1004   index = 0u;
1005
1006   for(const auto& cacheItem : mEmbeddedItemCache)
1007   {
1008     if((cacheItem.pixelBufferId == pixelBufferId) &&
1009        (cacheItem.width == width) &&
1010        (cacheItem.height == height))
1011     {
1012       index = cacheItem.index;
1013       return true;
1014     }
1015   }
1016
1017   return false;
1018 }
1019
1020 GlyphIndex FontClient::Plugin::CacheHandler::CacheEmbeddedItem(EmbeddedItem&& embeddedItem)
1021 {
1022   embeddedItem.index = static_cast<GlyphIndex>(mEmbeddedItemCache.size() + 1u);
1023
1024   // Cache the embedded item.
1025   mEmbeddedItemCache.emplace_back(std::move(embeddedItem));
1026
1027   // Set the font id to be returned.
1028   GlyphIndex index = static_cast<GlyphIndex>(mEmbeddedItemCache.size());
1029
1030   return index;
1031 }
1032
1033 } // namespace Dali::TextAbstraction::Internal