/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 */ |