2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/text/text-abstraction/font-client-impl.h>
22 #include <condition_variable>
25 #if defined(VCONF_ENABLED)
30 #include <dali/devel-api/adaptor-framework/thread-settings.h>
31 #include <dali/devel-api/common/singleton-service.h>
32 #include <dali/internal/system/common/logging.h>
33 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
34 #include <dali/internal/window-system/common/window-system.h>
36 #include <dali/devel-api/text-abstraction/glyph-info.h>
38 // Use this macro only if need to log messages before the log function is set.
39 #define FONT_LOG_MESSAGE(level, format, ...) \
43 int result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
44 if(result >= static_cast<int>(sizeof(buffer))) \
46 std::string log("Font log message is too long to fit in the buffer.\n"); \
47 Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log); \
50 std::string log(buffer); \
51 Dali::TizenPlatform::LogMessage(level, log); \
56 namespace TextAbstraction
63 * There are cases in the candidate process where the font thread is
64 * no UI or when the font client is not used.
65 * In such cases, we can ensure the join of the font thread
66 * by calling the join method in the destructor of this class.
74 if(mThread.joinable())
82 Dali::TextAbstraction::FontClient FontClient::gPreCreatedFontClient(NULL);
84 FontThread gPreCacheThread;
85 FontThread gPreLoadThread;
87 std::condition_variable gPreCacheCond;
88 std::condition_variable gPreLoadCond;
89 bool gPreCacheThreadReady;
90 bool gPreLoadThreadReady;
92 /* TODO: This is to prevent duplicate calls of font pre-cache.
93 * We may support this later, but currently we can't guarantee the behaviour
94 * if there is a pre-cache call from the user after the font client has been created. */
95 bool gFontPreCacheAvailable = true;
96 bool gFontPreLoadAvailable = true;
98 FontClient::FontClient()
105 FontClient::~FontClient()
110 Dali::TextAbstraction::FontClient FontClient::Get()
112 Dali::TextAbstraction::FontClient fontClientHandle;
114 Dali::SingletonService service(SingletonService::Get());
117 // Check whether the singleton is already created
118 Dali::BaseHandle handle = service.GetSingleton(typeid(Dali::TextAbstraction::FontClient));
121 // If so, downcast the handle
122 FontClient* impl = dynamic_cast<Dali::TextAbstraction::Internal::FontClient*>(handle.GetObjectPtr());
123 fontClientHandle = Dali::TextAbstraction::FontClient(impl);
125 else // create and register the object
127 // Check the joinable status of PreCahe and PreLoad thread, and join them
130 if(gPreCreatedFontClient)
132 fontClientHandle = gPreCreatedFontClient;
133 gPreCreatedFontClient.Reset(); // No longer needed
137 fontClientHandle = Dali::TextAbstraction::FontClient(new FontClient);
140 fontClientHandle.InitDefaultFontDescription();
142 gFontPreCacheAvailable = false;
143 gFontPreLoadAvailable = false;
145 uint32_t horizontalDpi, verticalDpi;
146 fontClientHandle.GetDpi(horizontalDpi, verticalDpi);
147 if(horizontalDpi == 0u || verticalDpi == 0u)
149 horizontalDpi = verticalDpi = 0u;
150 Dali::Internal::Adaptor::WindowSystem::GetDpi(horizontalDpi, verticalDpi);
151 fontClientHandle.SetDpi(horizontalDpi, verticalDpi);
154 service.Register(typeid(fontClientHandle), fontClientHandle);
158 return fontClientHandle;
161 Dali::TextAbstraction::FontClient FontClient::PreInitialize()
163 // Pre-cached font client already exists or pre-cache thread already running.
164 // Font client pre-cache includes caching of the default font description.
165 if(gPreCreatedFontClient && !gFontPreCacheAvailable)
167 return gPreCreatedFontClient;
170 if(!gPreCreatedFontClient)
172 gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
175 gPreCreatedFontClient.InitDefaultFontDescription();
177 return gPreCreatedFontClient;
180 void FontClient::PreCacheRun(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool syncCreation)
182 SetThreadName("FontThread-fc");
186 std::unique_lock<std::mutex> lock(gMutex);
187 gPreCacheThreadReady = true;
188 gPreCacheCond.notify_one();
192 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_RUN\n");
193 GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
194 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_RUN\n");
197 void FontClient::PreLoadRun(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool syncCreation)
199 SetThreadName("FontThread-ft");
203 std::unique_lock<std::mutex> lock(gMutex);
204 gPreLoadThreadReady = true;
205 gPreLoadCond.notify_one();
209 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_FONT_PRELOAD_RUN\n");
210 GetImplementation(gPreCreatedFontClient).FontPreLoad(fontPathList, memoryFontPathList);
211 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_FONT_PRELOAD_RUN\n");
214 void FontClient::PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread, bool syncCreation)
216 if(!gFontPreCacheAvailable)
218 FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache run failed, as a pre-cached font client already exists.\n");
222 if(gPreCacheThread.mThread.joinable())
224 FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache thread already running.\n");
228 if(!gPreCreatedFontClient)
230 gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
233 gFontPreCacheAvailable = false;
235 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache fallbackFamilyList : %zu\n", fallbackFamilyList.size());
236 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache extraFamilyList : %zu\n", extraFamilyList.size());
237 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache localeFamily : %s\n", localeFamily.c_str());
238 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache useThread : %d\n", useThread);
244 // The main thread wakes up upon receiving a notification from the pre-cache thread.
245 // If it doesn't receive a notification within the specified time, it wakes up due to a timeout.
246 const std::chrono::milliseconds timeout(1000);
247 gPreCacheThreadReady = false;
248 std::unique_lock<std::mutex> lock(gMutex);
249 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_THREAD_SYNC_CREATION\n");
250 gPreCacheThread.mThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily, syncCreation);
251 gPreCacheCond.wait_for(lock, timeout, []{return gPreCacheThreadReady;});
252 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_THREAD_SYNC_CREATION\n");
256 gPreCacheThread.mThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily, syncCreation);
261 GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
265 void FontClient::PreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread, bool syncCreation)
267 if(!gFontPreLoadAvailable)
269 FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load run failed, as a pre-loaded font client already exists.\n");
273 if(gPreLoadThread.mThread.joinable())
275 FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load thread already running.\n");
279 if(!gPreCreatedFontClient)
281 gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
284 gFontPreLoadAvailable = false;
286 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad fontPathList : %zu\n", fontPathList.size());
287 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad memoryFontPathList : %zu\n", memoryFontPathList.size());
288 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad useThread : %d\n", useThread);
294 // The main thread wakes up upon receiving a notification from the pre-load thread.
295 // If it doesn't receive a notification within the specified time, it wakes up due to a timeout.
296 const std::chrono::milliseconds timeout(1000);
297 gPreLoadThreadReady = false;
298 std::unique_lock<std::mutex> lock(gMutex);
299 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_FONT_PRELOAD_THREAD_SYNC_CREATION\n");
300 gPreLoadThread.mThread = std::thread(PreLoadRun, fontPathList, memoryFontPathList, syncCreation);
301 gPreLoadCond.wait_for(lock, timeout, []{return gPreLoadThreadReady;});
302 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_FONT_PRELOAD_THREAD_SYNC_CREATION\n");
306 gPreLoadThread.mThread = std::thread(PreLoadRun, fontPathList, memoryFontPathList, syncCreation);
311 GetImplementation(gPreCreatedFontClient).FontPreLoad(fontPathList, memoryFontPathList);
315 void FontClient::JoinFontThreads()
317 if(gPreCacheThread.mThread.joinable())
319 gPreCacheThread.mThread.join();
320 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache thread join\n");
323 if(gPreLoadThread.mThread.joinable())
325 gPreLoadThread.mThread.join();
326 FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreLoad thread join\n");
330 void FontClient::ClearCache()
334 mPlugin->ClearCache();
338 void FontClient::ClearCacheOnLocaleChanged()
342 mPlugin->ClearCacheOnLocaleChanged();
346 void FontClient::SetDpi(unsigned int horizontalDpi, unsigned int verticalDpi)
348 mDpiHorizontal = horizontalDpi;
349 mDpiVertical = verticalDpi;
351 // Allow DPI to be set without loading plugin
354 mPlugin->SetDpi(horizontalDpi, verticalDpi);
358 void FontClient::GetDpi(unsigned int& horizontalDpi, unsigned int& verticalDpi)
360 horizontalDpi = mDpiHorizontal;
361 verticalDpi = mDpiVertical;
364 int FontClient::GetDefaultFontSize()
368 #if defined(VCONF_ENABLED)
369 vconf_get_int(VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_SIZE, &fontSize);
375 void FontClient::ResetSystemDefaults()
379 mPlugin->ResetSystemDefaults();
382 void FontClient::GetDefaultFonts(FontList& defaultFonts)
386 mPlugin->GetDefaultFonts(defaultFonts);
389 void FontClient::FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily)
393 mPlugin->FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
396 void FontClient::FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList)
400 mPlugin->FontPreLoad(fontPathList, memoryFontPathList);
403 void FontClient::InitDefaultFontDescription()
407 mPlugin->InitDefaultFontDescription();
410 void FontClient::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
414 mPlugin->GetDefaultPlatformFontDescription(fontDescription);
417 void FontClient::GetDescription(FontId fontId, FontDescription& fontDescription)
421 mPlugin->GetDescription(fontId, fontDescription);
424 PointSize26Dot6 FontClient::GetPointSize(FontId fontId)
428 return mPlugin->GetPointSize(fontId);
431 bool FontClient::IsCharacterSupportedByFont(FontId fontId, Character character)
435 return mPlugin->IsCharacterSupportedByFont(fontId, character);
438 void FontClient::GetSystemFonts(FontList& systemFonts)
442 mPlugin->GetSystemFonts(systemFonts);
445 FontId FontClient::FindDefaultFont(Character charcode,
446 PointSize26Dot6 requestedPointSize,
451 return mPlugin->FindDefaultFont(charcode,
456 FontId FontClient::FindFallbackFont(Character charcode,
457 const FontDescription& preferredFontDescription,
458 PointSize26Dot6 requestedPointSize,
463 return mPlugin->FindFallbackFont(charcode,
464 preferredFontDescription,
469 bool FontClient::IsScalable(const FontPath& path)
473 return mPlugin->IsScalable(path);
476 bool FontClient::IsScalable(const FontDescription& fontDescription)
480 return mPlugin->IsScalable(fontDescription);
483 void FontClient::GetFixedSizes(const FontPath& path, Dali::Vector<PointSize26Dot6>& sizes)
487 mPlugin->GetFixedSizes(path, sizes);
490 void FontClient::GetFixedSizes(const FontDescription& fontDescription,
491 Dali::Vector<PointSize26Dot6>& sizes)
495 mPlugin->GetFixedSizes(fontDescription, sizes);
498 bool FontClient::HasItalicStyle(FontId fontId) const
504 return mPlugin->HasItalicStyle(fontId);
507 FontId FontClient::GetFontId(const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex faceIndex)
511 return mPlugin->GetFontIdByPath(path,
517 FontId FontClient::GetFontId(const FontDescription& fontDescription,
518 PointSize26Dot6 requestedPointSize,
523 return mPlugin->GetFontId(fontDescription,
528 FontId FontClient::GetFontId(const BitmapFont& bitmapFont)
532 return mPlugin->GetFontId(bitmapFont);
535 void FontClient::GetFontMetrics(FontId fontId, FontMetrics& metrics)
539 mPlugin->GetFontMetrics(fontId, metrics);
542 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode)
546 return mPlugin->GetGlyphIndex(fontId, charcode);
549 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode, Character variantSelector)
553 return mPlugin->GetGlyphIndex(fontId, charcode, variantSelector);
556 bool FontClient::GetGlyphMetrics(GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal)
560 return mPlugin->GetGlyphMetrics(array, size, type, horizontal);
563 void FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::GlyphBufferData& data, int outlineWidth)
567 mPlugin->CreateBitmap(fontId, glyphIndex, isItalicRequired, isBoldRequired, data, outlineWidth);
570 PixelData FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
574 return mPlugin->CreateBitmap(fontId, glyphIndex, outlineWidth);
577 void FontClient::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
581 mPlugin->CreateVectorBlob(fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
584 const GlyphInfo& FontClient::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
588 return mPlugin->GetEllipsisGlyph(requestedPointSize);
591 bool FontClient::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
595 return mPlugin->IsColorGlyph(fontId, glyphIndex);
598 GlyphIndex FontClient::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
602 return mPlugin->CreateEmbeddedItem(description, pixelFormat);
605 void FontClient::EnableAtlasLimitation(bool enabled)
608 return mPlugin->EnableAtlasLimitation(enabled);
611 bool FontClient::IsAtlasLimitationEnabled() const
615 return mPlugin->IsAtlasLimitationEnabled();
617 return TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED;
620 Size FontClient::GetMaximumTextAtlasSize() const
624 return mPlugin->GetMaximumTextAtlasSize();
626 return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
629 Size FontClient::GetDefaultTextAtlasSize() const
633 return mPlugin->GetDefaultTextAtlasSize();
635 return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
638 Size FontClient::GetCurrentMaximumBlockSizeFitInAtlas() const
642 return mPlugin->GetCurrentMaximumBlockSizeFitInAtlas();
644 return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
647 bool FontClient::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
650 return mPlugin->SetCurrentMaximumBlockSizeFitInAtlas(currentMaximumBlockSizeFitInAtlas);
653 uint32_t FontClient::GetNumberOfPointsPerOneUnitOfPointSize() const
657 return mPlugin->GetNumberOfPointsPerOneUnitOfPointSize();
659 return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
663 FT_FaceRec_* FontClient::GetFreetypeFace(FontId fontId)
667 return mPlugin->GetFreetypeFace(fontId);
670 FontDescription::Type FontClient::GetFontType(FontId fontId)
674 return mPlugin->GetFontType(fontId);
677 bool FontClient::AddCustomFontDirectory(const FontPath& path)
681 return mPlugin->AddCustomFontDirectory(path);
684 HarfBuzzFontHandle FontClient::GetHarfBuzzFont(FontId fontId)
688 return mPlugin->GetHarfBuzzFont(fontId);
691 void FontClient::CreatePlugin()
693 std::scoped_lock lock(gMutex);
696 mPlugin = new Plugin(mDpiHorizontal, mDpiVertical);
700 } // namespace Internal
702 } // namespace TextAbstraction