Home | History | Annotate | Download | only in truetype
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  ttgxvar.c                                                              */
      4 /*                                                                         */
      5 /*    TrueType GX Font Variation loader                                    */
      6 /*                                                                         */
      7 /*  Copyright 2004-2015 by                                                 */
      8 /*  David Turner, Robert Wilhelm, Werner Lemberg, and George Williams.     */
      9 /*                                                                         */
     10 /*  This file is part of the FreeType project, and may only be used,       */
     11 /*  modified, and distributed under the terms of the FreeType project      */
     12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
     13 /*  this file you indicate that you have read the license and              */
     14 /*  understand and accept it fully.                                        */
     15 /*                                                                         */
     16 /***************************************************************************/
     17 
     18 
     19   /*************************************************************************/
     20   /*                                                                       */
     21   /* Apple documents the `fvar', `gvar', `cvar', and `avar' tables at      */
     22   /*                                                                       */
     23   /*   https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6[fgca]var.html */
     24   /*                                                                       */
     25   /* The documentation for `fvar' is inconsistent.  At one point it says   */
     26   /* that `countSizePairs' should be 3, at another point 2.  It should     */
     27   /* be 2.                                                                 */
     28   /*                                                                       */
     29   /* The documentation for `gvar' is not intelligible; `cvar' refers you   */
     30   /* to `gvar' and is thus also incomprehensible.                          */
     31   /*                                                                       */
     32   /* The documentation for `avar' appears correct, but Apple has no fonts  */
     33   /* with an `avar' table, so it is hard to test.                          */
     34   /*                                                                       */
     35   /* Many thanks to John Jenkins (at Apple) in figuring this out.          */
     36   /*                                                                       */
     37   /*                                                                       */
     38   /* Apple's `kern' table has some references to tuple indices, but as     */
     39   /* there is no indication where these indices are defined, nor how to    */
     40   /* interpolate the kerning values (different tuples have different       */
     41   /* classes) this issue is ignored.                                       */
     42   /*                                                                       */
     43   /*************************************************************************/
     44 
     45 
     46 #include <ft2build.h>
     47 #include FT_INTERNAL_DEBUG_H
     48 #include FT_CONFIG_CONFIG_H
     49 #include FT_INTERNAL_STREAM_H
     50 #include FT_INTERNAL_SFNT_H
     51 #include FT_TRUETYPE_TAGS_H
     52 #include FT_MULTIPLE_MASTERS_H
     53 
     54 #include "ttpload.h"
     55 #include "ttgxvar.h"
     56 
     57 #include "tterrors.h"
     58 
     59 
     60 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
     61 
     62 
     63 #define FT_Stream_FTell( stream )                         \
     64           (FT_ULong)( (stream)->cursor - (stream)->base )
     65 #define FT_Stream_SeekSet( stream, off )                  \
     66           ( (stream)->cursor = (stream)->base + (off) )
     67 
     68 
     69   /*************************************************************************/
     70   /*                                                                       */
     71   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
     72   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
     73   /* messages during execution.                                            */
     74   /*                                                                       */
     75 #undef  FT_COMPONENT
     76 #define FT_COMPONENT  trace_ttgxvar
     77 
     78 
     79   /*************************************************************************/
     80   /*************************************************************************/
     81   /*****                                                               *****/
     82   /*****                       Internal Routines                       *****/
     83   /*****                                                               *****/
     84   /*************************************************************************/
     85   /*************************************************************************/
     86 
     87 
     88   /*************************************************************************/
     89   /*                                                                       */
     90   /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'.  It        */
     91   /* indicates that there is a delta for every point without needing to    */
     92   /* enumerate all of them.                                                */
     93   /*                                                                       */
     94 
     95   /* ensure that value `0' has the same width as a pointer */
     96 #define ALL_POINTS  (FT_UShort*)~(FT_PtrDist)0
     97 
     98 
     99 #define GX_PT_POINTS_ARE_WORDS      0x80U
    100 #define GX_PT_POINT_RUN_COUNT_MASK  0x7FU
    101 
    102 
    103   /*************************************************************************/
    104   /*                                                                       */
    105   /* <Function>                                                            */
    106   /*    ft_var_readpackedpoints                                            */
    107   /*                                                                       */
    108   /* <Description>                                                         */
    109   /*    Read a set of points to which the following deltas will apply.     */
    110   /*    Points are packed with a run length encoding.                      */
    111   /*                                                                       */
    112   /* <Input>                                                               */
    113   /*    stream    :: The data stream.                                      */
    114   /*                                                                       */
    115   /*    size      :: The size of the table holding the data.               */
    116   /*                                                                       */
    117   /* <Output>                                                              */
    118   /*    point_cnt :: The number of points read.  A zero value means that   */
    119   /*                 all points in the glyph will be affected, without     */
    120   /*                 enumerating them individually.                        */
    121   /*                                                                       */
    122   /* <Return>                                                              */
    123   /*    An array of FT_UShort containing the affected points or the        */
    124   /*    special value ALL_POINTS.                                          */
    125   /*                                                                       */
    126   static FT_UShort*
    127   ft_var_readpackedpoints( FT_Stream  stream,
    128                            FT_ULong   size,
    129                            FT_UInt   *point_cnt )
    130   {
    131     FT_UShort *points = NULL;
    132     FT_UInt    n;
    133     FT_UInt    runcnt;
    134     FT_UInt    i, j;
    135     FT_UShort  first;
    136     FT_Memory  memory = stream->memory;
    137     FT_Error   error  = FT_Err_Ok;
    138 
    139     FT_UNUSED( error );
    140 
    141 
    142     *point_cnt = 0;
    143 
    144     n = FT_GET_BYTE();
    145     if ( n == 0 )
    146       return ALL_POINTS;
    147 
    148     if ( n & GX_PT_POINTS_ARE_WORDS )
    149     {
    150       n  &= GX_PT_POINT_RUN_COUNT_MASK;
    151       n <<= 8;
    152       n  |= FT_GET_BYTE();
    153     }
    154 
    155     if ( n > size )
    156     {
    157       FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" ));
    158       return NULL;
    159     }
    160 
    161     if ( FT_NEW_ARRAY( points, n ) )
    162       return NULL;
    163 
    164     *point_cnt = n;
    165 
    166     i = 0;
    167     while ( i < n )
    168     {
    169       runcnt = FT_GET_BYTE();
    170       if ( runcnt & GX_PT_POINTS_ARE_WORDS )
    171       {
    172         runcnt     &= GX_PT_POINT_RUN_COUNT_MASK;
    173         first       = FT_GET_USHORT();
    174         points[i++] = first;
    175 
    176         if ( runcnt < 1 || i + runcnt > n )
    177           goto Exit;
    178 
    179         /* first point not included in run count */
    180         for ( j = 0; j < runcnt; j++ )
    181         {
    182           first      += FT_GET_USHORT();
    183           points[i++] = first;
    184         }
    185       }
    186       else
    187       {
    188         first       = FT_GET_BYTE();
    189         points[i++] = first;
    190 
    191         if ( runcnt < 1 || i + runcnt > n )
    192           goto Exit;
    193 
    194         for ( j = 0; j < runcnt; j++ )
    195         {
    196           first      += FT_GET_BYTE();
    197           points[i++] = first;
    198         }
    199       }
    200     }
    201 
    202   Exit:
    203     return points;
    204   }
    205 
    206 
    207 #define GX_DT_DELTAS_ARE_ZERO       0x80U
    208 #define GX_DT_DELTAS_ARE_WORDS      0x40U
    209 #define GX_DT_DELTA_RUN_COUNT_MASK  0x3FU
    210 
    211 
    212   /*************************************************************************/
    213   /*                                                                       */
    214   /* <Function>                                                            */
    215   /*    ft_var_readpackeddeltas                                            */
    216   /*                                                                       */
    217   /* <Description>                                                         */
    218   /*    Read a set of deltas.  These are packed slightly differently than  */
    219   /*    points.  In particular there is no overall count.                  */
    220   /*                                                                       */
    221   /* <Input>                                                               */
    222   /*    stream    :: The data stream.                                      */
    223   /*                                                                       */
    224   /*    size      :: The size of the table holding the data.               */
    225   /*                                                                       */
    226   /*    delta_cnt :: The number of deltas to be read.                      */
    227   /*                                                                       */
    228   /* <Return>                                                              */
    229   /*    An array of FT_Short containing the deltas for the affected        */
    230   /*    points.  (This only gets the deltas for one dimension.  It will    */
    231   /*    generally be called twice, once for x, once for y.  When used in   */
    232   /*    cvt table, it will only be called once.)                           */
    233   /*                                                                       */
    234   static FT_Short*
    235   ft_var_readpackeddeltas( FT_Stream  stream,
    236                            FT_ULong   size,
    237                            FT_UInt    delta_cnt )
    238   {
    239     FT_Short  *deltas = NULL;
    240     FT_UInt    runcnt, cnt;
    241     FT_UInt    i, j;
    242     FT_Memory  memory = stream->memory;
    243     FT_Error   error  = FT_Err_Ok;
    244 
    245     FT_UNUSED( error );
    246 
    247 
    248     if ( delta_cnt > size )
    249     {
    250       FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" ));
    251       return NULL;
    252     }
    253 
    254     if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
    255       return NULL;
    256 
    257     i = 0;
    258     while ( i < delta_cnt )
    259     {
    260       runcnt = FT_GET_BYTE();
    261       cnt    = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
    262 
    263       if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
    264       {
    265         /* `runcnt' zeroes get added */
    266         for ( j = 0; j <= cnt && i < delta_cnt; j++ )
    267           deltas[i++] = 0;
    268       }
    269       else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
    270       {
    271         /* `runcnt' shorts from the stack */
    272         for ( j = 0; j <= cnt && i < delta_cnt; j++ )
    273           deltas[i++] = FT_GET_SHORT();
    274       }
    275       else
    276       {
    277         /* `runcnt' signed bytes from the stack */
    278         for ( j = 0; j <= cnt && i < delta_cnt; j++ )
    279           deltas[i++] = FT_GET_CHAR();
    280       }
    281 
    282       if ( j <= cnt )
    283       {
    284         /* bad format */
    285         FT_FREE( deltas );
    286         return NULL;
    287       }
    288     }
    289 
    290     return deltas;
    291   }
    292 
    293 
    294   /*************************************************************************/
    295   /*                                                                       */
    296   /* <Function>                                                            */
    297   /*    ft_var_load_avar                                                   */
    298   /*                                                                       */
    299   /* <Description>                                                         */
    300   /*    Parse the `avar' table if present.  It need not be, so we return   */
    301   /*    nothing.                                                           */
    302   /*                                                                       */
    303   /* <InOut>                                                               */
    304   /*    face :: The font face.                                             */
    305   /*                                                                       */
    306   static void
    307   ft_var_load_avar( TT_Face  face )
    308   {
    309     FT_Stream       stream = FT_FACE_STREAM( face );
    310     FT_Memory       memory = stream->memory;
    311     GX_Blend        blend  = face->blend;
    312     GX_AVarSegment  segment;
    313     FT_Error        error = FT_Err_Ok;
    314     FT_Long         version;
    315     FT_Long         axisCount;
    316     FT_Int          i, j;
    317     FT_ULong        table_len;
    318 
    319     FT_UNUSED( error );
    320 
    321 
    322     FT_TRACE2(( "AVAR " ));
    323 
    324     blend->avar_checked = TRUE;
    325     error = face->goto_table( face, TTAG_avar, stream, &table_len );
    326     if ( error )
    327     {
    328       FT_TRACE2(( "is missing\n" ));
    329       return;
    330     }
    331 
    332     if ( FT_FRAME_ENTER( table_len ) )
    333       return;
    334 
    335     version   = FT_GET_LONG();
    336     axisCount = FT_GET_LONG();
    337 
    338     if ( version != 0x00010000L )
    339     {
    340       FT_TRACE2(( "bad table version\n" ));
    341       goto Exit;
    342     }
    343 
    344     FT_TRACE2(( "loaded\n" ));
    345 
    346     if ( axisCount != (FT_Long)blend->mmvar->num_axis )
    347     {
    348       FT_TRACE2(( "ft_var_load_avar: number of axes in `avar' and `cvar'\n"
    349                   "                  table are different\n" ));
    350       goto Exit;
    351     }
    352 
    353     if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
    354       goto Exit;
    355 
    356     segment = &blend->avar_segment[0];
    357     for ( i = 0; i < axisCount; i++, segment++ )
    358     {
    359       FT_TRACE5(( "  axis %d:\n", i ));
    360 
    361       segment->pairCount = FT_GET_USHORT();
    362       if ( (FT_ULong)segment->pairCount * 4 > table_len                ||
    363            FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
    364       {
    365         /* Failure.  Free everything we have done so far.  We must do */
    366         /* it right now since loading the `avar' table is optional.   */
    367 
    368         for ( j = i - 1; j >= 0; j-- )
    369           FT_FREE( blend->avar_segment[j].correspondence );
    370 
    371         FT_FREE( blend->avar_segment );
    372         blend->avar_segment = NULL;
    373         goto Exit;
    374       }
    375 
    376       for ( j = 0; j < segment->pairCount; j++ )
    377       {
    378         /* convert to Fixed */
    379         segment->correspondence[j].fromCoord = FT_GET_SHORT() * 4;
    380         segment->correspondence[j].toCoord   = FT_GET_SHORT() * 4;
    381 
    382         FT_TRACE5(( "    mapping %.4f to %.4f\n",
    383                     segment->correspondence[j].fromCoord / 65536.0,
    384                     segment->correspondence[j].toCoord / 65536.0 ));
    385       }
    386 
    387       FT_TRACE5(( "\n" ));
    388     }
    389 
    390   Exit:
    391     FT_FRAME_EXIT();
    392   }
    393 
    394 
    395   typedef struct  GX_GVar_Head_
    396   {
    397     FT_Long    version;
    398     FT_UShort  axisCount;
    399     FT_UShort  globalCoordCount;
    400     FT_ULong   offsetToCoord;
    401     FT_UShort  glyphCount;
    402     FT_UShort  flags;
    403     FT_ULong   offsetToData;
    404 
    405   } GX_GVar_Head;
    406 
    407 
    408   /*************************************************************************/
    409   /*                                                                       */
    410   /* <Function>                                                            */
    411   /*    ft_var_load_gvar                                                   */
    412   /*                                                                       */
    413   /* <Description>                                                         */
    414   /*    Parse the `gvar' table if present.  If `fvar' is there, `gvar' had */
    415   /*    better be there too.                                               */
    416   /*                                                                       */
    417   /* <InOut>                                                               */
    418   /*    face :: The font face.                                             */
    419   /*                                                                       */
    420   /* <Return>                                                              */
    421   /*    FreeType error code.  0 means success.                             */
    422   /*                                                                       */
    423   static FT_Error
    424   ft_var_load_gvar( TT_Face  face )
    425   {
    426     FT_Stream     stream = FT_FACE_STREAM( face );
    427     FT_Memory     memory = stream->memory;
    428     GX_Blend      blend  = face->blend;
    429     FT_Error      error;
    430     FT_UInt       i, j;
    431     FT_ULong      table_len;
    432     FT_ULong      gvar_start;
    433     FT_ULong      offsetToData;
    434     GX_GVar_Head  gvar_head;
    435 
    436     static const FT_Frame_Field  gvar_fields[] =
    437     {
    438 
    439 #undef  FT_STRUCTURE
    440 #define FT_STRUCTURE  GX_GVar_Head
    441 
    442       FT_FRAME_START( 20 ),
    443         FT_FRAME_LONG  ( version ),
    444         FT_FRAME_USHORT( axisCount ),
    445         FT_FRAME_USHORT( globalCoordCount ),
    446         FT_FRAME_ULONG ( offsetToCoord ),
    447         FT_FRAME_USHORT( glyphCount ),
    448         FT_FRAME_USHORT( flags ),
    449         FT_FRAME_ULONG ( offsetToData ),
    450       FT_FRAME_END
    451     };
    452 
    453 
    454     FT_TRACE2(( "GVAR " ));
    455 
    456     if ( ( error = face->goto_table( face,
    457                                      TTAG_gvar,
    458                                      stream,
    459                                      &table_len ) ) != 0 )
    460     {
    461       FT_TRACE2(( "is missing\n" ));
    462       goto Exit;
    463     }
    464 
    465     gvar_start = FT_STREAM_POS( );
    466     if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
    467       goto Exit;
    468 
    469     if ( gvar_head.version != 0x00010000L )
    470     {
    471       FT_TRACE1(( "bad table version\n" ));
    472       error = FT_THROW( Invalid_Table );
    473       goto Exit;
    474     }
    475 
    476     if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
    477     {
    478       FT_TRACE1(( "ft_var_load_gvar: number of axes in `gvar' and `cvar'\n"
    479                   "                  table are different\n" ));
    480       error = FT_THROW( Invalid_Table );
    481       goto Exit;
    482     }
    483 
    484     /* rough sanity check, ignoring offsets */
    485     if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount >
    486            table_len / 2 )
    487     {
    488       FT_TRACE1(( "ft_var_load_gvar:"
    489                   " invalid number of global coordinates\n" ));
    490       error = FT_THROW( Invalid_Table );
    491       goto Exit;
    492     }
    493 
    494     /* rough sanity check: offsets can be either 2 or 4 bytes, */
    495     /* and a single variation needs at least 4 bytes per glyph */
    496     if ( (FT_ULong)gvar_head.glyphCount *
    497            ( ( gvar_head.flags & 1 ) ? 8 : 6 ) > table_len )
    498     {
    499       FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" ));
    500       error = FT_THROW( Invalid_Table );
    501       goto Exit;
    502     }
    503 
    504     FT_TRACE2(( "loaded\n" ));
    505 
    506     blend->gvar_size   = table_len;
    507     blend->tuplecount  = gvar_head.globalCoordCount;
    508     blend->gv_glyphcnt = gvar_head.glyphCount;
    509     offsetToData       = gvar_start + gvar_head.offsetToData;
    510 
    511     FT_TRACE5(( "gvar: there are %d shared coordinates:\n",
    512                 blend->tuplecount ));
    513 
    514     if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
    515       goto Exit;
    516 
    517     if ( gvar_head.flags & 1 )
    518     {
    519       /* long offsets (one more offset than glyphs, to mark size of last) */
    520       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
    521         goto Exit;
    522 
    523       for ( i = 0; i <= blend->gv_glyphcnt; i++ )
    524         blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG();
    525 
    526       FT_FRAME_EXIT();
    527     }
    528     else
    529     {
    530       /* short offsets (one more offset than glyphs, to mark size of last) */
    531       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
    532         goto Exit;
    533 
    534       for ( i = 0; i <= blend->gv_glyphcnt; i++ )
    535         blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
    536                                                /* XXX: Undocumented: `*2'! */
    537 
    538       FT_FRAME_EXIT();
    539     }
    540 
    541     if ( blend->tuplecount != 0 )
    542     {
    543       if ( FT_NEW_ARRAY( blend->tuplecoords,
    544                          gvar_head.axisCount * blend->tuplecount ) )
    545         goto Exit;
    546 
    547       if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord )         ||
    548            FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) )
    549         goto Exit;
    550 
    551       for ( i = 0; i < blend->tuplecount; i++ )
    552       {
    553         FT_TRACE5(( "  [ " ));
    554         for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; j++ )
    555         {
    556           blend->tuplecoords[i * gvar_head.axisCount + j] =
    557             FT_GET_SHORT() * 4;                 /* convert to FT_Fixed */
    558           FT_TRACE5(( "%.4f ",
    559             blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 ));
    560         }
    561         FT_TRACE5(( "]\n" ));
    562       }
    563 
    564       FT_TRACE5(( "\n" ));
    565 
    566       FT_FRAME_EXIT();
    567     }
    568 
    569   Exit:
    570     return error;
    571   }
    572 
    573 
    574   /*************************************************************************/
    575   /*                                                                       */
    576   /* <Function>                                                            */
    577   /*    ft_var_apply_tuple                                                 */
    578   /*                                                                       */
    579   /* <Description>                                                         */
    580   /*    Figure out whether a given tuple (design) applies to the current   */
    581   /*    blend, and if so, what is the scaling factor.                      */
    582   /*                                                                       */
    583   /* <Input>                                                               */
    584   /*    blend           :: The current blend of the font.                  */
    585   /*                                                                       */
    586   /*    tupleIndex      :: A flag saying whether this is an intermediate   */
    587   /*                       tuple or not.                                   */
    588   /*                                                                       */
    589   /*    tuple_coords    :: The coordinates of the tuple in normalized axis */
    590   /*                       units.                                          */
    591   /*                                                                       */
    592   /*    im_start_coords :: The initial coordinates where this tuple starts */
    593   /*                       to apply (for intermediate coordinates).        */
    594   /*                                                                       */
    595   /*    im_end_coords   :: The final coordinates after which this tuple no */
    596   /*                       longer applies (for intermediate coordinates).  */
    597   /*                                                                       */
    598   /* <Return>                                                              */
    599   /*    An FT_Fixed value containing the scaling factor.                   */
    600   /*                                                                       */
    601   static FT_Fixed
    602   ft_var_apply_tuple( GX_Blend   blend,
    603                       FT_UShort  tupleIndex,
    604                       FT_Fixed*  tuple_coords,
    605                       FT_Fixed*  im_start_coords,
    606                       FT_Fixed*  im_end_coords )
    607   {
    608     FT_UInt   i;
    609     FT_Fixed  apply = 0x10000L;
    610 
    611 
    612     for ( i = 0; i < blend->num_axis; i++ )
    613     {
    614       FT_TRACE6(( "    axis coordinate %d (%.4f):\n",
    615                   i, blend->normalizedcoords[i] / 65536.0 ));
    616 
    617       /* It's not clear why (for intermediate tuples) we don't need     */
    618       /* to check against start/end -- the documentation says we don't. */
    619       /* Similarly, it's unclear why we don't need to scale along the   */
    620       /* axis.                                                          */
    621 
    622       if ( tuple_coords[i] == 0 )
    623       {
    624         FT_TRACE6(( "      tuple coordinate is zero, ignored\n", i ));
    625         continue;
    626       }
    627 
    628       else if ( blend->normalizedcoords[i] == 0 )
    629       {
    630         FT_TRACE6(( "      axis coordinate is zero, stop\n" ));
    631         apply = 0;
    632         break;
    633       }
    634 
    635       else if ( ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) ||
    636                 ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) )
    637       {
    638         FT_TRACE6(( "      tuple coordinate value %.4f is exceeded, stop\n",
    639                     tuple_coords[i] / 65536.0 ));
    640         apply = 0;
    641         break;
    642       }
    643 
    644       else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
    645       {
    646         FT_TRACE6(( "      tuple coordinate value %.4f fits\n",
    647                     tuple_coords[i] / 65536.0 ));
    648         /* not an intermediate tuple */
    649         apply = FT_MulFix( apply,
    650                            blend->normalizedcoords[i] > 0
    651                              ? blend->normalizedcoords[i]
    652                              : -blend->normalizedcoords[i] );
    653       }
    654 
    655       else if ( blend->normalizedcoords[i] < im_start_coords[i] ||
    656                 blend->normalizedcoords[i] > im_end_coords[i]   )
    657       {
    658         FT_TRACE6(( "      intermediate tuple range [%.4f;%.4f] is exceeded,"
    659                     " stop\n",
    660                     im_start_coords[i] / 65536.0,
    661                     im_end_coords[i] / 65536.0 ));
    662         apply = 0;
    663         break;
    664       }
    665 
    666       else if ( blend->normalizedcoords[i] < tuple_coords[i] )
    667       {
    668         FT_TRACE6(( "      intermediate tuple range [%.4f;%.4f] fits\n",
    669                     im_start_coords[i] / 65536.0,
    670                     im_end_coords[i] / 65536.0 ));
    671         apply = FT_MulDiv( apply,
    672                            blend->normalizedcoords[i] - im_start_coords[i],
    673                            tuple_coords[i] - im_start_coords[i] );
    674       }
    675 
    676       else
    677       {
    678         FT_TRACE6(( "      intermediate tuple range [%.4f;%.4f] fits\n",
    679                     im_start_coords[i] / 65536.0,
    680                     im_end_coords[i] / 65536.0 ));
    681         apply = FT_MulDiv( apply,
    682                            im_end_coords[i] - blend->normalizedcoords[i],
    683                            im_end_coords[i] - tuple_coords[i] );
    684       }
    685     }
    686 
    687     FT_TRACE6(( "    apply factor is %.4f\n", apply / 65536.0 ));
    688 
    689     return apply;
    690   }
    691 
    692 
    693   /*************************************************************************/
    694   /*************************************************************************/
    695   /*****                                                               *****/
    696   /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
    697   /*****                                                               *****/
    698   /*************************************************************************/
    699   /*************************************************************************/
    700 
    701 
    702   typedef struct  GX_FVar_Head_
    703   {
    704     FT_Long    version;
    705     FT_UShort  offsetToData;
    706     FT_UShort  countSizePairs;
    707     FT_UShort  axisCount;
    708     FT_UShort  axisSize;
    709     FT_UShort  instanceCount;
    710     FT_UShort  instanceSize;
    711 
    712   } GX_FVar_Head;
    713 
    714 
    715   typedef struct  fvar_axis_
    716   {
    717     FT_ULong   axisTag;
    718     FT_Fixed   minValue;
    719     FT_Fixed   defaultValue;
    720     FT_Fixed   maxValue;
    721     FT_UShort  flags;
    722     FT_UShort  nameID;
    723 
    724   } GX_FVar_Axis;
    725 
    726 
    727   /*************************************************************************/
    728   /*                                                                       */
    729   /* <Function>                                                            */
    730   /*    TT_Get_MM_Var                                                      */
    731   /*                                                                       */
    732   /* <Description>                                                         */
    733   /*    Check that the font's `fvar' table is valid, parse it, and return  */
    734   /*    those data.                                                        */
    735   /*                                                                       */
    736   /* <InOut>                                                               */
    737   /*    face   :: The font face.                                           */
    738   /*              TT_Get_MM_Var initializes the blend structure.           */
    739   /*                                                                       */
    740   /* <Output>                                                              */
    741   /*    master :: The `fvar' data (must be freed by caller).  Can be NULL, */
    742   /*              which makes this function simply load MM support.        */
    743   /*                                                                       */
    744   /* <Return>                                                              */
    745   /*    FreeType error code.  0 means success.                             */
    746   /*                                                                       */
    747   FT_LOCAL_DEF( FT_Error )
    748   TT_Get_MM_Var( TT_Face      face,
    749                  FT_MM_Var*  *master )
    750   {
    751     FT_Stream            stream = face->root.stream;
    752     FT_Memory            memory = face->root.memory;
    753     FT_ULong             table_len;
    754     FT_Error             error  = FT_Err_Ok;
    755     FT_ULong             fvar_start;
    756     FT_Int               i, j;
    757     FT_MM_Var*           mmvar = NULL;
    758     FT_Fixed*            next_coords;
    759     FT_String*           next_name;
    760     FT_Var_Axis*         a;
    761     FT_Var_Named_Style*  ns;
    762     GX_FVar_Head         fvar_head;
    763 
    764     static const FT_Frame_Field  fvar_fields[] =
    765     {
    766 
    767 #undef  FT_STRUCTURE
    768 #define FT_STRUCTURE  GX_FVar_Head
    769 
    770       FT_FRAME_START( 16 ),
    771         FT_FRAME_LONG  ( version ),
    772         FT_FRAME_USHORT( offsetToData ),
    773         FT_FRAME_USHORT( countSizePairs ),
    774         FT_FRAME_USHORT( axisCount ),
    775         FT_FRAME_USHORT( axisSize ),
    776         FT_FRAME_USHORT( instanceCount ),
    777         FT_FRAME_USHORT( instanceSize ),
    778       FT_FRAME_END
    779     };
    780 
    781     static const FT_Frame_Field  fvaraxis_fields[] =
    782     {
    783 
    784 #undef  FT_STRUCTURE
    785 #define FT_STRUCTURE  GX_FVar_Axis
    786 
    787       FT_FRAME_START( 20 ),
    788         FT_FRAME_ULONG ( axisTag ),
    789         FT_FRAME_LONG  ( minValue ),
    790         FT_FRAME_LONG  ( defaultValue ),
    791         FT_FRAME_LONG  ( maxValue ),
    792         FT_FRAME_USHORT( flags ),
    793         FT_FRAME_USHORT( nameID ),
    794       FT_FRAME_END
    795     };
    796 
    797 
    798     /* read the font data and set up the internal representation */
    799     /* if not already done                                       */
    800 
    801     if ( face->blend == NULL )
    802     {
    803       FT_TRACE2(( "FVAR " ));
    804 
    805       /* both `fvar' and `gvar' must be present */
    806       if ( ( error = face->goto_table( face, TTAG_gvar,
    807                                        stream, &table_len ) ) != 0 )
    808       {
    809         FT_TRACE1(( "\n"
    810                     "TT_Get_MM_Var: `gvar' table is missing\n" ));
    811         goto Exit;
    812       }
    813 
    814       if ( ( error = face->goto_table( face, TTAG_fvar,
    815                                        stream, &table_len ) ) != 0 )
    816       {
    817         FT_TRACE1(( "is missing\n" ));
    818         goto Exit;
    819       }
    820 
    821       fvar_start = FT_STREAM_POS( );
    822 
    823       if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
    824         goto Exit;
    825 
    826       if ( fvar_head.version != (FT_Long)0x00010000L                      ||
    827 #if 0
    828            /* fonts like `JamRegular.ttf' have an incorrect value for */
    829            /* `countSizePairs'; since value 2 is hard-coded in `fvar' */
    830            /* version 1.0, we simply ignore it                        */
    831            fvar_head.countSizePairs != 2                                  ||
    832 #endif
    833            fvar_head.axisSize != 20                                       ||
    834            /* axisCount limit implied by 16-bit instanceSize */
    835            fvar_head.axisCount > 0x3FFE                                   ||
    836            fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount          ||
    837            /* instanceCount limit implied by limited range of name IDs */
    838            fvar_head.instanceCount > 0x7EFF                               ||
    839            fvar_head.offsetToData + fvar_head.axisCount * 20U +
    840              fvar_head.instanceCount * fvar_head.instanceSize > table_len )
    841       {
    842         FT_TRACE1(( "\n"
    843                     "TT_Get_MM_Var: invalid `fvar' header\n" ));
    844         error = FT_THROW( Invalid_Table );
    845         goto Exit;
    846       }
    847 
    848       FT_TRACE2(( "loaded\n" ));
    849 
    850       FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount ));
    851 
    852       if ( FT_NEW( face->blend ) )
    853         goto Exit;
    854 
    855       /* cannot overflow 32-bit arithmetic because of limits above */
    856       face->blend->mmvar_len =
    857         sizeof ( FT_MM_Var ) +
    858         fvar_head.axisCount * sizeof ( FT_Var_Axis ) +
    859         fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) +
    860         fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) +
    861         5 * fvar_head.axisCount;
    862 
    863       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
    864         goto Exit;
    865       face->blend->mmvar = mmvar;
    866 
    867       /* set up pointers and offsets into the `mmvar' array; */
    868       /* the data gets filled in later on                    */
    869 
    870       mmvar->num_axis =
    871         fvar_head.axisCount;
    872       mmvar->num_designs =
    873         ~0U;                   /* meaningless in this context; each glyph */
    874                                /* may have a different number of designs  */
    875                                /* (or tuples, as called by Apple)         */
    876       mmvar->num_namedstyles =
    877         fvar_head.instanceCount;
    878       mmvar->axis =
    879         (FT_Var_Axis*)&( mmvar[1] );
    880       mmvar->namedstyle =
    881         (FT_Var_Named_Style*)&( mmvar->axis[fvar_head.axisCount] );
    882 
    883       next_coords =
    884         (FT_Fixed*)&( mmvar->namedstyle[fvar_head.instanceCount] );
    885       for ( i = 0; i < fvar_head.instanceCount; i++ )
    886       {
    887         mmvar->namedstyle[i].coords  = next_coords;
    888         next_coords                 += fvar_head.axisCount;
    889       }
    890 
    891       next_name = (FT_String*)next_coords;
    892       for ( i = 0; i < fvar_head.axisCount; i++ )
    893       {
    894         mmvar->axis[i].name  = next_name;
    895         next_name           += 5;
    896       }
    897 
    898       /* now fill in the data */
    899 
    900       if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
    901         goto Exit;
    902 
    903       a = mmvar->axis;
    904       for ( i = 0; i < fvar_head.axisCount; i++ )
    905       {
    906         GX_FVar_Axis  axis_rec;
    907 
    908 
    909         if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
    910           goto Exit;
    911         a->tag     = axis_rec.axisTag;
    912         a->minimum = axis_rec.minValue;
    913         a->def     = axis_rec.defaultValue;
    914         a->maximum = axis_rec.maxValue;
    915         a->strid   = axis_rec.nameID;
    916 
    917         a->name[0] = (FT_String)(   a->tag >> 24 );
    918         a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
    919         a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
    920         a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
    921         a->name[4] = '\0';
    922 
    923         FT_TRACE5(( "  \"%s\": minimum=%.4f, default=%.4f, maximum=%.4f\n",
    924                     a->name,
    925                     a->minimum / 65536.0,
    926                     a->def / 65536.0,
    927                     a->maximum / 65536.0 ));
    928 
    929         a++;
    930       }
    931 
    932       FT_TRACE5(( "\n" ));
    933 
    934       ns = mmvar->namedstyle;
    935       for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
    936       {
    937         if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
    938           goto Exit;
    939 
    940         ns->strid       =    FT_GET_USHORT();
    941         (void) /* flags = */ FT_GET_USHORT();
    942 
    943         for ( j = 0; j < fvar_head.axisCount; j++ )
    944           ns->coords[j] = FT_GET_LONG();
    945 
    946         FT_FRAME_EXIT();
    947       }
    948     }
    949 
    950     /* fill the output array if requested */
    951 
    952     if ( master != NULL )
    953     {
    954       FT_UInt  n;
    955 
    956 
    957       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
    958         goto Exit;
    959       FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
    960 
    961       mmvar->axis =
    962         (FT_Var_Axis*)&( mmvar[1] );
    963       mmvar->namedstyle =
    964         (FT_Var_Named_Style*)&( mmvar->axis[mmvar->num_axis] );
    965       next_coords =
    966         (FT_Fixed*)&( mmvar->namedstyle[mmvar->num_namedstyles] );
    967 
    968       for ( n = 0; n < mmvar->num_namedstyles; n++ )
    969       {
    970         mmvar->namedstyle[n].coords  = next_coords;
    971         next_coords                 += mmvar->num_axis;
    972       }
    973 
    974       a         = mmvar->axis;
    975       next_name = (FT_String*)next_coords;
    976       for ( n = 0; n < mmvar->num_axis; n++ )
    977       {
    978         a->name = next_name;
    979 
    980         /* standard PostScript names for some standard apple tags */
    981         if ( a->tag == TTAG_wght )
    982           a->name = (char*)"Weight";
    983         else if ( a->tag == TTAG_wdth )
    984           a->name = (char*)"Width";
    985         else if ( a->tag == TTAG_opsz )
    986           a->name = (char*)"OpticalSize";
    987         else if ( a->tag == TTAG_slnt )
    988           a->name = (char*)"Slant";
    989 
    990         next_name += 5;
    991         a++;
    992       }
    993 
    994       *master = mmvar;
    995     }
    996 
    997   Exit:
    998     return error;
    999   }
   1000 
   1001 
   1002   /*************************************************************************/
   1003   /*                                                                       */
   1004   /* <Function>                                                            */
   1005   /*    TT_Set_MM_Blend                                                    */
   1006   /*                                                                       */
   1007   /* <Description>                                                         */
   1008   /*    Set the blend (normalized) coordinates for this instance of the    */
   1009   /*    font.  Check that the `gvar' table is reasonable and does some     */
   1010   /*    initial preparation.                                               */
   1011   /*                                                                       */
   1012   /* <InOut>                                                               */
   1013   /*    face       :: The font.                                            */
   1014   /*                  Initialize the blend structure with `gvar' data.     */
   1015   /*                                                                       */
   1016   /* <Input>                                                               */
   1017   /*    num_coords :: The number of available coordinates.  If it is       */
   1018   /*                  larger than the number of axes, ignore the excess    */
   1019   /*                  values.  If it is smaller than the number of axes,   */
   1020   /*                  use the default value (0) for the remaining axes.    */
   1021   /*                                                                       */
   1022   /*    coords     :: An array of `num_coords', each between [-1,1].       */
   1023   /*                                                                       */
   1024   /* <Return>                                                              */
   1025   /*    FreeType error code.  0 means success.                             */
   1026   /*                                                                       */
   1027   FT_LOCAL_DEF( FT_Error )
   1028   TT_Set_MM_Blend( TT_Face    face,
   1029                    FT_UInt    num_coords,
   1030                    FT_Fixed*  coords )
   1031   {
   1032     FT_Error    error = FT_Err_Ok;
   1033     GX_Blend    blend;
   1034     FT_MM_Var*  mmvar;
   1035     FT_UInt     i;
   1036     FT_Memory   memory = face->root.memory;
   1037 
   1038     enum
   1039     {
   1040       mcvt_retain,
   1041       mcvt_modify,
   1042       mcvt_load
   1043 
   1044     } manageCvt;
   1045 
   1046 
   1047     face->doblend = FALSE;
   1048 
   1049     if ( face->blend == NULL )
   1050     {
   1051       if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 )
   1052         goto Exit;
   1053     }
   1054 
   1055     blend = face->blend;
   1056     mmvar = blend->mmvar;
   1057 
   1058     if ( num_coords > mmvar->num_axis )
   1059     {
   1060       FT_TRACE2(( "TT_Set_MM_Blend: only using first %d of %d coordinates\n",
   1061                   mmvar->num_axis, num_coords ));
   1062       num_coords = mmvar->num_axis;
   1063     }
   1064 
   1065     FT_TRACE5(( "normalized design coordinates:\n" ));
   1066 
   1067     for ( i = 0; i < num_coords; i++ )
   1068     {
   1069       FT_TRACE5(( "  %.4f\n", coords[i] / 65536.0 ));
   1070       if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
   1071       {
   1072         FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.4f\n"
   1073                     "                 is out of range [-1;1]\n",
   1074                     coords[i] / 65536.0 ));
   1075         error = FT_THROW( Invalid_Argument );
   1076         goto Exit;
   1077       }
   1078     }
   1079 
   1080     FT_TRACE5(( "\n" ));
   1081 
   1082     if ( blend->glyphoffsets == NULL )
   1083       if ( ( error = ft_var_load_gvar( face ) ) != 0 )
   1084         goto Exit;
   1085 
   1086     if ( blend->normalizedcoords == NULL )
   1087     {
   1088       if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) )
   1089         goto Exit;
   1090 
   1091       manageCvt = mcvt_modify;
   1092 
   1093       /* If we have not set the blend coordinates before this, then the  */
   1094       /* cvt table will still be what we read from the `cvt ' table and  */
   1095       /* we don't need to reload it.  We may need to change it though... */
   1096     }
   1097     else
   1098     {
   1099       manageCvt = mcvt_retain;
   1100 
   1101       for ( i = 0; i < num_coords; i++ )
   1102       {
   1103         if ( blend->normalizedcoords[i] != coords[i] )
   1104         {
   1105           manageCvt = mcvt_load;
   1106           break;
   1107         }
   1108       }
   1109 
   1110       for ( ; i < mmvar->num_axis; i++ )
   1111       {
   1112         if ( blend->normalizedcoords[i] != 0 )
   1113         {
   1114           manageCvt = mcvt_load;
   1115           break;
   1116         }
   1117       }
   1118 
   1119       /* If we don't change the blend coords then we don't need to do  */
   1120       /* anything to the cvt table.  It will be correct.  Otherwise we */
   1121       /* no longer have the original cvt (it was modified when we set  */
   1122       /* the blend last time), so we must reload and then modify it.   */
   1123     }
   1124 
   1125     blend->num_axis = mmvar->num_axis;
   1126     FT_MEM_COPY( blend->normalizedcoords,
   1127                  coords,
   1128                  num_coords * sizeof ( FT_Fixed ) );
   1129 
   1130     face->doblend = TRUE;
   1131 
   1132     if ( face->cvt != NULL )
   1133     {
   1134       switch ( manageCvt )
   1135       {
   1136       case mcvt_load:
   1137         /* The cvt table has been loaded already; every time we change the */
   1138         /* blend we may need to reload and remodify the cvt table.         */
   1139         FT_FREE( face->cvt );
   1140         face->cvt = NULL;
   1141 
   1142         error = tt_face_load_cvt( face, face->root.stream );
   1143         break;
   1144 
   1145       case mcvt_modify:
   1146         /* The original cvt table is in memory.  All we need to do is */
   1147         /* apply the `cvar' table (if any).                           */
   1148         error = tt_face_vary_cvt( face, face->root.stream );
   1149         break;
   1150 
   1151       case mcvt_retain:
   1152         /* The cvt table is correct for this set of coordinates. */
   1153         break;
   1154       }
   1155     }
   1156 
   1157   Exit:
   1158     return error;
   1159   }
   1160 
   1161 
   1162   /*************************************************************************/
   1163   /*                                                                       */
   1164   /* <Function>                                                            */
   1165   /*    TT_Set_Var_Design                                                  */
   1166   /*                                                                       */
   1167   /* <Description>                                                         */
   1168   /*    Set the coordinates for the instance, measured in the user         */
   1169   /*    coordinate system.  Parse the `avar' table (if present) to convert */
   1170   /*    from user to normalized coordinates.                               */
   1171   /*                                                                       */
   1172   /* <InOut>                                                               */
   1173   /*    face       :: The font face.                                       */
   1174   /*                  Initialize the blend struct with `gvar' data.        */
   1175   /*                                                                       */
   1176   /* <Input>                                                               */
   1177   /*    num_coords :: The number of available coordinates.  If it is       */
   1178   /*                  larger than the number of axes, ignore the excess    */
   1179   /*                  values.  If it is smaller than the number of axes,   */
   1180   /*                  use the default values for the remaining axes.       */
   1181   /*                                                                       */
   1182   /*    coords     :: A coordinate array with `num_coords' elements.       */
   1183   /*                                                                       */
   1184   /* <Return>                                                              */
   1185   /*    FreeType error code.  0 means success.                             */
   1186   /*                                                                       */
   1187   FT_LOCAL_DEF( FT_Error )
   1188   TT_Set_Var_Design( TT_Face    face,
   1189                      FT_UInt    num_coords,
   1190                      FT_Fixed*  coords )
   1191   {
   1192     FT_Error        error      = FT_Err_Ok;
   1193     FT_Fixed*       normalized = NULL;
   1194     GX_Blend        blend;
   1195     FT_MM_Var*      mmvar;
   1196     FT_UInt         i, j;
   1197     FT_Var_Axis*    a;
   1198     GX_AVarSegment  av;
   1199     FT_Memory       memory = face->root.memory;
   1200 
   1201 
   1202     if ( face->blend == NULL )
   1203     {
   1204       if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 )
   1205         goto Exit;
   1206     }
   1207 
   1208     blend = face->blend;
   1209     mmvar = blend->mmvar;
   1210 
   1211     if ( num_coords > mmvar->num_axis )
   1212     {
   1213       FT_TRACE2(( "TT_Set_Var_Design:"
   1214                   " only using first %d of %d coordinates\n",
   1215                   mmvar->num_axis, num_coords ));
   1216       num_coords = mmvar->num_axis;
   1217     }
   1218 
   1219     /* Axis normalization is a two stage process.  First we normalize */
   1220     /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
   1221     /* Then, if there's an `avar' table, we renormalize this range.   */
   1222 
   1223     if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
   1224       goto Exit;
   1225 
   1226     FT_TRACE5(( "design coordinates:\n" ));
   1227 
   1228     a = mmvar->axis;
   1229     for ( i = 0; i < num_coords; i++, a++ )
   1230     {
   1231       FT_TRACE5(( "  %.4f\n", coords[i] / 65536.0 ));
   1232       if ( coords[i] > a->maximum || coords[i] < a->minimum )
   1233       {
   1234         FT_TRACE1(( "TT_Set_Var_Design: normalized design coordinate %.4f\n"
   1235                     "                   is out of range [%.4f;%.4f]\n",
   1236                     coords[i] / 65536.0,
   1237                     a->minimum / 65536.0,
   1238                     a->maximum / 65536.0 ));
   1239         error = FT_THROW( Invalid_Argument );
   1240         goto Exit;
   1241       }
   1242 
   1243       if ( coords[i] < a->def )
   1244         normalized[i] = -FT_DivFix( coords[i] - a->def,
   1245                                     a->minimum - a->def );
   1246       else if ( a->maximum == a->def )
   1247         normalized[i] = 0;
   1248       else
   1249         normalized[i] = FT_DivFix( coords[i] - a->def,
   1250                                    a->maximum - a->def );
   1251     }
   1252 
   1253     FT_TRACE5(( "\n" ));
   1254 
   1255     for ( ; i < mmvar->num_axis; i++ )
   1256       normalized[i] = 0;
   1257 
   1258     if ( !blend->avar_checked )
   1259       ft_var_load_avar( face );
   1260 
   1261     if ( blend->avar_segment != NULL )
   1262     {
   1263       FT_TRACE5(( "normalized design coordinates"
   1264                   " before applying `avar' data:\n" ));
   1265 
   1266       av = blend->avar_segment;
   1267       for ( i = 0; i < mmvar->num_axis; i++, av++ )
   1268       {
   1269         for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
   1270         {
   1271           FT_TRACE5(( "  %.4f\n", normalized[i] / 65536.0 ));
   1272           if ( normalized[i] < av->correspondence[j].fromCoord )
   1273           {
   1274             normalized[i] =
   1275               FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
   1276                          av->correspondence[j].toCoord -
   1277                            av->correspondence[j - 1].toCoord,
   1278                          av->correspondence[j].fromCoord -
   1279                            av->correspondence[j - 1].fromCoord ) +
   1280               av->correspondence[j - 1].toCoord;
   1281             break;
   1282           }
   1283         }
   1284       }
   1285     }
   1286 
   1287     error = TT_Set_MM_Blend( face, mmvar->num_axis, normalized );
   1288 
   1289   Exit:
   1290     FT_FREE( normalized );
   1291     return error;
   1292   }
   1293 
   1294 
   1295   /*************************************************************************/
   1296   /*************************************************************************/
   1297   /*****                                                               *****/
   1298   /*****                     GX VAR PARSING ROUTINES                   *****/
   1299   /*****                                                               *****/
   1300   /*************************************************************************/
   1301   /*************************************************************************/
   1302 
   1303 
   1304   /*************************************************************************/
   1305   /*                                                                       */
   1306   /* <Function>                                                            */
   1307   /*    tt_face_vary_cvt                                                   */
   1308   /*                                                                       */
   1309   /* <Description>                                                         */
   1310   /*    Modify the loaded cvt table according to the `cvar' table and the  */
   1311   /*    font's blend.                                                      */
   1312   /*                                                                       */
   1313   /* <InOut>                                                               */
   1314   /*    face   :: A handle to the target face object.                      */
   1315   /*                                                                       */
   1316   /* <Input>                                                               */
   1317   /*    stream :: A handle to the input stream.                            */
   1318   /*                                                                       */
   1319   /* <Return>                                                              */
   1320   /*    FreeType error code.  0 means success.                             */
   1321   /*                                                                       */
   1322   /*    Most errors are ignored.  It is perfectly valid not to have a      */
   1323   /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
   1324   /*                                                                       */
   1325   FT_LOCAL_DEF( FT_Error )
   1326   tt_face_vary_cvt( TT_Face    face,
   1327                     FT_Stream  stream )
   1328   {
   1329     FT_Error    error;
   1330     FT_Memory   memory = stream->memory;
   1331     FT_ULong    table_start;
   1332     FT_ULong    table_len;
   1333     FT_UInt     tupleCount;
   1334     FT_ULong    offsetToData;
   1335     FT_ULong    here;
   1336     FT_UInt     i, j;
   1337     FT_Fixed*   tuple_coords    = NULL;
   1338     FT_Fixed*   im_start_coords = NULL;
   1339     FT_Fixed*   im_end_coords   = NULL;
   1340     GX_Blend    blend           = face->blend;
   1341     FT_UInt     point_count;
   1342     FT_UShort*  localpoints;
   1343     FT_Short*   deltas;
   1344 
   1345 
   1346     FT_TRACE2(( "CVAR " ));
   1347 
   1348     if ( blend == NULL )
   1349     {
   1350       FT_TRACE2(( "\n"
   1351                   "tt_face_vary_cvt: no blend specified\n" ));
   1352       error = FT_Err_Ok;
   1353       goto Exit;
   1354     }
   1355 
   1356     if ( face->cvt == NULL )
   1357     {
   1358       FT_TRACE2(( "\n"
   1359                   "tt_face_vary_cvt: no `cvt ' table\n" ));
   1360       error = FT_Err_Ok;
   1361       goto Exit;
   1362     }
   1363 
   1364     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
   1365     if ( error )
   1366     {
   1367       FT_TRACE2(( "is missing\n" ));
   1368 
   1369       error = FT_Err_Ok;
   1370       goto Exit;
   1371     }
   1372 
   1373     if ( FT_FRAME_ENTER( table_len ) )
   1374     {
   1375       error = FT_Err_Ok;
   1376       goto Exit;
   1377     }
   1378 
   1379     table_start = FT_Stream_FTell( stream );
   1380     if ( FT_GET_LONG() != 0x00010000L )
   1381     {
   1382       FT_TRACE2(( "bad table version\n" ));
   1383 
   1384       error = FT_Err_Ok;
   1385       goto FExit;
   1386     }
   1387 
   1388     FT_TRACE2(( "loaded\n" ));
   1389 
   1390     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
   1391          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
   1392          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
   1393       goto FExit;
   1394 
   1395     tupleCount   = FT_GET_USHORT();
   1396     offsetToData = FT_GET_USHORT();
   1397 
   1398     /* rough sanity test */
   1399     if ( offsetToData + tupleCount * 4 > table_len )
   1400     {
   1401       FT_TRACE2(( "tt_face_vary_cvt:"
   1402                   " invalid CVT variation array header\n" ));
   1403 
   1404       error = FT_THROW( Invalid_Table );
   1405       goto FExit;
   1406     }
   1407 
   1408     offsetToData += table_start;
   1409 
   1410     /* The documentation implies there are flags packed into              */
   1411     /* `tupleCount', but John Jenkins says that shared points don't apply */
   1412     /* to `cvar', and no other flags are defined.                         */
   1413 
   1414     FT_TRACE5(( "cvar: there are %d tuples:\n", tupleCount & 0xFFF ));
   1415 
   1416     for ( i = 0; i < ( tupleCount & 0xFFF ); i++ )
   1417     {
   1418       FT_UInt   tupleDataSize;
   1419       FT_UInt   tupleIndex;
   1420       FT_Fixed  apply;
   1421 
   1422 
   1423       FT_TRACE6(( "  tuple %d:\n", i ));
   1424 
   1425       tupleDataSize = FT_GET_USHORT();
   1426       tupleIndex    = FT_GET_USHORT();
   1427 
   1428       /* There is no provision here for a global tuple coordinate section, */
   1429       /* so John says.  There are no tuple indices, just embedded tuples.  */
   1430 
   1431       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
   1432       {
   1433         for ( j = 0; j < blend->num_axis; j++ )
   1434           tuple_coords[j] = FT_GET_SHORT() * 4;  /* convert from        */
   1435                                                  /* short frac to fixed */
   1436       }
   1437       else
   1438       {
   1439         /* skip this tuple; it makes no sense */
   1440 
   1441         if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
   1442           for ( j = 0; j < 2 * blend->num_axis; j++ )
   1443             (void)FT_GET_SHORT();
   1444 
   1445         offsetToData += tupleDataSize;
   1446         continue;
   1447       }
   1448 
   1449       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
   1450       {
   1451         for ( j = 0; j < blend->num_axis; j++ )
   1452           im_start_coords[j] = FT_GET_SHORT() * 4;
   1453         for ( j = 0; j < blend->num_axis; j++ )
   1454           im_end_coords[j] = FT_GET_SHORT() * 4;
   1455       }
   1456 
   1457       apply = ft_var_apply_tuple( blend,
   1458                                   (FT_UShort)tupleIndex,
   1459                                   tuple_coords,
   1460                                   im_start_coords,
   1461                                   im_end_coords );
   1462       if ( /* tuple isn't active for our blend */
   1463            apply == 0                                    ||
   1464            /* global points not allowed,           */
   1465            /* if they aren't local, makes no sense */
   1466            !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
   1467       {
   1468         offsetToData += tupleDataSize;
   1469         continue;
   1470       }
   1471 
   1472       here = FT_Stream_FTell( stream );
   1473 
   1474       FT_Stream_SeekSet( stream, offsetToData );
   1475 
   1476       localpoints = ft_var_readpackedpoints( stream,
   1477                                              table_len,
   1478                                              &point_count );
   1479       deltas      = ft_var_readpackeddeltas( stream,
   1480                                              table_len,
   1481                                              point_count == 0 ? face->cvt_size
   1482                                                               : point_count );
   1483       if ( localpoints == NULL || deltas == NULL )
   1484         ; /* failure, ignore it */
   1485 
   1486       else if ( localpoints == ALL_POINTS )
   1487       {
   1488 #ifdef FT_DEBUG_LEVEL_TRACE
   1489         int  count = 0;
   1490 #endif
   1491 
   1492 
   1493         FT_TRACE7(( "    CVT deltas:\n" ));
   1494 
   1495         /* this means that there are deltas for every entry in cvt */
   1496         for ( j = 0; j < face->cvt_size; j++ )
   1497         {
   1498           FT_Long  orig_cvt = face->cvt[j];
   1499 
   1500 
   1501           face->cvt[j] = (FT_Short)( orig_cvt +
   1502                                      FT_MulFix( deltas[j], apply ) );
   1503 
   1504 #ifdef FT_DEBUG_LEVEL_TRACE
   1505           if ( orig_cvt != face->cvt[j] )
   1506           {
   1507             FT_TRACE7(( "      %d: %d -> %d\n",
   1508                         j, orig_cvt, face->cvt[j] ));
   1509             count++;
   1510           }
   1511 #endif
   1512         }
   1513 
   1514 #ifdef FT_DEBUG_LEVEL_TRACE
   1515         if ( !count )
   1516           FT_TRACE7(( "      none\n" ));
   1517 #endif
   1518       }
   1519 
   1520       else
   1521       {
   1522 #ifdef FT_DEBUG_LEVEL_TRACE
   1523         int  count = 0;
   1524 #endif
   1525 
   1526 
   1527         FT_TRACE7(( "    CVT deltas:\n" ));
   1528 
   1529         for ( j = 0; j < point_count; j++ )
   1530         {
   1531           int      pindex   = localpoints[j];
   1532           FT_Long  orig_cvt = face->cvt[pindex];
   1533 
   1534 
   1535           face->cvt[pindex] = (FT_Short)( orig_cvt +
   1536                                           FT_MulFix( deltas[j], apply ) );
   1537 
   1538 #ifdef FT_DEBUG_LEVEL_TRACE
   1539           if ( orig_cvt != face->cvt[pindex] )
   1540           {
   1541             FT_TRACE7(( "      %d: %d -> %d\n",
   1542                         pindex, orig_cvt, face->cvt[pindex] ));
   1543             count++;
   1544           }
   1545 #endif
   1546         }
   1547 
   1548 #ifdef FT_DEBUG_LEVEL_TRACE
   1549         if ( !count )
   1550           FT_TRACE7(( "      none\n" ));
   1551 #endif
   1552       }
   1553 
   1554       if ( localpoints != ALL_POINTS )
   1555         FT_FREE( localpoints );
   1556       FT_FREE( deltas );
   1557 
   1558       offsetToData += tupleDataSize;
   1559 
   1560       FT_Stream_SeekSet( stream, here );
   1561     }
   1562 
   1563     FT_TRACE5(( "\n" ));
   1564 
   1565   FExit:
   1566     FT_FRAME_EXIT();
   1567 
   1568   Exit:
   1569     FT_FREE( tuple_coords );
   1570     FT_FREE( im_start_coords );
   1571     FT_FREE( im_end_coords );
   1572 
   1573     return error;
   1574   }
   1575 
   1576 
   1577   /* Shift the original coordinates of all points between indices `p1' */
   1578   /* and `p2', using the same difference as given by index `ref'.      */
   1579 
   1580   /* modeled after `af_iup_shift' */
   1581 
   1582   static void
   1583   tt_delta_shift( int         p1,
   1584                   int         p2,
   1585                   int         ref,
   1586                   FT_Vector*  in_points,
   1587                   FT_Vector*  out_points )
   1588   {
   1589     int        p;
   1590     FT_Vector  delta;
   1591 
   1592 
   1593     delta.x = out_points[ref].x - in_points[ref].x;
   1594     delta.y = out_points[ref].y - in_points[ref].y;
   1595 
   1596     if ( delta.x == 0 && delta.y == 0 )
   1597       return;
   1598 
   1599     for ( p = p1; p < ref; p++ )
   1600     {
   1601       out_points[p].x += delta.x;
   1602       out_points[p].y += delta.y;
   1603     }
   1604 
   1605     for ( p = ref + 1; p <= p2; p++ )
   1606     {
   1607       out_points[p].x += delta.x;
   1608       out_points[p].y += delta.y;
   1609     }
   1610   }
   1611 
   1612 
   1613   /* Interpolate the original coordinates of all points with indices */
   1614   /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
   1615   /* point indices.                                                  */
   1616 
   1617   /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */
   1618   /* `Ins_IUP'                                                     */
   1619 
   1620   static void
   1621   tt_delta_interpolate( int         p1,
   1622                         int         p2,
   1623                         int         ref1,
   1624                         int         ref2,
   1625                         FT_Vector*  in_points,
   1626                         FT_Vector*  out_points )
   1627   {
   1628     int  p, i;
   1629 
   1630     FT_Pos  out, in1, in2, out1, out2, d1, d2;
   1631 
   1632 
   1633     if ( p1 > p2 )
   1634       return;
   1635 
   1636     /* handle both horizontal and vertical coordinates */
   1637     for ( i = 0; i <= 1; i++ )
   1638     {
   1639       /* shift array pointers so that we can access `foo.y' as `foo.x' */
   1640       in_points  = (FT_Vector*)( (FT_Pos*)in_points + i );
   1641       out_points = (FT_Vector*)( (FT_Pos*)out_points + i );
   1642 
   1643       if ( in_points[ref1].x > in_points[ref2].x )
   1644       {
   1645         p    = ref1;
   1646         ref1 = ref2;
   1647         ref2 = p;
   1648       }
   1649 
   1650       in1  = in_points[ref1].x;
   1651       in2  = in_points[ref2].x;
   1652       out1 = out_points[ref1].x;
   1653       out2 = out_points[ref2].x;
   1654       d1   = out1 - in1;
   1655       d2   = out2 - in2;
   1656 
   1657       if ( out1 == out2 || in1 == in2 )
   1658       {
   1659         for ( p = p1; p <= p2; p++ )
   1660         {
   1661           out = in_points[p].x;
   1662 
   1663           if ( out <= in1 )
   1664             out += d1;
   1665           else if ( out >= in2 )
   1666             out += d2;
   1667           else
   1668             out = out1;
   1669 
   1670           out_points[p].x = out;
   1671         }
   1672       }
   1673       else
   1674       {
   1675         FT_Fixed  scale = FT_DivFix( out2 - out1, in2 - in1 );
   1676 
   1677 
   1678         for ( p = p1; p <= p2; p++ )
   1679         {
   1680           out = in_points[p].x;
   1681 
   1682           if ( out <= in1 )
   1683             out += d1;
   1684           else if ( out >= in2 )
   1685             out += d2;
   1686           else
   1687             out = out1 + FT_MulFix( out - in1, scale );
   1688 
   1689           out_points[p].x = out;
   1690         }
   1691       }
   1692     }
   1693   }
   1694 
   1695 
   1696   /* Interpolate points without delta values, similar to */
   1697   /* the `IUP' hinting instruction.                      */
   1698 
   1699   /* modeled after `Ins_IUP */
   1700 
   1701   static void
   1702   tt_handle_deltas( FT_Outline*  outline,
   1703                     FT_Vector*   in_points,
   1704                     FT_Bool*     has_delta )
   1705   {
   1706     FT_Vector*  out_points;
   1707 
   1708     FT_Int  first_point;
   1709     FT_Int  end_point;
   1710 
   1711     FT_Int  first_delta;
   1712     FT_Int  cur_delta;
   1713 
   1714     FT_Int    point;
   1715     FT_Short  contour;
   1716 
   1717 
   1718     /* ignore empty outlines */
   1719     if ( !outline->n_contours )
   1720       return;
   1721 
   1722     out_points = outline->points;
   1723 
   1724     contour = 0;
   1725     point   = 0;
   1726 
   1727     do
   1728     {
   1729       end_point   = outline->contours[contour];
   1730       first_point = point;
   1731 
   1732       /* search first point that has a delta */
   1733       while ( point <= end_point && !has_delta[point] )
   1734         point++;
   1735 
   1736       if ( point <= end_point )
   1737       {
   1738         first_delta = point;
   1739         cur_delta   = point;
   1740 
   1741         point++;
   1742 
   1743         while ( point <= end_point )
   1744         {
   1745           /* search next point that has a delta  */
   1746           /* and interpolate intermediate points */
   1747           if ( has_delta[point] )
   1748           {
   1749             tt_delta_interpolate( cur_delta + 1,
   1750                                   point - 1,
   1751                                   cur_delta,
   1752                                   point,
   1753                                   in_points,
   1754                                   out_points );
   1755             cur_delta = point;
   1756           }
   1757 
   1758           point++;
   1759         }
   1760 
   1761         /* shift contour if we only have a single delta */
   1762         if ( cur_delta == first_delta )
   1763           tt_delta_shift( first_point,
   1764                           end_point,
   1765                           cur_delta,
   1766                           in_points,
   1767                           out_points );
   1768         else
   1769         {
   1770           /* otherwise handle remaining points       */
   1771           /* at the end and beginning of the contour */
   1772           tt_delta_interpolate( cur_delta + 1,
   1773                                 end_point,
   1774                                 cur_delta,
   1775                                 first_delta,
   1776                                 in_points,
   1777                                 out_points );
   1778 
   1779           if ( first_delta > 0 )
   1780             tt_delta_interpolate( first_point,
   1781                                   first_delta - 1,
   1782                                   cur_delta,
   1783                                   first_delta,
   1784                                   in_points,
   1785                                   out_points );
   1786         }
   1787       }
   1788       contour++;
   1789 
   1790     } while ( contour < outline->n_contours );
   1791   }
   1792 
   1793 
   1794   /*************************************************************************/
   1795   /*                                                                       */
   1796   /* <Function>                                                            */
   1797   /*    TT_Vary_Apply_Glyph_Deltas                                         */
   1798   /*                                                                       */
   1799   /* <Description>                                                         */
   1800   /*    Apply the appropriate deltas to the current glyph.                 */
   1801   /*                                                                       */
   1802   /* <Input>                                                               */
   1803   /*    face        :: A handle to the target face object.                 */
   1804   /*                                                                       */
   1805   /*    glyph_index :: The index of the glyph being modified.              */
   1806   /*                                                                       */
   1807   /*    n_points    :: The number of the points in the glyph, including    */
   1808   /*                   phantom points.                                     */
   1809   /*                                                                       */
   1810   /* <InOut>                                                               */
   1811   /*    outline     :: The outline to change.                              */
   1812   /*                                                                       */
   1813   /* <Return>                                                              */
   1814   /*    FreeType error code.  0 means success.                             */
   1815   /*                                                                       */
   1816   FT_LOCAL_DEF( FT_Error )
   1817   TT_Vary_Apply_Glyph_Deltas( TT_Face      face,
   1818                               FT_UInt      glyph_index,
   1819                               FT_Outline*  outline,
   1820                               FT_UInt      n_points )
   1821   {
   1822     FT_Stream   stream = face->root.stream;
   1823     FT_Memory   memory = stream->memory;
   1824     GX_Blend    blend  = face->blend;
   1825 
   1826     FT_Vector*  points_org = NULL;
   1827     FT_Bool*    has_delta  = NULL;
   1828 
   1829     FT_Error    error;
   1830     FT_ULong    glyph_start;
   1831     FT_UInt     tupleCount;
   1832     FT_ULong    offsetToData;
   1833     FT_ULong    here;
   1834     FT_UInt     i, j;
   1835     FT_Fixed*   tuple_coords    = NULL;
   1836     FT_Fixed*   im_start_coords = NULL;
   1837     FT_Fixed*   im_end_coords   = NULL;
   1838     FT_UInt     point_count, spoint_count = 0;
   1839     FT_UShort*  sharedpoints = NULL;
   1840     FT_UShort*  localpoints  = NULL;
   1841     FT_UShort*  points;
   1842     FT_Short    *deltas_x, *deltas_y;
   1843 
   1844 
   1845     if ( !face->doblend || blend == NULL )
   1846       return FT_THROW( Invalid_Argument );
   1847 
   1848     if ( glyph_index >= blend->gv_glyphcnt      ||
   1849          blend->glyphoffsets[glyph_index] ==
   1850            blend->glyphoffsets[glyph_index + 1] )
   1851     {
   1852       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
   1853                   " no variation data for this glyph\n" ));
   1854       return FT_Err_Ok;
   1855     }
   1856 
   1857     if ( FT_NEW_ARRAY( points_org, n_points ) ||
   1858          FT_NEW_ARRAY( has_delta, n_points )  )
   1859       goto Fail1;
   1860 
   1861     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] )   ||
   1862          FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
   1863                            blend->glyphoffsets[glyph_index] ) )
   1864       goto Fail1;
   1865 
   1866     glyph_start = FT_Stream_FTell( stream );
   1867 
   1868     /* each set of glyph variation data is formatted similarly to `cvar' */
   1869     /* (except we get shared points and global tuples)                   */
   1870 
   1871     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
   1872          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
   1873          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
   1874       goto Fail2;
   1875 
   1876     tupleCount   = FT_GET_USHORT();
   1877     offsetToData = FT_GET_USHORT();
   1878 
   1879     /* rough sanity test */
   1880     if ( offsetToData + tupleCount * 4 > blend->gvar_size )
   1881     {
   1882       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
   1883                   " invalid glyph variation array header\n" ));
   1884 
   1885       error = FT_THROW( Invalid_Table );
   1886       goto Fail2;
   1887     }
   1888 
   1889     offsetToData += glyph_start;
   1890 
   1891     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
   1892     {
   1893       here = FT_Stream_FTell( stream );
   1894 
   1895       FT_Stream_SeekSet( stream, offsetToData );
   1896 
   1897       sharedpoints = ft_var_readpackedpoints( stream,
   1898                                               blend->gvar_size,
   1899                                               &spoint_count );
   1900       offsetToData = FT_Stream_FTell( stream );
   1901 
   1902       FT_Stream_SeekSet( stream, here );
   1903     }
   1904 
   1905     FT_TRACE5(( "gvar: there are %d tuples:\n",
   1906                 tupleCount & GX_TC_TUPLE_COUNT_MASK ));
   1907 
   1908     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
   1909     {
   1910       FT_UInt   tupleDataSize;
   1911       FT_UInt   tupleIndex;
   1912       FT_Fixed  apply;
   1913 
   1914 
   1915       FT_TRACE6(( "  tuple %d:\n", i ));
   1916 
   1917       tupleDataSize = FT_GET_USHORT();
   1918       tupleIndex    = FT_GET_USHORT();
   1919 
   1920       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
   1921       {
   1922         for ( j = 0; j < blend->num_axis; j++ )
   1923           tuple_coords[j] = FT_GET_SHORT() * 4;   /* convert from        */
   1924                                                   /* short frac to fixed */
   1925       }
   1926       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
   1927       {
   1928         FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
   1929                     " invalid tuple index\n" ));
   1930 
   1931         error = FT_THROW( Invalid_Table );
   1932         goto Fail2;
   1933       }
   1934       else
   1935         FT_MEM_COPY(
   1936           tuple_coords,
   1937           &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
   1938           blend->num_axis * sizeof ( FT_Fixed ) );
   1939 
   1940       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
   1941       {
   1942         for ( j = 0; j < blend->num_axis; j++ )
   1943           im_start_coords[j] = FT_GET_SHORT() * 4;
   1944         for ( j = 0; j < blend->num_axis; j++ )
   1945           im_end_coords[j] = FT_GET_SHORT() * 4;
   1946       }
   1947 
   1948       apply = ft_var_apply_tuple( blend,
   1949                                   (FT_UShort)tupleIndex,
   1950                                   tuple_coords,
   1951                                   im_start_coords,
   1952                                   im_end_coords );
   1953 
   1954       if ( apply == 0 )              /* tuple isn't active for our blend */
   1955       {
   1956         offsetToData += tupleDataSize;
   1957         continue;
   1958       }
   1959 
   1960       here = FT_Stream_FTell( stream );
   1961 
   1962       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
   1963       {
   1964         FT_Stream_SeekSet( stream, offsetToData );
   1965 
   1966         localpoints = ft_var_readpackedpoints( stream,
   1967                                                blend->gvar_size,
   1968                                                &point_count );
   1969         points      = localpoints;
   1970       }
   1971       else
   1972       {
   1973         points      = sharedpoints;
   1974         point_count = spoint_count;
   1975       }
   1976 
   1977       deltas_x = ft_var_readpackeddeltas( stream,
   1978                                           blend->gvar_size,
   1979                                           point_count == 0 ? n_points
   1980                                                            : point_count );
   1981       deltas_y = ft_var_readpackeddeltas( stream,
   1982                                           blend->gvar_size,
   1983                                           point_count == 0 ? n_points
   1984                                                            : point_count );
   1985 
   1986       if ( points == NULL || deltas_y == NULL || deltas_x == NULL )
   1987         ; /* failure, ignore it */
   1988 
   1989       else if ( points == ALL_POINTS )
   1990       {
   1991 #ifdef FT_DEBUG_LEVEL_TRACE
   1992         int  count = 0;
   1993 #endif
   1994 
   1995 
   1996         FT_TRACE7(( "    point deltas:\n" ));
   1997 
   1998         /* this means that there are deltas for every point in the glyph */
   1999         for ( j = 0; j < n_points; j++ )
   2000         {
   2001 #ifdef FT_DEBUG_LEVEL_TRACE
   2002           FT_Vector  point_org = outline->points[j];
   2003 #endif
   2004 
   2005 
   2006           outline->points[j].x += FT_MulFix( deltas_x[j], apply );
   2007           outline->points[j].y += FT_MulFix( deltas_y[j], apply );
   2008 
   2009 #ifdef FT_DEBUG_LEVEL_TRACE
   2010           if ( ( point_org.x != outline->points[j].x ) ||
   2011                ( point_org.y != outline->points[j].y ) )
   2012           {
   2013             FT_TRACE7(( "      %d: (%d, %d) -> (%d, %d)\n",
   2014                         j,
   2015                         point_org.x,
   2016                         point_org.y,
   2017                         outline->points[j].x,
   2018                         outline->points[j].y ));
   2019             count++;
   2020           }
   2021 #endif
   2022         }
   2023 
   2024 #ifdef FT_DEBUG_LEVEL_TRACE
   2025         if ( !count )
   2026           FT_TRACE7(( "      none\n" ));
   2027 #endif
   2028       }
   2029 
   2030       else if ( localpoints == NULL )
   2031         ; /* failure, ignore it */
   2032 
   2033       else
   2034       {
   2035 #ifdef FT_DEBUG_LEVEL_TRACE
   2036         int  count = 0;
   2037 #endif
   2038 
   2039 
   2040         /* we have to interpolate the missing deltas similar to the */
   2041         /* IUP bytecode instruction                                 */
   2042         for ( j = 0; j < n_points; j++ )
   2043         {
   2044           points_org[j] = outline->points[j];
   2045           has_delta[j]  = FALSE;
   2046         }
   2047 
   2048         for ( j = 0; j < point_count; j++ )
   2049         {
   2050           FT_UShort  idx = localpoints[j];
   2051 
   2052 
   2053           if ( idx >= n_points )
   2054             continue;
   2055 
   2056           has_delta[idx] = TRUE;
   2057 
   2058           outline->points[idx].x += FT_MulFix( deltas_x[j], apply );
   2059           outline->points[idx].y += FT_MulFix( deltas_y[j], apply );
   2060         }
   2061 
   2062         /* no need to handle phantom points here,      */
   2063         /* since solitary points can't be interpolated */
   2064         tt_handle_deltas( outline,
   2065                           points_org,
   2066                           has_delta );
   2067 
   2068 #ifdef FT_DEBUG_LEVEL_TRACE
   2069         FT_TRACE7(( "    point deltas:\n" ));
   2070 
   2071         for ( j = 0; j < n_points; j++)
   2072         {
   2073           if ( ( points_org[j].x != outline->points[j].x ) ||
   2074                ( points_org[j].y != outline->points[j].y ) )
   2075           {
   2076             FT_TRACE7(( "      %d: (%d, %d) -> (%d, %d)\n",
   2077                         j,
   2078                         points_org[j].x,
   2079                         points_org[j].y,
   2080                         outline->points[j].x,
   2081                         outline->points[j].y ));
   2082             count++;
   2083           }
   2084         }
   2085 
   2086         if ( !count )
   2087           FT_TRACE7(( "      none\n" ));
   2088 #endif
   2089       }
   2090 
   2091       if ( localpoints != ALL_POINTS )
   2092         FT_FREE( localpoints );
   2093       FT_FREE( deltas_x );
   2094       FT_FREE( deltas_y );
   2095 
   2096       offsetToData += tupleDataSize;
   2097 
   2098       FT_Stream_SeekSet( stream, here );
   2099     }
   2100 
   2101     FT_TRACE5(( "\n" ));
   2102 
   2103   Fail2:
   2104     if ( sharedpoints != ALL_POINTS )
   2105       FT_FREE( sharedpoints );
   2106     FT_FREE( tuple_coords );
   2107     FT_FREE( im_start_coords );
   2108     FT_FREE( im_end_coords );
   2109 
   2110     FT_FRAME_EXIT();
   2111 
   2112   Fail1:
   2113     FT_FREE( points_org );
   2114     FT_FREE( has_delta );
   2115 
   2116     return error;
   2117   }
   2118 
   2119 
   2120   /*************************************************************************/
   2121   /*                                                                       */
   2122   /* <Function>                                                            */
   2123   /*    tt_done_blend                                                      */
   2124   /*                                                                       */
   2125   /* <Description>                                                         */
   2126   /*    Free the blend internal data structure.                            */
   2127   /*                                                                       */
   2128   FT_LOCAL_DEF( void )
   2129   tt_done_blend( FT_Memory  memory,
   2130                  GX_Blend   blend )
   2131   {
   2132     if ( blend != NULL )
   2133     {
   2134       FT_UInt  i;
   2135 
   2136 
   2137       FT_FREE( blend->normalizedcoords );
   2138       FT_FREE( blend->mmvar );
   2139 
   2140       if ( blend->avar_segment != NULL )
   2141       {
   2142         for ( i = 0; i < blend->num_axis; i++ )
   2143           FT_FREE( blend->avar_segment[i].correspondence );
   2144         FT_FREE( blend->avar_segment );
   2145       }
   2146 
   2147       FT_FREE( blend->tuplecoords );
   2148       FT_FREE( blend->glyphoffsets );
   2149       FT_FREE( blend );
   2150     }
   2151   }
   2152 
   2153 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
   2154 
   2155 
   2156 /* END */
   2157