1b35539fe1371eee9e938a7792438371abae7729
[framework/graphics/freetype.git] / src / truetype / ttgxvar.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ttgxvar.c                                                              */
4 /*                                                                         */
5 /*    TrueType GX Font Variation loader                                    */
6 /*                                                                         */
7 /*  Copyright 2004-2014 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   /*   http://developer.apple.com/fonts/TTRefMan/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      0x80
100 #define GX_PT_POINT_RUN_COUNT_MASK  0x7F
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   /* <Output>                                                              */
116   /*    point_cnt :: The number of points read.  A zero value means that   */
117   /*                 all points in the glyph will be affected, without     */
118   /*                 enumerating them individually.                        */
119   /*                                                                       */
120   /* <Return>                                                              */
121   /*    An array of FT_UShort containing the affected points or the        */
122   /*    special value ALL_POINTS.                                          */
123   /*                                                                       */
124   static FT_UShort*
125   ft_var_readpackedpoints( FT_Stream  stream,
126                            FT_UInt   *point_cnt )
127   {
128     FT_UShort *points = NULL;
129     FT_Int     n;
130     FT_Int     runcnt;
131     FT_Int     i;
132     FT_Int     j;
133     FT_Int     first;
134     FT_Memory  memory = stream->memory;
135     FT_Error   error  = FT_Err_Ok;
136
137     FT_UNUSED( error );
138
139
140     *point_cnt = n = FT_GET_BYTE();
141     if ( n == 0 )
142       return ALL_POINTS;
143
144     if ( n & GX_PT_POINTS_ARE_WORDS )
145       n = FT_GET_BYTE() | ( ( n & GX_PT_POINT_RUN_COUNT_MASK ) << 8 );
146
147     if ( FT_NEW_ARRAY( points, n ) )
148       return NULL;
149
150     i = 0;
151     while ( i < n )
152     {
153       runcnt = FT_GET_BYTE();
154       if ( runcnt & GX_PT_POINTS_ARE_WORDS )
155       {
156         runcnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
157         first  = points[i++] = FT_GET_USHORT();
158
159         if ( runcnt < 1 || i + runcnt >= n )
160           goto Exit;
161
162         /* first point not included in runcount */
163         for ( j = 0; j < runcnt; ++j )
164           points[i++] = (FT_UShort)( first += FT_GET_USHORT() );
165       }
166       else
167       {
168         first = points[i++] = FT_GET_BYTE();
169
170         if ( runcnt < 1 || i + runcnt >= n )
171           goto Exit;
172
173         for ( j = 0; j < runcnt; ++j )
174           points[i++] = (FT_UShort)( first += FT_GET_BYTE() );
175       }
176     }
177
178   Exit:
179     return points;
180   }
181
182
183   enum
184   {
185     GX_DT_DELTAS_ARE_ZERO      = 0x80,
186     GX_DT_DELTAS_ARE_WORDS     = 0x40,
187     GX_DT_DELTA_RUN_COUNT_MASK = 0x3F
188   };
189
190
191   /*************************************************************************/
192   /*                                                                       */
193   /* <Function>                                                            */
194   /*    ft_var_readpackeddeltas                                            */
195   /*                                                                       */
196   /* <Description>                                                         */
197   /*    Read a set of deltas.  These are packed slightly differently than  */
198   /*    points.  In particular there is no overall count.                  */
199   /*                                                                       */
200   /* <Input>                                                               */
201   /*    stream    :: The data stream.                                      */
202   /*                                                                       */
203   /*    delta_cnt :: The number of to be read.                             */
204   /*                                                                       */
205   /* <Return>                                                              */
206   /*    An array of FT_Short containing the deltas for the affected        */
207   /*    points.  (This only gets the deltas for one dimension.  It will    */
208   /*    generally be called twice, once for x, once for y.  When used in   */
209   /*    cvt table, it will only be called once.)                           */
210   /*                                                                       */
211   static FT_Short*
212   ft_var_readpackeddeltas( FT_Stream  stream,
213                            FT_Offset  delta_cnt )
214   {
215     FT_Short  *deltas = NULL;
216     FT_UInt    runcnt;
217     FT_Offset  i;
218     FT_UInt    j;
219     FT_Memory  memory = stream->memory;
220     FT_Error   error  = FT_Err_Ok;
221
222     FT_UNUSED( error );
223
224
225     if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
226       return NULL;
227
228     i = 0;
229     while ( i < delta_cnt )
230     {
231       runcnt = FT_GET_BYTE();
232       if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
233       {
234         /* runcnt zeroes get added */
235         for ( j = 0;
236               j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
237               ++j )
238           deltas[i++] = 0;
239       }
240       else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
241       {
242         /* runcnt shorts from the stack */
243         for ( j = 0;
244               j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
245               ++j )
246           deltas[i++] = FT_GET_SHORT();
247       }
248       else
249       {
250         /* runcnt signed bytes from the stack */
251         for ( j = 0;
252               j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
253               ++j )
254           deltas[i++] = FT_GET_CHAR();
255       }
256
257       if ( j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) )
258       {
259         /* Bad format */
260         FT_FREE( deltas );
261         return NULL;
262       }
263     }
264
265     return deltas;
266   }
267
268
269   /*************************************************************************/
270   /*                                                                       */
271   /* <Function>                                                            */
272   /*    ft_var_load_avar                                                   */
273   /*                                                                       */
274   /* <Description>                                                         */
275   /*    Parse the `avar' table if present.  It need not be, so we return   */
276   /*    nothing.                                                           */
277   /*                                                                       */
278   /* <InOut>                                                               */
279   /*    face :: The font face.                                             */
280   /*                                                                       */
281   static void
282   ft_var_load_avar( TT_Face  face )
283   {
284     FT_Stream       stream = FT_FACE_STREAM(face);
285     FT_Memory       memory = stream->memory;
286     GX_Blend        blend  = face->blend;
287     GX_AVarSegment  segment;
288     FT_Error        error = FT_Err_Ok;
289     FT_ULong        version;
290     FT_Long         axisCount;
291     FT_Int          i, j;
292     FT_ULong        table_len;
293
294     FT_UNUSED( error );
295
296
297     blend->avar_checked = TRUE;
298     if ( (error = face->goto_table( face, TTAG_avar, stream, &table_len )) != 0 )
299       return;
300
301     if ( FT_FRAME_ENTER( table_len ) )
302       return;
303
304     version   = FT_GET_LONG();
305     axisCount = FT_GET_LONG();
306
307     if ( version != 0x00010000L                       ||
308          axisCount != (FT_Long)blend->mmvar->num_axis )
309       goto Exit;
310
311     if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
312       goto Exit;
313
314     segment = &blend->avar_segment[0];
315     for ( i = 0; i < axisCount; ++i, ++segment )
316     {
317       segment->pairCount = FT_GET_USHORT();
318       if ( FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
319       {
320         /* Failure.  Free everything we have done so far.  We must do */
321         /* it right now since loading the `avar' table is optional.   */
322
323         for ( j = i - 1; j >= 0; --j )
324           FT_FREE( blend->avar_segment[j].correspondence );
325
326         FT_FREE( blend->avar_segment );
327         blend->avar_segment = NULL;
328         goto Exit;
329       }
330
331       for ( j = 0; j < segment->pairCount; ++j )
332       {
333         segment->correspondence[j].fromCoord =
334           FT_GET_SHORT() << 2;    /* convert to Fixed */
335         segment->correspondence[j].toCoord =
336           FT_GET_SHORT()<<2;    /* convert to Fixed */
337       }
338     }
339
340   Exit:
341     FT_FRAME_EXIT();
342   }
343
344
345   typedef struct  GX_GVar_Head_
346   {
347     FT_Long    version;
348     FT_UShort  axisCount;
349     FT_UShort  globalCoordCount;
350     FT_ULong   offsetToCoord;
351     FT_UShort  glyphCount;
352     FT_UShort  flags;
353     FT_ULong   offsetToData;
354
355   } GX_GVar_Head;
356
357
358   /*************************************************************************/
359   /*                                                                       */
360   /* <Function>                                                            */
361   /*    ft_var_load_gvar                                                   */
362   /*                                                                       */
363   /* <Description>                                                         */
364   /*    Parses the `gvar' table if present.  If `fvar' is there, `gvar'    */
365   /*    had better be there too.                                           */
366   /*                                                                       */
367   /* <InOut>                                                               */
368   /*    face :: The font face.                                             */
369   /*                                                                       */
370   /* <Return>                                                              */
371   /*    FreeType error code.  0 means success.                             */
372   /*                                                                       */
373   static FT_Error
374   ft_var_load_gvar( TT_Face  face )
375   {
376     FT_Stream     stream = FT_FACE_STREAM(face);
377     FT_Memory     memory = stream->memory;
378     GX_Blend      blend  = face->blend;
379     FT_Error      error;
380     FT_UInt       i, j;
381     FT_ULong      table_len;
382     FT_ULong      gvar_start;
383     FT_ULong      offsetToData;
384     GX_GVar_Head  gvar_head;
385
386     static const FT_Frame_Field  gvar_fields[] =
387     {
388
389 #undef  FT_STRUCTURE
390 #define FT_STRUCTURE  GX_GVar_Head
391
392       FT_FRAME_START( 20 ),
393         FT_FRAME_LONG  ( version ),
394         FT_FRAME_USHORT( axisCount ),
395         FT_FRAME_USHORT( globalCoordCount ),
396         FT_FRAME_ULONG ( offsetToCoord ),
397         FT_FRAME_USHORT( glyphCount ),
398         FT_FRAME_USHORT( flags ),
399         FT_FRAME_ULONG ( offsetToData ),
400       FT_FRAME_END
401     };
402
403     if ( (error = face->goto_table( face, TTAG_gvar, stream, &table_len )) != 0 )
404       goto Exit;
405
406     gvar_start = FT_STREAM_POS( );
407     if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
408       goto Exit;
409
410     blend->tuplecount  = gvar_head.globalCoordCount;
411     blend->gv_glyphcnt = gvar_head.glyphCount;
412     offsetToData       = gvar_start + gvar_head.offsetToData;
413
414     if ( gvar_head.version   != (FT_Long)0x00010000L              ||
415          gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
416     {
417       error = FT_THROW( Invalid_Table );
418       goto Exit;
419     }
420
421     if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
422       goto Exit;
423
424     if ( gvar_head.flags & 1 )
425     {
426       /* long offsets (one more offset than glyphs, to mark size of last) */
427       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
428         goto Exit;
429
430       for ( i = 0; i <= blend->gv_glyphcnt; ++i )
431         blend->glyphoffsets[i] = offsetToData + FT_GET_LONG();
432
433       FT_FRAME_EXIT();
434     }
435     else
436     {
437       /* short offsets (one more offset than glyphs, to mark size of last) */
438       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
439         goto Exit;
440
441       for ( i = 0; i <= blend->gv_glyphcnt; ++i )
442         blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
443                                               /* XXX: Undocumented: `*2'! */
444
445       FT_FRAME_EXIT();
446     }
447
448     if ( blend->tuplecount != 0 )
449     {
450       if ( FT_NEW_ARRAY( blend->tuplecoords,
451                          gvar_head.axisCount * blend->tuplecount ) )
452         goto Exit;
453
454       if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord )       ||
455            FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L )                   )
456         goto Exit;
457
458       for ( i = 0; i < blend->tuplecount; ++i )
459         for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; ++j )
460           blend->tuplecoords[i * gvar_head.axisCount + j] =
461             FT_GET_SHORT() << 2;                /* convert to FT_Fixed */
462
463       FT_FRAME_EXIT();
464     }
465
466   Exit:
467     return error;
468   }
469
470
471   /*************************************************************************/
472   /*                                                                       */
473   /* <Function>                                                            */
474   /*    ft_var_apply_tuple                                                 */
475   /*                                                                       */
476   /* <Description>                                                         */
477   /*    Figure out whether a given tuple (design) applies to the current   */
478   /*    blend, and if so, what is the scaling factor.                      */
479   /*                                                                       */
480   /* <Input>                                                               */
481   /*    blend           :: The current blend of the font.                  */
482   /*                                                                       */
483   /*    tupleIndex      :: A flag saying whether this is an intermediate   */
484   /*                       tuple or not.                                   */
485   /*                                                                       */
486   /*    tuple_coords    :: The coordinates of the tuple in normalized axis */
487   /*                       units.                                          */
488   /*                                                                       */
489   /*    im_start_coords :: The initial coordinates where this tuple starts */
490   /*                       to apply (for intermediate coordinates).        */
491   /*                                                                       */
492   /*    im_end_coords   :: The final coordinates after which this tuple no */
493   /*                       longer applies (for intermediate coordinates).  */
494   /*                                                                       */
495   /* <Return>                                                              */
496   /*    An FT_Fixed value containing the scaling factor.                   */
497   /*                                                                       */
498   static FT_Fixed
499   ft_var_apply_tuple( GX_Blend   blend,
500                       FT_UShort  tupleIndex,
501                       FT_Fixed*  tuple_coords,
502                       FT_Fixed*  im_start_coords,
503                       FT_Fixed*  im_end_coords )
504   {
505     FT_UInt   i;
506     FT_Fixed  apply = 0x10000L;
507
508
509     for ( i = 0; i < blend->num_axis; ++i )
510     {
511       if ( tuple_coords[i] == 0 )
512         /* It's not clear why (for intermediate tuples) we don't need     */
513         /* to check against start/end -- the documentation says we don't. */
514         /* Similarly, it's unclear why we don't need to scale along the   */
515         /* axis.                                                          */
516         continue;
517
518       else if ( blend->normalizedcoords[i] == 0                           ||
519                 ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) ||
520                 ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) )
521       {
522         apply = 0;
523         break;
524       }
525
526       else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
527         /* not an intermediate tuple */
528         apply = FT_MulFix( apply,
529                            blend->normalizedcoords[i] > 0
530                              ? blend->normalizedcoords[i]
531                              : -blend->normalizedcoords[i] );
532
533       else if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
534                 blend->normalizedcoords[i] >= im_end_coords[i]   )
535       {
536         apply = 0;
537         break;
538       }
539
540       else if ( blend->normalizedcoords[i] < tuple_coords[i] )
541         apply = FT_MulDiv( apply,
542                            blend->normalizedcoords[i] - im_start_coords[i],
543                            tuple_coords[i] - im_start_coords[i] );
544
545       else
546         apply = FT_MulDiv( apply,
547                            im_end_coords[i] - blend->normalizedcoords[i],
548                            im_end_coords[i] - tuple_coords[i] );
549     }
550
551     return apply;
552   }
553
554
555   /*************************************************************************/
556   /*************************************************************************/
557   /*****                                                               *****/
558   /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
559   /*****                                                               *****/
560   /*************************************************************************/
561   /*************************************************************************/
562
563
564   typedef struct  GX_FVar_Head_
565   {
566     FT_Long    version;
567     FT_UShort  offsetToData;
568     FT_UShort  countSizePairs;
569     FT_UShort  axisCount;
570     FT_UShort  axisSize;
571     FT_UShort  instanceCount;
572     FT_UShort  instanceSize;
573
574   } GX_FVar_Head;
575
576
577   typedef struct  fvar_axis_
578   {
579     FT_ULong   axisTag;
580     FT_ULong   minValue;
581     FT_ULong   defaultValue;
582     FT_ULong   maxValue;
583     FT_UShort  flags;
584     FT_UShort  nameID;
585
586   } GX_FVar_Axis;
587
588
589   /*************************************************************************/
590   /*                                                                       */
591   /* <Function>                                                            */
592   /*    TT_Get_MM_Var                                                      */
593   /*                                                                       */
594   /* <Description>                                                         */
595   /*    Check that the font's `fvar' table is valid, parse it, and return  */
596   /*    those data.                                                        */
597   /*                                                                       */
598   /* <InOut>                                                               */
599   /*    face   :: The font face.                                           */
600   /*              TT_Get_MM_Var initializes the blend structure.           */
601   /*                                                                       */
602   /* <Output>                                                              */
603   /*    master :: The `fvar' data (must be freed by caller).               */
604   /*                                                                       */
605   /* <Return>                                                              */
606   /*    FreeType error code.  0 means success.                             */
607   /*                                                                       */
608   FT_LOCAL_DEF( FT_Error )
609   TT_Get_MM_Var( TT_Face      face,
610                  FT_MM_Var*  *master )
611   {
612     FT_Stream            stream = face->root.stream;
613     FT_Memory            memory = face->root.memory;
614     FT_ULong             table_len;
615     FT_Error             error  = FT_Err_Ok;
616     FT_ULong             fvar_start;
617     FT_Int               i, j;
618     FT_MM_Var*           mmvar = NULL;
619     FT_Fixed*            next_coords;
620     FT_String*           next_name;
621     FT_Var_Axis*         a;
622     FT_Var_Named_Style*  ns;
623     GX_FVar_Head         fvar_head;
624
625     static const FT_Frame_Field  fvar_fields[] =
626     {
627
628 #undef  FT_STRUCTURE
629 #define FT_STRUCTURE  GX_FVar_Head
630
631       FT_FRAME_START( 16 ),
632         FT_FRAME_LONG  ( version ),
633         FT_FRAME_USHORT( offsetToData ),
634         FT_FRAME_USHORT( countSizePairs ),
635         FT_FRAME_USHORT( axisCount ),
636         FT_FRAME_USHORT( axisSize ),
637         FT_FRAME_USHORT( instanceCount ),
638         FT_FRAME_USHORT( instanceSize ),
639       FT_FRAME_END
640     };
641
642     static const FT_Frame_Field  fvaraxis_fields[] =
643     {
644
645 #undef  FT_STRUCTURE
646 #define FT_STRUCTURE  GX_FVar_Axis
647
648       FT_FRAME_START( 20 ),
649         FT_FRAME_ULONG ( axisTag ),
650         FT_FRAME_ULONG ( minValue ),
651         FT_FRAME_ULONG ( defaultValue ),
652         FT_FRAME_ULONG ( maxValue ),
653         FT_FRAME_USHORT( flags ),
654         FT_FRAME_USHORT( nameID ),
655       FT_FRAME_END
656     };
657
658
659     if ( face->blend == NULL )
660     {
661       /* both `fvar' and `gvar' must be present */
662       if ( (error = face->goto_table( face, TTAG_gvar,
663                                       stream, &table_len )) != 0 )
664         goto Exit;
665
666       if ( (error = face->goto_table( face, TTAG_fvar,
667                                       stream, &table_len )) != 0 )
668         goto Exit;
669
670       fvar_start = FT_STREAM_POS( );
671
672       if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
673         goto Exit;
674
675       if ( fvar_head.version != (FT_Long)0x00010000L                      ||
676            fvar_head.countSizePairs != 2                                  ||
677            fvar_head.axisSize != 20                                       ||
678            /* axisCount limit implied by 16-bit instanceSize */
679            fvar_head.axisCount > 0x3FFE                                   ||
680            fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount          ||
681            /* instanceCount limit implied by limited range of name IDs */
682            fvar_head.instanceCount > 0x7EFF                               ||
683            fvar_head.offsetToData + fvar_head.axisCount * 20U +
684              fvar_head.instanceCount * fvar_head.instanceSize > table_len )
685       {
686         error = FT_THROW( Invalid_Table );
687         goto Exit;
688       }
689
690       if ( FT_NEW( face->blend ) )
691         goto Exit;
692
693       /* cannot overflow 32-bit arithmetic because of limits above */
694       face->blend->mmvar_len =
695         sizeof ( FT_MM_Var ) +
696         fvar_head.axisCount * sizeof ( FT_Var_Axis ) +
697         fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) +
698         fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) +
699         5 * fvar_head.axisCount;
700
701       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
702         goto Exit;
703       face->blend->mmvar = mmvar;
704
705       mmvar->num_axis =
706         fvar_head.axisCount;
707       mmvar->num_designs =
708         ~0U;                   /* meaningless in this context; each glyph */
709                                /* may have a different number of designs  */
710                                /* (or tuples, as called by Apple)         */
711       mmvar->num_namedstyles =
712         fvar_head.instanceCount;
713       mmvar->axis =
714         (FT_Var_Axis*)&(mmvar[1]);
715       mmvar->namedstyle =
716         (FT_Var_Named_Style*)&(mmvar->axis[fvar_head.axisCount]);
717
718       next_coords =
719         (FT_Fixed*)&(mmvar->namedstyle[fvar_head.instanceCount]);
720       for ( i = 0; i < fvar_head.instanceCount; ++i )
721       {
722         mmvar->namedstyle[i].coords  = next_coords;
723         next_coords                 += fvar_head.axisCount;
724       }
725
726       next_name = (FT_String*)next_coords;
727       for ( i = 0; i < fvar_head.axisCount; ++i )
728       {
729         mmvar->axis[i].name  = next_name;
730         next_name           += 5;
731       }
732
733       if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
734         goto Exit;
735
736       a = mmvar->axis;
737       for ( i = 0; i < fvar_head.axisCount; ++i )
738       {
739         GX_FVar_Axis  axis_rec;
740
741
742         if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
743           goto Exit;
744         a->tag     = axis_rec.axisTag;
745         a->minimum = axis_rec.minValue;     /* A Fixed */
746         a->def     = axis_rec.defaultValue; /* A Fixed */
747         a->maximum = axis_rec.maxValue;     /* A Fixed */
748         a->strid   = axis_rec.nameID;
749
750         a->name[0] = (FT_String)(   a->tag >> 24 );
751         a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
752         a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
753         a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
754         a->name[4] = 0;
755
756         ++a;
757       }
758
759       ns = mmvar->namedstyle;
760       for ( i = 0; i < fvar_head.instanceCount; ++i, ++ns )
761       {
762         if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
763           goto Exit;
764
765         ns->strid       =    FT_GET_USHORT();
766         (void) /* flags = */ FT_GET_USHORT();
767
768         for ( j = 0; j < fvar_head.axisCount; ++j )
769           ns->coords[j] = FT_GET_ULONG();     /* A Fixed */
770
771         FT_FRAME_EXIT();
772       }
773     }
774
775     if ( master != NULL )
776     {
777       FT_UInt  n;
778
779
780       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
781         goto Exit;
782       FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
783
784       mmvar->axis =
785         (FT_Var_Axis*)&(mmvar[1]);
786       mmvar->namedstyle =
787         (FT_Var_Named_Style*)&(mmvar->axis[mmvar->num_axis]);
788       next_coords =
789         (FT_Fixed*)&(mmvar->namedstyle[mmvar->num_namedstyles]);
790
791       for ( n = 0; n < mmvar->num_namedstyles; ++n )
792       {
793         mmvar->namedstyle[n].coords  = next_coords;
794         next_coords                 += mmvar->num_axis;
795       }
796
797       a = mmvar->axis;
798       next_name = (FT_String*)next_coords;
799       for ( n = 0; n < mmvar->num_axis; ++n )
800       {
801         a->name = next_name;
802
803         /* standard PostScript names for some standard apple tags */
804         if ( a->tag == TTAG_wght )
805           a->name = (char *)"Weight";
806         else if ( a->tag == TTAG_wdth )
807           a->name = (char *)"Width";
808         else if ( a->tag == TTAG_opsz )
809           a->name = (char *)"OpticalSize";
810         else if ( a->tag == TTAG_slnt )
811           a->name = (char *)"Slant";
812
813         next_name += 5;
814         ++a;
815       }
816
817       *master = mmvar;
818     }
819
820   Exit:
821     return error;
822   }
823
824
825   /*************************************************************************/
826   /*                                                                       */
827   /* <Function>                                                            */
828   /*    TT_Set_MM_Blend                                                    */
829   /*                                                                       */
830   /* <Description>                                                         */
831   /*    Set the blend (normalized) coordinates for this instance of the    */
832   /*    font.  Check that the `gvar' table is reasonable and does some     */
833   /*    initial preparation.                                               */
834   /*                                                                       */
835   /* <InOut>                                                               */
836   /*    face       :: The font.                                            */
837   /*                  Initialize the blend structure with `gvar' data.     */
838   /*                                                                       */
839   /* <Input>                                                               */
840   /*    num_coords :: Must be the axis count of the font.                  */
841   /*                                                                       */
842   /*    coords     :: An array of num_coords, each between [-1,1].         */
843   /*                                                                       */
844   /* <Return>                                                              */
845   /*    FreeType error code.  0 means success.                             */
846   /*                                                                       */
847   FT_LOCAL_DEF( FT_Error )
848   TT_Set_MM_Blend( TT_Face    face,
849                    FT_UInt    num_coords,
850                    FT_Fixed*  coords )
851   {
852     FT_Error    error = FT_Err_Ok;
853     GX_Blend    blend;
854     FT_MM_Var*  mmvar;
855     FT_UInt     i;
856     FT_Memory   memory = face->root.memory;
857
858     enum
859     {
860       mcvt_retain,
861       mcvt_modify,
862       mcvt_load
863
864     } manageCvt;
865
866
867     face->doblend = FALSE;
868
869     if ( face->blend == NULL )
870     {
871       if ( (error = TT_Get_MM_Var( face, NULL)) != 0 )
872         goto Exit;
873     }
874
875     blend = face->blend;
876     mmvar = blend->mmvar;
877
878     if ( num_coords != mmvar->num_axis )
879     {
880       error = FT_THROW( Invalid_Argument );
881       goto Exit;
882     }
883
884     for ( i = 0; i < num_coords; ++i )
885       if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
886       {
887         error = FT_THROW( Invalid_Argument );
888         goto Exit;
889       }
890
891     if ( blend->glyphoffsets == NULL )
892       if ( (error = ft_var_load_gvar( face )) != 0 )
893         goto Exit;
894
895     if ( blend->normalizedcoords == NULL )
896     {
897       if ( FT_NEW_ARRAY( blend->normalizedcoords, num_coords ) )
898         goto Exit;
899
900       manageCvt = mcvt_modify;
901
902       /* If we have not set the blend coordinates before this, then the  */
903       /* cvt table will still be what we read from the `cvt ' table and  */
904       /* we don't need to reload it.  We may need to change it though... */
905     }
906     else
907     {
908       manageCvt = mcvt_retain;
909       for ( i = 0; i < num_coords; ++i )
910       {
911         if ( blend->normalizedcoords[i] != coords[i] )
912         {
913           manageCvt = mcvt_load;
914           break;
915         }
916       }
917
918       /* If we don't change the blend coords then we don't need to do  */
919       /* anything to the cvt table.  It will be correct.  Otherwise we */
920       /* no longer have the original cvt (it was modified when we set  */
921       /* the blend last time), so we must reload and then modify it.   */
922     }
923
924     blend->num_axis = num_coords;
925     FT_MEM_COPY( blend->normalizedcoords,
926                  coords,
927                  num_coords * sizeof ( FT_Fixed ) );
928
929     face->doblend = TRUE;
930
931     if ( face->cvt != NULL )
932     {
933       switch ( manageCvt )
934       {
935       case mcvt_load:
936         /* The cvt table has been loaded already; every time we change the */
937         /* blend we may need to reload and remodify the cvt table.         */
938         FT_FREE( face->cvt );
939         face->cvt = NULL;
940
941         error = tt_face_load_cvt( face, face->root.stream );
942         break;
943
944       case mcvt_modify:
945         /* The original cvt table is in memory.  All we need to do is */
946         /* apply the `cvar' table (if any).                           */
947         error = tt_face_vary_cvt( face, face->root.stream );
948         break;
949
950       case mcvt_retain:
951         /* The cvt table is correct for this set of coordinates. */
952         break;
953       }
954     }
955
956   Exit:
957     return error;
958   }
959
960
961   /*************************************************************************/
962   /*                                                                       */
963   /* <Function>                                                            */
964   /*    TT_Set_Var_Design                                                  */
965   /*                                                                       */
966   /* <Description>                                                         */
967   /*    Set the coordinates for the instance, measured in the user         */
968   /*    coordinate system.  Parse the `avar' table (if present) to convert */
969   /*    from user to normalized coordinates.                               */
970   /*                                                                       */
971   /* <InOut>                                                               */
972   /*    face       :: The font face.                                       */
973   /*                  Initialize the blend struct with `gvar' data.        */
974   /*                                                                       */
975   /* <Input>                                                               */
976   /*    num_coords :: This must be the axis count of the font.             */
977   /*                                                                       */
978   /*    coords     :: A coordinate array with `num_coords' elements.       */
979   /*                                                                       */
980   /* <Return>                                                              */
981   /*    FreeType error code.  0 means success.                             */
982   /*                                                                       */
983   FT_LOCAL_DEF( FT_Error )
984   TT_Set_Var_Design( TT_Face    face,
985                      FT_UInt    num_coords,
986                      FT_Fixed*  coords )
987   {
988     FT_Error        error      = FT_Err_Ok;
989     FT_Fixed*       normalized = NULL;
990     GX_Blend        blend;
991     FT_MM_Var*      mmvar;
992     FT_UInt         i, j;
993     FT_Var_Axis*    a;
994     GX_AVarSegment  av;
995     FT_Memory       memory = face->root.memory;
996
997
998     if ( face->blend == NULL )
999     {
1000       if ( (error = TT_Get_MM_Var( face, NULL )) != 0 )
1001         goto Exit;
1002     }
1003
1004     blend = face->blend;
1005     mmvar = blend->mmvar;
1006
1007     if ( num_coords != mmvar->num_axis )
1008     {
1009       error = FT_THROW( Invalid_Argument );
1010       goto Exit;
1011     }
1012
1013     /* Axis normalization is a two stage process.  First we normalize */
1014     /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
1015     /* Then, if there's an `avar' table, we renormalize this range.   */
1016
1017     if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
1018       goto Exit;
1019
1020     a = mmvar->axis;
1021     for ( i = 0; i < mmvar->num_axis; ++i, ++a )
1022     {
1023       if ( coords[i] > a->maximum || coords[i] < a->minimum )
1024       {
1025         error = FT_THROW( Invalid_Argument );
1026         goto Exit;
1027       }
1028
1029       if ( coords[i] < a->def )
1030         normalized[i] = -FT_DivFix( coords[i] - a->def, a->minimum - a->def );
1031       else if ( a->maximum == a->def )
1032         normalized[i] = 0;
1033       else
1034         normalized[i] = FT_DivFix( coords[i] - a->def, a->maximum - a->def );
1035     }
1036
1037     if ( !blend->avar_checked )
1038       ft_var_load_avar( face );
1039
1040     if ( blend->avar_segment != NULL )
1041     {
1042       av = blend->avar_segment;
1043       for ( i = 0; i < mmvar->num_axis; ++i, ++av )
1044       {
1045         for ( j = 1; j < (FT_UInt)av->pairCount; ++j )
1046           if ( normalized[i] < av->correspondence[j].fromCoord )
1047           {
1048             normalized[i] =
1049               FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
1050                          av->correspondence[j].toCoord -
1051                            av->correspondence[j - 1].toCoord,
1052                          av->correspondence[j].fromCoord -
1053                            av->correspondence[j - 1].fromCoord ) +
1054               av->correspondence[j - 1].toCoord;
1055             break;
1056           }
1057       }
1058     }
1059
1060     error = TT_Set_MM_Blend( face, num_coords, normalized );
1061
1062   Exit:
1063     FT_FREE( normalized );
1064     return error;
1065   }
1066
1067
1068   /*************************************************************************/
1069   /*************************************************************************/
1070   /*****                                                               *****/
1071   /*****                     GX VAR PARSING ROUTINES                   *****/
1072   /*****                                                               *****/
1073   /*************************************************************************/
1074   /*************************************************************************/
1075
1076
1077   /*************************************************************************/
1078   /*                                                                       */
1079   /* <Function>                                                            */
1080   /*    tt_face_vary_cvt                                                   */
1081   /*                                                                       */
1082   /* <Description>                                                         */
1083   /*    Modify the loaded cvt table according to the `cvar' table and the  */
1084   /*    font's blend.                                                      */
1085   /*                                                                       */
1086   /* <InOut>                                                               */
1087   /*    face   :: A handle to the target face object.                      */
1088   /*                                                                       */
1089   /* <Input>                                                               */
1090   /*    stream :: A handle to the input stream.                            */
1091   /*                                                                       */
1092   /* <Return>                                                              */
1093   /*    FreeType error code.  0 means success.                             */
1094   /*                                                                       */
1095   /*    Most errors are ignored.  It is perfectly valid not to have a      */
1096   /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
1097   /*                                                                       */
1098   FT_LOCAL_DEF( FT_Error )
1099   tt_face_vary_cvt( TT_Face    face,
1100                     FT_Stream  stream )
1101   {
1102     FT_Error    error;
1103     FT_Memory   memory = stream->memory;
1104     FT_ULong    table_start;
1105     FT_ULong    table_len;
1106     FT_UInt     tupleCount;
1107     FT_ULong    offsetToData;
1108     FT_ULong    here;
1109     FT_UInt     i, j;
1110     FT_Fixed*   tuple_coords    = NULL;
1111     FT_Fixed*   im_start_coords = NULL;
1112     FT_Fixed*   im_end_coords   = NULL;
1113     GX_Blend    blend           = face->blend;
1114     FT_UInt     point_count;
1115     FT_UShort*  localpoints;
1116     FT_Short*   deltas;
1117
1118
1119     FT_TRACE2(( "CVAR " ));
1120
1121     if ( blend == NULL )
1122     {
1123       FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" ));
1124
1125       error = FT_Err_Ok;
1126       goto Exit;
1127     }
1128
1129     if ( face->cvt == NULL )
1130     {
1131       FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" ));
1132
1133       error = FT_Err_Ok;
1134       goto Exit;
1135     }
1136
1137     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
1138     if ( error )
1139     {
1140       FT_TRACE2(( "is missing\n" ));
1141
1142       error = FT_Err_Ok;
1143       goto Exit;
1144     }
1145
1146     if ( FT_FRAME_ENTER( table_len ) )
1147     {
1148       error = FT_Err_Ok;
1149       goto Exit;
1150     }
1151
1152     table_start = FT_Stream_FTell( stream );
1153     if ( FT_GET_LONG() != 0x00010000L )
1154     {
1155       FT_TRACE2(( "bad table version\n" ));
1156
1157       error = FT_Err_Ok;
1158       goto FExit;
1159     }
1160
1161     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
1162          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
1163          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
1164       goto FExit;
1165
1166     tupleCount   = FT_GET_USHORT();
1167     offsetToData = table_start + FT_GET_USHORT();
1168
1169     /* The documentation implies there are flags packed into the        */
1170     /* tuplecount, but John Jenkins says that shared points don't apply */
1171     /* to `cvar', and no other flags are defined.                       */
1172
1173     for ( i = 0; i < ( tupleCount & 0xFFF ); ++i )
1174     {
1175       FT_UInt   tupleDataSize;
1176       FT_UInt   tupleIndex;
1177       FT_Fixed  apply;
1178
1179
1180       tupleDataSize = FT_GET_USHORT();
1181       tupleIndex    = FT_GET_USHORT();
1182
1183       /* There is no provision here for a global tuple coordinate section, */
1184       /* so John says.  There are no tuple indices, just embedded tuples.  */
1185
1186       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
1187       {
1188         for ( j = 0; j < blend->num_axis; ++j )
1189           tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from        */
1190                                                  /* short frac to fixed */
1191       }
1192       else
1193       {
1194         /* skip this tuple; it makes no sense */
1195
1196         if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1197           for ( j = 0; j < 2 * blend->num_axis; ++j )
1198             (void)FT_GET_SHORT();
1199
1200         offsetToData += tupleDataSize;
1201         continue;
1202       }
1203
1204       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1205       {
1206         for ( j = 0; j < blend->num_axis; ++j )
1207           im_start_coords[j] = FT_GET_SHORT() << 2;
1208         for ( j = 0; j < blend->num_axis; ++j )
1209           im_end_coords[j] = FT_GET_SHORT() << 2;
1210       }
1211
1212       apply = ft_var_apply_tuple( blend,
1213                                   (FT_UShort)tupleIndex,
1214                                   tuple_coords,
1215                                   im_start_coords,
1216                                   im_end_coords );
1217       if ( /* tuple isn't active for our blend */
1218            apply == 0                                    ||
1219            /* global points not allowed,           */
1220            /* if they aren't local, makes no sense */
1221            !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
1222       {
1223         offsetToData += tupleDataSize;
1224         continue;
1225       }
1226
1227       here = FT_Stream_FTell( stream );
1228
1229       FT_Stream_SeekSet( stream, offsetToData );
1230
1231       localpoints = ft_var_readpackedpoints( stream, &point_count );
1232       deltas      = ft_var_readpackeddeltas( stream,
1233                                              point_count == 0 ? face->cvt_size
1234                                                               : point_count );
1235       if ( localpoints == NULL || deltas == NULL )
1236         /* failure, ignore it */;
1237
1238       else if ( localpoints == ALL_POINTS )
1239       {
1240         /* this means that there are deltas for every entry in cvt */
1241         for ( j = 0; j < face->cvt_size; ++j )
1242           face->cvt[j] = (FT_Short)( face->cvt[j] +
1243                                      FT_MulFix( deltas[j], apply ) );
1244       }
1245
1246       else
1247       {
1248         for ( j = 0; j < point_count; ++j )
1249         {
1250           int  pindex = localpoints[j];
1251
1252           face->cvt[pindex] = (FT_Short)( face->cvt[pindex] +
1253                                           FT_MulFix( deltas[j], apply ) );
1254         }
1255       }
1256
1257       if ( localpoints != ALL_POINTS )
1258         FT_FREE( localpoints );
1259       FT_FREE( deltas );
1260
1261       offsetToData += tupleDataSize;
1262
1263       FT_Stream_SeekSet( stream, here );
1264     }
1265
1266   FExit:
1267     FT_FRAME_EXIT();
1268
1269   Exit:
1270     FT_FREE( tuple_coords );
1271     FT_FREE( im_start_coords );
1272     FT_FREE( im_end_coords );
1273
1274     return error;
1275   }
1276
1277
1278   /*************************************************************************/
1279   /*                                                                       */
1280   /* <Function>                                                            */
1281   /*    TT_Vary_Get_Glyph_Deltas                                           */
1282   /*                                                                       */
1283   /* <Description>                                                         */
1284   /*    Load the appropriate deltas for the current glyph.                 */
1285   /*                                                                       */
1286   /* <Input>                                                               */
1287   /*    face        :: A handle to the target face object.                 */
1288   /*                                                                       */
1289   /*    glyph_index :: The index of the glyph being modified.              */
1290   /*                                                                       */
1291   /*    n_points    :: The number of the points in the glyph, including    */
1292   /*                   phantom points.                                     */
1293   /*                                                                       */
1294   /* <Output>                                                              */
1295   /*    deltas      :: The array of points to change.                      */
1296   /*                                                                       */
1297   /* <Return>                                                              */
1298   /*    FreeType error code.  0 means success.                             */
1299   /*                                                                       */
1300   FT_LOCAL_DEF( FT_Error )
1301   TT_Vary_Get_Glyph_Deltas( TT_Face      face,
1302                             FT_UInt      glyph_index,
1303                             FT_Vector*  *deltas,
1304                             FT_UInt      n_points )
1305   {
1306     FT_Stream   stream = face->root.stream;
1307     FT_Memory   memory = stream->memory;
1308     GX_Blend    blend  = face->blend;
1309     FT_Vector*  delta_xy = NULL;
1310
1311     FT_Error    error;
1312     FT_ULong    glyph_start;
1313     FT_UInt     tupleCount;
1314     FT_ULong    offsetToData;
1315     FT_ULong    here;
1316     FT_UInt     i, j;
1317     FT_Fixed*   tuple_coords    = NULL;
1318     FT_Fixed*   im_start_coords = NULL;
1319     FT_Fixed*   im_end_coords   = NULL;
1320     FT_UInt     point_count, spoint_count = 0;
1321     FT_UShort*  sharedpoints = NULL;
1322     FT_UShort*  localpoints  = NULL;
1323     FT_UShort*  points;
1324     FT_Short    *deltas_x, *deltas_y;
1325
1326
1327     if ( !face->doblend || blend == NULL )
1328       return FT_THROW( Invalid_Argument );
1329
1330     /* to be freed by the caller */
1331     if ( FT_NEW_ARRAY( delta_xy, n_points ) )
1332       goto Exit;
1333     *deltas = delta_xy;
1334
1335     if ( glyph_index >= blend->gv_glyphcnt      ||
1336          blend->glyphoffsets[glyph_index] ==
1337            blend->glyphoffsets[glyph_index + 1] )
1338       return FT_Err_Ok;               /* no variation data for this glyph */
1339
1340     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] )   ||
1341          FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
1342                            blend->glyphoffsets[glyph_index] ) )
1343       goto Fail1;
1344
1345     glyph_start = FT_Stream_FTell( stream );
1346
1347     /* each set of glyph variation data is formatted similarly to `cvar' */
1348     /* (except we get shared points and global tuples)                   */
1349
1350     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
1351          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
1352          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
1353       goto Fail2;
1354
1355     tupleCount   = FT_GET_USHORT();
1356     offsetToData = glyph_start + FT_GET_USHORT();
1357
1358     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
1359     {
1360       here = FT_Stream_FTell( stream );
1361
1362       FT_Stream_SeekSet( stream, offsetToData );
1363
1364       sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
1365       offsetToData = FT_Stream_FTell( stream );
1366
1367       FT_Stream_SeekSet( stream, here );
1368     }
1369
1370     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i )
1371     {
1372       FT_UInt   tupleDataSize;
1373       FT_UInt   tupleIndex;
1374       FT_Fixed  apply;
1375
1376
1377       tupleDataSize = FT_GET_USHORT();
1378       tupleIndex    = FT_GET_USHORT();
1379
1380       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
1381       {
1382         for ( j = 0; j < blend->num_axis; ++j )
1383           tuple_coords[j] = FT_GET_SHORT() << 2;  /* convert from        */
1384                                                   /* short frac to fixed */
1385       }
1386       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
1387       {
1388         error = FT_THROW( Invalid_Table );
1389         goto Fail3;
1390       }
1391       else
1392       {
1393         FT_MEM_COPY(
1394           tuple_coords,
1395           &blend->tuplecoords[(tupleIndex & 0xFFF) * blend->num_axis],
1396           blend->num_axis * sizeof ( FT_Fixed ) );
1397       }
1398
1399       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1400       {
1401         for ( j = 0; j < blend->num_axis; ++j )
1402           im_start_coords[j] = FT_GET_SHORT() << 2;
1403         for ( j = 0; j < blend->num_axis; ++j )
1404           im_end_coords[j] = FT_GET_SHORT() << 2;
1405       }
1406
1407       apply = ft_var_apply_tuple( blend,
1408                                   (FT_UShort)tupleIndex,
1409                                   tuple_coords,
1410                                   im_start_coords,
1411                                   im_end_coords );
1412
1413       if ( apply == 0 )              /* tuple isn't active for our blend */
1414       {
1415         offsetToData += tupleDataSize;
1416         continue;
1417       }
1418
1419       here = FT_Stream_FTell( stream );
1420
1421       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
1422       {
1423         FT_Stream_SeekSet( stream, offsetToData );
1424
1425         localpoints = ft_var_readpackedpoints( stream, &point_count );
1426         points      = localpoints;
1427       }
1428       else
1429       {
1430         points      = sharedpoints;
1431         point_count = spoint_count;
1432       }
1433
1434       deltas_x = ft_var_readpackeddeltas( stream,
1435                                           point_count == 0 ? n_points
1436                                                            : point_count );
1437       deltas_y = ft_var_readpackeddeltas( stream,
1438                                           point_count == 0 ? n_points
1439                                                            : point_count );
1440
1441       if ( points == NULL || deltas_y == NULL || deltas_x == NULL )
1442         ; /* failure, ignore it */
1443
1444       else if ( points == ALL_POINTS )
1445       {
1446         /* this means that there are deltas for every point in the glyph */
1447         for ( j = 0; j < n_points; ++j )
1448         {
1449           delta_xy[j].x += FT_MulFix( deltas_x[j], apply );
1450           delta_xy[j].y += FT_MulFix( deltas_y[j], apply );
1451         }
1452       }
1453
1454       else
1455       {
1456         for ( j = 0; j < point_count; ++j )
1457         {
1458           if ( localpoints[j] >= n_points )
1459             continue;
1460
1461           delta_xy[localpoints[j]].x += FT_MulFix( deltas_x[j], apply );
1462           delta_xy[localpoints[j]].y += FT_MulFix( deltas_y[j], apply );
1463         }
1464       }
1465
1466       if ( localpoints != ALL_POINTS )
1467         FT_FREE( localpoints );
1468       FT_FREE( deltas_x );
1469       FT_FREE( deltas_y );
1470
1471       offsetToData += tupleDataSize;
1472
1473       FT_Stream_SeekSet( stream, here );
1474     }
1475
1476   Fail3:
1477     FT_FREE( tuple_coords );
1478     FT_FREE( im_start_coords );
1479     FT_FREE( im_end_coords );
1480
1481   Fail2:
1482     FT_FRAME_EXIT();
1483
1484   Fail1:
1485     if ( error )
1486     {
1487       FT_FREE( delta_xy );
1488       *deltas = NULL;
1489     }
1490
1491   Exit:
1492     return error;
1493   }
1494
1495
1496   /*************************************************************************/
1497   /*                                                                       */
1498   /* <Function>                                                            */
1499   /*    tt_done_blend                                                      */
1500   /*                                                                       */
1501   /* <Description>                                                         */
1502   /*    Frees the blend internal data structure.                           */
1503   /*                                                                       */
1504   FT_LOCAL_DEF( void )
1505   tt_done_blend( FT_Memory  memory,
1506                  GX_Blend   blend )
1507   {
1508     if ( blend != NULL )
1509     {
1510       FT_UInt  i;
1511
1512
1513       FT_FREE( blend->normalizedcoords );
1514       FT_FREE( blend->mmvar );
1515
1516       if ( blend->avar_segment != NULL )
1517       {
1518         for ( i = 0; i < blend->num_axis; ++i )
1519           FT_FREE( blend->avar_segment[i].correspondence );
1520         FT_FREE( blend->avar_segment );
1521       }
1522
1523       FT_FREE( blend->tuplecoords );
1524       FT_FREE( blend->glyphoffsets );
1525       FT_FREE( blend );
1526     }
1527   }
1528
1529 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
1530
1531
1532 /* END */