source: trunk/poppler/freetype-2.1.10/src/truetype/ttgxvar.c @ 2

Last change on this file since 2 was 2, checked in by Eugene Romanenko, 15 years ago

First import

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