1 /***************************************************************************/
2 /* */
3 /* ttgxvar.c */
4 /* */
5 /* TrueType GX Font Variation loader */
6 /* */
7 /* Copyright 2004-2018 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 `gvar' is not intelligible; `cvar' refers you */
26 /* to `gvar' and is thus also incomprehensible. */
27 /* */
28 /* The documentation for `avar' appears correct, but Apple has no fonts */
29 /* with an `avar' table, so it is hard to test. */
30 /* */
31 /* Many thanks to John Jenkins (at Apple) in figuring this out. */
32 /* */
33 /* */
34 /* Apple's `kern' table has some references to tuple indices, but as */
35 /* there is no indication where these indices are defined, nor how to */
36 /* interpolate the kerning values (different tuples have different */
37 /* classes) this issue is ignored. */
38 /* */
39 /*************************************************************************/
40
41
42 #include <ft2build.h>
43 #include FT_INTERNAL_DEBUG_H
44 #include FT_CONFIG_CONFIG_H
45 #include FT_INTERNAL_STREAM_H
46 #include FT_INTERNAL_SFNT_H
47 #include FT_TRUETYPE_TAGS_H
48 #include FT_TRUETYPE_IDS_H
49 #include FT_MULTIPLE_MASTERS_H
50 #include FT_LIST_H
51
52 #include "ttpload.h"
53 #include "ttgxvar.h"
54
55 #include "tterrors.h"
56
57
58 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
59
60
61 #define FT_Stream_FTell( stream ) \
62 (FT_ULong)( (stream)->cursor - (stream)->base )
63 #define FT_Stream_SeekSet( stream, off ) \
64 (stream)->cursor = \
65 ( (off) < (FT_ULong)( (stream)->limit - (stream)->base ) ) \
66 ? (stream)->base + (off) \
67 : (stream)->limit
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
96 /* ensure that value `0' has the same width as a pointer */
97 #define ALL_POINTS (FT_UShort*)~(FT_PtrDist)0
98
99
100 #define GX_PT_POINTS_ARE_WORDS 0x80U
101 #define GX_PT_POINT_RUN_COUNT_MASK 0x7FU
102
103
104 /*************************************************************************/
105 /* */
106 /* <Function> */
107 /* ft_var_readpackedpoints */
108 /* */
109 /* <Description> */
110 /* Read a set of points to which the following deltas will apply. */
111 /* Points are packed with a run length encoding. */
112 /* */
113 /* <Input> */
114 /* stream :: The data stream. */
115 /* */
116 /* size :: The size of the table holding the data. */
117 /* */
118 /* <Output> */
119 /* point_cnt :: The number of points read. A zero value means that */
120 /* all points in the glyph will be affected, without */
121 /* enumerating them individually. */
122 /* */
123 /* <Return> */
124 /* An array of FT_UShort containing the affected points or the */
125 /* special value ALL_POINTS. */
126 /* */
127 static FT_UShort*
128 ft_var_readpackedpoints( FT_Stream stream,
129 FT_ULong size,
130 FT_UInt *point_cnt )
131 {
132 FT_UShort *points = NULL;
133 FT_UInt n;
134 FT_UInt runcnt;
135 FT_UInt i, j;
136 FT_UShort first;
137 FT_Memory memory = stream->memory;
138 FT_Error error = FT_Err_Ok;
139
140 FT_UNUSED( error );
141
142
143 *point_cnt = 0;
144
145 n = FT_GET_BYTE();
146 if ( n == 0 )
194
195 for ( j = 0; j < runcnt; j++ )
196 {
197 first += FT_GET_BYTE();
198 points[i++] = first;
199 if ( i >= n )
200 break;
201 }
202 }
203 }
204
205 return points;
206 }
207
208
209 #define GX_DT_DELTAS_ARE_ZERO 0x80U
210 #define GX_DT_DELTAS_ARE_WORDS 0x40U
211 #define GX_DT_DELTA_RUN_COUNT_MASK 0x3FU
212
213
214 /*************************************************************************/
215 /* */
216 /* <Function> */
217 /* ft_var_readpackeddeltas */
218 /* */
219 /* <Description> */
220 /* Read a set of deltas. These are packed slightly differently than */
221 /* points. In particular there is no overall count. */
222 /* */
223 /* <Input> */
224 /* stream :: The data stream. */
225 /* */
226 /* size :: The size of the table holding the data. */
227 /* */
228 /* delta_cnt :: The number of deltas to be read. */
229 /* */
230 /* <Return> */
231 /* An array of FT_Short containing the deltas for the affected */
232 /* points. (This only gets the deltas for one dimension. It will */
233 /* generally be called twice, once for x, once for y. When used in */
234 /* cvt table, it will only be called once.) */
235 /* */
236 static FT_Short*
237 ft_var_readpackeddeltas( FT_Stream stream,
238 FT_ULong size,
239 FT_UInt delta_cnt )
240 {
241 FT_Short *deltas = NULL;
242 FT_UInt runcnt, cnt;
243 FT_UInt i, j;
244 FT_Memory memory = stream->memory;
245 FT_Error error = FT_Err_Ok;
246
247 FT_UNUSED( error );
248
249
250 if ( delta_cnt > size )
251 {
252 FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" ));
253 return NULL;
254 }
255
256 if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
257 return NULL;
258
259 i = 0;
260 while ( i < delta_cnt )
261 {
262 runcnt = FT_GET_BYTE();
263 cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
264
265 if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
266 {
267 /* `runcnt' zeroes get added */
268 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
269 deltas[i++] = 0;
270 }
271 else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
272 {
273 /* `runcnt' shorts from the stack */
274 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
275 deltas[i++] = FT_GET_SHORT();
276 }
277 else
278 {
279 /* `runcnt' signed bytes from the stack */
280 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
281 deltas[i++] = FT_GET_CHAR();
282 }
283
284 if ( j <= cnt )
285 {
286 /* bad format */
287 FT_FREE( deltas );
288 return NULL;
289 }
290 }
291
292 return deltas;
293 }
294
295
296 /*************************************************************************/
297 /* */
298 /* <Function> */
299 /* ft_var_load_avar */
300 /* */
301 /* <Description> */
302 /* Parse the `avar' table if present. It need not be, so we return */
303 /* nothing. */
304 /* */
305 /* <InOut> */
306 /* face :: The font face. */
307 /* */
308 static void
309 ft_var_load_avar( TT_Face face )
310 {
311 FT_Stream stream = FT_FACE_STREAM( face );
312 FT_Memory memory = stream->memory;
313 GX_Blend blend = face->blend;
314 GX_AVarSegment segment;
315 FT_Error error = FT_Err_Ok;
316 FT_Long version;
317 FT_Long axisCount;
318 FT_Int i, j;
319 FT_ULong table_len;
320
321 FT_UNUSED( error );
322
323
324 FT_TRACE2(( "AVAR " ));
325
326 blend->avar_loaded = TRUE;
327 error = face->goto_table( face, TTAG_avar, stream, &table_len );
377
378 for ( j = 0; j < segment->pairCount; j++ )
379 {
380 /* convert to Fixed */
381 segment->correspondence[j].fromCoord = FT_GET_SHORT() * 4;
382 segment->correspondence[j].toCoord = FT_GET_SHORT() * 4;
383
384 FT_TRACE5(( " mapping %.5f to %.5f\n",
385 segment->correspondence[j].fromCoord / 65536.0,
386 segment->correspondence[j].toCoord / 65536.0 ));
387 }
388
389 FT_TRACE5(( "\n" ));
390 }
391
392 Exit:
393 FT_FRAME_EXIT();
394 }
395
396
397 /* some macros we need */
398 #define FT_FIXED_ONE ( (FT_Fixed)0x10000 )
399
400 #define FT_fdot14ToFixed( x ) \
401 ( (FT_Fixed)( (FT_ULong)(x) << 2 ) )
402 #define FT_intToFixed( i ) \
403 ( (FT_Fixed)( (FT_ULong)(i) << 16 ) )
404 #define FT_fixedToInt( x ) \
405 ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
406
407
408 static FT_Error
409 ft_var_load_item_variation_store( TT_Face face,
410 FT_ULong offset,
411 GX_ItemVarStore itemStore )
412 {
413 FT_Stream stream = FT_FACE_STREAM( face );
414 FT_Memory memory = stream->memory;
415
416 FT_Error error;
417 FT_UShort format;
418 FT_ULong region_offset;
419 FT_UInt i, j, k;
420 FT_UInt shortDeltaCount;
421
422 GX_Blend blend = face->blend;
423 GX_ItemVarData varData;
424
425 FT_ULong* dataOffsetArray = NULL;
426
427
685
686 innerIndex = mapData & innerIndexMask;
687
688 if ( innerIndex >= itemStore->varData[outerIndex].itemCount )
689 {
690 FT_TRACE2(( "innerIndex[%d] == %d out of range\n",
691 i,
692 innerIndex ));
693 error = FT_THROW( Invalid_Table );
694 goto Exit;
695 }
696
697 map->innerIndex[i] = innerIndex;
698 }
699
700 Exit:
701 return error;
702 }
703
704
705 /*************************************************************************/
706 /* */
707 /* <Function> */
708 /* ft_var_load_hvvar */
709 /* */
710 /* <Description> */
711 /* If `vertical' is zero, parse the `HVAR' table and set */
712 /* `blend->hvar_loaded' to TRUE. On success, `blend->hvar_checked' */
713 /* is set to TRUE. */
714 /* */
715 /* If `vertical' is not zero, parse the `VVAR' table and set */
716 /* `blend->vvar_loaded' to TRUE. On success, `blend->vvar_checked' */
717 /* is set to TRUE. */
718 /* */
719 /* Some memory may remain allocated on error; it is always freed in */
720 /* `tt_done_blend', however. */
721 /* */
722 /* <InOut> */
723 /* face :: The font face. */
724 /* */
725 /* <Return> */
726 /* FreeType error code. 0 means success. */
727 /* */
728 static FT_Error
729 ft_var_load_hvvar( TT_Face face,
730 FT_Bool vertical )
731 {
732 FT_Stream stream = FT_FACE_STREAM( face );
733 FT_Memory memory = stream->memory;
734
735 GX_Blend blend = face->blend;
736
737 GX_HVVarTable table;
738
739 FT_Error error;
740 FT_UShort majorVersion;
741 FT_ULong table_len;
742 FT_ULong table_offset;
743 FT_ULong store_offset;
744 FT_ULong widthMap_offset;
745
746
747 if ( vertical )
855 FT_UInt innerIndex )
856 {
857 GX_ItemVarData varData;
858 FT_Short* deltaSet;
859
860 FT_UInt master, j;
861 FT_Fixed netAdjustment = 0; /* accumulated adjustment */
862 FT_Fixed scaledDelta;
863 FT_Fixed delta;
864
865
866 /* See pseudo code from `Font Variations Overview' */
867 /* in the OpenType specification. */
868
869 varData = &itemStore->varData[outerIndex];
870 deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex];
871
872 /* outer loop steps through master designs to be blended */
873 for ( master = 0; master < varData->regionIdxCount; master++ )
874 {
875 FT_Fixed scalar = FT_FIXED_ONE;
876 FT_UInt regionIndex = varData->regionIndices[master];
877
878 GX_AxisCoords axis = itemStore->varRegionList[regionIndex].axisList;
879
880
881 /* inner loop steps through axes in this region */
882 for ( j = 0; j < itemStore->axisCount; j++, axis++ )
883 {
884 FT_Fixed axisScalar;
885
886
887 /* compute the scalar contribution of this axis; */
888 /* ignore invalid ranges */
889 if ( axis->startCoord > axis->peakCoord ||
890 axis->peakCoord > axis->endCoord )
891 axisScalar = FT_FIXED_ONE;
892
893 else if ( axis->startCoord < 0 &&
894 axis->endCoord > 0 &&
895 axis->peakCoord != 0 )
896 axisScalar = FT_FIXED_ONE;
897
898 /* peak of 0 means ignore this axis */
899 else if ( axis->peakCoord == 0 )
900 axisScalar = FT_FIXED_ONE;
901
902 /* ignore this region if coords are out of range */
903 else if ( face->blend->normalizedcoords[j] < axis->startCoord ||
904 face->blend->normalizedcoords[j] > axis->endCoord )
905 axisScalar = 0;
906
907 /* calculate a proportional factor */
908 else
909 {
910 if ( face->blend->normalizedcoords[j] == axis->peakCoord )
911 axisScalar = FT_FIXED_ONE;
912 else if ( face->blend->normalizedcoords[j] < axis->peakCoord )
913 axisScalar =
914 FT_DivFix( face->blend->normalizedcoords[j] - axis->startCoord,
915 axis->peakCoord - axis->startCoord );
916 else
917 axisScalar =
918 FT_DivFix( axis->endCoord - face->blend->normalizedcoords[j],
919 axis->endCoord - axis->peakCoord );
920 }
921
922 /* take product of all the axis scalars */
923 scalar = FT_MulFix( scalar, axisScalar );
924
925 } /* per-axis loop */
926
927 /* get the scaled delta for this region */
928 delta = FT_intToFixed( deltaSet[master] );
929 scaledDelta = FT_MulFix( scalar, delta );
930
931 /* accumulate the adjustments from each region */
932 netAdjustment = netAdjustment + scaledDelta;
933
934 } /* per-region loop */
935
936 return FT_fixedToInt( netAdjustment );
937 }
938
939
940 /*************************************************************************/
941 /* */
942 /* <Function> */
943 /* tt_hvadvance_adjust */
944 /* */
945 /* <Description> */
946 /* Apply `HVAR' advance width or `VVAR' advance height adjustment of */
947 /* a given glyph. */
948 /* */
949 /* <Input> */
950 /* gindex :: The glyph index. */
951 /* */
952 /* vertical :: If set, handle `VVAR' table. */
953 /* */
954 /* <InOut> */
955 /* face :: The font face. */
956 /* */
957 /* adelta :: Points to width or height value that gets modified. */
958 /* */
959 static FT_Error
960 tt_hvadvance_adjust( TT_Face face,
961 FT_UInt gindex,
962 FT_Int *avalue,
963 FT_Bool vertical )
964 {
965 FT_Error error = FT_Err_Ok;
966 FT_UInt innerIndex, outerIndex;
967 FT_Int delta;
968
969 GX_HVVarTable table;
970
971
972 if ( !face->doblend || !face->blend )
973 goto Exit;
974
975 if ( vertical )
976 {
977 if ( !face->blend->vvar_loaded )
978 {
1134 GX_VALUE_CASE( STRS, os2.yStrikeoutSize );
1135 GX_VALUE_CASE( UNDO, postscript.underlinePosition );
1136 GX_VALUE_CASE( UNDS, postscript.underlineThickness );
1137 GX_VALUE_CASE( VASC, vertical.Ascender );
1138 GX_VALUE_CASE( VCOF, vertical.caret_Offset );
1139 GX_VALUE_CASE( VCRN, vertical.caret_Slope_Run );
1140 GX_VALUE_CASE( VCRS, vertical.caret_Slope_Rise );
1141 GX_VALUE_CASE( VDSC, vertical.Descender );
1142 GX_VALUE_CASE( VLGP, vertical.Line_Gap );
1143 GX_VALUE_CASE( XHGT, os2.sxHeight );
1144
1145 default:
1146 /* ignore unknown tag */
1147 p = NULL;
1148 }
1149
1150 return p;
1151 }
1152
1153
1154 /*************************************************************************/
1155 /* */
1156 /* <Function> */
1157 /* ft_var_load_mvar */
1158 /* */
1159 /* <Description> */
1160 /* Parse the `MVAR' table. */
1161 /* */
1162 /* Some memory may remain allocated on error; it is always freed in */
1163 /* `tt_done_blend', however. */
1164 /* */
1165 /* <InOut> */
1166 /* face :: The font face. */
1167 /* */
1168 static void
1169 ft_var_load_mvar( TT_Face face )
1170 {
1171 FT_Stream stream = FT_FACE_STREAM( face );
1172 FT_Memory memory = stream->memory;
1173
1174 GX_Blend blend = face->blend;
1175 GX_ItemVarStore itemStore;
1176 GX_Value value, limit;
1177
1178 FT_Error error;
1179 FT_UShort majorVersion;
1180 FT_ULong table_len;
1181 FT_ULong table_offset;
1182 FT_UShort store_offset;
1183 FT_ULong records_offset;
1184
1185
1186 FT_TRACE2(( "MVAR " ));
1187
1280
1281 face->variation_support |= TT_FACE_FLAG_VAR_MVAR;
1282 }
1283
1284
1285 static FT_Error
1286 tt_size_reset_iterator( FT_ListNode node,
1287 void* user )
1288 {
1289 TT_Size size = (TT_Size)node->data;
1290
1291 FT_UNUSED( user );
1292
1293
1294 tt_size_reset( size, 1 );
1295
1296 return FT_Err_Ok;
1297 }
1298
1299
1300 /*************************************************************************/
1301 /* */
1302 /* <Function> */
1303 /* tt_apply_mvar */
1304 /* */
1305 /* <Description> */
1306 /* Apply `MVAR' table adjustments. */
1307 /* */
1308 /* <InOut> */
1309 /* face :: The font face. */
1310 /* */
1311 FT_LOCAL_DEF( void )
1312 tt_apply_mvar( TT_Face face )
1313 {
1314 GX_Blend blend = face->blend;
1315 GX_Value value, limit;
1316
1317
1318 if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
1319 return;
1320
1321 value = blend->mvar_table->values;
1322 limit = value + blend->mvar_table->valueCount;
1323
1324 for ( ; value < limit; value++ )
1325 {
1326 FT_Short* p = ft_var_get_value_pointer( face, value->tag );
1327 FT_Int delta;
1328
1329
1330 delta = ft_var_get_item_delta( face,
1331 &blend->mvar_table->itemStore,
1332 value->outerIndex,
1333 value->innerIndex );
1334
1335 if ( p )
1336 {
1337 FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n",
1338 (FT_Char)( value->tag >> 24 ),
1339 (FT_Char)( value->tag >> 16 ),
1340 (FT_Char)( value->tag >> 8 ),
1341 (FT_Char)( value->tag ),
1342 value->unmodified,
1343 value->unmodified == 1 ? "" : "s",
1344 delta,
1345 delta == 1 ? "" : "s" ));
1346
1347 /* since we handle both signed and unsigned values as FT_Short, */
1348 /* ensure proper overflow arithmetic */
1349 *p = (FT_Short)( value->unmodified + (FT_Short)delta );
1350 }
1351 }
1352
1353 /* adjust all derived values */
1354 {
1355 FT_Face root = &face->root;
1356
1357
1358 if ( face->os2.version != 0xFFFFU )
1359 {
1360 if ( face->os2.sTypoAscender || face->os2.sTypoDescender )
1361 {
1362 root->ascender = face->os2.sTypoAscender;
1363 root->descender = face->os2.sTypoDescender;
1364
1365 root->height = root->ascender - root->descender +
1366 face->os2.sTypoLineGap;
1367 }
1368 else
1369 {
1370 root->ascender = (FT_Short)face->os2.usWinAscent;
1371 root->descender = -(FT_Short)face->os2.usWinDescent;
1372
1373 root->height = root->ascender - root->descender;
1374 }
1375 }
1376
1377 root->underline_position = face->postscript.underlinePosition -
1378 face->postscript.underlineThickness / 2;
1379 root->underline_thickness = face->postscript.underlineThickness;
1380
1381 /* iterate over all FT_Size objects and call `tt_size_reset' */
1382 /* to propagate the metrics changes */
1383 FT_List_Iterate( &root->sizes_list,
1384 tt_size_reset_iterator,
1385 NULL );
1386 }
1387 }
1388
1389
1390 typedef struct GX_GVar_Head_
1391 {
1392 FT_Long version;
1393 FT_UShort axisCount;
1394 FT_UShort globalCoordCount;
1395 FT_ULong offsetToCoord;
1396 FT_UShort glyphCount;
1397 FT_UShort flags;
1398 FT_ULong offsetToData;
1399
1400 } GX_GVar_Head;
1401
1402
1403 /*************************************************************************/
1404 /* */
1405 /* <Function> */
1406 /* ft_var_load_gvar */
1407 /* */
1408 /* <Description> */
1409 /* Parse the `gvar' table if present. If `fvar' is there, `gvar' had */
1410 /* better be there too. */
1411 /* */
1412 /* <InOut> */
1413 /* face :: The font face. */
1414 /* */
1415 /* <Return> */
1416 /* FreeType error code. 0 means success. */
1417 /* */
1418 static FT_Error
1419 ft_var_load_gvar( TT_Face face )
1420 {
1421 FT_Stream stream = FT_FACE_STREAM( face );
1422 FT_Memory memory = stream->memory;
1423 GX_Blend blend = face->blend;
1424 FT_Error error;
1425 FT_UInt i, j;
1426 FT_ULong table_len;
1427 FT_ULong gvar_start;
1428 FT_ULong offsetToData;
1429 GX_GVar_Head gvar_head;
1430
1431 static const FT_Frame_Field gvar_fields[] =
1432 {
1433
1434 #undef FT_STRUCTURE
1435 #define FT_STRUCTURE GX_GVar_Head
1436
1437 FT_FRAME_START( 20 ),
1495 goto Exit;
1496 }
1497
1498 FT_TRACE2(( "loaded\n" ));
1499
1500 blend->gvar_size = table_len;
1501 blend->tuplecount = gvar_head.globalCoordCount;
1502 blend->gv_glyphcnt = gvar_head.glyphCount;
1503 offsetToData = gvar_start + gvar_head.offsetToData;
1504
1505 FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n",
1506 blend->tuplecount == 1 ? "is" : "are",
1507 blend->tuplecount,
1508 blend->tuplecount == 1 ? "" : "s" ));
1509
1510 if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
1511 goto Exit;
1512
1513 if ( gvar_head.flags & 1 )
1514 {
1515 /* long offsets (one more offset than glyphs, to mark size of last) */
1516 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
1517 goto Exit;
1518
1519 for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1520 blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG();
1521
1522 FT_FRAME_EXIT();
1523 }
1524 else
1525 {
1526 /* short offsets (one more offset than glyphs, to mark size of last) */
1527 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
1528 goto Exit;
1529
1530 for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1531 blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
1532 /* XXX: Undocumented: `*2'! */
1533
1534 FT_FRAME_EXIT();
1535 }
1536
1537 if ( blend->tuplecount != 0 )
1538 {
1539 if ( FT_NEW_ARRAY( blend->tuplecoords,
1540 gvar_head.axisCount * blend->tuplecount ) )
1541 goto Exit;
1542
1543 if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) ||
1544 FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) )
1545 goto Exit;
1546
1547 for ( i = 0; i < blend->tuplecount; i++ )
1548 {
1549 FT_TRACE5(( " [ " ));
1550 for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ )
1551 {
1552 blend->tuplecoords[i * gvar_head.axisCount + j] =
1553 FT_GET_SHORT() * 4; /* convert to FT_Fixed */
1554 FT_TRACE5(( "%.5f ",
1555 blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 ));
1556 }
1557 FT_TRACE5(( "]\n" ));
1558 }
1559
1560 FT_TRACE5(( "\n" ));
1561
1562 FT_FRAME_EXIT();
1563 }
1564
1565 Exit:
1566 return error;
1567 }
1568
1569
1570 /*************************************************************************/
1571 /* */
1572 /* <Function> */
1573 /* ft_var_apply_tuple */
1574 /* */
1575 /* <Description> */
1576 /* Figure out whether a given tuple (design) applies to the current */
1577 /* blend, and if so, what is the scaling factor. */
1578 /* */
1579 /* <Input> */
1580 /* blend :: The current blend of the font. */
1581 /* */
1582 /* tupleIndex :: A flag saying whether this is an intermediate */
1583 /* tuple or not. */
1584 /* */
1585 /* tuple_coords :: The coordinates of the tuple in normalized axis */
1586 /* units. */
1587 /* */
1588 /* im_start_coords :: The initial coordinates where this tuple starts */
1589 /* to apply (for intermediate coordinates). */
1590 /* */
1591 /* im_end_coords :: The final coordinates after which this tuple no */
1592 /* longer applies (for intermediate coordinates). */
1593 /* */
1594 /* <Return> */
1595 /* An FT_Fixed value containing the scaling factor. */
1596 /* */
1597 static FT_Fixed
1598 ft_var_apply_tuple( GX_Blend blend,
1599 FT_UShort tupleIndex,
1600 FT_Fixed* tuple_coords,
1601 FT_Fixed* im_start_coords,
1602 FT_Fixed* im_end_coords )
1603 {
1604 FT_UInt i;
1605 FT_Fixed apply = 0x10000L;
1606
1607
1608 for ( i = 0; i < blend->num_axis; i++ )
1609 {
1610 FT_TRACE6(( " axis coordinate %d (%.5f):\n",
1611 i, blend->normalizedcoords[i] / 65536.0 ));
1612 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
1613 FT_TRACE6(( " intermediate coordinates %d (%.5f, %.5f):\n",
1614 i,
1615 im_start_coords[i] / 65536.0,
1616 im_end_coords[i] / 65536.0 ));
1617
1618 /* It's not clear why (for intermediate tuples) we don't need */
1619 /* to check against start/end -- the documentation says we don't. */
1620 /* Similarly, it's unclear why we don't need to scale along the */
1621 /* axis. */
1622
1623 if ( tuple_coords[i] == 0 )
1624 {
1625 FT_TRACE6(( " tuple coordinate is zero, ignored\n", i ));
1626 continue;
1627 }
1628
1629 if ( blend->normalizedcoords[i] == 0 )
1630 {
1631 FT_TRACE6(( " axis coordinate is zero, stop\n" ));
1632 apply = 0;
1633 break;
1634 }
1635
1636 if ( blend->normalizedcoords[i] == tuple_coords[i] )
1637 {
1638 FT_TRACE6(( " tuple coordinate value %.5f fits perfectly\n",
1639 tuple_coords[i] / 65536.0 ));
1640 /* `apply' does not change */
1641 continue;
1642 }
1643
1644 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
1645 {
1646 /* not an intermediate tuple */
1647
1648 if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
1649 blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
1650 {
1651 FT_TRACE6(( " tuple coordinate value %.5f is exceeded, stop\n",
1652 tuple_coords[i] / 65536.0 ));
1653 apply = 0;
1654 break;
1655 }
1656
1657 FT_TRACE6(( " tuple coordinate value %.5f fits\n",
1658 tuple_coords[i] / 65536.0 ));
1659 apply = FT_MulDiv( apply,
1660 blend->normalizedcoords[i],
1661 tuple_coords[i] );
1662 }
1663 else
1664 {
1665 /* intermediate tuple */
1666
1667 if ( blend->normalizedcoords[i] < im_start_coords[i] ||
1668 blend->normalizedcoords[i] > im_end_coords[i] )
1669 {
1670 FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] is exceeded,"
1671 " stop\n",
1672 im_start_coords[i] / 65536.0,
1673 im_end_coords[i] / 65536.0 ));
1674 apply = 0;
1675 break;
1676 }
1677
1678 else if ( blend->normalizedcoords[i] < tuple_coords[i] )
1679 {
1680 FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] fits\n",
1681 im_start_coords[i] / 65536.0,
1682 im_end_coords[i] / 65536.0 ));
1683 apply = FT_MulDiv( apply,
1684 blend->normalizedcoords[i] - im_start_coords[i],
1685 tuple_coords[i] - im_start_coords[i] );
1686 }
1687
1688 else
1689 {
1690 FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] fits\n",
1691 im_start_coords[i] / 65536.0,
1692 im_end_coords[i] / 65536.0 ));
1693 apply = FT_MulDiv( apply,
1694 im_end_coords[i] - blend->normalizedcoords[i],
1695 im_end_coords[i] - tuple_coords[i] );
1696 }
1697 }
1698 }
1699
1700 FT_TRACE6(( " apply factor is %.5f\n", apply / 65536.0 ));
1701
1702 return apply;
1703 }
1704
1705
1706 /* convert from design coordinates to normalized coordinates */
1707
1708 static void
1709 ft_var_to_normalized( TT_Face face,
1710 FT_UInt num_coords,
1711 FT_Fixed* coords,
1712 FT_Fixed* normalized )
1713 {
1714 GX_Blend blend;
1715 FT_MM_Var* mmvar;
1716 FT_UInt i, j;
1717 FT_Var_Axis* a;
1718 GX_AVarSegment av;
1739 FT_Fixed coord = coords[i];
1740
1741
1742 FT_TRACE5(( " %d: %.5f\n", i, coord / 65536.0 ));
1743 if ( coord > a->maximum || coord < a->minimum )
1744 {
1745 FT_TRACE1((
1746 "ft_var_to_normalized: design coordinate %.5f\n"
1747 " is out of range [%.5f;%.5f]; clamping\n",
1748 coord / 65536.0,
1749 a->minimum / 65536.0,
1750 a->maximum / 65536.0 ));
1751
1752 if ( coord > a->maximum )
1753 coord = a->maximum;
1754 else
1755 coord = a->minimum;
1756 }
1757
1758 if ( coord < a->def )
1759 normalized[i] = -FT_DivFix( coord - a->def,
1760 a->minimum - a->def );
1761 else if ( coord > a->def )
1762 normalized[i] = FT_DivFix( coord - a->def,
1763 a->maximum - a->def );
1764 else
1765 normalized[i] = 0;
1766 }
1767
1768 FT_TRACE5(( "\n" ));
1769
1770 for ( ; i < mmvar->num_axis; i++ )
1771 normalized[i] = 0;
1772
1773 if ( blend->avar_segment )
1774 {
1775 FT_TRACE5(( "normalized design coordinates"
1776 " before applying `avar' data:\n" ));
1777
1778 av = blend->avar_segment;
1779 for ( i = 0; i < mmvar->num_axis; i++, av++ )
1780 {
1781 for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1782 {
1783 if ( normalized[i] < av->correspondence[j].fromCoord )
1893 FT_UShort axisCount;
1894 FT_UShort axisSize;
1895 FT_UShort instanceCount;
1896 FT_UShort instanceSize;
1897
1898 } GX_FVar_Head;
1899
1900
1901 typedef struct fvar_axis_
1902 {
1903 FT_ULong axisTag;
1904 FT_Fixed minValue;
1905 FT_Fixed defaultValue;
1906 FT_Fixed maxValue;
1907 FT_UShort flags;
1908 FT_UShort nameID;
1909
1910 } GX_FVar_Axis;
1911
1912
1913 /*************************************************************************/
1914 /* */
1915 /* <Function> */
1916 /* TT_Get_MM_Var */
1917 /* */
1918 /* <Description> */
1919 /* Check that the font's `fvar' table is valid, parse it, and return */
1920 /* those data. It also loads (and parses) the `MVAR' table, if */
1921 /* possible. */
1922 /* */
1923 /* <InOut> */
1924 /* face :: The font face. */
1925 /* TT_Get_MM_Var initializes the blend structure. */
1926 /* */
1927 /* <Output> */
1928 /* master :: The `fvar' data (must be freed by caller). Can be NULL, */
1929 /* which makes this function simply load MM support. */
1930 /* */
1931 /* <Return> */
1932 /* FreeType error code. 0 means success. */
1933 /* */
1934 FT_LOCAL_DEF( FT_Error )
1935 TT_Get_MM_Var( TT_Face face,
1936 FT_MM_Var* *master )
1937 {
1938 FT_Stream stream = face->root.stream;
1939 FT_Memory memory = face->root.memory;
1940 FT_ULong table_len;
1941 FT_Error error = FT_Err_Ok;
1942 FT_ULong fvar_start = 0;
1943 FT_UInt i, j;
1944 FT_MM_Var* mmvar = NULL;
1945 FT_Fixed* next_coords;
1946 FT_Fixed* nsc;
1947 FT_String* next_name;
1948 FT_Var_Axis* a;
1949 FT_Fixed* c;
1950 FT_Var_Named_Style* ns;
1951 GX_FVar_Head fvar_head;
1952 FT_Bool usePsName = 0;
1953 FT_UInt num_instances;
2490 FT_Bool have_diff = 0;
2491 FT_UInt j;
2492 FT_Fixed* c;
2493 FT_Fixed* n;
2494
2495
2496 manageCvt = mcvt_retain;
2497
2498 for ( i = 0; i < num_coords; i++ )
2499 {
2500 if ( blend->normalizedcoords[i] != coords[i] )
2501 {
2502 manageCvt = mcvt_load;
2503 have_diff = 1;
2504 break;
2505 }
2506 }
2507
2508 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2509 {
2510 FT_UInt idx = (FT_UInt)face->root.face_index >> 16;
2511
2512
2513 c = blend->normalizedcoords + i;
2514 n = blend->normalized_stylecoords + idx * mmvar->num_axis + i;
2515 for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
2516 if ( *c != *n )
2517 have_diff = 1;
2518 }
2519 else
2520 {
2521 c = blend->normalizedcoords + i;
2522 for ( j = i; j < mmvar->num_axis; j++, c++ )
2523 if ( *c != 0 )
2524 have_diff = 1;
2525 }
2526
2527 /* return value -1 indicates `no change' */
2528 if ( !have_diff )
2529 return -1;
2530
2531 for ( ; i < mmvar->num_axis; i++ )
2532 {
2533 if ( blend->normalizedcoords[i] != 0 )
2534 {
2535 manageCvt = mcvt_load;
2536 break;
2537 }
2538 }
2539
2540 /* If we don't change the blend coords then we don't need to do */
2541 /* anything to the cvt table. It will be correct. Otherwise we */
2542 /* no longer have the original cvt (it was modified when we set */
2543 /* the blend last time), so we must reload and then modify it. */
2544 }
2545
2546 blend->num_axis = mmvar->num_axis;
2547 FT_MEM_COPY( blend->normalizedcoords,
2548 coords,
2549 num_coords * sizeof ( FT_Fixed ) );
2573 /* The original cvt table is in memory. All we need to do is */
2574 /* apply the `cvar' table (if any). */
2575 error = tt_face_vary_cvt( face, face->root.stream );
2576 break;
2577
2578 case mcvt_retain:
2579 /* The cvt table is correct for this set of coordinates. */
2580 break;
2581 }
2582 }
2583
2584 /* enforce recomputation of the PostScript name; */
2585 FT_FREE( face->postscript_name );
2586 face->postscript_name = NULL;
2587
2588 Exit:
2589 return error;
2590 }
2591
2592
2593 /*************************************************************************/
2594 /* */
2595 /* <Function> */
2596 /* TT_Set_MM_Blend */
2597 /* */
2598 /* <Description> */
2599 /* Set the blend (normalized) coordinates for this instance of the */
2600 /* font. Check that the `gvar' table is reasonable and does some */
2601 /* initial preparation. */
2602 /* */
2603 /* <InOut> */
2604 /* face :: The font. */
2605 /* Initialize the blend structure with `gvar' data. */
2606 /* */
2607 /* <Input> */
2608 /* num_coords :: The number of available coordinates. If it is */
2609 /* larger than the number of axes, ignore the excess */
2610 /* values. If it is smaller than the number of axes, */
2611 /* use the default value (0) for the remaining axes. */
2612 /* */
2613 /* coords :: An array of `num_coords', each between [-1,1]. */
2614 /* */
2615 /* <Return> */
2616 /* FreeType error code. 0 means success. */
2617 /* */
2618 FT_LOCAL_DEF( FT_Error )
2619 TT_Set_MM_Blend( TT_Face face,
2620 FT_UInt num_coords,
2621 FT_Fixed* coords )
2622 {
2623 FT_Error error;
2624
2625
2626 error = tt_set_mm_blend( face, num_coords, coords, 1 );
2627 if ( error )
2628 return error;
2629
2630 if ( num_coords )
2631 face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2632 else
2633 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2634
2635 return FT_Err_Ok;
2636 }
2637
2638
2639 /*************************************************************************/
2640 /* */
2641 /* <Function> */
2642 /* TT_Get_MM_Blend */
2643 /* */
2644 /* <Description> */
2645 /* Get the blend (normalized) coordinates for this instance of the */
2646 /* font. */
2647 /* */
2648 /* <InOut> */
2649 /* face :: The font. */
2650 /* Initialize the blend structure with `gvar' data. */
2651 /* */
2652 /* <Input> */
2653 /* num_coords :: The number of available coordinates. If it is */
2654 /* larger than the number of axes, set the excess */
2655 /* values to 0. */
2656 /* */
2657 /* coords :: An array of `num_coords', each between [-1,1]. */
2658 /* */
2659 /* <Return> */
2660 /* FreeType error code. 0 means success. */
2661 /* */
2662 FT_LOCAL_DEF( FT_Error )
2663 TT_Get_MM_Blend( TT_Face face,
2664 FT_UInt num_coords,
2665 FT_Fixed* coords )
2666 {
2667 FT_Error error = FT_Err_Ok;
2668 GX_Blend blend;
2669 FT_UInt i, nc;
2670
2671
2672 if ( !face->blend )
2673 {
2674 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2675 return error;
2676 }
2677
2678 blend = face->blend;
2679
2680 if ( !blend->coords )
2681 {
2695 }
2696
2697 if ( face->doblend )
2698 {
2699 for ( i = 0; i < nc; i++ )
2700 coords[i] = blend->normalizedcoords[i];
2701 }
2702 else
2703 {
2704 for ( i = 0; i < nc; i++ )
2705 coords[i] = 0;
2706 }
2707
2708 for ( ; i < num_coords; i++ )
2709 coords[i] = 0;
2710
2711 return FT_Err_Ok;
2712 }
2713
2714
2715 /*************************************************************************/
2716 /* */
2717 /* <Function> */
2718 /* TT_Set_Var_Design */
2719 /* */
2720 /* <Description> */
2721 /* Set the coordinates for the instance, measured in the user */
2722 /* coordinate system. Parse the `avar' table (if present) to convert */
2723 /* from user to normalized coordinates. */
2724 /* */
2725 /* <InOut> */
2726 /* face :: The font face. */
2727 /* Initialize the blend struct with `gvar' data. */
2728 /* */
2729 /* <Input> */
2730 /* num_coords :: The number of available coordinates. If it is */
2731 /* larger than the number of axes, ignore the excess */
2732 /* values. If it is smaller than the number of axes, */
2733 /* use the default values for the remaining axes. */
2734 /* */
2735 /* coords :: A coordinate array with `num_coords' elements. */
2736 /* */
2737 /* <Return> */
2738 /* FreeType error code. 0 means success. */
2739 /* */
2740 FT_LOCAL_DEF( FT_Error )
2741 TT_Set_Var_Design( TT_Face face,
2742 FT_UInt num_coords,
2743 FT_Fixed* coords )
2744 {
2745 FT_Error error = FT_Err_Ok;
2746 GX_Blend blend;
2747 FT_MM_Var* mmvar;
2748 FT_UInt i;
2749 FT_Memory memory = face->root.memory;
2750
2751 FT_Fixed* c;
2752 FT_Fixed* n;
2753 FT_Fixed* normalized = NULL;
2754
2755 FT_Bool have_diff = 0;
2756
2757
2758 if ( !face->blend )
2759 {
2837
2838 FT_TRACE5(( "TT_Set_Var_Design:\n"
2839 " normalized design coordinates:\n" ));
2840 ft_var_to_normalized( face, num_coords, blend->coords, normalized );
2841
2842 error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
2843 if ( error )
2844 goto Exit;
2845
2846 if ( num_coords )
2847 face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2848 else
2849 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2850
2851 Exit:
2852 FT_FREE( normalized );
2853 return error;
2854 }
2855
2856
2857 /*************************************************************************/
2858 /* */
2859 /* <Function> */
2860 /* TT_Get_Var_Design */
2861 /* */
2862 /* <Description> */
2863 /* Get the design coordinates of the currently selected interpolated */
2864 /* font. */
2865 /* */
2866 /* <Input> */
2867 /* face :: A handle to the source face. */
2868 /* */
2869 /* num_coords :: The number of design coordinates to retrieve. If it */
2870 /* is larger than the number of axes, set the excess */
2871 /* values to~0. */
2872 /* */
2873 /* <Output> */
2874 /* coords :: The design coordinates array. */
2875 /* */
2876 /* <Return> */
2877 /* FreeType error code. 0~means success. */
2878 /* */
2879 FT_LOCAL_DEF( FT_Error )
2880 TT_Get_Var_Design( TT_Face face,
2881 FT_UInt num_coords,
2882 FT_Fixed* coords )
2883 {
2884 FT_Error error = FT_Err_Ok;
2885 GX_Blend blend;
2886 FT_UInt i, nc;
2887
2888
2889 if ( !face->blend )
2890 {
2891 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2892 return error;
2893 }
2894
2895 blend = face->blend;
2896
2897 if ( !blend->coords )
2898 {
2912 }
2913
2914 if ( face->doblend )
2915 {
2916 for ( i = 0; i < nc; i++ )
2917 coords[i] = blend->coords[i];
2918 }
2919 else
2920 {
2921 for ( i = 0; i < nc; i++ )
2922 coords[i] = 0;
2923 }
2924
2925 for ( ; i < num_coords; i++ )
2926 coords[i] = 0;
2927
2928 return FT_Err_Ok;
2929 }
2930
2931
2932 /*************************************************************************/
2933 /* */
2934 /* <Function> */
2935 /* TT_Set_Named_Instance */
2936 /* */
2937 /* <Description> */
2938 /* Set the given named instance, also resetting any further */
2939 /* variation. */
2940 /* */
2941 /* <Input> */
2942 /* face :: A handle to the source face. */
2943 /* */
2944 /* instance_index :: The instance index, starting with value 1. */
2945 /* Value 0 indicates to not use an instance. */
2946 /* */
2947 /* <Return> */
2948 /* FreeType error code. 0~means success. */
2949 /* */
2950 FT_LOCAL_DEF( FT_Error )
2951 TT_Set_Named_Instance( TT_Face face,
2952 FT_UInt instance_index )
2953 {
2954 FT_Error error = FT_ERR( Invalid_Argument );
2955 GX_Blend blend;
2956 FT_MM_Var* mmvar;
2957
2958 FT_UInt num_instances;
2959
2960
2961 if ( !face->blend )
2962 {
2963 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2964 goto Exit;
2965 }
2966
2967 blend = face->blend;
2968 mmvar = blend->mmvar;
2969
3005 error = TT_Set_Var_Design( face, 0, NULL );
3006
3007 face->root.face_index = ( instance_index << 16 ) |
3008 ( face->root.face_index & 0xFFFFL );
3009 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
3010
3011 Exit:
3012 return error;
3013 }
3014
3015
3016 /*************************************************************************/
3017 /*************************************************************************/
3018 /***** *****/
3019 /***** GX VAR PARSING ROUTINES *****/
3020 /***** *****/
3021 /*************************************************************************/
3022 /*************************************************************************/
3023
3024
3025 /*************************************************************************/
3026 /* */
3027 /* <Function> */
3028 /* tt_face_vary_cvt */
3029 /* */
3030 /* <Description> */
3031 /* Modify the loaded cvt table according to the `cvar' table and the */
3032 /* font's blend. */
3033 /* */
3034 /* <InOut> */
3035 /* face :: A handle to the target face object. */
3036 /* */
3037 /* <Input> */
3038 /* stream :: A handle to the input stream. */
3039 /* */
3040 /* <Return> */
3041 /* FreeType error code. 0 means success. */
3042 /* */
3043 /* Most errors are ignored. It is perfectly valid not to have a */
3044 /* `cvar' table even if there is a `gvar' and `fvar' table. */
3045 /* */
3046 FT_LOCAL_DEF( FT_Error )
3047 tt_face_vary_cvt( TT_Face face,
3048 FT_Stream stream )
3049 {
3050 FT_Error error;
3051 FT_Memory memory = stream->memory;
3052 FT_ULong table_start;
3053 FT_ULong table_len;
3054 FT_UInt tupleCount;
3055 FT_ULong offsetToData;
3056 FT_ULong here;
3057 FT_UInt i, j;
3058 FT_Fixed* tuple_coords = NULL;
3059 FT_Fixed* im_start_coords = NULL;
3060 FT_Fixed* im_end_coords = NULL;
3061 GX_Blend blend = face->blend;
3062 FT_UInt point_count, spoint_count = 0;
3063 FT_UShort* sharedpoints = NULL;
3064 FT_UShort* localpoints = NULL;
3065 FT_UShort* points;
3066 FT_Short* deltas;
3067
3068
3069 FT_TRACE2(( "CVAR " ));
3070
3071 if ( !blend )
3072 {
3073 FT_TRACE2(( "\n"
3074 "tt_face_vary_cvt: no blend specified\n" ));
3075 error = FT_Err_Ok;
3076 goto Exit;
3077 }
3078
3079 if ( !face->cvt )
3080 {
3081 FT_TRACE2(( "\n"
3082 "tt_face_vary_cvt: no `cvt ' table\n" ));
3083 error = FT_Err_Ok;
3084 goto Exit;
3085 }
3086
3129 goto FExit;
3130 }
3131
3132 offsetToData += table_start;
3133
3134 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3135 {
3136 here = FT_Stream_FTell( stream );
3137
3138 FT_Stream_SeekSet( stream, offsetToData );
3139
3140 sharedpoints = ft_var_readpackedpoints( stream,
3141 table_len,
3142 &spoint_count );
3143 offsetToData = FT_Stream_FTell( stream );
3144
3145 FT_Stream_SeekSet( stream, here );
3146 }
3147
3148 FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
3149 ( tupleCount & 0xFFF ) == 1 ? "is" : "are",
3150 tupleCount & 0xFFF,
3151 ( tupleCount & 0xFFF ) == 1 ? "" : "s" ));
3152
3153 for ( i = 0; i < ( tupleCount & 0xFFF ); i++ )
3154 {
3155 FT_UInt tupleDataSize;
3156 FT_UInt tupleIndex;
3157 FT_Fixed apply;
3158
3159
3160 FT_TRACE6(( " tuple %d:\n", i ));
3161
3162 tupleDataSize = FT_GET_USHORT();
3163 tupleIndex = FT_GET_USHORT();
3164
3165 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3166 {
3167 for ( j = 0; j < blend->num_axis; j++ )
3168 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */
3169 /* short frac to fixed */
3170 }
3171 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3172 {
3173 FT_TRACE2(( "tt_face_vary_cvt:"
3174 " invalid tuple index\n" ));
3175
3176 error = FT_THROW( Invalid_Table );
3177 goto Exit;
3178 }
3179 else
3180 FT_MEM_COPY(
3181 tuple_coords,
3182 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3183 blend->num_axis * sizeof ( FT_Fixed ) );
3184
3185 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3186 {
3187 for ( j = 0; j < blend->num_axis; j++ )
3188 im_start_coords[j] = FT_GET_SHORT() * 4;
3189 for ( j = 0; j < blend->num_axis; j++ )
3190 im_end_coords[j] = FT_GET_SHORT() * 4;
3191 }
3192
3193 apply = ft_var_apply_tuple( blend,
3194 (FT_UShort)tupleIndex,
3195 tuple_coords,
3196 im_start_coords,
3197 im_end_coords );
3198
3199 if ( apply == 0 ) /* tuple isn't active for our blend */
3200 {
3201 offsetToData += tupleDataSize;
3202 continue;
3203 }
3224 point_count == 0 ? face->cvt_size
3225 : point_count );
3226
3227 if ( !points ||
3228 !deltas ||
3229 ( localpoints == ALL_POINTS && point_count != face->cvt_size ) )
3230 ; /* failure, ignore it */
3231
3232 else if ( localpoints == ALL_POINTS )
3233 {
3234 #ifdef FT_DEBUG_LEVEL_TRACE
3235 int count = 0;
3236 #endif
3237
3238
3239 FT_TRACE7(( " CVT deltas:\n" ));
3240
3241 /* this means that there are deltas for every entry in cvt */
3242 for ( j = 0; j < face->cvt_size; j++ )
3243 {
3244 FT_Long orig_cvt = face->cvt[j];
3245
3246
3247 face->cvt[j] = (FT_Short)( orig_cvt +
3248 FT_MulFix( deltas[j], apply ) );
3249
3250 #ifdef FT_DEBUG_LEVEL_TRACE
3251 if ( orig_cvt != face->cvt[j] )
3252 {
3253 FT_TRACE7(( " %d: %d -> %d\n",
3254 j, orig_cvt, face->cvt[j] ));
3255 count++;
3256 }
3257 #endif
3258 }
3259
3260 #ifdef FT_DEBUG_LEVEL_TRACE
3261 if ( !count )
3262 FT_TRACE7(( " none\n" ));
3263 #endif
3264 }
3265
3266 else
3267 {
3268 #ifdef FT_DEBUG_LEVEL_TRACE
3269 int count = 0;
3270 #endif
3271
3272
3273 FT_TRACE7(( " CVT deltas:\n" ));
3274
3275 for ( j = 0; j < point_count; j++ )
3276 {
3277 int pindex;
3278 FT_Long orig_cvt;
3279
3280
3281 pindex = points[j];
3282 if ( (FT_ULong)pindex >= face->cvt_size )
3283 continue;
3284
3285 orig_cvt = face->cvt[pindex];
3286 face->cvt[pindex] = (FT_Short)( orig_cvt +
3287 FT_MulFix( deltas[j], apply ) );
3288
3289 #ifdef FT_DEBUG_LEVEL_TRACE
3290 if ( orig_cvt != face->cvt[pindex] )
3291 {
3292 FT_TRACE7(( " %d: %d -> %d\n",
3293 pindex, orig_cvt, face->cvt[pindex] ));
3294 count++;
3295 }
3296 #endif
3297 }
3298
3299 #ifdef FT_DEBUG_LEVEL_TRACE
3300 if ( !count )
3301 FT_TRACE7(( " none\n" ));
3302 #endif
3303 }
3304
3305 if ( localpoints != ALL_POINTS )
3306 FT_FREE( localpoints );
3307 FT_FREE( deltas );
3308
3309 offsetToData += tupleDataSize;
3310
3311 FT_Stream_SeekSet( stream, here );
3312 }
3313
3314 FT_TRACE5(( "\n" ));
3315
3316 FExit:
3317 FT_FRAME_EXIT();
3318
3319 Exit:
3320 if ( sharedpoints != ALL_POINTS )
3321 FT_FREE( sharedpoints );
3322 FT_FREE( tuple_coords );
3323 FT_FREE( im_start_coords );
3324 FT_FREE( im_end_coords );
3325
3326 return error;
3327 }
3328
3329
3330 /* Shift the original coordinates of all points between indices `p1' */
3331 /* and `p2', using the same difference as given by index `ref'. */
3332
3333 /* modeled after `af_iup_shift' */
3334
3335 static void
3336 tt_delta_shift( int p1,
3337 int p2,
3338 int ref,
3339 FT_Vector* in_points,
3340 FT_Vector* out_points )
3341 {
3342 int p;
3343 FT_Vector delta;
3344
3351
3352 for ( p = p1; p < ref; p++ )
3353 {
3354 out_points[p].x += delta.x;
3355 out_points[p].y += delta.y;
3356 }
3357
3358 for ( p = ref + 1; p <= p2; p++ )
3359 {
3360 out_points[p].x += delta.x;
3361 out_points[p].y += delta.y;
3362 }
3363 }
3364
3365
3366 /* Interpolate the original coordinates of all points with indices */
3367 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
3368 /* point indices. */
3369
3370 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */
3371 /* `Ins_IUP' */
3372
3373 static void
3374 tt_delta_interpolate( int p1,
3375 int p2,
3376 int ref1,
3377 int ref2,
3378 FT_Vector* in_points,
3379 FT_Vector* out_points )
3380 {
3381 int p, i;
3382
3383 FT_Pos out, in1, in2, out1, out2, d1, d2;
3384
3385
3386 if ( p1 > p2 )
3387 return;
3388
3389 /* handle both horizontal and vertical coordinates */
3390 for ( i = 0; i <= 1; i++ )
3391 {
3392 /* shift array pointers so that we can access `foo.y' as `foo.x' */
3511 cur_delta,
3512 first_delta,
3513 in_points,
3514 out_points );
3515
3516 if ( first_delta > 0 )
3517 tt_delta_interpolate( first_point,
3518 first_delta - 1,
3519 cur_delta,
3520 first_delta,
3521 in_points,
3522 out_points );
3523 }
3524 }
3525 contour++;
3526
3527 } while ( contour < outline->n_contours );
3528 }
3529
3530
3531 /*************************************************************************/
3532 /* */
3533 /* <Function> */
3534 /* TT_Vary_Apply_Glyph_Deltas */
3535 /* */
3536 /* <Description> */
3537 /* Apply the appropriate deltas to the current glyph. */
3538 /* */
3539 /* <Input> */
3540 /* face :: A handle to the target face object. */
3541 /* */
3542 /* glyph_index :: The index of the glyph being modified. */
3543 /* */
3544 /* n_points :: The number of the points in the glyph, including */
3545 /* phantom points. */
3546 /* */
3547 /* <InOut> */
3548 /* outline :: The outline to change. */
3549 /* */
3550 /* <Return> */
3551 /* FreeType error code. 0 means success. */
3552 /* */
3553 FT_LOCAL_DEF( FT_Error )
3554 TT_Vary_Apply_Glyph_Deltas( TT_Face face,
3555 FT_UInt glyph_index,
3556 FT_Outline* outline,
3557 FT_UInt n_points )
3558 {
3559 FT_Stream stream = face->root.stream;
3560 FT_Memory memory = stream->memory;
3561 GX_Blend blend = face->blend;
3562
3563 FT_Vector* points_org = NULL;
3564 FT_Vector* points_out = NULL;
3565 FT_Bool* has_delta = NULL;
3566
3567 FT_Error error;
3568 FT_ULong glyph_start;
3569 FT_UInt tupleCount;
3570 FT_ULong offsetToData;
3571 FT_ULong here;
3572 FT_UInt i, j;
3573 FT_Fixed* tuple_coords = NULL;
3574 FT_Fixed* im_start_coords = NULL;
3575 FT_Fixed* im_end_coords = NULL;
3576 FT_UInt point_count, spoint_count = 0;
3577 FT_UShort* sharedpoints = NULL;
3578 FT_UShort* localpoints = NULL;
3579 FT_UShort* points;
3580 FT_Short *deltas_x, *deltas_y;
3581
3582
3583 if ( !face->doblend || !blend )
3584 return FT_THROW( Invalid_Argument );
3585
3586 if ( glyph_index >= blend->gv_glyphcnt ||
3587 blend->glyphoffsets[glyph_index] ==
3588 blend->glyphoffsets[glyph_index + 1] )
3589 {
3590 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3591 " no variation data for this glyph\n" ));
3592 return FT_Err_Ok;
3593 }
3594
3595 if ( FT_NEW_ARRAY( points_org, n_points ) ||
3596 FT_NEW_ARRAY( points_out, n_points ) ||
3597 FT_NEW_ARRAY( has_delta, n_points ) )
3598 goto Fail1;
3599
3600 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
3601 FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
3602 blend->glyphoffsets[glyph_index] ) )
3603 goto Fail1;
3604
3605 glyph_start = FT_Stream_FTell( stream );
3606
3607 /* each set of glyph variation data is formatted similarly to `cvar' */
3608
3609 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
3610 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3611 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
3612 goto Fail2;
3613
3614 tupleCount = FT_GET_USHORT();
3615 offsetToData = FT_GET_USHORT();
3616
3617 /* rough sanity test */
3618 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3619 blend->gvar_size )
3620 {
3621 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3622 " invalid glyph variation array header\n" ));
3623
3624 error = FT_THROW( Invalid_Table );
3625 goto Fail2;
3626 }
3627
3628 offsetToData += glyph_start;
3629
3630 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3631 {
3632 here = FT_Stream_FTell( stream );
3633
3634 FT_Stream_SeekSet( stream, offsetToData );
3635
3636 sharedpoints = ft_var_readpackedpoints( stream,
3637 blend->gvar_size,
3638 &spoint_count );
3639 offsetToData = FT_Stream_FTell( stream );
3640
3641 FT_Stream_SeekSet( stream, here );
3642 }
3643
3644 FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
3645 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3646 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3647 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3648
3649 for ( j = 0; j < n_points; j++ )
3650 points_org[j] = outline->points[j];
3651
3652 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3653 {
3654 FT_UInt tupleDataSize;
3655 FT_UInt tupleIndex;
3656 FT_Fixed apply;
3657
3658
3659 FT_TRACE6(( " tuple %d:\n", i ));
3660
3661 tupleDataSize = FT_GET_USHORT();
3662 tupleIndex = FT_GET_USHORT();
3663
3664 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3665 {
3666 for ( j = 0; j < blend->num_axis; j++ )
3667 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */
3668 /* short frac to fixed */
3669 }
3670 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3671 {
3672 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3673 " invalid tuple index\n" ));
3674
3675 error = FT_THROW( Invalid_Table );
3676 goto Fail2;
3677 }
3678 else
3679 FT_MEM_COPY(
3680 tuple_coords,
3681 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3682 blend->num_axis * sizeof ( FT_Fixed ) );
3683
3684 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3685 {
3686 for ( j = 0; j < blend->num_axis; j++ )
3687 im_start_coords[j] = FT_GET_SHORT() * 4;
3688 for ( j = 0; j < blend->num_axis; j++ )
3689 im_end_coords[j] = FT_GET_SHORT() * 4;
3690 }
3691
3692 apply = ft_var_apply_tuple( blend,
3693 (FT_UShort)tupleIndex,
3694 tuple_coords,
3695 im_start_coords,
3696 im_end_coords );
3697
3698 if ( apply == 0 ) /* tuple isn't active for our blend */
3699 {
3700 offsetToData += tupleDataSize;
3701 continue;
3725 deltas_y = ft_var_readpackeddeltas( stream,
3726 blend->gvar_size,
3727 point_count == 0 ? n_points
3728 : point_count );
3729
3730 if ( !points || !deltas_y || !deltas_x )
3731 ; /* failure, ignore it */
3732
3733 else if ( points == ALL_POINTS )
3734 {
3735 #ifdef FT_DEBUG_LEVEL_TRACE
3736 int count = 0;
3737 #endif
3738
3739
3740 FT_TRACE7(( " point deltas:\n" ));
3741
3742 /* this means that there are deltas for every point in the glyph */
3743 for ( j = 0; j < n_points; j++ )
3744 {
3745 FT_Pos delta_x = FT_MulFix( deltas_x[j], apply );
3746 FT_Pos delta_y = FT_MulFix( deltas_y[j], apply );
3747
3748
3749 if ( j < n_points - 4 )
3750 {
3751 outline->points[j].x += delta_x;
3752 outline->points[j].y += delta_y;
3753 }
3754 else
3755 {
3756 /* To avoid double adjustment of advance width or height, */
3757 /* adjust phantom points only if there is no HVAR or VVAR */
3758 /* support, respectively. */
3759 if ( j == ( n_points - 4 ) &&
3760 !( face->variation_support &
3761 TT_FACE_FLAG_VAR_LSB ) )
3762 outline->points[j].x += delta_x;
3763
3764 else if ( j == ( n_points - 3 ) &&
3765 !( face->variation_support &
3766 TT_FACE_FLAG_VAR_HADVANCE ) )
3767 outline->points[j].x += delta_x;
3768
3769 else if ( j == ( n_points - 2 ) &&
3770 !( face->variation_support &
3771 TT_FACE_FLAG_VAR_TSB ) )
3772 outline->points[j].y += delta_y;
3773
3774 else if ( j == ( n_points - 1 ) &&
3775 !( face->variation_support &
3776 TT_FACE_FLAG_VAR_VADVANCE ) )
3777 outline->points[j].y += delta_y;
3778 }
3779
3780 #ifdef FT_DEBUG_LEVEL_TRACE
3781 if ( delta_x || delta_y )
3782 {
3783 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n",
3784 j,
3785 outline->points[j].x - delta_x,
3786 outline->points[j].y - delta_y,
3787 outline->points[j].x,
3788 outline->points[j].y ));
3789 count++;
3790 }
3791 #endif
3792 }
3793
3794 #ifdef FT_DEBUG_LEVEL_TRACE
3795 if ( !count )
3796 FT_TRACE7(( " none\n" ));
3797 #endif
3798 }
3799
3800 else
3801 {
3802 #ifdef FT_DEBUG_LEVEL_TRACE
3803 int count = 0;
3804 #endif
3805
3806
3807 /* we have to interpolate the missing deltas similar to the */
3808 /* IUP bytecode instruction */
3820 if ( idx >= n_points )
3821 continue;
3822
3823 has_delta[idx] = TRUE;
3824
3825 points_out[idx].x += FT_MulFix( deltas_x[j], apply );
3826 points_out[idx].y += FT_MulFix( deltas_y[j], apply );
3827 }
3828
3829 /* no need to handle phantom points here, */
3830 /* since solitary points can't be interpolated */
3831 tt_interpolate_deltas( outline,
3832 points_out,
3833 points_org,
3834 has_delta );
3835
3836 FT_TRACE7(( " point deltas:\n" ));
3837
3838 for ( j = 0; j < n_points; j++ )
3839 {
3840 FT_Pos delta_x = points_out[j].x - points_org[j].x;
3841 FT_Pos delta_y = points_out[j].y - points_org[j].y;
3842
3843
3844 if ( j < n_points - 4 )
3845 {
3846 outline->points[j].x += delta_x;
3847 outline->points[j].y += delta_y;
3848 }
3849 else
3850 {
3851 /* To avoid double adjustment of advance width or height, */
3852 /* adjust phantom points only if there is no HVAR or VVAR */
3853 /* support, respectively. */
3854 if ( j == ( n_points - 4 ) &&
3855 !( face->variation_support &
3856 TT_FACE_FLAG_VAR_LSB ) )
3857 outline->points[j].x += delta_x;
3858
3859 else if ( j == ( n_points - 3 ) &&
3860 !( face->variation_support &
3861 TT_FACE_FLAG_VAR_HADVANCE ) )
3862 outline->points[j].x += delta_x;
3863
3864 else if ( j == ( n_points - 2 ) &&
3865 !( face->variation_support &
3866 TT_FACE_FLAG_VAR_TSB ) )
3867 outline->points[j].y += delta_y;
3868
3869 else if ( j == ( n_points - 1 ) &&
3870 !( face->variation_support &
3871 TT_FACE_FLAG_VAR_VADVANCE ) )
3872 outline->points[j].y += delta_y;
3873 }
3874
3875 #ifdef FT_DEBUG_LEVEL_TRACE
3876 if ( delta_x || delta_y )
3877 {
3878 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n",
3879 j,
3880 outline->points[j].x - delta_x,
3881 outline->points[j].y - delta_y,
3882 outline->points[j].x,
3883 outline->points[j].y ));
3884 count++;
3885 }
3886 #endif
3887 }
3888
3889 #ifdef FT_DEBUG_LEVEL_TRACE
3890 if ( !count )
3891 FT_TRACE7(( " none\n" ));
3892 #endif
3893 }
3894
3895 if ( localpoints != ALL_POINTS )
3896 FT_FREE( localpoints );
3897 FT_FREE( deltas_x );
3898 FT_FREE( deltas_y );
3899
3900 offsetToData += tupleDataSize;
3901
3902 FT_Stream_SeekSet( stream, here );
3903 }
3904
3905 FT_TRACE5(( "\n" ));
3906
3907 Fail2:
3908 if ( sharedpoints != ALL_POINTS )
3909 FT_FREE( sharedpoints );
3910 FT_FREE( tuple_coords );
3911 FT_FREE( im_start_coords );
3912 FT_FREE( im_end_coords );
3913
3914 FT_FRAME_EXIT();
3915
3916 Fail1:
3917 FT_FREE( points_org );
3918 FT_FREE( points_out );
3919 FT_FREE( has_delta );
3920
3921 return error;
3922 }
3923
3924
3925 /*************************************************************************/
3926 /* */
3927 /* <Function> */
3928 /* tt_get_var_blend */
3929 /* */
3930 /* <Description> */
3931 /* An extended internal version of `TT_Get_MM_Blend' that returns */
3932 /* pointers instead of copying data, without any initialization of */
3933 /* the MM machinery in case it isn't loaded yet. */
3934 /* */
3935 FT_LOCAL_DEF( FT_Error )
3936 tt_get_var_blend( TT_Face face,
3937 FT_UInt *num_coords,
3938 FT_Fixed* *coords,
3939 FT_Fixed* *normalizedcoords,
3940 FT_MM_Var* *mm_var )
3941 {
3942 if ( face->blend )
3943 {
3944 if ( num_coords )
3945 *num_coords = face->blend->num_axis;
3946 if ( coords )
3947 *coords = face->blend->coords;
3948 if ( normalizedcoords )
3949 *normalizedcoords = face->blend->normalizedcoords;
3950 if ( mm_var )
3951 *mm_var = face->blend->mmvar;
3952 }
3953 else
3954 {
3976 {
3977 for ( i = 0; i < itemStore->dataCount; i++ )
3978 {
3979 FT_FREE( itemStore->varData[i].regionIndices );
3980 FT_FREE( itemStore->varData[i].deltaSet );
3981 }
3982
3983 FT_FREE( itemStore->varData );
3984 }
3985
3986 if ( itemStore->varRegionList )
3987 {
3988 for ( i = 0; i < itemStore->regionCount; i++ )
3989 FT_FREE( itemStore->varRegionList[i].axisList );
3990
3991 FT_FREE( itemStore->varRegionList );
3992 }
3993 }
3994
3995
3996 /*************************************************************************/
3997 /* */
3998 /* <Function> */
3999 /* tt_done_blend */
4000 /* */
4001 /* <Description> */
4002 /* Free the blend internal data structure. */
4003 /* */
4004 FT_LOCAL_DEF( void )
4005 tt_done_blend( TT_Face face )
4006 {
4007 FT_Memory memory = FT_FACE_MEMORY( face );
4008 GX_Blend blend = face->blend;
4009
4010
4011 if ( blend )
4012 {
4013 FT_UInt i, num_axes;
4014
4015
4016 /* blend->num_axis might not be set up yet */
4017 num_axes = blend->mmvar->num_axis;
4018
4019 FT_FREE( blend->coords );
4020 FT_FREE( blend->normalizedcoords );
4021 FT_FREE( blend->normalized_stylecoords );
4022 FT_FREE( blend->mmvar );
4023
|
1 /****************************************************************************
2 *
3 * ttgxvar.c
4 *
5 * TrueType GX Font Variation loader
6 *
7 * Copyright (C) 2004-2019 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 `gvar' is not intelligible; `cvar' refers you
26 * to `gvar' and is thus also incomprehensible.
27 *
28 * The documentation for `avar' appears correct, but Apple has no fonts
29 * with an `avar' table, so it is hard to test.
30 *
31 * Many thanks to John Jenkins (at Apple) in figuring this out.
32 *
33 *
34 * Apple's `kern' table has some references to tuple indices, but as
35 * there is no indication where these indices are defined, nor how to
36 * interpolate the kerning values (different tuples have different
37 * classes) this issue is ignored.
38 *
39 */
40
41
42 #include <ft2build.h>
43 #include FT_INTERNAL_DEBUG_H
44 #include FT_CONFIG_CONFIG_H
45 #include FT_INTERNAL_STREAM_H
46 #include FT_INTERNAL_SFNT_H
47 #include FT_TRUETYPE_TAGS_H
48 #include FT_TRUETYPE_IDS_H
49 #include FT_MULTIPLE_MASTERS_H
50 #include FT_LIST_H
51
52 #include "ttpload.h"
53 #include "ttgxvar.h"
54
55 #include "tterrors.h"
56
57
58 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
59
60
61 #define FT_Stream_FTell( stream ) \
62 (FT_ULong)( (stream)->cursor - (stream)->base )
63 #define FT_Stream_SeekSet( stream, off ) \
64 (stream)->cursor = \
65 ( (off) < (FT_ULong)( (stream)->limit - (stream)->base ) ) \
66 ? (stream)->base + (off) \
67 : (stream)->limit
68
69
70 /* some macros we need */
71 #define FT_fdot14ToFixed( x ) \
72 ( (FT_Fixed)( (FT_ULong)(x) << 2 ) )
73 #define FT_intToFixed( i ) \
74 ( (FT_Fixed)( (FT_ULong)(i) << 16 ) )
75 #define FT_fixedToInt( x ) \
76 ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
77
78
79 /**************************************************************************
80 *
81 * The macro FT_COMPONENT is used in trace mode. It is an implicit
82 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
83 * messages during execution.
84 */
85 #undef FT_COMPONENT
86 #define FT_COMPONENT ttgxvar
87
88
89 /*************************************************************************/
90 /*************************************************************************/
91 /***** *****/
92 /***** Internal Routines *****/
93 /***** *****/
94 /*************************************************************************/
95 /*************************************************************************/
96
97
98 /**************************************************************************
99 *
100 * The macro ALL_POINTS is used in `ft_var_readpackedpoints'. It
101 * indicates that there is a delta for every point without needing to
102 * enumerate all of them.
103 */
104
105 /* ensure that value `0' has the same width as a pointer */
106 #define ALL_POINTS (FT_UShort*)~(FT_PtrDist)0
107
108
109 #define GX_PT_POINTS_ARE_WORDS 0x80U
110 #define GX_PT_POINT_RUN_COUNT_MASK 0x7FU
111
112
113 /**************************************************************************
114 *
115 * @Function:
116 * ft_var_readpackedpoints
117 *
118 * @Description:
119 * Read a set of points to which the following deltas will apply.
120 * Points are packed with a run length encoding.
121 *
122 * @Input:
123 * stream ::
124 * The data stream.
125 *
126 * size ::
127 * The size of the table holding the data.
128 *
129 * @Output:
130 * point_cnt ::
131 * The number of points read. A zero value means that
132 * all points in the glyph will be affected, without
133 * enumerating them individually.
134 *
135 * @Return:
136 * An array of FT_UShort containing the affected points or the
137 * special value ALL_POINTS.
138 */
139 static FT_UShort*
140 ft_var_readpackedpoints( FT_Stream stream,
141 FT_ULong size,
142 FT_UInt *point_cnt )
143 {
144 FT_UShort *points = NULL;
145 FT_UInt n;
146 FT_UInt runcnt;
147 FT_UInt i, j;
148 FT_UShort first;
149 FT_Memory memory = stream->memory;
150 FT_Error error = FT_Err_Ok;
151
152 FT_UNUSED( error );
153
154
155 *point_cnt = 0;
156
157 n = FT_GET_BYTE();
158 if ( n == 0 )
206
207 for ( j = 0; j < runcnt; j++ )
208 {
209 first += FT_GET_BYTE();
210 points[i++] = first;
211 if ( i >= n )
212 break;
213 }
214 }
215 }
216
217 return points;
218 }
219
220
221 #define GX_DT_DELTAS_ARE_ZERO 0x80U
222 #define GX_DT_DELTAS_ARE_WORDS 0x40U
223 #define GX_DT_DELTA_RUN_COUNT_MASK 0x3FU
224
225
226 /**************************************************************************
227 *
228 * @Function:
229 * ft_var_readpackeddeltas
230 *
231 * @Description:
232 * Read a set of deltas. These are packed slightly differently than
233 * points. In particular there is no overall count.
234 *
235 * @Input:
236 * stream ::
237 * The data stream.
238 *
239 * size ::
240 * The size of the table holding the data.
241 *
242 * delta_cnt ::
243 * The number of deltas to be read.
244 *
245 * @Return:
246 * An array of FT_Fixed containing the deltas for the affected
247 * points. (This only gets the deltas for one dimension. It will
248 * generally be called twice, once for x, once for y. When used in
249 * cvt table, it will only be called once.)
250 *
251 * We use FT_Fixed to avoid accumulation errors while summing up all
252 * deltas (the rounding to integer values happens as the very last
253 * step).
254 */
255 static FT_Fixed*
256 ft_var_readpackeddeltas( FT_Stream stream,
257 FT_ULong size,
258 FT_UInt delta_cnt )
259 {
260 FT_Fixed *deltas = NULL;
261 FT_UInt runcnt, cnt;
262 FT_UInt i, j;
263 FT_Memory memory = stream->memory;
264 FT_Error error = FT_Err_Ok;
265
266 FT_UNUSED( error );
267
268
269 if ( delta_cnt > size )
270 {
271 FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" ));
272 return NULL;
273 }
274
275 if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
276 return NULL;
277
278 i = 0;
279 while ( i < delta_cnt )
280 {
281 runcnt = FT_GET_BYTE();
282 cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
283
284 if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
285 {
286 /* `runcnt' zeroes get added */
287 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
288 deltas[i++] = 0;
289 }
290 else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
291 {
292 /* `runcnt' shorts from the stack */
293 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
294 deltas[i++] = FT_intToFixed( FT_GET_SHORT() );
295 }
296 else
297 {
298 /* `runcnt' signed bytes from the stack */
299 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
300 deltas[i++] = FT_intToFixed( FT_GET_CHAR() );
301 }
302
303 if ( j <= cnt )
304 {
305 /* bad format */
306 FT_FREE( deltas );
307 return NULL;
308 }
309 }
310
311 return deltas;
312 }
313
314
315 /**************************************************************************
316 *
317 * @Function:
318 * ft_var_load_avar
319 *
320 * @Description:
321 * Parse the `avar' table if present. It need not be, so we return
322 * nothing.
323 *
324 * @InOut:
325 * face ::
326 * The font face.
327 */
328 static void
329 ft_var_load_avar( TT_Face face )
330 {
331 FT_Stream stream = FT_FACE_STREAM( face );
332 FT_Memory memory = stream->memory;
333 GX_Blend blend = face->blend;
334 GX_AVarSegment segment;
335 FT_Error error = FT_Err_Ok;
336 FT_Long version;
337 FT_Long axisCount;
338 FT_Int i, j;
339 FT_ULong table_len;
340
341 FT_UNUSED( error );
342
343
344 FT_TRACE2(( "AVAR " ));
345
346 blend->avar_loaded = TRUE;
347 error = face->goto_table( face, TTAG_avar, stream, &table_len );
397
398 for ( j = 0; j < segment->pairCount; j++ )
399 {
400 /* convert to Fixed */
401 segment->correspondence[j].fromCoord = FT_GET_SHORT() * 4;
402 segment->correspondence[j].toCoord = FT_GET_SHORT() * 4;
403
404 FT_TRACE5(( " mapping %.5f to %.5f\n",
405 segment->correspondence[j].fromCoord / 65536.0,
406 segment->correspondence[j].toCoord / 65536.0 ));
407 }
408
409 FT_TRACE5(( "\n" ));
410 }
411
412 Exit:
413 FT_FRAME_EXIT();
414 }
415
416
417 static FT_Error
418 ft_var_load_item_variation_store( TT_Face face,
419 FT_ULong offset,
420 GX_ItemVarStore itemStore )
421 {
422 FT_Stream stream = FT_FACE_STREAM( face );
423 FT_Memory memory = stream->memory;
424
425 FT_Error error;
426 FT_UShort format;
427 FT_ULong region_offset;
428 FT_UInt i, j, k;
429 FT_UInt shortDeltaCount;
430
431 GX_Blend blend = face->blend;
432 GX_ItemVarData varData;
433
434 FT_ULong* dataOffsetArray = NULL;
435
436
694
695 innerIndex = mapData & innerIndexMask;
696
697 if ( innerIndex >= itemStore->varData[outerIndex].itemCount )
698 {
699 FT_TRACE2(( "innerIndex[%d] == %d out of range\n",
700 i,
701 innerIndex ));
702 error = FT_THROW( Invalid_Table );
703 goto Exit;
704 }
705
706 map->innerIndex[i] = innerIndex;
707 }
708
709 Exit:
710 return error;
711 }
712
713
714 /**************************************************************************
715 *
716 * @Function:
717 * ft_var_load_hvvar
718 *
719 * @Description:
720 * If `vertical' is zero, parse the `HVAR' table and set
721 * `blend->hvar_loaded' to TRUE. On success, `blend->hvar_checked'
722 * is set to TRUE.
723 *
724 * If `vertical' is not zero, parse the `VVAR' table and set
725 * `blend->vvar_loaded' to TRUE. On success, `blend->vvar_checked'
726 * is set to TRUE.
727 *
728 * Some memory may remain allocated on error; it is always freed in
729 * `tt_done_blend', however.
730 *
731 * @InOut:
732 * face ::
733 * The font face.
734 *
735 * @Return:
736 * FreeType error code. 0 means success.
737 */
738 static FT_Error
739 ft_var_load_hvvar( TT_Face face,
740 FT_Bool vertical )
741 {
742 FT_Stream stream = FT_FACE_STREAM( face );
743 FT_Memory memory = stream->memory;
744
745 GX_Blend blend = face->blend;
746
747 GX_HVVarTable table;
748
749 FT_Error error;
750 FT_UShort majorVersion;
751 FT_ULong table_len;
752 FT_ULong table_offset;
753 FT_ULong store_offset;
754 FT_ULong widthMap_offset;
755
756
757 if ( vertical )
865 FT_UInt innerIndex )
866 {
867 GX_ItemVarData varData;
868 FT_Short* deltaSet;
869
870 FT_UInt master, j;
871 FT_Fixed netAdjustment = 0; /* accumulated adjustment */
872 FT_Fixed scaledDelta;
873 FT_Fixed delta;
874
875
876 /* See pseudo code from `Font Variations Overview' */
877 /* in the OpenType specification. */
878
879 varData = &itemStore->varData[outerIndex];
880 deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex];
881
882 /* outer loop steps through master designs to be blended */
883 for ( master = 0; master < varData->regionIdxCount; master++ )
884 {
885 FT_Fixed scalar = 0x10000L;
886 FT_UInt regionIndex = varData->regionIndices[master];
887
888 GX_AxisCoords axis = itemStore->varRegionList[regionIndex].axisList;
889
890
891 /* inner loop steps through axes in this region */
892 for ( j = 0; j < itemStore->axisCount; j++, axis++ )
893 {
894 /* compute the scalar contribution of this axis; */
895 /* ignore invalid ranges */
896 if ( axis->startCoord > axis->peakCoord ||
897 axis->peakCoord > axis->endCoord )
898 continue;
899
900 else if ( axis->startCoord < 0 &&
901 axis->endCoord > 0 &&
902 axis->peakCoord != 0 )
903 continue;
904
905 /* peak of 0 means ignore this axis */
906 else if ( axis->peakCoord == 0 )
907 continue;
908
909 else if ( face->blend->normalizedcoords[j] == axis->peakCoord )
910 continue;
911
912 /* ignore this region if coords are out of range */
913 else if ( face->blend->normalizedcoords[j] <= axis->startCoord ||
914 face->blend->normalizedcoords[j] >= axis->endCoord )
915 {
916 scalar = 0;
917 break;
918 }
919
920 /* cumulative product of all the axis scalars */
921 else if ( face->blend->normalizedcoords[j] < axis->peakCoord )
922 scalar =
923 FT_MulDiv( scalar,
924 face->blend->normalizedcoords[j] - axis->startCoord,
925 axis->peakCoord - axis->startCoord );
926 else
927 scalar =
928 FT_MulDiv( scalar,
929 axis->endCoord - face->blend->normalizedcoords[j],
930 axis->endCoord - axis->peakCoord );
931 } /* per-axis loop */
932
933 /* get the scaled delta for this region */
934 delta = FT_intToFixed( deltaSet[master] );
935 scaledDelta = FT_MulFix( scalar, delta );
936
937 /* accumulate the adjustments from each region */
938 netAdjustment = netAdjustment + scaledDelta;
939
940 } /* per-region loop */
941
942 return FT_fixedToInt( netAdjustment );
943 }
944
945
946 /**************************************************************************
947 *
948 * @Function:
949 * tt_hvadvance_adjust
950 *
951 * @Description:
952 * Apply `HVAR' advance width or `VVAR' advance height adjustment of
953 * a given glyph.
954 *
955 * @Input:
956 * gindex ::
957 * The glyph index.
958 *
959 * vertical ::
960 * If set, handle `VVAR' table.
961 *
962 * @InOut:
963 * face ::
964 * The font face.
965 *
966 * adelta ::
967 * Points to width or height value that gets modified.
968 */
969 static FT_Error
970 tt_hvadvance_adjust( TT_Face face,
971 FT_UInt gindex,
972 FT_Int *avalue,
973 FT_Bool vertical )
974 {
975 FT_Error error = FT_Err_Ok;
976 FT_UInt innerIndex, outerIndex;
977 FT_Int delta;
978
979 GX_HVVarTable table;
980
981
982 if ( !face->doblend || !face->blend )
983 goto Exit;
984
985 if ( vertical )
986 {
987 if ( !face->blend->vvar_loaded )
988 {
1144 GX_VALUE_CASE( STRS, os2.yStrikeoutSize );
1145 GX_VALUE_CASE( UNDO, postscript.underlinePosition );
1146 GX_VALUE_CASE( UNDS, postscript.underlineThickness );
1147 GX_VALUE_CASE( VASC, vertical.Ascender );
1148 GX_VALUE_CASE( VCOF, vertical.caret_Offset );
1149 GX_VALUE_CASE( VCRN, vertical.caret_Slope_Run );
1150 GX_VALUE_CASE( VCRS, vertical.caret_Slope_Rise );
1151 GX_VALUE_CASE( VDSC, vertical.Descender );
1152 GX_VALUE_CASE( VLGP, vertical.Line_Gap );
1153 GX_VALUE_CASE( XHGT, os2.sxHeight );
1154
1155 default:
1156 /* ignore unknown tag */
1157 p = NULL;
1158 }
1159
1160 return p;
1161 }
1162
1163
1164 /**************************************************************************
1165 *
1166 * @Function:
1167 * ft_var_load_mvar
1168 *
1169 * @Description:
1170 * Parse the `MVAR' table.
1171 *
1172 * Some memory may remain allocated on error; it is always freed in
1173 * `tt_done_blend', however.
1174 *
1175 * @InOut:
1176 * face ::
1177 * The font face.
1178 */
1179 static void
1180 ft_var_load_mvar( TT_Face face )
1181 {
1182 FT_Stream stream = FT_FACE_STREAM( face );
1183 FT_Memory memory = stream->memory;
1184
1185 GX_Blend blend = face->blend;
1186 GX_ItemVarStore itemStore;
1187 GX_Value value, limit;
1188
1189 FT_Error error;
1190 FT_UShort majorVersion;
1191 FT_ULong table_len;
1192 FT_ULong table_offset;
1193 FT_UShort store_offset;
1194 FT_ULong records_offset;
1195
1196
1197 FT_TRACE2(( "MVAR " ));
1198
1291
1292 face->variation_support |= TT_FACE_FLAG_VAR_MVAR;
1293 }
1294
1295
1296 static FT_Error
1297 tt_size_reset_iterator( FT_ListNode node,
1298 void* user )
1299 {
1300 TT_Size size = (TT_Size)node->data;
1301
1302 FT_UNUSED( user );
1303
1304
1305 tt_size_reset( size, 1 );
1306
1307 return FT_Err_Ok;
1308 }
1309
1310
1311 /**************************************************************************
1312 *
1313 * @Function:
1314 * tt_apply_mvar
1315 *
1316 * @Description:
1317 * Apply `MVAR' table adjustments.
1318 *
1319 * @InOut:
1320 * face ::
1321 * The font face.
1322 */
1323 FT_LOCAL_DEF( void )
1324 tt_apply_mvar( TT_Face face )
1325 {
1326 GX_Blend blend = face->blend;
1327 GX_Value value, limit;
1328 FT_Short mvar_hasc_delta = 0;
1329 FT_Short mvar_hdsc_delta = 0;
1330 FT_Short mvar_hlgp_delta = 0;
1331
1332
1333 if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
1334 return;
1335
1336 value = blend->mvar_table->values;
1337 limit = value + blend->mvar_table->valueCount;
1338
1339 for ( ; value < limit; value++ )
1340 {
1341 FT_Short* p = ft_var_get_value_pointer( face, value->tag );
1342 FT_Int delta;
1343
1344
1345 delta = ft_var_get_item_delta( face,
1346 &blend->mvar_table->itemStore,
1347 value->outerIndex,
1348 value->innerIndex );
1349
1350 if ( p )
1351 {
1352 FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n",
1353 (FT_Char)( value->tag >> 24 ),
1354 (FT_Char)( value->tag >> 16 ),
1355 (FT_Char)( value->tag >> 8 ),
1356 (FT_Char)( value->tag ),
1357 value->unmodified,
1358 value->unmodified == 1 ? "" : "s",
1359 delta,
1360 delta == 1 ? "" : "s" ));
1361
1362 /* since we handle both signed and unsigned values as FT_Short, */
1363 /* ensure proper overflow arithmetic */
1364 *p = (FT_Short)( value->unmodified + (FT_Short)delta );
1365
1366 /* Treat hasc, hdsc and hlgp specially, see below. */
1367 if ( value->tag == MVAR_TAG_HASC )
1368 mvar_hasc_delta = (FT_Short)delta;
1369 else if ( value->tag == MVAR_TAG_HDSC )
1370 mvar_hdsc_delta = (FT_Short)delta;
1371 else if ( value->tag == MVAR_TAG_HLGP )
1372 mvar_hlgp_delta = (FT_Short)delta;
1373 }
1374 }
1375
1376 /* adjust all derived values */
1377 {
1378 FT_Face root = &face->root;
1379
1380 /*
1381 * Apply the deltas of hasc, hdsc and hlgp to the FT_Face's ascender,
1382 * descender and height attributes, no matter how they were originally
1383 * computed.
1384 *
1385 * (Code that ignores those and accesses the font's metrics values
1386 * directly is already served by the delta application code above.)
1387 *
1388 * The MVAR table supports variations for both typo and win metrics.
1389 * According to Behdad Esfahbod, the thinking of the working group was
1390 * that no one uses win metrics anymore for setting line metrics (the
1391 * specification even calls these metrics "horizontal clipping
1392 * ascent/descent", probably for their role on the Windows platform in
1393 * computing clipping boxes), and new fonts should use typo metrics, so
1394 * typo deltas should be applied to whatever sfnt_load_face decided the
1395 * line metrics should be.
1396 *
1397 * Before, the following led to different line metrics between default
1398 * outline and instances, visible when e.g. the default outlines were
1399 * used as the regular face and instances for everything else:
1400 *
1401 * 1. sfnt_load_face applied the hhea metrics by default.
1402 * 2. This code later applied the typo metrics by default, regardless of
1403 * whether they were actually changed or the font had the OS/2 table's
1404 * fsSelection's bit 7 (USE_TYPO_METRICS) set.
1405 */
1406 FT_Short current_line_gap = root->height - root->ascender +
1407 root->descender;
1408
1409
1410 root->ascender = root->ascender + mvar_hasc_delta;
1411 root->descender = root->descender + mvar_hdsc_delta;
1412 root->height = root->ascender - root->descender +
1413 current_line_gap + mvar_hlgp_delta;
1414
1415 root->underline_position = face->postscript.underlinePosition -
1416 face->postscript.underlineThickness / 2;
1417 root->underline_thickness = face->postscript.underlineThickness;
1418
1419 /* iterate over all FT_Size objects and call `tt_size_reset' */
1420 /* to propagate the metrics changes */
1421 FT_List_Iterate( &root->sizes_list,
1422 tt_size_reset_iterator,
1423 NULL );
1424 }
1425 }
1426
1427
1428 typedef struct GX_GVar_Head_
1429 {
1430 FT_Long version;
1431 FT_UShort axisCount;
1432 FT_UShort globalCoordCount;
1433 FT_ULong offsetToCoord;
1434 FT_UShort glyphCount;
1435 FT_UShort flags;
1436 FT_ULong offsetToData;
1437
1438 } GX_GVar_Head;
1439
1440
1441 /**************************************************************************
1442 *
1443 * @Function:
1444 * ft_var_load_gvar
1445 *
1446 * @Description:
1447 * Parse the `gvar' table if present. If `fvar' is there, `gvar' had
1448 * better be there too.
1449 *
1450 * @InOut:
1451 * face ::
1452 * The font face.
1453 *
1454 * @Return:
1455 * FreeType error code. 0 means success.
1456 */
1457 static FT_Error
1458 ft_var_load_gvar( TT_Face face )
1459 {
1460 FT_Stream stream = FT_FACE_STREAM( face );
1461 FT_Memory memory = stream->memory;
1462 GX_Blend blend = face->blend;
1463 FT_Error error;
1464 FT_UInt i, j;
1465 FT_ULong table_len;
1466 FT_ULong gvar_start;
1467 FT_ULong offsetToData;
1468 GX_GVar_Head gvar_head;
1469
1470 static const FT_Frame_Field gvar_fields[] =
1471 {
1472
1473 #undef FT_STRUCTURE
1474 #define FT_STRUCTURE GX_GVar_Head
1475
1476 FT_FRAME_START( 20 ),
1534 goto Exit;
1535 }
1536
1537 FT_TRACE2(( "loaded\n" ));
1538
1539 blend->gvar_size = table_len;
1540 blend->tuplecount = gvar_head.globalCoordCount;
1541 blend->gv_glyphcnt = gvar_head.glyphCount;
1542 offsetToData = gvar_start + gvar_head.offsetToData;
1543
1544 FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n",
1545 blend->tuplecount == 1 ? "is" : "are",
1546 blend->tuplecount,
1547 blend->tuplecount == 1 ? "" : "s" ));
1548
1549 if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
1550 goto Exit;
1551
1552 if ( gvar_head.flags & 1 )
1553 {
1554 FT_ULong limit = gvar_start + table_len;
1555
1556
1557 /* long offsets (one more offset than glyphs, to mark size of last) */
1558 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
1559 goto Exit;
1560
1561 for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1562 {
1563 blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG();
1564 /* use `>', not `>=' */
1565 if ( blend->glyphoffsets[i] > limit )
1566 {
1567 FT_TRACE2(( "ft_var_load_gvar:"
1568 " invalid glyph variation data offset for index %d\n",
1569 i ));
1570 error = FT_THROW( Invalid_Table );
1571 break;
1572 }
1573 }
1574 }
1575 else
1576 {
1577 FT_ULong limit = gvar_start + table_len;
1578
1579
1580 /* short offsets (one more offset than glyphs, to mark size of last) */
1581 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
1582 goto Exit;
1583
1584 for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1585 {
1586 blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
1587 /* use `>', not `>=' */
1588 if ( blend->glyphoffsets[i] > limit )
1589 {
1590 FT_TRACE2(( "ft_var_load_gvar:"
1591 " invalid glyph variation data offset for index %d\n",
1592 i ));
1593 error = FT_THROW( Invalid_Table );
1594 break;
1595 }
1596 }
1597 }
1598
1599 FT_FRAME_EXIT();
1600 if ( error )
1601 goto Exit;
1602
1603 if ( blend->tuplecount != 0 )
1604 {
1605 if ( FT_NEW_ARRAY( blend->tuplecoords,
1606 gvar_head.axisCount * blend->tuplecount ) )
1607 goto Exit;
1608
1609 if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) ||
1610 FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) )
1611 goto Exit;
1612
1613 for ( i = 0; i < blend->tuplecount; i++ )
1614 {
1615 FT_TRACE5(( " [ " ));
1616 for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ )
1617 {
1618 blend->tuplecoords[i * gvar_head.axisCount + j] =
1619 FT_GET_SHORT() * 4; /* convert to FT_Fixed */
1620 FT_TRACE5(( "%.5f ",
1621 blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 ));
1622 }
1623 FT_TRACE5(( "]\n" ));
1624 }
1625
1626 FT_TRACE5(( "\n" ));
1627
1628 FT_FRAME_EXIT();
1629 }
1630
1631 Exit:
1632 return error;
1633 }
1634
1635
1636 /**************************************************************************
1637 *
1638 * @Function:
1639 * ft_var_apply_tuple
1640 *
1641 * @Description:
1642 * Figure out whether a given tuple (design) applies to the current
1643 * blend, and if so, what is the scaling factor.
1644 *
1645 * @Input:
1646 * blend ::
1647 * The current blend of the font.
1648 *
1649 * tupleIndex ::
1650 * A flag saying whether this is an intermediate
1651 * tuple or not.
1652 *
1653 * tuple_coords ::
1654 * The coordinates of the tuple in normalized axis
1655 * units.
1656 *
1657 * im_start_coords ::
1658 * The initial coordinates where this tuple starts
1659 * to apply (for intermediate coordinates).
1660 *
1661 * im_end_coords ::
1662 * The final coordinates after which this tuple no
1663 * longer applies (for intermediate coordinates).
1664 *
1665 * @Return:
1666 * An FT_Fixed value containing the scaling factor.
1667 */
1668 static FT_Fixed
1669 ft_var_apply_tuple( GX_Blend blend,
1670 FT_UShort tupleIndex,
1671 FT_Fixed* tuple_coords,
1672 FT_Fixed* im_start_coords,
1673 FT_Fixed* im_end_coords )
1674 {
1675 FT_UInt i;
1676 FT_Fixed apply = 0x10000L;
1677
1678
1679 for ( i = 0; i < blend->num_axis; i++ )
1680 {
1681 FT_TRACE6(( " axis %d coordinate %.5f:\n",
1682 i, blend->normalizedcoords[i] / 65536.0 ));
1683
1684 /* It's not clear why (for intermediate tuples) we don't need */
1685 /* to check against start/end -- the documentation says we don't. */
1686 /* Similarly, it's unclear why we don't need to scale along the */
1687 /* axis. */
1688
1689 if ( tuple_coords[i] == 0 )
1690 {
1691 FT_TRACE6(( " tuple coordinate is zero, ignore\n", i ));
1692 continue;
1693 }
1694
1695 if ( blend->normalizedcoords[i] == 0 )
1696 {
1697 FT_TRACE6(( " axis coordinate is zero, stop\n" ));
1698 apply = 0;
1699 break;
1700 }
1701
1702 if ( blend->normalizedcoords[i] == tuple_coords[i] )
1703 {
1704 FT_TRACE6(( " tuple coordinate %.5f fits perfectly\n",
1705 tuple_coords[i] / 65536.0 ));
1706 /* `apply' does not change */
1707 continue;
1708 }
1709
1710 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
1711 {
1712 /* not an intermediate tuple */
1713
1714 if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
1715 blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
1716 {
1717 FT_TRACE6(( " tuple coordinate %.5f is exceeded, stop\n",
1718 tuple_coords[i] / 65536.0 ));
1719 apply = 0;
1720 break;
1721 }
1722
1723 FT_TRACE6(( " tuple coordinate %.5f fits\n",
1724 tuple_coords[i] / 65536.0 ));
1725 apply = FT_MulDiv( apply,
1726 blend->normalizedcoords[i],
1727 tuple_coords[i] );
1728 }
1729 else
1730 {
1731 /* intermediate tuple */
1732
1733 if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
1734 blend->normalizedcoords[i] >= im_end_coords[i] )
1735 {
1736 FT_TRACE6(( " intermediate tuple range ]%.5f;%.5f[ is exceeded,"
1737 " stop\n",
1738 im_start_coords[i] / 65536.0,
1739 im_end_coords[i] / 65536.0 ));
1740 apply = 0;
1741 break;
1742 }
1743
1744 FT_TRACE6(( " intermediate tuple range ]%.5f;%.5f[ fits\n",
1745 im_start_coords[i] / 65536.0,
1746 im_end_coords[i] / 65536.0 ));
1747 if ( blend->normalizedcoords[i] < tuple_coords[i] )
1748 apply = FT_MulDiv( apply,
1749 blend->normalizedcoords[i] - im_start_coords[i],
1750 tuple_coords[i] - im_start_coords[i] );
1751 else
1752 apply = FT_MulDiv( apply,
1753 im_end_coords[i] - blend->normalizedcoords[i],
1754 im_end_coords[i] - tuple_coords[i] );
1755 }
1756 }
1757
1758 FT_TRACE6(( " apply factor is %.5f\n", apply / 65536.0 ));
1759
1760 return apply;
1761 }
1762
1763
1764 /* convert from design coordinates to normalized coordinates */
1765
1766 static void
1767 ft_var_to_normalized( TT_Face face,
1768 FT_UInt num_coords,
1769 FT_Fixed* coords,
1770 FT_Fixed* normalized )
1771 {
1772 GX_Blend blend;
1773 FT_MM_Var* mmvar;
1774 FT_UInt i, j;
1775 FT_Var_Axis* a;
1776 GX_AVarSegment av;
1797 FT_Fixed coord = coords[i];
1798
1799
1800 FT_TRACE5(( " %d: %.5f\n", i, coord / 65536.0 ));
1801 if ( coord > a->maximum || coord < a->minimum )
1802 {
1803 FT_TRACE1((
1804 "ft_var_to_normalized: design coordinate %.5f\n"
1805 " is out of range [%.5f;%.5f]; clamping\n",
1806 coord / 65536.0,
1807 a->minimum / 65536.0,
1808 a->maximum / 65536.0 ));
1809
1810 if ( coord > a->maximum )
1811 coord = a->maximum;
1812 else
1813 coord = a->minimum;
1814 }
1815
1816 if ( coord < a->def )
1817 normalized[i] = -FT_DivFix( SUB_LONG( coord, a->def ),
1818 SUB_LONG( a->minimum, a->def ) );
1819 else if ( coord > a->def )
1820 normalized[i] = FT_DivFix( SUB_LONG( coord, a->def ),
1821 SUB_LONG( a->maximum, a->def ) );
1822 else
1823 normalized[i] = 0;
1824 }
1825
1826 FT_TRACE5(( "\n" ));
1827
1828 for ( ; i < mmvar->num_axis; i++ )
1829 normalized[i] = 0;
1830
1831 if ( blend->avar_segment )
1832 {
1833 FT_TRACE5(( "normalized design coordinates"
1834 " before applying `avar' data:\n" ));
1835
1836 av = blend->avar_segment;
1837 for ( i = 0; i < mmvar->num_axis; i++, av++ )
1838 {
1839 for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1840 {
1841 if ( normalized[i] < av->correspondence[j].fromCoord )
1951 FT_UShort axisCount;
1952 FT_UShort axisSize;
1953 FT_UShort instanceCount;
1954 FT_UShort instanceSize;
1955
1956 } GX_FVar_Head;
1957
1958
1959 typedef struct fvar_axis_
1960 {
1961 FT_ULong axisTag;
1962 FT_Fixed minValue;
1963 FT_Fixed defaultValue;
1964 FT_Fixed maxValue;
1965 FT_UShort flags;
1966 FT_UShort nameID;
1967
1968 } GX_FVar_Axis;
1969
1970
1971 /**************************************************************************
1972 *
1973 * @Function:
1974 * TT_Get_MM_Var
1975 *
1976 * @Description:
1977 * Check that the font's `fvar' table is valid, parse it, and return
1978 * those data. It also loads (and parses) the `MVAR' table, if
1979 * possible.
1980 *
1981 * @InOut:
1982 * face ::
1983 * The font face.
1984 * TT_Get_MM_Var initializes the blend structure.
1985 *
1986 * @Output:
1987 * master ::
1988 * The `fvar' data (must be freed by caller). Can be NULL,
1989 * which makes this function simply load MM support.
1990 *
1991 * @Return:
1992 * FreeType error code. 0 means success.
1993 */
1994 FT_LOCAL_DEF( FT_Error )
1995 TT_Get_MM_Var( TT_Face face,
1996 FT_MM_Var* *master )
1997 {
1998 FT_Stream stream = face->root.stream;
1999 FT_Memory memory = face->root.memory;
2000 FT_ULong table_len;
2001 FT_Error error = FT_Err_Ok;
2002 FT_ULong fvar_start = 0;
2003 FT_UInt i, j;
2004 FT_MM_Var* mmvar = NULL;
2005 FT_Fixed* next_coords;
2006 FT_Fixed* nsc;
2007 FT_String* next_name;
2008 FT_Var_Axis* a;
2009 FT_Fixed* c;
2010 FT_Var_Named_Style* ns;
2011 GX_FVar_Head fvar_head;
2012 FT_Bool usePsName = 0;
2013 FT_UInt num_instances;
2550 FT_Bool have_diff = 0;
2551 FT_UInt j;
2552 FT_Fixed* c;
2553 FT_Fixed* n;
2554
2555
2556 manageCvt = mcvt_retain;
2557
2558 for ( i = 0; i < num_coords; i++ )
2559 {
2560 if ( blend->normalizedcoords[i] != coords[i] )
2561 {
2562 manageCvt = mcvt_load;
2563 have_diff = 1;
2564 break;
2565 }
2566 }
2567
2568 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2569 {
2570 FT_UInt instance_index = (FT_UInt)face->root.face_index >> 16;
2571
2572
2573 c = blend->normalizedcoords + i;
2574 n = blend->normalized_stylecoords +
2575 ( instance_index - 1 ) * mmvar->num_axis +
2576 i;
2577
2578 for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
2579 if ( *c != *n )
2580 have_diff = 1;
2581 }
2582 else
2583 {
2584 c = blend->normalizedcoords + i;
2585 for ( j = i; j < mmvar->num_axis; j++, c++ )
2586 if ( *c != 0 )
2587 have_diff = 1;
2588 }
2589
2590 /* return value -1 indicates `no change' */
2591 if ( !have_diff )
2592 {
2593 face->doblend = TRUE;
2594
2595 return -1;
2596 }
2597
2598 for ( ; i < mmvar->num_axis; i++ )
2599 {
2600 if ( blend->normalizedcoords[i] != 0 )
2601 {
2602 manageCvt = mcvt_load;
2603 break;
2604 }
2605 }
2606
2607 /* If we don't change the blend coords then we don't need to do */
2608 /* anything to the cvt table. It will be correct. Otherwise we */
2609 /* no longer have the original cvt (it was modified when we set */
2610 /* the blend last time), so we must reload and then modify it. */
2611 }
2612
2613 blend->num_axis = mmvar->num_axis;
2614 FT_MEM_COPY( blend->normalizedcoords,
2615 coords,
2616 num_coords * sizeof ( FT_Fixed ) );
2640 /* The original cvt table is in memory. All we need to do is */
2641 /* apply the `cvar' table (if any). */
2642 error = tt_face_vary_cvt( face, face->root.stream );
2643 break;
2644
2645 case mcvt_retain:
2646 /* The cvt table is correct for this set of coordinates. */
2647 break;
2648 }
2649 }
2650
2651 /* enforce recomputation of the PostScript name; */
2652 FT_FREE( face->postscript_name );
2653 face->postscript_name = NULL;
2654
2655 Exit:
2656 return error;
2657 }
2658
2659
2660 /**************************************************************************
2661 *
2662 * @Function:
2663 * TT_Set_MM_Blend
2664 *
2665 * @Description:
2666 * Set the blend (normalized) coordinates for this instance of the
2667 * font. Check that the `gvar' table is reasonable and does some
2668 * initial preparation.
2669 *
2670 * @InOut:
2671 * face ::
2672 * The font.
2673 * Initialize the blend structure with `gvar' data.
2674 *
2675 * @Input:
2676 * num_coords ::
2677 * The number of available coordinates. If it is
2678 * larger than the number of axes, ignore the excess
2679 * values. If it is smaller than the number of axes,
2680 * use the default value (0) for the remaining axes.
2681 *
2682 * coords ::
2683 * An array of `num_coords', each between [-1,1].
2684 *
2685 * @Return:
2686 * FreeType error code. 0 means success.
2687 */
2688 FT_LOCAL_DEF( FT_Error )
2689 TT_Set_MM_Blend( TT_Face face,
2690 FT_UInt num_coords,
2691 FT_Fixed* coords )
2692 {
2693 FT_Error error;
2694
2695
2696 error = tt_set_mm_blend( face, num_coords, coords, 1 );
2697 if ( error )
2698 return error;
2699
2700 if ( num_coords )
2701 face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2702 else
2703 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2704
2705 return FT_Err_Ok;
2706 }
2707
2708
2709 /**************************************************************************
2710 *
2711 * @Function:
2712 * TT_Get_MM_Blend
2713 *
2714 * @Description:
2715 * Get the blend (normalized) coordinates for this instance of the
2716 * font.
2717 *
2718 * @InOut:
2719 * face ::
2720 * The font.
2721 * Initialize the blend structure with `gvar' data.
2722 *
2723 * @Input:
2724 * num_coords ::
2725 * The number of available coordinates. If it is
2726 * larger than the number of axes, set the excess
2727 * values to 0.
2728 *
2729 * coords ::
2730 * An array of `num_coords', each between [-1,1].
2731 *
2732 * @Return:
2733 * FreeType error code. 0 means success.
2734 */
2735 FT_LOCAL_DEF( FT_Error )
2736 TT_Get_MM_Blend( TT_Face face,
2737 FT_UInt num_coords,
2738 FT_Fixed* coords )
2739 {
2740 FT_Error error = FT_Err_Ok;
2741 GX_Blend blend;
2742 FT_UInt i, nc;
2743
2744
2745 if ( !face->blend )
2746 {
2747 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2748 return error;
2749 }
2750
2751 blend = face->blend;
2752
2753 if ( !blend->coords )
2754 {
2768 }
2769
2770 if ( face->doblend )
2771 {
2772 for ( i = 0; i < nc; i++ )
2773 coords[i] = blend->normalizedcoords[i];
2774 }
2775 else
2776 {
2777 for ( i = 0; i < nc; i++ )
2778 coords[i] = 0;
2779 }
2780
2781 for ( ; i < num_coords; i++ )
2782 coords[i] = 0;
2783
2784 return FT_Err_Ok;
2785 }
2786
2787
2788 /**************************************************************************
2789 *
2790 * @Function:
2791 * TT_Set_Var_Design
2792 *
2793 * @Description:
2794 * Set the coordinates for the instance, measured in the user
2795 * coordinate system. Parse the `avar' table (if present) to convert
2796 * from user to normalized coordinates.
2797 *
2798 * @InOut:
2799 * face ::
2800 * The font face.
2801 * Initialize the blend struct with `gvar' data.
2802 *
2803 * @Input:
2804 * num_coords ::
2805 * The number of available coordinates. If it is
2806 * larger than the number of axes, ignore the excess
2807 * values. If it is smaller than the number of axes,
2808 * use the default values for the remaining axes.
2809 *
2810 * coords ::
2811 * A coordinate array with `num_coords' elements.
2812 *
2813 * @Return:
2814 * FreeType error code. 0 means success.
2815 */
2816 FT_LOCAL_DEF( FT_Error )
2817 TT_Set_Var_Design( TT_Face face,
2818 FT_UInt num_coords,
2819 FT_Fixed* coords )
2820 {
2821 FT_Error error = FT_Err_Ok;
2822 GX_Blend blend;
2823 FT_MM_Var* mmvar;
2824 FT_UInt i;
2825 FT_Memory memory = face->root.memory;
2826
2827 FT_Fixed* c;
2828 FT_Fixed* n;
2829 FT_Fixed* normalized = NULL;
2830
2831 FT_Bool have_diff = 0;
2832
2833
2834 if ( !face->blend )
2835 {
2913
2914 FT_TRACE5(( "TT_Set_Var_Design:\n"
2915 " normalized design coordinates:\n" ));
2916 ft_var_to_normalized( face, num_coords, blend->coords, normalized );
2917
2918 error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
2919 if ( error )
2920 goto Exit;
2921
2922 if ( num_coords )
2923 face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2924 else
2925 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2926
2927 Exit:
2928 FT_FREE( normalized );
2929 return error;
2930 }
2931
2932
2933 /**************************************************************************
2934 *
2935 * @Function:
2936 * TT_Get_Var_Design
2937 *
2938 * @Description:
2939 * Get the design coordinates of the currently selected interpolated
2940 * font.
2941 *
2942 * @Input:
2943 * face ::
2944 * A handle to the source face.
2945 *
2946 * num_coords ::
2947 * The number of design coordinates to retrieve. If it
2948 * is larger than the number of axes, set the excess
2949 * values to~0.
2950 *
2951 * @Output:
2952 * coords ::
2953 * The design coordinates array.
2954 *
2955 * @Return:
2956 * FreeType error code. 0~means success.
2957 */
2958 FT_LOCAL_DEF( FT_Error )
2959 TT_Get_Var_Design( TT_Face face,
2960 FT_UInt num_coords,
2961 FT_Fixed* coords )
2962 {
2963 FT_Error error = FT_Err_Ok;
2964 GX_Blend blend;
2965 FT_UInt i, nc;
2966
2967
2968 if ( !face->blend )
2969 {
2970 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2971 return error;
2972 }
2973
2974 blend = face->blend;
2975
2976 if ( !blend->coords )
2977 {
2991 }
2992
2993 if ( face->doblend )
2994 {
2995 for ( i = 0; i < nc; i++ )
2996 coords[i] = blend->coords[i];
2997 }
2998 else
2999 {
3000 for ( i = 0; i < nc; i++ )
3001 coords[i] = 0;
3002 }
3003
3004 for ( ; i < num_coords; i++ )
3005 coords[i] = 0;
3006
3007 return FT_Err_Ok;
3008 }
3009
3010
3011 /**************************************************************************
3012 *
3013 * @Function:
3014 * TT_Set_Named_Instance
3015 *
3016 * @Description:
3017 * Set the given named instance, also resetting any further
3018 * variation.
3019 *
3020 * @Input:
3021 * face ::
3022 * A handle to the source face.
3023 *
3024 * instance_index ::
3025 * The instance index, starting with value 1.
3026 * Value 0 indicates to not use an instance.
3027 *
3028 * @Return:
3029 * FreeType error code. 0~means success.
3030 */
3031 FT_LOCAL_DEF( FT_Error )
3032 TT_Set_Named_Instance( TT_Face face,
3033 FT_UInt instance_index )
3034 {
3035 FT_Error error = FT_ERR( Invalid_Argument );
3036 GX_Blend blend;
3037 FT_MM_Var* mmvar;
3038
3039 FT_UInt num_instances;
3040
3041
3042 if ( !face->blend )
3043 {
3044 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
3045 goto Exit;
3046 }
3047
3048 blend = face->blend;
3049 mmvar = blend->mmvar;
3050
3086 error = TT_Set_Var_Design( face, 0, NULL );
3087
3088 face->root.face_index = ( instance_index << 16 ) |
3089 ( face->root.face_index & 0xFFFFL );
3090 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
3091
3092 Exit:
3093 return error;
3094 }
3095
3096
3097 /*************************************************************************/
3098 /*************************************************************************/
3099 /***** *****/
3100 /***** GX VAR PARSING ROUTINES *****/
3101 /***** *****/
3102 /*************************************************************************/
3103 /*************************************************************************/
3104
3105
3106 /**************************************************************************
3107 *
3108 * @Function:
3109 * tt_face_vary_cvt
3110 *
3111 * @Description:
3112 * Modify the loaded cvt table according to the `cvar' table and the
3113 * font's blend.
3114 *
3115 * @InOut:
3116 * face ::
3117 * A handle to the target face object.
3118 *
3119 * @Input:
3120 * stream ::
3121 * A handle to the input stream.
3122 *
3123 * @Return:
3124 * FreeType error code. 0 means success.
3125 *
3126 * Most errors are ignored. It is perfectly valid not to have a
3127 * `cvar' table even if there is a `gvar' and `fvar' table.
3128 */
3129 FT_LOCAL_DEF( FT_Error )
3130 tt_face_vary_cvt( TT_Face face,
3131 FT_Stream stream )
3132 {
3133 FT_Error error;
3134 FT_Memory memory = stream->memory;
3135
3136 FT_ULong table_start;
3137 FT_ULong table_len;
3138
3139 FT_UInt tupleCount;
3140 FT_ULong offsetToData;
3141
3142 FT_ULong here;
3143 FT_UInt i, j;
3144
3145 FT_Fixed* tuple_coords = NULL;
3146 FT_Fixed* im_start_coords = NULL;
3147 FT_Fixed* im_end_coords = NULL;
3148
3149 GX_Blend blend = face->blend;
3150
3151 FT_UInt point_count;
3152 FT_UInt spoint_count = 0;
3153
3154 FT_UShort* sharedpoints = NULL;
3155 FT_UShort* localpoints = NULL;
3156 FT_UShort* points;
3157
3158 FT_Fixed* deltas = NULL;
3159 FT_Fixed* cvt_deltas = NULL;
3160
3161
3162 FT_TRACE2(( "CVAR " ));
3163
3164 if ( !blend )
3165 {
3166 FT_TRACE2(( "\n"
3167 "tt_face_vary_cvt: no blend specified\n" ));
3168 error = FT_Err_Ok;
3169 goto Exit;
3170 }
3171
3172 if ( !face->cvt )
3173 {
3174 FT_TRACE2(( "\n"
3175 "tt_face_vary_cvt: no `cvt ' table\n" ));
3176 error = FT_Err_Ok;
3177 goto Exit;
3178 }
3179
3222 goto FExit;
3223 }
3224
3225 offsetToData += table_start;
3226
3227 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3228 {
3229 here = FT_Stream_FTell( stream );
3230
3231 FT_Stream_SeekSet( stream, offsetToData );
3232
3233 sharedpoints = ft_var_readpackedpoints( stream,
3234 table_len,
3235 &spoint_count );
3236 offsetToData = FT_Stream_FTell( stream );
3237
3238 FT_Stream_SeekSet( stream, here );
3239 }
3240
3241 FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
3242 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3243 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3244 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3245
3246 if ( FT_NEW_ARRAY( cvt_deltas, face->cvt_size ) )
3247 goto FExit;
3248
3249 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3250 {
3251 FT_UInt tupleDataSize;
3252 FT_UInt tupleIndex;
3253 FT_Fixed apply;
3254
3255
3256 FT_TRACE6(( " tuple %d:\n", i ));
3257
3258 tupleDataSize = FT_GET_USHORT();
3259 tupleIndex = FT_GET_USHORT();
3260
3261 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3262 {
3263 for ( j = 0; j < blend->num_axis; j++ )
3264 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */
3265 /* short frac to fixed */
3266 }
3267 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3268 {
3269 FT_TRACE2(( "tt_face_vary_cvt:"
3270 " invalid tuple index\n" ));
3271
3272 error = FT_THROW( Invalid_Table );
3273 goto FExit;
3274 }
3275 else
3276 {
3277 if ( !blend->tuplecoords )
3278 {
3279 FT_TRACE2(( "tt_face_vary_cvt:"
3280 " no valid tuple coordinates available\n" ));
3281
3282 error = FT_THROW( Invalid_Table );
3283 goto FExit;
3284 }
3285
3286 FT_MEM_COPY(
3287 tuple_coords,
3288 blend->tuplecoords +
3289 ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
3290 blend->num_axis * sizeof ( FT_Fixed ) );
3291 }
3292
3293 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3294 {
3295 for ( j = 0; j < blend->num_axis; j++ )
3296 im_start_coords[j] = FT_GET_SHORT() * 4;
3297 for ( j = 0; j < blend->num_axis; j++ )
3298 im_end_coords[j] = FT_GET_SHORT() * 4;
3299 }
3300
3301 apply = ft_var_apply_tuple( blend,
3302 (FT_UShort)tupleIndex,
3303 tuple_coords,
3304 im_start_coords,
3305 im_end_coords );
3306
3307 if ( apply == 0 ) /* tuple isn't active for our blend */
3308 {
3309 offsetToData += tupleDataSize;
3310 continue;
3311 }
3332 point_count == 0 ? face->cvt_size
3333 : point_count );
3334
3335 if ( !points ||
3336 !deltas ||
3337 ( localpoints == ALL_POINTS && point_count != face->cvt_size ) )
3338 ; /* failure, ignore it */
3339
3340 else if ( localpoints == ALL_POINTS )
3341 {
3342 #ifdef FT_DEBUG_LEVEL_TRACE
3343 int count = 0;
3344 #endif
3345
3346
3347 FT_TRACE7(( " CVT deltas:\n" ));
3348
3349 /* this means that there are deltas for every entry in cvt */
3350 for ( j = 0; j < face->cvt_size; j++ )
3351 {
3352 FT_Fixed old_cvt_delta;
3353
3354
3355 old_cvt_delta = cvt_deltas[j];
3356 cvt_deltas[j] = old_cvt_delta + FT_MulFix( deltas[j], apply );
3357
3358 #ifdef FT_DEBUG_LEVEL_TRACE
3359 if ( old_cvt_delta != cvt_deltas[j] )
3360 {
3361 FT_TRACE7(( " %d: %f -> %f\n",
3362 j,
3363 ( FT_intToFixed( face->cvt[j] ) +
3364 old_cvt_delta ) / 65536.0,
3365 ( FT_intToFixed( face->cvt[j] ) +
3366 cvt_deltas[j] ) / 65536.0 ));
3367 count++;
3368 }
3369 #endif
3370 }
3371
3372 #ifdef FT_DEBUG_LEVEL_TRACE
3373 if ( !count )
3374 FT_TRACE7(( " none\n" ));
3375 #endif
3376 }
3377
3378 else
3379 {
3380 #ifdef FT_DEBUG_LEVEL_TRACE
3381 int count = 0;
3382 #endif
3383
3384
3385 FT_TRACE7(( " CVT deltas:\n" ));
3386
3387 for ( j = 0; j < point_count; j++ )
3388 {
3389 int pindex;
3390 FT_Fixed old_cvt_delta;
3391
3392
3393 pindex = points[j];
3394 if ( (FT_ULong)pindex >= face->cvt_size )
3395 continue;
3396
3397 old_cvt_delta = cvt_deltas[pindex];
3398 cvt_deltas[pindex] = old_cvt_delta + FT_MulFix( deltas[j], apply );
3399
3400 #ifdef FT_DEBUG_LEVEL_TRACE
3401 if ( old_cvt_delta != cvt_deltas[pindex] )
3402 {
3403 FT_TRACE7(( " %d: %f -> %f\n",
3404 pindex,
3405 ( FT_intToFixed( face->cvt[pindex] ) +
3406 old_cvt_delta ) / 65536.0,
3407 ( FT_intToFixed( face->cvt[pindex] ) +
3408 cvt_deltas[pindex] ) / 65536.0 ));
3409 count++;
3410 }
3411 #endif
3412 }
3413
3414 #ifdef FT_DEBUG_LEVEL_TRACE
3415 if ( !count )
3416 FT_TRACE7(( " none\n" ));
3417 #endif
3418 }
3419
3420 if ( localpoints != ALL_POINTS )
3421 FT_FREE( localpoints );
3422 FT_FREE( deltas );
3423
3424 offsetToData += tupleDataSize;
3425
3426 FT_Stream_SeekSet( stream, here );
3427 }
3428
3429 FT_TRACE5(( "\n" ));
3430
3431 for ( i = 0; i < face->cvt_size; i++ )
3432 face->cvt[i] += FT_fixedToInt( cvt_deltas[i] );
3433
3434 FExit:
3435 FT_FRAME_EXIT();
3436
3437 Exit:
3438 if ( sharedpoints != ALL_POINTS )
3439 FT_FREE( sharedpoints );
3440 FT_FREE( tuple_coords );
3441 FT_FREE( im_start_coords );
3442 FT_FREE( im_end_coords );
3443 FT_FREE( cvt_deltas );
3444
3445 return error;
3446 }
3447
3448
3449 /* Shift the original coordinates of all points between indices `p1' */
3450 /* and `p2', using the same difference as given by index `ref'. */
3451
3452 /* modeled after `af_iup_shift' */
3453
3454 static void
3455 tt_delta_shift( int p1,
3456 int p2,
3457 int ref,
3458 FT_Vector* in_points,
3459 FT_Vector* out_points )
3460 {
3461 int p;
3462 FT_Vector delta;
3463
3470
3471 for ( p = p1; p < ref; p++ )
3472 {
3473 out_points[p].x += delta.x;
3474 out_points[p].y += delta.y;
3475 }
3476
3477 for ( p = ref + 1; p <= p2; p++ )
3478 {
3479 out_points[p].x += delta.x;
3480 out_points[p].y += delta.y;
3481 }
3482 }
3483
3484
3485 /* Interpolate the original coordinates of all points with indices */
3486 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
3487 /* point indices. */
3488
3489 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */
3490 /* `Ins_IUP' with spec differences in handling ill-defined cases. */
3491 static void
3492 tt_delta_interpolate( int p1,
3493 int p2,
3494 int ref1,
3495 int ref2,
3496 FT_Vector* in_points,
3497 FT_Vector* out_points )
3498 {
3499 int p, i;
3500
3501 FT_Pos out, in1, in2, out1, out2, d1, d2;
3502
3503
3504 if ( p1 > p2 )
3505 return;
3506
3507 /* handle both horizontal and vertical coordinates */
3508 for ( i = 0; i <= 1; i++ )
3509 {
3510 /* shift array pointers so that we can access `foo.y' as `foo.x' */
3629 cur_delta,
3630 first_delta,
3631 in_points,
3632 out_points );
3633
3634 if ( first_delta > 0 )
3635 tt_delta_interpolate( first_point,
3636 first_delta - 1,
3637 cur_delta,
3638 first_delta,
3639 in_points,
3640 out_points );
3641 }
3642 }
3643 contour++;
3644
3645 } while ( contour < outline->n_contours );
3646 }
3647
3648
3649 /**************************************************************************
3650 *
3651 * @Function:
3652 * TT_Vary_Apply_Glyph_Deltas
3653 *
3654 * @Description:
3655 * Apply the appropriate deltas to the current glyph.
3656 *
3657 * @Input:
3658 * face ::
3659 * A handle to the target face object.
3660 *
3661 * glyph_index ::
3662 * The index of the glyph being modified.
3663 *
3664 * n_points ::
3665 * The number of the points in the glyph, including
3666 * phantom points.
3667 *
3668 * @InOut:
3669 * outline ::
3670 * The outline to change.
3671 *
3672 * @Return:
3673 * FreeType error code. 0 means success.
3674 */
3675 FT_LOCAL_DEF( FT_Error )
3676 TT_Vary_Apply_Glyph_Deltas( TT_Face face,
3677 FT_UInt glyph_index,
3678 FT_Outline* outline,
3679 FT_UInt n_points )
3680 {
3681 FT_Error error;
3682 FT_Stream stream = face->root.stream;
3683 FT_Memory memory = stream->memory;
3684
3685 FT_Vector* points_org = NULL; /* coordinates in 16.16 format */
3686 FT_Vector* points_out = NULL; /* coordinates in 16.16 format */
3687 FT_Bool* has_delta = NULL;
3688
3689 FT_ULong glyph_start;
3690
3691 FT_UInt tupleCount;
3692 FT_ULong offsetToData;
3693 FT_ULong dataSize;
3694
3695 FT_ULong here;
3696 FT_UInt i, j;
3697
3698 FT_Fixed* tuple_coords = NULL;
3699 FT_Fixed* im_start_coords = NULL;
3700 FT_Fixed* im_end_coords = NULL;
3701
3702 GX_Blend blend = face->blend;
3703
3704 FT_UInt point_count;
3705 FT_UInt spoint_count = 0;
3706
3707 FT_UShort* sharedpoints = NULL;
3708 FT_UShort* localpoints = NULL;
3709 FT_UShort* points;
3710
3711 FT_Fixed* deltas_x = NULL;
3712 FT_Fixed* deltas_y = NULL;
3713 FT_Fixed* point_deltas_x = NULL;
3714 FT_Fixed* point_deltas_y = NULL;
3715
3716
3717 if ( !face->doblend || !blend )
3718 return FT_THROW( Invalid_Argument );
3719
3720 if ( glyph_index >= blend->gv_glyphcnt ||
3721 blend->glyphoffsets[glyph_index] ==
3722 blend->glyphoffsets[glyph_index + 1] )
3723 {
3724 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3725 " no variation data for this glyph\n" ));
3726 return FT_Err_Ok;
3727 }
3728
3729 if ( FT_NEW_ARRAY( points_org, n_points ) ||
3730 FT_NEW_ARRAY( points_out, n_points ) ||
3731 FT_NEW_ARRAY( has_delta, n_points ) )
3732 goto Fail1;
3733
3734 dataSize = blend->glyphoffsets[glyph_index + 1] -
3735 blend->glyphoffsets[glyph_index];
3736
3737 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
3738 FT_FRAME_ENTER( dataSize ) )
3739 goto Fail1;
3740
3741 glyph_start = FT_Stream_FTell( stream );
3742
3743 /* each set of glyph variation data is formatted similarly to `cvar' */
3744
3745 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
3746 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3747 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
3748 goto Fail2;
3749
3750 tupleCount = FT_GET_USHORT();
3751 offsetToData = FT_GET_USHORT();
3752
3753 /* rough sanity test */
3754 if ( offsetToData > dataSize ||
3755 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > dataSize )
3756 {
3757 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3758 " invalid glyph variation array header\n" ));
3759
3760 error = FT_THROW( Invalid_Table );
3761 goto Fail2;
3762 }
3763
3764 offsetToData += glyph_start;
3765
3766 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3767 {
3768 here = FT_Stream_FTell( stream );
3769
3770 FT_Stream_SeekSet( stream, offsetToData );
3771
3772 sharedpoints = ft_var_readpackedpoints( stream,
3773 blend->gvar_size,
3774 &spoint_count );
3775 offsetToData = FT_Stream_FTell( stream );
3776
3777 FT_Stream_SeekSet( stream, here );
3778 }
3779
3780 FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
3781 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3782 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3783 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3784
3785 if ( FT_NEW_ARRAY( point_deltas_x, n_points ) ||
3786 FT_NEW_ARRAY( point_deltas_y, n_points ) )
3787 goto Fail3;
3788
3789 for ( j = 0; j < n_points; j++ )
3790 {
3791 points_org[j].x = FT_intToFixed( outline->points[j].x );
3792 points_org[j].y = FT_intToFixed( outline->points[j].y );
3793 }
3794
3795 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3796 {
3797 FT_UInt tupleDataSize;
3798 FT_UInt tupleIndex;
3799 FT_Fixed apply;
3800
3801
3802 FT_TRACE6(( " tuple %d:\n", i ));
3803
3804 tupleDataSize = FT_GET_USHORT();
3805 tupleIndex = FT_GET_USHORT();
3806
3807 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3808 {
3809 for ( j = 0; j < blend->num_axis; j++ )
3810 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */
3811 /* short frac to fixed */
3812 }
3813 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3814 {
3815 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3816 " invalid tuple index\n" ));
3817
3818 error = FT_THROW( Invalid_Table );
3819 goto Fail3;
3820 }
3821 else
3822 FT_MEM_COPY(
3823 tuple_coords,
3824 blend->tuplecoords +
3825 ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
3826 blend->num_axis * sizeof ( FT_Fixed ) );
3827
3828 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3829 {
3830 for ( j = 0; j < blend->num_axis; j++ )
3831 im_start_coords[j] = FT_GET_SHORT() * 4;
3832 for ( j = 0; j < blend->num_axis; j++ )
3833 im_end_coords[j] = FT_GET_SHORT() * 4;
3834 }
3835
3836 apply = ft_var_apply_tuple( blend,
3837 (FT_UShort)tupleIndex,
3838 tuple_coords,
3839 im_start_coords,
3840 im_end_coords );
3841
3842 if ( apply == 0 ) /* tuple isn't active for our blend */
3843 {
3844 offsetToData += tupleDataSize;
3845 continue;
3869 deltas_y = ft_var_readpackeddeltas( stream,
3870 blend->gvar_size,
3871 point_count == 0 ? n_points
3872 : point_count );
3873
3874 if ( !points || !deltas_y || !deltas_x )
3875 ; /* failure, ignore it */
3876
3877 else if ( points == ALL_POINTS )
3878 {
3879 #ifdef FT_DEBUG_LEVEL_TRACE
3880 int count = 0;
3881 #endif
3882
3883
3884 FT_TRACE7(( " point deltas:\n" ));
3885
3886 /* this means that there are deltas for every point in the glyph */
3887 for ( j = 0; j < n_points; j++ )
3888 {
3889 FT_Fixed old_point_delta_x = point_deltas_x[j];
3890 FT_Fixed old_point_delta_y = point_deltas_y[j];
3891
3892 FT_Fixed point_delta_x = FT_MulFix( deltas_x[j], apply );
3893 FT_Fixed point_delta_y = FT_MulFix( deltas_y[j], apply );
3894
3895
3896 if ( j < n_points - 4 )
3897 {
3898 point_deltas_x[j] = old_point_delta_x + point_delta_x;
3899 point_deltas_y[j] = old_point_delta_y + point_delta_y;
3900 }
3901 else
3902 {
3903 /* To avoid double adjustment of advance width or height, */
3904 /* adjust phantom points only if there is no HVAR or VVAR */
3905 /* support, respectively. */
3906 if ( j == ( n_points - 4 ) &&
3907 !( face->variation_support &
3908 TT_FACE_FLAG_VAR_LSB ) )
3909 point_deltas_x[j] = old_point_delta_x + point_delta_x;
3910
3911 else if ( j == ( n_points - 3 ) &&
3912 !( face->variation_support &
3913 TT_FACE_FLAG_VAR_HADVANCE ) )
3914 point_deltas_x[j] = old_point_delta_x + point_delta_x;
3915
3916 else if ( j == ( n_points - 2 ) &&
3917 !( face->variation_support &
3918 TT_FACE_FLAG_VAR_TSB ) )
3919 point_deltas_y[j] = old_point_delta_y + point_delta_y;
3920
3921 else if ( j == ( n_points - 1 ) &&
3922 !( face->variation_support &
3923 TT_FACE_FLAG_VAR_VADVANCE ) )
3924 point_deltas_y[j] = old_point_delta_y + point_delta_y;
3925 }
3926
3927 #ifdef FT_DEBUG_LEVEL_TRACE
3928 if ( point_delta_x || point_delta_y )
3929 {
3930 FT_TRACE7(( " %d: (%f, %f) -> (%f, %f)\n",
3931 j,
3932 ( FT_intToFixed( outline->points[j].x ) +
3933 old_point_delta_x ) / 65536.0,
3934 ( FT_intToFixed( outline->points[j].y ) +
3935 old_point_delta_y ) / 65536.0,
3936 ( FT_intToFixed( outline->points[j].x ) +
3937 point_deltas_x[j] ) / 65536.0,
3938 ( FT_intToFixed( outline->points[j].y ) +
3939 point_deltas_y[j] ) / 65536.0 ));
3940 count++;
3941 }
3942 #endif
3943 }
3944
3945 #ifdef FT_DEBUG_LEVEL_TRACE
3946 if ( !count )
3947 FT_TRACE7(( " none\n" ));
3948 #endif
3949 }
3950
3951 else
3952 {
3953 #ifdef FT_DEBUG_LEVEL_TRACE
3954 int count = 0;
3955 #endif
3956
3957
3958 /* we have to interpolate the missing deltas similar to the */
3959 /* IUP bytecode instruction */
3971 if ( idx >= n_points )
3972 continue;
3973
3974 has_delta[idx] = TRUE;
3975
3976 points_out[idx].x += FT_MulFix( deltas_x[j], apply );
3977 points_out[idx].y += FT_MulFix( deltas_y[j], apply );
3978 }
3979
3980 /* no need to handle phantom points here, */
3981 /* since solitary points can't be interpolated */
3982 tt_interpolate_deltas( outline,
3983 points_out,
3984 points_org,
3985 has_delta );
3986
3987 FT_TRACE7(( " point deltas:\n" ));
3988
3989 for ( j = 0; j < n_points; j++ )
3990 {
3991 FT_Fixed old_point_delta_x = point_deltas_x[j];
3992 FT_Fixed old_point_delta_y = point_deltas_y[j];
3993
3994 FT_Pos point_delta_x = points_out[j].x - points_org[j].x;
3995 FT_Pos point_delta_y = points_out[j].y - points_org[j].y;
3996
3997
3998 if ( j < n_points - 4 )
3999 {
4000 point_deltas_x[j] = old_point_delta_x + point_delta_x;
4001 point_deltas_y[j] = old_point_delta_y + point_delta_y;
4002 }
4003 else
4004 {
4005 /* To avoid double adjustment of advance width or height, */
4006 /* adjust phantom points only if there is no HVAR or VVAR */
4007 /* support, respectively. */
4008 if ( j == ( n_points - 4 ) &&
4009 !( face->variation_support &
4010 TT_FACE_FLAG_VAR_LSB ) )
4011 point_deltas_x[j] = old_point_delta_x + point_delta_x;
4012
4013 else if ( j == ( n_points - 3 ) &&
4014 !( face->variation_support &
4015 TT_FACE_FLAG_VAR_HADVANCE ) )
4016 point_deltas_x[j] = old_point_delta_x + point_delta_x;
4017
4018 else if ( j == ( n_points - 2 ) &&
4019 !( face->variation_support &
4020 TT_FACE_FLAG_VAR_TSB ) )
4021 point_deltas_y[j] = old_point_delta_y + point_delta_y;
4022
4023 else if ( j == ( n_points - 1 ) &&
4024 !( face->variation_support &
4025 TT_FACE_FLAG_VAR_VADVANCE ) )
4026 point_deltas_y[j] = old_point_delta_y + point_delta_y;
4027 }
4028
4029 #ifdef FT_DEBUG_LEVEL_TRACE
4030 if ( point_delta_x || point_delta_y )
4031 {
4032 FT_TRACE7(( " %d: (%f, %f) -> (%f, %f)\n",
4033 j,
4034 ( FT_intToFixed( outline->points[j].x ) +
4035 old_point_delta_x ) / 65536.0,
4036 ( FT_intToFixed( outline->points[j].y ) +
4037 old_point_delta_y ) / 65536.0,
4038 ( FT_intToFixed( outline->points[j].x ) +
4039 point_deltas_x[j] ) / 65536.0,
4040 ( FT_intToFixed( outline->points[j].y ) +
4041 point_deltas_y[j] ) / 65536.0 ));
4042 count++;
4043 }
4044 #endif
4045 }
4046
4047 #ifdef FT_DEBUG_LEVEL_TRACE
4048 if ( !count )
4049 FT_TRACE7(( " none\n" ));
4050 #endif
4051 }
4052
4053 if ( localpoints != ALL_POINTS )
4054 FT_FREE( localpoints );
4055 FT_FREE( deltas_x );
4056 FT_FREE( deltas_y );
4057
4058 offsetToData += tupleDataSize;
4059
4060 FT_Stream_SeekSet( stream, here );
4061 }
4062
4063 FT_TRACE5(( "\n" ));
4064
4065 for ( i = 0; i < n_points; i++ )
4066 {
4067 outline->points[i].x += FT_fixedToInt( point_deltas_x[i] );
4068 outline->points[i].y += FT_fixedToInt( point_deltas_y[i] );
4069 }
4070
4071 Fail3:
4072 FT_FREE( point_deltas_x );
4073 FT_FREE( point_deltas_y );
4074
4075 Fail2:
4076 if ( sharedpoints != ALL_POINTS )
4077 FT_FREE( sharedpoints );
4078 FT_FREE( tuple_coords );
4079 FT_FREE( im_start_coords );
4080 FT_FREE( im_end_coords );
4081
4082 FT_FRAME_EXIT();
4083
4084 Fail1:
4085 FT_FREE( points_org );
4086 FT_FREE( points_out );
4087 FT_FREE( has_delta );
4088
4089 return error;
4090 }
4091
4092
4093 /**************************************************************************
4094 *
4095 * @Function:
4096 * tt_get_var_blend
4097 *
4098 * @Description:
4099 * An extended internal version of `TT_Get_MM_Blend' that returns
4100 * pointers instead of copying data, without any initialization of
4101 * the MM machinery in case it isn't loaded yet.
4102 */
4103 FT_LOCAL_DEF( FT_Error )
4104 tt_get_var_blend( TT_Face face,
4105 FT_UInt *num_coords,
4106 FT_Fixed* *coords,
4107 FT_Fixed* *normalizedcoords,
4108 FT_MM_Var* *mm_var )
4109 {
4110 if ( face->blend )
4111 {
4112 if ( num_coords )
4113 *num_coords = face->blend->num_axis;
4114 if ( coords )
4115 *coords = face->blend->coords;
4116 if ( normalizedcoords )
4117 *normalizedcoords = face->blend->normalizedcoords;
4118 if ( mm_var )
4119 *mm_var = face->blend->mmvar;
4120 }
4121 else
4122 {
4144 {
4145 for ( i = 0; i < itemStore->dataCount; i++ )
4146 {
4147 FT_FREE( itemStore->varData[i].regionIndices );
4148 FT_FREE( itemStore->varData[i].deltaSet );
4149 }
4150
4151 FT_FREE( itemStore->varData );
4152 }
4153
4154 if ( itemStore->varRegionList )
4155 {
4156 for ( i = 0; i < itemStore->regionCount; i++ )
4157 FT_FREE( itemStore->varRegionList[i].axisList );
4158
4159 FT_FREE( itemStore->varRegionList );
4160 }
4161 }
4162
4163
4164 /**************************************************************************
4165 *
4166 * @Function:
4167 * tt_done_blend
4168 *
4169 * @Description:
4170 * Free the blend internal data structure.
4171 */
4172 FT_LOCAL_DEF( void )
4173 tt_done_blend( TT_Face face )
4174 {
4175 FT_Memory memory = FT_FACE_MEMORY( face );
4176 GX_Blend blend = face->blend;
4177
4178
4179 if ( blend )
4180 {
4181 FT_UInt i, num_axes;
4182
4183
4184 /* blend->num_axis might not be set up yet */
4185 num_axes = blend->mmvar->num_axis;
4186
4187 FT_FREE( blend->coords );
4188 FT_FREE( blend->normalizedcoords );
4189 FT_FREE( blend->normalized_stylecoords );
4190 FT_FREE( blend->mmvar );
4191
|