]> git.eshelyaron.com Git - emacs.git/commitdiff
Round projs computed executing IP/ISECT instructions and improve IUP
authorPo Lu <luangruo@yahoo.com>
Sat, 20 Jan 2024 01:30:04 +0000 (09:30 +0800)
committerEshel Yaron <me@eshelyaron.com>
Sat, 20 Jan 2024 09:07:25 +0000 (10:07 +0100)
* src/sfnt.c (sfnt_multiply_divide_rounded): New function.
(sfnt_multiply_divide_signed): Always round values, as fonts
which rely on IP to move points in concert with prior motion and
subsequently round such points with MDAP are sensitive to minor
deviations in the behavior of the former instruction.
(load_unscaled): New macro.
(IUP_SINGLE_PAIR, sfnt_interpret_iup_1): Compute ratio w/
unscaled points if possible.

(cherry picked from commit b3e4fbe867f96a28c5dc9db19fcad2af5b4a4b7e)

src/sfnt.c

index ca4c60e8e3afaf25ab6affd7af21ab91d59cbb1e..88826e1b2c19f9223880e4b4367f3f4f02f62492 100644 (file)
@@ -3767,7 +3767,23 @@ sfnt_multiply_divide_2 (struct sfnt_large_integer *ab,
   return q;
 }
 
-#endif
+/* Add the specified unsigned 32-bit N to the large integer
+   INTEGER.  */
+
+static void
+sfnt_large_integer_add (struct sfnt_large_integer *integer,
+                       uint32_t n)
+{
+  struct sfnt_large_integer number;
+
+  number.low = integer->low + n;
+  number.high = integer->high + (number.low
+                                < integer->low);
+
+  *integer = number;
+}
+
+#endif /* !INT64_MAX */
 
 /* Calculate (A * B) / C with no rounding and return the result, using
    a 64 bit integer if necessary.  */
@@ -3780,32 +3796,37 @@ sfnt_multiply_divide (unsigned int a, unsigned int b, unsigned int c)
 
   sfnt_multiply_divide_1 (a, b, &temp);
   return sfnt_multiply_divide_2 (&temp, c);
-#else
+#else /* INT64_MAX */
   uint64_t temp;
 
   temp = (uint64_t) a * (uint64_t) b;
   return temp / c;
-#endif
+#endif /* !INT64_MAX */
 }
 
-#ifndef INT64_MAX
+/* Calculate (A * B) / C with rounding and return the result, using a
+   64 bit integer if necessary.  */
 
-/* Add the specified unsigned 32-bit N to the large integer
-   INTEGER.  */
-
-static void
-sfnt_large_integer_add (struct sfnt_large_integer *integer,
-                       uint32_t n)
+static unsigned int
+sfnt_multiply_divide_rounded (unsigned int a, unsigned int b,
+                             unsigned int c)
 {
-  struct sfnt_large_integer number;
+#ifndef INT64_MAX
+  struct sfnt_large_integer temp;
 
-  number.low = integer->low + n;
-  number.high = integer->high + (number.low
-                                < integer->low);
+  sfnt_multiply_divide_1 (a, b, &temp);
+  sfnt_large_integer_add (&temp, c / 2);
+  return sfnt_multiply_divide_2 (&temp, c);
+#else /* INT64_MAX */
+  uint64_t temp;
 
-  *integer = number;
+  temp = (uint64_t) a * (uint64_t) b + c / 2;
+  return temp / c;
+#endif /* !INT64_MAX */
 }
 
+#ifndef INT64_MAX
+
 /* Calculate (A * B) / C, rounding the result with a threshold of N.
    Use a 64 bit temporary.  */
 
@@ -3820,9 +3841,9 @@ sfnt_multiply_divide_round (unsigned int a, unsigned int b,
   return sfnt_multiply_divide_2 (&temp, c);
 }
 
-#endif /* INT64_MAX */
+#endif /* !INT64_MAX */
 
-/* The same as sfnt_multiply_divide, but handle signed values
+/* The same as sfnt_multiply_divide_rounded, but handle signed values
    instead.  */
 
 MAYBE_UNUSED static int
@@ -3841,8 +3862,8 @@ sfnt_multiply_divide_signed (int a, int b, int c)
   if (c < 0)
     sign = -sign;
 
-  return (sfnt_multiply_divide (abs (a), abs (b), abs (c))
-         * sign);
+  return (sfnt_multiply_divide_rounded (abs (a), abs (b),
+                                       abs (c)) * sign);
 }
 
 /* Multiply the two 16.16 fixed point numbers X and Y.  Return the
@@ -3858,7 +3879,7 @@ sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y)
 
   /* This can be done quickly with int64_t.  */
   return product / (int64_t) 65536;
-#else
+#else /* !INT64_MAX */
   int sign;
 
   sign = 1;
