text:
#
# FreeType 2 auto-fitter module configuration rules
#

# Copyright (C) 2003-2020 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
# and distributed under the terms of the FreeType project license,
# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
# indicate that you have read the license and understand and accept it
# fully.

# AUTOF driver directory
#
AUTOF_DIR := $(SRC_DIR)/autofit

# compilation flags for the driver
#
AUTOF_COMPILE := $(CC) $(ANSIFLAGS)                              \
                       $I$(subst /,$(COMPILER_SEP),$(AUTOF_DIR)) \
                       $(INCLUDE_FLAGS)                          \
                       $(FT_CFLAGS)

# AUTOF driver sources (i.e., C files)
#
AUTOF_DRV_SRC := $(AUTOF_DIR)/afangles.c \
                 $(AUTOF_DIR)/afblue.c   \
                 $(AUTOF_DIR)/afcjk.c    \
                 $(AUTOF_DIR)/afdummy.c  \
                 $(AUTOF_DIR)/afglobal.c \
                 $(AUTOF_DIR)/afhints.c  \
                 $(AUTOF_DIR)/afindic.c  \
                 $(AUTOF_DIR)/aflatin.c  \
                 $(AUTOF_DIR)/afloader.c \
                 $(AUTOF_DIR)/afmodule.c \
                 $(AUTOF_DIR)/afranges.c \
                 $(AUTOF_DIR)/afshaper.c \
                 $(AUTOF_DIR)/afwarp.c

# AUTOF driver headers
#
AUTOF_DRV_H := $(AUTOF_DRV_SRC:%c=%h)  \
               $(AUTOF_DIR)/afcover.h  \
               $(AUTOF_DIR)/aferrors.h \
               $(AUTOF_DIR)/afscript.h \
               $(AUTOF_DIR)/afstyles.h \
               $(AUTOF_DIR)/aftypes.h  \
               $(AUTOF_DIR)/afwrtsys.h

# AUTOF driver object(s)
#
#   AUTOF_DRV_OBJ_M is used during `multi' builds.
#   AUTOF_DRV_OBJ_S is used during `single' builds.
#
AUTOF_DRV_OBJ_M := $(AUTOF_DRV_SRC:$(AUTOF_DIR)/%.c=$(OBJ_DIR)/%.$O)
AUTOF_DRV_OBJ_S := $(OBJ_DIR)/autofit.$O

# AUTOF driver source file for single build
#
AUTOF_DRV_SRC_S := $(AUTOF_DIR)/autofit.c

# AUTOF driver - single object
#
$(AUTOF_DRV_OBJ_S): $(AUTOF_DRV_SRC_S) $(AUTOF_DRV_SRC) \
                   $(FREETYPE_H) $(AUTOF_DRV_H)
	$(AUTOF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(AUTOF_DRV_SRC_S))

# AUTOF driver - multiple objects
#
$(OBJ_DIR)/%.$O: $(AUTOF_DIR)/%.c $(FREETYPE_H) $(AUTOF_DRV_H)
	$(AUTOF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)

# update main driver object lists
#
DRV_OBJS_S += $(AUTOF_DRV_OBJ_S)
DRV_OBJS_M += $(AUTOF_DRV_OBJ_M)

# EOF


text:
/****************************************************************************
 *
 * afangles.c
 *
 *   Routines used to compute vector angles with limited accuracy
 *   and very high speed.  It also contains sorting routines (body).
 *
 * Copyright (C) 2003-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#include "aftypes.h"

  /*
   * We are not using `af_angle_atan' anymore, but we keep the source
   * code below just in case...
   */

#if 0

  /*
   * The trick here is to realize that we don't need a very accurate angle
   * approximation.  We are going to use the result of `af_angle_atan' to
   * only compare the sign of angle differences, or check whether its
   * magnitude is very small.
   *
   * The approximation
   *
   *   dy * PI / (|dx|+|dy|)
   *
   * should be enough, and much faster to compute.
   */
  FT_LOCAL_DEF( AF_Angle )
  af_angle_atan( FT_Fixed  dx,
                 FT_Fixed  dy )
  {
    AF_Angle  angle;
    FT_Fixed  ax = dx;
    FT_Fixed  ay = dy;

    if ( ax < 0 )
      ax = -ax;
    if ( ay < 0 )
      ay = -ay;

    ax += ay;

    if ( ax == 0 )
      angle = 0;
    else
    {
      angle = ( AF_ANGLE_PI2 * dy ) / ( ax + ay );
      if ( dx < 0 )
      {
        if ( angle >= 0 )
          angle = AF_ANGLE_PI - angle;
        else
          angle = -AF_ANGLE_PI - angle;
      }
    }

    return angle;
  }

#elif 0

  /* the following table has been automatically generated with */
  /* the `mather.py' Python script                             */

#define AF_ATAN_BITS  8

  static const FT_Byte  af_arctan[1L << AF_ATAN_BITS] =
  {
     0,  0,  1,  1,  1,  2,  2,  2,
     3,  3,  3,  3,  4,  4,  4,  5,
     5,  5,  6,  6,  6,  7,  7,  7,
     8,  8,  8,  9,  9,  9, 10, 10,
    10, 10, 11, 11, 11, 12, 12, 12,
    13, 13, 13, 14, 14, 14, 14, 15,
    15, 15, 16, 16, 16, 17, 17, 17,
    18, 18, 18, 18, 19, 19, 19, 20,
    20, 20, 21, 21, 21, 21, 22, 22,
    22, 23, 23, 23, 24, 24, 24, 24,
    25, 25, 25, 26, 26, 26, 26, 27,
    27, 27, 28, 28, 28, 28, 29, 29,
    29, 30, 30, 30, 30, 31, 31, 31,
    31, 32, 32, 32, 33, 33, 33, 33,
    34, 34, 34, 34, 35, 35, 35, 35,
    36, 36, 36, 36, 37, 37, 37, 38,
    38, 38, 38, 39, 39, 39, 39, 40,
    40, 40, 40, 41, 41, 41, 41, 42,
    42, 42, 42, 42, 43, 43, 43, 43,
    44, 44, 44, 44, 45, 45, 45, 45,
    46, 46, 46, 46, 46, 47, 47, 47,
    47, 48, 48, 48, 48, 48, 49, 49,
    49, 49, 50, 50, 50, 50, 50, 51,
    51, 51, 51, 51, 52, 52, 52, 52,
    52, 53, 53, 53, 53, 53, 54, 54,
    54, 54, 54, 55, 55, 55, 55, 55,
    56, 56, 56, 56, 56, 57, 57, 57,
    57, 57, 57, 58, 58, 58, 58, 58,
    59, 59, 59, 59, 59, 59, 60, 60,
    60, 60, 60, 61, 61, 61, 61, 61,
    61, 62, 62, 62, 62, 62, 62, 63,
    63, 63, 63, 63, 63, 64, 64, 64
  };

  FT_LOCAL_DEF( AF_Angle )
  af_angle_atan( FT_Fixed  dx,
                 FT_Fixed  dy )
  {
    AF_Angle  angle;

    /* check trivial cases */
    if ( dy == 0 )
    {
      angle = 0;
      if ( dx < 0 )
        angle = AF_ANGLE_PI;
      return angle;
    }
    else if ( dx == 0 )
    {
      angle = AF_ANGLE_PI2;
      if ( dy < 0 )
        angle = -AF_ANGLE_PI2;
      return angle;
    }

    angle = 0;
    if ( dx < 0 )
    {
      dx = -dx;
      dy = -dy;
      angle = AF_ANGLE_PI;
    }

    if ( dy < 0 )
    {
      FT_Pos  tmp;

      tmp = dx;
      dx  = -dy;
      dy  = tmp;
      angle -= AF_ANGLE_PI2;
    }

    if ( dx == 0 && dy == 0 )
      return 0;

    if ( dx == dy )
      angle += AF_ANGLE_PI4;
    else if ( dx > dy )
      angle += af_arctan[FT_DivFix( dy, dx ) >> ( 16 - AF_ATAN_BITS )];
    else
      angle += AF_ANGLE_PI2 -
               af_arctan[FT_DivFix( dx, dy ) >> ( 16 - AF_ATAN_BITS )];

    if ( angle > AF_ANGLE_PI )
      angle -= AF_ANGLE_2PI;

    return angle;
  }

#endif /* 0 */

  FT_LOCAL_DEF( void )
  af_sort_pos( FT_UInt  count,
               FT_Pos*  table )
  {
    FT_UInt  i, j;
    FT_Pos   swap;

    for ( i = 1; i < count; i++ )
    {
      for ( j = i; j > 0; j-- )
      {
        if ( table[j] >= table[j - 1] )
          break;

        swap         = table[j];
        table[j]     = table[j - 1];
        table[j - 1] = swap;
      }
    }
  }

  FT_LOCAL_DEF( void )
  af_sort_and_quantize_widths( FT_UInt*  count,
                               AF_Width  table,
                               FT_Pos    threshold )
  {
    FT_UInt      i, j;
    FT_UInt      cur_idx;
    FT_Pos       cur_val;
    FT_Pos       sum;
    AF_WidthRec  swap;

    if ( *count == 1 )
      return;

    /* sort */
    for ( i = 1; i < *count; i++ )
    {
      for ( j = i; j > 0; j-- )
      {
        if ( table[j].org >= table[j - 1].org )
          break;

        swap         = table[j];
        table[j]     = table[j - 1];
        table[j - 1] = swap;
      }
    }

    cur_idx = 0;
    cur_val = table[cur_idx].org;

    /* compute and use mean values for clusters not larger than  */
    /* `threshold'; this is very primitive and might not yield   */
    /* the best result, but normally, using reference character  */
    /* `o', `*count' is 2, so the code below is fully sufficient */
    for ( i = 1; i < *count; i++ )
    {
      if ( table[i].org - cur_val > threshold ||
           i == *count - 1                    )
      {
        sum = 0;

        /* fix loop for end of array */
        if ( table[i].org - cur_val <= threshold &&
             i == *count - 1                     )
          i++;

        for ( j = cur_idx; j < i; j++ )
        {
          sum         += table[j].org;
          table[j].org = 0;
        }
        table[cur_idx].org = sum / (FT_Pos)j;

        if ( i < *count - 1 )
        {
          cur_idx = i + 1;
          cur_val = table[cur_idx].org;
        }
      }
    }

    cur_idx = 1;

    /* compress array to remove zero values */
    for ( i = 1; i < *count; i++ )
    {
      if ( table[i].org )
        table[cur_idx++] = table[i];
    }

    *count = cur_idx;
  }

