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