Coverage Report

Created: 2026-02-26 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-cff-width-optimizer.hh
Line
Count
Source
1
/*
2
 * CFF Width Optimizer
3
 *
4
 * Determines optimal defaultWidthX and nominalWidthX values
5
 * to minimize CharString byte cost.
6
 *
7
 * Based on fontTools.cffLib.width
8
 */
9
10
#ifndef HB_CFF_WIDTH_OPTIMIZER_HH
11
#define HB_CFF_WIDTH_OPTIMIZER_HH
12
13
#include "hb.hh"
14
15
namespace CFF {
16
17
/* Calculate byte cost for encoding a width delta */
18
static inline unsigned
19
width_delta_cost (int delta)
20
0
{
21
0
  delta = abs (delta);
22
0
  if (delta <= 107) return 1;
23
0
  if (delta <= 1131) return 2;
24
0
  return 5;
25
0
}
26
27
/* Cumulative sum forward */
28
static void
29
cumsum_forward (const hb_hashmap_t<unsigned, unsigned> &freq,
30
                unsigned min_w, unsigned max_w,
31
                hb_vector_t<unsigned> &cumsum)
32
0
{
33
0
  cumsum.resize (max_w - min_w + 1);
34
0
  unsigned v = 0;
35
0
  for (unsigned x = min_w; x <= max_w; x++)
36
0
  {
37
0
    v += freq.get (x);
38
0
    cumsum[x - min_w] = v;
39
0
  }
40
0
}
41
42
/* Cumulative max forward */
43
static void
44
cummax_forward (const hb_hashmap_t<unsigned, unsigned> &freq,
45
                unsigned min_w, unsigned max_w,
46
                hb_vector_t<unsigned> &cummax)
47
0
{
48
0
  cummax.resize (max_w - min_w + 1);
49
0
  unsigned v = 0;
50
0
  for (unsigned x = min_w; x <= max_w; x++)
51
0
  {
52
0
    v = hb_max (v, freq.get (x));
53
0
    cummax[x - min_w] = v;
54
0
  }
55
0
}
56
57
/* Cumulative sum backward */
58
static void
59
cumsum_backward (const hb_hashmap_t<unsigned, unsigned> &freq,
60
                 unsigned min_w, unsigned max_w,
61
                 hb_vector_t<unsigned> &cumsum)
62
0
{
63
0
  cumsum.resize (max_w - min_w + 1);
64
0
  unsigned v = 0;
65
0
  for (int x = (int) max_w; x >= (int) min_w; x--)
66
0
  {
67
0
    v += freq.get ((unsigned) x);
68
0
    cumsum[x - min_w] = v;
69
0
  }
70
0
}
71
72
/* Cumulative max backward */
73
static void
74
cummax_backward (const hb_hashmap_t<unsigned, unsigned> &freq,
75
                 unsigned min_w, unsigned max_w,
76
                 hb_vector_t<unsigned> &cummax)
77
0
{
78
0
  cummax.resize (max_w - min_w + 1);
79
0
  unsigned v = 0;
80
0
  for (int x = (int) max_w; x >= (int) min_w; x--)
81
0
  {
82
0
    v = hb_max (v, freq.get ((unsigned) x));
83
0
    cummax[x - min_w] = v;
84
0
  }
85
0
}
86
87
/* Helper to safely get cumulative value with bounds checking */
88
static inline unsigned
89
safe_get (const hb_vector_t<unsigned> &vec, int x, unsigned min_w, unsigned max_w)
90
0
{
91
0
  if (x < (int) min_w || x > (int) max_w) return 0;
92
0
  return vec[x - min_w];
93
0
}
94
95
/* Optimize defaultWidthX and nominalWidthX from a list of widths
96
 * O(UPEM+numGlyphs) algorithm from fontTools.cffLib.width */
97
static void
98
optimize_widths (const hb_vector_t<unsigned> &width_list,
99
                 unsigned &default_width,
100
                 unsigned &nominal_width)
101
0
{
102
0
  if (width_list.length == 0)
103
0
  {
104
0
    default_width = nominal_width = 0;
105
0
    return;
106
0
  }
107
108
  /* Build frequency map */
109
0
  hb_hashmap_t<unsigned, unsigned> widths;
110
0
  unsigned min_w = width_list[0];
111
0
  unsigned max_w = width_list[0];
112
113
0
  for (unsigned w : width_list)
114
0
  {
115
0
    widths.set (w, widths.get (w) + 1);
116
0
    min_w = hb_min (min_w, w);
117
0
    max_w = hb_max (max_w, w);
118
0
  }
119
120
  /* Cumulative sum/max forward/backward */
121
0
  hb_vector_t<unsigned> cumFrqU, cumMaxU, cumFrqD, cumMaxD;
122
0
  cumsum_forward (widths, min_w, max_w, cumFrqU);
123
0
  cummax_forward (widths, min_w, max_w, cumMaxU);
124
0
  cumsum_backward (widths, min_w, max_w, cumFrqD);
125
0
  cummax_backward (widths, min_w, max_w, cumMaxD);
126
127
  /* Cost per nominal choice, without default consideration */
128
0
  auto nomnCost = [&] (unsigned x) -> unsigned {
129
0
    return safe_get (cumFrqU, x, min_w, max_w) +
130
0
           safe_get (cumFrqU, x - 108, min_w, max_w) +
131
0
           safe_get (cumFrqU, x - 1132, min_w, max_w) * 3 +
132
0
           safe_get (cumFrqD, x, min_w, max_w) +
133
0
           safe_get (cumFrqD, x + 108, min_w, max_w) +
134
0
           safe_get (cumFrqD, x + 1132, min_w, max_w) * 3 -
135
0
           widths.get (x);
136
0
  };
137
138
  /* Cost-saving per nominal choice, by best default choice */
139
0
  auto dfltCost = [&] (unsigned x) -> unsigned {
140
0
    unsigned u = hb_max (hb_max (safe_get (cumMaxU, x, min_w, max_w),
141
0
                                  safe_get (cumMaxU, x - 108, min_w, max_w) * 2),
142
0
                         safe_get (cumMaxU, x - 1132, min_w, max_w) * 5);
143
0
    unsigned d = hb_max (hb_max (safe_get (cumMaxD, x, min_w, max_w),
144
0
                                  safe_get (cumMaxD, x + 108, min_w, max_w) * 2),
145
0
                         safe_get (cumMaxD, x + 1132, min_w, max_w) * 5);
146
0
    return hb_max (u, d);
147
0
  };
148
149
  /* Find best nominal */
150
0
  unsigned best_nominal = min_w;
151
0
  unsigned best_cost = nomnCost (min_w) - dfltCost (min_w);
152
153
0
  for (unsigned x = min_w + 1; x <= max_w; x++)
154
0
  {
155
0
    unsigned cost = nomnCost (x) - dfltCost (x);
156
0
    if (cost < best_cost)
157
0
    {
158
0
      best_cost = cost;
159
0
      best_nominal = x;
160
0
    }
161
0
  }
162
163
  /* Work back the best default */
164
0
  unsigned best_default = best_nominal;
165
0
  unsigned best_default_cost = (unsigned) -1;
166
167
  /* Check candidates around best_nominal */
168
0
  int candidates[] = {
169
0
    (int) best_nominal,
170
0
    (int) best_nominal - 108,
171
0
    (int) best_nominal - 1132,
172
0
    (int) best_nominal + 108,
173
0
    (int) best_nominal + 1132
174
0
  };
175
176
0
  for (int candidate : candidates)
177
0
  {
178
0
    if (candidate < (int) min_w || candidate > (int) max_w)
179
0
      continue;
180
181
    /* Compute actual cost with this default */
182
0
    unsigned cost = 0;
183
0
    for (auto kv : widths.iter ())
184
0
    {
185
0
      unsigned w = kv.first;
186
0
      unsigned freq = kv.second;
187
188
0
      if (w == (unsigned) candidate)
189
0
        continue;
190
191
0
      cost += freq * width_delta_cost ((int) w - (int) best_nominal);
192
0
    }
193
194
0
    if (cost < best_default_cost)
195
0
    {
196
0
      best_default_cost = cost;
197
0
      best_default = (unsigned) candidate;
198
0
    }
199
0
  }
200
201
0
  default_width = best_default;
202
0
  nominal_width = best_nominal;
203
0
}
204
205
} /* namespace CFF */
206
207
#endif /* HB_CFF_WIDTH_OPTIMIZER_HH */