VFont: Use BLF for font & glyph loading #110187

Merged
Harley Acheson merged 22 commits from Harley/blender:vfontblf into main 2023-11-23 18:12:15 +01:00
6 changed files with 447 additions and 402 deletions

View File

@ -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.
*/

View File

@ -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)
{

View File

@ -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.
*
* <pre>
* * # 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
* </pre>
*
* 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<int *>(
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<BezTriple *>(
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;
}
/** \} */

View File

@ -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);

View File

@ -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().

View File

@ -12,321 +12,48 @@
* Code that uses exotic character maps is present but commented out.
*/
#include <ft2build.h>
#include FT_FREETYPE_H
/* not needed yet */
// #include FT_GLYPH_H
// #include FT_BBOX_H
// #include FT_SIZES_H
// #include <freetype/ttnameid.h>
#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<const FT_Byte *>(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<const unsigned char *>(pf->data), pf->size);
if (fontid == -1) {
return nullptr;
}
/* allocate blender font */
VFontData *vfd = static_cast<VFontData *>(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<const unsigned char *>(builtin_font_data),
builtin_font_size);
}
else if (vfont->temp_pf) {
font_id = BLF_load_mem(vfont->data->name,
static_cast<const unsigned char *>(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.
*
* <pre>
* * # 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
* </pre>
*
* 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
*/