]> git.eshelyaron.com Git - emacs.git/commitdiff
Properly instruct Italic Arial or BS Mono at small PPEM sizes
authorPo Lu <luangruo@yahoo.com>
Mon, 8 Jan 2024 07:32:07 +0000 (15:32 +0800)
committerPo Lu <luangruo@yahoo.com>
Mon, 8 Jan 2024 07:51:51 +0000 (15:51 +0800)
* src/sfnt.c (sfnt_read_simple_glyph): Correct alignment errors.
(PUSH2_UNCHECKED): Don't shift negative signed value to the
left.
(SLOOP): Permit LOOP to be set to 0, which inhibits the
execution of instructions it affects.
(sfnt_address_zp2, sfnt_address_zp1, sfnt_address_zp0): Permit X
and Y to be NULL.
(sfnt_dot_fix_14): Guarantee that the final value is rounded to
negative infinity, not zero.
(sfnt_project_zp1_zp0_org): New function.
(sfnt_interpret_mdrp): Avoid rounding issues by computing
original distance from unscaled coordinates, if at all possible.
(sfnt_interpret_simple_glyph, sfnt_interpret_compound_glyph_2):
Set zone->simple.
(all_tests) <LOOP>: Update test.
(sfnt_identify_instruction, main): Adjust tests.

* src/sfnt.h (struct sfnt_interpreter_zone): New field simple.

src/sfnt.c
src/sfnt.h

index 36240f4cdffd170381d36933c927125165a790a0..b300eb4ba89a599a96671c678764985d4d222204 100644 (file)
@@ -1937,8 +1937,11 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph,
      simple->instructions comes one word past number_of_contours,
      because end_pts_of_contours also contains the instruction
      length.  */
-  simple->instructions = (uint8_t *) (simple->end_pts_of_contours
-                                     + glyph->number_of_contours + 1);
+
+  simple->x_coordinates = (int16_t *) (simple->end_pts_of_contours
+                                      + glyph->number_of_contours + 1);
+  simple->y_coordinates = simple->x_coordinates + number_of_points;
+  simple->instructions = (uint8_t *) (simple->y_coordinates + number_of_points);
   simple->flags = simple->instructions + simple->instruction_length;
 
   /* Read instructions into the glyph.  */
@@ -2022,7 +2025,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph,
 
   /* Now that the flags have been decoded, start decoding the
      vectors.  */
-  simple->x_coordinates = (int16_t *) (simple->flags + number_of_points);
   vec_start = flags_start;
   i = 0;
   x = 0;
@@ -2080,7 +2082,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph,
      pointer to the flags for the current vector.  */
   flags_start = simple->flags;
   y = 0;
-  simple->y_coordinates = simple->x_coordinates + i;
   i = 0;
 
   while (i < number_of_points)
@@ -6944,7 +6945,7 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter,
   {                                            \
     int16_t word;                              \
                                                \
-    word = (((int8_t) high) << 8 | low);       \
+    word = (((uint8_t) high) << 8 | low);      \
     PUSH_UNCHECKED (word);                     \
   }                                            \
 
@@ -7024,14 +7025,18 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter,
 
 #define SLOOP()                                        \
   {                                            \
-    uint32_t loop;                             \
+    int32_t loop;                              \
                                                \
     loop = POP ();                             \
                                                \
-    if (!loop)                                 \
-      TRAP ("loop set to 0");                  \
+    if (loop < 0)                              \
+      TRAP ("loop set to invalid value");      \
                                                \
-    interpreter->state.loop = loop;            \
+    /* N.B. loop might be greater than 65535,  \
+       but no reasonable font should define    \
+       such values.  */                                \
+    interpreter->state.loop                    \
+      = MIN (65535, loop);                     \
   }
 
 #define SMD()                                  \
