Cache rendered glyph if required. + Copyless glyph creation
[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 #endif
27
28 namespace Dali::TextAbstraction::Internal
29 {
30 namespace
31 {
32 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
33
34 // NONE            -1  --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
35 // ULTRA_CONDENSED 50
36 // EXTRA_CONDENSED 63
37 // CONDENSED       75
38 // SEMI_CONDENSED  87
39 // NORMAL         100
40 // SEMI_EXPANDED  113
41 // EXPANDED       125
42 // EXTRA_EXPANDED 150
43 // ULTRA_EXPANDED 200
44 const int          FONT_WIDTH_TYPE_TO_INT[] = {-1, 50, 63, 75, 87, 100, 113, 125, 150, 200};
45 const unsigned int NUM_FONT_WIDTH_TYPE      = sizeof(FONT_WIDTH_TYPE_TO_INT) / sizeof(int);
46
47 // NONE                       -1  --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
48 // THIN                        0
49 // ULTRA_LIGHT, EXTRA_LIGHT   40
50 // LIGHT                      50
51 // DEMI_LIGHT, SEMI_LIGHT     55
52 // BOOK                       75
53 // NORMAL, REGULAR            80
54 // MEDIUM                    100
55 // DEMI_BOLD, SEMI_BOLD      180
56 // BOLD                      200
57 // ULTRA_BOLD, EXTRA_BOLD    205
58 // BLACK, HEAVY, EXTRA_BLACK 210
59 const int          FONT_WEIGHT_TYPE_TO_INT[] = {-1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210};
60 const unsigned int NUM_FONT_WEIGHT_TYPE      = sizeof(FONT_WEIGHT_TYPE_TO_INT) / sizeof(int);
61
62 // NONE             -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
63 // NORMAL, ROMAN     0
64 // ITALIC          100
65 // OBLIQUE         110
66 const int          FONT_SLANT_TYPE_TO_INT[] = {-1, 0, 100, 110};
67 const unsigned int NUM_FONT_SLANT_TYPE      = sizeof(FONT_SLANT_TYPE_TO_INT) / sizeof(int);
68
69 } // namespace
70
71 /**
72  * @brief Returns the FontWidth's enum index for the given width value.
73  *
74  * @param[in] width The width value.
75  *
76  * @return The FontWidth's enum index.
77  */
78 const FontWidth::Type IntToWidthType(int width)
79 {
80   return static_cast<FontWidth::Type>(ValueToIndex(width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u));
81 }
82
83 /**
84  * @brief Returns the FontWeight's enum index for the given weight value.
85  *
86  * @param[in] weight The weight value.
87  *
88  * @return The FontWeight's enum index.
89  */
90 const FontWeight::Type IntToWeightType(int weight)
91 {
92   return static_cast<FontWeight::Type>(ValueToIndex(weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u));
93 }
94
95 /**
96  * @brief Returns the FontSlant's enum index for the given slant value.
97  *
98  * @param[in] slant The slant value.
99  *
100  * @return The FontSlant's enum index.
101  */
102 const FontSlant::Type IntToSlantType(int slant)
103 {
104   return static_cast<FontSlant::Type>(ValueToIndex(slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u));
105 }
106
107 const int DEFAULT_FONT_WIDTH(100);
108 const int DEFAULT_FONT_WEIGHT(80);
109 const int DEFAULT_FONT_SLANT(0);
110
111 const FontWidth::Type DefaultFontWidth()
112 {
113   return IntToWidthType(DEFAULT_FONT_WIDTH);
114 }
115 const FontWeight::Type DefaultFontWeight()
116 {
117   return IntToWeightType(DEFAULT_FONT_WEIGHT);
118 }
119 const FontSlant::Type DefaultFontSlant()
120 {
121   return IntToSlantType(DEFAULT_FONT_SLANT);
122 }
123
124 /**
125  * @brief Copy the color bitmap given in @p srcBuffer to @p data.
126  *
127  * @param[out] data The bitmap data.
128  * @param[in] srcWidth The width of the bitmap.
129  * @param[in] srcHeight The height of the bitmap.
130  * @param[in] srcBuffer The buffer of the bitmap.
131  */
132 void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer, const Pixel::Format srcFormat)
133 {
134   // Set the input dimensions.
135   const ImageDimensions inputDimensions(srcWidth, srcHeight);
136
137   // Set the output dimensions.
138   // If the output dimension is not given, the input dimension is set
139   // and won't be downscaling.
140   data.width  = (data.width == 0) ? srcWidth : data.width;
141   data.height = (data.height == 0) ? srcHeight : data.height;
142   const ImageDimensions desiredDimensions(data.width, data.height);
143
144   data.format = srcFormat;
145
146   // Note we don't compress here
147   data.compressType = TextAbstraction::FontClient::GlyphBufferData::CompressType::NO_COMPRESS;
148
149   const uint32_t bytePerPixel = Dali::Pixel::GetBytesPerPixel(srcFormat);
150
151   // Creates the output buffer
152   const uint32_t bufferSize = data.width * data.height * bytePerPixel;
153
154   if(inputDimensions == desiredDimensions)
155   {
156     // There isn't downscaling.
157     data.isBufferOwned = false;
158     data.buffer        = const_cast<uint8_t*>(srcBuffer);
159   }
160   else
161   {
162     data.isBufferOwned = true;
163     data.buffer        = (uint8_t*)malloc(bufferSize); // @note The caller is responsible for deallocating the bitmap data using free.
164     Dali::Internal::Platform::LanczosSample(srcBuffer,
165                                             inputDimensions,
166                                             srcWidth,
167                                             srcFormat,
168                                             data.buffer,
169                                             desiredDimensions);
170   }
171 }
172
173 /**
174  * @brief Copy the FreeType bitmap to the given buffer.
175  *
176  * @param[out] data The bitmap data.
177  * @param[in,out] srcBitmap The FreeType bitmap.
178  * @param[in] isShearRequired Whether the bitmap needs a shear transform (for software italics).
179  * @param[in] moveBuffer Whether the bitmap buffer move. True if just copy buffer pointer. False if we use memcpy. (Default is false.)
180  * @note If you set moveBuffer=true, the bitmap's buffer moved frome srcBitmap to data. So srcBitmap buffer changed as nullptr.
181  */
182 void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap& srcBitmap, bool isShearRequired, bool moveBuffer)
183 {
184   data.buffer = nullptr;
185   if(srcBitmap.width * srcBitmap.rows > 0)
186   {
187     switch(srcBitmap.pixel_mode)
188     {
189       case FT_PIXEL_MODE_GRAY:
190       {
191         if(srcBitmap.pitch == static_cast<int>(srcBitmap.width))
192         {
193           uint8_t*     pixelsIn = srcBitmap.buffer;
194           unsigned int width    = srcBitmap.width;
195           unsigned     height   = srcBitmap.rows;
196
197           uint8_t* releaseRequiredPixelPtr = nullptr;
198
199           if(isShearRequired)
200           {
201             /**
202              * Glyphs' bitmaps with no slant retrieved from FreeType:
203              * __________     ____
204              * |XXXXXXXX|     |XX|
205              * |   XX   |     |XX|
206              * |   XX   |     |XX|
207              * |   XX   |     |XX|
208              * |   XX   |     |XX|
209              * |   XX   |     |XX|
210              * ----------     ----
211              *
212              * Expected glyphs' bitmaps with italic slant:
213              * ____________   ______
214              * |  XXXXXXXX|   |  XX|
215              * |     XX   |   |  XX|
216              * |    XX    |   | XX |
217              * |    XX    |   | XX |
218              * |   XX     |   |XX  |
219              * |   XX     |   |XX  |
220              * ------------   ------
221              *
222              * Glyphs' bitmaps with software italic slant retrieved from FreeType:
223              * __________     ______
224              * |XXXXXXXX|     |  XX|
225              * |   XX   |     |  XX|
226              * |  XX    |     | XX |
227              * |  XX    |     | XX |
228              * | XX     |     |XX  |
229              * | XX     |     |XX  |
230              * ----------     ------
231              *
232              * 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.
233              */
234             unsigned int widthOut  = 0u;
235             unsigned int heightOut = 0u;
236             uint8_t*     pixelsOut = nullptr;
237
238             Dali::Internal::Platform::HorizontalShear(pixelsIn,
239                                                       width,
240                                                       height,
241                                                       width,
242                                                       1u,
243                                                       -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
244                                                       pixelsOut,
245                                                       widthOut,
246                                                       heightOut);
247
248             if(DALI_LIKELY(pixelsOut))
249             {
250               width  = widthOut;
251               height = heightOut;
252
253               if(moveBuffer)
254               {
255                 releaseRequiredPixelPtr = pixelsIn;
256               }
257               else
258               {
259                 releaseRequiredPixelPtr = pixelsOut;
260               }
261
262               // Change input buffer ptr.
263               pixelsIn = pixelsOut;
264             }
265             else
266             {
267               DALI_LOG_ERROR("ERROR! software italic slant failed!\n");
268             }
269           }
270
271           data.width  = width;
272           data.height = height;
273           data.format = Pixel::L8; // Sets the pixel format.
274
275           // Note we don't compress here
276           data.compressType = TextAbstraction::FontClient::GlyphBufferData::CompressType::NO_COMPRESS;
277
278           if(moveBuffer)
279           {
280             data.isBufferOwned = true;
281             data.buffer        = pixelsIn;
282
283             // Happy trick for copyless convert bitmap!
284             srcBitmap.buffer = nullptr;
285           }
286           else
287           {
288             data.isBufferOwned = false;
289             data.buffer        = pixelsIn;
290           }
291
292           if(releaseRequiredPixelPtr)
293           {
294             free(releaseRequiredPixelPtr);
295           }
296         }
297         break;
298       }
299
300 #ifdef FREETYPE_BITMAP_SUPPORT
301       case FT_PIXEL_MODE_BGRA:
302       {
303         if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
304         {
305           // Color glyph doesn't support copyless convert bitmap. Just memcpy
306           ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer, Pixel::BGRA8888);
307         }
308         break;
309       }
310 #endif
311       default:
312       {
313         DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n");
314         break;
315       }
316     }
317   }
318 }
319
320 FcPattern* CreateFontFamilyPattern(const FontDescription& fontDescription)
321 {
322   // create the cached font family lookup pattern
323   // a pattern holds a set of names, each name refers to a property of the font
324   FcPattern* fontFamilyPattern = FcPatternCreate(); // FcPatternCreate creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
325
326   if(!fontFamilyPattern)
327   {
328     return nullptr;
329   }
330
331   // add a property to the pattern for the font family
332   FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fontDescription.family.c_str()));
333
334   // add a property to the pattern for local setting.
335   const char* locale = setlocale(LC_MESSAGES, nullptr);
336   if(locale != nullptr)
337   {
338     FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale));
339   }
340
341   int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
342   if(width < 0)
343   {
344     // Use default.
345     width = DEFAULT_FONT_WIDTH;
346   }
347
348   int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
349   if(weight < 0)
350   {
351     // Use default.
352     weight = DEFAULT_FONT_WEIGHT;
353   }
354
355   int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
356   if(slant < 0)
357   {
358     // Use default.
359     slant = DEFAULT_FONT_SLANT;
360   }
361
362   FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width);
363   FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight);
364   FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant);
365
366   // modify the config, with the mFontFamilyPatterm
367   FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern);
368
369   // provide default values for unspecified properties in the font pattern
370   // e.g. patterns without a specified style or weight are set to Medium
371   FcDefaultSubstitute(fontFamilyPattern);
372
373   return fontFamilyPattern;
374 }
375
376 FcCharSet* CreateCharacterSetFromDescription(const FontDescription& description)
377 {
378   FcCharSet* characterSet = nullptr;
379
380   FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
381
382   if(nullptr != pattern)
383   {
384     FcResult   result = FcResultMatch;
385     FcPattern* match  = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
386
387     FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
388
389     // Destroys the created patterns.
390     FcPatternDestroy(match);
391     FcPatternDestroy(pattern);
392   }
393
394   return characterSet;
395 }
396
397 } // namespace Dali::TextAbstraction::Internal