[AT-SPI] Rework intercepting key events
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-face-glyph-cache-manager.cpp
1 /*
2  * Copyright (c) 2023 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 // CLASS HEADER
18 #include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
19
20 // INTERNAL INCLUDES
21 #include <dali/integration-api/debug.h>
22 #include <dali/internal/imaging/common/image-operations.h>
23 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
24
25 // EXTERNAL INCLUDES
26 #include FT_BITMAP_H
27
28 #if defined(DEBUG_ENABLED)
29 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
30 #endif
31
32 namespace Dali::TextAbstraction::Internal
33 {
34 namespace
35 {
36 constexpr uint32_t THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION = 8; // The smallest width of glyph that we use RLE4 method.
37 } // namespace
38
39 GlyphCacheManager::GlyphCacheManager(std::size_t maxNumberOfGlyphCache)
40 : mGlyphCacheMaxSize(maxNumberOfGlyphCache),
41   mLRUGlyphCache(mGlyphCacheMaxSize)
42 {
43   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager Create with maximum size : %d\n", static_cast<int>(mGlyphCacheMaxSize));
44 }
45
46 GlyphCacheManager::~GlyphCacheManager()
47 {
48   ClearCache();
49 }
50
51 bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
52   const FT_Face      freeTypeFace,
53   const GlyphIndex   index,
54   const FT_Int32     flag,
55   const bool         isBoldRequired,
56   GlyphCacheDataPtr& glyphDataPtr,
57   FT_Error&          error)
58 {
59   // Append some error value here instead of FT_Err_Ok.
60   error = static_cast<FT_Error>(-1);
61
62   const GlyphCacheKey key  = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired);
63   auto                iter = mLRUGlyphCache.Find(key);
64
65   if(iter == mLRUGlyphCache.End())
66   {
67     // If cache size is full, remove oldest glyph.
68     if(mLRUGlyphCache.IsFull())
69     {
70       auto removedData = mLRUGlyphCache.Pop();
71
72       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Remove oldest cache for glyph : %p\n", removedData->mGlyph);
73     }
74
75     // Create new GlyphCacheData.
76     glyphDataPtr = std::make_shared<GlyphCacheData>();
77
78     GlyphCacheData& glyphData = *glyphDataPtr.get();
79
80     const bool loadSuccess = LoadGlyphDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphData, error);
81     if(loadSuccess)
82     {
83       // Copy and cached data.
84       mLRUGlyphCache.Push(key, glyphDataPtr);
85
86       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
87     }
88
89     return loadSuccess;
90   }
91   else
92   {
93     error = FT_Err_Ok;
94
95     // We already notify that we use this glyph. And now, copy cached data.
96     glyphDataPtr = mLRUGlyphCache.GetElement(iter);
97
98     DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast<int>(flag), isBoldRequired, glyphDataPtr->mIsBitmap, glyphDataPtr->mGlyph);
99     return true;
100   }
101 }
102
103 bool GlyphCacheManager::LoadGlyphDataFromIndex(
104   const FT_Face    freeTypeFace,
105   const GlyphIndex index,
106   const FT_Int32   flag,
107   const bool       isBoldRequired,
108   GlyphCacheData&  glyphData,
109   FT_Error&        error)
110 {
111   error = FT_Load_Glyph(freeTypeFace, index, flag);
112   if(FT_Err_Ok == error)
113   {
114     glyphData.mStyleFlags = freeTypeFace->style_flags;
115
116     const bool isEmboldeningRequired = isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
117     if(isEmboldeningRequired)
118     {
119       // Does the software bold.
120       FT_GlyphSlot_Embolden(freeTypeFace->glyph);
121     }
122
123     glyphData.mGlyphMetrics = freeTypeFace->glyph->metrics;
124     glyphData.mIsBitmap     = false;
125     // Load glyph
126     error = FT_Get_Glyph(freeTypeFace->glyph, &glyphData.mGlyph);
127
128     if(glyphData.mGlyph->format == FT_GLYPH_FORMAT_BITMAP)
129     {
130       // Copy original glyph infomation. Due to we use union, we should keep original handle.
131       FT_Glyph bitmapGlyph = glyphData.mGlyph;
132
133       // Copy rendered bitmap
134       // TODO : Is there any way to keep bitmap buffer without copy?
135       glyphData.mBitmap  = new FT_Bitmap();
136       *glyphData.mBitmap = freeTypeFace->glyph->bitmap;
137
138       // New allocate buffer
139       size_t bufferSize = 0;
140       switch(glyphData.mBitmap->pixel_mode)
141       {
142         case FT_PIXEL_MODE_GRAY:
143         {
144           if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width))
145           {
146             bufferSize = static_cast<size_t>(glyphData.mBitmap->width) * static_cast<size_t>(glyphData.mBitmap->rows);
147           }
148           break;
149         }
150 #ifdef FREETYPE_BITMAP_SUPPORT
151         case FT_PIXEL_MODE_BGRA:
152         {
153           if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width << 2u))
154           {
155             bufferSize = (static_cast<size_t>(glyphData.mBitmap->width) * static_cast<size_t>(glyphData.mBitmap->rows)) << 2u;
156           }
157           break;
158         }
159 #endif
160         default:
161         {
162           DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::LoadGlyphDataFromIndex. FontClient Unable to create Bitmap of this PixelType\n");
163           break;
164         }
165       }
166
167       if(bufferSize > 0)
168       {
169         glyphData.mIsBitmap       = true;
170         glyphData.mBitmap->buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
171         memcpy(glyphData.mBitmap->buffer, freeTypeFace->glyph->bitmap.buffer, bufferSize);
172       }
173       else
174       {
175         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::LoadGlyphDataFromIndex. Bitmap glyph buffer size is zero\n");
176         delete glyphData.mBitmap;
177         glyphData.mBitmap = nullptr;
178         error             = static_cast<FT_Error>(-1);
179       }
180
181       // Release glyph data.
182       FT_Done_Glyph(bitmapGlyph);
183     }
184
185     if(FT_Err_Ok == error)
186     {
187       return true;
188     }
189   }
190   return false;
191 }
192
193 void GlyphCacheManager::ResizeBitmapGlyph(
194   const FT_Face    freeTypeFace,
195   const GlyphIndex index,
196   const FT_Int32   flag,
197   const bool       isBoldRequired,
198   const uint32_t   desiredWidth,
199   const uint32_t   desiredHeight)
200 {
201   if(desiredWidth * desiredHeight <= 0)
202   {
203     // Skip this API if desired size is zero
204     return;
205   }
206   FT_Error          error;
207   GlyphCacheDataPtr glyphDataPtr;
208   if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphDataPtr, error))
209   {
210     GlyphCacheData& glyphData = *glyphDataPtr.get();
211     if(DALI_LIKELY(glyphData.mIsBitmap && glyphData.mBitmap))
212     {
213       const bool requiredResize = (glyphData.mBitmap->rows != desiredHeight) || (glyphData.mBitmap->width != desiredWidth);
214       if(requiredResize)
215       {
216         const ImageDimensions inputDimensions(glyphData.mBitmap->width, glyphData.mBitmap->rows);
217         const ImageDimensions desiredDimensions(desiredWidth, desiredHeight);
218
219         uint8_t* desiredBuffer = nullptr;
220
221         switch(glyphData.mBitmap->pixel_mode)
222         {
223           case FT_PIXEL_MODE_GRAY:
224           {
225             if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width))
226             {
227               desiredBuffer = (uint8_t*)malloc(desiredWidth * desiredHeight * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
228               // Resize bitmap here.
229               Dali::Internal::Platform::LanczosSample1BPP(glyphData.mBitmap->buffer,
230                                                           inputDimensions,
231                                                           glyphData.mBitmap->width,
232                                                           desiredBuffer,
233                                                           desiredDimensions);
234             }
235             break;
236           }
237 #ifdef FREETYPE_BITMAP_SUPPORT
238           case FT_PIXEL_MODE_BGRA:
239           {
240             if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width << 2u))
241             {
242               desiredBuffer = (uint8_t*)malloc((desiredWidth * desiredHeight * sizeof(uint8_t)) << 2u); // @note The caller is responsible for deallocating the bitmap data using free.
243               // Resize bitmap here.
244               Dali::Internal::Platform::LanczosSample4BPP(glyphData.mBitmap->buffer,
245                                                           inputDimensions,
246                                                           glyphData.mBitmap->width,
247                                                           desiredBuffer,
248                                                           desiredDimensions);
249             }
250             break;
251           }
252 #endif
253           default:
254           {
255             DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::ResizeBitmapGlyph. FontClient Unable to create Bitmap of this PixelType\n");
256             break;
257           }
258         }
259
260         if(desiredBuffer)
261         {
262           // Success to resize bitmap glyph.
263           // Release origin bitmap buffer.
264           free(glyphData.mBitmap->buffer);
265
266           // Replace as desired buffer and size.
267           glyphData.mBitmap->buffer = desiredBuffer;
268           glyphData.mBitmap->width  = desiredWidth;
269           glyphData.mBitmap->rows   = desiredHeight;
270           switch(glyphData.mBitmap->pixel_mode)
271           {
272             case FT_PIXEL_MODE_GRAY:
273             {
274               glyphData.mBitmap->pitch = desiredWidth;
275               break;
276             }
277 #ifdef FREETYPE_BITMAP_SUPPORT
278             case FT_PIXEL_MODE_BGRA:
279             {
280               glyphData.mBitmap->pitch = desiredWidth << 2u;
281               break;
282             }
283 #endif
284           }
285         }
286       }
287     }
288   }
289 }
290
291 void GlyphCacheManager::CacheRenderedGlyphBuffer(
292   const FT_Face               freeTypeFace,
293   const GlyphIndex            index,
294   const FT_Int32              flag,
295   const bool                  isBoldRequired,
296   const FT_Bitmap&            srcBitmap,
297   const CompressionPolicyType policy)
298 {
299   if(srcBitmap.width * srcBitmap.rows <= 0)
300   {
301     // Skip this API if rendered bitmap size is zero
302     return;
303   }
304   FT_Error          error;
305   GlyphCacheDataPtr glyphDataPtr;
306   if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphDataPtr, error))
307   {
308     GlyphCacheData& glyphData = *glyphDataPtr.get();
309     if(DALI_LIKELY(!glyphData.mIsBitmap && glyphData.mRenderedBuffer == nullptr))
310     {
311       glyphData.mRenderedBuffer = new TextAbstraction::GlyphBufferData();
312       if(DALI_UNLIKELY(!glyphData.mRenderedBuffer))
313       {
314         DALI_LOG_ERROR("Allocate GlyphBufferData failed\n");
315         return;
316       }
317
318       TextAbstraction::GlyphBufferData& renderBuffer = *glyphData.mRenderedBuffer;
319
320       // Set basic informations.
321       renderBuffer.width  = srcBitmap.width;
322       renderBuffer.height = srcBitmap.rows;
323
324       switch(srcBitmap.pixel_mode)
325       {
326         case FT_PIXEL_MODE_GRAY:
327         {
328           renderBuffer.format = Pixel::L8;
329
330           if(policy == CompressionPolicyType::SPEED)
331           {
332             // If policy is SPEED, we will not compress bitmap.
333             renderBuffer.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
334           }
335           else
336           {
337             // If small enough glyph, compress as BPP4 method.
338             if(srcBitmap.width < THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION)
339             {
340               renderBuffer.compressionType = TextAbstraction::GlyphBufferData::CompressionType::BPP_4;
341             }
342             else
343             {
344               renderBuffer.compressionType = TextAbstraction::GlyphBufferData::CompressionType::RLE_4;
345             }
346           }
347
348           const auto compressedBufferSize = TextAbstraction::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
349           if(DALI_UNLIKELY(compressedBufferSize == 0u))
350           {
351             DALI_ASSERT_DEBUG(0 == "Compress failed at FT_PIXEL_MODE_GRAY");
352             DALI_LOG_ERROR("Compress failed. Ignore cache\n");
353             delete glyphData.mRenderedBuffer;
354             glyphData.mRenderedBuffer = nullptr;
355             return;
356           }
357           break;
358         }
359 #ifdef FREETYPE_BITMAP_SUPPORT
360         case FT_PIXEL_MODE_BGRA:
361         {
362           // Copy buffer without compress
363           renderBuffer.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
364           renderBuffer.format          = Pixel::BGRA8888;
365
366           const auto compressedBufferSize = TextAbstraction::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
367           if(DALI_UNLIKELY(compressedBufferSize == 0u))
368           {
369             DALI_ASSERT_DEBUG(0 == "Compress failed at FT_PIXEL_MODE_BGRA");
370             DALI_LOG_ERROR("Compress failed. Ignore cache\n");
371             delete glyphData.mRenderedBuffer;
372             glyphData.mRenderedBuffer = nullptr;
373             return;
374           }
375           break;
376         }
377 #endif
378         default:
379         {
380           DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::CacheRenderedGlyphBuffer. FontClient Unable to create Bitmap of this PixelType\n");
381           delete glyphData.mRenderedBuffer;
382           glyphData.mRenderedBuffer = nullptr;
383           break;
384         }
385       }
386     }
387   }
388 }
389
390 void GlyphCacheManager::RemoveGlyphFromFace(const FT_Face freeTypeFace)
391 {
392   uint32_t removedItemCount = 0;
393
394   auto endIter = mLRUGlyphCache.End();
395   for(auto iter = mLRUGlyphCache.Begin(); iter != endIter;)
396   {
397     // Check whether this cached item has inputed freeTypeFace as key.
398     auto keyFace = mLRUGlyphCache.GetKey(iter).mFreeTypeFace;
399     if(keyFace == freeTypeFace)
400     {
401       ++removedItemCount;
402       iter = mLRUGlyphCache.Erase(iter);
403     }
404     else
405     {
406       ++iter;
407     }
408   }
409
410   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::RemoveGlyphFromFace. Remove all cached glyph with face : %p, removed glyph count : %u\n", freeTypeFace, removedItemCount);
411 }
412
413 void GlyphCacheManager::ClearCache(const std::size_t remainCount)
414 {
415   if(remainCount == 0u)
416   {
417     // Clear all cache.
418     mLRUGlyphCache.Clear();
419   }
420   else
421   {
422     // While the cache count is bigger than remainCount, remove oldest glyph.
423     while(mLRUGlyphCache.Count() > remainCount)
424     {
425       auto removedData = mLRUGlyphCache.Pop();
426
427       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::ClearCache[%zu / %zu]. Remove oldest cache for glyph : %p\n", mLRUGlyphCache.Count(), remainCount, removedData->mGlyph);
428     }
429   }
430 }
431
432 // GlyphCacheManager::GlyphCacheData
433
434 void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
435 {
436   if(mIsBitmap && mBitmap)
437   {
438     // Created FT_Bitmap object must be released with FT_Bitmap_Done
439     // But, this class's mBitmap it not an actual FT_Bitmap object. So free buffer is enough.
440     free(mBitmap->buffer); // This buffer created by malloc
441
442     delete mBitmap;
443     mBitmap = nullptr;
444   }
445   else if(mGlyph)
446   {
447     // Created FT_Glyph object must be released with FT_Done_Glyph
448     FT_Done_Glyph(mGlyph);
449     mGlyph = nullptr;
450   }
451
452   if(mRenderedBuffer)
453   {
454     delete mRenderedBuffer;
455     mRenderedBuffer = nullptr;
456   }
457
458   mStyleFlags = 0;
459 }
460
461 GlyphCacheManager::GlyphCacheData::GlyphCacheData()
462 : mGlyph{nullptr},
463   mGlyphMetrics{},
464   mStyleFlags{0},
465   mIsBitmap{false},
466   mRenderedBuffer{nullptr}
467 {
468 }
469
470 GlyphCacheManager::GlyphCacheData::~GlyphCacheData()
471 {
472   ReleaseGlyphData();
473 }
474
475 GlyphCacheManager::GlyphCacheData::GlyphCacheData(GlyphCacheData&& rhs) noexcept
476 : mGlyph{nullptr},
477   mGlyphMetrics{},
478   mStyleFlags{0},
479   mIsBitmap{false},
480   mRenderedBuffer{nullptr}
481 {
482   *this = std::move(rhs);
483 }
484
485 GlyphCacheManager::GlyphCacheData& GlyphCacheManager::GlyphCacheData::operator=(GlyphCacheData&& rhs) noexcept
486 {
487   // Self-assignment detection
488   if(this == &rhs)
489   {
490     return *this;
491   }
492
493   // Delete self data first.
494   ReleaseGlyphData();
495
496   mIsBitmap = false;
497
498   if(rhs.mIsBitmap && rhs.mBitmap)
499   {
500     mIsBitmap = true;
501     mBitmap   = rhs.mBitmap;
502
503     rhs.mBitmap = nullptr;
504   }
505   else if(rhs.mGlyph)
506   {
507     mGlyph = rhs.mGlyph;
508
509     rhs.mGlyph = nullptr;
510   }
511   else
512   {
513     mGlyph = nullptr;
514   }
515
516   if(rhs.mRenderedBuffer)
517   {
518     mRenderedBuffer     = rhs.mRenderedBuffer;
519     rhs.mRenderedBuffer = nullptr;
520   }
521   else
522   {
523     mRenderedBuffer = nullptr;
524   }
525
526   mStyleFlags = rhs.mStyleFlags;
527   return *this;
528 }
529
530 } // namespace Dali::TextAbstraction::Internal