53d3d0fc2c9da47af601cd9c2a517e7e850fb218
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / harfbuzz-proxy-font.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 // INTERNAL INCLUDES
18 #include <dali/integration-api/debug.h>
19 #include <dali/internal/text/text-abstraction/plugin/harfbuzz-proxy-font.h>
20
21 // EXTERNAL INCLUDES
22 #include FT_GLYPH_H
23 #include <harfbuzz/hb-ft.h>
24 #include <harfbuzz/hb.h>
25
26 #if defined(DEBUG_ENABLED)
27 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
28 #endif
29
30 namespace Dali::TextAbstraction::Internal
31 {
32 /**
33  * @brief Helper class to create and destroy harfbuzz font, and hold data in harfbuzz callback
34  * It also cache informations what harfbuzz font need to be created.
35  */
36 struct HarfBuzzProxyFont::Impl
37 {
38   /**
39    * @brief Constructor.
40    *
41    * @param[in] freeTypeFace The FreeType face.
42    * @param[in] glyphCacheManager Glyph caching system for this harfbuzz font. It will be used as harfbuzz callback data.
43    */
44   Impl(FT_Face freeTypeFace, GlyphCacheManager* glyphCacheManager)
45   : mFreeTypeFace(freeTypeFace),
46     mGlyphCacheManager(glyphCacheManager),
47     mHarfBuzzFont(nullptr)
48   {
49   }
50
51   // Destructor
52   ~Impl()
53   {
54     if(mHarfBuzzFont)
55     {
56       // It will reduce reference of freetype face automatically.
57       hb_font_destroy(mHarfBuzzFont);
58     }
59   }
60
61 public:
62   /**
63    * @brief Create new harfbuzz font.
64    *
65    * @param[in] requestedPointSize The requiested point size of font.
66    * @param[in] horizontalDpi Horizontal DPI.
67    * @param[in] verticalDpi Vertical DPI.
68    */
69   void CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi);
70
71 private:
72   /**
73    * @brief Register harfbuzz callback functions into current harfbuzz font.
74    */
75   void SetHarfBuzzFunctions();
76
77 public:
78   FT_Face            mFreeTypeFace;      ///< The FreeType face. Owned from font-face-cache-item.
79   GlyphCacheManager* mGlyphCacheManager; ///< Glyph caching system for this harfbuzz font. Owned from font-client-plugin-cache-handler.
80
81   hb_font_t* mHarfBuzzFont; ///< Harfbuzz font handle integrated with FT_Face.
82 };
83
84 HarfBuzzProxyFont::HarfBuzzProxyFont(FT_Face freeTypeFace, const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi, GlyphCacheManager* glyphCacheManager)
85 : mHorizontalDpi(horizontalDpi),
86   mVerticalDpi(verticalDpi),
87   mImpl(new Impl(freeTypeFace, glyphCacheManager))
88 {
89   mImpl->CreateHarfBuzzFont(requestedPointSize, mHorizontalDpi, mVerticalDpi);
90 }
91
92 HarfBuzzProxyFont::~HarfBuzzProxyFont()
93 {
94   if(mImpl)
95   {
96     delete mImpl;
97   }
98 }
99
100 HarfBuzzFontHandle HarfBuzzProxyFont::GetHarfBuzzFont() const
101 {
102   if(mImpl)
103   {
104     return static_cast<HarfBuzzFontHandle>(mImpl->mHarfBuzzFont);
105   }
106   return nullptr;
107 }
108
109 // Collection of harfbuzz custom callback functions.
110 // Reference : https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-ft.cc
111 namespace
112 {
113 /**
114  * @brief Get glyph informations by dali glyph cache system.
115  *
116  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
117  * @param[in] glyphIndex Index of glyph.
118  * @param[out] glyphData The result of cached glyph data.
119  * @return True if we success to get some glyph data. False otherwise.
120  */
121 static bool GetGlyphCacheData(void* font_data, const GlyphIndex& glyphIndex, GlyphCacheManager::GlyphCacheData& glyphData)
122 {
123   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
124
125   // Note : HarfBuzz used only FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING internally.
126   if(DALI_LIKELY(impl && impl->mGlyphCacheManager))
127   {
128     FT_Error error;
129     return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(impl->mFreeTypeFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error);
130   }
131   return false;
132 }
133
134 /**
135  * @brief Calculate font extents value both in vertical and horizontal.
136  *
137  * @param[in] font Current harfbuzz font data.
138  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
139  * @param[out] extents Extents value of font. (scale as 26.6)
140  * @param[in] user_data Registered user data.
141  * @return True if we success to get font extents. False otherwise.
142  */
143 static hb_bool_t FontExtentsFunc(hb_font_t* font, void* font_data, hb_font_extents_t* extents, void* user_data)
144 {
145   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
146
147   if(DALI_LIKELY(impl && impl->mFreeTypeFace))
148   {
149     FT_Size_Metrics& ftMetrics = impl->mFreeTypeFace->size->metrics;
150
151     extents->ascender  = ftMetrics.ascender;
152     extents->descender = ftMetrics.descender;
153     extents->line_gap  = ftMetrics.height - (extents->ascender - extents->descender);
154
155     return true;
156   }
157   return false;
158 }
159
160 /**
161  * @brief Convert from character into index of glyph.
162  *
163  * @param[in] font Current harfbuzz font data.
164  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
165  * @param[in] character The value of character what we want to get index.
166  * @param[out] glyphIndex Index of glyph that current font face used.
167  * @param[in] user_data Registered user data.
168  * @return True if we success to convert.
169  */
170 static hb_bool_t GlyphNormalIndexConvertFunc(hb_font_t* font, void* font_data, hb_codepoint_t character, hb_codepoint_t* glyphIndex, void* user_data)
171 {
172   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
173
174   if(DALI_LIKELY(impl && impl->mFreeTypeFace))
175   {
176     *glyphIndex = FT_Get_Char_Index(impl->mFreeTypeFace, character);
177     return *glyphIndex != 0;
178   }
179   return false;
180 }
181
182 /**
183  * @brief Convert from character and variant selector into index of glyph.
184  *
185  * @param[in] font Current harfbuzz font data.
186  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
187  * @param[in] character The value of character what we want to get index.
188  * @param[in] variantSelector Variant selector.
189  * @param[out] glyphIndex Index of glyph that current font face used.
190  * @param[in] user_data Registered user data.
191  * @return True if we success to convert.
192  */
193 static hb_bool_t GlyphVariantIndexConvertFunc(hb_font_t* font, void* font_data, hb_codepoint_t character, hb_codepoint_t variantSelector, hb_codepoint_t* glyphIndex, void* user_data)
194 {
195   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
196
197   if(DALI_LIKELY(impl && impl->mFreeTypeFace))
198   {
199     *glyphIndex = FT_Face_GetCharVariantIndex(impl->mFreeTypeFace, character, variantSelector);
200     return *glyphIndex != 0;
201   }
202   return false;
203 }
204
205 /**
206  * @brief Calculate glyph advance value in horizontal.
207  *
208  * @param[in] font Current harfbuzz font data.
209  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
210  * @param[in] glyphIndex Index of glyph.
211  * @param[in] user_data Registered user data.
212  * @return Horizontal advance value of glyphIndex. (scale as 26.6)
213  */
214 static hb_position_t GlyphHorizontalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data)
215 {
216   // Output data stored here.
217   GlyphCacheManager::GlyphCacheData glyphData;
218   if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
219   {
220     // Note : It may return invalid value for fixed size bitmap glyph.
221     // But, Harfbuzz library also return Undefined advanced value if it is fixed size font.
222     // So we'll also ignore that case.
223     return static_cast<hb_position_t>(glyphData.mGlyphMetrics.horiAdvance);
224   }
225   return 0;
226 }
227 /**
228  * @brief Calculate glyph advance value in vertical.
229  *
230  * @param[in] font Current harfbuzz font data.
231  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
232  * @param[in] glyphIndex Index of glyph.
233  * @param[in] user_data Registered user data.
234  * @return Vertical advance value of glyphIndex. (scale as 26.6)
235  */
236 static hb_position_t GlyphVerticalAdvanceFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, void* user_data)
237 {
238   // Output data stored here.
239   GlyphCacheManager::GlyphCacheData glyphData;
240   if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
241   {
242     // Note : It may return invalid value for fixed size bitmap glyph.
243     // But, Harfbuzz library also return Undefined advanced value if it is fixed size font.
244     // So we'll also ignore that case.
245     return static_cast<hb_position_t>(glyphData.mGlyphMetrics.vertAdvance);
246   }
247   return 0;
248 }
249
250 /**
251  * @brief Calculate glyph origin position value in horizontal.
252  *
253  * @param[in] font Current harfbuzz font data.
254  * @param[in] font_data HarfBuzzProxyFont::Impl that register this callback as void* type.
255  * @param[in] glyphIndex Index of glyph.
256  * @param[out] x Origin position x (scale as 26.6)
257  * @param[out] y Origin position y (scale as 26.6)
258  * @param[in] user_data Registered user data.
259  * @return True if we get data successfully. False if some error occured.
260  */
261 static hb_bool_t GlyphHorizontalOriginFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_position_t* x, hb_position_t* y, void* user_data)
262 {
263   // Nothing to do
264   return true;
265 }
266 /**
267  * @brief Calculate glyph origin position value in vertical.
268  *
269  * @param[in] font Current harfbuzz font data.
270  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
271  * @param[in] glyphIndex Index of glyph.
272  * @param[out] x Origin position x (scale as 26.6)
273  * @param[out] y Origin position y (scale as 26.6)
274  * @param[in] user_data Registered user data.
275  * @return True if we get data successfully. False if some error occured.
276  */
277 static hb_bool_t GlyphVerticalOriginFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_position_t* x, hb_position_t* y, void* user_data)
278 {
279   // Output data stored here.
280   GlyphCacheManager::GlyphCacheData glyphData;
281   if(GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
282   {
283     *x = glyphData.mGlyphMetrics.horiBearingX - glyphData.mGlyphMetrics.vertBearingX;
284     *y = glyphData.mGlyphMetrics.horiBearingY + glyphData.mGlyphMetrics.vertBearingY;
285     return true;
286   }
287   return false;
288 }
289
290 /**
291  * @brief Calculate glyph kerning value in horizontal.
292  *
293  * @param[in] font Current harfbuzz font data.
294  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
295  * @param[in] glyphIndex1 First index of glyph to get kerning.
296  * @param[in] glyphIndex2 Second index of glyph to get kerning.
297  * @param[in] user_data Registered user data.
298  * @return Horizontal kerning position. (scale as 26.6)
299  */
300 static hb_position_t GlyphHorizontalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data)
301 {
302   HarfBuzzProxyFont::Impl* impl = reinterpret_cast<HarfBuzzProxyFont::Impl*>(font_data);
303
304   if(DALI_LIKELY(impl && impl->mFreeTypeFace))
305   {
306     FT_Error  error;
307     FT_Vector kerning;
308
309     error = FT_Get_Kerning(impl->mFreeTypeFace, glyphIndex1, glyphIndex2, FT_KERNING_UNSCALED, &kerning);
310     if(error == FT_Err_Ok)
311     {
312       return kerning.x;
313     }
314   }
315   return 0;
316 }
317 /**
318  * @brief Calculate glyph kerning value in vertical.
319  *
320  * @param[in] font Current harfbuzz font data.
321  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
322  * @param[in] glyphIndex1 First index of glyph to get kerning.
323  * @param[in] glyphIndex2 Second index of glyph to get kerning.
324  * @param[in] user_data Registered user data.
325  * @return Vertical kerning position. (scale as 26.6)
326  */
327 static hb_position_t GlyphVerticalKerningFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex1, hb_codepoint_t glyphIndex2, void* user_data)
328 {
329   // FreeType doesn't support vertical kerning
330   return 0;
331 }
332
333 /**
334  * @brief Calculate glyph extents.
335  *
336  * @param[in] font Current harfbuzz font data.
337  * @param[in] font_data HarfBuzzProxyFont::Impl pointer that register this callback as void* type.
338  * @param[in] glyphIndex Index of glyph.
339  * @param[out] extents Extents value of glyph. (scale as 26.6)
340  * @param[in] user_data Registered user data.
341  * @return True if we get data successfully. False if some error occured.
342  */
343 static hb_bool_t GlyphExtentsFunc(hb_font_t* font, void* font_data, hb_codepoint_t glyphIndex, hb_glyph_extents_t* extents, void* user_data)
344 {
345   // Output data stored here.
346   GlyphCacheManager::GlyphCacheData glyphData;
347   if(!GetGlyphCacheData(font_data, static_cast<GlyphIndex>(glyphIndex), glyphData))
348   {
349     extents->x_bearing = glyphData.mGlyphMetrics.horiBearingX;
350     extents->y_bearing = glyphData.mGlyphMetrics.horiBearingY;
351     extents->width     = glyphData.mGlyphMetrics.width;
352     extents->height    = glyphData.mGlyphMetrics.height;
353     return true;
354   }
355   return false;
356 }
357
358 } // namespace
359
360 void HarfBuzzProxyFont::Impl::CreateHarfBuzzFont(const PointSize26Dot6& requestedPointSize, const uint32_t& horizontalDpi, const uint32_t& verticalDpi)
361 {
362   // Destroy previous hb_font_t if exist.
363   if(mHarfBuzzFont)
364   {
365     // It will reduce reference of freetype face automatically.
366     hb_font_destroy(mHarfBuzzFont);
367     mHarfBuzzFont = nullptr;
368   }
369
370   if(mFreeTypeFace)
371   {
372     // Before create hb_font_t, we must set FT_Char_Size
373     FT_Set_Char_Size(mFreeTypeFace,
374                      0u,
375                      requestedPointSize,
376                      horizontalDpi,
377                      verticalDpi);
378
379     // Create font face with increase font face's reference.
380     mHarfBuzzFont = hb_ft_font_create_referenced(mFreeTypeFace);
381
382     SetHarfBuzzFunctions();
383
384     if(mHarfBuzzFont)
385     {
386       DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::HarfBuzzManager::GetHarfBuzzFont. Create new harfbuzz font : %p freetype face : %p. Requested point size : %u, dpi : horizon %u vertial %u\n", mHarfBuzzFont, mFreeTypeFace, requestedPointSize, horizontalDpi, verticalDpi);
387     }
388     else
389     {
390       DALI_LOG_ERROR("ERROR! failed to create harfbuzz font.");
391     }
392   }
393   else
394   {
395     DALI_LOG_ERROR("ERROR! freetype face is null! something unknown problem occured.");
396   }
397 }
398
399 void HarfBuzzProxyFont::Impl::SetHarfBuzzFunctions()
400 {
401   if(mHarfBuzzFont)
402   {
403     hb_font_funcs_t* customFunctions = hb_font_funcs_create();
404
405     if(customFunctions)
406     {
407       // Bind custom functions here
408       hb_font_funcs_set_font_h_extents_func(customFunctions, FontExtentsFunc, 0, 0);
409       hb_font_funcs_set_font_v_extents_func(customFunctions, FontExtentsFunc, 0, 0);
410
411       hb_font_funcs_set_nominal_glyph_func(customFunctions, GlyphNormalIndexConvertFunc, 0, 0);
412       hb_font_funcs_set_variation_glyph_func(customFunctions, GlyphVariantIndexConvertFunc, 0, 0);
413
414       hb_font_funcs_set_glyph_h_advance_func(customFunctions, GlyphHorizontalAdvanceFunc, 0, 0);
415       hb_font_funcs_set_glyph_v_advance_func(customFunctions, GlyphVerticalAdvanceFunc, 0, 0);
416       hb_font_funcs_set_glyph_extents_func(customFunctions, GlyphExtentsFunc, 0, 0);
417
418       hb_font_funcs_set_glyph_h_origin_func(customFunctions, GlyphHorizontalOriginFunc, 0, 0);
419       hb_font_funcs_set_glyph_v_origin_func(customFunctions, GlyphVerticalOriginFunc, 0, 0);
420       hb_font_funcs_set_glyph_h_kerning_func(customFunctions, GlyphHorizontalKerningFunc, 0, 0);
421       hb_font_funcs_set_glyph_v_kerning_func(customFunctions, GlyphVerticalKerningFunc, 0, 0);
422
423       // Set custom functions into our own harfbuzz font
424       hb_font_set_funcs(mHarfBuzzFont, customFunctions, this, 0);
425
426       // We must release functions type what we create.
427       hb_font_funcs_destroy(customFunctions);
428     }
429     else
430     {
431       DALI_LOG_ERROR("ERROR! Fail to create custom harfbuzz functions.");
432
433       // Something wrong while create harfbuzz font. Destory it.
434       // It will reduce reference of freetype face automatically.
435       hb_font_destroy(mHarfBuzzFont);
436       mHarfBuzzFont = nullptr;
437     }
438   }
439 }
440
441 } // namespace Dali::TextAbstraction::Internal