/* END */

repo_language: C

mime_type: text/x-c


text:
/****************************************************************************
 *
 * afhints.h
 *
 *   Auto-fitter hinting routines (specification).
 *
 * Copyright (C) 2003-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#ifndef AFHINTS_H_
#define AFHINTS_H_

#include "aftypes.h"

#define xxAF_SORT_SEGMENTS

FT_BEGIN_HEADER

  /*
   * The definition of outline glyph hints.  These are shared by all
   * writing system analysis routines (until now).
   */

  typedef enum  AF_Dimension_
  {
    AF_DIMENSION_HORZ = 0,  /* x coordinates,                    */
                            /* i.e., vertical segments & edges   */
    AF_DIMENSION_VERT = 1,  /* y coordinates,                    */
                            /* i.e., horizontal segments & edges */

    AF_DIMENSION_MAX  /* do not remove */

  } AF_Dimension;

  /* hint directions -- the values are computed so that two vectors are */
  /* in opposite directions iff `dir1 + dir2 == 0'                      */
  typedef enum  AF_Direction_
  {
    AF_DIR_NONE  =  4,
    AF_DIR_RIGHT =  1,
    AF_DIR_LEFT  = -1,
    AF_DIR_UP    =  2,
    AF_DIR_DOWN  = -2

  } AF_Direction;

  /*
   * The following explanations are mostly taken from the article
   *
   *   Real-Time Grid Fitting of Typographic Outlines
   *
   * by David Turner and Werner Lemberg
   *
   *   https://www.tug.org/TUGboat/Articles/tb24-3/lemberg.pdf
   *
   * with appropriate updates.
   *
   *
   * Segments
   *
   *   `af_{cjk,latin,...}_hints_compute_segments' are the functions to
   *   find segments in an outline.
   *
   *   A segment is a series of at least two consecutive points that are
   *   approximately aligned along a coordinate axis.  The analysis to do
   *   so is specific to a writing system.
   *
   *
   * Edges
   *
   *   `af_{cjk,latin,...}_hints_compute_edges' are the functions to find
   *   edges.
   *
   *   As soon as segments are defined, the auto-hinter groups them into
   *   edges.  An edge corresponds to a single position on the main
   *   dimension that collects one or more segments (allowing for a small
   *   threshold).
   *
   *   As an example, the `latin' writing system first tries to grid-fit
   *   edges, then to align segments on the edges unless it detects that
   *   they form a serif.
   *
   *
   *                     A          H
   *                      |        |
   *                      |        |
   *                      |        |
   *                      |        |
   *        C             |        |             F
   *         +------<-----+        +-----<------+
   *         |             B      G             |
   *         |                                  |
   *         |                                  |
   *         +--------------->------------------+
   *        D                                    E
   *
   *
   * Stems
   *
   *   Stems are detected by `af_{cjk,latin,...}_hint_edges'.
   *
   *   Segments need to be `linked' to other ones in order to detect stems.
   *   A stem is made of two segments that face each other in opposite
   *   directions and that are sufficiently close to each other.  Using
   *   vocabulary from the TrueType specification, stem segments form a
   *   `black distance'.
   *
   *   In the above ASCII drawing, the horizontal segments are BC, DE, and
   *   FG; the vertical segments are AB, CD, EF, and GH.
   *
   *   Each segment has at most one `best' candidate to form a black
   *   distance, or no candidate at all.  Notice that two distinct segments
   *   can have the same candidate, which frequently means a serif.
   *
   *   A stem is recognized by the following condition:
   *
   *     best segment_1 = segment_2 && best segment_2 = segment_1
   *
   *   The best candidate is stored in field `link' in structure
   *   `AF_Segment'.
   *
   *   In the above ASCII drawing, the best candidate for both AB and CD is
   *   GH, while the best candidate for GH is AB.  Similarly, the best
   *   candidate for EF and GH is AB, while the best candidate for AB is
   *   GH.
   *
   *   The detection and handling of stems is dependent on the writing
   *   system.
   *
   *
   * Serifs
   *
   *   Serifs are detected by `af_{cjk,latin,...}_hint_edges'.
   *
   *   In comparison to a stem, a serif (as handled by the auto-hinter
   *   module that takes care of the `latin' writing system) has
   *
   *     best segment_1 = segment_2 && best segment_2 != segment_1
   *
   *   where segment_1 corresponds to the serif segment (CD and EF in the
   *   above ASCII drawing).
   *
   *   The best candidate is stored in field `serif' in structure
   *   `AF_Segment' (and `link' is set to NULL).
   *
   *
   * Touched points
   *
   *   A point is called `touched' if it has been processed somehow by the
   *   auto-hinter.  It basically means that it shouldn't be moved again
   *   (or moved only under certain constraints to preserve the already
   *   applied processing).
   *
   *
   * Flat and round segments
   *
   *   Segments are `round' or `flat', depending on the series of points
   *   that define them.  A segment is round if the next and previous point
   *   of an extremum (which can be either a single point or sequence of
   *   points) are both conic or cubic control points.  Otherwise, a
   *   segment with an extremum is flat.
   *
   *
   * Strong Points
   *
   *   Experience has shown that points not part of an edge need to be
   *   interpolated linearly between their two closest edges, even if these
   *   are not part of the contour of those particular points.  Typical
   *   candidates for this are
   *
   *   - angle points (i.e., points where the `in' and `out' direction
   *     differ greatly)
   *
   *   - inflection points (i.e., where the `in' and `out' angles are the
   *     same, but the curvature changes sign) [currently, such points
   *     aren't handled specially in the auto-hinter]
   *
   *   `af_glyph_hints_align_strong_points' is the function that takes
   *   care of such situations; it is equivalent to the TrueType `IP'
   *   hinting instruction.
   *
   *
   * Weak Points
   *
   *   Other points in the outline must be interpolated using the
   *   coordinates of their previous and next unfitted contour neighbours.
   *   These are called `weak points' and are touched by the function
   *   `af_glyph_hints_align_weak_points', equivalent to the TrueType `IUP'
   *   hinting instruction.  Typical candidates are control points and
   *   points on the contour without a major direction.
   *
   *   The major effect is to reduce possible distortion caused by
   *   alignment of edges and strong points, thus weak points are processed
   *   after strong points.
   */

  /* point hint flags */
#define AF_FLAG_NONE  0

  /* point type flags */
#define AF_FLAG_CONIC    ( 1U << 0 )
#define AF_FLAG_CUBIC    ( 1U << 1 )
#define AF_FLAG_CONTROL  ( AF_FLAG_CONIC | AF_FLAG_CUBIC )

  /* point touch flags */
#define AF_FLAG_TOUCH_X  ( 1U << 2 )
#define AF_FLAG_TOUCH_Y  ( 1U << 3 )

  /* candidates for weak interpolation have this flag set */
#define AF_FLAG_WEAK_INTERPOLATION  ( 1U << 4 )

  /* the distance to the next point is very small */
#define AF_FLAG_NEAR  ( 1U << 5 )

  /* edge hint flags */
#define AF_EDGE_NORMAL  0
#define AF_EDGE_ROUND    ( 1U << 0 )
#define AF_EDGE_SERIF    ( 1U << 1 )
#define AF_EDGE_DONE     ( 1U << 2 )
#define AF_EDGE_NEUTRAL  ( 1U << 3 ) /* edge aligns to a neutral blue zone */

  typedef struct AF_PointRec_*    AF_Point;
  typedef struct AF_SegmentRec_*  AF_Segment;
  typedef struct AF_EdgeRec_*     AF_Edge;

  typedef struct  AF_PointRec_
  {
    FT_UShort  flags;    /* point flags used by hinter   */
    FT_Char    in_dir;   /* direction of inwards vector  */
    FT_Char    out_dir;  /* direction of outwards vector */

    FT_Pos     ox, oy;   /* original, scaled position                   */
    FT_Short   fx, fy;   /* original, unscaled position (in font units) */
    FT_Pos     x, y;     /* current position                            */
    FT_Pos     u, v;     /* current (x,y) or (y,x) depending on context */

    AF_Point   next;     /* next point in contour     */
    AF_Point   prev;     /* previous point in contour */

#ifdef FT_DEBUG_AUTOFIT
    /* track `before' and `after' edges for strong points */
    AF_Edge    before[2];
    AF_Edge    after[2];
#endif

  } AF_PointRec;

  typedef struct  AF_SegmentRec_
  {
    FT_Byte     flags;       /* edge/segment flags for this segment */
    FT_Char     dir;         /* segment direction                   */
    FT_Short    pos;         /* position of segment                 */
    FT_Short    delta;       /* deviation from segment position     */
    FT_Short    min_coord;   /* minimum coordinate of segment       */
    FT_Short    max_coord;   /* maximum coordinate of segment       */
    FT_Short    height;      /* the hinted segment height           */

    AF_Edge     edge;        /* the segment's parent edge           */
    AF_Segment  edge_next;   /* link to next segment in parent edge */

    AF_Segment  link;        /* (stem) link segment        */
    AF_Segment  serif;       /* primary segment for serifs */
    FT_Pos      score;       /* used during stem matching  */
    FT_Pos      len;         /* used during stem matching  */

    AF_Point    first;       /* first point in edge segment */
    AF_Point    last;        /* last point in edge segment  */

  } AF_SegmentRec;

  typedef struct  AF_EdgeRec_
  {
    FT_Short    fpos;       /* original, unscaled position (in font units) */
    FT_Pos      opos;       /* original, scaled position                   */
    FT_Pos      pos;        /* current position                            */

    FT_Byte     flags;      /* edge flags                                   */
    FT_Char     dir;        /* edge direction                               */
    FT_Fixed    scale;      /* used to speed up interpolation between edges */

    AF_Width    blue_edge;  /* non-NULL if this is a blue edge */
    AF_Edge     link;       /* link edge                       */
    AF_Edge     serif;      /* primary edge for serifs         */
    FT_Int      score;      /* used during stem matching       */

    AF_Segment  first;      /* first segment in edge */
    AF_Segment  last;       /* last segment in edge  */

  } AF_EdgeRec;

