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