@@ -8570,8 +8575,11 @@ sfnt_address_zp2 (struct sfnt_interpreter *interpreter,
   if (number >= interpreter->glyph_zone->num_points)
     TRAP ("address to ZP2 (glyph zone) out of bounds");
 
-  *x = interpreter->glyph_zone->x_current[number];
-  *y = interpreter->glyph_zone->y_current[number];
+  if (x && y)
+    {
+      *x = interpreter->glyph_zone->x_current[number];
+      *y = interpreter->glyph_zone->y_current[number];
+    }
 
   if (x_org && y_org)
     {
@@ -8618,8 +8626,11 @@ sfnt_address_zp1 (struct sfnt_interpreter *interpreter,
   if (number >= interpreter->glyph_zone->num_points)
     TRAP ("address to ZP1 (glyph zone) out of bounds");
 
-  *x = interpreter->glyph_zone->x_current[number];
-  *y = interpreter->glyph_zone->y_current[number];
+  if (x && y)
+    {
+      *x = interpreter->glyph_zone->x_current[number];
+      *y = interpreter->glyph_zone->y_current[number];
+    }
 
   if (x_org && y_org)
     {
@@ -8666,8 +8677,11 @@ sfnt_address_zp0 (struct sfnt_interpreter *interpreter,
   if (number >= interpreter->glyph_zone->num_points)
     TRAP ("address to ZP0 (glyph zone) out of bounds");
 
-  *x = interpreter->glyph_zone->x_current[number];
-  *y = interpreter->glyph_zone->y_current[number];
+  if (x && y)
+    {
+      *x = interpreter->glyph_zone->x_current[number];
+      *y = interpreter->glyph_zone->y_current[number];
+    }
 
   if (x_org && y_org)
     {
@@ -10570,6 +10584,7 @@ sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by)
   return (int32_t) (((uint32_t) hi << 18) | (l >> 14));
 #else
   int64_t xx, yy;
+  int64_t temp;
 
   xx = (int64_t) ax * bx;
   yy = (int64_t) ay * by;
@@ -10578,7 +10593,12 @@ sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by)
   yy = xx >> 63;
   xx += 0x2000 + yy;
 
-  return (int32_t) (xx / (1 << 14));
+  /* TrueType fonts rely on "division" here truncating towards
+     negative infinity, so compute the arithmetic right shift in place
+     of division.  */
+  temp = -(xx < 0);
+  temp = (temp ^ xx) >> 14 ^ temp;
+  return (int32_t) (temp);
 #endif
 }
 
@@ -11412,6 +11432,63 @@ sfnt_interpret_mirp (struct sfnt_interpreter *interpreter,
     interpreter->state.rp0 = p;
 }
 
+/* Return the projection of the two points P1 and P2's original values
+   along the dual projection vector, with P1 inside ZP0 and P2 inside
+   ZP1.  If this zone is the glyph zone and the outline positions of
+   those points are directly accessible, project their original
+   positions and scale the result with rounding, so as to prevent
+   rounding-introduced inaccuracies.
+
+   The scenario where such inaccuracies are significant is generally
+   where an Italic glyph is being instructed at small PPEM sizes,
+   during which a point moved by MDAP[rN] is within 1/64th of a
+   pixel's distance from a point on the grid, yet the measurements
+   taken between such a point and the reference point against which
+   the distance to move is computed is such that the position of the
+   point after applying their rounded values differs by one grid
+   coordinate from the font designer's intentions, either exaggerating
+   or neutralizing the slant of the stem to which it belongs.
+
+   This behavior applies only to MDRP, which see.  */
+
+static sfnt_f26dot6
+sfnt_project_zp1_zp0_org (struct sfnt_interpreter *interpreter,
+                         uint32_t p1, uint32_t p2)
+{
+  sfnt_fword x1, y1, x2, y2, projection;
+  struct sfnt_simple_glyph *simple;
+  sfnt_f26dot6 org_x1, org_y1, org_x2, org_y2;
+
+  /* Addressing the twilight zone, perhaps only partially.  */
+  if (!interpreter->state.zp0
+      || !interpreter->state.zp1
+      /* Not interpreting a glyph.  */
+      || !interpreter->glyph_zone
+      /* Not interpreting a simple glyph.  */
+      || !interpreter->glyph_zone->simple
+      /* P1 or P2 are phantom points.  */
+      || p1 >= interpreter->glyph_zone->simple->number_of_points
+      || p2 >= interpreter->glyph_zone->simple->number_of_points)
+    goto project_normally;
+
+  simple = interpreter->glyph_zone->simple;
+  x1 = simple->x_coordinates[p1];
+  y1 = simple->y_coordinates[p1];
+  x2 = simple->x_coordinates[p2];
+  y2 = simple->y_coordinates[p2];
+
+  /* Compute the projection.  */
+  projection = DUAL_PROJECT (x1 - x2, y1 - y2);
+
+  /* Return the projection, scaled with rounding.  */
+  return sfnt_mul_fixed_round (projection, interpreter->scale);
+
+ project_normally:
+  sfnt_address_zp1 (interpreter, p1, NULL, NULL, &org_x1, &org_y1);
+  sfnt_address_zp0 (interpreter, p2, NULL, NULL, &org_x2, &org_y2);
+  return DUAL_PROJECT (org_x1 - org_x2, org_y1 - org_y2);
+}
+
 /* Interpret an MDRP instruction with the specified OPCODE in
    INTERPRETER.  Pop a point in ZP1, and move the point until its
    distance from RP0 in ZP0 is the same as in the original outline.
@@ -11428,20 +11505,19 @@ sfnt_interpret_mdrp (struct sfnt_interpreter *interpreter,
   uint32_t p;
   sfnt_f26dot6 distance, applied;
   sfnt_f26dot6 current_projection;
-  sfnt_f26dot6 x, y, org_x, org_y;
-  sfnt_f26dot6 rx, ry, org_rx, org_ry;
+  sfnt_f26dot6 x, y, rx, ry;
 
   /* Point number.  */
   p = POP ();
 
   /* Load the points.  */
-  sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y);
+  sfnt_address_zp1 (interpreter, p, &x, &y, NULL, NULL);
   sfnt_address_zp0 (interpreter, interpreter->state.rp0,
-                   &rx, &ry, &org_rx, &org_ry);
+                   &rx, &ry, NULL, NULL);
 
   /* Calculate the distance between P and rp0 prior to hinting.  */
-  distance = DUAL_PROJECT (org_x - org_rx,
-                          org_y - org_ry);
+  distance = sfnt_project_zp1_zp0_org (interpreter, p,
+                                      interpreter->state.rp0);
 
   /* Calculate the distance between P and rp0 as of now in the hinting
      process.  */
@@ -12478,6 +12554,7 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph,
   zone->y_current = zone->y_points + zone->num_points;
   zone->flags = (unsigned char *) (zone->y_current
                                   + zone->num_points);
+  zone->simple = glyph->simple;
 
   /* Load x_points and x_current.  */
   for (i = 0; i < glyph->simple->number_of_points; ++i)
@@ -12776,6 +12853,7 @@ sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph,
   zone->y_current = zone->y_points + zone->num_points;
   zone->flags = (unsigned char *) (zone->y_current
                                   + zone->num_points);
+  zone->simple = NULL;
 
   /* Copy and renumber all contour end points to start from
      base_index.  */
@@ -18459,13 +18537,13 @@ static struct sfnt_interpreter_test all_tests[] =
       "SLOOP",
       /* PUSHB[0] 2
         SLOOP[]
-        PUSHB[0] 0
+        PUSHW[0] 255 255 (-1)
         SLOOP[] */
       (unsigned char []) { 0xb0, 2,
                           0x17,
-                          0xb0, 0,
+                          0xb8, 255, 255,
                           0x17, },
-      6,
+      7,
       NULL,
       sfnt_check_sloop,
     },