#define AF_SEGMENTS_EMBEDDED  18   /* number of embedded segments   */
#define AF_EDGES_EMBEDDED     12   /* number of embedded edges      */

  typedef struct  AF_AxisHintsRec_
  {
    FT_Int        num_segments; /* number of used segments      */
    FT_Int        max_segments; /* number of allocated segments */
    AF_Segment    segments;     /* segments array               */
#ifdef AF_SORT_SEGMENTS
    FT_Int        mid_segments;
#endif

    FT_Int        num_edges;    /* number of used edges      */
    FT_Int        max_edges;    /* number of allocated edges */
    AF_Edge       edges;        /* edges array               */

    AF_Direction  major_dir;    /* either vertical or horizontal */

    /* two arrays to avoid allocation penalty */
    struct
    {
      AF_SegmentRec  segments[AF_SEGMENTS_EMBEDDED];
      AF_EdgeRec     edges[AF_EDGES_EMBEDDED];
    } embedded;

  } AF_AxisHintsRec, *AF_AxisHints;

#define AF_POINTS_EMBEDDED     96   /* number of embedded points   */
#define AF_CONTOURS_EMBEDDED    8   /* number of embedded contours */

  typedef struct  AF_GlyphHintsRec_
  {
    FT_Memory        memory;

    FT_Fixed         x_scale;
    FT_Pos           x_delta;

    FT_Fixed         y_scale;
    FT_Pos           y_delta;

    FT_Int           max_points;    /* number of allocated points */
    FT_Int           num_points;    /* number of used points      */
    AF_Point         points;        /* points array               */

    FT_Int           max_contours;  /* number of allocated contours */
    FT_Int           num_contours;  /* number of used contours      */
    AF_Point*        contours;      /* contours array               */

    AF_AxisHintsRec  axis[AF_DIMENSION_MAX];

    FT_UInt32        scaler_flags;  /* copy of scaler flags    */
    FT_UInt32        other_flags;   /* free for style-specific */
                                    /* implementations         */
    AF_StyleMetrics  metrics;

    FT_Pos           xmin_delta;    /* used for warping */
    FT_Pos           xmax_delta;

    /* Two arrays to avoid allocation penalty.            */
    /* The `embedded' structure must be the last element! */
    struct
    {
      AF_Point       contours[AF_CONTOURS_EMBEDDED];
      AF_PointRec    points[AF_POINTS_EMBEDDED];
    } embedded;

  } AF_GlyphHintsRec;

#define AF_HINTS_TEST_SCALER( h, f )  ( (h)->scaler_flags & (f) )
#define AF_HINTS_TEST_OTHER( h, f )   ( (h)->other_flags  & (f) )

#ifdef FT_DEBUG_AUTOFIT

#define AF_HINTS_DO_HORIZONTAL( h )                                     \
          ( !_af_debug_disable_horz_hints                            && \
            !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_HORIZONTAL ) )

#define AF_HINTS_DO_VERTICAL( h )                                     \
          ( !_af_debug_disable_vert_hints                          && \
            !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_VERTICAL ) )

#define AF_HINTS_DO_BLUES( h )  ( !_af_debug_disable_blue_hints )

#else /* !FT_DEBUG_AUTOFIT */

#define AF_HINTS_DO_HORIZONTAL( h )                                \
          !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_HORIZONTAL )

#define AF_HINTS_DO_VERTICAL( h )                                \
          !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_VERTICAL )

#define AF_HINTS_DO_BLUES( h )  1

#endif /* !FT_DEBUG_AUTOFIT */

#define AF_HINTS_DO_ADVANCE( h )                                \
          !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_ADVANCE )

#define AF_HINTS_DO_WARP( h )                                  \
          !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_WARPER )

  FT_LOCAL( AF_Direction )
  af_direction_compute( FT_Pos  dx,
                        FT_Pos  dy );

  FT_LOCAL( FT_Error )
  af_axis_hints_new_segment( AF_AxisHints  axis,
                             FT_Memory     memory,
                             AF_Segment   *asegment );

  FT_LOCAL( FT_Error)
  af_axis_hints_new_edge( AF_AxisHints  axis,
                          FT_Int        fpos,
                          AF_Direction  dir,
                          FT_Bool       top_to_bottom_hinting,
                          FT_Memory     memory,
                          AF_Edge      *edge );

  FT_LOCAL( void )
  af_glyph_hints_init( AF_GlyphHints  hints,
                       FT_Memory      memory );

  FT_LOCAL( void )
  af_glyph_hints_rescale( AF_GlyphHints    hints,
                          AF_StyleMetrics  metrics );

  FT_LOCAL( FT_Error )
  af_glyph_hints_reload( AF_GlyphHints  hints,
                         FT_Outline*    outline );

  FT_LOCAL( void )
  af_glyph_hints_save( AF_GlyphHints  hints,
                       FT_Outline*    outline );

  FT_LOCAL( void )
  af_glyph_hints_align_edge_points( AF_GlyphHints  hints,
                                    AF_Dimension   dim );

  FT_LOCAL( void )
  af_glyph_hints_align_strong_points( AF_GlyphHints  hints,
                                      AF_Dimension   dim );

  FT_LOCAL( void )
  af_glyph_hints_align_weak_points( AF_GlyphHints  hints,
                                    AF_Dimension   dim );

#ifdef AF_CONFIG_OPTION_USE_WARPER
  FT_LOCAL( void )
  af_glyph_hints_scale_dim( AF_GlyphHints  hints,
                            AF_Dimension   dim,
                            FT_Fixed       scale,
                            FT_Pos         delta );
#endif

  FT_LOCAL( void )
  af_glyph_hints_done( AF_GlyphHints  hints );

/* */

#define AF_SEGMENT_LEN( seg )          ( (seg)->max_coord - (seg)->min_coord )

#define AF_SEGMENT_DIST( seg1, seg2 )  ( ( (seg1)->pos > (seg2)->pos )   \
                                           ? (seg1)->pos - (seg2)->pos   \
                                           : (seg2)->pos - (seg1)->pos )

FT_END_HEADER

#endif /* AFHINTS_H_ */

/* END */


text:
/****************************************************************************
 *
 * afwarp.c
 *
 *   Auto-fitter warping algorithm (body).
 *
 * Copyright (C) 2006-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

  /*
   * The idea of the warping code is to slightly scale and shift a glyph
   * within a single dimension so that as much of its segments are aligned
   * (more or less) on the grid.  To find out the optimal scaling and
   * shifting value, various parameter combinations are tried and scored.
   */

#include "afwarp.h"

#ifdef AF_CONFIG_OPTION_USE_WARPER

  /**************************************************************************
   *
   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
   * messages during execution.
   */
#undef  FT_COMPONENT
#define FT_COMPONENT  afwarp

  /* The weights cover the range 0/64 - 63/64 of a pixel.  Obviously, */
  /* values around a half pixel (which means exactly between two grid */
  /* lines) gets the worst weight.                                    */
#if 1
  static const AF_WarpScore
  af_warper_weights[64] =
  {
    35, 32, 30, 25, 20, 15, 12, 10,  5,  1,  0,  0,  0,  0,  0,  0,
     0,  0,  0,  0,  0,  0, -1, -2, -5, -8,-10,-10,-20,-20,-30,-30,

   -30,-30,-20,-20,-10,-10, -8, -5, -2, -1,  0,  0,  0,  0,  0,  0,
     0,  0,  0,  0,  0,  0,  0,  1,  5, 10, 12, 15, 20, 25, 30, 32,
  };
#else
  static const AF_WarpScore
  af_warper_weights[64] =
  {
    30, 20, 10,  5,  4,  4,  3,  2,  1,  0,  0,  0,  0,  0,  0,  0,
     0,  0,  0,  0,  0,  0,  0, -1, -2, -2, -5, -5,-10,-10,-15,-20,

   -20,-15,-15,-10,-10, -5, -5, -2, -2, -1,  0,  0,  0,  0,  0,  0,
     0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  3,  4,  4,  5, 10, 20,
  };
