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