@@ -20258,7 +20336,8 @@ sfnt_identify_instruction (struct sfnt_interpreter *interpreter)
       return buffer;
     }
 
-  if (exec_fpgm->instructions
+  if (exec_fpgm
+      && exec_fpgm->instructions
       && where >= exec_fpgm->instructions
       && where < (exec_fpgm->instructions
                  + exec_fpgm->num_instructions))
@@ -20529,6 +20608,13 @@ main (int argc, char **argv)
       if (!interpreter)
        abort ();
 
+      if (getenv ("SFNT_VERBOSE"))
+       {
+         interpreter->run_hook = sfnt_verbose;
+         interpreter->push_hook = sfnt_push_hook;
+         interpreter->pop_hook = sfnt_pop_hook;
+       }
+
       for (i = 0; i < ARRAYELTS (all_tests); ++i)
        sfnt_run_interpreter_test (&all_tests[i], interpreter);
 
@@ -20631,8 +20717,8 @@ main (int argc, char **argv)
       return 1;
     }
 
-#define FANCY_PPEM 14
-#define EASY_PPEM  14
+#define FANCY_PPEM 16
+#define EASY_PPEM  16
 
   interpreter = NULL;
   head = sfnt_read_head_table (fd, font);
@@ -21023,6 +21109,16 @@ main (int argc, char **argv)
       interpreter = sfnt_make_interpreter (maxp, cvt, head,
                                           fvar, FANCY_PPEM,
                                           FANCY_PPEM);
+
+      if (getenv ("SFNT_DEBUG"))
+       interpreter->run_hook = sfnt_run_hook;
+      else if (getenv ("SFNT_VERBOSE"))
+       {
+         interpreter->run_hook = sfnt_verbose;
+         interpreter->push_hook = sfnt_push_hook;
+         interpreter->pop_hook = sfnt_pop_hook;
+       }
+
       state = interpreter->state;
 
       if (instance && gvar)
@@ -21236,15 +21332,6 @@ main (int argc, char **argv)
 
                  if (interpreter)
                    {
-                     if (getenv ("SFNT_DEBUG"))
-                       interpreter->run_hook = sfnt_run_hook;
-                     else if (getenv ("SFNT_VERBOSE"))
-                       {
-                         interpreter->run_hook = sfnt_verbose;
-                         interpreter->push_hook = sfnt_push_hook;
-                         interpreter->pop_hook = sfnt_pop_hook;
-                       }
-
                      if (!sfnt_lookup_glyph_metrics (code, &metrics,
                                                      hmtx, hhea, maxp))
                        {
index 2b92f9f540a71875ff73738ded9a15b7ee2e103c..5b01270e8ce67e6b3388278b72f5eca9dfe2255d 100644 (file)
@@ -1759,6 +1759,10 @@ struct sfnt_interpreter_zone
 
   /* Pointer to the flags associated with this data.  */
   unsigned char *flags;
+
+  /* If this structure was produced from a simple glyph, pointer to
+     the simple glyph itself.  NULL otherwise.  */
+  struct sfnt_simple_glyph *simple;
 };
 
 enum