From: Po Lu Date: Sat, 20 Jan 2024 01:30:04 +0000 (+0800) Subject: Round projs computed executing IP/ISECT instructions and improve IUP X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=f21be79431f84cf6820ddf92d2bb878ac8223a08;p=emacs.git Round projs computed executing IP/ISECT instructions and improve IUP * 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) --- diff --git a/src/sfnt.c b/src/sfnt.c index ca4c60e8e3a..88826e1b2c1 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -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