summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2024-01-01 11:27:59 +0800
committerPo Lu <luangruo@yahoo.com>2024-01-01 11:27:59 +0800
commitf80f1b23bfd277a9db0ee6961a3d9f09c4cba219 (patch)
treef7f167f20fc1076118064e82f5b0d870f22e4f96
parent82bb8de74617b3af083412614c126c82153c2a29 (diff)
downloademacs-f80f1b23bfd277a9db0ee6961a3d9f09c4cba219.tar.gz
Bring GX point interpolation further into standards compliance
* src/sfnt.c (sfnt_infer_deltas_2): New function; factor much of sfnt_infer_deltas_1 into this function, then modify its treatment of untouched points positioned at their reference points to align with standard GX treatment. (sfnt_infer_deltas_1): Remove all code not concerning anchor point discovery. (main): Adjust tests.
-rw-r--r--src/sfnt.c451
1 files changed, 182 insertions, 269 deletions
diff --git a/src/sfnt.c b/src/sfnt.c
index 7625254d0bd..deafaec31e7 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -15314,322 +15314,235 @@ sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p,
return scale;
}
-/* Infer point positions for points that have been partially moved
- within the contour in GLYPH denoted by START and END. */
+/* Move each point in the simple glyph GLYPH between PAIR_START and
+ PAIR_END to agree with the positions of those two anchor points as
+ compared with their initial positions recorded within the arrays X
+ and Y.
+
+ The range formed between PAIR_START and PAIR_END may encompass the
+ upper extreme of the contour between START and END. */
static void
-sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start,
- size_t end, bool *touched, sfnt_fword *x,
- sfnt_fword *y)
+sfnt_infer_deltas_2 (struct sfnt_glyph *glyph, size_t pair_start,
+ size_t pair_end, size_t start, size_t end,
+ sfnt_fword *x, sfnt_fword *y)
{
- size_t i, pair_start, pair_end, pair_first, j;
- sfnt_fword min_pos, max_pos, position;
+ size_t j;
+ sfnt_fword min_pos, max_pos, position, d1, d2;
sfnt_fixed ratio, delta;
- pair_start = pair_first = -1;
-
- /* Look for pairs of touched points. */
+ j = pair_start + 1;
- for (i = start; i <= end; ++i)
+ while (j != pair_end)
{
- if (!touched[i])
- continue;
+ /* Reset j to the contour's start position if it is about to
+ overrun this contour. */
- if (pair_start == -1)
+ if (j > end)
{
- pair_first = i;
- goto next;
+ /* The start of the contour might also be the end of this
+ reference point. */
+ if (start == pair_end)
+ return;
+
+ j = start;
}
- pair_end = i;
+ /* Consider the X axis. Set min_pos and max_pos to the
+ smallest and greatest values along that axis. */
+ min_pos = MIN (x[pair_start], x[pair_end]);
+ max_pos = MAX (x[pair_start], x[pair_end]);
- /* pair_start to pair_end are now a pair of points, where points
- in between should be interpolated. */
+ /* Now see if the current point lies between min and
+ max...
- for (j = pair_start + 1; j < pair_end; ++j)
+ GX interpolation differs from IUP in one important detail:
+ points are shifted to follow the movement of their reference
+ points if their positions are identical to those of any of
+ their reference points, whereas IUP considers such points to
+ fall within their reference points. */
+ if (x[j] > min_pos && x[j] < max_pos)
+ {
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->x_coordinates[j] = min_pos + delta;
+ }
+ else
{
- /* Consider the X axis. Set min_pos and max_pos to the
- smallest and greatest values along that axis. */
- min_pos = MIN (x[pair_start], x[pair_end]);
- max_pos = MAX (x[pair_start], x[pair_end]);
-
- /* Now see if the current point lies between min and
- max... */
- if (x[j] >= min_pos && x[j] <= max_pos)
+ /* ... otherwise, move point j by the delta of the
+ nearest touched point. */
+
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
{
- /* If min_pos and max_pos are the same, apply
- pair_start's delta if it is identical to that of
- pair_end, or apply nothing at all otherwise. */
+ d1 = (glyph->simple->x_coordinates[pair_start]
+ - x[pair_start]);
+ d2 = (glyph->simple->x_coordinates[pair_end]
+ - x[pair_start]);
- if (min_pos == max_pos)
- {
- if ((glyph->simple->x_coordinates[pair_start]
- - x[pair_start])
- == (glyph->simple->x_coordinates[pair_end]
- - x[pair_end]))
- glyph->simple->x_coordinates[j]
- += (glyph->simple->x_coordinates[pair_start]
- - x[pair_start]);
+ if (d1 == d2)
+ glyph->simple->x_coordinates[j] += d1;
- continue;
- }
+ goto consider_y;
+ }
- /* Interpolate between min_pos and max_pos. */
- ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
- * 65536),
- (sfnt_sub (max_pos, min_pos)
- * 65536));
-
- /* Load the current positions of pair_start and pair_end
- along this axis. */
- min_pos = MIN (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
- max_pos = MAX (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
-
- /* Lerp in between. */
- delta = sfnt_sub (max_pos, min_pos);
- delta = sfnt_mul_fixed (ratio, delta);
- glyph->simple->x_coordinates[j] = min_pos + delta;
+ if (x[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - max_pos;
}
else
{
- /* ... otherwise, move point j by the delta of the
- nearest touched point. */
+ position = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - min_pos;
+ }
- if (x[j] >= max_pos)
- {
- position = MAX (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
- delta = position - max_pos;
- }
- else
- {
- position = MIN (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
- delta = position - min_pos;
- }
+ glyph->simple->x_coordinates[j] = x[j] + delta;
+ }
- glyph->simple->x_coordinates[j] = x[j] + delta;
- }
+ consider_y:
+
+ /* Now, consider the Y axis. */
+ min_pos = MIN (y[pair_start], y[pair_end]);
+ max_pos = MAX (y[pair_start], y[pair_end]);
- /* Now, consider the Y axis. */
- min_pos = MIN (y[pair_start], y[pair_end]);
- max_pos = MAX (y[pair_start], y[pair_end]);
+ /* Now see if the current point lies between min and
+ max...
- /* Now see if the current point lies between min and
- max... */
- if (y[j] >= min_pos && y[j] <= max_pos)
+ GX interpolation differs from IUP in one important detail:
+ points are shifted to follow the movement of their reference
+ points if their positions are identical to those of any of
+ their reference points, whereas IUP considers such points to
+ fall within their reference points. */
+ if (y[j] > min_pos && y[j] < max_pos)
+ {
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->y_coordinates[j] = min_pos + delta;
+ }
+ else
+ {
+ /* ... otherwise, move point j by the delta of the
+ nearest touched point. */
+
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
{
- /* If min_pos and max_pos are the same, apply
- pair_start's delta if it is identical to that of
- pair_end, or apply nothing at all otherwise. */
+ d1 = (glyph->simple->y_coordinates[pair_start]
+ - y[pair_start]);
+ d2 = (glyph->simple->y_coordinates[pair_end]
+ - y[pair_start]);
- if (min_pos == max_pos)
- {
- if ((glyph->simple->y_coordinates[pair_start]
- - y[pair_start])
- == (glyph->simple->y_coordinates[pair_end]
- - y[pair_end]))
- glyph->simple->y_coordinates[j]
- += (glyph->simple->y_coordinates[pair_start]
- - y[pair_start]);
+ if (d1 == d2)
+ glyph->simple->y_coordinates[j] += d1;
- continue;
- }
+ goto next;
+ }
- /* Interpolate between min_pos and max_pos. */
- ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
- * 65536),
- (sfnt_sub (max_pos, min_pos)
- * 65536));
-
- /* Load the current positions of pair_start and pair_end
- along this axis. */
- min_pos = MIN (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
- max_pos = MAX (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
-
- /* Lerp in between. */
- delta = sfnt_sub (max_pos, min_pos);
- delta = sfnt_mul_fixed (ratio, delta);
- glyph->simple->y_coordinates[j] = min_pos + delta;
+ if (y[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - max_pos;
}
else
{
- /* ... otherwise, move point j by the delta of the
- nearest touched point. */
-
- if (y[j] >= max_pos)
- {
- position = MAX (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
- delta = position - max_pos;
- }
- else
- {
- position = MIN (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
- delta = position - min_pos;
- }
-
- glyph->simple->y_coordinates[j] = y[j] + delta;
+ position = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - min_pos;
}
+
+ glyph->simple->y_coordinates[j] = y[j] + delta;
}
next:
- pair_start = i;
+ j++;
}
+}
- /* If pair_start is set, then lerp points between it and
- pair_first. */
-
- if (pair_start != (size_t) -1)
- {
- j = pair_start + 1;
-
- if (j > end)
- j = start;
-
- pair_end = pair_first;
-
- while (j != pair_first)
- {
- /* Consider the X axis. Set min_pos and max_pos to the
- smallest and greatest values along that axis. */
- min_pos = MIN (x[pair_start], x[pair_end]);
- max_pos = MAX (x[pair_start], x[pair_end]);
-
- /* Now see if the current point lies between min and
- max... */
- if (x[j] >= min_pos && x[j] <= max_pos)
- {
- /* If min_pos and max_pos are the same, apply
- pair_start's delta if it is identical to that of
- pair_end, or apply nothing at all otherwise. */
+/* Infer point positions for points that have been partially moved
+ within the contour in GLYPH denoted by START and END. */
- if (min_pos == max_pos)
- {
- if ((glyph->simple->x_coordinates[pair_start]
- - x[pair_start])
- == (glyph->simple->x_coordinates[pair_end]
- - x[pair_end]))
- glyph->simple->x_coordinates[j]
- += (glyph->simple->x_coordinates[pair_start]
- - x[pair_start]);
-
- goto next_1;
- }
+static void
+sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start,
+ size_t end, bool *touched, sfnt_fword *x,
+ sfnt_fword *y)
+{
+ size_t i, pair_start, pair_end, pair_first;
- /* Interpolate between min_pos and max_pos. */
- ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
- * 65536),
- (sfnt_sub (max_pos, min_pos)
- * 65536));
-
- /* Load the current positions of pair_start and pair_end
- along this axis. */
- min_pos = MIN (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
- max_pos = MAX (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
-
- /* Lerp in between. */
- delta = sfnt_sub (max_pos, min_pos);
- delta = sfnt_mul_fixed (ratio, delta);
- glyph->simple->x_coordinates[j] = min_pos + delta;
- }
- else
- {
- /* ... otherwise, move point j by the delta of the
- nearest touched point. */
+ pair_start = pair_first = -1;
- if (x[j] >= max_pos)
- {
- position = MAX (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
- delta = position - max_pos;
- }
- else
- {
- position = MIN (glyph->simple->x_coordinates[pair_start],
- glyph->simple->x_coordinates[pair_end]);
- delta = position - min_pos;
- }
+ /* Look for pairs of touched points. */
- glyph->simple->x_coordinates[j] = x[j] + delta;
- }
+ for (i = start; i <= end; ++i)
+ {
+ if (!touched[i])
+ continue;
- /* Now, consider the Y axis. */
- min_pos = MIN (y[pair_start], y[pair_end]);
- max_pos = MAX (y[pair_start], y[pair_end]);
+ if (pair_start == -1)
+ {
+ pair_first = i;
+ goto next;
+ }
- /* Now see if the current point lies between min and
- max... */
- if (y[j] >= min_pos && y[j] <= max_pos)
- {
- /* If min_pos and max_pos are the same, apply
- pair_start's delta if it is identical to that of
- pair_end, or apply nothing at all otherwise. */
+ pair_end = i;
- if (min_pos == max_pos)
- {
- if ((glyph->simple->y_coordinates[pair_start]
- - y[pair_start])
- == (glyph->simple->y_coordinates[pair_end]
- - y[pair_end]))
- glyph->simple->y_coordinates[j]
- += (glyph->simple->y_coordinates[pair_start]
- - y[pair_start]);
-
- goto next_1;
- }
+ /* pair_start to pair_end are now a pair of points whose
+ intermediates should be interpolated. */
+ sfnt_infer_deltas_2 (glyph, pair_start, pair_end,
+ start, end, x, y);
- /* Interpolate between min_pos and max_pos. */
- ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
- * 65536),
- (sfnt_sub (max_pos, min_pos)
- * 65536));
-
- /* Load the current positions of pair_start and pair_end
- along this axis. */
- min_pos = MIN (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
- max_pos = MAX (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
-
- /* Lerp in between. */
- delta = sfnt_sub (max_pos, min_pos);
- delta = sfnt_mul_fixed (ratio, delta);
- glyph->simple->y_coordinates[j] = min_pos + delta;
- }
- else
- {
- /* ... otherwise, move point j by the delta of the
- nearest touched point. */
+ next:
+ pair_start = i;
+ }
- if (y[j] >= max_pos)
- {
- position = MAX (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
- delta = position - max_pos;
- }
- else
- {
- position = MIN (glyph->simple->y_coordinates[pair_start],
- glyph->simple->y_coordinates[pair_end]);
- delta = position - min_pos;
- }
+ /* If pair_start is set, then lerp points between it and
+ pair_first. */
- glyph->simple->y_coordinates[j] = y[j] + delta;
- }
+ if (pair_start != (size_t) -1)
+ {
+ pair_end = pair_first;
- next_1:
- j++;
- if (j > end)
- j = start;
- }
+ /* pair_start to pair_end are now a pair of points whose
+ intermediates should be interpolated. */
+ sfnt_infer_deltas_2 (glyph, pair_start, pair_end,
+ start, end, x, y);
}
}
@@ -20696,8 +20609,8 @@ main (int argc, char **argv)
return 1;
}
-#define FANCY_PPEM 30
-#define EASY_PPEM 30
+#define FANCY_PPEM 44
+#define EASY_PPEM 44
interpreter = NULL;
head = sfnt_read_head_table (fd, font);