@@ -3871,7 +3892,7 @@ sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y)
 
   return sfnt_multiply_divide (abs (x), abs (y),
                               65536) * sign;
-#endif
+#endif /* INT64_MAX */
 }
 
 /* Multiply the two 16.16 fixed point numbers X and Y, with rounding
@@ -3888,7 +3909,7 @@ sfnt_mul_fixed_round (sfnt_fixed x, sfnt_fixed y)
 
   /* This can be done quickly with int64_t.  */
   return (product + round) / (int64_t) 65536;
-#else
+#else /* !INT64_MAX */
   int sign;
 
   sign = 1;
@@ -3901,7 +3922,7 @@ sfnt_mul_fixed_round (sfnt_fixed x, sfnt_fixed y)
 
   return sfnt_multiply_divide_round (abs (x), abs (y),
                                     32768, 65536) * sign;
-#endif
+#endif /* INT64_MAX */
 }
 
 /* Set the pen size to the specified point and return.  POINT will be
@@ -6542,7 +6563,7 @@ sfnt_mul_f26dot6_fixed (sfnt_f26dot6 x, sfnt_fixed y)
     sign = -sign;
 
   sfnt_multiply_divide_1 (abs (x), abs (y), &temp);
-  sfnt_large_integer_add (&temp, 32676);
+  sfnt_large_integer_add (&temp, 32768);
   return sfnt_multiply_divide_2 (&temp, 65536) * sign;
 #endif
 }
@@ -11137,6 +11158,11 @@ sfnt_interpret_shp (struct sfnt_interpreter *interpreter,
    ? interpreter->glyph_zone->x_points[p]      \
    : interpreter->glyph_zone->y_points[p])
 
+#define load_unscaled(p)                               \
+  (opcode == 0x31                                      \
+   ? interpreter->glyph_zone->simple->x_coordinates[p] \
+   : interpreter->glyph_zone->simple->y_coordinates[p])
+
 #define IUP_SINGLE_PAIR()                                              \
   /* Now make touch_start the first point before, i.e. the first       \
      touched point in this pair.  */                                   \
@@ -11186,23 +11212,40 @@ sfnt_interpret_shp (struct sfnt_interpreter *interpreter,
       if (position >= original_min_pos                                 \
          && position <= original_max_pos)                              \
        {                                                               \
+         /* Compute the ratio between the two touched point positions  \
+            and the original position of the point being touched with  \
+            positions from the unscaled outline, if at all             \
+            possible.  */                                              \
+                                                                       \
+         if (interpreter->glyph_zone->simple)                          \
+           {                                                           \
+             org_max_pos = load_unscaled (point_max);                  \
+             org_min_pos = load_unscaled (point_min);                  \
+             position = load_unscaled (i);                             \
+           }                                                           \
+         else                                                          \
+           {                                                           \
+             org_max_pos = original_max_pos;                           \
+             org_min_pos = original_min_pos;                           \
+           }                                                           \
+                                                                       \
          /* Handle the degenerate case where original_min_pos and      \
             original_max_pos have not changed by placing the point in  \
             the middle.  */                                            \
-         if (original_min_pos == original_max_pos)                     \
+         if (org_min_pos == org_max_pos)                               \
            ratio = 077777;                                             \
          else                                                          \
            /* ... preserve the ratio of i between min_pos and          \
               max_pos...  */                                           \
            ratio = sfnt_div_fixed ((sfnt_sub (position,                \
-                                              original_min_pos)        \
+                                              org_min_pos)             \
                                     * 1024),                           \
-                                   (sfnt_sub (original_max_pos,        \
-                                              original_min_pos)        \
+                                   (sfnt_sub (org_max_pos,             \
+                                              org_min_pos)             \
                                     * 1024));                          \
                                                                        \
          delta = sfnt_sub (max_pos, min_pos);                          \
-         delta = sfnt_mul_fixed (ratio, delta);                        \
+         delta = sfnt_mul_fixed_round (ratio, delta);                  \
          store_point (i, sfnt_add (min_pos, delta));                   \
        }                                                               \
       else                                                             \
@@ -11237,8 +11280,8 @@ sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter,
   size_t first_point;
   size_t point_min, point_max, i;
   sfnt_f26dot6 position, min_pos, max_pos, delta, ratio;
-  sfnt_f26dot6 original_max_pos;
-  sfnt_f26dot6 original_min_pos;
+  sfnt_f26dot6 original_max_pos, org_max_pos;
+  sfnt_f26dot6 original_min_pos, org_min_pos;
 
   /* Find the first touched point.  If none is found, simply
      return.  */
@@ -11324,6 +11367,7 @@ sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter,
 #undef load_point
 #undef store_point
 #undef load_original
+#undef load_unscaled
 
 /* Interpret an IUP (``interpolate untouched points'') instruction.
    INTERPRETER is the interpreter, and OPCODE is the instruction