xref: /aosp_15_r20/external/harfbuzz_ng/src/hb-subset-instancer-iup.cc (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1*2d1272b8SAndroid Build Coastguard Worker /*
2*2d1272b8SAndroid Build Coastguard Worker  * Copyright © 2024  Google, Inc.
3*2d1272b8SAndroid Build Coastguard Worker  *
4*2d1272b8SAndroid Build Coastguard Worker  *  This is part of HarfBuzz, a text shaping library.
5*2d1272b8SAndroid Build Coastguard Worker  *
6*2d1272b8SAndroid Build Coastguard Worker  * Permission is hereby granted, without written agreement and without
7*2d1272b8SAndroid Build Coastguard Worker  * license or royalty fees, to use, copy, modify, and distribute this
8*2d1272b8SAndroid Build Coastguard Worker  * software and its documentation for any purpose, provided that the
9*2d1272b8SAndroid Build Coastguard Worker  * above copyright notice and the following two paragraphs appear in
10*2d1272b8SAndroid Build Coastguard Worker  * all copies of this software.
11*2d1272b8SAndroid Build Coastguard Worker  *
12*2d1272b8SAndroid Build Coastguard Worker  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13*2d1272b8SAndroid Build Coastguard Worker  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14*2d1272b8SAndroid Build Coastguard Worker  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15*2d1272b8SAndroid Build Coastguard Worker  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16*2d1272b8SAndroid Build Coastguard Worker  * DAMAGE.
17*2d1272b8SAndroid Build Coastguard Worker  *
18*2d1272b8SAndroid Build Coastguard Worker  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19*2d1272b8SAndroid Build Coastguard Worker  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20*2d1272b8SAndroid Build Coastguard Worker  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21*2d1272b8SAndroid Build Coastguard Worker  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22*2d1272b8SAndroid Build Coastguard Worker  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23*2d1272b8SAndroid Build Coastguard Worker  */
24*2d1272b8SAndroid Build Coastguard Worker 
25*2d1272b8SAndroid Build Coastguard Worker #include "hb-subset-instancer-iup.hh"
26*2d1272b8SAndroid Build Coastguard Worker 
27*2d1272b8SAndroid Build Coastguard Worker /* This file is a straight port of the following:
28*2d1272b8SAndroid Build Coastguard Worker  *
29*2d1272b8SAndroid Build Coastguard Worker  * https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/varLib/iup.py
30*2d1272b8SAndroid Build Coastguard Worker  *
31*2d1272b8SAndroid Build Coastguard Worker  * Where that file returns optimzied deltas vector, we return optimized
32*2d1272b8SAndroid Build Coastguard Worker  * referenced point indices.
33*2d1272b8SAndroid Build Coastguard Worker  */
34*2d1272b8SAndroid Build Coastguard Worker 
35*2d1272b8SAndroid Build Coastguard Worker constexpr static unsigned MAX_LOOKBACK = 8;
36*2d1272b8SAndroid Build Coastguard Worker 
_iup_contour_bound_forced_set(const hb_array_t<const contour_point_t> contour_points,const hb_array_t<const int> x_deltas,const hb_array_t<const int> y_deltas,hb_set_t & forced_set,double tolerance=0.0)37*2d1272b8SAndroid Build Coastguard Worker static void _iup_contour_bound_forced_set (const hb_array_t<const contour_point_t> contour_points,
38*2d1272b8SAndroid Build Coastguard Worker                                            const hb_array_t<const int> x_deltas,
39*2d1272b8SAndroid Build Coastguard Worker                                            const hb_array_t<const int> y_deltas,
40*2d1272b8SAndroid Build Coastguard Worker                                            hb_set_t& forced_set, /* OUT */
41*2d1272b8SAndroid Build Coastguard Worker                                            double tolerance = 0.0)
42*2d1272b8SAndroid Build Coastguard Worker {
43*2d1272b8SAndroid Build Coastguard Worker   unsigned len = contour_points.length;
44*2d1272b8SAndroid Build Coastguard Worker   unsigned next_i = 0;
45*2d1272b8SAndroid Build Coastguard Worker   for (int i = len - 1; i >= 0; i--)
46*2d1272b8SAndroid Build Coastguard Worker   {
47*2d1272b8SAndroid Build Coastguard Worker     unsigned last_i = (len + i -1) % len;
48*2d1272b8SAndroid Build Coastguard Worker     for (unsigned j = 0; j < 2; j++)
49*2d1272b8SAndroid Build Coastguard Worker     {
50*2d1272b8SAndroid Build Coastguard Worker       double cj, lcj, ncj;
51*2d1272b8SAndroid Build Coastguard Worker       int dj, ldj, ndj;
52*2d1272b8SAndroid Build Coastguard Worker       if (j == 0)
53*2d1272b8SAndroid Build Coastguard Worker       {
54*2d1272b8SAndroid Build Coastguard Worker         cj = static_cast<double> (contour_points.arrayZ[i].x);
55*2d1272b8SAndroid Build Coastguard Worker         dj = x_deltas.arrayZ[i];
56*2d1272b8SAndroid Build Coastguard Worker         lcj = static_cast<double> (contour_points.arrayZ[last_i].x);
57*2d1272b8SAndroid Build Coastguard Worker         ldj = x_deltas.arrayZ[last_i];
58*2d1272b8SAndroid Build Coastguard Worker         ncj = static_cast<double> (contour_points.arrayZ[next_i].x);
59*2d1272b8SAndroid Build Coastguard Worker         ndj = x_deltas.arrayZ[next_i];
60*2d1272b8SAndroid Build Coastguard Worker       }
61*2d1272b8SAndroid Build Coastguard Worker       else
62*2d1272b8SAndroid Build Coastguard Worker       {
63*2d1272b8SAndroid Build Coastguard Worker         cj = static_cast<double> (contour_points.arrayZ[i].y);
64*2d1272b8SAndroid Build Coastguard Worker         dj = y_deltas.arrayZ[i];
65*2d1272b8SAndroid Build Coastguard Worker         lcj = static_cast<double> (contour_points.arrayZ[last_i].y);
66*2d1272b8SAndroid Build Coastguard Worker         ldj = y_deltas.arrayZ[last_i];
67*2d1272b8SAndroid Build Coastguard Worker         ncj = static_cast<double> (contour_points.arrayZ[next_i].y);
68*2d1272b8SAndroid Build Coastguard Worker         ndj = y_deltas.arrayZ[next_i];
69*2d1272b8SAndroid Build Coastguard Worker       }
70*2d1272b8SAndroid Build Coastguard Worker 
71*2d1272b8SAndroid Build Coastguard Worker       double c1, c2;
72*2d1272b8SAndroid Build Coastguard Worker       int d1, d2;
73*2d1272b8SAndroid Build Coastguard Worker       if (lcj <= ncj)
74*2d1272b8SAndroid Build Coastguard Worker       {
75*2d1272b8SAndroid Build Coastguard Worker         c1 = lcj;
76*2d1272b8SAndroid Build Coastguard Worker         c2 = ncj;
77*2d1272b8SAndroid Build Coastguard Worker         d1 = ldj;
78*2d1272b8SAndroid Build Coastguard Worker         d2 = ndj;
79*2d1272b8SAndroid Build Coastguard Worker       }
80*2d1272b8SAndroid Build Coastguard Worker       else
81*2d1272b8SAndroid Build Coastguard Worker       {
82*2d1272b8SAndroid Build Coastguard Worker         c1 = ncj;
83*2d1272b8SAndroid Build Coastguard Worker         c2 = lcj;
84*2d1272b8SAndroid Build Coastguard Worker         d1 = ndj;
85*2d1272b8SAndroid Build Coastguard Worker         d2 = ldj;
86*2d1272b8SAndroid Build Coastguard Worker       }
87*2d1272b8SAndroid Build Coastguard Worker 
88*2d1272b8SAndroid Build Coastguard Worker       bool force = false;
89*2d1272b8SAndroid Build Coastguard Worker       if (c1 == c2)
90*2d1272b8SAndroid Build Coastguard Worker       {
91*2d1272b8SAndroid Build Coastguard Worker         if (abs (d1 - d2) > tolerance && abs (dj) > tolerance)
92*2d1272b8SAndroid Build Coastguard Worker           force = true;
93*2d1272b8SAndroid Build Coastguard Worker       }
94*2d1272b8SAndroid Build Coastguard Worker       else if (c1 <= cj && cj <= c2)
95*2d1272b8SAndroid Build Coastguard Worker       {
96*2d1272b8SAndroid Build Coastguard Worker         if (!(hb_min (d1, d2) - tolerance <= dj &&
97*2d1272b8SAndroid Build Coastguard Worker               dj <= hb_max (d1, d2) + tolerance))
98*2d1272b8SAndroid Build Coastguard Worker           force = true;
99*2d1272b8SAndroid Build Coastguard Worker       }
100*2d1272b8SAndroid Build Coastguard Worker       else
101*2d1272b8SAndroid Build Coastguard Worker       {
102*2d1272b8SAndroid Build Coastguard Worker         if (d1 != d2)
103*2d1272b8SAndroid Build Coastguard Worker         {
104*2d1272b8SAndroid Build Coastguard Worker           if (cj < c1)
105*2d1272b8SAndroid Build Coastguard Worker           {
106*2d1272b8SAndroid Build Coastguard Worker             if (abs (dj) > tolerance &&
107*2d1272b8SAndroid Build Coastguard Worker                 abs (dj - d1) > tolerance &&
108*2d1272b8SAndroid Build Coastguard Worker                 ((dj - tolerance < d1) != (d1 < d2)))
109*2d1272b8SAndroid Build Coastguard Worker                 force = true;
110*2d1272b8SAndroid Build Coastguard Worker           }
111*2d1272b8SAndroid Build Coastguard Worker           else
112*2d1272b8SAndroid Build Coastguard Worker           {
113*2d1272b8SAndroid Build Coastguard Worker             if (abs (dj) > tolerance &&
114*2d1272b8SAndroid Build Coastguard Worker                 abs (dj - d2) > tolerance &&
115*2d1272b8SAndroid Build Coastguard Worker                 ((d2 < dj + tolerance) != (d1 < d2)))
116*2d1272b8SAndroid Build Coastguard Worker               force = true;
117*2d1272b8SAndroid Build Coastguard Worker           }
118*2d1272b8SAndroid Build Coastguard Worker         }
119*2d1272b8SAndroid Build Coastguard Worker       }
120*2d1272b8SAndroid Build Coastguard Worker 
121*2d1272b8SAndroid Build Coastguard Worker       if (force)
122*2d1272b8SAndroid Build Coastguard Worker       {
123*2d1272b8SAndroid Build Coastguard Worker         forced_set.add (i);
124*2d1272b8SAndroid Build Coastguard Worker         break;
125*2d1272b8SAndroid Build Coastguard Worker       }
126*2d1272b8SAndroid Build Coastguard Worker     }
127*2d1272b8SAndroid Build Coastguard Worker     next_i = i;
128*2d1272b8SAndroid Build Coastguard Worker   }
129*2d1272b8SAndroid Build Coastguard Worker }
130*2d1272b8SAndroid Build Coastguard Worker 
131*2d1272b8SAndroid Build Coastguard Worker template <typename T,
132*2d1272b8SAndroid Build Coastguard Worker           hb_enable_if (hb_is_trivially_copyable (T))>
rotate_array(const hb_array_t<const T> & org_array,int k,hb_vector_t<T> & out)133*2d1272b8SAndroid Build Coastguard Worker static bool rotate_array (const hb_array_t<const T>& org_array,
134*2d1272b8SAndroid Build Coastguard Worker                           int k,
135*2d1272b8SAndroid Build Coastguard Worker                           hb_vector_t<T>& out)
136*2d1272b8SAndroid Build Coastguard Worker {
137*2d1272b8SAndroid Build Coastguard Worker   unsigned n = org_array.length;
138*2d1272b8SAndroid Build Coastguard Worker   if (!n) return true;
139*2d1272b8SAndroid Build Coastguard Worker   if (unlikely (!out.resize (n, false)))
140*2d1272b8SAndroid Build Coastguard Worker     return false;
141*2d1272b8SAndroid Build Coastguard Worker 
142*2d1272b8SAndroid Build Coastguard Worker   unsigned item_size = hb_static_size (T);
143*2d1272b8SAndroid Build Coastguard Worker   if (k < 0)
144*2d1272b8SAndroid Build Coastguard Worker     k = n - (-k) % n;
145*2d1272b8SAndroid Build Coastguard Worker   else
146*2d1272b8SAndroid Build Coastguard Worker     k %= n;
147*2d1272b8SAndroid Build Coastguard Worker 
148*2d1272b8SAndroid Build Coastguard Worker   hb_memcpy ((void *) out.arrayZ, (const void *) (org_array.arrayZ + n - k), k * item_size);
149*2d1272b8SAndroid Build Coastguard Worker   hb_memcpy ((void *) (out.arrayZ + k), (const void *) org_array.arrayZ, (n - k) * item_size);
150*2d1272b8SAndroid Build Coastguard Worker   return true;
151*2d1272b8SAndroid Build Coastguard Worker }
152*2d1272b8SAndroid Build Coastguard Worker 
rotate_set(const hb_set_t & org_set,int k,unsigned n,hb_set_t & out)153*2d1272b8SAndroid Build Coastguard Worker static bool rotate_set (const hb_set_t& org_set,
154*2d1272b8SAndroid Build Coastguard Worker                         int k,
155*2d1272b8SAndroid Build Coastguard Worker                         unsigned n,
156*2d1272b8SAndroid Build Coastguard Worker                         hb_set_t& out)
157*2d1272b8SAndroid Build Coastguard Worker {
158*2d1272b8SAndroid Build Coastguard Worker   if (!n) return false;
159*2d1272b8SAndroid Build Coastguard Worker   k %= n;
160*2d1272b8SAndroid Build Coastguard Worker   if (k < 0)
161*2d1272b8SAndroid Build Coastguard Worker     k = n + k;
162*2d1272b8SAndroid Build Coastguard Worker 
163*2d1272b8SAndroid Build Coastguard Worker   if (k == 0)
164*2d1272b8SAndroid Build Coastguard Worker   {
165*2d1272b8SAndroid Build Coastguard Worker     out.set (org_set);
166*2d1272b8SAndroid Build Coastguard Worker   }
167*2d1272b8SAndroid Build Coastguard Worker   else
168*2d1272b8SAndroid Build Coastguard Worker   {
169*2d1272b8SAndroid Build Coastguard Worker     for (auto v : org_set)
170*2d1272b8SAndroid Build Coastguard Worker       out.add ((v + k) % n);
171*2d1272b8SAndroid Build Coastguard Worker   }
172*2d1272b8SAndroid Build Coastguard Worker   return !out.in_error ();
173*2d1272b8SAndroid Build Coastguard Worker }
174*2d1272b8SAndroid Build Coastguard Worker 
175*2d1272b8SAndroid Build Coastguard Worker /* Given two reference coordinates (start and end of contour_points array),
176*2d1272b8SAndroid Build Coastguard Worker  * output interpolated deltas for points in between */
_iup_segment(const hb_array_t<const contour_point_t> contour_points,const hb_array_t<const int> x_deltas,const hb_array_t<const int> y_deltas,const contour_point_t & p1,const contour_point_t & p2,int p1_dx,int p2_dx,int p1_dy,int p2_dy,hb_vector_t<double> & interp_x_deltas,hb_vector_t<double> & interp_y_deltas)177*2d1272b8SAndroid Build Coastguard Worker static bool _iup_segment (const hb_array_t<const contour_point_t> contour_points,
178*2d1272b8SAndroid Build Coastguard Worker                           const hb_array_t<const int> x_deltas,
179*2d1272b8SAndroid Build Coastguard Worker                           const hb_array_t<const int> y_deltas,
180*2d1272b8SAndroid Build Coastguard Worker                           const contour_point_t& p1, const contour_point_t& p2,
181*2d1272b8SAndroid Build Coastguard Worker                           int p1_dx, int p2_dx,
182*2d1272b8SAndroid Build Coastguard Worker                           int p1_dy, int p2_dy,
183*2d1272b8SAndroid Build Coastguard Worker                           hb_vector_t<double>& interp_x_deltas, /* OUT */
184*2d1272b8SAndroid Build Coastguard Worker                           hb_vector_t<double>& interp_y_deltas /* OUT */)
185*2d1272b8SAndroid Build Coastguard Worker {
186*2d1272b8SAndroid Build Coastguard Worker   unsigned n = contour_points.length;
187*2d1272b8SAndroid Build Coastguard Worker   if (unlikely (!interp_x_deltas.resize (n, false) ||
188*2d1272b8SAndroid Build Coastguard Worker                 !interp_y_deltas.resize (n, false)))
189*2d1272b8SAndroid Build Coastguard Worker     return false;
190*2d1272b8SAndroid Build Coastguard Worker 
191*2d1272b8SAndroid Build Coastguard Worker   for (unsigned j = 0; j < 2; j++)
192*2d1272b8SAndroid Build Coastguard Worker   {
193*2d1272b8SAndroid Build Coastguard Worker     double x1, x2, d1, d2;
194*2d1272b8SAndroid Build Coastguard Worker     double *out;
195*2d1272b8SAndroid Build Coastguard Worker     if (j == 0)
196*2d1272b8SAndroid Build Coastguard Worker     {
197*2d1272b8SAndroid Build Coastguard Worker       x1 = static_cast<double> (p1.x);
198*2d1272b8SAndroid Build Coastguard Worker       x2 = static_cast<double> (p2.x);
199*2d1272b8SAndroid Build Coastguard Worker       d1 = p1_dx;
200*2d1272b8SAndroid Build Coastguard Worker       d2 = p2_dx;
201*2d1272b8SAndroid Build Coastguard Worker       out = interp_x_deltas.arrayZ;
202*2d1272b8SAndroid Build Coastguard Worker     }
203*2d1272b8SAndroid Build Coastguard Worker     else
204*2d1272b8SAndroid Build Coastguard Worker     {
205*2d1272b8SAndroid Build Coastguard Worker       x1 = static_cast<double> (p1.y);
206*2d1272b8SAndroid Build Coastguard Worker       x2 = static_cast<double> (p2.y);
207*2d1272b8SAndroid Build Coastguard Worker       d1 = p1_dy;
208*2d1272b8SAndroid Build Coastguard Worker       d2 = p2_dy;
209*2d1272b8SAndroid Build Coastguard Worker       out = interp_y_deltas.arrayZ;
210*2d1272b8SAndroid Build Coastguard Worker     }
211*2d1272b8SAndroid Build Coastguard Worker 
212*2d1272b8SAndroid Build Coastguard Worker     if (x1 == x2)
213*2d1272b8SAndroid Build Coastguard Worker     {
214*2d1272b8SAndroid Build Coastguard Worker       if (d1 == d2)
215*2d1272b8SAndroid Build Coastguard Worker       {
216*2d1272b8SAndroid Build Coastguard Worker         for (unsigned i = 0; i < n; i++)
217*2d1272b8SAndroid Build Coastguard Worker           out[i] = d1;
218*2d1272b8SAndroid Build Coastguard Worker       }
219*2d1272b8SAndroid Build Coastguard Worker       else
220*2d1272b8SAndroid Build Coastguard Worker       {
221*2d1272b8SAndroid Build Coastguard Worker         for (unsigned i = 0; i < n; i++)
222*2d1272b8SAndroid Build Coastguard Worker           out[i] = 0.0;
223*2d1272b8SAndroid Build Coastguard Worker       }
224*2d1272b8SAndroid Build Coastguard Worker       continue;
225*2d1272b8SAndroid Build Coastguard Worker     }
226*2d1272b8SAndroid Build Coastguard Worker 
227*2d1272b8SAndroid Build Coastguard Worker     if (x1 > x2)
228*2d1272b8SAndroid Build Coastguard Worker     {
229*2d1272b8SAndroid Build Coastguard Worker       hb_swap (x1, x2);
230*2d1272b8SAndroid Build Coastguard Worker       hb_swap (d1, d2);
231*2d1272b8SAndroid Build Coastguard Worker     }
232*2d1272b8SAndroid Build Coastguard Worker 
233*2d1272b8SAndroid Build Coastguard Worker     double scale = (d2 - d1) / (x2 - x1);
234*2d1272b8SAndroid Build Coastguard Worker     for (unsigned i = 0; i < n; i++)
235*2d1272b8SAndroid Build Coastguard Worker     {
236*2d1272b8SAndroid Build Coastguard Worker       double x = (j == 0 ? static_cast<double> (contour_points.arrayZ[i].x) : static_cast<double> (contour_points.arrayZ[i].y));
237*2d1272b8SAndroid Build Coastguard Worker       double d;
238*2d1272b8SAndroid Build Coastguard Worker       if (x <= x1)
239*2d1272b8SAndroid Build Coastguard Worker         d = d1;
240*2d1272b8SAndroid Build Coastguard Worker       else if (x >= x2)
241*2d1272b8SAndroid Build Coastguard Worker         d = d2;
242*2d1272b8SAndroid Build Coastguard Worker       else
243*2d1272b8SAndroid Build Coastguard Worker         d = d1 + (x - x1) * scale;
244*2d1272b8SAndroid Build Coastguard Worker 
245*2d1272b8SAndroid Build Coastguard Worker       out[i] = d;
246*2d1272b8SAndroid Build Coastguard Worker     }
247*2d1272b8SAndroid Build Coastguard Worker   }
248*2d1272b8SAndroid Build Coastguard Worker   return true;
249*2d1272b8SAndroid Build Coastguard Worker }
250*2d1272b8SAndroid Build Coastguard Worker 
_can_iup_in_between(const hb_array_t<const contour_point_t> contour_points,const hb_array_t<const int> x_deltas,const hb_array_t<const int> y_deltas,const contour_point_t & p1,const contour_point_t & p2,int p1_dx,int p2_dx,int p1_dy,int p2_dy,double tolerance)251*2d1272b8SAndroid Build Coastguard Worker static bool _can_iup_in_between (const hb_array_t<const contour_point_t> contour_points,
252*2d1272b8SAndroid Build Coastguard Worker                                  const hb_array_t<const int> x_deltas,
253*2d1272b8SAndroid Build Coastguard Worker                                  const hb_array_t<const int> y_deltas,
254*2d1272b8SAndroid Build Coastguard Worker                                  const contour_point_t& p1, const contour_point_t& p2,
255*2d1272b8SAndroid Build Coastguard Worker                                  int p1_dx, int p2_dx,
256*2d1272b8SAndroid Build Coastguard Worker                                  int p1_dy, int p2_dy,
257*2d1272b8SAndroid Build Coastguard Worker                                  double tolerance)
258*2d1272b8SAndroid Build Coastguard Worker {
259*2d1272b8SAndroid Build Coastguard Worker   hb_vector_t<double> interp_x_deltas, interp_y_deltas;
260*2d1272b8SAndroid Build Coastguard Worker   if (!_iup_segment (contour_points, x_deltas, y_deltas,
261*2d1272b8SAndroid Build Coastguard Worker                      p1, p2, p1_dx, p2_dx, p1_dy, p2_dy,
262*2d1272b8SAndroid Build Coastguard Worker                      interp_x_deltas, interp_y_deltas))
263*2d1272b8SAndroid Build Coastguard Worker     return false;
264*2d1272b8SAndroid Build Coastguard Worker 
265*2d1272b8SAndroid Build Coastguard Worker   unsigned num = contour_points.length;
266*2d1272b8SAndroid Build Coastguard Worker 
267*2d1272b8SAndroid Build Coastguard Worker   for (unsigned i = 0; i < num; i++)
268*2d1272b8SAndroid Build Coastguard Worker   {
269*2d1272b8SAndroid Build Coastguard Worker     double dx = static_cast<double> (x_deltas.arrayZ[i]) - interp_x_deltas.arrayZ[i];
270*2d1272b8SAndroid Build Coastguard Worker     double dy = static_cast<double> (y_deltas.arrayZ[i]) - interp_y_deltas.arrayZ[i];
271*2d1272b8SAndroid Build Coastguard Worker 
272*2d1272b8SAndroid Build Coastguard Worker     if (sqrt (dx * dx + dy * dy) > tolerance)
273*2d1272b8SAndroid Build Coastguard Worker       return false;
274*2d1272b8SAndroid Build Coastguard Worker   }
275*2d1272b8SAndroid Build Coastguard Worker   return true;
276*2d1272b8SAndroid Build Coastguard Worker }
277*2d1272b8SAndroid Build Coastguard Worker 
_iup_contour_optimize_dp(const contour_point_vector_t & contour_points,const hb_vector_t<int> & x_deltas,const hb_vector_t<int> & y_deltas,const hb_set_t & forced_set,double tolerance,unsigned lookback,hb_vector_t<unsigned> & costs,hb_vector_t<int> & chain)278*2d1272b8SAndroid Build Coastguard Worker static bool _iup_contour_optimize_dp (const contour_point_vector_t& contour_points,
279*2d1272b8SAndroid Build Coastguard Worker                                       const hb_vector_t<int>& x_deltas,
280*2d1272b8SAndroid Build Coastguard Worker                                       const hb_vector_t<int>& y_deltas,
281*2d1272b8SAndroid Build Coastguard Worker                                       const hb_set_t& forced_set,
282*2d1272b8SAndroid Build Coastguard Worker                                       double tolerance,
283*2d1272b8SAndroid Build Coastguard Worker                                       unsigned lookback,
284*2d1272b8SAndroid Build Coastguard Worker                                       hb_vector_t<unsigned>& costs, /* OUT */
285*2d1272b8SAndroid Build Coastguard Worker                                       hb_vector_t<int>& chain /* OUT */)
286*2d1272b8SAndroid Build Coastguard Worker {
287*2d1272b8SAndroid Build Coastguard Worker   unsigned n = contour_points.length;
288*2d1272b8SAndroid Build Coastguard Worker   if (unlikely (!costs.resize (n, false) ||
289*2d1272b8SAndroid Build Coastguard Worker                 !chain.resize (n, false)))
290*2d1272b8SAndroid Build Coastguard Worker     return false;
291*2d1272b8SAndroid Build Coastguard Worker 
292*2d1272b8SAndroid Build Coastguard Worker   lookback = hb_min (lookback, MAX_LOOKBACK);
293*2d1272b8SAndroid Build Coastguard Worker 
294*2d1272b8SAndroid Build Coastguard Worker   for (unsigned i = 0; i < n; i++)
295*2d1272b8SAndroid Build Coastguard Worker   {
296*2d1272b8SAndroid Build Coastguard Worker     unsigned best_cost = (i == 0 ? 1 : costs.arrayZ[i-1] + 1);
297*2d1272b8SAndroid Build Coastguard Worker 
298*2d1272b8SAndroid Build Coastguard Worker     costs.arrayZ[i] = best_cost;
299*2d1272b8SAndroid Build Coastguard Worker     chain.arrayZ[i] = (i == 0 ? -1 : i - 1);
300*2d1272b8SAndroid Build Coastguard Worker 
301*2d1272b8SAndroid Build Coastguard Worker     if (i > 0 && forced_set.has (i - 1))
302*2d1272b8SAndroid Build Coastguard Worker       continue;
303*2d1272b8SAndroid Build Coastguard Worker 
304*2d1272b8SAndroid Build Coastguard Worker     int lookback_index = hb_max ((int) i - (int) lookback + 1, -1);
305*2d1272b8SAndroid Build Coastguard Worker     for (int j = i - 2; j >= lookback_index; j--)
306*2d1272b8SAndroid Build Coastguard Worker     {
307*2d1272b8SAndroid Build Coastguard Worker       unsigned cost = j == -1 ? 1 : costs.arrayZ[j] + 1;
308*2d1272b8SAndroid Build Coastguard Worker       /* num points between i and j */
309*2d1272b8SAndroid Build Coastguard Worker       unsigned num_points = i - j - 1;
310*2d1272b8SAndroid Build Coastguard Worker       unsigned p1 = (j == -1 ? n - 1 : j);
311*2d1272b8SAndroid Build Coastguard Worker       if (cost < best_cost &&
312*2d1272b8SAndroid Build Coastguard Worker           _can_iup_in_between (contour_points.as_array ().sub_array (j + 1, num_points),
313*2d1272b8SAndroid Build Coastguard Worker                                x_deltas.as_array ().sub_array (j + 1, num_points),
314*2d1272b8SAndroid Build Coastguard Worker                                y_deltas.as_array ().sub_array (j + 1, num_points),
315*2d1272b8SAndroid Build Coastguard Worker                                contour_points.arrayZ[p1], contour_points.arrayZ[i],
316*2d1272b8SAndroid Build Coastguard Worker                                x_deltas.arrayZ[p1], x_deltas.arrayZ[i],
317*2d1272b8SAndroid Build Coastguard Worker                                y_deltas.arrayZ[p1], y_deltas.arrayZ[i],
318*2d1272b8SAndroid Build Coastguard Worker                                tolerance))
319*2d1272b8SAndroid Build Coastguard Worker       {
320*2d1272b8SAndroid Build Coastguard Worker         best_cost = cost;
321*2d1272b8SAndroid Build Coastguard Worker         costs.arrayZ[i] = best_cost;
322*2d1272b8SAndroid Build Coastguard Worker         chain.arrayZ[i] = j;
323*2d1272b8SAndroid Build Coastguard Worker       }
324*2d1272b8SAndroid Build Coastguard Worker 
325*2d1272b8SAndroid Build Coastguard Worker       if (j > 0 && forced_set.has (j))
326*2d1272b8SAndroid Build Coastguard Worker         break;
327*2d1272b8SAndroid Build Coastguard Worker     }
328*2d1272b8SAndroid Build Coastguard Worker   }
329*2d1272b8SAndroid Build Coastguard Worker   return true;
330*2d1272b8SAndroid Build Coastguard Worker }
331*2d1272b8SAndroid Build Coastguard Worker 
_iup_contour_optimize(const hb_array_t<const contour_point_t> contour_points,const hb_array_t<const int> x_deltas,const hb_array_t<const int> y_deltas,hb_array_t<bool> opt_indices,double tolerance=0.0)332*2d1272b8SAndroid Build Coastguard Worker static bool _iup_contour_optimize (const hb_array_t<const contour_point_t> contour_points,
333*2d1272b8SAndroid Build Coastguard Worker                                    const hb_array_t<const int> x_deltas,
334*2d1272b8SAndroid Build Coastguard Worker                                    const hb_array_t<const int> y_deltas,
335*2d1272b8SAndroid Build Coastguard Worker                                    hb_array_t<bool> opt_indices, /* OUT */
336*2d1272b8SAndroid Build Coastguard Worker                                    double tolerance = 0.0)
337*2d1272b8SAndroid Build Coastguard Worker {
338*2d1272b8SAndroid Build Coastguard Worker   unsigned n = contour_points.length;
339*2d1272b8SAndroid Build Coastguard Worker   if (opt_indices.length != n ||
340*2d1272b8SAndroid Build Coastguard Worker       x_deltas.length != n ||
341*2d1272b8SAndroid Build Coastguard Worker       y_deltas.length != n)
342*2d1272b8SAndroid Build Coastguard Worker     return false;
343*2d1272b8SAndroid Build Coastguard Worker 
344*2d1272b8SAndroid Build Coastguard Worker   bool all_within_tolerance = true;
345*2d1272b8SAndroid Build Coastguard Worker   for (unsigned i = 0; i < n; i++)
346*2d1272b8SAndroid Build Coastguard Worker   {
347*2d1272b8SAndroid Build Coastguard Worker     int dx = x_deltas.arrayZ[i];
348*2d1272b8SAndroid Build Coastguard Worker     int dy = y_deltas.arrayZ[i];
349*2d1272b8SAndroid Build Coastguard Worker     if (sqrt ((double) dx * dx + (double) dy * dy) > tolerance)
350*2d1272b8SAndroid Build Coastguard Worker     {
351*2d1272b8SAndroid Build Coastguard Worker       all_within_tolerance = false;
352*2d1272b8SAndroid Build Coastguard Worker       break;
353*2d1272b8SAndroid Build Coastguard Worker     }
354*2d1272b8SAndroid Build Coastguard Worker   }
355*2d1272b8SAndroid Build Coastguard Worker 
356*2d1272b8SAndroid Build Coastguard Worker   /* If all are within tolerance distance, do nothing, opt_indices is
357*2d1272b8SAndroid Build Coastguard Worker    * initilized to false */
358*2d1272b8SAndroid Build Coastguard Worker   if (all_within_tolerance)
359*2d1272b8SAndroid Build Coastguard Worker     return true;
360*2d1272b8SAndroid Build Coastguard Worker 
361*2d1272b8SAndroid Build Coastguard Worker   /* If there's exactly one point, return it */
362*2d1272b8SAndroid Build Coastguard Worker   if (n == 1)
363*2d1272b8SAndroid Build Coastguard Worker   {
364*2d1272b8SAndroid Build Coastguard Worker     opt_indices.arrayZ[0] = true;
365*2d1272b8SAndroid Build Coastguard Worker     return true;
366*2d1272b8SAndroid Build Coastguard Worker   }
367*2d1272b8SAndroid Build Coastguard Worker 
368*2d1272b8SAndroid Build Coastguard Worker   /* If all deltas are exactly the same, return just one (the first one) */
369*2d1272b8SAndroid Build Coastguard Worker   bool all_deltas_are_equal = true;
370*2d1272b8SAndroid Build Coastguard Worker   for (unsigned i = 1; i < n; i++)
371*2d1272b8SAndroid Build Coastguard Worker     if (x_deltas.arrayZ[i] != x_deltas.arrayZ[0] ||
372*2d1272b8SAndroid Build Coastguard Worker         y_deltas.arrayZ[i] != y_deltas.arrayZ[0])
373*2d1272b8SAndroid Build Coastguard Worker     {
374*2d1272b8SAndroid Build Coastguard Worker       all_deltas_are_equal = false;
375*2d1272b8SAndroid Build Coastguard Worker       break;
376*2d1272b8SAndroid Build Coastguard Worker     }
377*2d1272b8SAndroid Build Coastguard Worker 
378*2d1272b8SAndroid Build Coastguard Worker   if (all_deltas_are_equal)
379*2d1272b8SAndroid Build Coastguard Worker   {
380*2d1272b8SAndroid Build Coastguard Worker     opt_indices.arrayZ[0] = true;
381*2d1272b8SAndroid Build Coastguard Worker     return true;
382*2d1272b8SAndroid Build Coastguard Worker   }
383*2d1272b8SAndroid Build Coastguard Worker 
384*2d1272b8SAndroid Build Coastguard Worker   /* else, solve the general problem using Dynamic Programming */
385*2d1272b8SAndroid Build Coastguard Worker   hb_set_t forced_set;
386*2d1272b8SAndroid Build Coastguard Worker   _iup_contour_bound_forced_set (contour_points, x_deltas, y_deltas, forced_set, tolerance);
387*2d1272b8SAndroid Build Coastguard Worker 
388*2d1272b8SAndroid Build Coastguard Worker   if (!forced_set.is_empty ())
389*2d1272b8SAndroid Build Coastguard Worker   {
390*2d1272b8SAndroid Build Coastguard Worker     int k = n - 1 - forced_set.get_max ();
391*2d1272b8SAndroid Build Coastguard Worker     if (k < 0)
392*2d1272b8SAndroid Build Coastguard Worker       return false;
393*2d1272b8SAndroid Build Coastguard Worker 
394*2d1272b8SAndroid Build Coastguard Worker     hb_vector_t<int> rot_x_deltas, rot_y_deltas;
395*2d1272b8SAndroid Build Coastguard Worker     contour_point_vector_t rot_points;
396*2d1272b8SAndroid Build Coastguard Worker     hb_set_t rot_forced_set;
397*2d1272b8SAndroid Build Coastguard Worker     if (!rotate_array (contour_points, k, rot_points) ||
398*2d1272b8SAndroid Build Coastguard Worker         !rotate_array (x_deltas, k, rot_x_deltas) ||
399*2d1272b8SAndroid Build Coastguard Worker         !rotate_array (y_deltas, k, rot_y_deltas) ||
400*2d1272b8SAndroid Build Coastguard Worker         !rotate_set (forced_set, k, n, rot_forced_set))
401*2d1272b8SAndroid Build Coastguard Worker       return false;
402*2d1272b8SAndroid Build Coastguard Worker 
403*2d1272b8SAndroid Build Coastguard Worker     hb_vector_t<unsigned> costs;
404*2d1272b8SAndroid Build Coastguard Worker     hb_vector_t<int> chain;
405*2d1272b8SAndroid Build Coastguard Worker 
406*2d1272b8SAndroid Build Coastguard Worker     if (!_iup_contour_optimize_dp (rot_points, rot_x_deltas, rot_y_deltas,
407*2d1272b8SAndroid Build Coastguard Worker                                    rot_forced_set, tolerance, n,
408*2d1272b8SAndroid Build Coastguard Worker                                    costs, chain))
409*2d1272b8SAndroid Build Coastguard Worker       return false;
410*2d1272b8SAndroid Build Coastguard Worker 
411*2d1272b8SAndroid Build Coastguard Worker     hb_set_t solution;
412*2d1272b8SAndroid Build Coastguard Worker     int index = n - 1;
413*2d1272b8SAndroid Build Coastguard Worker     while (index != -1)
414*2d1272b8SAndroid Build Coastguard Worker     {
415*2d1272b8SAndroid Build Coastguard Worker       solution.add (index);
416*2d1272b8SAndroid Build Coastguard Worker       index = chain.arrayZ[index];
417*2d1272b8SAndroid Build Coastguard Worker     }
418*2d1272b8SAndroid Build Coastguard Worker 
419*2d1272b8SAndroid Build Coastguard Worker     if (solution.is_empty () ||
420*2d1272b8SAndroid Build Coastguard Worker         forced_set.get_population () > solution.get_population ())
421*2d1272b8SAndroid Build Coastguard Worker       return false;
422*2d1272b8SAndroid Build Coastguard Worker 
423*2d1272b8SAndroid Build Coastguard Worker     for (unsigned i : solution)
424*2d1272b8SAndroid Build Coastguard Worker       opt_indices.arrayZ[i] = true;
425*2d1272b8SAndroid Build Coastguard Worker 
426*2d1272b8SAndroid Build Coastguard Worker     hb_vector_t<bool> rot_indices;
427*2d1272b8SAndroid Build Coastguard Worker     const hb_array_t<const bool> opt_indices_array (opt_indices.arrayZ, opt_indices.length);
428*2d1272b8SAndroid Build Coastguard Worker     rotate_array (opt_indices_array, -k, rot_indices);
429*2d1272b8SAndroid Build Coastguard Worker 
430*2d1272b8SAndroid Build Coastguard Worker     for (unsigned i = 0; i < n; i++)
431*2d1272b8SAndroid Build Coastguard Worker       opt_indices.arrayZ[i] = rot_indices.arrayZ[i];
432*2d1272b8SAndroid Build Coastguard Worker   }
433*2d1272b8SAndroid Build Coastguard Worker   else
434*2d1272b8SAndroid Build Coastguard Worker   {
435*2d1272b8SAndroid Build Coastguard Worker     hb_vector_t<int> repeat_x_deltas, repeat_y_deltas;
436*2d1272b8SAndroid Build Coastguard Worker     contour_point_vector_t repeat_points;
437*2d1272b8SAndroid Build Coastguard Worker 
438*2d1272b8SAndroid Build Coastguard Worker     if (unlikely (!repeat_x_deltas.resize (n * 2, false) ||
439*2d1272b8SAndroid Build Coastguard Worker                   !repeat_y_deltas.resize (n * 2, false) ||
440*2d1272b8SAndroid Build Coastguard Worker                   !repeat_points.resize (n * 2, false)))
441*2d1272b8SAndroid Build Coastguard Worker       return false;
442*2d1272b8SAndroid Build Coastguard Worker 
443*2d1272b8SAndroid Build Coastguard Worker     unsigned contour_point_size = hb_static_size (contour_point_t);
444*2d1272b8SAndroid Build Coastguard Worker     for (unsigned i = 0; i < n; i++)
445*2d1272b8SAndroid Build Coastguard Worker     {
446*2d1272b8SAndroid Build Coastguard Worker       hb_memcpy ((void *) repeat_x_deltas.arrayZ, (const void *) x_deltas.arrayZ, n * sizeof (repeat_x_deltas[0]));
447*2d1272b8SAndroid Build Coastguard Worker       hb_memcpy ((void *) (repeat_x_deltas.arrayZ + n), (const void *) x_deltas.arrayZ, n * sizeof (repeat_x_deltas[0]));
448*2d1272b8SAndroid Build Coastguard Worker 
449*2d1272b8SAndroid Build Coastguard Worker       hb_memcpy ((void *) repeat_y_deltas.arrayZ, (const void *) y_deltas.arrayZ, n * sizeof (repeat_x_deltas[0]));
450*2d1272b8SAndroid Build Coastguard Worker       hb_memcpy ((void *) (repeat_y_deltas.arrayZ + n), (const void *) y_deltas.arrayZ, n * sizeof (repeat_x_deltas[0]));
451*2d1272b8SAndroid Build Coastguard Worker 
452*2d1272b8SAndroid Build Coastguard Worker       hb_memcpy ((void *) repeat_points.arrayZ, (const void *) contour_points.arrayZ, n * contour_point_size);
453*2d1272b8SAndroid Build Coastguard Worker       hb_memcpy ((void *) (repeat_points.arrayZ + n), (const void *) contour_points.arrayZ, n * contour_point_size);
454*2d1272b8SAndroid Build Coastguard Worker     }
455*2d1272b8SAndroid Build Coastguard Worker 
456*2d1272b8SAndroid Build Coastguard Worker     hb_vector_t<unsigned> costs;
457*2d1272b8SAndroid Build Coastguard Worker     hb_vector_t<int> chain;
458*2d1272b8SAndroid Build Coastguard Worker     if (!_iup_contour_optimize_dp (repeat_points, repeat_x_deltas, repeat_y_deltas,
459*2d1272b8SAndroid Build Coastguard Worker                                    forced_set, tolerance, n,
460*2d1272b8SAndroid Build Coastguard Worker                                    costs, chain))
461*2d1272b8SAndroid Build Coastguard Worker       return false;
462*2d1272b8SAndroid Build Coastguard Worker 
463*2d1272b8SAndroid Build Coastguard Worker     unsigned best_cost = n + 1;
464*2d1272b8SAndroid Build Coastguard Worker     int len = costs.length;
465*2d1272b8SAndroid Build Coastguard Worker     hb_set_t best_sol;
466*2d1272b8SAndroid Build Coastguard Worker     for (int start = n - 1; start < len; start++)
467*2d1272b8SAndroid Build Coastguard Worker     {
468*2d1272b8SAndroid Build Coastguard Worker       hb_set_t solution;
469*2d1272b8SAndroid Build Coastguard Worker       int i = start;
470*2d1272b8SAndroid Build Coastguard Worker       int lookback = start - (int) n;
471*2d1272b8SAndroid Build Coastguard Worker       while (i > lookback)
472*2d1272b8SAndroid Build Coastguard Worker       {
473*2d1272b8SAndroid Build Coastguard Worker         solution.add (i % n);
474*2d1272b8SAndroid Build Coastguard Worker         i = chain.arrayZ[i];
475*2d1272b8SAndroid Build Coastguard Worker       }
476*2d1272b8SAndroid Build Coastguard Worker       if (i == lookback)
477*2d1272b8SAndroid Build Coastguard Worker       {
478*2d1272b8SAndroid Build Coastguard Worker         unsigned cost_i = i < 0 ? 0 : costs.arrayZ[i];
479*2d1272b8SAndroid Build Coastguard Worker         unsigned cost = costs.arrayZ[start] - cost_i;
480*2d1272b8SAndroid Build Coastguard Worker         if (cost <= best_cost)
481*2d1272b8SAndroid Build Coastguard Worker         {
482*2d1272b8SAndroid Build Coastguard Worker           best_sol.set (solution);
483*2d1272b8SAndroid Build Coastguard Worker           best_cost = cost;
484*2d1272b8SAndroid Build Coastguard Worker         }
485*2d1272b8SAndroid Build Coastguard Worker       }
486*2d1272b8SAndroid Build Coastguard Worker     }
487*2d1272b8SAndroid Build Coastguard Worker 
488*2d1272b8SAndroid Build Coastguard Worker     for (unsigned i = 0; i < n; i++)
489*2d1272b8SAndroid Build Coastguard Worker       if (best_sol.has (i))
490*2d1272b8SAndroid Build Coastguard Worker         opt_indices.arrayZ[i] = true;
491*2d1272b8SAndroid Build Coastguard Worker   }
492*2d1272b8SAndroid Build Coastguard Worker   return true;
493*2d1272b8SAndroid Build Coastguard Worker }
494*2d1272b8SAndroid Build Coastguard Worker 
iup_delta_optimize(const contour_point_vector_t & contour_points,const hb_vector_t<int> & x_deltas,const hb_vector_t<int> & y_deltas,hb_vector_t<bool> & opt_indices,double tolerance)495*2d1272b8SAndroid Build Coastguard Worker bool iup_delta_optimize (const contour_point_vector_t& contour_points,
496*2d1272b8SAndroid Build Coastguard Worker                          const hb_vector_t<int>& x_deltas,
497*2d1272b8SAndroid Build Coastguard Worker                          const hb_vector_t<int>& y_deltas,
498*2d1272b8SAndroid Build Coastguard Worker                          hb_vector_t<bool>& opt_indices, /* OUT */
499*2d1272b8SAndroid Build Coastguard Worker                          double tolerance)
500*2d1272b8SAndroid Build Coastguard Worker {
501*2d1272b8SAndroid Build Coastguard Worker   if (!opt_indices.resize (contour_points.length))
502*2d1272b8SAndroid Build Coastguard Worker       return false;
503*2d1272b8SAndroid Build Coastguard Worker 
504*2d1272b8SAndroid Build Coastguard Worker   hb_vector_t<unsigned> end_points;
505*2d1272b8SAndroid Build Coastguard Worker   unsigned count = contour_points.length;
506*2d1272b8SAndroid Build Coastguard Worker   if (unlikely (!end_points.alloc (count)))
507*2d1272b8SAndroid Build Coastguard Worker     return false;
508*2d1272b8SAndroid Build Coastguard Worker 
509*2d1272b8SAndroid Build Coastguard Worker   for (unsigned i = 0; i < count - 4; i++)
510*2d1272b8SAndroid Build Coastguard Worker     if (contour_points.arrayZ[i].is_end_point)
511*2d1272b8SAndroid Build Coastguard Worker       end_points.push (i);
512*2d1272b8SAndroid Build Coastguard Worker 
513*2d1272b8SAndroid Build Coastguard Worker   /* phantom points */
514*2d1272b8SAndroid Build Coastguard Worker   for (unsigned i = count - 4; i < count; i++)
515*2d1272b8SAndroid Build Coastguard Worker     end_points.push (i);
516*2d1272b8SAndroid Build Coastguard Worker 
517*2d1272b8SAndroid Build Coastguard Worker   if (end_points.in_error ()) return false;
518*2d1272b8SAndroid Build Coastguard Worker 
519*2d1272b8SAndroid Build Coastguard Worker   unsigned start = 0;
520*2d1272b8SAndroid Build Coastguard Worker   for (unsigned end : end_points)
521*2d1272b8SAndroid Build Coastguard Worker   {
522*2d1272b8SAndroid Build Coastguard Worker     unsigned len = end - start + 1;
523*2d1272b8SAndroid Build Coastguard Worker     if (!_iup_contour_optimize (contour_points.as_array ().sub_array (start, len),
524*2d1272b8SAndroid Build Coastguard Worker                                 x_deltas.as_array ().sub_array (start, len),
525*2d1272b8SAndroid Build Coastguard Worker                                 y_deltas.as_array ().sub_array (start, len),
526*2d1272b8SAndroid Build Coastguard Worker                                 opt_indices.as_array ().sub_array (start, len),
527*2d1272b8SAndroid Build Coastguard Worker                                 tolerance))
528*2d1272b8SAndroid Build Coastguard Worker       return false;
529*2d1272b8SAndroid Build Coastguard Worker     start = end + 1;
530*2d1272b8SAndroid Build Coastguard Worker   }
531*2d1272b8SAndroid Build Coastguard Worker   return true;
532*2d1272b8SAndroid Build Coastguard Worker }
533