1/*
2 * Copyright © 2018 Adobe Inc.
3 *
4 * This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Adobe Author(s): Michiharu Ariza
25 */
26#ifndef HB_CFF2_INTERP_CS_HH
27#define HB_CFF2_INTERP_CS_HH
28
29#include "hb.hh"
30#include "hb-cff-interp-cs-common.hh"
31
32namespace CFF {
33
34using namespace OT;
35
36struct blend_arg_t : number_t
37{
38 void set_int (int v) { reset_blends (); number_t::set_int (v); }
39 void set_fixed (int32_t v) { reset_blends (); number_t::set_fixed (v); }
40 void set_real (double v) { reset_blends (); number_t::set_real (v); }
41
42 void set_blends (unsigned int numValues_, unsigned int valueIndex_,
43 hb_array_t<const blend_arg_t> blends_)
44 {
45 numValues = numValues_;
46 valueIndex = valueIndex_;
47 unsigned numBlends = blends_.length;
48 if (unlikely (!deltas.resize_exact (numBlends)))
49 return;
50 for (unsigned int i = 0; i < numBlends; i++)
51 deltas.arrayZ[i] = blends_.arrayZ[i];
52 }
53
54 bool blending () const { return deltas.length > 0; }
55 void reset_blends ()
56 {
57 numValues = valueIndex = 0;
58 deltas.shrink (0);
59 }
60
61 unsigned int numValues;
62 unsigned int valueIndex;
63 hb_vector_t<number_t> deltas;
64};
65
66typedef biased_subrs_t<CFF2Subrs> cff2_biased_subrs_t;
67
68template <typename ELEM>
69struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
70{
71 template <typename ACC>
72 cff2_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd,
73 const int *coords_=nullptr, unsigned int num_coords_=0)
74 : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs)
75 {
76 coords = coords_;
77 num_coords = num_coords_;
78 varStore = acc.varStore;
79 seen_blend = false;
80 seen_vsindex_ = false;
81 scalars.init ();
82 do_blend = num_coords && coords && varStore->size;
83 set_ivs (acc.privateDicts[fd].ivs);
84 }
85
86 void fini ()
87 {
88 scalars.fini ();
89 SUPER::fini ();
90 }
91
92 op_code_t fetch_op ()
93 {
94 if (this->str_ref.avail ())
95 return SUPER::fetch_op ();
96
97 /* make up return or endchar op */
98 if (this->callStack.is_empty ())
99 return OpCode_endchar;
100 else
101 return OpCode_return;
102 }
103
104 const ELEM& eval_arg (unsigned int i)
105 {
106 return SUPER::argStack[i];
107 }
108
109 const ELEM& pop_arg ()
110 {
111 return SUPER::argStack.pop ();
112 }
113
114 void process_blend ()
115 {
116 if (!seen_blend)
117 {
118 region_count = varStore->varStore.get_region_index_count (get_ivs ());
119 if (do_blend)
120 {
121 if (unlikely (!scalars.resize_exact (region_count)))
122 SUPER::set_error ();
123 else
124 varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords,
125 &scalars[0], region_count);
126 }
127 seen_blend = true;
128 }
129 }
130
131 void process_vsindex ()
132 {
133 unsigned int index = SUPER::argStack.pop_uint ();
134 if (unlikely (seen_vsindex () || seen_blend))
135 {
136 SUPER::set_error ();
137 }
138 else
139 {
140 set_ivs (index);
141 }
142 seen_vsindex_ = true;
143 }
144
145 unsigned int get_region_count () const { return region_count; }
146 void set_region_count (unsigned int region_count_) { region_count = region_count_; }
147 unsigned int get_ivs () const { return ivs; }
148 void set_ivs (unsigned int ivs_) { ivs = ivs_; }
149 bool seen_vsindex () const { return seen_vsindex_; }
150
151 double blend_deltas (hb_array_t<const ELEM> deltas) const
152 {
153 double v = 0;
154 if (do_blend)
155 {
156 if (likely (scalars.length == deltas.length))
157 {
158 unsigned count = scalars.length;
159 for (unsigned i = 0; i < count; i++)
160 v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real ();
161 }
162 }
163 return v;
164 }
165
166 bool have_coords () const { return num_coords; }
167
168 protected:
169 const int *coords;
170 unsigned int num_coords;
171 const CFF2VariationStore *varStore;
172 unsigned int region_count;
173 unsigned int ivs;
174 hb_vector_t<float> scalars;
175 bool do_blend;
176 bool seen_vsindex_;
177 bool seen_blend;
178
179 typedef cs_interp_env_t<ELEM, CFF2Subrs> SUPER;
180};
181template <typename OPSET, typename PARAM, typename ELEM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t<ELEM>, PARAM>>
182struct cff2_cs_opset_t : cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH>
183{
184 static void process_op (op_code_t op, cff2_cs_interp_env_t<ELEM> &env, PARAM& param)
185 {
186 switch (op) {
187 case OpCode_callsubr:
188 case OpCode_callgsubr:
189 /* a subroutine number shouldn't be a blended value */
190#if 0
191 if (unlikely (env.argStack.peek ().blending ()))
192 {
193 env.set_error ();
194 break;
195 }
196#endif
197 SUPER::process_op (op, env, param);
198 break;
199
200 case OpCode_blendcs:
201 OPSET::process_blend (env, param);
202 break;
203
204 case OpCode_vsindexcs:
205#if 0
206 if (unlikely (env.argStack.peek ().blending ()))
207 {
208 env.set_error ();
209 break;
210 }
211#endif
212 OPSET::process_vsindex (env, param);
213 break;
214
215 default:
216 SUPER::process_op (op, env, param);
217 }
218 }
219
220 template <typename T = ELEM,
221 hb_enable_if (hb_is_same (T, blend_arg_t))>
222 static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env,
223 ELEM &arg,
224 const hb_array_t<const ELEM> blends,
225 unsigned n, unsigned i)
226 {
227 if (env.have_coords ())
228 arg.set_int (round (arg.to_real () + env.blend_deltas (blends)));
229 else
230 arg.set_blends (n, i, blends);
231 }
232 template <typename T = ELEM,
233 hb_enable_if (!hb_is_same (T, blend_arg_t))>
234 static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env,
235 ELEM &arg,
236 const hb_array_t<const ELEM> blends,
237 unsigned n, unsigned i)
238 {
239 arg.set_real (arg.to_real () + env.blend_deltas (blends));
240 }
241
242 static void process_blend (cff2_cs_interp_env_t<ELEM> &env, PARAM& param)
243 {
244 unsigned int n, k;
245
246 env.process_blend ();
247 k = env.get_region_count ();
248 n = env.argStack.pop_uint ();
249 /* copy the blend values into blend array of the default values */
250 unsigned int start = env.argStack.get_count () - ((k+1) * n);
251 /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */
252 if (unlikely (start > env.argStack.get_count ()))
253 {
254 env.set_error ();
255 return;
256 }
257 for (unsigned int i = 0; i < n; i++)
258 {
259 const hb_array_t<const ELEM> blends = env.argStack.sub_array (start + n + (i * k), k);
260 process_arg_blend (env, env.argStack[start + i], blends, n, i);
261 }
262
263 /* pop off blend values leaving default values now adorned with blend values */
264 env.argStack.pop (k * n);
265 }
266
267 static void process_vsindex (cff2_cs_interp_env_t<ELEM> &env, PARAM& param)
268 {
269 env.process_vsindex ();
270 env.clear_args ();
271 }
272
273 private:
274 typedef cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH> SUPER;
275};
276
277template <typename OPSET, typename PARAM, typename ELEM>
278using cff2_cs_interpreter_t = cs_interpreter_t<cff2_cs_interp_env_t<ELEM>, OPSET, PARAM>;
279
280} /* namespace CFF */
281
282#endif /* HB_CFF2_INTERP_CS_HH */
283