get rid of O(n^2) loop from apply_deltas_to_points

This commit is contained in:
Michiharu Ariza 2019-05-03 10:42:11 -07:00
parent 157a414bd9
commit 49252c42bb
1 changed files with 38 additions and 17 deletions

View File

@ -577,6 +577,9 @@ struct gvar
return (1.f - r) * prev_delta + r * next_delta; return (1.f - r) * prev_delta + r * next_delta;
} }
static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end)
{ return (i >= end)? start: (i + 1); }
public: public:
bool apply_deltas_to_points (hb_codepoint_t glyph, bool apply_deltas_to_points (hb_codepoint_t glyph,
const int *coords, unsigned int coord_count, const int *coords, unsigned int coord_count,
@ -646,27 +649,45 @@ struct gvar
for (unsigned int c = 0; c < end_points.length; c++) for (unsigned int c = 0; c < end_points.length; c++)
{ {
unsigned int end_point = end_points[c]; unsigned int end_point = end_points[c];
for (unsigned int i = start_point; i <= end_point; i++) unsigned int i, j;
/* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */
unsigned int unref_count = 0;
for (i = start_point; i <= end_point; i++)
if (!deltas[i].flag) unref_count++;
if (unref_count == 0 || unref_count > end_point - start_point)
goto no_more_gaps;
j = start_point;
for (;;)
{ {
if (deltas[i].flag) continue; /* Locate the next gap of unreferenced points between two referenced points prev and next.
/* search in both directions within the contour for a pair of referenced points */ * Note that a gap may wrap around at left (start_point) and/or at right (end_point).
unsigned int prev; */
for (prev = i;;) unsigned int prev, next;
{ for (;;) {
if (prev-- <= start_point) prev = end_point; i = j;
if (prev == i || deltas[prev].flag) break; j = next_index (i, start_point, end_point);
if (deltas[i].flag && !deltas[j].flag) break;
} }
if (prev == i) continue; /* no (previous) referenced point was found */ prev = j = i;
unsigned int next; for (;;) {
for (next = i;;) i = j;
{ j = next_index (i, start_point, end_point);
if (next++ >= end_point) next = start_point; if (!deltas[i].flag && deltas[j].flag) break;
if (next == i || deltas[next].flag) break;
} }
assert (next != i); next = j;
/* Infer deltas for all unref points in the gap between prev and next */
i = prev;
for (;;) {
i = next_index (i, start_point, end_point);
if (i == next) break;
deltas[i].x = infer_delta<x_getter> (orig_points.as_array (), deltas.as_array (), i, prev, next); deltas[i].x = infer_delta<x_getter> (orig_points.as_array (), deltas.as_array (), i, prev, next);
deltas[i].y = infer_delta<y_getter> (orig_points.as_array (), deltas.as_array (), i, prev, next); deltas[i].y = infer_delta<y_getter> (orig_points.as_array (), deltas.as_array (), i, prev, next);
if (--unref_count == 0) goto no_more_gaps;
} }
}
no_more_gaps:
start_point = end_point + 1; start_point = end_point + 1;
} }