From 7275e32d0b0db412d6a20b3d656ef72ea72d8a44 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 6 Feb 2023 21:19:04 +0800 Subject: [PATCH] Update Android port * src/sfnt.c (struct sfnt_build_glyph_outline_context) (sfnt_build_glyph_outline, sfnt_fill_span): Improve glyph appearance by rounding coordinate values. (struct sfnt_interpreter): New fields `twilight_original_x', `twilight_original_y'. (sfnt_make_interpreter): Set new fields. (DELTAP1, DELTAP2, DELTAP3, SVTCAy, SPVTL, SFVTL, MD): Implement instructions. (sfnt_save_projection_vector): New argument `dual_only'. All callers changed. (sfnt_address_zp2, sfnt_address_zp1, sfnt_address_zp0): Obtain original positions in the twilight zone as well. (sfnt_check_zp1, sfnt_interpret_fliprgoff, sfnt_interpret_fliprgon) (sfnt_interpret_flippt, sfnt_interpret_scfs, sfnt_interpret_miap) (sfnt_interpret_alignrp, sfnt_line_to_vector, P) (sfnt_interpret_msirp, sfnt_interpret_ip, sfnt_interpret_call) (load_point, sfnt_interpret_iup_1, sfnt_interpret_iup) (sfnt_interpret_run, struct sfnt_scaled_outline) (struct sfnt_instructed_outline, sfnt_decompose_instructed_outline) (sfnt_build_instructed_outline, sfnt_compute_phantom_points) (sfnt_interpret_simple_glyph, all_tests, sfnt_setup_debugger) (sfnt_name_instruction, sfnt_draw_debugger, sfnt_run_hook) (sfnt_verbose, main): Make glyph instructing work. * src/sfnt.h (SFNT_POLY_ROUND): New enumerator. --- src/sfnt.c | 1668 +++++++++++++++++++++++++++++++++++++++++++++------- src/sfnt.h | 1 + 2 files changed, 1453 insertions(+), 216 deletions(-) diff --git a/src/sfnt.c b/src/sfnt.c index 6d060fde7df..124185a5073 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -43,6 +43,10 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include +#include +#include + +#include static void * xmalloc (size_t size) @@ -2711,7 +2715,7 @@ sfnt_lerp_half (struct sfnt_point *control1, struct sfnt_point *control2, return whether or not FREE_PROC will be called with the glyph after sfnt_decompose_glyph is done with it. - Both functions will be called with DCONTEXT as an argument. + All functions will be called with DCONTEXT as an argument. The winding rule used to fill the resulting lines is described in chapter 2 of the TrueType reference manual, under the heading @@ -3008,13 +3012,6 @@ struct sfnt_build_glyph_outline_context /* The outline being built. */ struct sfnt_glyph_outline *outline; - /* The head table. */ - struct sfnt_head_table *head; - - /* The pixel size being used, and any extra flags to apply to the - outline at this point. */ - int pixel_size; - /* Factor to multiply positions by to get the pixel width. */ sfnt_fixed factor; @@ -3473,8 +3470,6 @@ sfnt_build_glyph_outline (struct sfnt_glyph *glyph, variables must be used to communicate with the decomposition functions. */ build_outline_context.outline = outline; - build_outline_context.head = head; - build_outline_context.pixel_size = pixel_size; /* Clear outline bounding box. */ outline->xmin = 0; @@ -3860,6 +3855,11 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, if (x1 <= x0) return; + /* Round Y, X0 and X1. */ + y += SFNT_POLY_ROUND; + x0 += SFNT_POLY_ROUND; + x1 += SFNT_POLY_ROUND; + /* Figure out coverage based on Y axis fractional. */ coverage = sfnt_poly_coverage[(y >> (16 - SFNT_POLY_SHIFT)) & SFNT_POLY_MASK]; @@ -5093,6 +5093,16 @@ struct sfnt_interpreter /* The twilight zone. May not be NULL. */ sfnt_f26dot6 *restrict twilight_x, *restrict twilight_y; + /* The original X positions of points in the twilight zone. */ + sfnt_f26dot6 *restrict twilight_original_x; + + /* The original Y positions of points in the twilight zone. + + Apple does not directly say whether or not points in the twilight + zone can have their original positions changed. But this is + implied by ``create points in the twilight zone''. */ + sfnt_f26dot6 *restrict twilight_original_y; + /* The scaled outlines being manipulated. May be NULL. */ struct sfnt_interpreter_zone *glyph_zone; @@ -5133,6 +5143,12 @@ struct sfnt_interpreter /* What was the trap. */ const char *trap_reason; + +#ifdef TEST + /* If non-NULL, function called before each instruction is + executed. */ + void (*run_hook) (struct sfnt_interpreter *); +#endif }; /* Divide the specified two 26.6 fixed point numbers X and Y. @@ -5351,6 +5367,16 @@ sfnt_make_interpreter (struct sfnt_maxp_table *maxp, size, &size)) return NULL; + if (INT_ADD_WRAPV ((maxp->max_twilight_points + * sizeof *interpreter->twilight_y), + size, &size)) + return NULL; + + if (INT_ADD_WRAPV ((maxp->max_twilight_points + * sizeof *interpreter->twilight_y), + size, &size)) + return NULL; + /* Add the storage area. */ storage_size = maxp->max_storage * sizeof *interpreter->storage; if (INT_ADD_WRAPV (storage_size, size, &size)) @@ -5383,6 +5409,10 @@ sfnt_make_interpreter (struct sfnt_maxp_table *maxp, /* Allocate the interpreter. */ interpreter = xmalloc (size); +#ifdef TEST + interpreter->run_hook = NULL; +#endif + /* Fill in pointers and default values. */ interpreter->max_stack_elements = maxp->max_stack_elements; interpreter->num_instructions = 0; @@ -5401,10 +5431,15 @@ sfnt_make_interpreter (struct sfnt_maxp_table *maxp, + maxp->max_stack_elements); interpreter->twilight_y = (interpreter->twilight_x + maxp->max_twilight_points); + interpreter->twilight_original_x = (interpreter->twilight_y + + maxp->max_twilight_points); + interpreter->twilight_original_y + = (interpreter->twilight_original_x + + maxp->max_twilight_points); interpreter->glyph_zone = NULL; interpreter->advance_width = 0; interpreter->storage - = (uint32_t *) (interpreter->twilight_y + = (uint32_t *) (interpreter->twilight_original_y + maxp->max_twilight_points); interpreter->function_defs = (struct sfnt_interpreter_definition *) (interpreter->storage @@ -6537,6 +6572,7 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, argn = POP (); \ pn = POP (); \ sfnt_deltap (1, interpreter, argn, pn); \ + n--; \ goto deltap1_start; \ } @@ -6553,6 +6589,7 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, argn = POP (); \ pn = POP (); \ sfnt_deltap (2, interpreter, argn, pn); \ + n--; \ goto deltap2_start; \ } @@ -6569,6 +6606,7 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, argn = POP (); \ pn = POP (); \ sfnt_deltap (3, interpreter, argn, pn); \ + n--; \ goto deltap3_start; \ } @@ -6599,41 +6637,41 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, #define SVTCAy() \ { \ sfnt_set_freedom_vector (interpreter, \ - 040000, 0); \ + 0, 040000); \ sfnt_set_projection_vector (interpreter, \ - 040000, 0); \ + 0, 040000); \ } #define SVTCAx() \ { \ - sfnt_set_freedom_vector (interpreter, 0, \ - 040000); \ - sfnt_set_projection_vector (interpreter, 0, \ - 040000); \ + sfnt_set_freedom_vector (interpreter, \ + 040000, 0); \ + sfnt_set_projection_vector (interpreter, \ + 040000, 0); \ } #define SPvTCAy() \ { \ sfnt_set_projection_vector (interpreter, \ - 040000, 0); \ + 0, 040000); \ } #define SPvTCAx() \ { \ - sfnt_set_projection_vector (interpreter, 0, \ - 040000); \ + sfnt_set_projection_vector (interpreter, \ + 040000, 0); \ } #define SFvTCAy() \ { \ sfnt_set_freedom_vector (interpreter, \ - 040000, 0); \ + 0, 040000); \ } #define SFvTCAx() \ { \ - sfnt_set_freedom_vector (interpreter, 0, \ - 040000); \ + sfnt_set_freedom_vector (interpreter, \ + 040000, 0); \ } #define SPVTL() \ @@ -6646,10 +6684,12 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, \ sfnt_line_to_vector (interpreter, \ p2, p1, &vector, \ - opcode == 0x07); \ + opcode == 0x07, \ + false); \ \ - sfnt_save_projection_vector (interpreter, \ - &vector); \ + sfnt_save_projection_vector (interpreter, \ + &vector, \ + false); \ } #define SFVTL() \ @@ -6662,7 +6702,8 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, \ sfnt_line_to_vector (interpreter, \ p2, p1, &vector, \ - opcode == 0x09); \ + opcode == 0x09, \ + false); \ \ sfnt_save_freedom_vector (interpreter, \ &vector); \ @@ -6892,6 +6933,70 @@ sfnt_interpret_trap (struct sfnt_interpreter *interpreter, PUSH (distance); \ } +#define FLIPPT() \ + { \ + sfnt_interpret_flippt (interpreter); \ + } + +#define FLIPRGOFF() \ + { \ + uint32_t h, l; \ + \ + h = POP (); \ + l = POP (); \ + \ + sfnt_interpret_fliprgoff (interpreter, \ + h, l); \ + } + +#define FLIPRGON() \ + { \ + uint32_t h, l; \ + \ + h = POP (); \ + l = POP (); \ + \ + sfnt_interpret_fliprgon (interpreter, \ + h, l); \ + } + +#define SDPVTL() \ + { \ + struct sfnt_unit_vector vector; \ + uint32_t p2, p1; \ + \ + p2 = POP (); \ + p1 = POP (); \ + \ + sfnt_line_to_vector (interpreter, \ + p2, p1, &vector, \ + opcode == 0x87, \ + false); \ + \ + sfnt_save_projection_vector (interpreter, \ + &vector, \ + false); \ + \ + sfnt_line_to_vector (interpreter, \ + p2, p1, &vector, \ + opcode == 0x87, \ + true); \ + \ + sfnt_save_projection_vector (interpreter, \ + &vector, \ + true); \ + } + +#define MIRP() \ + { \ + sfnt_interpret_mirp (interpreter, opcode); \ + } + +#define MDRP() \ + { \ + sfnt_interpret_mdrp (interpreter, opcode); \ + } + #define NOT_IMPLEMENTED() \ @@ -6942,13 +7047,20 @@ sfnt_interpret_utp (struct sfnt_interpreter *interpreter, } /* Save the specified unit VECTOR into INTERPRETER's graphics state as - both the projection and the dual projection vectors. */ + both the projection and the dual projection vectors. + + If not DUAL_ONLY, set VECTOR as both the projection and dual + projection vectors. Otherwise, only set VECTOR as the dual + projection vector. */ static void sfnt_save_projection_vector (struct sfnt_interpreter *interpreter, - struct sfnt_unit_vector *vector) + struct sfnt_unit_vector *vector, + bool dual_only) { - interpreter->state.projection_vector = *vector; + if (!dual_only) + interpreter->state.projection_vector = *vector; + interpreter->state.dual_projection_vector = *vector; sfnt_validate_gs (&interpreter->state); @@ -6992,9 +7104,10 @@ sfnt_address_zp2 (struct sfnt_interpreter *interpreter, if (!x_org || !y_org) return; - /* The twilight zone is initially all zero. */ - *x_org = 0; - *y_org = 0; + /* The twilight zone is initially all zero, but initial + positions can still be changed. */ + *x_org = interpreter->twilight_original_x[number]; + *y_org = interpreter->twilight_original_y[number]; return; } @@ -7039,9 +7152,10 @@ sfnt_address_zp1 (struct sfnt_interpreter *interpreter, if (!x_org || !y_org) return; - /* The twilight zone is initially all zero. */ - *x_org = 0; - *y_org = 0; + /* The twilight zone is initially all zero, but initial + positions can still be changed. */ + *x_org = interpreter->twilight_original_x[number]; + *y_org = interpreter->twilight_original_y[number]; return; } @@ -7086,9 +7200,10 @@ sfnt_address_zp0 (struct sfnt_interpreter *interpreter, if (!x_org || !y_org) return; - /* The twilight zone is initially all zero. */ - *x_org = 0; - *y_org = 0; + /* The twilight zone is initially all zero, but initial + positions can still be changed. */ + *x_org = interpreter->twilight_original_x[number]; + *y_org = interpreter->twilight_original_y[number]; return; } @@ -7205,6 +7320,22 @@ sfnt_check_zp0 (struct sfnt_interpreter *interpreter, uint32_t point) TRAP ("point lies outside glyph zone (ZP0)"); } +/* Check that the specified POINT lies within the zone addressed by + INTERPRETER's ZP1 register. Trap if it does not. */ + +static void +sfnt_check_zp1 (struct sfnt_interpreter *interpreter, uint32_t point) +{ + if (!interpreter->state.zp1) + { + if (point >= interpreter->twilight_zone_size) + TRAP ("point lies outside twilight zone (ZP0)"); + } + else if (!interpreter->glyph_zone + || point >= interpreter->glyph_zone->num_points) + TRAP ("point lies outside glyph zone (ZP0)"); +} + /* Move N points starting from the specified POINT in the zone addressed by INTERPRETER's ZP0 register by the given DISTANCE along the freedom vector. @@ -7356,9 +7487,84 @@ sfnt_dual_project_vector (struct sfnt_interpreter *interpreter, return interpreter->state.dual_project (vx, vy, interpreter); } +/* Interpret a FLIPRGOFF instruction in INTERPRTER. Make each point + in ZP0 between L and H an off-curve point. */ + +static void +sfnt_interpret_fliprgoff (struct sfnt_interpreter *interpreter, + uint32_t l, uint32_t h) +{ + uint32_t i; + + sfnt_check_zp0 (interpreter, l); + sfnt_check_zp0 (interpreter, h); + + if (!interpreter->state.zp0) + return; + + for (i = l; i < h; ++i) + interpreter->glyph_zone->flags[i] &= ~01; +} + +/* Interpret a FLIPRGON instruction in INTERPRTER. Make each point in + ZP0 between L and H an on-curve point. */ + +static void +sfnt_interpret_fliprgon (struct sfnt_interpreter *interpreter, + uint32_t l, uint32_t h) +{ + uint32_t i; + + sfnt_check_zp0 (interpreter, l); + sfnt_check_zp0 (interpreter, h); + + if (!interpreter->state.zp0) + return; + + for (i = l; i < h; ++i) + interpreter->glyph_zone->flags[i] |= ~01; +} + +/* Interpret a FLIPPT instruction in INTERPRETER. For loop times, pop + a point in ZP0. If it is an on-curve point, make it an off-curve + one, and vice versa. */ + +static void +sfnt_interpret_flippt (struct sfnt_interpreter *interpreter) +{ + uint32_t point; + + while (interpreter->state.loop--) + { + point = POP (); + + /* There are no flags in the twilight zone. + But first check that the point is within bounds. */ + + sfnt_check_zp0 (interpreter, point); + + if (!interpreter->state.zp0) + continue; + + /* If POINT is on the curve, make it off the curve and vice + versa. */ + + if (interpreter->glyph_zone->flags[point] & 01) + interpreter->glyph_zone->flags[point] &= ~01; + else + interpreter->glyph_zone->flags[point] |= 01; + } + + /* Restore loop. */ + interpreter->state.loop = 1; +} + /* Interpret an SCFS instruction. Move P in ZP2 along the freedom vector until its projection is - equal to C. */ + equal to C. + + If ZP2 is the twilight zone, ``create'' P by setting its original + position to the projection. */ static void sfnt_interpret_scfs (struct sfnt_interpreter *interpreter, @@ -7369,6 +7575,14 @@ sfnt_interpret_scfs (struct sfnt_interpreter *interpreter, sfnt_address_zp2 (interpreter, p, &x, &y, NULL, NULL); distance = PROJECT (x, y); sfnt_move_zp2 (interpreter, p, 1, sfnt_sub (c, distance)); + + if (!interpreter->state.zp2) + { + interpreter->twilight_original_x[p] + = interpreter->twilight_x[p]; + interpreter->twilight_original_y[p] + = interpreter->twilight_y[p]; + } } /* Symmetrically round the 26.6 fixed point value X using the rounding @@ -7398,6 +7612,10 @@ sfnt_round_symmetric (struct sfnt_interpreter *interpreter, sfnt_f26dot6 x) Finally, set RP0 and RP1 to P. + If ZP0 is the twilight zone, then first create that point in the + twilight zone by setting its ``original position'' to the + projection of the value. + If OPCODE is 0x3f, then in addition check the CVT value against the control value cut-in, and round the magnitudes of the movement. */ @@ -7407,8 +7625,6 @@ sfnt_interpret_miap (struct sfnt_interpreter *interpreter, { sfnt_f26dot6 x, y, distance, value, delta; - sfnt_address_zp0 (interpreter, p, &x, &y, NULL, NULL); - /* Read the cvt value. */ if (cvt >= interpreter->cvt_size) @@ -7416,6 +7632,25 @@ sfnt_interpret_miap (struct sfnt_interpreter *interpreter, value = interpreter->cvt[cvt]; + /* Now load the point. */ + sfnt_address_zp0 (interpreter, p, &x, &y, NULL, NULL); + + /* Create the twilight zone point if necessary. + Note that the value used is not rounded. */ + + if (!interpreter->state.zp0) + { + x = interpreter->twilight_x[p] + = interpreter->twilight_original_x[p] + = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x, + value); + + y = interpreter->twilight_y[p] + = interpreter->twilight_original_y[p] + = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y, + value); + } + /* Obtain the original distance. */ distance = sfnt_project_vector (interpreter, x, y); @@ -7482,7 +7717,14 @@ sfnt_interpret_alignrp (struct sfnt_interpreter *interpreter) &rp0x, &rp0y, NULL, NULL); while (interpreter->state.loop--) - sfnt_interpret_alignrp_1 (interpreter, rp0x, rp0y); + { + sfnt_interpret_alignrp_1 (interpreter, rp0x, rp0y); + + /* Reload RP0 if it is in the same zone as ZP1. */ + if (interpreter->state.zp0 == interpreter->state.zp1) + sfnt_address_zp0 (interpreter, interpreter->state.rp0, + &rp0x, &rp0y, NULL, NULL); + } interpreter->state.loop = 1; } @@ -7748,20 +7990,26 @@ sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy, specified INTERPRETER's graphics state. If PERPENDICULAR, then *VECTOR will be rotated 90 degrees - counter-clockwise. Else, *VECTOR will be parallel to the line. */ + counter-clockwise. Else, *VECTOR will be parallel to the line. + + If ORIGINAL, then the coordinates used to calculate the line will + be those prior to instructing. Otherwise, the current coordinates + will be used. */ static void sfnt_line_to_vector (struct sfnt_interpreter *interpreter, uint32_t p2, uint32_t p1, struct sfnt_unit_vector *vector, - bool perpendicular) + bool perpendicular, bool original) { - sfnt_f26dot6 x2, y2; - sfnt_f26dot6 x1, y1; + sfnt_f26dot6 x2, y2, original_x2, original_y2; + sfnt_f26dot6 x1, y1, original_x1, original_y1; sfnt_f26dot6 a, b, temp; - sfnt_address_zp2 (interpreter, p2, &x2, &y2, NULL, NULL); - sfnt_address_zp1 (interpreter, p1, &x1, &y1, NULL, NULL); + sfnt_address_zp2 (interpreter, p2, &x2, &y2, &original_x2, + &original_y2); + sfnt_address_zp1 (interpreter, p1, &x1, &y1, &original_x1, + &original_y1); /* Calculate the vector between X2, Y2, and X1, Y1. */ a = sfnt_sub (x1, x2); @@ -7831,20 +8079,42 @@ sfnt_measure_distance (struct sfnt_interpreter *interpreter, Take a point P, and make the distance between P in ZP1 and the current position of RP0 in ZP0 equal to D. + If ZP1 is the twilight zone, then create the point P by setting its + position and relative positions. + Then, if OPCODE is equal to 0x3b, make P RP0. */ static void sfnt_interpret_msirp (struct sfnt_interpreter *interpreter, uint32_t p, sfnt_f26dot6 d, unsigned char opcode) { - sfnt_f26dot6 rp0x, rp0y; + sfnt_f26dot6 rp0x, rp0y, rp0_original_x, rp0_original_y; sfnt_f26dot6 x, y; - sfnt_f26dot6 old_distance; + sfnt_f26dot6 old_distance, temp; sfnt_address_zp0 (interpreter, interpreter->state.rp0, - &rp0x, &rp0y, NULL, NULL); + &rp0x, &rp0y, &rp0_original_x, + &rp0_original_y); sfnt_address_zp1 (interpreter, p, &x, &y, NULL, NULL); + if (!interpreter->state.zp1) + { + /* Create this point in the twilight zone at RP0. */ + + x = interpreter->twilight_x[p] = rp0x; + y = interpreter->twilight_y[p] = rp0y; + + /* Now set the original positions to the projected difference + from rp0. This makes sense once you think about it. */ + temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x, d); + temp = sfnt_add (temp, rp0_original_x); + interpreter->twilight_original_x[p] = temp; + + temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y, d); + temp = sfnt_add (temp, rp0_original_y); + interpreter->twilight_original_y[p] = temp; + } + /* Compute the original distance. */ old_distance = sfnt_project_vector (interpreter, sfnt_sub (x, rp0x), @@ -7892,14 +8162,14 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter) relative to the dual projection vector. */ range = sfnt_dual_project_vector (interpreter, sfnt_sub (rp2_original_x, - rp2_original_y), - sfnt_sub (rp1_original_x, + rp1_original_x), + sfnt_sub (rp2_original_y, rp1_original_y)); /* Get the new distance. */ new_range = sfnt_dual_project_vector (interpreter, - sfnt_sub (rp2x, rp2y), - sfnt_sub (rp1x, rp1y)); + sfnt_sub (rp2x, rp1x), + sfnt_sub (rp2y, rp1y)); while (interpreter->state.loop--) { @@ -7920,9 +8190,9 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter) /* And the current distance from this point to rp1, so how much to move can be determined. */ cur_distance - = sfnt_dual_project_vector (interpreter, - sfnt_sub (x, rp1x), - sfnt_sub (y, rp1y)); + = sfnt_project_vector (interpreter, + sfnt_sub (x, rp1x), + sfnt_sub (y, rp1y)); /* Finally, apply the ratio of the new distance between RP1 and RP2 to that of the old distance between the two reference @@ -7934,8 +8204,9 @@ sfnt_interpret_ip (struct sfnt_interpreter *interpreter) if (org_distance) { if (range) - new_distance = sfnt_multiply_divide (org_distance, new_range, - range); + new_distance + = sfnt_multiply_divide_signed (org_distance, + new_range, range); else new_distance = org_distance; } @@ -8263,8 +8534,8 @@ sfnt_interpret_call (struct sfnt_interpreter_definition *definition, unsigned char *instructions; /* Check that no recursion is going on. */ - if (interpreter->call_depth++ >= 64) - TRAP ("CALL called CALL more than 63 times"); + if (interpreter->call_depth++ >= 128) + TRAP ("CALL called CALL more than 127 times"); /* Save the old IP, instructions and number of instructions. */ num_instructions = interpreter->num_instructions; @@ -8274,7 +8545,6 @@ sfnt_interpret_call (struct sfnt_interpreter_definition *definition, /* Load and run the definition. */ interpreter->num_instructions = definition->instruction_count; interpreter->instructions = definition->instructions; - interpreter->glyph_zone = NULL; interpreter->IP = 0; sfnt_interpret_run (interpreter, context); @@ -8282,6 +8552,7 @@ sfnt_interpret_call (struct sfnt_interpreter_definition *definition, interpreter->num_instructions = num_instructions; interpreter->IP = IP; interpreter->instructions = instructions; + interpreter->call_depth--; } /* Set the detailed rounding state in interpreter, on behalf of either @@ -9217,6 +9488,93 @@ sfnt_interpret_shp (struct sfnt_interpreter *interpreter, interpreter->state.loop = 1; } +#define load_point(p) \ + (opcode == 0x31 \ + ? interpreter->glyph_zone->x_current[p] \ + : interpreter->glyph_zone->y_current[p]) + +#define store_point(p, val) \ + (opcode == 0x31 \ + ? (interpreter->glyph_zone->x_current[p] = (val)) \ + : (interpreter->glyph_zone->y_current[p] = (val))) + +#define load_original(p) \ + (opcode == 0x31 \ + ? interpreter->glyph_zone->x_points[p] \ + : interpreter->glyph_zone->y_points[p]) + +#define IUP_SINGLE_PAIR() \ + /* Now make touch_start the first point before, i.e. the first \ + touched point in this pair. */ \ + \ + if (touch_start == start) \ + touch_start = end; \ + else \ + touch_start = touch_start - 1; \ + \ + /* Set point_min and point_max based on which glyph is at a \ + lower value. */ \ + \ + if (load_original (touch_start) < load_original (touch_end)) \ + { \ + point_min = touch_start; \ + point_max = touch_end; \ + } \ + else \ + { \ + point_max = touch_start; \ + point_min = touch_end; \ + } \ + \ + min_pos = load_point (point_min); \ + max_pos = load_point (point_max); \ + \ + /* This is needed for interpolation. */ \ + original_max_pos = load_original (point_max); \ + original_min_pos = load_original (point_min); \ + \ + /* Now process points between touch_start and touch_end. */ \ + \ + i = touch_start + 1; \ + \ + for (; i <= end; ++i) \ + { \ + if (i > end) \ + i = start; \ + \ + /* Movement is always relative to the original position of \ + the point. */ \ + position = load_original (i); \ + \ + /* If i is in between touch_start and touch_end... */ \ + if (position >= original_min_pos \ + && position <= original_max_pos \ + && original_min_pos != original_max_pos) \ + { \ + /* ... preserve the ratio of i between min_pos and \ + max_pos... */ \ + ratio = sfnt_div_fixed (sfnt_sub (position, \ + original_min_pos) * 1024, \ + sfnt_sub (original_max_pos, \ + original_min_pos) * 1024); \ + delta = sfnt_sub (max_pos, min_pos); \ + delta = sfnt_mul_fixed (ratio, delta); \ + store_point (i, sfnt_add (min_pos, delta)); \ + } \ + else \ + { \ + /* ... otherwise, move i by how much the nearest touched \ + point moved. */ \ + \ + if (position >= original_max_pos) \ + delta = sfnt_sub (max_pos, original_max_pos); \ + else \ + delta = sfnt_sub (min_pos, original_min_pos); \ + \ + store_point (i, sfnt_add (position, delta)); \ + } \ + } \ + /* Interpolate untouched points in the contour between and including START and END inside INTERPRETER's glyph zone according to the rules specified for an IUP instruction. Perform interpolation on @@ -9231,25 +9589,10 @@ sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter, size_t touch_start, touch_end; size_t first_point; size_t point_min, point_max, i; - sfnt_f26dot6 position, min_pos, max_pos, delta; + sfnt_f26dot6 position, min_pos, max_pos, delta, ratio; sfnt_f26dot6 original_max_pos; sfnt_f26dot6 original_min_pos; -#define load_point(p) \ - (opcode == 0x31 \ - ? interpreter->glyph_zone->x_current[p] \ - : interpreter->glyph_zone->x_current[p]) - -#define store_point(p, val) \ - (opcode == 0x31 \ - ? (interpreter->glyph_zone->x_current[p] = (val)) \ - : (interpreter->glyph_zone->y_current[p] = (val))) - -#define load_original(p) \ - (opcode == 0x31 \ - ? interpreter->glyph_zone->x_points[p] \ - : interpreter->glyph_zone->y_points[p]) - /* Find the first touched point. If none is found, simply return. */ @@ -9265,25 +9608,27 @@ sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter, point = start; - while (true) + /* Find the first touched point. */ + while (!(interpreter->glyph_zone->flags[point] & mask)) { - /* first_point says when to stop looking for a closing - point. */ - first_point = point; + point++; + + /* There are no touched points. */ + if (point > end) + goto untouched; + } + first_point = point; + + while (point <= end) + { /* Find the next untouched point. */ while (interpreter->glyph_zone->flags[point] & mask) { point++; - /* Move back to start if point has gone past the end of the - contour. */ if (point > end) - point = start; - - if (point == first_point) - /* There are no more untouched points. */ - goto untouched; + goto wraparound; } /* touch_start is now the first untouched point. */ @@ -9297,94 +9642,31 @@ sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter, /* Move back to start if point has gone past the end of the contour. */ if (point > end) - point = start; - - if (point == touch_start) - /* There are no more touched points. */ - goto untouched; + goto wraparound_1; } /* touch_end is now the next touched point. */ touch_end = point; - /* Now make touch_start the first point before, i.e. the first - touched point in this pair. */ - - if (touch_start == start) - touch_start = end; - else - touch_start = touch_start - 1; - - /* Set point_min and point_max based on which glyph is at a - lower value. */ - - if (load_original (touch_start) < touch_end) - { - point_min = touch_start; - point_max = touch_end; - } - else - { - point_max = touch_start; - point_min = touch_end; - } - - min_pos = load_point (point_min); - max_pos = load_point (point_max); - - /* This is needed for interpolation. */ - original_max_pos = load_original (max_pos); - original_min_pos = load_original (min_pos); - - /* Now process points between touch_start and touch_end. */ - - i = touch_start; - - do - { - ++i; - - if (i == end) - i = start; - - /* Movement is always relative to the original position of - the point. */ - position = load_original (i); + /* Do the interpolation. */ + IUP_SINGLE_PAIR (); + } - /* If i is in between touch_start and touch_end... */ - if (position >= original_min_pos - && position <= original_max_pos) - { - /* ... linearly interpolate i between min_pos and - max_pos... */ - delta = sfnt_sub (max_pos, min_pos) / 2; - store_point (i, sfnt_add (max_pos, delta)); - } - else - { - /* ... otherwise, move i by how much the nearest touched - point moved. */ + goto untouched; - if (position >= original_max_pos) - delta = sfnt_sub (max_pos, original_max_pos); - else - delta = sfnt_sub (min_pos, original_min_pos); + wraparound: + /* This is like wraparound_1, except that no untouched points have + yet to be found. - store_point (i, position + delta); - } - } - while (i != touch_end); + This means the first untouched point is start. */ + touch_start = start; - /* Handle the degenerate case where the first and last points of - the entire contour are touched, and as a result the loop - continues forever. */ - if (touch_start == touch_end) - goto untouched; - } + wraparound_1: + /* If point > end, wrap around. Here, touch_start is set + properly, so touch_end must be first_point. */ -#undef load_point -#undef store_point -#undef load_original + touch_end = first_point; + IUP_SINGLE_PAIR (); untouched: /* No points were touched or all points have been considered, so @@ -9392,6 +9674,10 @@ sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter, return; } +#undef load_point +#undef store_point +#undef load_original + /* Interpret an IUP (``interpolate untouched points'') instruction. INTERPRETER is the interpreter, and OPCODE is the instruction number. See the TrueType Reference Manual for more details. */ @@ -9412,7 +9698,7 @@ sfnt_interpret_iup (struct sfnt_interpreter *interpreter, TRAP ("iup without loaded glyph!"); /* Figure out what axis to interpolate in based on the opcode. */ - if (opcode == 0x31) + if (opcode == 0x30) mask = SFNT_POINT_TOUCHED_Y; else mask = SFNT_POINT_TOUCHED_X; @@ -9435,20 +9721,247 @@ sfnt_interpret_iup (struct sfnt_interpreter *interpreter, } } -/* Execute the program now loaded into INTERPRETER. - WHY specifies why the interpreter is being run, and is used to - control the behavior of instructions such IDEF[] and FDEF[]. - - Transfer control to INTERPRETER->trap if interpretation is aborted - due to an error, and set INTERPRETER->trap_reason to a string - describing the error. +/* Interpret an MIRP instruction with the specified OPCODE in + INTERPRETER. Pop a point in ZP1 and CVT index, and move the point + until its distance from RP0 in ZP0 is the same as in the control + value. If the point lies in the twilight zone, then ``create'' it + as well. - INTERPRETER->glyph_zone should be cleared before calling this - function. */ + OPCODE contains a great many flags. + They are all described in the TrueType reference manual. */ static void -sfnt_interpret_run (struct sfnt_interpreter *interpreter, - enum sfnt_interpreter_run_context why) +sfnt_interpret_mirp (struct sfnt_interpreter *interpreter, + uint32_t opcode) +{ + uint32_t n; + uint32_t p; + sfnt_f26dot6 distance, delta, temp; + sfnt_f26dot6 current_projection, original_projection; + sfnt_f26dot6 x, y, org_x, org_y; + sfnt_f26dot6 rx, ry, org_rx, org_ry; + + /* CVT index. */ + n = POP (); + + /* Point number. */ + p = POP (); + + /* Now get the distance from the CVT. */ + if (n >= interpreter->cvt_size) + TRAP ("cvt index out of bounds"); + + distance = interpreter->cvt[n]; + + /* Test against the single width value. */ + + delta = sfnt_sub (distance, + interpreter->state.single_width_value); + + if (delta < 0) + delta = -delta; + + if (delta < interpreter->state.sw_cut_in) + { + /* Use the single width instead, as the CVT entry is too + small. */ + + if (distance >= 0) + distance = interpreter->state.single_width_value; + else + distance = -interpreter->state.single_width_value; + } + + /* Load the reference point. */ + sfnt_address_zp0 (interpreter, interpreter->state.rp0, + &rx, &ry, &org_rx, &org_ry); + + /* Create the point in the twilight zone, should that be ZP1. */ + + if (!interpreter->state.zp1) + { + /* Since P hasn't been loaded yet, whether or not it is valid is + not known. */ + sfnt_check_zp1 (interpreter, p); + + interpreter->twilight_x[p] = rx; + interpreter->twilight_y[p] = ry; + + temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x, + distance); + temp = sfnt_add (temp, org_rx); + interpreter->twilight_original_x[p] = temp; + + temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y, + distance); + temp = sfnt_add (temp, org_ry); + interpreter->twilight_original_y[p] = temp; + } + + /* Load P. */ + sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y); + + /* If distance would be negative and auto_flip is on, flip it. */ + + original_projection = DUAL_PROJECT (org_x - org_rx, + org_y - org_ry); + current_projection = PROJECT (x - rx, y - ry); + + if (interpreter->state.auto_flip) + { + if ((original_projection ^ distance) < 0) + distance = -distance; + } + + /* Flag B means look at the cvt cut in and round the + distance. */ + + if (opcode & 4) + { + delta = sfnt_sub (distance, original_projection); + + if (delta < 0) + delta = -delta; + + if (delta > interpreter->state.cvt_cut_in) + distance = original_projection; + + /* Now, round the distance. */ + distance = sfnt_round_symmetric (interpreter, distance); + } + + /* Flag C means look at the minimum distance. */ + + if (opcode & 8) + { + if (original_projection >= 0 + && distance < interpreter->state.minimum_distance) + distance = interpreter->state.minimum_distance; + else if (original_projection < 0 + && distance > -interpreter->state.minimum_distance) + distance = -interpreter->state.minimum_distance; + } + + /* Finally, move the point. */ + sfnt_move_zp1 (interpreter, p, 1, + sfnt_sub (distance, current_projection)); + + /* Set RP1 to RP0 and RP2 to the point. If flag 3 is set, also make + it RP0. */ + interpreter->state.rp1 = interpreter->state.rp0; + interpreter->state.rp2 = p; + + if (opcode & 16) + interpreter->state.rp0 = p; +} + +/* 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. + + This is almost like MIRP[abcde]. + + OPCODE contains a great many flags. + They are all described in the TrueType reference manual. */ + +static void +sfnt_interpret_mdrp (struct sfnt_interpreter *interpreter, + uint32_t opcode) +{ + uint32_t p; + sfnt_f26dot6 distance, delta; + sfnt_f26dot6 current_projection, original_projection; + sfnt_f26dot6 x, y, org_x, org_y; + sfnt_f26dot6 rx, ry, org_rx, org_ry; + + /* Point number. */ + p = POP (); + + /* Load the points. */ + sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y); + sfnt_address_zp0 (interpreter, interpreter->state.rp0, + &rx, &ry, &org_rx, &org_ry); + + distance = DUAL_PROJECT (org_x - org_rx, + org_y - org_ry); + original_projection = distance; + current_projection = PROJECT (x - rx, y - ry); + + /* Test against the single width value. */ + + delta = sfnt_sub (distance, + interpreter->state.single_width_value); + + if (delta < 0) + delta = -delta; + + if (delta < interpreter->state.sw_cut_in) + { + /* Use the single width instead, as the CVT entry is too + small. */ + + if (distance >= 0) + distance = interpreter->state.single_width_value; + else + distance = -interpreter->state.single_width_value; + } + + /* Flag B means look at the cvt cut in and round the + distance. */ + + if (opcode & 4) + { + delta = sfnt_sub (distance, original_projection); + + if (delta < 0) + delta = -delta; + + if (delta > interpreter->state.cvt_cut_in) + distance = original_projection; + + /* Now, round the distance. */ + distance = sfnt_round_symmetric (interpreter, distance); + } + + /* Flag C means look at the minimum distance. */ + + if (opcode & 8) + { + if (original_projection >= 0 + && distance < interpreter->state.minimum_distance) + distance = interpreter->state.minimum_distance; + else if (original_projection < 0 + && distance > -interpreter->state.minimum_distance) + distance = -interpreter->state.minimum_distance; + } + + /* Finally, move the point. */ + sfnt_move_zp1 (interpreter, p, 1, + sfnt_sub (distance, current_projection)); + + /* Set RP1 to RP0 and RP2 to the point. If flag 3 is set, also make + it RP0. */ + interpreter->state.rp1 = interpreter->state.rp0; + interpreter->state.rp2 = p; + + if (opcode & 16) + interpreter->state.rp0 = p; +} + +/* Execute the program now loaded into INTERPRETER. + WHY specifies why the interpreter is being run, and is used to + control the behavior of instructions such IDEF[] and FDEF[]. + + Transfer control to INTERPRETER->trap if interpretation is aborted + due to an error, and set INTERPRETER->trap_reason to a string + describing the error. + + INTERPRETER->glyph_zone should be cleared before calling this + function. */ + +static void +sfnt_interpret_run (struct sfnt_interpreter *interpreter, + enum sfnt_interpreter_run_context why) { unsigned char opcode; bool is_prep; @@ -9466,6 +9979,11 @@ sfnt_interpret_run (struct sfnt_interpreter *interpreter, { opcode = interpreter->instructions[interpreter->IP]; +#ifdef TEST + if (interpreter->run_hook) + interpreter->run_hook (interpreter); +#endif + switch (opcode) { case 0x00: /* SVTCA y */ @@ -9930,19 +10448,19 @@ sfnt_interpret_run (struct sfnt_interpreter *interpreter, break; case 0x80: /* FLIPPT */ - NOT_IMPLEMENTED (); + FLIPPT (); break; case 0x81: /* FLIPRGON */ - NOT_IMPLEMENTED (); + FLIPRGON (); break; case 0x82: /* FLIPRGOFF */ - NOT_IMPLEMENTED (); + FLIPRGOFF (); break; - case 0x83: /* UNKNOWN */ - case 0x84: /* UNKNOWN */ + case 0x83: /* RMVT */ + case 0x84: /* WMVT */ NOT_IMPLEMENTED (); break; @@ -9950,9 +10468,9 @@ sfnt_interpret_run (struct sfnt_interpreter *interpreter, SCANCTRL (); break; - case 0x86: /* SDPvTL */ - case 0x87: /* SDPvTL */ - NOT_IMPLEMENTED (); + case 0x86: /* SDPVTL */ + case 0x87: /* SDPVTL */ + SDPVTL (); break; case 0x88: /* GETINFO */ @@ -9993,9 +10511,13 @@ sfnt_interpret_run (struct sfnt_interpreter *interpreter, default: if (opcode >= 0xE0) /* MIRP */ - NOT_IMPLEMENTED (); + { + MIRP (); + } else if (opcode >= 0xC0) /* MDRP */ - NOT_IMPLEMENTED (); + { + MDRP (); + } else if (opcode >= 0xB8) /* PUSHW */ { PUSHW (); @@ -10094,7 +10616,7 @@ sfnt_interpret_control_value_program (struct sfnt_interpreter *interpreter, /* Structure describing a single scaled and fitted outline. */ -struct sfnt_scaled_outline +struct sfnt_instructed_outline { /* The number of points in this contour, including the two phantom points at the end. */ @@ -10109,10 +10631,217 @@ struct sfnt_scaled_outline /* The points of each contour, with two additional phantom points at the end. */ sfnt_f26dot6 *restrict x_points, *restrict y_points; + + /* The flags of each point. */ + unsigned char *flags; }; +/* Instructed glyph outline decomposition. This is separate from + sfnt_decompose_glyph because this file should be able to be built + with instructions disabled. */ + +/* Decompose OUTLINE, an instructed outline, into its individual + components. + + Call MOVE_TO to move to a specific location. For each line + encountered, call LINE_TO to draw a line to that location. For + each spline encountered, call CURVE_TO to draw the curves + comprising the spline. Call each of those functions with 16.16 + fixed point coordinates. + + All functions will be called with DCONTEXT as an argument. + + The winding rule used to fill the resulting lines is described in + chapter 2 of the TrueType reference manual, under the heading + "distinguishing the inside from the outside of a glyph." + + Value is 0 upon success, or some non-zero value upon failure, which + can happen if the glyph is invalid. */ + +static int +sfnt_decompose_instructed_outline (struct sfnt_instructed_outline *outline, + sfnt_move_to_proc move_to, + sfnt_line_to_proc line_to, + sfnt_curve_to_proc curve_to, + void *dcontext) +{ + size_t here, start, last; + struct sfnt_point pen, control1, control2; + size_t n; + + if (!outline->num_contours) + return 0; + + here = 0; + + for (n = 0; n < outline->num_contours; ++n) + { + /* here is the first index into the glyph's point arrays + belonging to the contour in question. last is the index + of the last point in the contour. */ + last = outline->contour_end_points[n]; + + /* Move to the start. */ + pen.x = outline->x_points[here] * 1024; + pen.y = outline->y_points[here] * 1024; + move_to (pen, dcontext); + + /* Record start so the contour can be closed. */ + start = here; + + /* If there is only one point in a contour, draw a one pixel + wide line. */ + if (last == here) + { + line_to (pen, dcontext); + here++; + + continue; + } + + if (here > last) + /* Indices moved backwards. */ + goto fail; + + /* Now start reading points. If the next point is on the + curve, then it is actually a line. */ + for (++here; here <= last; ++here) + { + /* Make sure here is within bounds. */ + if (here >= outline->num_points) + return 1; + + if (outline->flags[here] & 01) /* On Curve */ + { + pen.x = outline->x_points[here] * 1024; + pen.y = outline->y_points[here] * 1024; + + /* See if the last point was on the curve. If it + wasn't, then curve from there to here. */ + if (!(outline->flags[here - 1] & 01)) + { + control1.x = outline->x_points[here - 1] * 1024; + control1.y = outline->y_points[here - 1] * 1024; + curve_to (control1, pen, dcontext); + } + else + /* Otherwise, this is an ordinary line from there + to here. */ + line_to (pen, dcontext); + + continue; + } + + /* If the last point was on the curve, then there's + nothing extraordinary to do yet. */ + if (outline->flags[here - 1] & 01) + ; + else + { + /* Otherwise, interpolate the point halfway between + the last and current points and make that point + the pen. */ + control1.x = outline->x_points[here - 1] * 1024; + control1.y = outline->y_points[here - 1] * 1024; + control2.x = outline->x_points[here] * 1024; + control2.y = outline->y_points[here] * 1024; + sfnt_lerp_half (&control1, &control2, &pen); + curve_to (control1, pen, dcontext); + } + } + + /* Now close the contour if there is more than one point + inside it. */ + if (start != here - 1) + { + /* Restore here after the for loop increased it. */ + here --; + + if (outline->flags[start] & 01) /* On Curve */ + { + pen.x = outline->x_points[start] * 1024; + pen.y = outline->y_points[start] * 1024; + + /* See if the last point (in this case, `here') was + on the curve. If it wasn't, then curve from + there to here. */ + if (!(outline->flags[here] & 01)) + { + control1.x = outline->x_points[here] * 1024; + control1.y = outline->y_points[here] * 1024; + curve_to (control1, pen, dcontext); + } + else + /* Otherwise, this is an ordinary line from there + to here. */ + line_to (pen, dcontext); + } + + /* Restore here to where it was earlier. */ + here++; + } + } + + return 0; + + fail: + return 1; +} + +/* Decompose and build an outline for the specified instructed outline + INSTRUCTED. Return the outline data with a refcount of 0 upon + success, or NULL upon failure. + + This function is not reentrant. */ + +static struct sfnt_glyph_outline * +sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed) +{ + struct sfnt_glyph_outline *outline; + int rc; + + /* Allocate the outline now with enough for 44 words at the end. */ + outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline)); + outline->outline_size = 40; + outline->outline_used = 0; + outline->refcount = 0; + outline->outline + = (struct sfnt_glyph_outline_command *) (outline + 1); + + /* Clear outline bounding box. */ + outline->xmin = 0; + outline->ymin = 0; + outline->xmax = 0; + outline->ymax = 0; + + /* Set up the context. */ + build_outline_context.outline = outline; + build_outline_context.factor = 0177777; + + /* Start decomposing. */ + rc = sfnt_decompose_instructed_outline (instructed, + sfnt_move_to_and_build, + sfnt_line_to_and_build, + sfnt_curve_to_and_build, + NULL); + + /* Synchronize the outline object with what might have changed + inside sfnt_decompose_glyph. */ + outline = build_outline_context.outline; + + if (rc) + { + xfree (outline); + return NULL; + } + + return outline; +} + + + /* Compute phantom points for the specified glyph GLYPH. Use the unscaled metrics specified in METRICS, and the 16.16 fixed point scale SCALE. @@ -10134,7 +10863,7 @@ sfnt_compute_phantom_points (struct sfnt_glyph *glyph, represents the left-side bearing distance from xmin, while the other represents the advance width. Both are then used after the hinting process to ensure that the reported glyph metrics are - consistent with the hinted outline. */ + consistent with the instructed outline. */ /* First compute both values in fwords. */ f1 = glyph->xmin - metrics->lbearing; @@ -10160,33 +10889,28 @@ static const char * sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, struct sfnt_interpreter *interpreter, struct sfnt_glyph_metrics *metrics, - struct sfnt_scaled_outline **value) + struct sfnt_instructed_outline **value) { size_t zone_size, temp, outline_size, i; - struct sfnt_interpreter_zone *zone; + struct sfnt_interpreter_zone *volatile zone; sfnt_f26dot6 phantom_point_1_x; sfnt_f26dot6 phantom_point_1_y; sfnt_f26dot6 phantom_point_2_x; sfnt_f26dot6 phantom_point_2_y; sfnt_f26dot6 tem; - bool zone_was_allocated; - struct sfnt_scaled_outline *outline; + volatile bool zone_was_allocated; + struct sfnt_instructed_outline *outline; zone_size = 0; zone_was_allocated = false; - /* Calculate the size of the zone structure. - - This should include four longs for each point (including two - phantom points at the end), and then one size_t and one char for - each point, once again including the phantom points, and finally - the size of the zone structure. */ + /* Calculate the size of the zone structure. */ if (INT_MULTIPLY_WRAPV (glyph->simple->number_of_points + 2, sizeof *zone->x_points * 4, &temp) || INT_ADD_WRAPV (temp, zone_size, &zone_size) - || INT_MULTIPLY_WRAPV (glyph->simple->number_of_points + 2, + || INT_MULTIPLY_WRAPV (glyph->number_of_contours, sizeof *zone->contour_end_points, &temp) || INT_ADD_WRAPV (temp, zone_size, &zone_size) @@ -10194,7 +10918,7 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, sizeof *zone->flags, &temp) || INT_ADD_WRAPV (temp, zone_size, &zone_size) - || INT_ADD_WRAPV (sizeof (*zone), zone_size, &zone_size)) + || INT_ADD_WRAPV (sizeof *zone, zone_size, &zone_size)) return "Glyph exceeded maximum permissible size"; /* Don't use malloc if possible. */ @@ -10210,9 +10934,9 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, /* Now load the zone with data. */ zone->num_points = glyph->simple->number_of_points + 2; zone->num_contours = glyph->number_of_contours; - zone->contour_end_points = (size_t *) (glyph + 1); + zone->contour_end_points = (size_t *) (zone + 1); zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points - + zone->num_points); + + zone->num_contours); zone->x_current = zone->x_points + zone->num_points; zone->y_points = zone->x_current + zone->num_points; zone->y_current = zone->y_points + zone->num_points; @@ -10268,6 +10992,10 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, zone->y_current[i] = phantom_point_1_x; zone->y_current[i + 1] = phantom_point_2_x; + /* Load phantom point flags. */ + zone->flags[i] = 0; + zone->flags[i + 1] = 0; + /* Load contour end points. */ for (i = 0; i < zone->num_contours; ++i) zone->contour_end_points[i] @@ -10298,6 +11026,7 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, * sizeof *outline->contour_end_points); outline_size += (zone->num_points * sizeof *outline->x_points * 2); + outline_size += zone->num_points; /* Allocate the outline. */ outline = xmalloc (outline_size); @@ -10307,14 +11036,17 @@ sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph, outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points + outline->num_contours); outline->y_points = outline->x_points + outline->num_points; + outline->flags = (unsigned char *) (outline->y_points + + outline->num_points); - /* Copy over the contour endpoints and points. */ + /* Copy over the contour endpoints, points, and flags. */ memcpy (outline->contour_end_points, zone->contour_end_points, zone->num_contours * sizeof *outline->contour_end_points); memcpy (outline->x_points, zone->x_current, zone->num_points * sizeof *outline->x_points); memcpy (outline->y_points, zone->y_current, zone->num_points * sizeof *outline->y_points); + memcpy (outline->flags, zone->flags, zone->num_points); /* Free the zone if necessary. */ if (zone_was_allocated) @@ -13171,18 +13903,18 @@ static struct sfnt_interpreter_test all_tests[] = }, { "IDEF", - /* PUSHB[0] 247 + /* PUSHB[0] 0x83 IDEF[] PUSHB[3] 1 2 3 4 POP[] ENDF[] - 247 */ - (unsigned char []) { 0xb0, 247, + 0x83 */ + (unsigned char []) { 0xb0, 0x83, 0x89, 0xb3, 1, 2, 3, 4, 0x21, 0x2d, - 247, }, + 0x83, }, 11, &idef_test_args, sfnt_generic_check, @@ -13259,6 +13991,464 @@ static struct sfnt_interpreter_test all_tests[] = +/* Instruction debugger. */ + +/* The debugger's X display. */ +static Display *display; + +/* The debugger window. */ +static Window window; + +/* The GC. */ +static GC point_gc, background_gc; + +static void +sfnt_setup_debugger (void) +{ + XGCValues gcv; + Font font; + + display = XOpenDisplay (NULL); + + if (!display) + exit (1); + + window = XCreateSimpleWindow (display, DefaultRootWindow (display), + 0, 0, 200, 200, 0, 0, + WhitePixel (display, + DefaultScreen (display))); + XMapWindow (display, window); + + /* Select for the appropriate events. */ + XSelectInput (display, window, KeyPressMask | ExposureMask); + + /* Find an appropriate font. */ + font = XLoadFont (display, "6x13"); + + if (!font) + exit (1); + + /* The debugger has been set up. Set up the GCs for drawing points + and backgrounds. */ + + gcv.foreground = BlackPixel (display, DefaultScreen (display)); + gcv.font = font; + point_gc = XCreateGC (display, window, GCForeground | GCFont, + &gcv); + gcv.foreground = WhitePixel (display, DefaultScreen (display)); + background_gc = XCreateGC (display, window, GCForeground, &gcv); +} + +static const char * +sfnt_name_instruction (unsigned char opcode) +{ + static const char *const opcode_names[256] = { + "7 SVTCA y", + "7 SVTCA x", + "8 SPvTCA y", + "8 SPvTCA x", + "8 SFvTCA y", + "8 SFvTCA x", + "8 SPvTL ||", + "7 SPvTL +", + "8 SFvTL ||", + "7 SFvTL +", + "5 SPvFS", + "5 SFvFS", + "3 GPv", + "3 GFv", + "6 SFvTPv", + "5 ISECT", + + "4 SRP0", + "4 SRP1", + "4 SRP2", + "4 SZP0", + "4 SZP1", + "4 SZP2", + "4 SZPS", + "5 SLOOP", + "3 RTG", + "4 RTHG", + "3 SMD", + "4 ELSE", + "4 JMPR", + "6 SCvTCi", + "5 SSwCi", + "3 SSW", + + "3 DUP", + "3 POP", + "5 CLEAR", + "4 SWAP", + "5 DEPTH", + "6 CINDEX", + "6 MINDEX", + "8 AlignPTS", + "7 INS_$28", + "3 UTP", + "8 LOOPCALL", + "4 CALL", + "4 FDEF", + "4 ENDF", + "7 MDAP[0]", + "7 MDAP[1]", + + "6 IUP[0]", + "6 IUP[1]", + "6 SHP[0]", + "6 SHP[1]", + "6 SHC[0]", + "6 SHC[1]", + "6 SHZ[0]", + "6 SHZ[1]", + "5 SHPIX", + "2 IP", + "8 MSIRP[0]", + "8 MSIRP[1]", + "7 AlignRP", + "4 RTDG", + "7 MIAP[0]", + "7 MIAP[1]", + + "6 NPushB", + "6 NPushW", + "2 WS", + "2 RS", + "5 WCvtP", + "4 RCvt", + "5 GC[0]", + "5 GC[1]", + "4 SCFS", + "5 MD[0]", + "5 MD[1]", + "5 MPPEM", + "3 MPS", + "6 FlipON", + "7 FlipOFF", + "5 DEBUG", + + "2 LT", + "4 LTEQ", + "2 GT", + "4 GTEQ", + "2 EQ", + "3 NEQ", + "3 ODD", + "4 EVEN", + "2 IF", + "3 EIF", + "3 AND", + "2 OR", + "3 NOT", + "7 DeltaP1", + "3 SDB", + "3 SDS", + + "3 ADD", + "3 SUB", + "3 DIV", + "3 MUL", + "3 ABS", + "3 NEG", + "5 FLOOR", + "7 CEILING", + "8 ROUND[0]", + "8 ROUND[1]", + "8 ROUND[2]", + "8 ROUND[3]", + "9 NROUND[0]", + "9 NROUND[1]", + "9 NROUND[2]", + "9 NROUND[3]", + + "5 WCvtF", + "7 DeltaP2", + "7 DeltaP3", + "A DeltaCn[0]", + "A DeltaCn[1]", + "A DeltaCn[2]", + "6 SROUND", + "8 S45Round", + "4 JROT", + "4 JROF", + "4 ROFF", + "7 INS_$7B", + "4 RUTG", + "4 RDTG", + "5 SANGW", + "2 AA", + + "6 FlipPT", + "8 FlipRgON", + "9 FlipRgOFF", + "7 INS_$83", + "7 INS_$84", + "8 ScanCTRL", + "9 SDPvTL[0]", + "9 SDPvTL[1]", + "7 GetINFO", + "4 IDEF", + "4 ROLL", + "3 MAX", + "3 MIN", + "8 ScanTYPE", + "8 InstCTRL", + "7 INS_$8F", + + "7 INS_$90", +#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT + "6 GETVAR", + "7 GETDATA", +#else + "7 INS_$91", + "7 INS_$92", +#endif + "7 INS_$93", + "7 INS_$94", + "7 INS_$95", + "7 INS_$96", + "7 INS_$97", + "7 INS_$98", + "7 INS_$99", + "7 INS_$9A", + "7 INS_$9B", + "7 INS_$9C", + "7 INS_$9D", + "7 INS_$9E", + "7 INS_$9F", + + "7 INS_$A0", + "7 INS_$A1", + "7 INS_$A2", + "7 INS_$A3", + "7 INS_$A4", + "7 INS_$A5", + "7 INS_$A6", + "7 INS_$A7", + "7 INS_$A8", + "7 INS_$A9", + "7 INS_$AA", + "7 INS_$AB", + "7 INS_$AC", + "7 INS_$AD", + "7 INS_$AE", + "7 INS_$AF", + + "8 PushB[0]", + "8 PushB[1]", + "8 PushB[2]", + "8 PushB[3]", + "8 PushB[4]", + "8 PushB[5]", + "8 PushB[6]", + "8 PushB[7]", + "8 PushW[0]", + "8 PushW[1]", + "8 PushW[2]", + "8 PushW[3]", + "8 PushW[4]", + "8 PushW[5]", + "8 PushW[6]", + "8 PushW[7]", + + "7 MDRP[G]", + "7 MDRP[B]", + "7 MDRP[W]", + "7 MDRP[?]", + "8 MDRP[rG]", + "8 MDRP[rB]", + "8 MDRP[rW]", + "8 MDRP[r?]", + "8 MDRP[mG]", + "8 MDRP[mB]", + "8 MDRP[mW]", + "8 MDRP[m?]", + "9 MDRP[mrG]", + "9 MDRP[mrB]", + "9 MDRP[mrW]", + "9 MDRP[mr?]", + + "8 MDRP[pG]", + "8 MDRP[pB]", + "8 MDRP[pW]", + "8 MDRP[p?]", + "9 MDRP[prG]", + "9 MDRP[prB]", + "9 MDRP[prW]", + "9 MDRP[pr?]", + "9 MDRP[pmG]", + "9 MDRP[pmB]", + "9 MDRP[pmW]", + "9 MDRP[pm?]", + "A MDRP[pmrG]", + "A MDRP[pmrB]", + "A MDRP[pmrW]", + "A MDRP[pmr?]", + + "7 MIRP[G]", + "7 MIRP[B]", + "7 MIRP[W]", + "7 MIRP[?]", + "8 MIRP[rG]", + "8 MIRP[rB]", + "8 MIRP[rW]", + "8 MIRP[r?]", + "8 MIRP[mG]", + "8 MIRP[mB]", + "8 MIRP[mW]", + "8 MIRP[m?]", + "9 MIRP[mrG]", + "9 MIRP[mrB]", + "9 MIRP[mrW]", + "9 MIRP[mr?]", + + "8 MIRP[pG]", + "8 MIRP[pB]", + "8 MIRP[pW]", + "8 MIRP[p?]", + "9 MIRP[prG]", + "9 MIRP[prB]", + "9 MIRP[prW]", + "9 MIRP[pr?]", + "9 MIRP[pmG]", + "9 MIRP[pmB]", + "9 MIRP[pmW]", + "9 MIRP[pm?]", + "A MIRP[pmrG]", + "A MIRP[pmrB]", + "A MIRP[pmrW]", + "A MIRP[pmr?]" + }; + + return opcode_names[opcode]; +} + +static void +sfnt_draw_debugger (struct sfnt_interpreter *interpreter) +{ + int x, y, i; + char buffer[80]; + const char *name; + int opcode; + + sprintf (buffer, "opcode:IP:depth: 0x%x:%d:%d", + interpreter->instructions[interpreter->IP], + interpreter->IP, + interpreter->call_depth); + + /* Clear the window. */ + XFillRectangle (display, window, background_gc, + 0, 0, 65535, 65535); + + /* Draw some information about the opcode. */ + XDrawString (display, window, point_gc, 0, 13, buffer, + strlen (buffer)); + + opcode = interpreter->instructions[interpreter->IP]; + + sprintf (buffer, "opcode: %s", + sfnt_name_instruction (opcode)); + + XDrawString (display, window, point_gc, 14, 27, buffer, + strlen (buffer)); + + if (interpreter->state.project + == sfnt_project_onto_x_axis_vector) + name = "X axis"; + else if (interpreter->state.project + == sfnt_project_onto_y_axis_vector) + name = "Y axis"; + else + name = "Any"; + + sprintf (buffer, "projection function: %s", name); + + XDrawString (display, window, point_gc, 28, 42, buffer, + strlen (buffer)); + + /* Draw each point onto the window. */ + for (i = 0; i < interpreter->glyph_zone->num_points; ++i) + { + x = interpreter->glyph_zone->x_current[i] / 16; + y = (200 - interpreter->glyph_zone->y_current[i] / 16); + + XFillRectangle (display, window, point_gc, x, y, 4, 4); + } +} + +static void +sfnt_run_hook (struct sfnt_interpreter *interpreter) +{ + pid_t pid; + XEvent event; + + pid = fork (); + + if (pid == 0) + { + sfnt_setup_debugger (); + + while (true) + { + XNextEvent (display, &event); + + switch (event.type) + { + case KeyPress: + XDestroyWindow (display, window); + XCloseDisplay (display); + exit (0); + break; + + case Expose: + sfnt_draw_debugger (interpreter); + break; + } + } + } + else + { + while (waitpid (pid, NULL, 0) != pid && errno == EINTR) + /* Spin. */; + } +} + +static void +sfnt_verbose (struct sfnt_interpreter *interpreter) +{ + struct sfnt_instructed_outline temp; + struct sfnt_glyph_outline *outline; + struct sfnt_raster *raster; + unsigned char opcode; + + /* Build a temporary outline containing the values of the + interpreter's glyph zone. */ + temp.num_points = interpreter->glyph_zone->num_points; + temp.num_contours = interpreter->glyph_zone->num_contours; + temp.contour_end_points = interpreter->glyph_zone->contour_end_points; + temp.x_points = interpreter->glyph_zone->x_current; + temp.y_points = interpreter->glyph_zone->y_current; + temp.flags = interpreter->glyph_zone->flags; + outline = sfnt_build_instructed_outline (&temp); + + if (!outline) + return; + + raster = sfnt_raster_glyph_outline (outline); + + if (raster) + sfnt_test_raster (raster); + + xfree (outline); + xfree (raster); + + opcode = interpreter->instructions[interpreter->IP]; + printf ("opcode: %s\n", sfnt_name_instruction (opcode)); +} + + + /* Main entry point. */ /* Simple tests that were used while developing this file. By the @@ -13283,7 +14473,7 @@ static struct sfnt_interpreter_test all_tests[] = -Wno-missing-field-initializers -Wno-override-init -Wno-sign-compare -Wno-type-limits -Wno-unused-parameter -Wno-format-nonliteral -Wno-bidi-chars -g3 -O0 -DTEST sfnt.c -o - sfnt ../lib/libgnu.a + sfnt ../lib/libgnu.a -lX11 after gnulib has been built. Then, run ./sfnt /path/to/font.ttf. */ @@ -13322,6 +14512,7 @@ main (int argc, char **argv) const char *trap; struct sfnt_prep_table *prep; struct sfnt_graphics_state state; + struct sfnt_instructed_outline *value; if (argc != 2) return 1; @@ -13439,7 +14630,8 @@ main (int argc, char **argv) cvt ? cvt->num_elements : 0ul); interpreter = sfnt_make_interpreter (maxp, cvt, head, - 17, 17); + 12, 12); + state = interpreter->state; if (fpgm) { @@ -13614,7 +14806,7 @@ main (int argc, char **argv) /* Time this important bit. */ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); outline = sfnt_build_glyph_outline (glyph, head, - 50, + 12, sfnt_test_get_glyph, sfnt_test_free_glyph, &dcontext); @@ -13681,13 +14873,57 @@ main (int argc, char **argv) if (hmtx && head) { - if (!sfnt_lookup_glyph_metrics (code, 50, + if (!sfnt_lookup_glyph_metrics (code, 12, &metrics, hmtx, hhea, head, maxp)) printf ("lbearing, advance: %g, %g\n", sfnt_coerce_fixed (metrics.lbearing), sfnt_coerce_fixed (metrics.advance)); + + if (interpreter) + { + if (getenv ("SFNT_DEBUG")) + interpreter->run_hook = sfnt_run_hook; + else if (getenv ("SFNT_VERBOSE")) + interpreter->run_hook = sfnt_verbose; + + if (glyph->simple + && sfnt_lookup_glyph_metrics (code, -1, + &metrics, + hmtx, hhea, + head, maxp)) + { + printf ("interpreting glyph\n"); + interpreter->state = state; + trap = sfnt_interpret_simple_glyph (glyph, interpreter, + &metrics, &value); + + if (trap) + printf ("**TRAP**: %s\n", trap); + else + { + printf ("rasterizing instructed outline\n"); + if (outline) + xfree (outline); + outline = sfnt_build_instructed_outline (value); + xfree (value); + + if (outline) + { + raster = sfnt_raster_glyph_outline (outline); + + if (raster) + { + sfnt_test_raster (raster); + xfree (raster); + } + } + } + } + + interpreter->run_hook = NULL; + } } printf ("time spent outlining: %lld sec %ld nsec\n", diff --git a/src/sfnt.h b/src/sfnt.h index ad8ba810dbb..baed1d257dd 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -731,6 +731,7 @@ enum SFNT_POLY_MASK = (SFNT_POLY_SAMPLE - 1), SFNT_POLY_STEP = (0x10000 >> SFNT_POLY_SHIFT), SFNT_POLY_START = (SFNT_POLY_STEP >> 1), + SFNT_POLY_ROUND = ((1 << (16 - SFNT_POLY_SHIFT)) / 2) - 1, }; -- 2.39.5