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