From 604ee2d0367784170e12e7150a4950625eebff01 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Thu, 23 Nov 2023 18:12:04 +0100 Subject: [PATCH] VFont: Use BLF for font & glyph loading Load VFont curves using BLF API rather than local FreeType calls. Pull Request: https://projects.blender.org/blender/blender/pulls/110187 --- source/blender/blenfont/BLF_api.h | 15 + source/blender/blenfont/intern/blf.cc | 65 +++ source/blender/blenfont/intern/blf_glyph.cc | 322 ++++++++++++- source/blender/blenfont/intern/blf_internal.h | 8 + source/blender/blenkernel/intern/vfont.cc | 12 +- .../blenkernel/intern/vfontdata_freetype.cc | 427 ++---------------- 6 files changed, 447 insertions(+), 402 deletions(-) diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 9838835349c..52e5eabf222 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -71,6 +71,21 @@ void BLF_unload_all(void); char *BLF_display_name_from_file(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +char *BLF_display_name_from_id(int fontid); + +/** + * Get the metrics needed for the initial sizing of text objects. + */ +bool BLF_get_vfont_metrics(int fontid, float *ascend_ratio, float *em_ratio, float *scale); + +/** + * Convert a character's outlines into curves. + */ +float BLF_character_to_curves(int fontid, + unsigned int unicode, + struct ListBase *nurbsbase, + const float scale); + /** * Check if font supports a particular glyph. */ diff --git a/source/blender/blenfont/intern/blf.cc b/source/blender/blenfont/intern/blf.cc index 1ab4bcb8b19..cc4314aff2b 100644 --- a/source/blender/blenfont/intern/blf.cc +++ b/source/blender/blenfont/intern/blf.cc @@ -952,6 +952,71 @@ char *BLF_display_name_from_file(const char *filepath) return name; } +char *BLF_display_name_from_id(int fontid) +{ + FontBLF *font = blf_get(fontid); + if (!font) { + return nullptr; + } + + return blf_display_name(font); +} + +bool BLF_get_vfont_metrics(int fontid, float *ascend_ratio, float *em_ratio, float *scale) +{ + FontBLF *font = blf_get(fontid); + if (!font) { + return false; + } + + if (!blf_ensure_face(font)) { + return false; + } + + /* Copied without change from vfontdata_freetype.cc to ensure consistant sizing. */ + + /* Blender default BFont is not "complete". */ + const bool complete_font = (font->face->ascender != 0) && (font->face->descender != 0) && + (font->face->ascender != font->face->descender); + + if (complete_font) { + /* We can get descender as well, but we simple store descender in relation to the ascender. + * Also note that descender is stored as a negative number. */ + *ascend_ratio = float(font->face->ascender) / (font->face->ascender - font->face->descender); + } + else { + *ascend_ratio = 0.8f; + *em_ratio = 1.0f; + } + + /* Adjust font size */ + if (font->face->bbox.yMax != font->face->bbox.yMin) { + *scale = float(1.0 / double(font->face->bbox.yMax - font->face->bbox.yMin)); + + if (complete_font) { + *em_ratio = float(font->face->ascender - font->face->descender) / + (font->face->bbox.yMax - font->face->bbox.yMin); + } + } + else { + *scale = 1.0f / 1000.0f; + } + + return true; +} + +float BLF_character_to_curves(int fontid, + unsigned int unicode, + ListBase *nurbsbase, + const float scale) +{ + FontBLF *font = blf_get(fontid); + if (!font) { + return 0.0f; + } + return blf_character_to_curves(font, unicode, nurbsbase, scale); +} + #ifdef DEBUG void BLF_state_print(int fontid) { diff --git a/source/blender/blenfont/intern/blf_glyph.cc b/source/blender/blenfont/intern/blf_glyph.cc index f03fd9e3f2e..4dc54409e93 100644 --- a/source/blender/blenfont/intern/blf_glyph.cc +++ b/source/blender/blenfont/intern/blf_glyph.cc @@ -30,6 +30,8 @@ #include "BLF_api.h" +#include "DNA_curve_types.h" + #include "GPU_capabilities.h" #include "blf_internal.h" @@ -718,11 +720,14 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode /** * Load a glyph into the glyph slot of a font's face object. */ -static FT_GlyphSlot blf_glyph_load(FontBLF *font, FT_UInt glyph_index) +static FT_GlyphSlot blf_glyph_load(FontBLF *font, FT_UInt glyph_index, bool outline_only) { int load_flags; - if (font->flags & BLF_MONOCHROME) { + if (outline_only) { + load_flags = FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP; + } + else if (font->flags & BLF_MONOCHROME) { load_flags = FT_LOAD_TARGET_MONO; } else { @@ -1117,7 +1122,8 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, FT_UInt glyph_index, uint charcode, uint8_t subpixel, - int fixed_width) + int fixed_width, + bool outline_only) { if (glyph_font != settings_font) { blf_font_size(glyph_font, settings_font->size); @@ -1161,7 +1167,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, FT_Set_Var_Design_Coordinates(glyph_font->face, BLF_VARIATIONS_MAX, &coords[0]); } - FT_GlyphSlot glyph = blf_glyph_load(glyph_font, glyph_index); + FT_GlyphSlot glyph = blf_glyph_load(glyph_font, glyph_index, outline_only); if (!glyph) { return nullptr; } @@ -1188,6 +1194,10 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, blf_glyph_transform_spacing(glyph, spacing_target - spacing); } + if (outline_only) { + return glyph; + } + FT_Outline_Translate(&glyph->outline, (FT_Pos)subpixel, 0); if (blf_glyph_render_bitmap(glyph_font, glyph)) { @@ -1215,7 +1225,7 @@ static GlyphBLF *blf_glyph_ensure_ex(FontBLF *font, } FT_GlyphSlot glyph = blf_glyph_render( - font, font_with_glyph, glyph_index, charcode, subpixel, gc->fixed_width); + font, font_with_glyph, glyph_index, charcode, subpixel, gc->fixed_width, false); if (glyph) { /* Save this glyph in the initial font's cache. */ @@ -1476,4 +1486,306 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, const int x, #endif } +/* -------------------------------------------------------------------- */ +/** \name Convert Glyph to Curves + * \{ */ + +/** + * from: http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-1 + * + * Vectorial representation of Freetype glyphs + * + * The source format of outlines is a collection of closed paths called "contours". Each contour is + * made of a series of line segments and bezier arcs. Depending on the file format, these can be + * second-order or third-order polynomials. The former are also called quadratic or conic arcs, and + * they come from the TrueType format. The latter are called cubic arcs and mostly come from the + * Type1 format. + * + * Each arc is described through a series of start, end and control points. + * Each point of the outline has a specific tag which indicates whether it is + * used to describe a line segment or an arc. + * The following rules are applied to decompose the contour's points into segments and arcs : + * + * # two successive "on" points indicate a line segment joining them. + * + * # one conic "off" point midst two "on" points indicates a conic bezier arc, + * the "off" point being the control point, and the "on" ones the start and end points. + * + * # Two successive cubic "off" points midst two "on" points indicate a cubic bezier arc. + * There must be exactly two cubic control points and two on points for each cubic arc + * (using a single cubic "off" point between two "on" points is forbidden, for example). + * + * # finally, two successive conic "off" points forces the rasterizer to create + * (during the scan-line conversion process exclusively) a virtual "on" point midst them, + * at their exact middle. + * This greatly facilitates the definition of successive conic bezier arcs. + * Moreover, it's the way outlines are described in the TrueType specification. + * + * Note that it is possible to mix conic and cubic arcs in a single contour, even though no current + * font driver produces such outlines. + * + *
+ *                                   *            # on
+ *                                                * off
+ *                                __---__
+ *   #-__                      _--       -_
+ *       --__                _-            -
+ *           --__           #               \
+ *               --__                        #
+ *                   -#
+ *                            Two "on" points
+ *    Two "on" points       and one "conic" point
+ *                             between them
+ *                 *
+ *   #            __      Two "on" points with two "conic"
+ *    \          -  -     points between them. The point
+ *     \        /    \    marked '0' is the middle of the
+ *      -      0      \   "off" points, and is a 'virtual'
+ *       -_  _-       #   "on" point where the curve passes.
+ *         --             It does not appear in the point
+ *                        list.
+ *         *
+ *         *                # on
+ *                    *     * off
+ *          __---__
+ *       _--       -_
+ *     _-            -
+ *    #               \
+ *                     #
+ *
+ *      Two "on" points
+ *    and two "cubic" point
+ *       between them
+ * 
+ * + * Each glyphs original outline points are located on a grid of indivisible units. + * The points are stored in the font file as 16-bit integer grid coordinates, + * with the grid origin's being at (0, 0); they thus range from -16384 to 16383. + * + * Convert conic to bezier arcs: + * Conic P0 P1 P2 + * Bezier B0 B1 B2 B3 + * B0=P0 + * B1=(P0+2*P1)/3 + * B2=(P2+2*P1)/3 + * B3=P2 + */ + +static void blf_glyph_to_curves(FT_Outline ftoutline, ListBase *nurbsbase, const float scale) +{ + const float eps = 0.0001f; + const float eps_sq = eps * eps; + Nurb *nu; + BezTriple *bezt; + float dx, dy; + int j, k, l, l_first = 0; + + /* initialize as -1 to add 1 on first loop each time */ + int contour_prev; + + /* Start converting the FT data */ + int *onpoints = static_cast( + MEM_callocN(size_t(ftoutline.n_contours) * sizeof(int), "onpoints")); + + /* Get number of on-curve points for bezier-triples (including conic virtual on-points). */ + for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) { + const int n = ftoutline.contours[j] - contour_prev; + contour_prev = ftoutline.contours[j]; + + for (k = 0; k < n; k++) { + l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k; + if (k == 0) { + l_first = l; + } + + if (ftoutline.tags[l] == FT_Curve_Tag_On) { + onpoints[j]++; + } + + { + const int l_next = (k < n - 1) ? (l + 1) : l_first; + if (ftoutline.tags[l] == FT_Curve_Tag_Conic && + ftoutline.tags[l_next] == FT_Curve_Tag_Conic) { + onpoints[j]++; + } + } + } + } + + /* contour loop, bezier & conic styles merged */ + for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) { + const int n = ftoutline.contours[j] - contour_prev; + contour_prev = ftoutline.contours[j]; + + /* add new curve */ + nu = (Nurb *)MEM_callocN(sizeof(Nurb), "objfnt_nurb"); + bezt = static_cast( + MEM_callocN(size_t(onpoints[j]) * sizeof(BezTriple), "objfnt_bezt")); + BLI_addtail(nurbsbase, nu); + + nu->type = CU_BEZIER; + nu->pntsu = onpoints[j]; + nu->resolu = 8; + nu->flagu = CU_NURB_CYCLIC; + nu->bezt = bezt; + + /* individual curve loop, start-end */ + for (k = 0; k < n; k++) { + l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k; + if (k == 0) { + l_first = l; + } + + /* virtual conic on-curve points */ + { + const int l_next = (k < n - 1) ? (l + 1) : l_first; + if (ftoutline.tags[l] == FT_Curve_Tag_Conic && + ftoutline.tags[l_next] == FT_Curve_Tag_Conic) { + dx = float(ftoutline.points[l].x + ftoutline.points[l_next].x) * scale / 2.0f; + dy = float(ftoutline.points[l].y + ftoutline.points[l_next].y) * scale / 2.0f; + + /* left handle */ + bezt->vec[0][0] = (dx + (2.0f * float(ftoutline.points[l].x)) * scale) / 3.0f; + bezt->vec[0][1] = (dy + (2.0f * float(ftoutline.points[l].y)) * scale) / 3.0f; + + /* midpoint (virtual on-curve point) */ + bezt->vec[1][0] = dx; + bezt->vec[1][1] = dy; + + /* right handle */ + bezt->vec[2][0] = (dx + (2.0f * float(ftoutline.points[l_next].x)) * scale) / 3.0f; + bezt->vec[2][1] = (dy + (2.0f * float(ftoutline.points[l_next].y)) * scale) / 3.0f; + + bezt->h1 = bezt->h2 = HD_ALIGN; + bezt->radius = 1.0f; + bezt++; + } + } + + /* on-curve points */ + if (ftoutline.tags[l] == FT_Curve_Tag_On) { + const int l_prev = (k > 0) ? (l - 1) : ftoutline.contours[j]; + const int l_next = (k < n - 1) ? (l + 1) : l_first; + + /* left handle */ + if (ftoutline.tags[l_prev] == FT_Curve_Tag_Cubic) { + bezt->vec[0][0] = float(ftoutline.points[l_prev].x) * scale; + bezt->vec[0][1] = float(ftoutline.points[l_prev].y) * scale; + bezt->h1 = HD_FREE; + } + else if (ftoutline.tags[l_prev] == FT_Curve_Tag_Conic) { + bezt->vec[0][0] = (float(ftoutline.points[l].x) + + (2.0f * float(ftoutline.points[l_prev].x))) * + scale / 3.0f; + bezt->vec[0][1] = (float(ftoutline.points[l].y) + + (2.0f * float(ftoutline.points[l_prev].y))) * + scale / 3.0f; + bezt->h1 = HD_FREE; + } + else { + bezt->vec[0][0] = float(ftoutline.points[l].x) * scale - + (float(ftoutline.points[l].x) - float(ftoutline.points[l_prev].x)) * + scale / 3.0f; + bezt->vec[0][1] = float(ftoutline.points[l].y) * scale - + (float(ftoutline.points[l].y) - float(ftoutline.points[l_prev].y)) * + scale / 3.0f; + bezt->h1 = HD_VECT; + } + + /* midpoint (on-curve point) */ + bezt->vec[1][0] = float(ftoutline.points[l].x) * scale; + bezt->vec[1][1] = float(ftoutline.points[l].y) * scale; + + /* right handle */ + if (ftoutline.tags[l_next] == FT_Curve_Tag_Cubic) { + bezt->vec[2][0] = float(ftoutline.points[l_next].x) * scale; + bezt->vec[2][1] = float(ftoutline.points[l_next].y) * scale; + bezt->h2 = HD_FREE; + } + else if (ftoutline.tags[l_next] == FT_Curve_Tag_Conic) { + bezt->vec[2][0] = (float(ftoutline.points[l].x) + + (2.0f * float(ftoutline.points[l_next].x))) * + scale / 3.0f; + bezt->vec[2][1] = (float(ftoutline.points[l].y) + + (2.0f * float(ftoutline.points[l_next].y))) * + scale / 3.0f; + bezt->h2 = HD_FREE; + } + else { + bezt->vec[2][0] = float(ftoutline.points[l].x) * scale - + (float(ftoutline.points[l].x) - float(ftoutline.points[l_next].x)) * + scale / 3.0f; + bezt->vec[2][1] = float(ftoutline.points[l].y) * scale - + (float(ftoutline.points[l].y) - float(ftoutline.points[l_next].y)) * + scale / 3.0f; + bezt->h2 = HD_VECT; + } + + /* get the handles that are aligned, tricky... + * - check if one of them is a vector handle. + * - dist_squared_to_line_v2, check if the three beztriple points are on one line + * - len_squared_v2v2, see if there's a distance between the three points + * - len_squared_v2v2 again, to check the angle between the handles + */ + if ((bezt->h1 != HD_VECT && bezt->h2 != HD_VECT) && + (dist_squared_to_line_v2(bezt->vec[0], bezt->vec[1], bezt->vec[2]) < + (0.001f * 0.001f)) && + (len_squared_v2v2(bezt->vec[0], bezt->vec[1]) > eps_sq) && + (len_squared_v2v2(bezt->vec[1], bezt->vec[2]) > eps_sq) && + (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) > eps_sq) && + (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) > + max_ff(len_squared_v2v2(bezt->vec[0], bezt->vec[1]), + len_squared_v2v2(bezt->vec[1], bezt->vec[2])))) + { + bezt->h1 = bezt->h2 = HD_ALIGN; + } + bezt->radius = 1.0f; + bezt++; + } + } + } + + MEM_freeN(onpoints); +} + +static FT_GlyphSlot blf_glyphslot_ensure_outline(FontBLF *font, const uint charcode) +{ + /* Glyph might not come from the initial font. */ + FontBLF *font_with_glyph = font; + FT_UInt glyph_index = blf_glyph_index_from_charcode(&font_with_glyph, charcode); + + if (!blf_ensure_face(font_with_glyph)) { + return nullptr; + } + + FT_GlyphSlot glyph = blf_glyph_render(font, font_with_glyph, glyph_index, charcode, 0, 0, true); + + if (font != font_with_glyph) { + if (!blf_ensure_face(font)) { + return nullptr; + } + double ratio = float(font->face->units_per_EM) / float(font_with_glyph->face->units_per_EM); + FT_Matrix transform = {to_16dot16(ratio), 0, 0, to_16dot16(ratio)}; + FT_Outline_Transform(&glyph->outline, &transform); + glyph->advance.x = int(float(glyph->advance.x) * ratio); + glyph->metrics.horiAdvance = int(float(glyph->metrics.horiAdvance) * ratio); + } + + return glyph; +} + +float blf_character_to_curves(FontBLF *font, + unsigned int unicode, + ListBase *nurbsbase, + const float scale) +{ + FT_GlyphSlot glyph = blf_glyphslot_ensure_outline(font, unicode); + if (!glyph) { + return 0.0f; + } + + blf_glyph_to_curves(glyph->outline, nurbsbase, scale); + return float(glyph->advance.x) * scale; +} + /** \} */ diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 01346613881..bdc43270eb3 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -186,6 +186,14 @@ struct GlyphBLF *blf_glyph_ensure_subpixel(struct FontBLF *font, int32_t pen_x); #endif +/** + * Convert a character's outlines into curves. + */ +float blf_character_to_curves(FontBLF *font, + unsigned int unicode, + struct ListBase *nurbsbase, + const float scale); + void blf_glyph_free(struct GlyphBLF *g); void blf_glyph_draw( struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, int x, int y); diff --git a/source/blender/blenkernel/intern/vfont.cc b/source/blender/blenkernel/intern/vfont.cc index 7d2945ac89e..e164944719c 100644 --- a/source/blender/blenkernel/intern/vfont.cc +++ b/source/blender/blenkernel/intern/vfont.cc @@ -58,6 +58,9 @@ static PackedFile *get_builtin_packedfile(void); /****************************** VFont Datablock ************************/ +const void *builtin_font_data = nullptr; +int builtin_font_size = 0; + static void vfont_init_data(ID *id) { VFont *vfont = (VFont *)id; @@ -218,9 +221,6 @@ void BKE_vfont_free_data(VFont *vfont) } } -static const void *builtin_font_data = nullptr; -static int builtin_font_size = 0; - bool BKE_vfont_is_builtin(const VFont *vfont) { return STREQ(vfont->filepath, FO_BUILTIN_NAME); @@ -1004,10 +1004,8 @@ static bool vfont_to_curve(Object *ob, che = find_vfont_char(vfd, ascii); BLI_rw_mutex_unlock(&vfont_rwlock); - /* The character wasn't in the current curve base so load it. - * But if the font is built-in then do not try loading since - * whole font is in the memory already. */ - if (che == nullptr && BKE_vfont_is_builtin(vfont) == false) { + /* The character wasn't in the current curve base so load it. */ + if (che == nullptr) { BLI_rw_mutex_lock(&vfont_rwlock, THREAD_LOCK_WRITE); /* Check it once again, char might have been already load * between previous #BLI_rw_mutex_unlock() and this #BLI_rw_mutex_lock(). diff --git a/source/blender/blenkernel/intern/vfontdata_freetype.cc b/source/blender/blenkernel/intern/vfontdata_freetype.cc index 7ab71c29422..94bda6cb19b 100644 --- a/source/blender/blenkernel/intern/vfontdata_freetype.cc +++ b/source/blender/blenkernel/intern/vfontdata_freetype.cc @@ -12,321 +12,48 @@ * Code that uses exotic character maps is present but commented out. */ -#include -#include FT_FREETYPE_H -/* not needed yet */ -// #include FT_GLYPH_H -// #include FT_BBOX_H -// #include FT_SIZES_H -// #include - #include "MEM_guardedalloc.h" +#include "BLF_api.h" + #include "BLI_ghash.h" #include "BLI_listbase.h" -#include "BLI_math_geom.h" -#include "BLI_math_vector.h" #include "BLI_string.h" #include "BLI_string_utf8.h" -#include "BLI_utildefines.h" #include "BKE_curve.hh" +#include "BKE_vfont.h" #include "BKE_vfontdata.h" -#include "DNA_curve_types.h" #include "DNA_packedFile_types.h" #include "DNA_vfont_types.h" -static void freetype_outline_to_curves(FT_Outline ftoutline, - ListBase *nurbsbase, - const float scale) -{ - const float eps = 0.0001f; - const float eps_sq = eps * eps; - Nurb *nu; - BezTriple *bezt; - float dx, dy; - int j, k, l, l_first = 0; - - /* initialize as -1 to add 1 on first loop each time */ - int contour_prev; - - /* Start converting the FT data */ - int *onpoints = (int *)MEM_callocN((ftoutline.n_contours) * sizeof(int), "onpoints"); - - /* Get number of on-curve points for bezier-triples (including conic virtual on-points). */ - for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) { - const int n = ftoutline.contours[j] - contour_prev; - contour_prev = ftoutline.contours[j]; - - for (k = 0; k < n; k++) { - l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k; - if (k == 0) { - l_first = l; - } - - if (ftoutline.tags[l] == FT_Curve_Tag_On) { - onpoints[j]++; - } - - { - const int l_next = (k < n - 1) ? (l + 1) : l_first; - if (ftoutline.tags[l] == FT_Curve_Tag_Conic && - ftoutline.tags[l_next] == FT_Curve_Tag_Conic) { - onpoints[j]++; - } - } - } - } - - /* contour loop, bezier & conic styles merged */ - for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) { - const int n = ftoutline.contours[j] - contour_prev; - contour_prev = ftoutline.contours[j]; - - /* add new curve */ - nu = (Nurb *)MEM_callocN(sizeof(Nurb), "objfnt_nurb"); - bezt = (BezTriple *)MEM_callocN((onpoints[j]) * sizeof(BezTriple), "objfnt_bezt"); - BLI_addtail(nurbsbase, nu); - - nu->type = CU_BEZIER; - nu->pntsu = onpoints[j]; - nu->resolu = 8; - nu->flagu = CU_NURB_CYCLIC; - nu->bezt = bezt; - - /* individual curve loop, start-end */ - for (k = 0; k < n; k++) { - l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k; - if (k == 0) { - l_first = l; - } - - /* virtual conic on-curve points */ - { - const int l_next = (k < n - 1) ? (l + 1) : l_first; - if (ftoutline.tags[l] == FT_Curve_Tag_Conic && - ftoutline.tags[l_next] == FT_Curve_Tag_Conic) { - dx = (ftoutline.points[l].x + ftoutline.points[l_next].x) * scale / 2.0f; - dy = (ftoutline.points[l].y + ftoutline.points[l_next].y) * scale / 2.0f; - - /* left handle */ - bezt->vec[0][0] = (dx + (2 * ftoutline.points[l].x) * scale) / 3.0f; - bezt->vec[0][1] = (dy + (2 * ftoutline.points[l].y) * scale) / 3.0f; - - /* midpoint (virtual on-curve point) */ - bezt->vec[1][0] = dx; - bezt->vec[1][1] = dy; - - /* right handle */ - bezt->vec[2][0] = (dx + (2 * ftoutline.points[l_next].x) * scale) / 3.0f; - bezt->vec[2][1] = (dy + (2 * ftoutline.points[l_next].y) * scale) / 3.0f; - - bezt->h1 = bezt->h2 = HD_ALIGN; - bezt->radius = 1.0f; - bezt++; - } - } - - /* on-curve points */ - if (ftoutline.tags[l] == FT_Curve_Tag_On) { - const int l_prev = (k > 0) ? (l - 1) : ftoutline.contours[j]; - const int l_next = (k < n - 1) ? (l + 1) : l_first; - - /* left handle */ - if (ftoutline.tags[l_prev] == FT_Curve_Tag_Cubic) { - bezt->vec[0][0] = ftoutline.points[l_prev].x * scale; - bezt->vec[0][1] = ftoutline.points[l_prev].y * scale; - bezt->h1 = HD_FREE; - } - else if (ftoutline.tags[l_prev] == FT_Curve_Tag_Conic) { - bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l_prev].x)) * scale / - 3.0f; - bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l_prev].y)) * scale / - 3.0f; - bezt->h1 = HD_FREE; - } - else { - bezt->vec[0][0] = ftoutline.points[l].x * scale - - (ftoutline.points[l].x - ftoutline.points[l_prev].x) * scale / 3.0f; - bezt->vec[0][1] = ftoutline.points[l].y * scale - - (ftoutline.points[l].y - ftoutline.points[l_prev].y) * scale / 3.0f; - bezt->h1 = HD_VECT; - } - - /* midpoint (on-curve point) */ - bezt->vec[1][0] = ftoutline.points[l].x * scale; - bezt->vec[1][1] = ftoutline.points[l].y * scale; - - /* right handle */ - if (ftoutline.tags[l_next] == FT_Curve_Tag_Cubic) { - bezt->vec[2][0] = ftoutline.points[l_next].x * scale; - bezt->vec[2][1] = ftoutline.points[l_next].y * scale; - bezt->h2 = HD_FREE; - } - else if (ftoutline.tags[l_next] == FT_Curve_Tag_Conic) { - bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l_next].x)) * scale / - 3.0f; - bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l_next].y)) * scale / - 3.0f; - bezt->h2 = HD_FREE; - } - else { - bezt->vec[2][0] = ftoutline.points[l].x * scale - - (ftoutline.points[l].x - ftoutline.points[l_next].x) * scale / 3.0f; - bezt->vec[2][1] = ftoutline.points[l].y * scale - - (ftoutline.points[l].y - ftoutline.points[l_next].y) * scale / 3.0f; - bezt->h2 = HD_VECT; - } - - /* get the handles that are aligned, tricky... - * - check if one of them is a vector handle. - * - dist_squared_to_line_v2, check if the three beztriple points are on one line - * - len_squared_v2v2, see if there's a distance between the three points - * - len_squared_v2v2 again, to check the angle between the handles - */ - if ((bezt->h1 != HD_VECT && bezt->h2 != HD_VECT) && - (dist_squared_to_line_v2(bezt->vec[0], bezt->vec[1], bezt->vec[2]) < - (0.001f * 0.001f)) && - (len_squared_v2v2(bezt->vec[0], bezt->vec[1]) > eps_sq) && - (len_squared_v2v2(bezt->vec[1], bezt->vec[2]) > eps_sq) && - (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) > eps_sq) && - (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) > - max_ff(len_squared_v2v2(bezt->vec[0], bezt->vec[1]), - len_squared_v2v2(bezt->vec[1], bezt->vec[2])))) - { - bezt->h1 = bezt->h2 = HD_ALIGN; - } - bezt->radius = 1.0f; - bezt++; - } - } - } - - MEM_freeN(onpoints); -} - -static VChar *freetypechar_to_vchar(FT_Face face, FT_ULong charcode, const VFontData *vfd) -{ - FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); - if (FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP) != FT_Err_Ok) { - return nullptr; - } - - VChar *che = (VChar *)MEM_callocN(sizeof(VChar), "objfnt_char"); - freetype_outline_to_curves(face->glyph->outline, &che->nurbsbase, vfd->scale); - che->index = charcode; - che->width = face->glyph->advance.x * vfd->scale; - BLI_ghash_insert(vfd->characters, POINTER_FROM_UINT(che->index), che); - - return che; -} - -static FT_Face vfont_face_load_from_packed_file(FT_Library library, PackedFile *pf) -{ - FT_Face face = nullptr; - FT_New_Memory_Face(library, static_cast(pf->data), pf->size, 0, &face); - if (!face) { - return nullptr; - } - - /* Font must contain vectors, not bitmaps. */ - if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) { - FT_Done_Face(face); - return nullptr; - } - - /* Select a character map. */ - FT_Error err = FT_Select_Charmap(face, FT_ENCODING_UNICODE); - if (err) { - err = FT_Select_Charmap(face, FT_ENCODING_APPLE_ROMAN); - } - if (err && face->num_charmaps > 0) { - err = FT_Select_Charmap(face, face->charmaps[0]->encoding); - } - if (err) { - FT_Done_Face(face); - return nullptr; - } - - /* Test that we can load glyphs from this font. */ - FT_UInt glyph_index = 0; - FT_Get_First_Char(face, &glyph_index); - if (!glyph_index || - FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP) != FT_Err_Ok) - { - FT_Done_Face(face); - return nullptr; - } - - return face; -} +extern const void *builtin_font_data; +extern int builtin_font_size; VFontData *BKE_vfontdata_from_freetypefont(PackedFile *pf) { - FT_Library library = nullptr; - if (FT_Init_FreeType(&library) != FT_Err_Ok) { - return nullptr; - } - - FT_Face face = vfont_face_load_from_packed_file(library, pf); - if (!face) { - FT_Done_FreeType(library); + int fontid = BLF_load_mem("FTVFont", static_cast(pf->data), pf->size); + if (fontid == -1) { return nullptr; } /* allocate blender font */ VFontData *vfd = static_cast(MEM_callocN(sizeof(*vfd), "FTVFontData")); - /* Get the name. */ - if (face->family_name) { - SNPRINTF(vfd->name, "%s %s", face->family_name, face->style_name); - BLI_str_utf8_invalid_strip(vfd->name, strlen(vfd->name)); - } + /* Get the font name. */ + char *name = BLF_display_name_from_id(fontid); + STRNCPY(vfd->name, name); + /* BLF_display_name result must be freed. */ + MEM_freeN(name); - /* Blender default BFont is not "complete". */ - const bool complete_font = (face->ascender != 0) && (face->descender != 0) && - (face->ascender != face->descender); + BLI_str_utf8_invalid_strip(vfd->name, ARRAY_SIZE(vfd->name)); - if (complete_font) { - /* We can get descender as well, but we simple store descender in relation to the ascender. - * Also note that descender is stored as a negative number. */ - vfd->ascender = float(face->ascender) / (face->ascender - face->descender); - } - else { - vfd->ascender = 0.8f; - vfd->em_height = 1.0f; - } + BLF_get_vfont_metrics(fontid, &vfd->ascender, &vfd->em_height, &vfd->scale); - /* Adjust font size */ - if (face->bbox.yMax != face->bbox.yMin) { - vfd->scale = float(1.0 / double(face->bbox.yMax - face->bbox.yMin)); + vfd->characters = BLI_ghash_int_new_ex(__func__, 255); - if (complete_font) { - vfd->em_height = float(face->ascender - face->descender) / - (face->bbox.yMax - face->bbox.yMin); - } - } - else { - vfd->scale = 1.0f / 1000.0f; - } - - /* Load the first 256 glyphs. */ - - const FT_ULong preload_count = 256; - vfd->characters = BLI_ghash_int_new_ex(__func__, preload_count); - - FT_ULong charcode = 0; - FT_UInt glyph_index; - for (int i = 0; i < preload_count; i++) { - charcode = FT_Get_Next_Char(face, charcode, &glyph_index); - if (!charcode || !glyph_index) { - break; - } - freetypechar_to_vchar(face, charcode, vfd); - } - - FT_Done_FreeType(library); + BLF_unload_id(fontid); return vfd; } @@ -354,32 +81,33 @@ VChar *BKE_vfontdata_char_from_freetypefont(VFont *vfont, ulong character) return nullptr; } - /* nullptr when the font file can't be found on disk. */ - if (vfont->temp_pf == nullptr) { + int font_id = -1; + + if (BKE_vfont_is_builtin(vfont)) { + font_id = BLF_load_mem(vfont->data->name, + static_cast(builtin_font_data), + builtin_font_size); + } + else if (vfont->temp_pf) { + font_id = BLF_load_mem(vfont->data->name, + static_cast(vfont->temp_pf->data), + vfont->temp_pf->size); + } + + if (font_id == -1) { return nullptr; } - /* Initialize Freetype. */ - FT_Library library = nullptr; - FT_Error err = FT_Init_FreeType(&library); - if (err) { - // error("Failed to load the Freetype font library"); - return nullptr; - } + VChar *che = (VChar *)MEM_callocN(sizeof(VChar), "objfnt_char"); + che->index = character; - FT_Face face = vfont_face_load_from_packed_file(library, vfont->temp_pf); - if (!face) { - FT_Done_FreeType(library); - return nullptr; - } + /* need to set a size for embolden, etc. */ + BLF_size(font_id, 16); - /* Load the character */ - VChar *che = freetypechar_to_vchar(face, character, vfont->data); - - /* Free Freetype */ - FT_Done_Face(face); - FT_Done_FreeType(library); + che->width = BLF_character_to_curves(font_id, character, &che->nurbsbase, vfont->data->scale); + BLI_ghash_insert(vfont->data->characters, POINTER_FROM_UINT(che->index), che); + BLF_unload_id(font_id); return che; } @@ -392,84 +120,3 @@ VChar *BKE_vfontdata_char_copy(const VChar *vchar_src) return vchar_dst; } - -/** - * from: http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-1 - * - * Vectorial representation of Freetype glyphs - * - * The source format of outlines is a collection of closed paths called "contours". Each contour is - * made of a series of line segments and bezier arcs. Depending on the file format, these can be - * second-order or third-order polynomials. The former are also called quadratic or conic arcs, and - * they come from the TrueType format. The latter are called cubic arcs and mostly come from the - * Type1 format. - * - * Each arc is described through a series of start, end and control points. - * Each point of the outline has a specific tag which indicates whether it is - * used to describe a line segment or an arc. - * The following rules are applied to decompose the contour's points into segments and arcs : - * - * # two successive "on" points indicate a line segment joining them. - * - * # one conic "off" point midst two "on" points indicates a conic bezier arc, - * the "off" point being the control point, and the "on" ones the start and end points. - * - * # Two successive cubic "off" points midst two "on" points indicate a cubic bezier arc. - * There must be exactly two cubic control points and two on points for each cubic arc - * (using a single cubic "off" point between two "on" points is forbidden, for example). - * - * # finally, two successive conic "off" points forces the rasterizer to create - * (during the scan-line conversion process exclusively) a virtual "on" point midst them, - * at their exact middle. - * This greatly facilitates the definition of successive conic bezier arcs. - * Moreover, it's the way outlines are described in the TrueType specification. - * - * Note that it is possible to mix conic and cubic arcs in a single contour, even though no current - * font driver produces such outlines. - * - *
- *                                   *            # on
- *                                                * off
- *                                __---__
- *   #-__                      _--       -_
- *       --__                _-            -
- *           --__           #               \
- *               --__                        #
- *                   -#
- *                            Two "on" points
- *    Two "on" points       and one "conic" point
- *                             between them
- *                 *
- *   #            __      Two "on" points with two "conic"
- *    \          -  -     points between them. The point
- *     \        /    \    marked '0' is the middle of the
- *      -      0      \   "off" points, and is a 'virtual'
- *       -_  _-       #   "on" point where the curve passes.
- *         --             It does not appear in the point
- *                        list.
- *         *
- *         *                # on
- *                    *     * off
- *          __---__
- *       _--       -_
- *     _-            -
- *    #               \
- *                     #
- *
- *      Two "on" points
- *    and two "cubic" point
- *       between them
- * 
- * - * Each glyphs original outline points are located on a grid of indivisible units. - * The points are stored in the font file as 16-bit integer grid coordinates, - * with the grid origin's being at (0, 0); they thus range from -16384 to 16383. - * - * Convert conic to bezier arcs: - * Conic P0 P1 P2 - * Bezier B0 B1 B2 B3 - * B0=P0 - * B1=(P0+2*P1)/3 - * B2=(P2+2*P1)/3 - * B3=P2 - */