Fix svace issue for image-operator
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / plugin / font-client-utils.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/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 std::string 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::FontClient::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::FontClient::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     Dali::Internal::Platform::LanczosSample(srcBuffer,
197                                             inputDimensions,
198                                             srcWidth,
199                                             srcFormat,
200                                             data.buffer,
201                                             desiredDimensions);
202   }
203 }
204
205 /**
206  * @brief Copy the FreeType bitmap to the given buffer.
207  *
208  * @param[out] data The bitmap data.
209  * @param[in,out] srcBitmap The FreeType bitmap.
210  * @param[in] isShearRequired Whether the bitmap needs a shear transform (for software italics).
211  * @param[in] moveBuffer Whether the bitmap buffer move. True if just copy buffer pointer. False if we use memcpy. (Default is false.)
212  * @note If you set moveBuffer=true, the bitmap's buffer moved frome srcBitmap to data. So srcBitmap buffer changed as nullptr.
213  */
214 void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap& srcBitmap, bool isShearRequired, bool moveBuffer)
215 {
216   data.buffer = nullptr;
217   if(srcBitmap.width * srcBitmap.rows > 0)
218   {
219     switch(srcBitmap.pixel_mode)
220     {
221       case FT_PIXEL_MODE_GRAY:
222       {
223         if(srcBitmap.pitch == static_cast<int>(srcBitmap.width))
224         {
225           uint8_t*     pixelsIn = srcBitmap.buffer;
226           unsigned int width    = srcBitmap.width;
227           unsigned     height   = srcBitmap.rows;
228
229           uint8_t* releaseRequiredPixelPtr = nullptr;
230
231           if(isShearRequired)
232           {
233             /**
234              * Glyphs' bitmaps with no slant retrieved from FreeType:
235              * __________     ____
236              * |XXXXXXXX|     |XX|
237              * |   XX   |     |XX|
238              * |   XX   |     |XX|
239              * |   XX   |     |XX|
240              * |   XX   |     |XX|
241              * |   XX   |     |XX|
242              * ----------     ----
243              *
244              * Expected glyphs' bitmaps with italic slant:
245              * ____________   ______
246              * |  XXXXXXXX|   |  XX|
247              * |     XX   |   |  XX|
248              * |    XX    |   | XX |
249              * |    XX    |   | XX |
250              * |   XX     |   |XX  |
251              * |   XX     |   |XX  |
252              * ------------   ------
253              *
254              * Glyphs' bitmaps with software italic slant retrieved from FreeType:
255              * __________     ______
256              * |XXXXXXXX|     |  XX|
257              * |   XX   |     |  XX|
258              * |  XX    |     | XX |
259              * |  XX    |     | XX |
260              * | XX     |     |XX  |
261              * | XX     |     |XX  |
262              * ----------     ------
263              *
264              * 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.
265              */
266             unsigned int widthOut  = 0u;
267             unsigned int heightOut = 0u;
268             uint8_t*     pixelsOut = nullptr;
269
270             Dali::Internal::Platform::HorizontalShear(pixelsIn,
271                                                       width,
272                                                       height,
273                                                       width,
274                                                       1u,
275                                                       -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
276                                                       pixelsOut,
277                                                       widthOut,
278                                                       heightOut);
279
280             if(DALI_LIKELY(pixelsOut))
281             {
282               width  = widthOut;
283               height = heightOut;
284
285               if(moveBuffer)
286               {
287                 releaseRequiredPixelPtr = pixelsIn;
288               }
289               else
290               {
291                 releaseRequiredPixelPtr = pixelsOut;
292               }
293
294               // Change input buffer ptr.
295               pixelsIn = pixelsOut;
296             }
297             else
298             {
299               DALI_LOG_ERROR("ERROR! software italic slant failed!\n");
300             }
301           }
302
303           data.width  = width;
304           data.height = height;
305           data.format = Pixel::L8; // Sets the pixel format.
306
307           // Note we don't compress here
308           data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
309
310           if(moveBuffer)
311           {
312             data.isBufferOwned = true;
313             data.buffer        = pixelsIn;
314
315             // Happy trick for copyless convert bitmap!
316             srcBitmap.buffer = nullptr;
317           }
318           else
319           {
320             data.isBufferOwned = false;
321             data.buffer        = pixelsIn;
322           }
323
324           if(releaseRequiredPixelPtr)
325           {
326             free(releaseRequiredPixelPtr);
327           }
328         }
329         break;
330       }
331
332 #ifdef FREETYPE_BITMAP_SUPPORT
333       case FT_PIXEL_MODE_BGRA:
334       {
335         if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
336         {
337           // Color glyph doesn't support copyless convert bitmap. Just memcpy
338           ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer, Pixel::BGRA8888);
339         }
340         break;
341       }
342 #endif
343       default:
344       {
345         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n");
346         break;
347       }
348     }
349   }
350 }
351
352 FcPattern* CreateFontFamilyPattern(const FontDescription& fontDescription)
353 {
354   // create the cached font family lookup pattern
355   // a pattern holds a set of names, each name refers to a property of the font
356   FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
357
358   if(!fontFamilyPattern)
359   {
360     return nullptr;
361   }
362
363   // add a property to the pattern for the font family
364   FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fontDescription.family.c_str()));
365
366   // add a property to the pattern for local setting.
367   const char* locale = setlocale(LC_MESSAGES, nullptr);
368   if(locale != nullptr)
369   {
370     FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale));
371   }
372
373   int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
374   if(width < 0)
375   {
376     // Use default.
377     width = DEFAULT_FONT_WIDTH;
378   }
379
380   int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
381   if(weight < 0)
382   {
383     // Use default.
384     weight = DEFAULT_FONT_WEIGHT;
385   }
386
387   int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
388   if(slant < 0)
389   {
390     // Use default.
391     slant = DEFAULT_FONT_SLANT;
392   }
393
394   FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width);
395   FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight);
396   FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant);
397
398   // modify the config, with the mFontFamilyPatterm
399   FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern);
400
401   // provide default values for unspecified properties in the font pattern
402   // e.g. patterns without a specified style or weight are set to Medium
403   FcDefaultSubstitute(fontFamilyPattern);
404
405   return fontFamilyPattern;
406 }
407
408 FcCharSet* CreateCharacterSetFromDescription(const FontDescription& description)
409 {
410   FcCharSet* characterSet = nullptr;
411
412   FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
413
414   if(nullptr != pattern)
415   {
416     FcResult   result = FcResultMatch;
417     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
418
419     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
420
421     // Destroys the created patterns.
422     FcPatternDestroy(match);
423     FcPatternDestroy(pattern);
424   }
425
426   return characterSet;
427 }
428
429 bool MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
430 {
431   DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
432
433   FcResult   result = FcResultMatch;
434   FcPattern* match  = FcFontMatch(nullptr /* use default configure */, pattern, &result); // Creates a new font pattern that needs to be destroyed by calling FcPatternDestroy.
435
436   const bool matched = nullptr != match;
437   DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "  pattern matched : %s\n", (matched ? "true" : "false"));
438
439   if(matched)
440   {
441     int width  = 0;
442     int weight = 0;
443     int slant  = 0;
444     GetFcString(match, FC_FILE, fontDescription.path);
445     GetFcString(match, FC_FAMILY, fontDescription.family);
446     GetFcInt(match, FC_WIDTH, width);
447     GetFcInt(match, FC_WEIGHT, weight);
448     GetFcInt(match, FC_SLANT, slant);
449     fontDescription.width  = IntToWidthType(width);
450     fontDescription.weight = IntToWeightType(weight);
451     fontDescription.slant  = IntToSlantType(slant);
452
453     // Retrieve the character set and increase the reference counter.
454     FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
455     *characterSet = FcCharSetCopy(*characterSet);
456
457     // destroyed the matched pattern
458     FcPatternDestroy(match);
459     FONT_LOG_DESCRIPTION(fontDescription, "");
460   }
461   return matched;
462 }
463
464 bool GetFcString(const FcPattern* const pattern, const char* const n, std::string& string)
465 {
466   FcChar8*       file   = nullptr;
467   const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
468
469   if(FcResultMatch == retVal)
470   {
471     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
472     string.assign(reinterpret_cast<const char*>(file));
473
474     return true;
475   }
476
477   return false;
478 }
479
480 bool GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
481 {
482   const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
483
484   if(FcResultMatch == retVal)
485   {
486     return true;
487   }
488
489   return false;
490 }
491
492 } // namespace Dali::TextAbstraction::Internal