2 * Copyright (c) 2021 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/integration-api/debug.h>
18 #include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
19 #include <dali/internal/text/text-abstraction/plugin/font-face-cache-item.h>
21 #if defined(DEBUG_ENABLED)
22 extern Dali::Integration::Log::Filter* gFontClientLogFilter;
25 namespace Dali::TextAbstraction::Internal
27 const float FROM_266 = 1.0f / 64.0f;
28 const float POINTS_PER_INCH = 72.f;
30 FontFaceCacheItem::FontFaceCacheItem(FT_Library& freeTypeLibrary,
33 PointSize26Dot6 requestedPointSize,
35 const FontMetrics& metrics)
36 : mFreeTypeLibrary(freeTypeLibrary),
37 mFreeTypeFace(ftFace),
39 mRequestedPointSize(requestedPointSize),
42 mCharacterSet(nullptr),
44 mFixedWidthPixels(0.f),
45 mFixedHeightPixels(0.f),
48 mIsFixedSizeBitmap(false),
49 mHasColorTables(false)
53 FontFaceCacheItem::FontFaceCacheItem(FT_Library& freeTypeLibrary,
56 PointSize26Dot6 requestedPointSize,
58 const FontMetrics& metrics,
63 : mFreeTypeLibrary(freeTypeLibrary),
64 mFreeTypeFace(ftFace),
66 mRequestedPointSize(requestedPointSize),
69 mCharacterSet(nullptr),
70 mFixedSizeIndex(fixedSizeIndex),
71 mFixedWidthPixels(fixedWidth),
72 mFixedHeightPixels(fixedHeight),
75 mIsFixedSizeBitmap(true),
76 mHasColorTables(hasColorTables)
80 void FontFaceCacheItem::GetFontMetrics(FontMetrics& metrics, unsigned int dpiVertical) const
84 // Adjust the metrics if the fixed-size font should be down-scaled
85 if(mIsFixedSizeBitmap)
87 const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
89 if(desiredFixedSize > 0.f)
91 const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
93 metrics.ascender = round(metrics.ascender * scaleFactor);
94 metrics.descender = round(metrics.descender * scaleFactor);
95 metrics.height = round(metrics.height * scaleFactor);
96 metrics.underlinePosition = metrics.underlinePosition * scaleFactor;
97 metrics.underlineThickness = metrics.underlineThickness * scaleFactor;
102 bool FontFaceCacheItem::GetGlyphMetrics(GlyphInfo& glyph, unsigned int dpiVertical, bool horizontal) const
106 FT_Face ftFace = mFreeTypeFace;
108 #ifdef FREETYPE_BITMAP_SUPPORT
109 // Check to see if we should be loading a Fixed Size bitmap?
110 if(mIsFixedSizeBitmap)
112 FT_Select_Size(ftFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
113 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_COLOR);
114 if(FT_Err_Ok == error)
116 glyph.width = mFixedWidthPixels;
117 glyph.height = mFixedHeightPixels;
118 glyph.advance = mFixedWidthPixels;
119 glyph.xBearing = 0.0f;
123 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
127 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
130 // Adjust the metrics if the fixed-size font should be down-scaled
131 const float desiredFixedSize = static_cast<float>(mRequestedPointSize) * FROM_266 / POINTS_PER_INCH * dpiVertical;
133 if(desiredFixedSize > 0.f)
135 const float scaleFactor = desiredFixedSize / mFixedHeightPixels;
136 glyph.width = round(glyph.width * scaleFactor);
137 glyph.height = round(glyph.height * scaleFactor);
138 glyph.advance = round(glyph.advance * scaleFactor);
139 glyph.xBearing = round(glyph.xBearing * scaleFactor);
140 glyph.yBearing = round(glyph.yBearing * scaleFactor);
142 glyph.scaleFactor = scaleFactor;
147 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error);
154 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
155 // i.e. with the SNum-3R font.
156 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
157 int error = FT_Load_Glyph(ftFace, glyph.index, FT_LOAD_NO_AUTOHINT);
159 // Keep the width of the glyph before doing the software emboldening.
160 // It will be used to calculate a scale factor to be applied to the
161 // advance as Harfbuzz doesn't apply any SW emboldening to calculate
162 // the advance of the glyph.
163 const float width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
165 if(FT_Err_Ok == error)
167 const bool isEmboldeningRequired = glyph.isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD);
168 if(isEmboldeningRequired)
170 // Does the software bold.
171 FT_GlyphSlot_Embolden(ftFace->glyph);
174 glyph.width = static_cast<float>(ftFace->glyph->metrics.width) * FROM_266;
175 glyph.height = static_cast<float>(ftFace->glyph->metrics.height) * FROM_266;
178 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingX) * FROM_266;
179 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.horiBearingY) * FROM_266;
183 glyph.xBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingX) * FROM_266;
184 glyph.yBearing += static_cast<float>(ftFace->glyph->metrics.vertBearingY) * FROM_266;
187 if(isEmboldeningRequired && !Dali::EqualsZero(width))
189 // If the glyph is emboldened by software, the advance is multiplied by a
190 // scale factor to make it slightly bigger.
191 glyph.advance *= (glyph.width / width);
194 // Use the bounding box of the bitmap to correct the metrics.
195 // For some fonts i.e the SNum-3R the metrics need to be corrected,
196 // otherwise the glyphs 'dance' up and down depending on the
197 // font's point size.
200 error = FT_Get_Glyph(ftFace->glyph, &ftGlyph);
203 FT_Glyph_Get_CBox(ftGlyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
205 const float descender = glyph.height - glyph.yBearing;
206 glyph.height = (bbox.yMax - bbox.yMin) * FROM_266;
207 glyph.yBearing = glyph.height - round(descender);
209 // Created FT_Glyph object must be released with FT_Done_Glyph
210 FT_Done_Glyph(ftGlyph);
221 * @brief Create a bitmap representation of a glyph from a face font
223 * @param[in] glyphIndex The index of a glyph within the specified font.
224 * @param[in] isItalicRequired Whether the glyph requires italic style.
225 * @param[in] isBoldRequired Whether the glyph requires bold style.
226 * @param[out] data The bitmap data.
227 * @param[in] outlineWidth The width of the glyph outline in pixels.
229 void FontFaceCacheItem::CreateBitmap(
230 GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
232 FT_Face ftFace = mFreeTypeFace;
234 // For the software italics.
235 bool isShearRequired = false;
237 #ifdef FREETYPE_BITMAP_SUPPORT
238 // Check to see if this is fixed size bitmap
239 if(mIsFixedSizeBitmap)
241 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_COLOR);
246 // FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
247 // i.e. with the SNum-3R font.
248 // @todo: add an option to use the FT_LOAD_DEFAULT if required?
249 error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT);
251 if(FT_Err_Ok == error)
253 if(isBoldRequired && !(ftFace->style_flags & FT_STYLE_FLAG_BOLD))
255 // Does the software bold.
256 FT_GlyphSlot_Embolden(ftFace->glyph);
259 if(isItalicRequired && !(ftFace->style_flags & FT_STYLE_FLAG_ITALIC))
261 // Will do the software italic.
262 isShearRequired = true;
266 error = FT_Get_Glyph(ftFace->glyph, &glyph);
268 // Convert to bitmap if necessary
269 if(FT_Err_Ok == error)
271 if(glyph->format != FT_GLYPH_FORMAT_BITMAP)
273 int offsetX = 0, offsetY = 0;
274 bool isOutlineGlyph = (glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0);
276 // Create a bitmap for the outline
279 // Retrieve the horizontal and vertical distance from the current pen position to the
280 // left and top border of the glyph bitmap for a normal glyph before applying the outline.
281 if(FT_Err_Ok == error)
283 FT_Glyph normalGlyph;
284 error = FT_Get_Glyph(ftFace->glyph, &normalGlyph);
286 error = FT_Glyph_To_Bitmap(&normalGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
287 if(FT_Err_Ok == error)
289 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(normalGlyph);
291 offsetX = bitmapGlyph->left;
292 offsetY = bitmapGlyph->top;
295 // Created FT_Glyph object must be released with FT_Done_Glyph
296 FT_Done_Glyph(normalGlyph);
299 // Now apply the outline
303 error = FT_Stroker_New(mFreeTypeLibrary, &stroker);
305 if(FT_Err_Ok == error)
307 FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
308 error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
310 if(FT_Err_Ok == error)
312 FT_Stroker_Done(stroker);
316 DALI_LOG_ERROR("FT_Glyph_StrokeBorder Failed with error: %d\n", error);
321 DALI_LOG_ERROR("FT_Stroker_New Failed with error: %d\n", error);
325 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
326 if(FT_Err_Ok == error)
328 FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
332 // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
333 data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
334 data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
337 ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired);
341 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
346 ConvertBitmap(data, ftFace->glyph->bitmap, isShearRequired);
349 data.isColorEmoji = mIsFixedSizeBitmap;
351 // Created FT_Glyph object must be released with FT_Done_Glyph
352 FT_Done_Glyph(glyph);
357 DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error);
361 bool FontFaceCacheItem::IsColorGlyph(GlyphIndex glyphIndex) const
365 #ifdef FREETYPE_BITMAP_SUPPORT
366 // Check to see if this is fixed size bitmap
369 error = FT_Load_Glyph(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR);
372 return FT_Err_Ok == error;
376 * Check if the character is supported by this font
377 * @param[in] character The character to test
379 bool FontFaceCacheItem::IsCharacterSupported(Character character)
381 if(nullptr == mCharacterSet)
383 // Create again the character set.
384 // It can be null if the ResetSystemDefaults() method has been called.
386 FontDescription description;
387 description.path = mPath;
388 description.family = std::move(FontFamily(mFreeTypeFace->family_name));
389 description.weight = FontWeight::NONE;
390 description.width = FontWidth::NONE;
391 description.slant = FontSlant::NONE;
393 // Note FreeType doesn't give too much info to build a proper font style.
394 if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC)
396 description.slant = FontSlant::ITALIC;
398 if(mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD)
400 description.weight = FontWeight::BOLD;
403 mCharacterSet = FcCharSetCopy(CreateCharacterSetFromDescription(description));
406 return FcCharSetHasChar(mCharacterSet, character);
409 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character) const
411 return FT_Get_Char_Index(mFreeTypeFace, character);
414 GlyphIndex FontFaceCacheItem::GetGlyphIndex(Character character, Character variantSelector) const
416 return FT_Face_GetCharVariantIndex(mFreeTypeFace, character, variantSelector);
419 } // namespace Dali::TextAbstraction::Internal