Merge "DragAndDrop: clean up drop target's resources when drag window is left" into...
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-face-cache-item.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 // EXTERNAL HEADERS
18 #include <dali/integration-api/debug.h>
19
20 // INTERNAL HEADERS
21 #include <dali/devel-api/adaptor-framework/environment-variable.h>
22 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
23 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
24
25 #if defined(DEBUG_ENABLED)
26 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
27 #endif
28
29 namespace Dali::TextAbstraction::Internal
30 {
31 namespace
32 {
33 const float FROM_266        = 1.0f / 64.0f;
34 const float POINTS_PER_INCH = 72.f;
35
36 /**
37  * @brief Maximum size of glyph cache per each font face.
38  */
39 constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX         = 128;
40 constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 2u;
41
42 constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
43
44 /**
45  * @brief Get maximum size of glyph cache size from environment.
46  * If not settuped, default as 128.
47  * @note This value fixed when we call it first time.
48  * @return The max size of glyph cache.
49  */
50 size_t GetMaxNumberOfGlyphCache()
51 {
52   using Dali::EnvironmentVariable::GetEnvironmentVariable;
53   static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV);
54   static auto number       = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX;
55   return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number;
56 }
57 } // namespace
58
59 FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
60                                      FT_Face            ftFace,
61                                      const FontPath&    path,
62                                      PointSize26Dot6    requestedPointSize,
63                                      FaceIndex          face,
64                                      const FontMetrics& metrics)
65 : mFreeTypeLibrary(freeTypeLibrary),
66   mFreeTypeFace(ftFace),
67   mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())),
68   mPath(path),
69   mRequestedPointSize(requestedPointSize),
70   mFaceIndex(face),
71   mMetrics(metrics),
72   mCharacterSet(nullptr),
73   mFixedSizeIndex(0),
74   mFixedWidthPixels(0.f),
75   mFixedHeightPixels(0.f),
76   mVectorFontId(0u),
77   mFontId(0u),
78   mIsFixedSizeBitmap(false),
79   mHasColorTables(false)
80 {
81 }
82
83 FontFaceCacheItem::FontFaceCacheItem(FT_Library&        freeTypeLibrary,
84                                      FT_Face            ftFace,
85                                      const FontPath&    path,
86                                      PointSize26Dot6    requestedPointSize,
87                                      FaceIndex          face,
88                                      const FontMetrics& metrics,
89                                      int                fixedSizeIndex,
90                                      float              fixedWidth,
91                                      float              fixedHeight,
92                                      bool               hasColorTables)
93 : mFreeTypeLibrary(freeTypeLibrary),
94   mFreeTypeFace(ftFace),
95   mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())),
96   mPath(path),
97   mRequestedPointSize(requestedPointSize),
98   mFaceIndex(face),
99   mMetrics(metrics),
100   mCharacterSet(nullptr),
101   mFixedSizeIndex(fixedSizeIndex),
102   mFixedWidthPixels(fixedWidth),
103   mFixedHeightPixels(fixedHeight),
104   mVectorFontId(0u),
105   mFontId(0u),
106   mIsFixedSizeBitmap(true),
107   mHasColorTables(hasColorTables)
108 {
109 }
110
111 // Move constructor. font client plugin container may call this.
112 // Note that we make nullptr of some reference sensitive values here.
113 FontFaceCacheItem::FontFaceCacheItem(FontFaceCacheItem&& rhs)
114 : mFreeTypeLibrary(rhs.mFreeTypeLibrary)
115 {
116   mFreeTypeFace       = rhs.mFreeTypeFace;
117   mGlyphCacheManager  = rhs.mGlyphCacheManager;
118   mPath               = std::move(rhs.mPath);
119   mRequestedPointSize = rhs.mRequestedPointSize;
120   mFaceIndex          = rhs.mFaceIndex;
121   mMetrics            = rhs.mMetrics;
122   mCharacterSet       = rhs.mCharacterSet;
123   mFixedSizeIndex     = rhs.mFixedSizeIndex;
124   mFixedWidthPixels   = rhs.mFixedWidthPixels;
125   mFixedHeightPixels  = rhs.mFixedWidthPixels;
126   mVectorFontId       = rhs.mVectorFontId;
127   mFontId             = rhs.mFontId;
128   mIsFixedSizeBitmap  = rhs.mIsFixedSizeBitmap;
129   mHasColorTables     = rhs.mHasColorTables;
130
131   rhs.mGlyphCacheManager = nullptr;
132   rhs.mFreeTypeFace      = nullptr;
133 }
134
135 FontFaceCacheItem::~FontFaceCacheItem()
136 {
137   // delete glyph cache manager before free face.
138   if(mGlyphCacheManager)
139   {
140     delete mGlyphCacheManager;
141   }
142
143   // Free face.
144   if(mFreeTypeFace)
145   {
146     FT_Done_Face(mFreeTypeFace);
147   }
148 }
149
150 void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
151 {
152   metrics = mMetrics;
153
154   // Adjust the metrics if the fixed-size font should be down-scaled
155   if(mIsFixedSizeBitmap)
156   {
157     const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
158
159     if(desiredFixedSize > 0.f)
160     {
161       const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
162
163       metrics.ascender           = round(metrics.ascender * scaleFactor);
164       metrics.descender          = round(metrics.descender * scaleFactor);
165       metrics.height             = round(metrics.height * scaleFactor);
166       metrics.underlinePosition  = metrics.underlinePosition * scaleFactor;
167       metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
168     }
169   }
170 }
171
172 bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyphInfo, unsigned int dpiVertical, bool horizontal) const
173 {
174   bool success(true);
175
176   GlyphCacheManager::GlyphCacheData glyphData;
177   FT_Error                          error;
178
179 #ifdef FREETYPE_BITMAP_SUPPORT
180   // Check to see if we should be loading a Fixed Size bitmap?
181   if(mIsFixedSizeBitmap)
182   {
183     FT_Select_Size(mFreeTypeFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
184     mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_COLOR, false, glyphData, error);
185
186     if(FT_Err_Ok == error)
187     {
188       glyphInfo.width    = mFixedWidthPixels;
189       glyphInfo.height   = mFixedHeightPixels;
190       glyphInfo.advance  = mFixedWidthPixels;
191       glyphInfo.xBearing = 0.0f;
192
193       const auto& metrics = glyphData.mGlyphMetrics;
194
195       if(horizontal)
196       {
197         glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
198       }
199       else
200       {
201         glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
202       }
203
204       // Adjust the metrics if the fixed-size font should be down-scaled
205       const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
206
207       if(desiredFixedSize > 0.f)
208       {
209         const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
210         glyphInfo.width         = round(glyphInfo.width * scaleFactor);
211         glyphInfo.height        = round(glyphInfo.height * scaleFactor);
212         glyphInfo.advance       = round(glyphInfo.advance * scaleFactor);
213         glyphInfo.xBearing      = round(glyphInfo.xBearing * scaleFactor);
214         glyphInfo.yBearing      = round(glyphInfo.yBearing * scaleFactor);
215
216         glyphInfo.scaleFactor = scaleFactor;
217       }
218     }
219     else
220     {
221       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
222       success = false;
223     }
224   }
225   else
226 #endif
227   {
228     // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
229     // i.e. with the SNum-3R font.
230     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
231     mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error);
232
233     // Keep the width of the glyph before doing the software emboldening.
234     // It will be used to calculate a scale factor to be applied to the
235     // advance as Harfbuzz doesn't apply any SW emboldening to calculate
236     // the advance of the glyph.
237
238     if(FT_Err_Ok == error)
239     {
240       const auto& metrics = glyphData.mGlyphMetrics;
241
242       glyphInfo.width  = static_cast<float>(metrics.width) * FROM_266;
243       glyphInfo.height = static_cast<float>(metrics.height) * FROM_266;
244       if(horizontal)
245       {
246         glyphInfo.xBearing += static_cast<float>(metrics.horiBearingX) * FROM_266;
247         glyphInfo.yBearing += static_cast<float>(metrics.horiBearingY) * FROM_266;
248       }
249       else
250       {
251         glyphInfo.xBearing += static_cast<float>(metrics.vertBearingX) * FROM_266;
252         glyphInfo.yBearing += static_cast<float>(metrics.vertBearingY) * FROM_266;
253       }
254
255       const bool isEmboldeningRequired = glyphInfo.isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
256       if(isEmboldeningRequired)
257       {
258         // Get dummy glyph data without embolden.
259         GlyphCacheManager::GlyphCacheData dummyData;
260         if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error))
261         {
262           // If the glyph is emboldened by software, the advance is multiplied by a
263           // scale factor to make it slightly bigger.
264           const float width = static_cast<float>(dummyData.mGlyphMetrics.width) * FROM_266;
265           if(!EqualsZero(width))
266           {
267             glyphInfo.advance *= (glyphInfo.width / width);
268           }
269         }
270       }
271
272       // Use the bounding box of the bitmap to correct the metrics.
273       // For some fonts i.e the SNum-3R the metrics need to be corrected,
274       // otherwise the glyphs 'dance' up and down depending on the
275       // font's point size.
276       FT_Glyph glyph = glyphData.mGlyph;
277
278       FT_BBox bbox;
279       FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
280
281       const float descender = glyphInfo.height - glyphInfo.yBearing;
282       glyphInfo.height      = (bbox.yMax - bbox.yMin) * FROM_266;
283       glyphInfo.yBearing    = glyphInfo.height - round(descender);
284     }
285     else
286     {
287       success = false;
288     }
289   }
290   return success;
291 }
292
293 /**
294  * @brief Create a bitmap representation of a glyph from a face font
295  *
296  * @param[in]  glyphIndex        The index of a glyph within the specified font.
297  * @param[in]  isItalicRequired  Whether the glyph requires italic style.
298  * @param[in]  isBoldRequired    Whether the glyph requires bold style.
299  * @param[out] data              The bitmap data.
300  * @param[in]  outlineWidth      The width of the glyph outline in pixels.
301  */
302 void FontFaceCacheItem::CreateBitmap(
303   GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
304 {
305   GlyphCacheManager::GlyphCacheData glyphData;
306   FT_Error                          error;
307   // For the software italics.
308   bool isShearRequired = false;
309
310 #ifdef FREETYPE_BITMAP_SUPPORT
311   // Check to see if this is fixed size bitmap
312   if(mIsFixedSizeBitmap)
313   {
314     mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_COLOR, isBoldRequired, glyphData, error);
315   }
316   else
317 #endif
318   {
319     // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
320     // i.e. with the SNum-3R font.
321     // @todo: add an option to use the FT_LOAD_DEFAULT if required?
322     mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_NO_AUTOHINT, isBoldRequired, glyphData, error);
323   }
324   if(FT_Err_Ok == error)
325   {
326     if(isItalicRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_ITALIC))
327     {
328       // Will do the software italic.
329       isShearRequired = true;
330     }
331
332     // Convert to bitmap if necessary
333     if(!glyphData.mIsBitmap)
334     {
335       FT_Glyph glyph = glyphData.mGlyph;
336
337       DALI_ASSERT_ALWAYS(glyph->format != FT_GLYPH_FORMAT_BITMAP && "Something wrong with cashing. Some bitmap glyph cached failed.");
338
339       int  offsetX = 0, offsetY = 0;
340       bool isOutlineGlyph       = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
341       bool isStrokeGlyphSuccess = false;
342
343       // Create a bitmap for the outline
344       if(isOutlineGlyph)
345       {
346         // Retrieve the horizontal and vertical distance from the current pen position to the
347         // left and top border of the glyph bitmap for a normal glyph before applying the outline.
348         if(FT_Err_Ok == error)
349         {
350           // Copy new glyph, and keep original cached glyph.
351           error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0);
352           if(FT_Err_Ok == error)
353           {
354             FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
355
356             offsetX = bitmapGlyph->left;
357             offsetY = bitmapGlyph->top;
358
359             // Copied FT_Glyph object must be released with FT_Done_Glyph
360             FT_Done_Glyph(glyph);
361           }
362
363           // Replace as original glyph
364           glyph = glyphData.mGlyph;
365         }
366
367         // Now apply the outline
368
369         // Set up a stroker
370         FT_Stroker stroker;
371         error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
372
373         if(FT_Err_Ok == error)
374         {
375           // Copy glyph pointer for release memory.
376           FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
377           error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 0);
378
379           if(FT_Err_Ok == error)
380           {
381             FT_Stroker_Done(stroker);
382             isStrokeGlyphSuccess = true;
383           }
384           else
385           {
386             DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
387           }
388         }
389         else
390         {
391           DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
392         }
393       }
394
395       // Copy new glyph, and keep original cached glyph.
396       // If we already copy new glyph by stroke, just re-use that.
397       error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, isStrokeGlyphSuccess);
398       if(FT_Err_Ok == error)
399       {
400         FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
401
402         if(isOutlineGlyph)
403         {
404           // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
405           data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
406           data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
407         }
408
409         ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
410
411         // Copied FT_Glyph object must be released with FT_Done_Glyph
412         FT_Done_Glyph(glyph);
413       }
414       else
415       {
416         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
417       }
418     }
419     else
420     {
421       ConvertBitmap(data, *glyphData.mBitmap, isShearRequired);
422     }
423
424     data.isColorEmoji = mIsFixedSizeBitmap;
425   }
426   else
427   {
428     DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
429   }
430 }
431
432 bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
433 {
434   FT_Error error = -1;
435
436 #ifdef FREETYPE_BITMAP_SUPPORT
437   // Check to see if this is fixed size bitmap
438   if(mHasColorTables)
439   {
440     GlyphCacheManager::GlyphCacheData dummyData;
441     mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_COLOR, false, dummyData, error);
442   }
443 #endif
444   return FT_Err_Ok == error;
445 }
446
447 /**
448  * Check if the character is supported by this font
449  * @param[in] character The character to test
450  */
451 bool FontFaceCacheItem::IsCharacterSupported(Character character)
452 {
453   if(nullptr == mCharacterSet)
454   {
455     // Create again the character set.
456     // It can be null if the ResetSystemDefaults() method has been called.
457
458     FontDescription description;
459     description.path   = mPath;
460     description.family = std::move(FontFamily(mFreeTypeFace->family_name));
461     description.weight = FontWeight::NONE;
462     description.width  = FontWidth::NONE;
463     description.slant  = FontSlant::NONE;
464
465     // Note FreeType doesn't give too much info to build a proper font style.
466     if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
467     {
468       description.slant = FontSlant::ITALIC;
469     }
470     if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
471     {
472       description.weight = FontWeight::BOLD;
473     }
474
475     mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
476   }
477
478   return FcCharSetHasChar(mCharacterSet, character);
479 }
480
481 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character) const
482 {
483   return FT_Get_Char_Index(mFreeTypeFace, character);
484 }
485
486 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character, Character variantSelector) const
487 {
488   return FT_Face_GetCharVariantIndex(mFreeTypeFace, character, variantSelector);
489 }
490
491 } // namespace Dali::TextAbstraction::Internal