Commit: b9c0eed206b0d7d1b6af6809c6d2cb6c2187bcc8
Author: Harley Acheson
Date:   Thu Jul 7 12:59:16 2022 -0700
Branches: master
https://developer.blender.org/rBb9c0eed206b0d7d1b6af6809c6d2cb6c2187bcc8

BLF: Add Support for Variable Fonts

Add support for Variable/Multiple Master font features. These are fonts
that contain a range of design variations along multiple axes. This
contains no client-facing options.

See D12977 for details and examples

Differential Revision: https://developer.blender.org/D12977

Reviewed by Brecht Van Lommel

===================================================================

M       source/blender/blenfont/intern/blf_font.c
M       source/blender/blenfont/intern/blf_glyph.c
M       source/blender/blenfont/intern/blf_internal_types.h

===================================================================

diff --git a/source/blender/blenfont/intern/blf_font.c 
b/source/blender/blenfont/intern/blf_font.c
index 3e2927d581e..038e73cc928 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -18,7 +18,8 @@
 
 #include FT_FREETYPE_H
 #include FT_GLYPH_H
-#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */
+#include FT_TRUETYPE_TABLES_H  /* For TT_OS2 */
+#include FT_MULTIPLE_MASTERS_H /* Variable font support. */
 
 #include "MEM_guardedalloc.h"
 
@@ -1285,6 +1286,10 @@ FontBLF *blf_font_new(const char *name, const char 
*filepath)
     MEM_freeN(mfile);
   }
 
+  if (FT_HAS_MULTIPLE_MASTERS(font->face)) {
+    FT_Get_MM_Var(font->face, &(font->variations));
+  }
+
   font->name = BLI_strdup(name);
   font->filepath = BLI_strdup(filepath);
   blf_font_fill(font);
@@ -1351,6 +1356,10 @@ FontBLF *blf_font_new_from_mem(const char *name, const 
unsigned char *mem, int m
     return NULL;
   }
 
+  if (FT_HAS_MULTIPLE_MASTERS(font->face)) {
+    FT_Get_MM_Var(font->face, &(font->variations));
+  }
+
   font->name = BLI_strdup(name);
   font->filepath = NULL;
   blf_font_fill(font);
@@ -1365,6 +1374,10 @@ void blf_font_free(FontBLF *font)
     MEM_freeN(font->kerning_cache);
   }
 
+  if (font->variations) {
+    FT_Done_MM_Var(ft_lib, font->variations);
+  }
+
   FT_Done_Face(font->face);
   if (font->filepath) {
     MEM_freeN(font->filepath);
diff --git a/source/blender/blenfont/intern/blf_glyph.c 
b/source/blender/blenfont/intern/blf_glyph.c
index 2eb43e3df43..4db083366c3 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -18,7 +18,8 @@
 #include FT_GLYPH_H
 #include FT_OUTLINE_H
 #include FT_BITMAP_H
-#include FT_ADVANCES_H /* For FT_Get_Advance. */
+#include FT_ADVANCES_H         /* For FT_Get_Advance. */
+#include FT_MULTIPLE_MASTERS_H /* Variable font support. */
 
 #include "MEM_guardedalloc.h"
 
@@ -64,7 +65,9 @@ static GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, 
float size, unsigned i
   GlyphCacheBLF *gc = (GlyphCacheBLF *)font->cache.first;
   while (gc) {
     if (gc->size == size && gc->dpi == dpi && (gc->bold == ((font->flags & 
BLF_BOLD) != 0)) &&
-        (gc->italic == ((font->flags & BLF_ITALIC) != 0))) {
+        (gc->italic == ((font->flags & BLF_ITALIC) != 0)) &&
+        (gc->char_weight == font->char_weight) && (gc->char_slant == 
font->char_slant) &&
+        (gc->char_width == font->char_width) && (gc->char_spacing == 
font->char_spacing)) {
       return gc;
     }
     gc = gc->next;
@@ -82,6 +85,10 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
   gc->dpi = font->dpi;
   gc->bold = ((font->flags & BLF_BOLD) != 0);
   gc->italic = ((font->flags & BLF_ITALIC) != 0);
+  gc->char_weight = font->char_weight;
+  gc->char_slant = font->char_slant;
+  gc->char_width = font->char_width;
+  gc->char_spacing = font->char_spacing;
 
   memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table));
   memset(gc->bucket, 0, sizeof(gc->bucket));
