FontClient plugin cache optimize
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-face-glyph-cache-manager.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 #include <dali/integration-api/debug.h>
18 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
19 #include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
20 #include FT_BITMAP_H
21
22 #if defined(DEBUG_ENABLED)
23 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
24 #endif
25
26 namespace Dali::TextAbstraction::Internal
27 {
28 namespace
29 {
30 } // namespace
31
32 GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache)
33 : mFreeTypeFace(ftFace),
34   mGlyphCacheMaxSize(maxNumberOfGlyphCache),
35   mLRUGlyphCache(mGlyphCacheMaxSize)
36 {
37   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager Create with maximum size : %d\n", static_cast<int>(mGlyphCacheMaxSize));
38 }
39 GlyphCacheManager::~GlyphCacheManager()
40 {
41   while(!mLRUGlyphCache.IsEmpty())
42   {
43     auto removedData = mLRUGlyphCache.Pop();
44
45     // Release Glyph data resource
46     removedData.ReleaseGlyphData();
47   }
48   mLRUGlyphCache.Clear();
49 }
50
51 bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
52   const GlyphIndex& index,
53   const FT_Int32&   flag,
54   const bool&       isBoldRequired,
55   GlyphCacheData&   glyphData,
56   FT_Error&         error)
57 {
58   // Append some error value here instead of FT_Err_Ok.
59   error = static_cast<FT_Error>(-1);
60
61   const GlyphCacheKey key  = GlyphCacheKey(index, flag, isBoldRequired);
62   auto                iter = mLRUGlyphCache.Find(key);
63
64   if(iter == mLRUGlyphCache.End())
65   {
66     // If cache size is full, remove oldest glyph.
67     if(mLRUGlyphCache.IsFull())
68     {
69       auto removedData = mLRUGlyphCache.Pop();
70
71       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Remove oldest cache for glyph : %p\n", removedData.mGlyph);
72
73       // Release Glyph data resource
74       removedData.ReleaseGlyphData();
75     }
76
77     const bool loadSuccess = LoadGlyphDataFromIndex(index, flag, isBoldRequired, glyphData, error);
78     if(loadSuccess)
79     {
80       // Copy and cached data.
81       mLRUGlyphCache.Push(key, glyphData);
82
83       DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
84     }
85
86     return loadSuccess;
87   }
88   else
89   {
90     error = FT_Err_Ok;
91
92     // We already notify that we use this glyph. And now, copy cached data.
93     glyphData = iter->element;
94
95     DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
96     return true;
97   }
98 }
99
100 bool GlyphCacheManager::LoadGlyphDataFromIndex(
101   const GlyphIndex& index,
102   const FT_Int32&   flag,
103   const bool&       isBoldRequired,
104   GlyphCacheData&   glyphData,
105   FT_Error&         error)
106 {
107   error = FT_Load_Glyph(mFreeTypeFace, index, flag);
108   if(FT_Err_Ok == error)
109   {
110     glyphData.mStyleFlags = mFreeTypeFace->style_flags;
111
112     const bool isEmboldeningRequired = isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
113     if(isEmboldeningRequired)
114     {
115       // Does the software bold.
116       FT_GlyphSlot_Embolden(mFreeTypeFace->glyph);
117     }
118
119     glyphData.mGlyphMetrics = mFreeTypeFace->glyph->metrics;
120     glyphData.mIsBitmap     = false;
121     // Load glyph
122     error = FT_Get_Glyph(mFreeTypeFace->glyph, &glyphData.mGlyph);
123
124     if(glyphData.mGlyph->format == FT_GLYPH_FORMAT_BITMAP)
125     {
126       // Copy original glyph infomation. Due to we use union, we should keep original handle.
127       FT_Glyph bitmapGlyph = glyphData.mGlyph;
128
129       // Copy rendered bitmap
130       // TODO : Is there any way to keep bitmap buffer without copy?
131       glyphData.mBitmap  = new FT_Bitmap();
132       *glyphData.mBitmap = mFreeTypeFace->glyph->bitmap;
133
134       // New allocate buffer
135       size_t bufferSize = 0;
136       switch(glyphData.mBitmap->pixel_mode)
137       {
138         case FT_PIXEL_MODE_GRAY:
139         {
140           if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width))
141           {
142             bufferSize = static_cast<size_t>(glyphData.mBitmap->width) * static_cast<size_t>(glyphData.mBitmap->rows);
143           }
144           break;
145         }
146 #ifdef FREETYPE_BITMAP_SUPPORT
147         case FT_PIXEL_MODE_BGRA:
148         {
149           if(glyphData.mBitmap->pitch == static_cast<int>(glyphData.mBitmap->width << 2u))
150           {
151             bufferSize = (static_cast<size_t>(glyphData.mBitmap->width) * static_cast<size_t>(glyphData.mBitmap->rows)) << 2u;
152           }
153           break;
154         }
155 #endif
156         default:
157         {
158           DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::LoadGlyphDataFromIndex. FontClient Unable to create Bitmap of this PixelType\n");
159           break;
160         }
161       }
162
163       if(bufferSize > 0)
164       {
165         glyphData.mIsBitmap       = true;
166         glyphData.mBitmap->buffer = new uint8_t[bufferSize];
167         memcpy(glyphData.mBitmap->buffer, mFreeTypeFace->glyph->bitmap.buffer, bufferSize);
168       }
169       else
170       {
171         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::LoadGlyphDataFromIndex. Bitmap glyph buffer size is zero\n");
172         delete glyphData.mBitmap;
173         glyphData.mBitmap = nullptr;
174         error             = static_cast<FT_Error>(-1);
175       }
176
177       // Release glyph data.
178       FT_Done_Glyph(bitmapGlyph);
179     }
180
181     if(FT_Err_Ok == error)
182     {
183       return true;
184     }
185   }
186   return false;
187 }
188
189 void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
190 {
191   if(mIsBitmap && mBitmap)
192   {
193     // Created FT_Bitmap object must be released with FT_Bitmap_Done
194     delete[] mBitmap->buffer;
195     delete mBitmap;
196     mBitmap = nullptr;
197   }
198   else if(mGlyph)
199   {
200     // Created FT_Glyph object must be released with FT_Done_Glyph
201     FT_Done_Glyph(mGlyph);
202     mGlyph = nullptr;
203   }
204 }
205
206 } // namespace Dali::TextAbstraction::Internal