#endif

  /* Score segments for a given `scale' and `delta' in the range */
  /* `xx1' to `xx2', and store the best result in `warper'.  If  */
  /* the new best score is equal to the old one, prefer the      */
  /* value with a smaller distortion (around `base_distort').    */

  static void
  af_warper_compute_line_best( AF_Warper     warper,
                               FT_Fixed      scale,
                               FT_Pos        delta,
                               FT_Pos        xx1,
                               FT_Pos        xx2,
                               AF_WarpScore  base_distort,
                               AF_Segment    segments,
                               FT_Int        num_segments )
  {
    FT_Int        idx_min, idx_max, idx0;
    FT_Int        nn;
    AF_WarpScore  scores[65];

    for ( nn = 0; nn < 65; nn++ )
      scores[nn] = 0;

    idx0 = xx1 - warper->t1;

    /* compute minimum and maximum indices */
    {
      FT_Pos  xx1min = warper->x1min;
      FT_Pos  xx1max = warper->x1max;
      FT_Pos  w      = xx2 - xx1;

      if ( xx1min + w < warper->x2min )
        xx1min = warper->x2min - w;

      if ( xx1max + w > warper->x2max )
        xx1max = warper->x2max - w;

      idx_min = xx1min - warper->t1;
      idx_max = xx1max - warper->t1;

      if ( idx_min < 0 || idx_min > idx_max || idx_max > 64 )
      {
        FT_TRACE5(( "invalid indices:\n"
                    "  min=%d max=%d, xx1=%ld xx2=%ld,\n"
                    "  x1min=%ld x1max=%ld, x2min=%ld x2max=%ld\n",
                    idx_min, idx_max, xx1, xx2,
                    warper->x1min, warper->x1max,
                    warper->x2min, warper->x2max ));
        return;
      }
    }

    for ( nn = 0; nn < num_segments; nn++ )
    {
      FT_Pos  len = segments[nn].max_coord - segments[nn].min_coord;
      FT_Pos  y0  = FT_MulFix( segments[nn].pos, scale ) + delta;
      FT_Pos  y   = y0 + ( idx_min - idx0 );
      FT_Int  idx;

      /* score the length of the segments for the given range */
      for ( idx = idx_min; idx <= idx_max; idx++, y++ )
        scores[idx] += af_warper_weights[y & 63] * len;
    }

    /* find best score */
    {
      FT_Int  idx;

      for ( idx = idx_min; idx <= idx_max; idx++ )
      {
        AF_WarpScore  score = scores[idx];
        AF_WarpScore  distort = base_distort + ( idx - idx0 );

        if ( score > warper->best_score         ||
             ( score == warper->best_score    &&
               distort < warper->best_distort ) )
        {
          warper->best_score   = score;
          warper->best_distort = distort;
          warper->best_scale   = scale;
          warper->best_delta   = delta + ( idx - idx0 );
        }
      }
    }
  }

  /* Compute optimal scaling and delta values for a given glyph and */
  /* dimension.                                                     */

  FT_LOCAL_DEF( void )
  af_warper_compute( AF_Warper      warper,
                     AF_GlyphHints  hints,
                     AF_Dimension   dim,
                     FT_Fixed      *a_scale,
                     FT_Pos        *a_delta )
  {
    AF_AxisHints  axis;
    AF_Point      points;

    FT_Fixed      org_scale;
    FT_Pos        org_delta;

    FT_Int        nn, num_points, num_segments;
    FT_Int        X1, X2;
    FT_Int        w;

    AF_WarpScore  base_distort;
    AF_Segment    segments;

    /* get original scaling transformation */
    if ( dim == AF_DIMENSION_VERT )
    {
      org_scale = hints->y_scale;
      org_delta = hints->y_delta;
    }
    else
    {
      org_scale = hints->x_scale;
      org_delta = hints->x_delta;
    }

    warper->best_scale   = org_scale;
    warper->best_delta   = org_delta;
    warper->best_score   = FT_INT_MIN;
    warper->best_distort = 0;

    axis         = &hints->axis[dim];
    segments     = axis->segments;
    num_segments = axis->num_segments;
    points       = hints->points;
    num_points   = hints->num_points;

    *a_scale = org_scale;
    *a_delta = org_delta;

    /* get X1 and X2, minimum and maximum in original coordinates */
    if ( num_segments < 1 )
      return;

#if 1
    X1 = X2 = points[0].fx;
    for ( nn = 1; nn < num_points; nn++ )
    {
      FT_Int  X = points[nn].fx;

      if ( X < X1 )
        X1 = X;
      if ( X > X2 )
        X2 = X;
    }
#else
    X1 = X2 = segments[0].pos;
    for ( nn = 1; nn < num_segments; nn++ )
    {
      FT_Int  X = segments[nn].pos;

      if ( X < X1 )
        X1 = X;
      if ( X > X2 )
        X2 = X;
    }
#endif

    if ( X1 >= X2 )
      return;

    warper->x1 = FT_MulFix( X1, org_scale ) + org_delta;
    warper->x2 = FT_MulFix( X2, org_scale ) + org_delta;

    warper->t1 = AF_WARPER_FLOOR( warper->x1 );
    warper->t2 = AF_WARPER_CEIL( warper->x2 );

    /* examine a half pixel wide range around the maximum coordinates */
    warper->x1min = warper->x1 & ~31;
    warper->x1max = warper->x1min + 32;
    warper->x2min = warper->x2 & ~31;
    warper->x2max = warper->x2min + 32;

    if ( warper->x1max > warper->x2 )
      warper->x1max = warper->x2;

    if ( warper->x2min < warper->x1 )
      warper->x2min = warper->x1;

    warper->w0 = warper->x2 - warper->x1;

    if ( warper->w0 <= 64 )
    {
      warper->x1max = warper->x1;
      warper->x2min = warper->x2;
    }

    /* examine (at most) a pixel wide range around the natural width */
    warper->wmin = warper->x2min - warper->x1max;
    warper->wmax = warper->x2max - warper->x1min;

#if 1
    /* some heuristics to reduce the number of widths to be examined */
    {
      int  margin = 16;

      if ( warper->w0 <= 128 )
      {
         margin = 8;
         if ( warper->w0 <= 96 )
           margin = 4;
      }

      if ( warper->wmin < warper->w0 - margin )
        warper->wmin = warper->w0 - margin;

      if ( warper->wmax > warper->w0 + margin )
        warper->wmax = warper->w0 + margin;
    }

    if ( warper->wmin < warper->w0 * 3 / 4 )
      warper->wmin = warper->w0 * 3 / 4;

    if ( warper->wmax > warper->w0 * 5 / 4 )
      warper->wmax = warper->w0 * 5 / 4;
#else
    /* no scaling, just translation */
    warper->wmin = warper->wmax = warper->w0;
#endif

    for ( w = warper->wmin; w <= warper->wmax; w++ )
    {
      FT_Fixed  new_scale;
      FT_Pos    new_delta;
      FT_Pos    xx1, xx2;

      /* compute min and max positions for given width,       */
      /* assuring that they stay within the coordinate ranges */
      xx1 = warper->x1;
      xx2 = warper->x2;
      if ( w >= warper->w0 )
      {
        xx1 -= w - warper->w0;
        if ( xx1 < warper->x1min )
        {
          xx2 += warper->x1min - xx1;
          xx1  = warper->x1min;
        }
      }
      else
      {
        xx1 -= w - warper->w0;
        if ( xx1 > warper->x1max )
        {
          xx2 -= xx1 - warper->x1max;
          xx1  = warper->x1max;
        }
      }

      if ( xx1 < warper->x1 )
        base_distort = warper->x1 - xx1;
      else
        base_distort = xx1 - warper->x1;

      if ( xx2 < warper->x2 )
        base_distort += warper->x2 - xx2;
      else
        base_distort += xx2 - warper->x2;

      /* give base distortion a greater weight while scoring */
      base_distort *= 10;

      new_scale = org_scale + FT_DivFix( w - warper->w0, X2 - X1 );
      new_delta = xx1 - FT_MulFix( X1, new_scale );

      af_warper_compute_line_best( warper, new_scale, new_delta, xx1, xx2,
                                   base_distort,
                                   segments, num_segments );
    }

    {
      FT_Fixed  best_scale = warper->best_scale;
      FT_Pos    best_delta = warper->best_delta;

      hints->xmin_delta = FT_MulFix( X1, best_scale - org_scale )
                          + best_delta;
      hints->xmax_delta = FT_MulFix( X2, best_scale - org_scale )
                          + best_delta;

      *a_scale = best_scale;
      *a_delta = best_delta;
    }
  }

#else /* !AF_CONFIG_OPTION_USE_WARPER */

  /* ANSI C doesn't like empty source files */
  typedef int  _af_warp_dummy;

#endif /* !AF_CONFIG_OPTION_USE_WARPER */

/* END */


text:
/****************************************************************************
 *
 * pshinter.c
 *
 *   FreeType PostScript Hinting module
 *
 * Copyright (C) 2001-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#define FT_MAKE_OPTION_SINGLE_OBJECT
#include <ft2build.h>

#include "pshalgo.c"
#include "pshglob.c"
#include "pshmod.c"
#include "pshrec.c"

/* END */


text:
/****************************************************************************
 *
 * pshrec.h
 *
 *   Postscript (Type1/Type2) hints recorder (specification).
 *
 * Copyright (C) 2001-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

  /***************************************************************************
   *
   * The functions defined here are called from the Type 1, CID and CFF
   * font drivers to record the hints of a given character/glyph.
   *
   * The hints are recorded in a unified format, and are later processed
   * by the `optimizer' and `fitter' to adjust the outlines to the pixel
   * grid.
   *
   */

#ifndef PSHREC_H_
#define PSHREC_H_

#include <ft2build.h>
#include FT_INTERNAL_POSTSCRIPT_HINTS_H
#include "pshglob.h"

FT_BEGIN_HEADER

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                 GLYPH HINTS RECORDER INTERNALS                *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /* handle to hint record */
  typedef struct PS_HintRec_*  PS_Hint;

  /* hint types */
  typedef enum  PS_Hint_Type_
  {
    PS_HINT_TYPE_1 = 1,
    PS_HINT_TYPE_2 = 2

  } PS_Hint_Type;

  /* hint flags */
#define PS_HINT_FLAG_GHOST   1U
#define PS_HINT_FLAG_BOTTOM  2U

  /* hint descriptor */
  typedef struct  PS_HintRec_
  {
    FT_Int   pos;
    FT_Int   len;
    FT_UInt  flags;

  } PS_HintRec;

#define ps_hint_is_active( x )  ( (x)->flags & PS_HINT_FLAG_ACTIVE )
#define ps_hint_is_ghost( x )   ( (x)->flags & PS_HINT_FLAG_GHOST  )
#define ps_hint_is_bottom( x )  ( (x)->flags & PS_HINT_FLAG_BOTTOM )

  /* hints table descriptor */
  typedef struct  PS_Hint_TableRec_
  {
    FT_UInt  num_hints;
    FT_UInt  max_hints;
    PS_Hint  hints;

  } PS_Hint_TableRec, *PS_Hint_Table;

  /* hint and counter mask descriptor */
  typedef struct  PS_MaskRec_
  {
    FT_UInt   num_bits;
    FT_UInt   max_bits;
    FT_Byte*  bytes;
    FT_UInt   end_point;

  } PS_MaskRec, *PS_Mask;

  /* masks and counters table descriptor */
  typedef struct  PS_Mask_TableRec_
  {
    FT_UInt  num_masks;
    FT_UInt  max_masks;
    PS_Mask  masks;

  } PS_Mask_TableRec, *PS_Mask_Table;

 /* dimension-specific hints descriptor */
  typedef struct  PS_DimensionRec_
  {
    PS_Hint_TableRec  hints;
    PS_Mask_TableRec  masks;
    PS_Mask_TableRec  counters;

  } PS_DimensionRec, *PS_Dimension;

  /* glyph hints descriptor                                */
  /* dimension 0 => X coordinates + vertical hints/stems   */
  /* dimension 1 => Y coordinates + horizontal hints/stems */
  typedef struct  PS_HintsRec_
  {
    FT_Memory        memory;
    FT_Error         error;
    FT_UInt32        magic;
    PS_Hint_Type     hint_type;
    PS_DimensionRec  dimension[2];

  } PS_HintsRec, *PS_Hints;

  /* */

  /* initialize hints recorder */
  FT_LOCAL( void )
  ps_hints_init( PS_Hints   hints,
                 FT_Memory  memory );

  /* finalize hints recorder */
  FT_LOCAL( void )
  ps_hints_done( PS_Hints  hints );

  /* initialize Type1 hints recorder interface */
  FT_LOCAL( void )
  t1_hints_funcs_init( T1_Hints_FuncsRec*  funcs );

  /* initialize Type2 hints recorder interface */
  FT_LOCAL( void )
  t2_hints_funcs_init( T2_Hints_FuncsRec*  funcs );

