µGFX  2.9
version 2.9
mf_kerning.c
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.io/license.html
6  */
7 
8 #include "mf_kerning.h"
9 
10 #ifndef MF_NO_COMPILE
11 
12 #include <stdbool.h>
13 
14 #if MF_USE_KERNING
15 
16 /* Structure for keeping track of the edge of the glyph as it is rendered. */
17 struct kerning_state_s
18 {
19  gU8 edgepos[MF_KERNING_ZONES];
20  gU8 zoneheight;
21 };
22 
23 /* Pixel callback for analyzing the left edge of a glyph. */
24 static void fit_leftedge(gI16 x, gI16 y, gU8 count, gU8 alpha,
25  void *state)
26 {
27  struct kerning_state_s *s = state;
28  (void)count;
29 
30  if (alpha > 7)
31  {
32  gU8 zone = y / s->zoneheight;
33  if (x < s->edgepos[zone])
34  s->edgepos[zone] = x;
35  }
36 }
37 
38 /* Pixel callback for analyzing the right edge of a glyph. */
39 static void fit_rightedge(gI16 x, gI16 y, gU8 count, gU8 alpha,
40  void *state)
41 {
42  struct kerning_state_s *s = state;
43 
44  if (alpha > 7)
45  {
46  gU8 zone = y / s->zoneheight;
47  x += count - 1;
48  if (x > s->edgepos[zone])
49  s->edgepos[zone] = x;
50  }
51 }
52 
53 /* Should kerning be done against this character? */
54 static bool do_kerning(mf_char c)
55 {
56  /* Just a speed optimization, spaces would be ignored anyway. */
57  if (c == ' ' || c == '\n' || c == '\r' || c == '\t')
58  return false;
59 
60  /* Do not kern against digits, in order to keep values in tables nicely
61  * aligned. Most fonts have constant width for digits. */
62  if (c >= '0' && c <= '9')
63  return false;
64 
65  return true;
66 }
67 
68 //static gI16 min16(gI16 a, gI16 b) { return (a < b) ? a : b; }
69 static gI16 max16(gI16 a, gI16 b) { return (a > b) ? a : b; }
70 static gI16 avg16(gI16 a, gI16 b) { return (a + b) / 2; }
71 
72 gI8 mf_compute_kerning(const struct mf_font_s *font,
73  mf_char c1, mf_char c2)
74 {
75  struct kerning_state_s leftedge, rightedge;
76  gU8 w1, w2, i, min_space;
77  gI16 normal_space, adjust, max_adjust;
78 
79  if (font->flags & MF_FONT_FLAG_MONOSPACE)
80  return 0; /* No kerning for monospace fonts */
81 
82  if (!do_kerning(c1) || !do_kerning(c2))
83  return 0;
84 
85  /* Compute the height of one kerning zone in pixels */
86  i = (font->height + MF_KERNING_ZONES - 1) / MF_KERNING_ZONES;
87  if (i < 1) i = 1;
88 
89  /* Initialize structures */
90  leftedge.zoneheight = rightedge.zoneheight = i;
91  for (i = 0; i < MF_KERNING_ZONES; i++)
92  {
93  leftedge.edgepos[i] = 255;
94  rightedge.edgepos[i] = 0;
95  }
96 
97  /* Analyze the edges of both glyphs. */
98  w1 = mf_render_character(font, 0, 0, c1, fit_rightedge, &rightedge);
99  w2 = mf_render_character(font, 0, 0, c2, fit_leftedge, &leftedge);
100 
101  /* Find the minimum horizontal space between the glyphs. */
102  min_space = 255;
103  for (i = 0; i < MF_KERNING_ZONES; i++)
104  {
105  gU8 space;
106  if (leftedge.edgepos[i] == 255 || rightedge.edgepos[i] == 0)
107  continue; /* Outside glyph area. */
108 
109  space = w1 - rightedge.edgepos[i] + leftedge.edgepos[i];
110  if (space < min_space)
111  min_space = space;
112  }
113 
114  if (min_space == 255)
115  return 0; /* One of the characters is space, or both are punctuation. */
116 
117  /* Compute the adjustment of the glyph position. */
118  normal_space = avg16(w1, w2) * MF_KERNING_SPACE_PERCENT / 100;
119  normal_space += MF_KERNING_SPACE_PIXELS;
120  adjust = normal_space - min_space;
121  max_adjust = -max16(w1, w2) * MF_KERNING_LIMIT / 100;
122 
123  if (adjust > 0) adjust = 0;
124  if (adjust < max_adjust) adjust = max_adjust;
125 
126  return adjust;
127 }
128 
129 #endif
130 
131 #endif //MF_NO_COMPILE