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