[Tizen] Add support for FontClientFontPreLoad API
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / font-client-impl.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/font-client-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <mutex>
23 #include <thread>
24 #if defined(VCONF_ENABLED)
25 #include <vconf.h>
26 #endif
27
28 // INTERNAL INCLUDES
29 #include <dali/devel-api/common/singleton-service.h>
30 #include <dali/internal/system/common/logging.h>
31 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
32 #include <dali/internal/window-system/common/window-system.h>
33
34 #include <dali/devel-api/text-abstraction/glyph-info.h>
35
36 // Use this macro only if need to log messages before the log function is set.
37 #define FONT_LOG_MESSAGE(level, format, ...)                                    \
38   do                                                                            \
39   {                                                                             \
40     char buffer[256];                                                           \
41     int  result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
42     if(result >= static_cast<int>(sizeof(buffer)))                              \
43     {                                                                           \
44       std::string log("Font log message is too long to fit in the buffer.\n");  \
45       Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log);      \
46       break;                                                                    \
47     }                                                                           \
48     std::string log(buffer);                                                    \
49     Dali::TizenPlatform::LogMessage(level, log);                                \
50   } while(0)
51
52 namespace Dali
53 {
54 namespace TextAbstraction
55 {
56 namespace Internal
57 {
58 Dali::TextAbstraction::FontClient FontClient::gPreCreatedFontClient(NULL);
59 std::thread                       gPreCacheThread;
60 std::thread                       gPreLoadThread;
61 std::mutex                        gMutex;
62
63 /* TODO: This is to prevent duplicate calls of font pre-cache.
64  * We may support this later, but currently we can't guarantee the behaviour
65  * if there is a pre-cache call from the user after the font client has been created. */
66 bool gFontPreCacheAvailable = true;
67 bool gFontPreLoadAvailable  = true;
68
69 FontClient::FontClient()
70 : mPlugin(nullptr),
71   mDpiHorizontal(0),
72   mDpiVertical(0)
73 {
74 }
75
76 FontClient::~FontClient()
77 {
78   delete mPlugin;
79 }
80
81 Dali::TextAbstraction::FontClient FontClient::Get()
82 {
83   Dali::TextAbstraction::FontClient fontClientHandle;
84
85   Dali::SingletonService service(SingletonService::Get());
86   if(service)
87   {
88     // Check whether the singleton is already created
89     Dali::BaseHandle handle = service.GetSingleton(typeid(Dali::TextAbstraction::FontClient));
90     if(handle)
91     {
92       // If so, downcast the handle
93       FontClient* impl = dynamic_cast<Dali::TextAbstraction::Internal::FontClient*>(handle.GetObjectPtr());
94       fontClientHandle = Dali::TextAbstraction::FontClient(impl);
95     }
96     else // create and register the object
97     {
98       if(gPreCacheThread.joinable())
99       {
100         gPreCacheThread.join();
101         FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache thread join\n");
102       }
103
104       if(gPreLoadThread.joinable())
105       {
106         gPreLoadThread.join();
107         FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreLoad thread join\n");
108       }
109
110       if(gPreCreatedFontClient)
111       {
112         fontClientHandle = gPreCreatedFontClient;
113         gPreCreatedFontClient.Reset(); // No longer needed
114       }
115       else
116       {
117         fontClientHandle = Dali::TextAbstraction::FontClient(new FontClient);
118       }
119
120       fontClientHandle.InitDefaultFontDescription();
121
122       gFontPreCacheAvailable = false;
123       gFontPreLoadAvailable  = false;
124
125       uint32_t horizontalDpi, verticalDpi;
126       fontClientHandle.GetDpi(horizontalDpi, verticalDpi);
127       if(horizontalDpi == 0u || verticalDpi == 0u)
128       {
129         horizontalDpi = verticalDpi = 0u;
130         Dali::Internal::Adaptor::WindowSystem::GetDpi(horizontalDpi, verticalDpi);
131         fontClientHandle.SetDpi(horizontalDpi, verticalDpi);
132       }
133
134       service.Register(typeid(fontClientHandle), fontClientHandle);
135     }
136   }
137
138   return fontClientHandle;
139 }
140
141 Dali::TextAbstraction::FontClient FontClient::PreInitialize()
142 {
143   // Pre-cached font client already exists or pre-cache thread already running.
144   // Font client pre-cache includes caching of the default font description.
145   if(gPreCreatedFontClient && !gFontPreCacheAvailable)
146   {
147     return gPreCreatedFontClient;
148   }
149
150   if(!gPreCreatedFontClient)
151   {
152     gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
153   }
154
155   gPreCreatedFontClient.InitDefaultFontDescription();
156
157   return gPreCreatedFontClient;
158 }
159
160 void FontClient::PreCacheRun(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily)
161 {
162   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_RUN\n");
163   GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
164   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_RUN\n");
165 }
166
167 void FontClient::PreLoadRun(const FontPathList& fontPathList, const FontPathList& memoryFontPathList)
168 {
169   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_FONT_PRELOAD_RUN\n");
170   GetImplementation(gPreCreatedFontClient).FontPreLoad(fontPathList, memoryFontPathList);
171   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_FONT_PRELOAD_RUN\n");
172 }
173
174 void FontClient::PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread)
175 {
176   if(!gFontPreCacheAvailable)
177   {
178     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache run failed, as a pre-cached font client already exists.\n");
179     return;
180   }
181
182   if(gPreCacheThread.joinable())
183   {
184     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache thread already running.\n");
185   }
186
187   if(!gPreCreatedFontClient)
188   {
189     gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
190   }
191
192   gFontPreCacheAvailable = false;
193
194   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache fallbackFamilyList : %zu\n", fallbackFamilyList.size());
195   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache extraFamilyList    : %zu\n", extraFamilyList.size());
196   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache localeFamily       : %s\n", localeFamily.c_str());
197   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache useThread          : %d\n", useThread);
198
199   if(useThread)
200   {
201     gPreCacheThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily);
202   }
203   else
204   {
205     PreCacheRun(fallbackFamilyList, extraFamilyList, localeFamily);
206   }
207 }
208
209 void FontClient::PreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread)
210 {
211   if(!gFontPreLoadAvailable)
212   {
213     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load run failed, as a pre-loaded font client already exists.\n");
214     return;
215   }
216
217   if(gPreLoadThread.joinable())
218   {
219     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load thread already running.\n");
220     return;
221   }
222
223   if(!gPreCreatedFontClient)
224   {
225     gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
226   }
227
228   gFontPreLoadAvailable = false;
229
230   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad fontPathList       : %zu\n", fontPathList.size());
231   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad memoryFontPathList : %zu\n", memoryFontPathList.size());
232   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad useThread          : %d\n", useThread);
233
234   if(useThread)
235   {
236     gPreLoadThread = std::thread(PreLoadRun, fontPathList, memoryFontPathList);
237   }
238   else
239   {
240     PreLoadRun(fontPathList, memoryFontPathList);
241   }
242 }
243
244 void FontClient::ClearCache()
245 {
246   if(mPlugin)
247   {
248     mPlugin->ClearCache();
249   }
250 }
251
252 void FontClient::SetDpi(unsigned int horizontalDpi, unsigned int verticalDpi)
253 {
254   mDpiHorizontal = horizontalDpi;
255   mDpiVertical   = verticalDpi;
256
257   // Allow DPI to be set without loading plugin
258   if(mPlugin)
259   {
260     mPlugin->SetDpi(horizontalDpi, verticalDpi);
261   }
262 }
263
264 void FontClient::GetDpi(unsigned int& horizontalDpi, unsigned int& verticalDpi)
265 {
266   horizontalDpi = mDpiHorizontal;
267   verticalDpi   = mDpiVertical;
268 }
269
270 int FontClient::GetDefaultFontSize()
271 {
272   int fontSize(-1);
273
274 #if defined(VCONF_ENABLED)
275   vconf_get_int(VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_SIZE, &fontSize);
276 #endif
277
278   return fontSize;
279 }
280
281 void FontClient::ResetSystemDefaults()
282 {
283   CreatePlugin();
284
285   mPlugin->ResetSystemDefaults();
286 }
287
288 void FontClient::GetDefaultFonts(FontList& defaultFonts)
289 {
290   CreatePlugin();
291
292   mPlugin->GetDefaultFonts(defaultFonts);
293 }
294
295 void FontClient::FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily)
296 {
297   CreatePlugin();
298
299   mPlugin->FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
300 }
301
302 void FontClient::FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList)
303 {
304   CreatePlugin();
305
306   mPlugin->FontPreLoad(fontPathList, memoryFontPathList);
307 }
308
309 void FontClient::InitDefaultFontDescription()
310 {
311   CreatePlugin();
312
313   mPlugin->InitDefaultFontDescription();
314 }
315
316 void FontClient::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
317 {
318   CreatePlugin();
319
320   mPlugin->GetDefaultPlatformFontDescription(fontDescription);
321 }
322
323 void FontClient::GetDescription(FontId fontId, FontDescription& fontDescription)
324 {
325   CreatePlugin();
326
327   mPlugin->GetDescription(fontId, fontDescription);
328 }
329
330 PointSize26Dot6 FontClient::GetPointSize(FontId fontId)
331 {
332   CreatePlugin();
333
334   return mPlugin->GetPointSize(fontId);
335 }
336
337 bool FontClient::IsCharacterSupportedByFont(FontId fontId, Character character)
338 {
339   CreatePlugin();
340
341   return mPlugin->IsCharacterSupportedByFont(fontId, character);
342 }
343
344 void FontClient::GetSystemFonts(FontList& systemFonts)
345 {
346   CreatePlugin();
347
348   mPlugin->GetSystemFonts(systemFonts);
349 }
350
351 FontId FontClient::FindDefaultFont(Character       charcode,
352                                    PointSize26Dot6 requestedPointSize,
353                                    bool            preferColor)
354 {
355   CreatePlugin();
356
357   return mPlugin->FindDefaultFont(charcode,
358                                   requestedPointSize,
359                                   preferColor);
360 }
361
362 FontId FontClient::FindFallbackFont(Character              charcode,
363                                     const FontDescription& preferredFontDescription,
364                                     PointSize26Dot6        requestedPointSize,
365                                     bool                   preferColor)
366 {
367   CreatePlugin();
368
369   return mPlugin->FindFallbackFont(charcode,
370                                    preferredFontDescription,
371                                    requestedPointSize,
372                                    preferColor);
373 }
374
375 bool FontClient::IsScalable(const FontPath& path)
376 {
377   CreatePlugin();
378
379   return mPlugin->IsScalable(path);
380 }
381
382 bool FontClient::IsScalable(const FontDescription& fontDescription)
383 {
384   CreatePlugin();
385
386   return mPlugin->IsScalable(fontDescription);
387 }
388
389 void FontClient::GetFixedSizes(const FontPath& path, Dali::Vector<PointSize26Dot6>& sizes)
390 {
391   CreatePlugin();
392
393   mPlugin->GetFixedSizes(path, sizes);
394 }
395
396 void FontClient::GetFixedSizes(const FontDescription&         fontDescription,
397                                Dali::Vector<PointSize26Dot6>& sizes)
398 {
399   CreatePlugin();
400
401   mPlugin->GetFixedSizes(fontDescription, sizes);
402 }
403
404 bool FontClient::HasItalicStyle(FontId fontId) const
405 {
406   if(!mPlugin)
407   {
408     return false;
409   }
410   return mPlugin->HasItalicStyle(fontId);
411 }
412
413 FontId FontClient::GetFontId(const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex faceIndex)
414 {
415   CreatePlugin();
416
417   return mPlugin->GetFontIdByPath(path,
418                                   requestedPointSize,
419                                   faceIndex,
420                                   true);
421 }
422
423 FontId FontClient::GetFontId(const FontDescription& fontDescription,
424                              PointSize26Dot6        requestedPointSize,
425                              FaceIndex              faceIndex)
426 {
427   CreatePlugin();
428
429   return mPlugin->GetFontId(fontDescription,
430                             requestedPointSize,
431                             faceIndex);
432 }
433
434 FontId FontClient::GetFontId(const BitmapFont& bitmapFont)
435 {
436   CreatePlugin();
437
438   return mPlugin->GetFontId(bitmapFont);
439 }
440
441 void FontClient::GetFontMetrics(FontId fontId, FontMetrics& metrics)
442 {
443   CreatePlugin();
444
445   mPlugin->GetFontMetrics(fontId, metrics);
446 }
447
448 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode)
449 {
450   CreatePlugin();
451
452   return mPlugin->GetGlyphIndex(fontId, charcode);
453 }
454
455 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode, Character variantSelector)
456 {
457   CreatePlugin();
458
459   return mPlugin->GetGlyphIndex(fontId, charcode, variantSelector);
460 }
461
462 bool FontClient::GetGlyphMetrics(GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal)
463 {
464   CreatePlugin();
465
466   return mPlugin->GetGlyphMetrics(array, size, type, horizontal);
467 }
468
469 void FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
470 {
471   CreatePlugin();
472
473   mPlugin->CreateBitmap(fontId, glyphIndex, isItalicRequired, isBoldRequired, data, outlineWidth);
474 }
475
476 PixelData FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
477 {
478   CreatePlugin();
479
480   return mPlugin->CreateBitmap(fontId, glyphIndex, outlineWidth);
481 }
482
483 void FontClient::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
484 {
485   CreatePlugin();
486
487   mPlugin->CreateVectorBlob(fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
488 }
489
490 const GlyphInfo& FontClient::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
491 {
492   CreatePlugin();
493
494   return mPlugin->GetEllipsisGlyph(requestedPointSize);
495 }
496
497 bool FontClient::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
498 {
499   CreatePlugin();
500
501   return mPlugin->IsColorGlyph(fontId, glyphIndex);
502 }
503
504 GlyphIndex FontClient::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
505 {
506   CreatePlugin();
507
508   return mPlugin->CreateEmbeddedItem(description, pixelFormat);
509 }
510
511 void FontClient::EnableAtlasLimitation(bool enabled)
512 {
513   CreatePlugin();
514   return mPlugin->EnableAtlasLimitation(enabled);
515 }
516
517 bool FontClient::IsAtlasLimitationEnabled() const
518 {
519   if(mPlugin)
520   {
521     return mPlugin->IsAtlasLimitationEnabled();
522   }
523   return TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED;
524 }
525
526 Size FontClient::GetMaximumTextAtlasSize() const
527 {
528   if(mPlugin)
529   {
530     return mPlugin->GetMaximumTextAtlasSize();
531   }
532   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
533 }
534
535 Size FontClient::GetDefaultTextAtlasSize() const
536 {
537   if(mPlugin)
538   {
539     return mPlugin->GetDefaultTextAtlasSize();
540   }
541   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
542 }
543
544 Size FontClient::GetCurrentMaximumBlockSizeFitInAtlas() const
545 {
546   if(mPlugin)
547   {
548     return mPlugin->GetCurrentMaximumBlockSizeFitInAtlas();
549   }
550   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
551 }
552
553 bool FontClient::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
554 {
555   CreatePlugin();
556   return mPlugin->SetCurrentMaximumBlockSizeFitInAtlas(currentMaximumBlockSizeFitInAtlas);
557 }
558
559 uint32_t FontClient::GetNumberOfPointsPerOneUnitOfPointSize() const
560 {
561   if(mPlugin)
562   {
563     return mPlugin->GetNumberOfPointsPerOneUnitOfPointSize();
564   }
565   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
566   ;
567 }
568
569 FT_FaceRec_* FontClient::GetFreetypeFace(FontId fontId)
570 {
571   CreatePlugin();
572
573   return mPlugin->GetFreetypeFace(fontId);
574 }
575
576 FontDescription::Type FontClient::GetFontType(FontId fontId)
577 {
578   CreatePlugin();
579
580   return mPlugin->GetFontType(fontId);
581 }
582
583 bool FontClient::AddCustomFontDirectory(const FontPath& path)
584 {
585   CreatePlugin();
586
587   return mPlugin->AddCustomFontDirectory(path);
588 }
589
590 HarfBuzzFontHandle FontClient::GetHarfBuzzFont(FontId fontId)
591 {
592   CreatePlugin();
593
594   return mPlugin->GetHarfBuzzFont(fontId);
595 }
596
597 void FontClient::CreatePlugin()
598 {
599   std::scoped_lock lock(gMutex);
600   if(!mPlugin)
601   {
602     mPlugin = new Plugin(mDpiHorizontal, mDpiVertical);
603   }
604 }
605
606 } // namespace Internal
607
608 } // namespace TextAbstraction
609
610 } // namespace Dali