|
|
|
@ -723,22 +723,28 @@ struct tuple_delta_t |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool compile_deltas () |
|
|
|
|
{ return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas); } |
|
|
|
|
|
|
|
|
|
bool compile_deltas (const hb_vector_t<bool> &point_indices, |
|
|
|
|
const hb_vector_t<float> &x_deltas, |
|
|
|
|
const hb_vector_t<float> &y_deltas, |
|
|
|
|
hb_vector_t<char> &compiled_deltas /* OUT */) |
|
|
|
|
{ |
|
|
|
|
hb_vector_t<int> rounded_deltas; |
|
|
|
|
if (unlikely (!rounded_deltas.alloc (indices.length))) |
|
|
|
|
if (unlikely (!rounded_deltas.alloc (point_indices.length))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < indices.length; i++) |
|
|
|
|
for (unsigned i = 0; i < point_indices.length; i++) |
|
|
|
|
{ |
|
|
|
|
if (!indices[i]) continue; |
|
|
|
|
int rounded_delta = (int) roundf (deltas_x[i]); |
|
|
|
|
if (!point_indices[i]) continue; |
|
|
|
|
int rounded_delta = (int) roundf (x_deltas.arrayZ[i]); |
|
|
|
|
rounded_deltas.push (rounded_delta); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!rounded_deltas) return false; |
|
|
|
|
if (!rounded_deltas) return true; |
|
|
|
|
/* allocate enough memories 3 * num_deltas */ |
|
|
|
|
unsigned alloc_len = 3 * rounded_deltas.length; |
|
|
|
|
if (deltas_y) |
|
|
|
|
if (y_deltas) |
|
|
|
|
alloc_len *= 2; |
|
|
|
|
|
|
|
|
|
if (unlikely (!compiled_deltas.resize (alloc_len))) return false; |
|
|
|
@ -746,14 +752,14 @@ struct tuple_delta_t |
|
|
|
|
unsigned i = 0; |
|
|
|
|
unsigned encoded_len = encode_delta_run (i, compiled_deltas.as_array (), rounded_deltas); |
|
|
|
|
|
|
|
|
|
if (deltas_y) |
|
|
|
|
if (y_deltas) |
|
|
|
|
{ |
|
|
|
|
/* reuse the rounded_deltas vector, check that deltas_y have the same num of deltas as deltas_x */ |
|
|
|
|
/* reuse the rounded_deltas vector, check that y_deltas have the same num of deltas as x_deltas */ |
|
|
|
|
unsigned j = 0; |
|
|
|
|
for (unsigned idx = 0; idx < indices.length; idx++) |
|
|
|
|
for (unsigned idx = 0; idx < point_indices.length; idx++) |
|
|
|
|
{ |
|
|
|
|
if (!indices[idx]) continue; |
|
|
|
|
int rounded_delta = (int) roundf (deltas_y[idx]); |
|
|
|
|
if (!point_indices[idx]) continue; |
|
|
|
|
int rounded_delta = (int) roundf (y_deltas.arrayZ[idx]); |
|
|
|
|
|
|
|
|
|
if (j >= rounded_deltas.length) return false; |
|
|
|
|
|
|
|
|
@ -761,7 +767,7 @@ struct tuple_delta_t |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (j != rounded_deltas.length) return false; |
|
|
|
|
/* reset i because we reuse rounded_deltas for deltas_y */ |
|
|
|
|
/* reset i because we reuse rounded_deltas for y_deltas */ |
|
|
|
|
i = 0; |
|
|
|
|
encoded_len += encode_delta_run (i, compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas); |
|
|
|
|
} |
|
|
|
@ -1020,6 +1026,585 @@ struct tuple_delta_t |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool optimize (const contour_point_vector_t& contour_points, |
|
|
|
|
bool is_composite, |
|
|
|
|
float tolerance = 0.5f) |
|
|
|
|
{ |
|
|
|
|
unsigned count = contour_points.length; |
|
|
|
|
if (deltas_x.length != count || |
|
|
|
|
deltas_y.length != count) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
hb_vector_t<bool> opt_indices; |
|
|
|
|
hb_vector_t<float> opt_deltas_x, opt_deltas_y; |
|
|
|
|
|
|
|
|
|
if (!opt_indices.resize (count)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
if (!iup_delta_optimize (contour_points, opt_indices, tolerance)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
unsigned ref_count = 0; |
|
|
|
|
for (bool ref_flag : opt_indices) |
|
|
|
|
ref_count += ref_flag; |
|
|
|
|
|
|
|
|
|
if (ref_count == count) return true; |
|
|
|
|
|
|
|
|
|
bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0); |
|
|
|
|
if (is_comp_glyph_wo_deltas) |
|
|
|
|
{ |
|
|
|
|
if (unlikely (!opt_deltas_x.resize (count) || |
|
|
|
|
!opt_deltas_y.resize (count))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
opt_indices.arrayZ[0] = true; |
|
|
|
|
for (unsigned i = 1; i < count; i++) |
|
|
|
|
opt_indices.arrayZ[i] = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
hb_vector_t<char> opt_point_data; |
|
|
|
|
if (!compile_point_set (opt_indices, opt_point_data)) |
|
|
|
|
return false; |
|
|
|
|
hb_vector_t<char> opt_deltas_data; |
|
|
|
|
if (!compile_deltas (opt_indices, |
|
|
|
|
is_comp_glyph_wo_deltas ? opt_deltas_x : deltas_x, |
|
|
|
|
is_comp_glyph_wo_deltas ? opt_deltas_y : deltas_y, |
|
|
|
|
opt_deltas_data)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
hb_vector_t<char> point_data; |
|
|
|
|
if (!compile_point_set (indices, point_data)) |
|
|
|
|
return false; |
|
|
|
|
hb_vector_t<char> deltas_data; |
|
|
|
|
if (!compile_deltas (indices, deltas_x, deltas_y, deltas_data)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
if (opt_point_data.length + opt_deltas_data.length < point_data.length + deltas_data.length) |
|
|
|
|
{ |
|
|
|
|
indices.fini (); |
|
|
|
|
indices = std::move (opt_indices); |
|
|
|
|
|
|
|
|
|
if (is_comp_glyph_wo_deltas) |
|
|
|
|
{ |
|
|
|
|
deltas_x.fini (); |
|
|
|
|
deltas_x = std::move (opt_deltas_x); |
|
|
|
|
|
|
|
|
|
deltas_y.fini (); |
|
|
|
|
deltas_y = std::move (opt_deltas_y); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return !indices.in_error () && !deltas_x.in_error () && !deltas_y.in_error (); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool iup_delta_optimize (const contour_point_vector_t& contour_points, |
|
|
|
|
hb_vector_t<bool>& opt_indices, /* OUT */ |
|
|
|
|
float tolerance = 0.f) |
|
|
|
|
{ |
|
|
|
|
hb_vector_t<unsigned> end_points; |
|
|
|
|
unsigned count = contour_points.length; |
|
|
|
|
if (unlikely (!end_points.alloc (count))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < count - 4; i++) |
|
|
|
|
if (contour_points.arrayZ[i].is_end_point) |
|
|
|
|
end_points.push (i); |
|
|
|
|
|
|
|
|
|
/* phantom points */ |
|
|
|
|
for (unsigned i = count - 4; i < count; i++) |
|
|
|
|
end_points.push (i); |
|
|
|
|
|
|
|
|
|
if (end_points.in_error ()) return false; |
|
|
|
|
|
|
|
|
|
hb_vector_t<int> rounded_x_deltas, rounded_y_deltas; |
|
|
|
|
|
|
|
|
|
if (unlikely (!rounded_x_deltas.alloc (count) || |
|
|
|
|
!rounded_y_deltas.alloc (count))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < count; i++) |
|
|
|
|
{ |
|
|
|
|
int rounded_x_delta = (int) roundf (deltas_x.arrayZ[i]); |
|
|
|
|
int rounded_y_delta = (int) roundf (deltas_y.arrayZ[i]); |
|
|
|
|
rounded_x_deltas.push (rounded_x_delta); |
|
|
|
|
rounded_y_deltas.push (rounded_y_delta); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsigned start = 0; |
|
|
|
|
for (unsigned end : end_points) |
|
|
|
|
{ |
|
|
|
|
unsigned len = end - start + 1; |
|
|
|
|
if (!_iup_contour_optimize (contour_points.as_array ().sub_array (start, len), |
|
|
|
|
rounded_x_deltas.as_array ().sub_array (start, len), |
|
|
|
|
rounded_y_deltas.as_array ().sub_array (start, len), |
|
|
|
|
opt_indices.as_array ().sub_array (start, len), |
|
|
|
|
tolerance)) |
|
|
|
|
return false; |
|
|
|
|
start = end + 1; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool _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, /* OUT */ |
|
|
|
|
float tolerance = 0.f) |
|
|
|
|
{ |
|
|
|
|
unsigned n = contour_points.length; |
|
|
|
|
if (opt_indices.length != n || |
|
|
|
|
x_deltas.length != n || |
|
|
|
|
y_deltas.length != n) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
bool all_within_tolerance = true; |
|
|
|
|
for (unsigned i = 0; i < n; i++) |
|
|
|
|
{ |
|
|
|
|
int dx = x_deltas.arrayZ[i]; |
|
|
|
|
int dy = y_deltas.arrayZ[i]; |
|
|
|
|
if (sqrtf ((float)dx * dx + (float)dy * dy) > tolerance) |
|
|
|
|
{ |
|
|
|
|
all_within_tolerance = false; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* If all are within tolerance distance, do nothing, opt_indices is
|
|
|
|
|
* initilized to false */ |
|
|
|
|
if (all_within_tolerance) |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
/* If there's exactly one point, return it */ |
|
|
|
|
if (n == 1) |
|
|
|
|
{ |
|
|
|
|
opt_indices.arrayZ[0] = true; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* If all deltas are exactly the same, return just one (the first one) */ |
|
|
|
|
bool all_deltas_are_equal = true; |
|
|
|
|
for (unsigned i = 1; i < n; i++) |
|
|
|
|
if (x_deltas.arrayZ[i] != x_deltas.arrayZ[0] || |
|
|
|
|
y_deltas.arrayZ[i] != y_deltas.arrayZ[0]) |
|
|
|
|
{ |
|
|
|
|
all_deltas_are_equal = false; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (all_deltas_are_equal) |
|
|
|
|
{ |
|
|
|
|
opt_indices.arrayZ[0] = true; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* else, solve the general problem using Dynamic Programming */ |
|
|
|
|
hb_set_t forced_set; |
|
|
|
|
_iup_contour_bound_forced_set (contour_points, x_deltas, y_deltas, forced_set, tolerance); |
|
|
|
|
|
|
|
|
|
if (!forced_set.is_empty ()) |
|
|
|
|
{ |
|
|
|
|
int k = n - 1 - forced_set.get_max (); |
|
|
|
|
if (k < 0) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
hb_vector_t<int> rot_x_deltas, rot_y_deltas; |
|
|
|
|
contour_point_vector_t rot_points; |
|
|
|
|
hb_set_t rot_forced_set; |
|
|
|
|
if (!rotate_array (contour_points, k, rot_points) || |
|
|
|
|
!rotate_array (x_deltas, k, rot_x_deltas) || |
|
|
|
|
!rotate_array (y_deltas, k, rot_y_deltas) || |
|
|
|
|
!rotate_set (forced_set, k, n, rot_forced_set)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
hb_vector_t<unsigned> costs; |
|
|
|
|
hb_vector_t<int> chain; |
|
|
|
|
|
|
|
|
|
if (!_iup_contour_optimize_dp (rot_points, rot_x_deltas, rot_y_deltas, |
|
|
|
|
rot_forced_set, tolerance, n, |
|
|
|
|
costs, chain)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
hb_set_t solution; |
|
|
|
|
int index = n - 1; |
|
|
|
|
while (index != -1) |
|
|
|
|
{ |
|
|
|
|
solution.add (index); |
|
|
|
|
index = chain.arrayZ[index]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (solution.is_empty () || |
|
|
|
|
forced_set.get_population () > solution.get_population ()) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
for (unsigned i : solution) |
|
|
|
|
opt_indices.arrayZ[i] = true; |
|
|
|
|
|
|
|
|
|
hb_vector_t<bool> rot_indices; |
|
|
|
|
const hb_array_t<const bool> opt_indices_array (opt_indices.arrayZ, opt_indices.length); |
|
|
|
|
rotate_array (opt_indices_array, -k, rot_indices); |
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < n; i++) |
|
|
|
|
opt_indices.arrayZ[i] = rot_indices.arrayZ[i]; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
hb_vector_t<int> repeat_x_deltas, repeat_y_deltas; |
|
|
|
|
contour_point_vector_t repeat_points; |
|
|
|
|
|
|
|
|
|
if (unlikely (!repeat_x_deltas.resize (n * 2, false) || |
|
|
|
|
!repeat_y_deltas.resize (n * 2, false) || |
|
|
|
|
!repeat_points.resize (n * 2, false))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
unsigned contour_point_size = hb_static_size (contour_point_t); |
|
|
|
|
for (unsigned i = 0; i < n; i++) |
|
|
|
|
{ |
|
|
|
|
hb_memcpy ((void *) repeat_x_deltas.arrayZ, (const void *) x_deltas.arrayZ, n * sizeof (float)); |
|
|
|
|
hb_memcpy ((void *) (repeat_x_deltas.arrayZ + n), (const void *) x_deltas.arrayZ, n * sizeof (float)); |
|
|
|
|
|
|
|
|
|
hb_memcpy ((void *) repeat_y_deltas.arrayZ, (const void *) y_deltas.arrayZ, n * sizeof (float)); |
|
|
|
|
hb_memcpy ((void *) (repeat_y_deltas.arrayZ + n), (const void *) y_deltas.arrayZ, n * sizeof (float)); |
|
|
|
|
|
|
|
|
|
hb_memcpy ((void *) repeat_points.arrayZ, (const void *) contour_points.arrayZ, n * contour_point_size); |
|
|
|
|
hb_memcpy ((void *) (repeat_points.arrayZ + n), (const void *) contour_points.arrayZ, n * contour_point_size); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
hb_vector_t<unsigned> costs; |
|
|
|
|
hb_vector_t<int> chain; |
|
|
|
|
if (!_iup_contour_optimize_dp (repeat_points, repeat_x_deltas, repeat_y_deltas, |
|
|
|
|
forced_set, tolerance, n, |
|
|
|
|
costs, chain)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
unsigned best_cost = n + 1; |
|
|
|
|
int len = costs.length; |
|
|
|
|
hb_set_t best_sol; |
|
|
|
|
for (int start = n - 1; start < len; start++) |
|
|
|
|
{ |
|
|
|
|
hb_set_t solution; |
|
|
|
|
int i = start; |
|
|
|
|
int lookback = start - (int) n; |
|
|
|
|
while (i > lookback) |
|
|
|
|
{ |
|
|
|
|
solution.add (i % n); |
|
|
|
|
i = chain.arrayZ[i]; |
|
|
|
|
} |
|
|
|
|
if (i == lookback) |
|
|
|
|
{ |
|
|
|
|
unsigned cost_i = i < 0 ? 0 : costs.arrayZ[i]; |
|
|
|
|
unsigned cost = costs.arrayZ[start] - cost_i; |
|
|
|
|
if (cost <= best_cost) |
|
|
|
|
{ |
|
|
|
|
best_sol.set (solution); |
|
|
|
|
best_cost = cost; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < n; i++) |
|
|
|
|
if (best_sol.has (i)) |
|
|
|
|
opt_indices.arrayZ[i] = true; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool _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, |
|
|
|
|
float tolerance, |
|
|
|
|
unsigned lookback, |
|
|
|
|
hb_vector_t<unsigned>& costs, /* OUT */ |
|
|
|
|
hb_vector_t<int>& chain /* OUT */) |
|
|
|
|
{ |
|
|
|
|
unsigned n = contour_points.length; |
|
|
|
|
if (unlikely (!costs.resize (n, false) || |
|
|
|
|
!chain.resize (n, false))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
constexpr unsigned max_lookback = 8; |
|
|
|
|
lookback = hb_min (lookback, max_lookback); |
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < n; i++) |
|
|
|
|
{ |
|
|
|
|
unsigned best_cost = (i == 0 ? 1 : costs.arrayZ[i-1] + 1); |
|
|
|
|
|
|
|
|
|
costs.arrayZ[i] = best_cost; |
|
|
|
|
chain.arrayZ[i] = (i == 0 ? -1 : i - 1); |
|
|
|
|
|
|
|
|
|
if (i > 0 && forced_set.has (i - 1)) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
int lookback_index = hb_max ((int) i - (int) lookback + 1, -1); |
|
|
|
|
for (int j = i - 2; j >= lookback_index; j--) |
|
|
|
|
{ |
|
|
|
|
unsigned cost = j == -1 ? 1 : costs.arrayZ[j] + 1; |
|
|
|
|
/* num points between i and j */ |
|
|
|
|
unsigned num_points = i - j - 1; |
|
|
|
|
unsigned p1 = (j == -1 ? n - 1 : j); |
|
|
|
|
if (cost < best_cost && |
|
|
|
|
_can_iup_in_between (contour_points.as_array ().sub_array (j + 1, num_points), |
|
|
|
|
x_deltas.as_array ().sub_array (j + 1, num_points), |
|
|
|
|
y_deltas.as_array ().sub_array (j + 1, num_points), |
|
|
|
|
contour_points.arrayZ[p1], contour_points.arrayZ[i], |
|
|
|
|
x_deltas.arrayZ[p1], x_deltas.arrayZ[i], |
|
|
|
|
y_deltas.arrayZ[p1], y_deltas.arrayZ[i], |
|
|
|
|
tolerance)) |
|
|
|
|
{ |
|
|
|
|
best_cost = cost; |
|
|
|
|
costs.arrayZ[i] = best_cost; |
|
|
|
|
chain.arrayZ[i] = j; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (j > 0 && forced_set.has (j)) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool _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, |
|
|
|
|
float tolerance) |
|
|
|
|
{ |
|
|
|
|
hb_vector_t<float> interp_x_deltas, interp_y_deltas; |
|
|
|
|
if (!_iup_segment (contour_points, x_deltas, y_deltas, |
|
|
|
|
p1, p2, p1_dx, p2_dx, p1_dy, p2_dy, |
|
|
|
|
interp_x_deltas, interp_y_deltas)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
unsigned num = contour_points.length; |
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < num; i++) |
|
|
|
|
{ |
|
|
|
|
float dx = x_deltas.arrayZ[i] - interp_x_deltas.arrayZ[i]; |
|
|
|
|
float dy = y_deltas.arrayZ[i] - interp_y_deltas.arrayZ[i]; |
|
|
|
|
|
|
|
|
|
if (sqrtf ((float)dx * dx + (float)dy * dy) > tolerance) |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Given two reference coordinates (start and end of contour_points array),
|
|
|
|
|
* output interpolated deltas for points in between */ |
|
|
|
|
static bool _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<float>& interp_x_deltas, /* OUT */ |
|
|
|
|
hb_vector_t<float>& interp_y_deltas /* OUT */) |
|
|
|
|
{ |
|
|
|
|
unsigned n = contour_points.length; |
|
|
|
|
if (unlikely (!interp_x_deltas.resize (n, false) || |
|
|
|
|
!interp_y_deltas.resize (n, false))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
for (unsigned j = 0; j < 2; j++) |
|
|
|
|
{ |
|
|
|
|
float x1, x2, d1, d2; |
|
|
|
|
float *out; |
|
|
|
|
if (j == 0) |
|
|
|
|
{ |
|
|
|
|
x1 = p1.x; |
|
|
|
|
x2 = p2.x; |
|
|
|
|
d1 = p1_dx; |
|
|
|
|
d2 = p2_dx; |
|
|
|
|
out = interp_x_deltas.arrayZ; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
x1 = p1.y; |
|
|
|
|
x2 = p2.y; |
|
|
|
|
d1 = p1_dy; |
|
|
|
|
d2 = p2_dy; |
|
|
|
|
out = interp_y_deltas.arrayZ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (x1 == x2) |
|
|
|
|
{ |
|
|
|
|
if (d1 == d2) |
|
|
|
|
{ |
|
|
|
|
for (unsigned i = 0; i < n; i++) |
|
|
|
|
out[i] = d1; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
for (unsigned i = 0; i < n; i++) |
|
|
|
|
out[i] = 0.f; |
|
|
|
|
} |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (x1 > x2) |
|
|
|
|
{ |
|
|
|
|
hb_swap (x1, x2); |
|
|
|
|
hb_swap (d1, d2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float scale = (d2 - d1) / (x2 - x1); |
|
|
|
|
for (unsigned i = 0; i < n; i++) |
|
|
|
|
{ |
|
|
|
|
float x = j == 0 ? contour_points.arrayZ[i].x : contour_points.arrayZ[i].y; |
|
|
|
|
float d; |
|
|
|
|
if (x <= x1) |
|
|
|
|
d = d1; |
|
|
|
|
else if (x >= x2) |
|
|
|
|
d = d2; |
|
|
|
|
else |
|
|
|
|
d = d1 + (x - x1) * scale; |
|
|
|
|
|
|
|
|
|
out[i] = d; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename T, |
|
|
|
|
hb_enable_if (hb_is_trivially_copyable (T))> |
|
|
|
|
static bool rotate_array (const hb_array_t<const T>& org_array, |
|
|
|
|
int k, |
|
|
|
|
hb_vector_t<T>& out) |
|
|
|
|
{ |
|
|
|
|
unsigned n = org_array.length; |
|
|
|
|
if (!n) return true; |
|
|
|
|
if (unlikely (!out.resize (n, false))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
unsigned item_size = hb_static_size (T); |
|
|
|
|
if (k < 0) |
|
|
|
|
k = n - (-k) % n; |
|
|
|
|
else |
|
|
|
|
k %= n; |
|
|
|
|
|
|
|
|
|
hb_memcpy ((void *) out.arrayZ, (const void *) (org_array.arrayZ + n - k), k * item_size); |
|
|
|
|
hb_memcpy ((void *) (out.arrayZ + k), (const void *) org_array.arrayZ, (n - k) * item_size); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool rotate_set (const hb_set_t& org_set, |
|
|
|
|
int k, |
|
|
|
|
unsigned n, |
|
|
|
|
hb_set_t& out) |
|
|
|
|
{ |
|
|
|
|
if (!n) return false; |
|
|
|
|
k %= n; |
|
|
|
|
if (k < 0) |
|
|
|
|
k = n + k; |
|
|
|
|
|
|
|
|
|
if (k == 0) |
|
|
|
|
{ |
|
|
|
|
out.set (org_set); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
for (auto v : org_set) |
|
|
|
|
out.add ((v + k) % n); |
|
|
|
|
} |
|
|
|
|
return !out.in_error (); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ported from fonttools */ |
|
|
|
|
static void _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, /* OUT */ |
|
|
|
|
float tolerance = 0.f) |
|
|
|
|
{ |
|
|
|
|
unsigned len = contour_points.length; |
|
|
|
|
unsigned next_i = 0; |
|
|
|
|
for (int i = len - 1; i >= 0; i--) |
|
|
|
|
{ |
|
|
|
|
unsigned last_i = (len + i -1) % len; |
|
|
|
|
for (unsigned j = 0; j < 2; j++) |
|
|
|
|
{ |
|
|
|
|
float cj, lcj, ncj; |
|
|
|
|
int dj, ldj, ndj; |
|
|
|
|
if (j == 0) |
|
|
|
|
{ |
|
|
|
|
cj = contour_points.arrayZ[i].x; |
|
|
|
|
dj = x_deltas.arrayZ[i]; |
|
|
|
|
lcj = contour_points.arrayZ[last_i].x; |
|
|
|
|
ldj = x_deltas.arrayZ[last_i]; |
|
|
|
|
ncj = contour_points.arrayZ[next_i].x; |
|
|
|
|
ndj = x_deltas.arrayZ[next_i]; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
cj = contour_points.arrayZ[i].y; |
|
|
|
|
dj = y_deltas.arrayZ[i]; |
|
|
|
|
lcj = contour_points.arrayZ[last_i].y; |
|
|
|
|
ldj = y_deltas.arrayZ[last_i]; |
|
|
|
|
ncj = contour_points.arrayZ[next_i].y; |
|
|
|
|
ndj = y_deltas.arrayZ[next_i]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float c1, c2; |
|
|
|
|
int d1, d2; |
|
|
|
|
if (lcj <= ncj) |
|
|
|
|
{ |
|
|
|
|
c1 = lcj; |
|
|
|
|
c2 = ncj; |
|
|
|
|
d1 = ldj; |
|
|
|
|
d2 = ndj; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
c1 = ncj; |
|
|
|
|
c2 = lcj; |
|
|
|
|
d1 = ndj; |
|
|
|
|
d2 = ldj; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool force = false; |
|
|
|
|
if (c1 == c2) |
|
|
|
|
{ |
|
|
|
|
if (fabsf (d1 - d2) > tolerance && fabsf (dj) > tolerance) |
|
|
|
|
force = true; |
|
|
|
|
} |
|
|
|
|
else if (c1 <= cj && cj <= c2) |
|
|
|
|
{ |
|
|
|
|
if (!(hb_min (d1, d2) - tolerance <= dj && |
|
|
|
|
dj <= hb_max (d1, d2) + tolerance)) |
|
|
|
|
force = true; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
if (d1 != d2) |
|
|
|
|
{ |
|
|
|
|
if (cj < c1) |
|
|
|
|
{ |
|
|
|
|
if (fabsf (dj) > tolerance && |
|
|
|
|
fabsf (dj - d1) > tolerance && |
|
|
|
|
((dj - tolerance < d1) != (d1 < d2))) |
|
|
|
|
force = true; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
if (fabsf (dj) > tolerance && |
|
|
|
|
fabsf (dj - d2) > tolerance && |
|
|
|
|
((d2 < dj + tolerance) != (d1 < d2))) |
|
|
|
|
force = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (force) |
|
|
|
|
{ |
|
|
|
|
forced_set.add (i); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
next_i = i; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool compile_point_set (const hb_vector_t<bool> &point_indices, |
|
|
|
|
hb_vector_t<char>& compiled_points /* OUT */) |
|
|
|
|
{ |
|
|
|
|