Merge "[AT-SPI] Rework intercepting key events" into devel/master
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-client-utils.cpp
1 /*
2  * Copyright (c) 2024 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/internal/text/text-abstraction/plugin/font-client-utils.h>
18
19 #include <dali/integration-api/debug.h>
20 #include <dali/internal/imaging/common/image-operations.h>
21
22 #include <memory>
23
24 #if defined(DEBUG_ENABLED)
25 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
26
27 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)                                                                            \
28   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, #prefix "  description; family : [%s]\n", fontDescription.family.c_str()); \
29   DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose,                                                                            \
30                 "                 path : [%s]\n"                                                                                 \
31                 "                width : [%s]\n"                                                                                 \
32                 "               weight : [%s]\n"                                                                                 \
33                 "                slant : [%s]\n\n",                                                                              \
34                 fontDescription.path.c_str(),                                                                                    \
35                 FontWidth::Name[fontDescription.width],                                                                          \
36                 FontWeight::Name[fontDescription.weight],                                                                        \
37                 FontSlant::Name[fontDescription.slant])
38
39 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
40   DALI_LOG_INFO(gFontClientLogFilter, Debug::General,               \
41                 "           character : %p\n"                       \
42                 "  requestedPointSize : %d\n"                       \
43                 "         preferColor : %s\n",                      \
44                 charcode,                                           \
45                 requestedPointSize,                                 \
46                 (preferColor ? "true" : "false"))
47
48 #else
49
50 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
51 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
52
53 #endif
54
55 namespace Dali::TextAbstraction::Internal
56 {
57 namespace
58 {
59 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
60
61 // NONE            -1  --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
62 // ULTRA_CONDENSED 50
63 // EXTRA_CONDENSED 63
64 // CONDENSED       75
65 // SEMI_CONDENSED  87
66 // NORMAL         100
67 // SEMI_EXPANDED  113
68 // EXPANDED       125
69 // EXTRA_EXPANDED 150
70 // ULTRA_EXPANDED 200
71 const int          FONT_WIDTH_TYPE_TO_INT[] = {-1, 50, 63, 75, 87, 100, 113, 125, 150, 200};
72 const unsigned int NUM_FONT_WIDTH_TYPE      = sizeof(FONT_WIDTH_TYPE_TO_INT) / sizeof(int);
73
74 // NONE                       -1  --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
75 // THIN                        0
76 // ULTRA_LIGHT, EXTRA_LIGHT   40
77 // LIGHT                      50
78 // DEMI_LIGHT, SEMI_LIGHT     55
79 // BOOK                       75
80 // NORMAL, REGULAR            80
81 // MEDIUM                    100
82 // DEMI_BOLD, SEMI_BOLD      180
83 // BOLD                      200
84 // ULTRA_BOLD, EXTRA_BOLD    205
85 // BLACK, HEAVY, EXTRA_BLACK 210
86 const int          FONT_WEIGHT_TYPE_TO_INT[] = {-1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210};
87 const unsigned int NUM_FONT_WEIGHT_TYPE      = sizeof(FONT_WEIGHT_TYPE_TO_INT) / sizeof(int);
88
89 // NONE             -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
90 // NORMAL, ROMAN     0
91 // ITALIC          100
92 // OBLIQUE         110
93 const int          FONT_SLANT_TYPE_TO_INT[] = {-1, 0, 100, 110};
94 const unsigned int NUM_FONT_SLANT_TYPE      = sizeof(FONT_SLANT_TYPE_TO_INT) / sizeof(int);
95
96 } // namespace
97
98 /**
99  * @brief Returns the FontWidth's enum index for the given width value.
100  *
101  * @param[in] width The width value.
102  *
103  * @return The FontWidth's enum index.
104  */
105 const FontWidth::Type IntToWidthType(int width)
106 {
107   return static_cast<FontWidth::Type>(ValueToIndex(width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u));
108 }
109
110 /**
111  * @brief Returns the FontWeight's enum index for the given weight value.
112  *
113  * @param[in] weight The weight value.
114  *
115  * @return The FontWeight's enum index.
116  */
117 const FontWeight::Type IntToWeightType(int weight)
118 {
119   return static_cast<FontWeight::Type>(ValueToIndex(weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u));
120 }
121
122 /**
123  * @brief Returns the FontSlant's enum index for the given slant value.
124  *
125  * @param[in] slant The slant value.
126  *
127  * @return The FontSlant's enum index.
128  */
129 const FontSlant::Type IntToSlantType(int slant)
130 {
131   return static_cast<FontSlant::Type>(ValueToIndex(slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u));
132 }
133
134 const char* DEFAULT_FONT_FAMILY_NAME("Tizen");
135 const int   DEFAULT_FONT_WIDTH(100);
136 const int   DEFAULT_FONT_WEIGHT(80);
137 const int   DEFAULT_FONT_SLANT(0);
138
139 const std::string_view DefaultFontFamily()
140 {
141   return DEFAULT_FONT_FAMILY_NAME;
142 }
143 const FontWidth::Type DefaultFontWidth()
144 {
145   return IntToWidthType(DEFAULT_FONT_WIDTH);
146 }
147 const FontWeight::Type DefaultFontWeight()
148 {
149   return IntToWeightType(DEFAULT_FONT_WEIGHT);
150 }
151 const FontSlant::Type DefaultFontSlant()
152 {
153   return IntToSlantType(DEFAULT_FONT_SLANT);
154 }
155
156 /**
157  * @brief Copy the color bitmap given in @p srcBuffer to @p data.
158  *
159  * @param[out] data The bitmap data.
160  * @param[in] srcWidth The width of the bitmap.
161  * @param[in] srcHeight The height of the bitmap.
162  * @param[in] srcBuffer The buffer of the bitmap.
163  */
164 void ConvertBitmap(TextAbstraction::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer, const Pixel::Format srcFormat)
165 {
166   // Set the input dimensions.
167   const ImageDimensions inputDimensions(srcWidth, srcHeight);
168
169   // Set the output dimensions.
170   // If the output dimension is not given, the input dimension is set
171   // and won't be downscaling.
172   data.width  = (data.width == 0) ? srcWidth : data.width;
173   data.height = (data.height == 0) ? srcHeight : data.height;
174   const ImageDimensions desiredDimensions(data.width, data.height);
175
176   data.format = srcFormat;
177
178   // Note we don't compress here
179   data.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
180
181   const uint32_t bytePerPixel = Dali::Pixel::GetBytesPerPixel(srcFormat);
182
183   // Creates the output buffer
184   const uint32_t bufferSize = data.width * data.height * bytePerPixel;
185
186   if(inputDimensions == desiredDimensions)
187   {
188     // There isn't downscaling.
189     data.isBufferOwned = false;
190     data.buffer        = const_cast<uint8_t*>(srcBuffer);
191   }
192   else
193   {
194     data.isBufferOwned = true;
195     data.buffer        = (uint8_t*)malloc(bufferSize); // @note The caller is responsible for deallocating the bitmap data using free.
196
197     if(DALI_UNLIKELY(!data.buffer))
198     {
199       DALI_LOG_ERROR("malloc is failed. request malloc size : %u\n", bufferSize);
200       return;
201     }
202     Dali::Internal::Platform::LanczosSample(srcBuffer,
203                                             inputDimensions,
204                                             srcWidth,
205                                             srcFormat,
206                                             data.buffer,
207                                             desiredDimensions);
208   }
209 }
210
211 /**
212  * @brief Copy the FreeType bitmap to the given buffer.
213  *
214  * @param[out] data The bitmap data.
215  * @param[in,out] srcBitmap The FreeType bitmap.
216  * @param[in] isShearRequired Whether the bitmap needs a shear transform (for software italics).
217  * @param[in] moveBuffer Whether the bitmap buffer move. True if just copy buffer pointer. False if we use memcpy. (Default is false.)
218  * @note If you set moveBuffer=true, the bitmap's buffer moved frome srcBitmap to data. So srcBitmap buffer changed as nullptr.
219  */
220 void ConvertBitmap(TextAbstraction::GlyphBufferData& data, FT_Bitmap& srcBitmap, bool isShearRequired, bool moveBuffer)
221 {
222   data.buffer = nullptr;
223   if(srcBitmap.width * srcBitmap.rows > 0)
224   {
225     switch(srcBitmap.pixel_mode)
226     {
227       case FT_PIXEL_MODE_GRAY:
228       {
229         if(srcBitmap.pitch == static_cast<int>(srcBitmap.width))
230         {
231           uint8_t*     pixelsIn = srcBitmap.buffer;
232           unsigned int width    = srcBitmap.width;
233           unsigned     height   = srcBitmap.rows;
234
235           uint8_t* releaseRequiredPixelPtr = nullptr;
236
237           if(isShearRequired)
238           {
239             /**
240              * Glyphs' bitmaps with no slant retrieved from FreeType:
241              * __________     ____
242              * |XXXXXXXX|     |XX|
243              * |   XX   |     |XX|
244              * |   XX   |     |XX|
245              * |   XX   |     |XX|
246              * |   XX   |     |XX|
247              * |   XX   |     |XX|
248              * ----------     ----
249              *
250              * Expected glyphs' bitmaps with italic slant:
251              * ____________   ______
252              * |  XXXXXXXX|   |  XX|
253              * |     XX   |   |  XX|
254              * |    XX    |   | XX |
255              * |    XX    |   | XX |
256              * |   XX     |   |XX  |
257              * |   XX     |   |XX  |
258              * ------------   ------
259              *
260              * Glyphs' bitmaps with software italic slant retrieved from FreeType:
261              * __________     ______
262              * |XXXXXXXX|     |  XX|
263              * |   XX   |     |  XX|
264              * |  XX    |     | XX |
265              * |  XX    |     | XX |
266              * | XX     |     |XX  |
267              * | XX     |     |XX  |
268              * ----------     ------
269              *
270              * This difference in some bitmaps' width causes an overlap of some glyphs. This is the reason why a shear operation is done here instead of relying on the experimental FT_GlyphSlot_Oblique() implementation.
271              */
272             unsigned int widthOut  = 0u;
273             unsigned int heightOut = 0u;
274             uint8_t*     pixelsOut = nullptr;
275
276             Dali::Internal::Platform::HorizontalShear(pixelsIn,
277                                                       width,
278                                                       height,
279                                                       width,
280                                                       1u,
281                                                       -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
282                                                       pixelsOut,
283                                                       widthOut,
284                                                       heightOut);
285
286             if(DALI_LIKELY(pixelsOut))
287             {
288               width  = widthOut;
289               height = heightOut;
290
291               if(moveBuffer)
292               {
293                 releaseRequiredPixelPtr = pixelsIn;
294               }
295               else
296               {
297                 releaseRequiredPixelPtr = pixelsOut;
298               }
299
300               // Change input buffer ptr.
301               pixelsIn = pixelsOut;
302             }
303             else
304             {
305               DALI_LOG_ERROR("ERROR! software italic slant failed!\n");
306             }
307           }
308
309           data.width  = width;
310           data.height = height;
311           data.format = Pixel::L8; // Sets the pixel format.
312
313           // Note we don't compress here
314           data.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
315
316           if(moveBuffer)
317           {
318             data.isBufferOwned = true;
319             data.buffer        = pixelsIn;
320
321             // Happy trick for copyless convert bitmap!
322             srcBitmap.buffer = nullptr;
323           }
324           else
325           {
326             data.isBufferOwned = false;
327             data.buffer        = pixelsIn;
328           }
329
330           if(releaseRequiredPixelPtr)
331           {
332             free(releaseRequiredPixelPtr);
333           }
334         }
335         break;
336       }
337
338 #ifdef FREETYPE_BITMAP_SUPPORT
339       case FT_PIXEL_MODE_BGRA:
340       {
341         if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
342         {
343           // Color glyph doesn't support copyless convert bitmap. Just memcpy
344           ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer, Pixel::BGRA8888);
345         }
346         break;
347       }
348 #endif
349       default:
350       {
351         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n");
352         break;
353       }
354     }
355   }
356 }
357
358 FcPattern* CreateFontFamilyPattern(const FontDescription& fontDescription)
359 {
360   // create the cached font family lookup pattern
361   // a pattern holds a set of names, each name refers to a property of the font
362   FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
363
364   if(!fontFamilyPattern)
365   {
366     return nullptr;
367   }
368
369   // add a property to the pattern for the font family
370   FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fontDescription.family.c_str()));
371
372   // add a property to the pattern for local setting.
373   const char* locale = setlocale(LC_MESSAGES, nullptr);
374   if(locale != nullptr)
375   {
376     FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale));
377   }
378
379   int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
380   if(width < 0)
381   {
382     // Use default.
383     width = DEFAULT_FONT_WIDTH;
384   }
385
386   int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
387   if(weight < 0)
388   {
389     // Use default.
390     weight = DEFAULT_FONT_WEIGHT;
391   }
392
393   int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
394   if(slant < 0)
395   {
396     // Use default.
397     slant = DEFAULT_FONT_SLANT;
398   }
399
400   FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width);
401   FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight);
402   FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant);
403
404   // modify the config, with the mFontFamilyPatterm
405   FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern);
406
407   // provide default values for unspecified properties in the font pattern
408   // e.g. patterns without a specified style or weight are set to Medium
409   FcDefaultSubstitute(fontFamilyPattern);
410
411   return fontFamilyPattern;
412 }
413
414 FcCharSet* CreateCharacterSetFromDescription(const FontDescription& description)
415 {
416   FcCharSet* characterSet = nullptr;
417
418   FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
419
420   if(nullptr != pattern)
421   {
422     FcResult   result = FcResultMatch;
423     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
424
425     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
426
427     // Destroys the created patterns.
428     FcPatternDestroy(match);
429     FcPatternDestroy(pattern);
430   }
431
432   return characterSet;
433 }
434
435 bool MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
436 {
437   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
438
439   FcResult   result = FcResultMatch;
440   FcPattern* match  = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
441
442   const bool matched = nullptr != match;
443   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  pattern matched : %s\n", (matched ? "true" : "false"));
444
445   if(matched)
446   {
447     int width  = 0;
448     int weight = 0;
449     int slant  = 0;
450     GetFcString(match, FC_FILE, fontDescription.path);
451     GetFcString(match, FC_FAMILY, fontDescription.family);
452     GetFcInt(match, FC_WIDTH, width);
453     GetFcInt(match, FC_WEIGHT, weight);
454     GetFcInt(match, FC_SLANT, slant);
455     fontDescription.width  = IntToWidthType(width);
456     fontDescription.weight = IntToWeightType(weight);
457     fontDescription.slant  = IntToSlantType(slant);
458
459     // Retrieve the character set and increase the reference counter.
460     FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
461     *characterSet = FcCharSetCopy(*characterSet);
462
463     // destroyed the matched pattern
464     FcPatternDestroy(match);
465     FONT_LOG_DESCRIPTION(fontDescription, "");
466   }
467   return matched;
468 }
469
470 bool GetFcString(const FcPattern* const pattern, const char* const n, std::string& string)
471 {
472   FcChar8*       file   = nullptr;
473   const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
474
475   if(FcResultMatch == retVal)
476   {
477     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
478     string.assign(reinterpret_cast<const char*>(file));
479
480     return true;
481   }
482
483   return false;
484 }
485
486 bool GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
487 {
488   const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
489
490   if(FcResultMatch == retVal)
491   {
492     return true;
493   }
494
495   return false;
496 }
497
498 } // namespace Dali::TextAbstraction::Internal