#ifdef DEBUG_HINTER
  extern PS_Hints  ps_debug_hints;
  extern  int      ps_debug_no_horz_hints;
  extern  int      ps_debug_no_vert_hints;
#endif

 /* */

FT_END_HEADER

#endif /* PSHREC_H_ */

/* END */


text:
/****************************************************************************
 *
 * pshglob.c
 *
 *   PostScript hinter global hinting management (body).
 *   Inspired by the new auto-hinter module.
 *
 * Copyright (C) 2001-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used
 * modified and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_CALC_H
#include "pshglob.h"

#ifdef DEBUG_HINTER
  PSH_Globals  ps_debug_globals = NULL;
#endif

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                       STANDARD WIDTHS                         *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /* scale the widths/heights table */
  static void
  psh_globals_scale_widths( PSH_Globals  globals,
                            FT_UInt      direction )
  {
    PSH_Dimension  dim   = &globals->dimension[direction];
    PSH_Widths     stdw  = &dim->stdw;
    FT_UInt        count = stdw->count;
    PSH_Width      width = stdw->widths;
    PSH_Width      stand = width;               /* standard width/height */
    FT_Fixed       scale = dim->scale_mult;

    if ( count > 0 )
    {
      width->cur = FT_MulFix( width->org, scale );
      width->fit = FT_PIX_ROUND( width->cur );

      width++;
      count--;

      for ( ; count > 0; count--, width++ )
      {
        FT_Pos  w, dist;

        w    = FT_MulFix( width->org, scale );
        dist = w - stand->cur;

        if ( dist < 0 )
          dist = -dist;

        if ( dist < 128 )
          w = stand->cur;

        width->cur = w;
        width->fit = FT_PIX_ROUND( w );
      }
    }
  }

#if 0

  /* org_width is in font units, result in device pixels, 26.6 format */
  FT_LOCAL_DEF( FT_Pos )
  psh_dimension_snap_width( PSH_Dimension  dimension,
                            FT_Int         org_width )
  {
    FT_UInt  n;
    FT_Pos   width     = FT_MulFix( org_width, dimension->scale_mult );
    FT_Pos   best      = 64 + 32 + 2;
    FT_Pos   reference = width;

    for ( n = 0; n < dimension->stdw.count; n++ )
    {
      FT_Pos  w;
      FT_Pos  dist;

      w = dimension->stdw.widths[n].cur;
      dist = width - w;
      if ( dist < 0 )
        dist = -dist;
      if ( dist < best )
      {
        best      = dist;
        reference = w;
      }
    }

    if ( width >= reference )
    {
      width -= 0x21;
      if ( width < reference )
        width = reference;
    }
    else
    {
      width += 0x21;
      if ( width > reference )
        width = reference;
    }

    return width;
  }

