emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master f80f1b23bfd: Bring GX point interpolation further into standards


From: Po Lu
Subject: master f80f1b23bfd: Bring GX point interpolation further into standards compliance
Date: Sun, 31 Dec 2023 22:31:21 -0500 (EST)

branch: master
commit f80f1b23bfd277a9db0ee6961a3d9f09c4cba219
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    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.
---
 src/sfnt.c | 451 +++++++++++++++++++++++++------------------------------------
 1 file 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);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]