Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2024 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "fontfaceft.h"
23 :
24 : #include <cmath>
25 : #include <unordered_map>
26 :
27 : #include <ft2build.h>
28 : #include FT_FREETYPE_H
29 : #include FT_GLYPH_H
30 : #include FT_BBOX_H
31 : #include FT_TRUETYPE_TABLES_H
32 : #include <hb-ft.h>
33 :
34 : #ifndef MUSE_MODULE_DRAW_USE_QTTEXTDRAW
35 : #include <msdfgen.h>
36 : #include <ext/import-font.h>
37 : #endif
38 :
39 : #include "global/types/bytearray.h"
40 :
41 : #include "log.h"
42 :
43 : using namespace muse::draw;
44 :
45 : static const hb_tag_t KernTag = HB_TAG('k', 'e', 'r', 'n'); // kerning operations
46 : static const hb_tag_t LigaTag = HB_TAG('l', 'i', 'g', 'a'); // standard ligature substitution
47 : static const hb_tag_t CligTag = HB_TAG('c', 'l', 'i', 'g'); // contextual ligature substitution
48 : static const hb_tag_t DligTag = HB_TAG('d', 'l', 'i', 'g'); // contextual ligature substitution
49 : static const hb_tag_t HligTag = HB_TAG('h', 'l', 'i', 'g'); // contextual ligature substitution
50 :
51 : static const std::vector<hb_feature_t> HB_FEATURES = {
52 : { KernTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
53 : { LigaTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
54 : { CligTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
55 : { DligTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
56 : { HligTag, 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END }
57 : };
58 :
59 : static FT_Library ftlib = nullptr;
60 110 : static bool _init_ft()
61 : {
62 110 : int error = 0;
63 110 : if (!ftlib) {
64 1 : error = FT_Init_FreeType(&ftlib);
65 1 : if (!ftlib || error) {
66 0 : LOGE() << "init freetype library failed";
67 : }
68 : }
69 110 : return error == 0;
70 : }
71 :
72 : #define FLOOR(x) ((x) & -64)
73 : #define CEIL(x) (((x) + 63) & -64)
74 : #define TRUNC(x) ((x) >> 6)
75 : #define ROUND(x) (((x) + 32) & -64)
76 :
77 : struct muse::draw::GlyphMetrics
78 : {
79 : FBBox bbox;
80 : FT_Fixed linearAdvance = 0;
81 : };
82 :
83 : struct muse::draw::SymbolMetrics
84 : {
85 : glyph_idx_t idx = 0;
86 : FT_BBox bbox;
87 : FT_Fixed linearAdvance = 0;
88 : };
89 :
90 : struct HeightMetrics
91 : {
92 : f26dot6_t ascent = 0;
93 : f26dot6_t descent = 0;
94 : f26dot6_t leading = 0;
95 : };
96 :
97 : struct muse::draw::FData
98 : {
99 : ByteArray fontData;
100 : FT_Face face = nullptr;
101 : hb_font_t* hb_font = nullptr;
102 : std::unordered_map<glyph_idx_t, GlyphMetrics> glyphsMetrics;
103 : std::unordered_map<glyph_idx_t, SymbolMetrics> symbolMetrics;
104 : FT_Size_Metrics metrics;
105 : HeightMetrics heightMetrics;
106 : };
107 :
108 : #ifndef MUSE_MODULE_DRAW_USE_QTTEXTDRAW
109 : static const int SDF_DIM = 28; //! default dimension of generated SDF. Real SDF can have another size
110 : static const int SDF_SHAPE_SIZE = 100; //! effective shape size
111 : static const double MIN_SDF_OUTLINE_PIXELS = 7.0;
112 : static const double MAX_SDF_SCALE = 4.0;
113 :
114 : static double sdfScaleForShape(const msdfgen::Vector2& shapeDims, double baseScale)
115 : {
116 : double minDim = std::min(shapeDims.x, shapeDims.y);
117 : if (minDim <= 0.0) {
118 : return baseScale;
119 : }
120 :
121 : return std::min(std::max(baseScale, MIN_SDF_OUTLINE_PIXELS / minDim), MAX_SDF_SCALE);
122 : }
123 :
124 : static void generateSdf(GlyphImage& out, msdfgen::Shape& shape)
125 : {
126 : struct Bounds
127 : {
128 : double l, b, r, t;
129 : };
130 : Bounds bounds = { 1e240, 1e240, -1e240, -1e240 };
131 :
132 : shape.normalize();
133 : shape.orientContours();
134 :
135 : shape.bound(bounds.l, bounds.b, bounds.r, bounds.t);
136 : msdfgen::Vector2 shapeDims(bounds.r - bounds.l, bounds.t - bounds.b);
137 :
138 : int pxRange = 4;
139 : int pixelFrameWidth = 3;
140 : double scale = static_cast<double>(SDF_DIM) / SDF_SHAPE_SIZE;
141 : scale = sdfScaleForShape(shapeDims, scale);
142 :
143 : int sdfWidth = static_cast<int>(std::ceil(scale * shapeDims.x));
144 : int sdfHeight = static_cast<int>(std::ceil(scale * shapeDims.y));
145 :
146 : sdfWidth += 2 * pixelFrameWidth;
147 : sdfHeight += 2 * pixelFrameWidth;
148 :
149 : msdfgen::Vector2 translate = { -bounds.l, -bounds.b };
150 :
151 : double pxRangeScaled = pixelFrameWidth / scale;
152 :
153 : double left = bounds.l - pxRangeScaled;
154 : double top = -bounds.t - pxRangeScaled;
155 : double width = shapeDims.x + pxRangeScaled * 2;
156 : double height = shapeDims.y + pxRangeScaled * 2;
157 :
158 : double range = pxRange / scale;
159 : translate += pxRangeScaled;
160 :
161 : auto sdf = msdfgen::Bitmap<float, 1>(sdfWidth, sdfHeight);
162 : msdfgen::SDFTransformation t(msdfgen::Projection(scale, translate), msdfgen::Range(range));
163 : msdfgen::generateSDF(sdf, shape, t);
164 : msdfgen::distanceSignCorrection(sdf, shape, t, msdfgen::FILL_NONZERO);
165 :
166 : out.sdf.bitmap.reserve(sdfWidth * sdfHeight);
167 : for (int y = 0; y < sdf.height(); ++y) {
168 : for (int x = 0; x < sdf.width(); ++x) {
169 : uint8_t px = static_cast<uint8_t>(msdfgen::pixelFloatToByte(*sdf(x, y)));
170 : out.sdf.bitmap.push_back(px);
171 : }
172 : }
173 : out.sdf.width = sdfWidth;
174 : out.sdf.height = sdfHeight;
175 : out.range = range;
176 :
177 : out.rect.setTop(top);
178 : out.rect.setLeft(left);
179 : out.rect.setWidth(width);
180 : out.rect.setHeight(height);
181 :
182 : out.sdf.hash = std::hash<std::string_view> {}({ reinterpret_cast<const char*>(out.sdf.bitmap.data()), out.sdf.bitmap.size() });
183 : }
184 :
185 : #endif
186 :
187 660 : static f26dot6_t tableValueToF26Dot6(FT_Face face, long value, int pixelSize)
188 : {
189 660 : if (!face || face->units_per_EM == 0) {
190 0 : return 0;
191 : }
192 :
193 660 : return static_cast<f26dot6_t>(value * pixelSize * 64.0 / face->units_per_EM);
194 : }
195 :
196 110 : static bool processHheaTable(FT_Face face, int pixelSize, HeightMetrics& metrics)
197 : {
198 110 : TT_HoriHeader* hhea = (TT_HoriHeader*)FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
199 110 : if (!hhea) {
200 0 : return false;
201 : }
202 :
203 110 : if (hhea->Ascender == 0 && hhea->Descender == 0) {
204 0 : return false;
205 : }
206 :
207 110 : metrics.ascent = tableValueToF26Dot6(face, hhea->Ascender, pixelSize);
208 110 : metrics.descent = -tableValueToF26Dot6(face, hhea->Descender, pixelSize);
209 110 : metrics.leading = tableValueToF26Dot6(face, hhea->Line_Gap, pixelSize);
210 110 : return true;
211 : }
212 :
213 110 : static bool processOS2Table(FT_Face face, int pixelSize, bool preferTypoLineMetrics, HeightMetrics& metrics)
214 : {
215 110 : TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
216 110 : if (!os2) {
217 0 : return false;
218 : }
219 :
220 : enum {
221 : USE_TYPO_METRICS = 0x80
222 : };
223 110 : if (preferTypoLineMetrics || (os2->fsSelection & USE_TYPO_METRICS)) {
224 110 : if (os2->sTypoAscender == 0 && os2->sTypoDescender == 0) {
225 0 : return false;
226 : }
227 :
228 110 : metrics.ascent = tableValueToF26Dot6(face, os2->sTypoAscender, pixelSize);
229 110 : metrics.descent = -tableValueToF26Dot6(face, os2->sTypoDescender, pixelSize);
230 110 : metrics.leading = tableValueToF26Dot6(face, os2->sTypoLineGap, pixelSize);
231 : } else {
232 0 : if (os2->usWinAscent == 0 && os2->usWinDescent == 0) {
233 0 : return false;
234 : }
235 :
236 0 : metrics.ascent = tableValueToF26Dot6(face, os2->usWinAscent, pixelSize);
237 0 : metrics.descent = tableValueToF26Dot6(face, os2->usWinDescent, pixelSize);
238 0 : metrics.leading = 0;
239 : }
240 :
241 110 : return true;
242 : }
243 :
244 110 : static HeightMetrics calculateHeightMetrics(FData* data, int pixelSize)
245 : {
246 110 : const FT_Size_Metrics& metrics = data->metrics;
247 110 : const double scale = static_cast<double>(pixelSize) / metrics.y_ppem;
248 :
249 110 : HeightMetrics result;
250 110 : result.ascent = static_cast<f26dot6_t>(metrics.ascender * scale);
251 110 : result.descent = static_cast<f26dot6_t>(-metrics.descender * scale);
252 110 : result.leading = static_cast<f26dot6_t>((metrics.height - metrics.ascender + metrics.descender) * scale);
253 :
254 110 : processHheaTable(data->face, pixelSize, result);
255 110 : processOS2Table(data->face, pixelSize, true, result);
256 110 : return result;
257 : }
258 :
259 110 : static void initializeHeightMetrics(FData* data, int pixelSize)
260 : {
261 110 : data->heightMetrics = calculateHeightMetrics(data, pixelSize);
262 110 : }
263 :
264 110 : FontFaceFT::FontFaceFT()
265 : {
266 110 : m_data = new FData();
267 110 : }
268 :
269 214 : FontFaceFT::~FontFaceFT()
270 : {
271 110 : if (m_data->hb_font) {
272 108 : hb_font_destroy(m_data->hb_font);
273 : }
274 110 : FT_Done_Face(m_data->face);
275 110 : delete m_data;
276 214 : }
277 :
278 110 : bool FontFaceFT::load(const FaceKey& key, const FontData& fontData, bool isSymbolMode)
279 : {
280 110 : if (!_init_ft()) {
281 0 : return false;
282 : }
283 :
284 110 : m_key = key;
285 110 : m_isSymbolMode = isSymbolMode;
286 :
287 110 : if (!fontData.valid()) {
288 0 : LOGE() << "empty font data: " << m_key.dataKey.family().id();
289 0 : return false;
290 : }
291 :
292 110 : m_data->fontData = fontData.data;
293 :
294 110 : int rval = FT_New_Memory_Face(ftlib, (FT_Byte*)m_data->fontData.constData(),
295 110 : (FT_Long)m_data->fontData.size(), 0, &m_data->face);
296 110 : if (rval) {
297 0 : LOGE() << "freetype: cannot create face: " << m_key.dataKey.family().id() << ", rval: " << rval;
298 0 : return false;
299 : }
300 :
301 110 : if (m_isSymbolMode) {
302 2 : FT_Set_Pixel_Sizes(m_data->face, 0, int(m_key.pixelSize + .5));
303 : } else {
304 108 : FT_Set_Char_Size(m_data->face, to_f26d6(m_key.pixelSize), to_f26d6(m_key.pixelSize), 0, 0);
305 :
306 : static FT_Matrix matrix;
307 108 : matrix.xx = 0x10000;
308 108 : matrix.yy = 0x10000;
309 108 : matrix.xy = 0;
310 108 : matrix.yx = 0;
311 108 : FT_Set_Transform(m_data->face, &matrix, nullptr);
312 :
313 108 : m_data->hb_font = hb_ft_font_create(m_data->face, NULL);
314 : }
315 :
316 110 : m_data->metrics = m_data->face->size->metrics;
317 110 : initializeHeightMetrics(m_data, m_key.pixelSize);
318 :
319 110 : return true;
320 : }
321 :
322 1513 : const FaceKey& FontFaceFT::key() const
323 : {
324 1513 : return m_key;
325 : }
326 :
327 2312 : bool FontFaceFT::isSymbolMode() const
328 : {
329 2312 : return m_isSymbolMode;
330 : }
331 :
332 307 : std::vector<ShapedGlyph> FontFaceFT::glyphs(const char32_t* text, int text_length) const
333 : {
334 307 : if (text_length < 1) {
335 0 : return std::vector<ShapedGlyph>();
336 : }
337 :
338 307 : std::vector<ShapedGlyph> result;
339 307 : if (m_isSymbolMode) {
340 0 : for (int i = 0; i < text_length; ++i) {
341 0 : glyph_idx_t idx = glyphIndex(text[i]);
342 0 : SymbolMetrics* sm = symbolMetrics(idx);
343 0 : IF_ASSERT_FAILED(sm) {
344 0 : return std::vector<ShapedGlyph>();
345 : }
346 :
347 0 : ShapedGlyph p;
348 0 : p.idx = sm->idx;
349 0 : p.x_advance = sm->linearAdvance;
350 0 : p.cluster = static_cast<uint32_t>(i);
351 :
352 0 : result.push_back(std::move(p));
353 : }
354 : } else {
355 307 : hb_buffer_t* hb_buffer = hb_buffer_create();
356 307 : hb_segment_properties_t props = HB_SEGMENT_PROPERTIES_DEFAULT;
357 :
358 307 : hb_buffer_add_utf32(hb_buffer, (uint32_t*)text, text_length, 0, -1);
359 307 : hb_buffer_set_direction(hb_buffer, props.direction);
360 307 : hb_buffer_set_script(hb_buffer, props.script);
361 :
362 307 : hb_buffer_set_segment_properties(hb_buffer, &props);
363 307 : hb_buffer_guess_segment_properties(hb_buffer);
364 :
365 307 : hb_shape(m_data->hb_font, hb_buffer, &HB_FEATURES[0], static_cast<unsigned int>(HB_FEATURES.size()));
366 307 : unsigned int len = hb_buffer_get_length(hb_buffer);
367 307 : result.reserve(len);
368 :
369 307 : hb_glyph_info_t* info = hb_buffer_get_glyph_infos(hb_buffer, NULL);
370 307 : hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(hb_buffer, NULL);
371 :
372 1583 : for (unsigned int i = 0; i < len; i++) {
373 1276 : f26dot6_t xAdvance = static_cast<f26dot6_t>(pos[i].x_advance);
374 1276 : hb_position_t hbAdvance = hb_font_get_glyph_h_advance(m_data->hb_font, info[i].codepoint);
375 1276 : if (GlyphMetrics* metrics = glyphMetrics(info[i].codepoint)) {
376 1274 : xAdvance += metrics->linearAdvance - hbAdvance;
377 : }
378 :
379 1276 : result.push_back({ info[i].codepoint, xAdvance, info[i].cluster });
380 : }
381 :
382 307 : hb_buffer_destroy(hb_buffer);
383 : }
384 :
385 307 : return result;
386 307 : }
387 :
388 1664 : glyph_idx_t FontFaceFT::glyphIndex(char32_t ucs4) const
389 : {
390 1664 : if (ucs4 == 0) {
391 0 : return 0;
392 : }
393 :
394 1664 : FT_UInt index = FT_Get_Char_Index(m_data->face, ucs4);
395 1664 : return static_cast<glyph_idx_t>(index);
396 : }
397 :
398 0 : glyph_idx_t FontFaceFT::glyphIndex(const std::string& glyphName) const
399 : {
400 0 : FT_UInt index = FT_Get_Name_Index(m_data->face, glyphName.c_str());
401 0 : return static_cast<glyph_idx_t>(index);
402 : }
403 :
404 19 : char32_t FontFaceFT::findCharCode(glyph_idx_t idx) const
405 : {
406 19 : auto findC = [this](glyph_idx_t idx)
407 : {
408 19 : FT_UInt gindex = 0;
409 19 : FT_ULong charcode = FT_Get_First_Char(m_data->face, &gindex);
410 19 : if (gindex == idx) {
411 0 : return charcode;
412 : }
413 :
414 1192 : while (gindex != 0)
415 : {
416 1192 : charcode = FT_Get_Next_Char(m_data->face, charcode, &gindex);
417 1192 : if (gindex == idx) {
418 19 : return charcode;
419 : }
420 : }
421 :
422 0 : return FT_ULong(0);
423 19 : };
424 :
425 19 : char32_t c = findC(idx);
426 :
427 : // check
428 19 : assert(glyphIndex(c) == idx);
429 :
430 19 : return c;
431 : }
432 :
433 942 : FBBox FontFaceFT::glyphBbox(glyph_idx_t idx) const
434 : {
435 942 : if (isSymbolMode()) {
436 58 : FT_UInt index = static_cast<FT_UInt>(idx);
437 58 : if (index == 0) {
438 0 : return FBBox();
439 : }
440 :
441 58 : if (FT_Load_Glyph(m_data->face, index, FT_LOAD_NO_BITMAP) != 0) {
442 0 : return FBBox();
443 : }
444 :
445 58 : FT_GlyphSlot slot = m_data->face->glyph;
446 58 : if (slot->format != FT_GLYPH_FORMAT_OUTLINE) {
447 0 : return FBBox();
448 : }
449 :
450 : FT_BBox outlineBox;
451 58 : FT_Outline_Get_BBox(&slot->outline, &outlineBox);
452 :
453 58 : FBBox bbox;
454 58 : bbox.setCoords(outlineBox.xMin, -outlineBox.yMax, outlineBox.xMax, -outlineBox.yMin);
455 58 : return bbox;
456 : } else {
457 884 : GlyphMetrics* gm = glyphMetrics(idx);
458 884 : if (!gm) {
459 0 : return FBBox();
460 : }
461 884 : return gm->bbox;
462 : }
463 : }
464 :
465 685 : f26dot6_t FontFaceFT::glyphAdvance(glyph_idx_t idx) const
466 : {
467 685 : if (isSymbolMode()) {
468 0 : SymbolMetrics* sm = symbolMetrics(idx);
469 0 : IF_ASSERT_FAILED(sm) {
470 0 : return 0.0;
471 : }
472 0 : return sm->linearAdvance;
473 : } else {
474 685 : GlyphMetrics* gm = glyphMetrics(idx);
475 685 : if (!gm) {
476 0 : return 0.0;
477 : }
478 685 : return gm->linearAdvance;
479 : }
480 : }
481 :
482 0 : const GlyphImage& FontFaceFT::glyphImage(glyph_idx_t idx) const
483 : {
484 0 : static GlyphImage null;
485 :
486 : #ifndef MUSE_MODULE_DRAW_USE_QTTEXTDRAW
487 : FT_UInt index = static_cast<FT_UInt>(idx);
488 : if (index == 0) {
489 : return null;
490 : }
491 :
492 : auto it = m_cache.find(idx);
493 : if (it != m_cache.end()) {
494 : return it->second;
495 : }
496 :
497 : if (FT_Load_Glyph(m_data->face, index, FT_LOAD_DEFAULT) != 0) {
498 : return null;
499 : }
500 :
501 : msdfgen::Shape shape;
502 : msdfgen::readFreetypeOutline(shape, &m_data->face->glyph->outline);
503 : if (shape.contours.empty()) {
504 : return m_cache.insert({ idx, GlyphImage() }).first->second;
505 : }
506 :
507 : std::pair<glyph_idx_t, GlyphImage> v;
508 : v.first = idx;
509 : shape.inverseYAxis = true;
510 : generateSdf(v.second, shape);
511 :
512 : return m_cache.insert(std::move(v)).first->second;
513 : #else
514 : UNUSED(idx);
515 0 : return null;
516 : #endif
517 : }
518 :
519 7 : f26dot6_t FontFaceFT::leading() const
520 : {
521 7 : return m_data->heightMetrics.leading;
522 : }
523 :
524 121 : f26dot6_t FontFaceFT::ascent() const
525 : {
526 121 : return m_data->heightMetrics.ascent;
527 : }
528 :
529 67 : f26dot6_t FontFaceFT::descent() const
530 : {
531 67 : return m_data->heightMetrics.descent;
532 : }
533 :
534 7 : f26dot6_t FontFaceFT::xHeight() const
535 : {
536 7 : TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table(m_data->face, ft_sfnt_os2);
537 7 : if (os2 && os2->sxHeight) {
538 7 : f26dot6_t result = std::round(os2->sxHeight * m_data->face->size->metrics.y_ppem * 64.0 / (double)m_data->face->units_per_EM);
539 7 : return result;
540 : }
541 :
542 0 : const glyph_idx_t glyph = glyphIndex('x');
543 0 : if (glyph == 0) {
544 0 : return 0;
545 : }
546 :
547 0 : GlyphMetrics* gm = glyphMetrics(glyph);
548 0 : IF_ASSERT_FAILED(gm) {
549 0 : return 0;
550 : }
551 0 : return gm->bbox.height();
552 : }
553 :
554 1 : f26dot6_t FontFaceFT::capHeight() const
555 : {
556 1 : TT_OS2* os2 = (TT_OS2*)FT_Get_Sfnt_Table(m_data->face, ft_sfnt_os2);
557 1 : if (os2 && os2->sCapHeight) {
558 1 : f26dot6_t result = std::round(os2->sCapHeight * m_data->face->size->metrics.y_ppem * 64.0 / (double)m_data->face->units_per_EM);
559 1 : return result;
560 : }
561 :
562 0 : const glyph_idx_t glyph = glyphIndex('X');
563 0 : if (glyph == 0) {
564 0 : return 0;
565 : }
566 :
567 0 : GlyphMetrics* gm = glyphMetrics(glyph);
568 0 : IF_ASSERT_FAILED(gm) {
569 0 : return 0;
570 : }
571 0 : return gm->bbox.height();
572 : }
573 :
574 7 : f26dot6_t FontFaceFT::underlinePos() const
575 : {
576 7 : f26dot6_t thickness = FT_MulFix(m_data->face->underline_thickness, m_data->face->size->metrics.y_scale);
577 7 : f26dot6_t centerPos = -FT_MulFix(m_data->face->underline_position, m_data->face->size->metrics.y_scale);
578 7 : return centerPos - thickness / 2;
579 : }
580 :
581 7 : f26dot6_t FontFaceFT::lineWidth() const
582 : {
583 7 : return FT_MulFix(m_data->face->underline_thickness, m_data->face->size->metrics.y_scale);
584 : }
585 :
586 2845 : GlyphMetrics* FontFaceFT::glyphMetrics(glyph_idx_t idx) const
587 : {
588 2845 : if (m_data->glyphsMetrics.find(idx) != m_data->glyphsMetrics.end()) {
589 1859 : return &m_data->glyphsMetrics.at(idx);
590 : }
591 :
592 986 : FT_UInt index = static_cast<FT_UInt>(idx);
593 986 : if (index == 0) {
594 2 : return nullptr;
595 : }
596 :
597 984 : if (FT_Load_Glyph(m_data->face, index, FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING) != 0) {
598 0 : return nullptr;
599 : }
600 :
601 984 : FT_GlyphSlot slot = m_data->face->glyph;
602 :
603 984 : GlyphMetrics& gm = m_data->glyphsMetrics[idx];
604 :
605 1968 : auto floorF26Dot6 = [](FT_Pos value) {
606 1968 : return value & -64;
607 : };
608 :
609 1968 : auto ceilF26Dot6 = [](FT_Pos value) {
610 1968 : return (value + 63) & -64;
611 : };
612 :
613 984 : FT_Pos left = floorF26Dot6(slot->metrics.horiBearingX);
614 984 : FT_Pos right = ceilF26Dot6(slot->metrics.horiBearingX + slot->metrics.width);
615 984 : FT_Pos top = ceilF26Dot6(slot->metrics.horiBearingY);
616 984 : FT_Pos bottom = floorF26Dot6(slot->metrics.horiBearingY - slot->metrics.height);
617 :
618 984 : gm.bbox.setLeft(left);
619 984 : gm.bbox.setTop(-top);
620 984 : gm.bbox.setWidth(right - left);
621 984 : gm.bbox.setHeight(top - bottom);
622 :
623 984 : gm.linearAdvance = slot->linearHoriAdvance >> 10;
624 :
625 984 : return &gm;
626 : }
627 :
628 0 : SymbolMetrics* FontFaceFT::symbolMetrics(glyph_idx_t idx) const
629 : {
630 0 : if (m_data->symbolMetrics.find(idx) != m_data->symbolMetrics.end()) {
631 0 : return &m_data->symbolMetrics.at(idx);
632 : }
633 :
634 0 : FT_UInt index = static_cast<FT_UInt>(idx);
635 0 : if (index == 0) {
636 0 : return nullptr;
637 : }
638 :
639 0 : if (FT_Load_Glyph(m_data->face, index, FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING) != 0) {
640 0 : return nullptr;
641 : }
642 :
643 0 : SymbolMetrics& sm = m_data->symbolMetrics[idx];
644 :
645 0 : sm.idx = static_cast<glyph_idx_t>(index);
646 :
647 0 : FT_GlyphSlot slot = m_data->face->glyph;
648 0 : sm.bbox.xMin = slot->metrics.horiBearingX;
649 0 : sm.bbox.xMax = slot->metrics.horiBearingX + slot->metrics.width;
650 0 : sm.bbox.yMin = slot->metrics.horiBearingY - slot->metrics.height;
651 0 : sm.bbox.yMax = slot->metrics.horiBearingY;
652 :
653 : //! NOTE Moved form MSS FontEngineFT::advance
654 : //! double advance = linearHoriAdvance * dpi_f / 655360.0;
655 : //! -> f26dot6_t advance = linearHoriAdvance * dpi_f * 64.0 / 655360.0;
656 : //! -> dpi_f = 5.0 constant
657 : //! -> f26dot6_t advance = linearHoriAdvance * 320.0 / 655360.0;
658 : //! -> f26dot6_t advance = linearHoriAdvance / 2048;
659 0 : sm.linearAdvance = m_data->face->glyph->linearHoriAdvance / 2048;
660 :
661 0 : return &sm;
662 : }
|