@@ -673,6 +680,96 @@ static bool blf_glyph_render_bitmap(FontBLF *font, 
FT_GlyphSlot glyph)
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name Variations (Multiple Masters) support
+ * \{ */
+
+/**
+ * Return a design axis that matches an identifying tag.
+ *
+ * \param variations: Variation descriptors from `FT_Get_MM_Var`.
+ * \param tag: Axis tag (4-character string as uint), like 'wght'
+ * \param axis_index: returns index of axis in variations array.
+ */
+static FT_Var_Axis *blf_var_axis_by_tag(FT_MM_Var *variations, uint tag, int 
*axis_index)
+{
+  *axis_index = -1;
+  if (!variations) {
+    return NULL;
+  }
+  for (int i = 0; i < (int)variations->num_axis; i++) {
+    if (variations->axis[i].tag == tag) {
+      *axis_index = i;
+      return &(variations->axis)[i];
+      break;
+    }
+  }
+  return NULL;
+}
+
+/**
+ * Convert a float factor to a fixed-point design coordinate.
+ *
+ * \param axis: Pointer to a design space axis structure.
+ * \param factor: -1 to 1 with 0 meaning "default"
+ */
+static FT_Fixed blf_factor_to_coordinate(FT_Var_Axis *axis, float factor)
+{
+  FT_Fixed value = axis->def;
+  if (factor > 0) {
+    /* Map 0-1 to axis->def - axis->maximum */
+    value += (FT_Fixed)((double)(axis->maximum - axis->def) * factor);
+  }
+  else if (factor < 0) {
+    /* Map -1-0 to axis->minimum - axis->def */
+    value += (FT_Fixed)((double)(axis->def - axis->minimum) * factor);
+  }
+  return value;
+}
+
+/**
+ * Alter a face variation axis by a factor
+ *
+ * \param coords: array of design coordinates, per axis.
+ * \param tag: Axis tag (4-character string as uint), like 'wght'
+ * \param factor: -1 to 1 with 0 meaning "default"
+ */
+static bool blf_glyph_set_variation_normalized(FontBLF *font,
+                                               FT_Fixed coords[],
+                                               uint tag,
+                                               float factor)
+{
+  int axis_index;
+  FT_Var_Axis *axis = blf_var_axis_by_tag(font->variations, tag, &axis_index);
+  if (axis && (axis_index < BLF_VARIATIONS_MAX)) {
+    coords[axis_index] = blf_factor_to_coordinate(axis, factor);
+    return true;
+  }
+  return false;
+}
+
+/**
+ * Set a face variation axis to an exact float value
+ *
+ * \param coords: array of design coordinates, per axis.
+ * \param tag: Axis tag (4-character string as uint), like 'opsz'
+ * \param value: New float value. Converted to 16.16 and clamped within 
allowed range.
+ */
+static bool blf_glyph_set_variation_float(FontBLF *font, FT_Fixed coords[], 
uint tag, float value)
+{
+  int axis_index;
+  FT_Var_Axis *axis = blf_var_axis_by_tag(font->variations, tag, &axis_index);
+  if (axis && (axis_index < BLF_VARIATIONS_MAX)) {
+    FT_Fixed int_value = to_16dot16(value);
+    CLAMP(int_value, axis->minimum, axis->maximum);
+    coords[axis_index] = int_value;
+    return true;
+  }
+  return false;
+}
+
+/** \} */
+
 /* -------------------------------------------------------------------- */
 /** \name Glyph Transformations
  * \{ */
@@ -682,9 +779,7 @@ static bool blf_glyph_render_bitmap(FontBLF *font, 
FT_GlyphSlot glyph)
  *
  * \param factor: -1 (min stroke width) <= 0 (normal) => 1 (max boldness).
  */
-static bool UNUSED_FUNCTION(blf_glyph_transform_weight)(FT_GlyphSlot glyph,
-                                                        float factor,
-                                                        bool monospaced)
+static bool blf_glyph_transform_weight(FT_GlyphSlot glyph, float factor, bool 
monospaced)
 {
   if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
     /* Fake bold if the font does not have this variable axis. */
@@ -713,7 +808,7 @@ static bool 
UNUSED_FUNCTION(blf_glyph_transform_weight)(FT_GlyphSlot glyph,
  *
  * \note that left-leaning italics are possible in some RTL writing systems.
  */
-static bool UNUSED_FUNCTION(blf_glyph_transform_slant)(FT_GlyphSlot glyph, 
float factor)
+static bool blf_glyph_transform_slant(FT_GlyphSlot glyph, float factor)
 {
   if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
     FT_Matrix transform = {to_16dot16(1), to_16dot16(factor / 2.0f), 0, 
to_16dot16(1)};
@@ -728,7 +823,7 @@ static bool 
UNUSED_FUNCTION(blf_glyph_transform_slant)(FT_GlyphSlot glyph, float
  *
  * \param factor: -1 (min width) <= 0 (normal) => 1 (max width).
  */
-static bool UNUSED_FUNCTION(blf_glyph_transform_width)(FT_GlyphSlot glyph, 
float factor)
+static bool blf_glyph_transform_width(FT_GlyphSlot glyph, float factor)
 {
   if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
     float scale = (factor * 0.4f) + 1.0f; /* 0.6f - 1.4f */
@@ -740,6 +835,21 @@ static bool 
UNUSED_FUNCTION(blf_glyph_transform_width)(FT_GlyphSlot glyph, float
   return false;
 }
 
+/**
+ * Change glyph advance to alter letter-spacing (tracking).
+ *
+ * \param factor: -1 (min tightness) <= 0 (normal) => 1 (max looseness).
+ */
+static bool blf_glyph_transform_spacing(FT_GlyphSlot glyph, float factor)
+{
+  if (glyph->advance.x > 0) {
+    const int size = glyph->face->size->metrics.height;
+    glyph->advance.x += (FT_Pos)(factor * (float)size / 6.0f);
+    return true;
+  }
+  return false;
+}
+
 /**
  * Transform glyph to fit nicely within a fixed column width.
  */
@@ -791,6 +901,48 @@ static FT_GlyphSlot blf_glyph_render(FontBLF 
*settings_font,
     glyph_font->dpi = settings_font->dpi;
   }
 
+  /* We need to keep track if changes are still needed. */
+  bool weight_done = false;
+  bool slant_done = false;
+  bool width_done = false;
+  bool spacing_done = false;
+
+  /* 70% of maximum weight results in the same amount of boldness and 
horizontal
+   * expansion as the bold version (DejaVuSans-Bold.ttf) of our default font.
+   * Worth reevaluating if we change default font. */
+  float weight = (settings_font->flags & BLF_BOLD) ? 0.7f : 
settings_font->char_weight;
+
+  /* 37.5% of maximum rightward slant results in 6 degree slope, matching 
italic
+   * version (DejaVuSans-Oblique.ttf) of our current font. But a nice median 
when
+   * checking others. Worth reevaluating if we change default font. We could 
also
+   * narrow the glyph slightly as most italics do, but this one does not. */
+  float slant = (settings_font->flags & BLF_ITALIC) ? 0.375f : 
settings_font->char_slant;
+
+  float width = settings_font->char_width;
+  float spacing = settings_font->char_spacing;
+
+  /* Font variations need to be set before glyph loading. Even if new value is 
zero. */
+
+  if (glyph_font->variations) {
+    FT_Fixed coords[BLF_VARIATIONS_MAX];
+    /* Load current design coordinates. */
+    FT_Get_Var_Design_Coordinates(glyph_font->face, BLF_VARIATIONS_MAX, 
&coords[0]);
+    /* Update design coordinates with new values. */
+    weight_done = blf_glyph_set_variation_normalized(
+        glyph_font, coords, blf_variation_axis_weight, weight);
+    slant_done = blf_glyph_set_variation_normalized(
+        glyph_font, coords, blf_variation_axis_slant, slant);
+    width_done = blf_glyph_set_variation_normalized(
+        glyph_font, coords, blf_variation_axis_width, width);
+    spacing_done = blf_glyph_set_variation_normalized(
+        glyph_font, coords, blf_variation_axis_spacing, sp

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
List details, subscription details or unsubscribe:
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to