Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/harfbuzz/src/hb-subset-cff2-round.hh
Line
Count
Source
1
/*
2
 * CFF2 instancing: round absolute outline points and stem edges before
3
 * converting them back to relative operands (fonttools/fonttools#4338).
4
 */
5
6
#ifndef HB_SUBSET_CFF2_ROUND_HH
7
#define HB_SUBSET_CFF2_ROUND_HH
8
9
#include "hb-cff2-interp-cs.hh"
10
11
namespace CFF {
12
13
struct cff2_cs_rounding_t
14
{
15
  struct coordinate_t
16
  {
17
    double value = 0.;
18
    hb_vector_t<double> deltas;
19
20
    bool round (blend_arg_t &arg, unsigned k)
21
0
    {
22
0
      if (unlikely (!deltas.resize (k) || !arg.deltas.resize (k)))
23
0
  return false;
24
25
0
      double previous = value;
26
0
      value += arg.to_real ();
27
      /* OpenType rounding, including negative half-integers. */
28
0
      arg.number_t::set_real (floor (value + .5) - floor (previous + .5));
29
0
      bool variable = false;
30
0
      for (unsigned r = 0; r < k; r++)
31
0
      {
32
0
  previous = deltas.arrayZ[r];
33
0
  deltas.arrayZ[r] += arg.deltas.arrayZ[r].to_real ();
34
0
  double d = floor (deltas.arrayZ[r] + .5) - floor (previous + .5);
35
0
  arg.deltas.arrayZ[r].set_real (d);
36
0
  variable |= d != 0.;
37
0
      }
38
0
      if (!variable) arg.deltas.clear ();
39
0
      return true;
40
0
    }
41
  };
42
43
  bool round (op_code_t op, hb_array_t<blend_arg_t> args, unsigned k)
44
0
  {
45
0
    unsigned n = args.length;
46
0
    switch (op)
47
0
    {
48
0
      case OpCode_rmoveto:
49
0
  return n == 2 && alternating (args, k);
50
0
      case OpCode_hmoveto:
51
0
      case OpCode_vmoveto:
52
0
  return n == 1 && point[op == OpCode_vmoveto].round (args[0], k);
53
0
      case OpCode_rlineto:
54
0
  return n && !(n % 2) && alternating (args, k);
55
0
      case OpCode_rrcurveto:
56
0
  return n && !(n % 6) && alternating (args, k);
57
0
      case OpCode_rcurveline:
58
0
  return n >= 8 && !((n - 2) % 6) && alternating (args, k);
59
0
      case OpCode_rlinecurve:
60
0
  return n >= 8 && !(n % 2) && alternating (args, k);
61
0
      case OpCode_hlineto:
62
0
      case OpCode_vlineto:
63
0
  return n && alternating (args, k, op == OpCode_vlineto);
64
65
0
      case OpCode_hhcurveto:
66
0
      case OpCode_vvcurveto:
67
0
      {
68
0
  if (unlikely (n < 4 || (n % 4) > 1)) return false;
69
0
  unsigned axis = op == OpCode_vvcurveto;
70
0
  unsigned i = 0;
71
0
  if (n & 1)
72
0
    if (!point[!axis].round (args[i++], k)) return false;
73
0
  for (; i < n; i += 4)
74
0
  {
75
0
    if (!point[axis].round (args[i], k) ||
76
0
        !point[0].round (args[i + 1], k) ||
77
0
        !point[1].round (args[i + 2], k) ||
78
0
        !point[axis].round (args[i + 3], k)) return false;
79
0
  }
80
0
  return true;
81
0
      }
82
83
0
      case OpCode_hvcurveto:
84
0
      case OpCode_vhcurveto:
85
0
      {
86
0
  if (unlikely (n < 4 || (n % 4) > 1)) return false;
87
0
  unsigned axis = op == OpCode_vhcurveto;
88
0
  unsigned i = 0;
89
0
  for (; i + 4 <= n; i += 4, axis = !axis)
90
0
  {
91
0
    if (!point[axis].round (args[i], k) ||
92
0
        !point[0].round (args[i + 1], k) ||
93
0
        !point[1].round (args[i + 2], k) ||
94
0
        !point[!axis].round (args[i + 3], k)) return false;
95
0
    if (i + 5 == n)
96
0
      return point[axis].round (args[i + 4], k);
97
0
  }
98
0
  return true;
99
0
      }
100
101
0
      case OpCode_flex:
102
0
  if (unlikely (n != 13 || !alternating (args.sub_array (0, 12), k)))
103
0
    return false;
104
0
  args[12].number_t::set_real (floor (args[12].to_real () + .5));
105
0
  for (auto &d : args[12].deltas) d.set_real (floor (d.to_real () + .5));
106
0
  return true;
107
108
0
      case OpCode_hflex:
109
0
      case OpCode_hflex1:
110
0
      {
111
0
  if (unlikely (n != (op == OpCode_hflex ? 7u : 9u))) return false;
112
  /* These operators implicitly return to the initial y coordinate. */
113
0
  coordinate_t y = point[1];
114
0
  if (unlikely (y.deltas.in_error ())) return false;
115
0
  for (unsigned i = 0; i < n; i++)
116
0
  {
117
0
    unsigned axis = op == OpCode_hflex ? i == 2 : i == 1 || i == 3 || i == 7;
118
0
    if (!point[axis].round (args[i], k)) return false;
119
0
  }
120
0
  point[1] = std::move (y);
121
0
  return true;
122
0
      }
123
124
0
      case OpCode_hstem:
125
0
      case OpCode_hstemhm:
126
0
      case OpCode_vstem:
127
0
      case OpCode_vstemhm:
128
0
      case OpCode_hintmask:
129
0
      case OpCode_cntrmask:
130
0
      {
131
  /* Stem operands are successive edges, relative to zero per operator.
132
   * A mask's operands declare implicit vertical stems. */
133
0
  coordinate_t stem;
134
0
  if (unlikely (n % 2)) return false;
135
0
  for (auto &arg : args)
136
0
    if (!stem.round (arg, k)) return false;
137
0
  return true;
138
0
      }
139
140
0
      default:
141
0
  return true;
142
0
    }
143
0
  }
144
145
  private:
146
  bool alternating (hb_array_t<blend_arg_t> args, unsigned k, unsigned axis = 0)
147
0
  {
148
0
    for (auto &arg : args)
149
0
    {
150
0
      if (!point[axis].round (arg, k)) return false;
151
0
      axis = !axis;
152
0
    }
153
0
    return true;
154
0
  }
155
156
  coordinate_t point[2];
157
};
158
159
} /* namespace CFF */
160
161
#endif /* HB_SUBSET_CFF2_ROUND_HH */