#endif /* 0 */

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                       BLUE ZONES                              *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  static void
  psh_blues_set_zones_0( PSH_Blues       target,
                         FT_Bool         is_others,
                         FT_UInt         read_count,
                         FT_Short*       read,
                         PSH_Blue_Table  top_table,
                         PSH_Blue_Table  bot_table )
  {
    FT_UInt  count_top = top_table->count;
    FT_UInt  count_bot = bot_table->count;
    FT_Bool  first     = 1;

    FT_UNUSED( target );

    for ( ; read_count > 1; read_count -= 2 )
    {
      FT_Int         reference, delta;
      FT_UInt        count;
      PSH_Blue_Zone  zones, zone;
      FT_Bool        top;

      /* read blue zone entry, and select target top/bottom zone */
      top = 0;
      if ( first || is_others )
      {
        reference = read[1];
        delta     = read[0] - reference;

        zones = bot_table->zones;
        count = count_bot;
        first = 0;
      }
      else
      {
        reference = read[0];
        delta     = read[1] - reference;

        zones = top_table->zones;
        count = count_top;
        top   = 1;
      }

      /* insert into sorted table */
      zone = zones;
      for ( ; count > 0; count--, zone++ )
      {
        if ( reference < zone->org_ref )
          break;

        if ( reference == zone->org_ref )
        {
          FT_Int  delta0 = zone->org_delta;

          /* we have two zones on the same reference position -- */
          /* only keep the largest one                           */
          if ( delta < 0 )
          {
            if ( delta < delta0 )
              zone->org_delta = delta;
          }
          else
          {
            if ( delta > delta0 )
              zone->org_delta = delta;
          }
          goto Skip;
        }
      }

      for ( ; count > 0; count-- )
        zone[count] = zone[count-1];

      zone->org_ref   = reference;
      zone->org_delta = delta;

      if ( top )
        count_top++;
      else
        count_bot++;

    Skip:
      read += 2;
    }

    top_table->count = count_top;
    bot_table->count = count_bot;
  }

  /* Re-read blue zones from the original fonts and store them into our */
  /* private structure.  This function re-orders, sanitizes, and        */
  /* fuzz-expands the zones as well.                                    */
  static void
  psh_blues_set_zones( PSH_Blues  target,
                       FT_UInt    count,
                       FT_Short*  blues,
                       FT_UInt    count_others,
                       FT_Short*  other_blues,
                       FT_Int     fuzz,
                       FT_Int     family )
  {
    PSH_Blue_Table  top_table, bot_table;
    FT_UInt         count_top, count_bot;

    if ( family )
    {
      top_table = &target->family_top;
      bot_table = &target->family_bottom;
    }
    else
    {
      top_table = &target->normal_top;
      bot_table = &target->normal_bottom;
    }

    /* read the input blue zones, and build two sorted tables  */
    /* (one for the top zones, the other for the bottom zones) */
    top_table->count = 0;
    bot_table->count = 0;

    /* first, the blues */
    psh_blues_set_zones_0( target, 0,
                           count, blues, top_table, bot_table );
    psh_blues_set_zones_0( target, 1,
                           count_others, other_blues, top_table, bot_table );

    count_top = top_table->count;
    count_bot = bot_table->count;

    /* sanitize top table */
    if ( count_top > 0 )
    {
      PSH_Blue_Zone  zone = top_table->zones;

      for ( count = count_top; count > 0; count--, zone++ )
      {
        FT_Int  delta;

        if ( count > 1 )
        {
          delta = zone[1].org_ref - zone[0].org_ref;
          if ( zone->org_delta > delta )
            zone->org_delta = delta;
        }

        zone->org_bottom = zone->org_ref;
        zone->org_top    = zone->org_delta + zone->org_ref;
      }
    }

    /* sanitize bottom table */
    if ( count_bot > 0 )
    {
      PSH_Blue_Zone  zone = bot_table->zones;

      for ( count = count_bot; count > 0; count--, zone++ )
      {
        FT_Int  delta;

        if ( count > 1 )
        {
          delta = zone[0].org_ref - zone[1].org_ref;
          if ( zone->org_delta < delta )
            zone->org_delta = delta;
        }

        zone->org_top    = zone->org_ref;
        zone->org_bottom = zone->org_delta + zone->org_ref;
      }
    }

    /* expand top and bottom tables with blue fuzz */
    {
      FT_Int         dim, top, bot, delta;
      PSH_Blue_Zone  zone;

      zone  = top_table->zones;
      count = count_top;

      for ( dim = 1; dim >= 0; dim-- )
      {
        if ( count > 0 )
        {
          /* expand the bottom of the lowest zone normally */
          zone->org_bottom -= fuzz;

          /* expand the top and bottom of intermediate zones;    */
          /* checking that the interval is smaller than the fuzz */
          top = zone->org_top;

          for ( count--; count > 0; count-- )
          {
            bot   = zone[1].org_bottom;
            delta = bot - top;

            if ( delta / 2 < fuzz )
              zone[0].org_top = zone[1].org_bottom = top + delta / 2;
            else
            {
              zone[0].org_top    = top + fuzz;
              zone[1].org_bottom = bot - fuzz;
            }

            zone++;
            top = zone->org_top;
          }

          /* expand the top of the highest zone normally */
          zone->org_top = top + fuzz;
        }
        zone  = bot_table->zones;
        count = count_bot;
      }
    }
  }

  /* reset the blues table when the device transform changes */
  static void
  psh_blues_scale_zones( PSH_Blues  blues,
                         FT_Fixed   scale,
                         FT_Pos     delta )
  {
    FT_UInt         count;
    FT_UInt         num;
    PSH_Blue_Table  table = NULL;

    /*                                                        */
    /* Determine whether we need to suppress overshoots or    */
    /* not.  We simply need to compare the vertical scale     */
    /* parameter to the raw bluescale value.  Here is why:    */
    /*                                                        */
    /*   We need to suppress overshoots for all pointsizes.   */
    /*   At 300dpi that satisfies:                            */
    /*                                                        */
    /*      pointsize < 240*bluescale + 0.49                  */
    /*                                                        */
    /*   This corresponds to:                                 */
    /*                                                        */
    /*      pixelsize < 1000*bluescale + 49/24                */
    /*                                                        */
    /*      scale*EM_Size < 1000*bluescale + 49/24            */
    /*                                                        */
    /*   However, for normal Type 1 fonts, EM_Size is 1000!   */
    /*   We thus only check:                                  */
    /*                                                        */
    /*      scale < bluescale + 49/24000                      */
    /*                                                        */
    /*   which we shorten to                                  */
    /*                                                        */
    /*      "scale < bluescale"                               */
    /*                                                        */
    /* Note that `blue_scale' is stored 1000 times its real   */
    /* value, and that `scale' converts from font units to    */
    /* fractional pixels.                                     */
    /*                                                        */

    /* 1000 / 64 = 125 / 8 */
    if ( scale >= 0x20C49BAL )
      blues->no_overshoots = FT_BOOL( scale < blues->blue_scale * 8 / 125 );
    else
      blues->no_overshoots = FT_BOOL( scale * 125 < blues->blue_scale * 8 );

    /*                                                        */
    /*  The blue threshold is the font units distance under   */
    /*  which overshoots are suppressed due to the BlueShift  */
    /*  even if the scale is greater than BlueScale.          */
    /*                                                        */
    /*  It is the smallest distance such that                 */
    /*                                                        */
    /*    dist <= BlueShift && dist*scale <= 0.5 pixels       */
    /*                                                        */
    {
      FT_Int  threshold = blues->blue_shift;

      while ( threshold > 0 && FT_MulFix( threshold, scale ) > 32 )
        threshold--;

      blues->blue_threshold = threshold;
    }

    for ( num = 0; num < 4; num++ )
    {
      PSH_Blue_Zone  zone;

      switch ( num )
      {
      case 0:
        table = &blues->normal_top;
        break;
      case 1:
        table = &blues->normal_bottom;
        break;
      case 2:
        table = &blues->family_top;
        break;
      default:
        table = &blues->family_bottom;
        break;
      }

      zone  = table->zones;
      count = table->count;
      for ( ; count > 0; count--, zone++ )
      {
        zone->cur_top    = FT_MulFix( zone->org_top,    scale ) + delta;
        zone->cur_bottom = FT_MulFix( zone->org_bottom, scale ) + delta;
        zone->cur_ref    = FT_MulFix( zone->org_ref,    scale ) + delta;
        zone->cur_delta  = FT_MulFix( zone->org_delta,  scale );

        /* round scaled reference position */
        zone->cur_ref = FT_PIX_ROUND( zone->cur_ref );

#if 0
        if ( zone->cur_ref > zone->cur_top )
          zone->cur_ref -= 64;
        else if ( zone->cur_ref < zone->cur_bottom )
          zone->cur_ref += 64;
#endif
      }
    }

    /* process the families now */

    for ( num = 0; num < 2; num++ )
    {
      PSH_Blue_Zone   zone1, zone2;
      FT_UInt         count1, count2;
      PSH_Blue_Table  normal, family;

      switch ( num )
      {
      case 0:
        normal = &blues->normal_top;
        family = &blues->family_top;
        break;

      default:
        normal = &blues->normal_bottom;
        family = &blues->family_bottom;
      }

      zone1  = normal->zones;
      count1 = normal->count;

      for ( ; count1 > 0; count1--, zone1++ )
      {
        /* try to find a family zone whose reference position is less */
        /* than 1 pixel far from the current zone                     */
        zone2  = family->zones;
        count2 = family->count;

        for ( ; count2 > 0; count2--, zone2++ )
        {
          FT_Pos  Delta;

          Delta = zone1->org_ref - zone2->org_ref;
          if ( Delta < 0 )
            Delta = -Delta;

          if ( FT_MulFix( Delta, scale ) < 64 )
          {
            zone1->cur_top    = zone2->cur_top;
            zone1->cur_bottom = zone2->cur_bottom;
            zone1->cur_ref    = zone2->cur_ref;
            zone1->cur_delta  = zone2->cur_delta;
            break;
          }
        }
      }
    }
  }

  /* calculate the maximum height of given blue zones */
  static FT_Short
  psh_calc_max_height( FT_UInt          num,
                       const FT_Short*  values,
                       FT_Short         cur_max )
  {
    FT_UInt  count;

    for ( count = 0; count < num; count += 2 )
    {
      FT_Short  cur_height = values[count + 1] - values[count];

      if ( cur_height > cur_max )
        cur_max = cur_height;
    }

    return cur_max;
  }

  FT_LOCAL_DEF( void )
  psh_blues_snap_stem( PSH_Blues      blues,
                       FT_Int         stem_top,
                       FT_Int         stem_bot,
                       PSH_Alignment  alignment )
  {
    PSH_Blue_Table  table;
    FT_UInt         count;
    FT_Pos          delta;
    PSH_Blue_Zone   zone;
    FT_Int          no_shoots;

    alignment->align = PSH_BLUE_ALIGN_NONE;

    no_shoots = blues->no_overshoots;

    /* look up stem top in top zones table */
    table = &blues->normal_top;
    count = table->count;
    zone  = table->zones;

    for ( ; count > 0; count--, zone++ )
    {
      delta = SUB_LONG( stem_top, zone->org_bottom );
      if ( delta < -blues->blue_fuzz )
        break;

      if ( stem_top <= zone->org_top + blues->blue_fuzz )
      {
        if ( no_shoots || delta <= blues->blue_threshold )
        {
          alignment->align    |= PSH_BLUE_ALIGN_TOP;
          alignment->align_top = zone->cur_ref;
        }
        break;
      }
    }

    /* look up stem bottom in bottom zones table */
    table = &blues->normal_bottom;
    count = table->count;
    zone  = table->zones + count-1;

    for ( ; count > 0; count--, zone-- )
    {
      delta = SUB_LONG( zone->org_top, stem_bot );
      if ( delta < -blues->blue_fuzz )
        break;

      if ( stem_bot >= zone->org_bottom - blues->blue_fuzz )
      {
        if ( no_shoots || delta < blues->blue_threshold )
        {
          alignment->align    |= PSH_BLUE_ALIGN_BOT;
          alignment->align_bot = zone->cur_ref;
        }
        break;
      }
    }
  }

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                        GLOBAL HINTS                           *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  static void
  psh_globals_destroy( PSH_Globals  globals )
  {
    if ( globals )
    {
      FT_Memory  memory;

      memory = globals->memory;
      globals->dimension[0].stdw.count = 0;
      globals->dimension[1].stdw.count = 0;

      globals->blues.normal_top.count    = 0;
      globals->blues.normal_bottom.count = 0;
      globals->blues.family_top.count    = 0;
      globals->blues.family_bottom.count = 0;

      FT_FREE( globals );

#ifdef DEBUG_HINTER
      ps_debug_globals = NULL;
#endif
    }
  }

  static FT_Error
  psh_globals_new( FT_Memory     memory,
                   T1_Private*   priv,
                   PSH_Globals  *aglobals )
  {
    PSH_Globals  globals = NULL;
    FT_Error     error;

    if ( !FT_NEW( globals ) )
    {
      FT_UInt    count;
      FT_Short*  read;

      globals->memory = memory;

      /* copy standard widths */
      {
        PSH_Dimension  dim   = &globals->dimension[1];
        PSH_Width      write = dim->stdw.widths;

        write->org = priv->standard_width[0];
        write++;

        read = priv->snap_widths;
        for ( count = priv->num_snap_widths; count > 0; count-- )
        {
          write->org = *read;
          write++;
          read++;
        }

        dim->stdw.count = priv->num_snap_widths + 1;
      }

      /* copy standard heights */
      {
        PSH_Dimension  dim = &globals->dimension[0];
        PSH_Width      write = dim->stdw.widths;

        write->org = priv->standard_height[0];
        write++;
        read = priv->snap_heights;
        for ( count = priv->num_snap_heights; count > 0; count-- )
        {
          write->org = *read;
          write++;
          read++;
        }

        dim->stdw.count = priv->num_snap_heights + 1;
      }

      /* copy blue zones */
      psh_blues_set_zones( &globals->blues, priv->num_blue_values,
                           priv->blue_values, priv->num_other_blues,
                           priv->other_blues, priv->blue_fuzz, 0 );

      psh_blues_set_zones( &globals->blues, priv->num_family_blues,
                           priv->family_blues, priv->num_family_other_blues,
                           priv->family_other_blues, priv->blue_fuzz, 1 );

      /* limit the BlueScale value to `1 / max_of_blue_zone_heights' */
      {
        FT_Fixed  max_scale;
        FT_Short  max_height = 1;

        max_height = psh_calc_max_height( priv->num_blue_values,
                                          priv->blue_values,
                                          max_height );
        max_height = psh_calc_max_height( priv->num_other_blues,
                                          priv->other_blues,
                                          max_height );
        max_height = psh_calc_max_height( priv->num_family_blues,
                                          priv->family_blues,
                                          max_height );
        max_height = psh_calc_max_height( priv->num_family_other_blues,
                                          priv->family_other_blues,
                                          max_height );

        /* BlueScale is scaled 1000 times */
        max_scale = FT_DivFix( 1000, max_height );
        globals->blues.blue_scale = priv->blue_scale < max_scale
                                      ? priv->blue_scale
                                      : max_scale;
      }

      globals->blues.blue_shift = priv->blue_shift;
      globals->blues.blue_fuzz  = priv->blue_fuzz;

      globals->dimension[0].scale_mult  = 0;
      globals->dimension[0].scale_delta = 0;
      globals->dimension[1].scale_mult  = 0;
      globals->dimension[1].scale_delta = 0;

#ifdef DEBUG_HINTER
      ps_debug_globals = globals;
#endif
    }

    *aglobals = globals;
    return error;
  }

  FT_LOCAL_DEF( void )
  psh_globals_set_scale( PSH_Globals  globals,
                         FT_Fixed     x_scale,
                         FT_Fixed     y_scale,
                         FT_Fixed     x_delta,
                         FT_Fixed     y_delta )
  {
    PSH_Dimension  dim;

    dim = &globals->dimension[0];
    if ( x_scale != dim->scale_mult  ||
         x_delta != dim->scale_delta )
    {
      dim->scale_mult  = x_scale;
      dim->scale_delta = x_delta;

      psh_globals_scale_widths( globals, 0 );
    }

    dim = &globals->dimension[1];
    if ( y_scale != dim->scale_mult  ||
         y_delta != dim->scale_delta )
    {
      dim->scale_mult  = y_scale;
      dim->scale_delta = y_delta;

      psh_globals_scale_widths( globals, 1 );
      psh_blues_scale_zones( &globals->blues, y_scale, y_delta );
    }
  }

  FT_LOCAL_DEF( void )
  psh_globals_funcs_init( PSH_Globals_FuncsRec*  funcs )
  {
    funcs->create    = psh_globals_new;
    funcs->set_scale = psh_globals_set_scale;
    funcs->destroy   = psh_globals_destroy;
  }

/* END */


