From daec3e7b410cdb8deefbb241d056f8b42dfb40ac Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 17 Jan 2024 09:30:47 +0800 Subject: [PATCH] Increase accuracy of IP instruction * src/sfnt.c (sfnt_interpret_ip): Avoid precision loss by retrieving original positions from the unscaled outline, whenever possible. --- src/sfnt.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/src/sfnt.c b/src/sfnt.c index 2f0153b9a75..ca4c60e8e3a 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -9640,6 +9640,8 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter) sfnt_f26dot6 new_distance; uint32_t p; sfnt_f26dot6 x, y, original_x, original_y; + struct sfnt_interpreter_zone *zone; + bool scale; /* First load both reference points. */ sfnt_address_zp0 (interpreter, interpreter->state.rp1, @@ -9649,6 +9651,57 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter) &rp2x, &rp2y, &rp2_original_x, &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 relative to the dual projection vector. */ range = sfnt_dual_project_vector (interpreter, @@ -9657,6 +9710,9 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter) sfnt_sub (rp2_original_y, rp1_original_y)); + if (scale) + range = sfnt_mul_fixed_round (range, interpreter->scale); + /* Get the new distance. */ new_range = sfnt_dual_project_vector (interpreter, sfnt_sub (rp2x, rp1x), @@ -9670,6 +9726,25 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter) sfnt_address_zp2 (interpreter, p, &x, &y, &original_x, &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. */ org_distance = sfnt_dual_project_vector (interpreter, @@ -9678,6 +9753,10 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter) sfnt_sub (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 how much to move can be determined. */ cur_distance @@ -11447,7 +11526,8 @@ sfnt_interpret_mirp (struct sfnt_interpreter *interpreter, 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. */ + This behavior applies only to MDRP (which see), although a similar + strategy is also applied while interpreting IP instructions. */ static sfnt_f26dot6 sfnt_project_zp1_zp0_org (struct sfnt_interpreter *interpreter, @@ -20715,8 +20795,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 16 -#define EASY_PPEM 16 +#define FANCY_PPEM 14 +#define EASY_PPEM 14 interpreter = NULL; head = sfnt_read_head_table (fd, font); -- 2.39.2