2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
19 #include <dali/integration-api/debug.h>
20 #include <dali/internal/imaging/common/image-operations.h>
24 #if defined(DEBUG_ENABLED)
25 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
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, \
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])
39 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor) \
40 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, \
42 " requestedPointSize : %d\n" \
43 " preferColor : %s\n", \
46 (preferColor ? "true" : "false"))
50 #define FONT_LOG_DESCRIPTION(fontDescription, prefix)
51 #define FONT_LOG_REQUEST(charcode, requestedPointSize, preferColor)
55 namespace Dali::TextAbstraction::Internal
59 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
61 // NONE -1 --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
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);
74 // NONE -1 --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
76 // ULTRA_LIGHT, EXTRA_LIGHT 40
78 // DEMI_LIGHT, SEMI_LIGHT 55
82 // DEMI_BOLD, SEMI_BOLD 180
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);
89 // NONE -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
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);
99 * @brief Returns the FontWidth's enum index for the given width value.
101 * @param[in] width The width value.
103 * @return The FontWidth's enum index.
105 const FontWidth::Type IntToWidthType(int width)
107 return static_cast<FontWidth::Type>(ValueToIndex(width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u));
111 * @brief Returns the FontWeight's enum index for the given weight value.
113 * @param[in] weight The weight value.
115 * @return The FontWeight's enum index.
117 const FontWeight::Type IntToWeightType(int weight)
119 return static_cast<FontWeight::Type>(ValueToIndex(weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u));
123 * @brief Returns the FontSlant's enum index for the given slant value.
125 * @param[in] slant The slant value.
127 * @return The FontSlant's enum index.
129 const FontSlant::Type IntToSlantType(int slant)
131 return static_cast<FontSlant::Type>(ValueToIndex(slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u));
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);
139 const std::string_view DefaultFontFamily()
141 return DEFAULT_FONT_FAMILY_NAME;
143 const FontWidth::Type DefaultFontWidth()
145 return IntToWidthType(DEFAULT_FONT_WIDTH);
147 const FontWeight::Type DefaultFontWeight()
149 return IntToWeightType(DEFAULT_FONT_WEIGHT);
151 const FontSlant::Type DefaultFontSlant()
153 return IntToSlantType(DEFAULT_FONT_SLANT);
157 * @brief Copy the color bitmap given in @p srcBuffer to @p data.
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.
164 void ConvertBitmap(TextAbstraction::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer, const Pixel::Format srcFormat)
166 // Set the input dimensions.
167 const ImageDimensions inputDimensions(srcWidth, srcHeight);
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);
176 data.format = srcFormat;
178 // Note we don't compress here
179 data.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
181 const uint32_t bytePerPixel = Dali::Pixel::GetBytesPerPixel(srcFormat);
183 // Creates the output buffer
184 const uint32_t bufferSize = data.width * data.height * bytePerPixel;
186 if(inputDimensions == desiredDimensions)
188 // There isn't downscaling.
189 data.isBufferOwned = false;
190 data.buffer = const_cast<uint8_t*>(srcBuffer);
194 data.isBufferOwned = true;
195 data.buffer = (uint8_t*)malloc(bufferSize); // @note The caller is responsible for deallocating the bitmap data using free.
197 if(DALI_UNLIKELY(!data.buffer))
199 DALI_LOG_ERROR("malloc is failed. request malloc size : %u\n", bufferSize);
202 Dali::Internal::Platform::LanczosSample(srcBuffer,
212 * @brief Copy the FreeType bitmap to the given buffer.
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.
220 void ConvertBitmap(TextAbstraction::GlyphBufferData& data, FT_Bitmap& srcBitmap, bool isShearRequired, bool moveBuffer)
222 data.buffer = nullptr;
223 if(srcBitmap.width * srcBitmap.rows > 0)
225 switch(srcBitmap.pixel_mode)
227 case FT_PIXEL_MODE_GRAY:
229 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width))
231 uint8_t* pixelsIn = srcBitmap.buffer;
232 unsigned int width = srcBitmap.width;
233 unsigned height = srcBitmap.rows;
235 uint8_t* releaseRequiredPixelPtr = nullptr;
240 * Glyphs' bitmaps with no slant retrieved from FreeType:
250 * Expected glyphs' bitmaps with italic slant:
251 * ____________ ______
258 * ------------ ------
260 * Glyphs' bitmaps with software italic slant retrieved from FreeType:
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.
272 unsigned int widthOut = 0u;
273 unsigned int heightOut = 0u;
274 uint8_t* pixelsOut = nullptr;
276 Dali::Internal::Platform::HorizontalShear(pixelsIn,
281 -TextAbstraction::FontClient::DEFAULT_ITALIC_ANGLE,
286 if(DALI_LIKELY(pixelsOut))
293 releaseRequiredPixelPtr = pixelsIn;
297 releaseRequiredPixelPtr = pixelsOut;
300 // Change input buffer ptr.
301 pixelsIn = pixelsOut;
305 DALI_LOG_ERROR("ERROR! software italic slant failed!\n");
310 data.height = height;
311 data.format = Pixel::L8; // Sets the pixel format.
313 // Note we don't compress here
314 data.compressionType = TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION;
318 data.isBufferOwned = true;
319 data.buffer = pixelsIn;
321 // Happy trick for copyless convert bitmap!
322 srcBitmap.buffer = nullptr;
326 data.isBufferOwned = false;
327 data.buffer = pixelsIn;
330 if(releaseRequiredPixelPtr)
332 free(releaseRequiredPixelPtr);
338 #ifdef FREETYPE_BITMAP_SUPPORT
339 case FT_PIXEL_MODE_BGRA:
341 if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
343 // Color glyph doesn't support copyless convert bitmap. Just memcpy
344 ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer, Pixel::BGRA8888);
351 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n");
358 FcPattern* CreateFontFamilyPattern(const FontDescription& fontDescription)
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.
364 if(!fontFamilyPattern)
369 // add a property to the pattern for the font family
370 FcPatternAddString(fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fontDescription.family.c_str()));
372 // add a property to the pattern for local setting.
373 const char* locale = setlocale(LC_MESSAGES, nullptr);
374 if(locale != nullptr)
376 FcPatternAddString(fontFamilyPattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale));
379 int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
383 width = DEFAULT_FONT_WIDTH;
386 int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
390 weight = DEFAULT_FONT_WEIGHT;
393 int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
397 slant = DEFAULT_FONT_SLANT;
400 FcPatternAddInteger(fontFamilyPattern, FC_WIDTH, width);
401 FcPatternAddInteger(fontFamilyPattern, FC_WEIGHT, weight);
402 FcPatternAddInteger(fontFamilyPattern, FC_SLANT, slant);
404 // modify the config, with the mFontFamilyPatterm
405 FcConfigSubstitute(nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern);
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);
411 return fontFamilyPattern;
414 FcCharSet* CreateCharacterSetFromDescription(const FontDescription& description)
416 FcCharSet* characterSet = nullptr;
418 FcPattern* pattern = CreateFontFamilyPattern(description); // Creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
420 if(nullptr != pattern)
422 FcResult result = FcResultMatch;
423 FcPattern* match = FcFontMatch(nullptr, pattern, &result); // FcFontMatch creates a new pattern that needs to be destroyed by calling FcPatternDestroy.
425 FcPatternGetCharSet(match, FC_CHARSET, 0u, &characterSet);
427 // Destroys the created patterns.
428 FcPatternDestroy(match);
429 FcPatternDestroy(pattern);
435 bool MatchFontDescriptionToPattern(FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet)
437 DALI_LOG_TRACE_METHOD(gFontClientLogFilter);
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.
442 const bool matched = nullptr != match;
443 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, " pattern matched : %s\n", (matched ? "true" : "false"));
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);
459 // Retrieve the character set and increase the reference counter.
460 FcPatternGetCharSet(match, FC_CHARSET, 0u, characterSet);
461 *characterSet = FcCharSetCopy(*characterSet);
463 // destroyed the matched pattern
464 FcPatternDestroy(match);
465 FONT_LOG_DESCRIPTION(fontDescription, "");
470 bool GetFcString(const FcPattern* const pattern, const char* const n, std::string& string)
472 FcChar8* file = nullptr;
473 const FcResult retVal = FcPatternGetString(pattern, n, 0u, &file);
475 if(FcResultMatch == retVal)
477 // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
478 string.assign(reinterpret_cast<const char*>(file));
486 bool GetFcInt(const _FcPattern* const pattern, const char* const n, int& intVal)
488 const FcResult retVal = FcPatternGetInteger(pattern, n, 0u, &intVal);
490 if(FcResultMatch == retVal)
498 } // namespace Dali::TextAbstraction::Internal