text:
/****************************************************************************
 *
 * pshglob.h
 *
 *   PostScript hinter global hinting management.
 *
 * Copyright (C) 2001-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#ifndef PSHGLOB_H_
#define PSHGLOB_H_

#include FT_FREETYPE_H
#include FT_INTERNAL_POSTSCRIPT_HINTS_H

FT_BEGIN_HEADER

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                    GLOBAL HINTS INTERNALS                     *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /**************************************************************************
   *
   * @constant:
   *   PS_GLOBALS_MAX_BLUE_ZONES
   *
   * @description:
   *   The maximum number of blue zones in a font global hints structure.
   *   See @PS_Globals_BluesRec.
   */
#define PS_GLOBALS_MAX_BLUE_ZONES  16

  /**************************************************************************
   *
   * @constant:
   *   PS_GLOBALS_MAX_STD_WIDTHS
   *
   * @description:
   *   The maximum number of standard and snap widths in either the
   *   horizontal or vertical direction.  See @PS_Globals_WidthsRec.
   */
#define PS_GLOBALS_MAX_STD_WIDTHS  16

  /* standard and snap width */
  typedef struct  PSH_WidthRec_
  {
    FT_Int  org;
    FT_Pos  cur;
    FT_Pos  fit;

  } PSH_WidthRec, *PSH_Width;

  /* standard and snap widths table */
  typedef struct  PSH_WidthsRec_
  {
    FT_UInt       count;
    PSH_WidthRec  widths[PS_GLOBALS_MAX_STD_WIDTHS];

  } PSH_WidthsRec, *PSH_Widths;

  typedef struct  PSH_DimensionRec_
  {
    PSH_WidthsRec  stdw;
    FT_Fixed       scale_mult;
    FT_Fixed       scale_delta;

  } PSH_DimensionRec, *PSH_Dimension;

  /* blue zone descriptor */
  typedef struct  PSH_Blue_ZoneRec_
  {
    FT_Int  org_ref;
    FT_Int  org_delta;
    FT_Int  org_top;
    FT_Int  org_bottom;

    FT_Pos  cur_ref;
    FT_Pos  cur_delta;
    FT_Pos  cur_bottom;
    FT_Pos  cur_top;

  } PSH_Blue_ZoneRec, *PSH_Blue_Zone;

  typedef struct  PSH_Blue_TableRec_
  {
    FT_UInt           count;
    PSH_Blue_ZoneRec  zones[PS_GLOBALS_MAX_BLUE_ZONES];

  } PSH_Blue_TableRec, *PSH_Blue_Table;

  /* blue zones table */
  typedef struct  PSH_BluesRec_
  {
    PSH_Blue_TableRec  normal_top;
    PSH_Blue_TableRec  normal_bottom;
    PSH_Blue_TableRec  family_top;
    PSH_Blue_TableRec  family_bottom;

    FT_Fixed           blue_scale;
    FT_Int             blue_shift;
    FT_Int             blue_threshold;
    FT_Int             blue_fuzz;
    FT_Bool            no_overshoots;

  } PSH_BluesRec, *PSH_Blues;

  /* font globals.                                         */
  /* dimension 0 => X coordinates + vertical hints/stems   */
  /* dimension 1 => Y coordinates + horizontal hints/stems */
  typedef struct  PSH_GlobalsRec_
  {
    FT_Memory         memory;
    PSH_DimensionRec  dimension[2];
    PSH_BluesRec      blues;

  } PSH_GlobalsRec;

#define PSH_BLUE_ALIGN_NONE  0
#define PSH_BLUE_ALIGN_TOP   1
#define PSH_BLUE_ALIGN_BOT   2

  typedef struct  PSH_AlignmentRec_
  {
    int     align;
    FT_Pos  align_top;
    FT_Pos  align_bot;

  } PSH_AlignmentRec, *PSH_Alignment;

  FT_LOCAL( void )
  psh_globals_funcs_init( PSH_Globals_FuncsRec*  funcs );

#if 0
  /* snap a stem width to fitter coordinates.  `org_width' is in font */
  /* units.  The result is in device pixels (26.6 format).            */
  FT_LOCAL( FT_Pos )
  psh_dimension_snap_width( PSH_Dimension  dimension,
                            FT_Int         org_width );
#endif

  FT_LOCAL( void )
  psh_globals_set_scale( PSH_Globals  globals,
                         FT_Fixed     x_scale,
                         FT_Fixed     y_scale,
                         FT_Fixed     x_delta,
                         FT_Fixed     y_delta );

  /* snap a stem to one or two blue zones */
  FT_LOCAL( void )
  psh_blues_snap_stem( PSH_Blues      blues,
                       FT_Int         stem_top,
                       FT_Int         stem_bot,
                       PSH_Alignment  alignment );
  /* */

#ifdef DEBUG_HINTER
  extern PSH_Globals  ps_debug_globals;
#endif

FT_END_HEADER

#endif /* PSHGLOB_H_ */

/* END */


text:
/****************************************************************************
 *
 * pshalgo.h
 *
 *   PostScript hinting algorithm (specification).
 *
 * Copyright (C) 2001-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#ifndef PSHALGO_H_
#define PSHALGO_H_

#include "pshrec.h"
#include "pshglob.h"

FT_BEGIN_HEADER

  /* handle to Hint structure */
  typedef struct PSH_HintRec_*  PSH_Hint;

  /* hint bit-flags */
#define PSH_HINT_GHOST   PS_HINT_FLAG_GHOST
#define PSH_HINT_BOTTOM  PS_HINT_FLAG_BOTTOM
#define PSH_HINT_ACTIVE  4U
#define PSH_HINT_FITTED  8U

#define psh_hint_is_active( x )  ( ( (x)->flags & PSH_HINT_ACTIVE ) != 0 )
#define psh_hint_is_ghost( x )   ( ( (x)->flags & PSH_HINT_GHOST  ) != 0 )
#define psh_hint_is_fitted( x )  ( ( (x)->flags & PSH_HINT_FITTED ) != 0 )

#define psh_hint_activate( x )    (x)->flags |=  PSH_HINT_ACTIVE
#define psh_hint_deactivate( x )  (x)->flags &= ~PSH_HINT_ACTIVE
#define psh_hint_set_fitted( x )  (x)->flags |=  PSH_HINT_FITTED

  /* hint structure */
  typedef struct  PSH_HintRec_
  {
    FT_Int    org_pos;
    FT_Int    org_len;
    FT_Pos    cur_pos;
    FT_Pos    cur_len;
    FT_UInt   flags;
    PSH_Hint  parent;
    FT_Int    order;

  } PSH_HintRec;

  /* this is an interpolation zone used for strong points;  */
  /* weak points are interpolated according to their strong */
  /* neighbours                                             */
  typedef struct  PSH_ZoneRec_
  {
    FT_Fixed  scale;
    FT_Fixed  delta;
    FT_Pos    min;
    FT_Pos    max;

  } PSH_ZoneRec, *PSH_Zone;

  typedef struct  PSH_Hint_TableRec_
  {
    FT_UInt        max_hints;
    FT_UInt        num_hints;
    PSH_Hint       hints;
    PSH_Hint*      sort;
    PSH_Hint*      sort_global;
    FT_UInt        num_zones;
    PSH_ZoneRec*   zones;
    PSH_Zone       zone;
    PS_Mask_Table  hint_masks;
    PS_Mask_Table  counter_masks;

  } PSH_Hint_TableRec, *PSH_Hint_Table;

  typedef struct PSH_PointRec_*    PSH_Point;
  typedef struct PSH_ContourRec_*  PSH_Contour;

  enum
  {
    PSH_DIR_NONE  =  4,
    PSH_DIR_UP    = -1,
    PSH_DIR_DOWN  =  1,
    PSH_DIR_LEFT  = -2,
    PSH_DIR_RIGHT =  2
  };

#define PSH_DIR_HORIZONTAL  2
#define PSH_DIR_VERTICAL    1

#define PSH_DIR_COMPARE( d1, d2 )   ( (d1) == (d2) || (d1) == -(d2) )
#define PSH_DIR_IS_HORIZONTAL( d )  PSH_DIR_COMPARE( d, PSH_DIR_HORIZONTAL )
#define PSH_DIR_IS_VERTICAL( d )    PSH_DIR_COMPARE( d, PSH_DIR_VERTICAL )

  /* the following bit-flags are computed once by the glyph */
  /* analyzer, for both dimensions                          */
#define PSH_POINT_OFF     1U      /* point is off the curve */
#define PSH_POINT_SMOOTH  2U      /* point is smooth        */
#define PSH_POINT_INFLEX  4U      /* point is inflection    */

#define psh_point_is_smooth( p )  ( (p)->flags & PSH_POINT_SMOOTH )
#define psh_point_is_off( p )     ( (p)->flags & PSH_POINT_OFF    )
#define psh_point_is_inflex( p )  ( (p)->flags & PSH_POINT_INFLEX )

#define psh_point_set_smooth( p )  (p)->flags |= PSH_POINT_SMOOTH
#define psh_point_set_off( p )     (p)->flags |= PSH_POINT_OFF
#define psh_point_set_inflex( p )  (p)->flags |= PSH_POINT_INFLEX

  /* the following bit-flags are re-computed for each dimension */
#define PSH_POINT_STRONG      16U /* point is strong                           */
#define PSH_POINT_FITTED      32U /* point is already fitted                   */
#define PSH_POINT_EXTREMUM    64U /* point is local extremum                   */
#define PSH_POINT_POSITIVE   128U /* extremum has positive contour flow        */
#define PSH_POINT_NEGATIVE   256U /* extremum has negative contour flow        */
#define PSH_POINT_EDGE_MIN   512U /* point is aligned to left/bottom stem edge */
#define PSH_POINT_EDGE_MAX  1024U /* point is aligned to top/right stem edge   */

#define psh_point_is_strong( p )    ( (p)->flags2 & PSH_POINT_STRONG )
#define psh_point_is_fitted( p )    ( (p)->flags2 & PSH_POINT_FITTED )
#define psh_point_is_extremum( p )  ( (p)->flags2 & PSH_POINT_EXTREMUM )
#define psh_point_is_positive( p )  ( (p)->flags2 & PSH_POINT_POSITIVE )
#define psh_point_is_negative( p )  ( (p)->flags2 & PSH_POINT_NEGATIVE )
#define psh_point_is_edge_min( p )  ( (p)->flags2 & PSH_POINT_EDGE_MIN )
#define psh_point_is_edge_max( p )  ( (p)->flags2 & PSH_POINT_EDGE_MAX )

