Increase accuracy of IP instruction

* src/sfnt.c (sfnt_interpret_ip): Avoid precision loss by
retrieving original positions from the unscaled outline,
whenever possible.
This commit is contained in:
Po Lu 2024-01-17 09:30:47 +08:00
parent f19f5604de
commit daec3e7b41

View file

@ -9640,6 +9640,8 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter)
sfnt_f26dot6 new_distance; sfnt_f26dot6 new_distance;
uint32_t p; uint32_t p;
sfnt_f26dot6 x, y, original_x, original_y; sfnt_f26dot6 x, y, original_x, original_y;
struct sfnt_interpreter_zone *zone;
bool scale;
/* First load both reference points. */ /* First load both reference points. */
sfnt_address_zp0 (interpreter, interpreter->state.rp1, sfnt_address_zp0 (interpreter, interpreter->state.rp1,
@ -9649,6 +9651,57 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter)
&rp2x, &rp2y, &rp2_original_x, &rp2x, &rp2y, &rp2_original_x,
&rp2_original_y); &rp2_original_y);
/* If RP1, RP2, and all arguments all fall within the glyph zone and
a simple glyph is loaded, replace their original coordinates as
loaded here with coordinates from the unscaled glyph outline. */
zone = interpreter->glyph_zone;
scale = false;
if (zone && zone->simple
&& interpreter->state.zp0
&& interpreter->state.zp1
&& interpreter->state.zp2)
{
p = interpreter->state.rp1;
/* If P is a phantom point... */
if (p >= zone->simple->number_of_points)
{
/* ...scale the phantom point to the size of the original
outline. */
rp1_original_x = sfnt_div_fixed (rp1_original_x,
interpreter->scale);
rp1_original_y = sfnt_div_fixed (rp1_original_y,
interpreter->scale);
}
else
{
rp1_original_x = zone->simple->x_coordinates[p];
rp1_original_y = zone->simple->y_coordinates[p];
}
p = interpreter->state.rp2;
/* If P is a phantom point... */
if (p >= zone->simple->number_of_points)
{
/* ...scale the phantom point to the size of the original
outline. */
rp2_original_x = sfnt_div_fixed (rp2_original_x,
interpreter->scale);
rp2_original_y = sfnt_div_fixed (rp2_original_y,
interpreter->scale);
}
else
{
rp2_original_x = zone->simple->x_coordinates[p];
rp2_original_y = zone->simple->y_coordinates[p];
}
scale = true;
}
/* Get the original distance between of RP1 and RP2 measured /* Get the original distance between of RP1 and RP2 measured
relative to the dual projection vector. */ relative to the dual projection vector. */
range = sfnt_dual_project_vector (interpreter, range = sfnt_dual_project_vector (interpreter,
@ -9657,6 +9710,9 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter)
sfnt_sub (rp2_original_y, sfnt_sub (rp2_original_y,
rp1_original_y)); rp1_original_y));
if (scale)
range = sfnt_mul_fixed_round (range, interpreter->scale);
/* Get the new distance. */ /* Get the new distance. */
new_range = sfnt_dual_project_vector (interpreter, new_range = sfnt_dual_project_vector (interpreter,
sfnt_sub (rp2x, rp1x), sfnt_sub (rp2x, rp1x),
@ -9670,6 +9726,25 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter)
sfnt_address_zp2 (interpreter, p, &x, &y, &original_x, sfnt_address_zp2 (interpreter, p, &x, &y, &original_x,
&original_y); &original_y);
if (scale)
{
/* If P is a phantom point... */
if (p >= zone->simple->number_of_points)
{
/* ...scale the phantom point to the size of the original
outline. */
original_x = sfnt_div_fixed (original_x,
interpreter->scale);
original_y = sfnt_div_fixed (original_y,
interpreter->scale);
}
else
{
original_x = zone->simple->x_coordinates[p];
original_y = zone->simple->y_coordinates[p];
}
}
/* Now compute the old distance from this point to rp1. */ /* Now compute the old distance from this point to rp1. */
org_distance org_distance
= sfnt_dual_project_vector (interpreter, = sfnt_dual_project_vector (interpreter,
@ -9678,6 +9753,10 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter)
sfnt_sub (original_y, sfnt_sub (original_y,
rp1_original_y)); rp1_original_y));
if (scale)
org_distance = sfnt_mul_fixed_round (org_distance,
interpreter->scale);
/* And the current distance from this point to rp1, so /* And the current distance from this point to rp1, so
how much to move can be determined. */ how much to move can be determined. */
cur_distance cur_distance
@ -11447,7 +11526,8 @@ sfnt_interpret_mirp (struct sfnt_interpreter *interpreter,
coordinate from the font designer's intentions, either exaggerating coordinate from the font designer's intentions, either exaggerating
or neutralizing the slant of the stem to which it belongs. or neutralizing the slant of the stem to which it belongs.
This behavior applies only to MDRP, which see. */ This behavior applies only to MDRP (which see), although a similar
strategy is also applied while interpreting IP instructions. */
static sfnt_f26dot6 static sfnt_f26dot6
sfnt_project_zp1_zp0_org (struct sfnt_interpreter *interpreter, sfnt_project_zp1_zp0_org (struct sfnt_interpreter *interpreter,
@ -20715,8 +20795,8 @@ main (int argc, char **argv)
return 1; return 1;
} }
#define FANCY_PPEM 16 #define FANCY_PPEM 14
#define EASY_PPEM 16 #define EASY_PPEM 14
interpreter = NULL; interpreter = NULL;
head = sfnt_read_head_table (fd, font); head = sfnt_read_head_table (fd, font);