#define psh_point_set_strong( p )    (p)->flags2 |= PSH_POINT_STRONG
#define psh_point_set_fitted( p )    (p)->flags2 |= PSH_POINT_FITTED
#define psh_point_set_extremum( p )  (p)->flags2 |= PSH_POINT_EXTREMUM
#define psh_point_set_positive( p )  (p)->flags2 |= PSH_POINT_POSITIVE
#define psh_point_set_negative( p )  (p)->flags2 |= PSH_POINT_NEGATIVE
#define psh_point_set_edge_min( p )  (p)->flags2 |= PSH_POINT_EDGE_MIN
#define psh_point_set_edge_max( p )  (p)->flags2 |= PSH_POINT_EDGE_MAX

  typedef struct  PSH_PointRec_
  {
    PSH_Point    prev;
    PSH_Point    next;
    PSH_Contour  contour;
    FT_UInt      flags;
    FT_UInt      flags2;
    FT_Char      dir_in;
    FT_Char      dir_out;
    PSH_Hint     hint;
    FT_Pos       org_u;
    FT_Pos       org_v;
    FT_Pos       cur_u;
#ifdef DEBUG_HINTER
    FT_Pos       org_x;
    FT_Pos       cur_x;
    FT_Pos       org_y;
    FT_Pos       cur_y;
    FT_UInt      flags_x;
    FT_UInt      flags_y;
#endif

  } PSH_PointRec;

  typedef struct  PSH_ContourRec_
  {
    PSH_Point  start;
    FT_UInt    count;

  } PSH_ContourRec;

  typedef struct  PSH_GlyphRec_
  {
    FT_UInt            num_points;
    FT_UInt            num_contours;

    PSH_Point          points;
    PSH_Contour        contours;

    FT_Memory          memory;
    FT_Outline*        outline;
    PSH_Globals        globals;
    PSH_Hint_TableRec  hint_tables[2];

    FT_Bool            vertical;
    FT_Int             major_dir;
    FT_Int             minor_dir;

    FT_Bool            do_horz_hints;
    FT_Bool            do_vert_hints;
    FT_Bool            do_horz_snapping;
    FT_Bool            do_vert_snapping;
    FT_Bool            do_stem_adjust;

  } PSH_GlyphRec, *PSH_Glyph;

#ifdef DEBUG_HINTER
  extern PSH_Hint_Table  ps_debug_hint_table;

  typedef void
  (*PSH_HintFunc)( PSH_Hint  hint,
                   FT_Bool   vertical );

  extern PSH_HintFunc    ps_debug_hint_func;

  extern PSH_Glyph       ps_debug_glyph;
#endif

  extern FT_Error
  ps_hints_apply( PS_Hints        ps_hints,
                  FT_Outline*     outline,
                  PSH_Globals     globals,
                  FT_Render_Mode  hint_mode );

FT_END_HEADER

#endif /* PSHALGO_H_ */

/* END */


text:
/****************************************************************************
 *
 * pshmod.h
 *
 *   PostScript hinter module interface (specification).
 *
 * Copyright (C) 2001-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#ifndef PSHMOD_H_
#define PSHMOD_H_

#include <ft2build.h>
#include FT_MODULE_H

FT_BEGIN_HEADER

  FT_DECLARE_MODULE( pshinter_module_class )

FT_END_HEADER

#endif /* PSHMOD_H_ */

/* END */


text:
# FreeType 2 src/pshinter Jamfile
#
# Copyright (C) 2001-2020 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
# and distributed under the terms of the FreeType project license,
# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
# indicate that you have read the license and understand and accept it
# fully.

SubDir  FT2_TOP $(FT2_SRC_DIR) pshinter ;

{
  local  _sources ;

  if $(FT2_MULTI)
  {
    _sources = pshalgo
               pshglob
               pshmod
               pshpic
               pshrec
               ;
  }
  else
  {
    _sources = pshinter ;
  }

  Library  $(FT2_LIB) : $(_sources).c ;
}

# end of src/pshinter Jamfile


text:
#
# FreeType 2 PSHinter module definition
#

# Copyright (C) 1996-2020 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
# and distributed under the terms of the FreeType project license,
# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
# indicate that you have read the license and understand and accept it
# fully.

FTMODULE_H_COMMANDS += PSHINTER_MODULE

define PSHINTER_MODULE
$(OPEN_DRIVER) FT_Module_Class, pshinter_module_class $(CLOSE_DRIVER)
$(ECHO_DRIVER)pshinter  $(ECHO_DRIVER_DESC)Postscript hinter module$(ECHO_DRIVER_DONE)
endef

# EOF


text:
/****************************************************************************
 *
 * pshmod.c
 *
 *   FreeType PostScript hinter module implementation (body).
 *
 * Copyright (C) 2001-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#include <ft2build.h>
#include FT_INTERNAL_OBJECTS_H
#include "pshrec.h"
#include "pshalgo.h"

  /* the Postscript Hinter module structure */
  typedef struct  PS_Hinter_Module_Rec_
  {
    FT_ModuleRec          root;
    PS_HintsRec           ps_hints;

    PSH_Globals_FuncsRec  globals_funcs;
    T1_Hints_FuncsRec     t1_funcs;
    T2_Hints_FuncsRec     t2_funcs;

  } PS_Hinter_ModuleRec, *PS_Hinter_Module;

  /* finalize module */
  FT_CALLBACK_DEF( void )
  ps_hinter_done( PS_Hinter_Module  module )
  {
    module->t1_funcs.hints = NULL;
    module->t2_funcs.hints = NULL;

    ps_hints_done( &module->ps_hints );
  }

  /* initialize module, create hints recorder and the interface */
  FT_CALLBACK_DEF( FT_Error )
  ps_hinter_init( PS_Hinter_Module  module )
  {
    FT_Memory  memory = module->root.memory;
    void*      ph     = &module->ps_hints;

    ps_hints_init( &module->ps_hints, memory );

    psh_globals_funcs_init( &module->globals_funcs );

    t1_hints_funcs_init( &module->t1_funcs );
    module->t1_funcs.hints = (T1_Hints)ph;

    t2_hints_funcs_init( &module->t2_funcs );
    module->t2_funcs.hints = (T2_Hints)ph;

    return 0;
  }

  /* returns global hints interface */
  FT_CALLBACK_DEF( PSH_Globals_Funcs )
  pshinter_get_globals_funcs( FT_Module  module )
  {
    return &((PS_Hinter_Module)module)->globals_funcs;
  }

  /* return Type 1 hints interface */
  FT_CALLBACK_DEF( T1_Hints_Funcs )
  pshinter_get_t1_funcs( FT_Module  module )
  {
    return &((PS_Hinter_Module)module)->t1_funcs;
  }

  /* return Type 2 hints interface */
  FT_CALLBACK_DEF( T2_Hints_Funcs )
  pshinter_get_t2_funcs( FT_Module  module )
  {
    return &((PS_Hinter_Module)module)->t2_funcs;
  }

  FT_DEFINE_PSHINTER_INTERFACE(
    pshinter_interface,

    pshinter_get_globals_funcs,
    pshinter_get_t1_funcs,
    pshinter_get_t2_funcs
  )

  FT_DEFINE_MODULE(
    pshinter_module_class,

    0,
    sizeof ( PS_Hinter_ModuleRec ),
    "pshinter",
    0x10000L,
    0x20000L,

    &pshinter_interface,        /* module-specific interface */

    (FT_Module_Constructor)ps_hinter_init,  /* module_init   */
    (FT_Module_Destructor) ps_hinter_done,  /* module_done   */
    (FT_Module_Requester)  NULL             /* get_interface */
  )

/* END */


text:
#
# FreeType 2 PSHinter driver configuration rules
#

# Copyright (C) 2001-2020 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
# and distributed under the terms of the FreeType project license,
# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
# indicate that you have read the license and understand and accept it
# fully.

# PSHINTER driver directory
#
PSHINTER_DIR := $(SRC_DIR)/pshinter

# compilation flags for the driver
#
PSHINTER_COMPILE := $(CC) $(ANSIFLAGS)                                 \
                          $I$(subst /,$(COMPILER_SEP),$(PSHINTER_DIR)) \
                          $(INCLUDE_FLAGS)                             \
                          $(FT_CFLAGS)

# PSHINTER driver sources (i.e., C files)
#
PSHINTER_DRV_SRC := $(PSHINTER_DIR)/pshalgo.c \
                    $(PSHINTER_DIR)/pshglob.c \
                    $(PSHINTER_DIR)/pshmod.c  \
                    $(PSHINTER_DIR)/pshrec.c

# PSHINTER driver headers
#
PSHINTER_DRV_H := $(PSHINTER_DRV_SRC:%c=%h) \
                  $(PSHINTER_DIR)/pshnterr.h

# PSHINTER driver object(s)
#
#   PSHINTER_DRV_OBJ_M is used during `multi' builds.
#   PSHINTER_DRV_OBJ_S is used during `single' builds.
#
PSHINTER_DRV_OBJ_M := $(PSHINTER_DRV_SRC:$(PSHINTER_DIR)/%.c=$(OBJ_DIR)/%.$O)
PSHINTER_DRV_OBJ_S := $(OBJ_DIR)/pshinter.$O

# PSHINTER driver source file for single build
#
PSHINTER_DRV_SRC_S := $(PSHINTER_DIR)/pshinter.c

# PSHINTER driver - single object
#
$(PSHINTER_DRV_OBJ_S): $(PSHINTER_DRV_SRC_S) $(PSHINTER_DRV_SRC) \
                       $(FREETYPE_H) $(PSHINTER_DRV_H)
	$(PSHINTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PSHINTER_DRV_SRC_S))

# PSHINTER driver - multiple objects
#
$(OBJ_DIR)/%.$O: $(PSHINTER_DIR)/%.c $(FREETYPE_H) $(PSHINTER_DRV_H)
	$(PSHINTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)

# update main driver object lists
#
DRV_OBJS_S += $(PSHINTER_DRV_OBJ_S)
DRV_OBJS_M += $(PSHINTER_DRV_OBJ_M)

# EOF