1 /***************************************************************************/ 2 /* */ 3 /* ttgxvar.c */ 4 /* */ 5 /* TrueType GX Font Variation loader */ 6 /* */ 7 /* Copyright 2004, 2005, 2006, 2007, 2008, 2009 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; 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 ) 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 ) 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; 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; 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 fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount || 686 fvar_head.offsetToData + fvar_head.axisCount * 20U + 687 fvar_head.instanceCount * fvar_head.instanceSize > table_len ) 688 { 689 error = TT_Err_Invalid_Table; 690 goto Exit; 691 } 692 693 if ( FT_NEW( face->blend ) ) 694 goto Exit; 695 696 /* XXX: TODO - check for overflows */ 697 face->blend->mmvar_len = 698 sizeof ( FT_MM_Var ) + 699 fvar_head.axisCount * sizeof ( FT_Var_Axis ) + 700 fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) + 701 fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) + 702 5 * fvar_head.axisCount; 703 704 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 705 goto Exit; 706 face->blend->mmvar = mmvar; 707 708 mmvar->num_axis = 709 fvar_head.axisCount; 710 mmvar->num_designs = 711 (FT_UInt)-1; /* meaningless in this context; each glyph */ 712 /* may have a different number of designs */ 713 /* (or tuples, as called by Apple) */ 714 mmvar->num_namedstyles = 715 fvar_head.instanceCount; 716 mmvar->axis = 717 (FT_Var_Axis*)&(mmvar[1]); 718 mmvar->namedstyle = 719 (FT_Var_Named_Style*)&(mmvar->axis[fvar_head.axisCount]); 720 721 next_coords = 722 (FT_Fixed*)&(mmvar->namedstyle[fvar_head.instanceCount]); 723 for ( i = 0; i < fvar_head.instanceCount; ++i ) 724 { 725 mmvar->namedstyle[i].coords = next_coords; 726 next_coords += fvar_head.axisCount; 727 } 728 729 next_name = (FT_String*)next_coords; 730 for ( i = 0; i < fvar_head.axisCount; ++i ) 731 { 732 mmvar->axis[i].name = next_name; 733 next_name += 5; 734 } 735 736 if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) ) 737 goto Exit; 738 739 a = mmvar->axis; 740 for ( i = 0; i < fvar_head.axisCount; ++i ) 741 { 742 GX_FVar_Axis axis_rec; 743 744 745 if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) ) 746 goto Exit; 747 a->tag = axis_rec.axisTag; 748 a->minimum = axis_rec.minValue; /* A Fixed */ 749 a->def = axis_rec.defaultValue; /* A Fixed */ 750 a->maximum = axis_rec.maxValue; /* A Fixed */ 751 a->strid = axis_rec.nameID; 752 753 a->name[0] = (FT_String)( a->tag >> 24 ); 754 a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF ); 755 a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF ); 756 a->name[3] = (FT_String)( ( a->tag ) & 0xFF ); 757 a->name[4] = 0; 758 759 ++a; 760 } 761 762 ns = mmvar->namedstyle; 763 for ( i = 0; i < fvar_head.instanceCount; ++i, ++ns ) 764 { 765 if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) ) 766 goto Exit; 767 768 ns->strid = FT_GET_USHORT(); 769 (void) /* flags = */ FT_GET_USHORT(); 770 771 for ( j = 0; j < fvar_head.axisCount; ++j ) 772 ns->coords[j] = FT_GET_ULONG(); /* A Fixed */ 773 774 FT_FRAME_EXIT(); 775 } 776 } 777 778 if ( master != NULL ) 779 { 780 FT_UInt n; 781 782 783 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 784 goto Exit; 785 FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len ); 786 787 mmvar->axis = 788 (FT_Var_Axis*)&(mmvar[1]); 789 mmvar->namedstyle = 790 (FT_Var_Named_Style*)&(mmvar->axis[mmvar->num_axis]); 791 next_coords = 792 (FT_Fixed*)&(mmvar->namedstyle[mmvar->num_namedstyles]); 793 794 for ( n = 0; n < mmvar->num_namedstyles; ++n ) 795 { 796 mmvar->namedstyle[n].coords = next_coords; 797 next_coords += mmvar->num_axis; 798 } 799 800 a = mmvar->axis; 801 next_name = (FT_String*)next_coords; 802 for ( n = 0; n < mmvar->num_axis; ++n ) 803 { 804 a->name = next_name; 805 806 /* standard PostScript names for some standard apple tags */ 807 if ( a->tag == TTAG_wght ) 808 a->name = (char *)"Weight"; 809 else if ( a->tag == TTAG_wdth ) 810 a->name = (char *)"Width"; 811 else if ( a->tag == TTAG_opsz ) 812 a->name = (char *)"OpticalSize"; 813 else if ( a->tag == TTAG_slnt ) 814 a->name = (char *)"Slant"; 815 816 next_name += 5; 817 ++a; 818 } 819 820 *master = mmvar; 821 } 822 823 Exit: 824 return error; 825 } 826 827 828 /*************************************************************************/ 829 /* */ 830 /* <Function> */ 831 /* TT_Set_MM_Blend */ 832 /* */ 833 /* <Description> */ 834 /* Set the blend (normalized) coordinates for this instance of the */ 835 /* font. Check that the `gvar' table is reasonable and does some */ 836 /* initial preparation. */ 837 /* */ 838 /* <InOut> */ 839 /* face :: The font. */ 840 /* Initialize the blend structure with `gvar' data. */ 841 /* */ 842 /* <Input> */ 843 /* num_coords :: Must be the axis count of the font. */ 844 /* */ 845 /* coords :: An array of num_coords, each between [-1,1]. */ 846 /* */ 847 /* <Return> */ 848 /* FreeType error code. 0 means success. */ 849 /* */ 850 FT_LOCAL_DEF( FT_Error ) 851 TT_Set_MM_Blend( TT_Face face, 852 FT_UInt num_coords, 853 FT_Fixed* coords ) 854 { 855 FT_Error error = TT_Err_Ok; 856 GX_Blend blend; 857 FT_MM_Var* mmvar; 858 FT_UInt i; 859 FT_Memory memory = face->root.memory; 860 861 enum 862 { 863 mcvt_retain, 864 mcvt_modify, 865 mcvt_load 866 867 } manageCvt; 868 869 870 face->doblend = FALSE; 871 872 if ( face->blend == NULL ) 873 { 874 if ( (error = TT_Get_MM_Var( face, NULL)) != 0 ) 875 goto Exit; 876 } 877 878 blend = face->blend; 879 mmvar = blend->mmvar; 880 881 if ( num_coords != mmvar->num_axis ) 882 { 883 error = TT_Err_Invalid_Argument; 884 goto Exit; 885 } 886 887 for ( i = 0; i < num_coords; ++i ) 888 if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) 889 { 890 error = TT_Err_Invalid_Argument; 891 goto Exit; 892 } 893 894 if ( blend->glyphoffsets == NULL ) 895 if ( (error = ft_var_load_gvar( face )) != 0 ) 896 goto Exit; 897 898 if ( blend->normalizedcoords == NULL ) 899 { 900 if ( FT_NEW_ARRAY( blend->normalizedcoords, num_coords ) ) 901 goto Exit; 902 903 manageCvt = mcvt_modify; 904 905 /* If we have not set the blend coordinates before this, then the */ 906 /* cvt table will still be what we read from the `cvt ' table and */ 907 /* we don't need to reload it. We may need to change it though... */ 908 } 909 else 910 { 911 manageCvt = mcvt_retain; 912 for ( i = 0; i < num_coords; ++i ) 913 { 914 if ( blend->normalizedcoords[i] != coords[i] ) 915 { 916 manageCvt = mcvt_load; 917 break; 918 } 919 } 920 921 /* If we don't change the blend coords then we don't need to do */ 922 /* anything to the cvt table. It will be correct. Otherwise we */ 923 /* no longer have the original cvt (it was modified when we set */ 924 /* the blend last time), so we must reload and then modify it. */ 925 } 926 927 blend->num_axis = num_coords; 928 FT_MEM_COPY( blend->normalizedcoords, 929 coords, 930 num_coords * sizeof ( FT_Fixed ) ); 931 932 face->doblend = TRUE; 933 934 if ( face->cvt != NULL ) 935 { 936 switch ( manageCvt ) 937 { 938 case mcvt_load: 939 /* The cvt table has been loaded already; every time we change the */ 940 /* blend we may need to reload and remodify the cvt table. */ 941 FT_FREE( face->cvt ); 942 face->cvt = NULL; 943 944 tt_face_load_cvt( face, face->root.stream ); 945 break; 946 947 case mcvt_modify: 948 /* The original cvt table is in memory. All we need to do is */ 949 /* apply the `cvar' table (if any). */ 950 tt_face_vary_cvt( face, face->root.stream ); 951 break; 952 953 case mcvt_retain: 954 /* The cvt table is correct for this set of coordinates. */ 955 break; 956 } 957 } 958 959 Exit: 960 return error; 961 } 962 963 964 /*************************************************************************/ 965 /* */ 966 /* <Function> */ 967 /* TT_Set_Var_Design */ 968 /* */ 969 /* <Description> */ 970 /* Set the coordinates for the instance, measured in the user */ 971 /* coordinate system. Parse the `avar' table (if present) to convert */ 972 /* from user to normalized coordinates. */ 973 /* */ 974 /* <InOut> */ 975 /* face :: The font face. */ 976 /* Initialize the blend struct with `gvar' data. */ 977 /* */ 978 /* <Input> */ 979 /* num_coords :: This must be the axis count of the font. */ 980 /* */ 981 /* coords :: A coordinate array with `num_coords' elements. */ 982 /* */ 983 /* <Return> */ 984 /* FreeType error code. 0 means success. */ 985 /* */ 986 FT_LOCAL_DEF( FT_Error ) 987 TT_Set_Var_Design( TT_Face face, 988 FT_UInt num_coords, 989 FT_Fixed* coords ) 990 { 991 FT_Error error = TT_Err_Ok; 992 FT_Fixed* normalized = NULL; 993 GX_Blend blend; 994 FT_MM_Var* mmvar; 995 FT_UInt i, j; 996 FT_Var_Axis* a; 997 GX_AVarSegment av; 998 FT_Memory memory = face->root.memory; 999 1000 1001 if ( face->blend == NULL ) 1002 { 1003 if ( (error = TT_Get_MM_Var( face, NULL )) != 0 ) 1004 goto Exit; 1005 } 1006 1007 blend = face->blend; 1008 mmvar = blend->mmvar; 1009 1010 if ( num_coords != mmvar->num_axis ) 1011 { 1012 error = TT_Err_Invalid_Argument; 1013 goto Exit; 1014 } 1015 1016 /* Axis normalization is a two stage process. First we normalize */ 1017 /* based on the [min,def,max] values for the axis to be [-1,0,1]. */ 1018 /* Then, if there's an `avar' table, we renormalize this range. */ 1019 1020 if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) 1021 goto Exit; 1022 1023 a = mmvar->axis; 1024 for ( i = 0; i < mmvar->num_axis; ++i, ++a ) 1025 { 1026 if ( coords[i] > a->maximum || coords[i] < a->minimum ) 1027 { 1028 error = TT_Err_Invalid_Argument; 1029 goto Exit; 1030 } 1031 1032 if ( coords[i] < a->def ) 1033 { 1034 normalized[i] = -FT_MulDiv( coords[i] - a->def, 1035 0x10000L, 1036 a->minimum - a->def ); 1037 } 1038 else if ( a->maximum == a->def ) 1039 normalized[i] = 0; 1040 else 1041 { 1042 normalized[i] = FT_MulDiv( coords[i] - a->def, 1043 0x10000L, 1044 a->maximum - a->def ); 1045 } 1046 } 1047 1048 if ( !blend->avar_checked ) 1049 ft_var_load_avar( face ); 1050 1051 if ( blend->avar_segment != NULL ) 1052 { 1053 av = blend->avar_segment; 1054 for ( i = 0; i < mmvar->num_axis; ++i, ++av ) 1055 { 1056 for ( j = 1; j < (FT_UInt)av->pairCount; ++j ) 1057 if ( normalized[i] < av->correspondence[j].fromCoord ) 1058 { 1059 normalized[i] = 1060 FT_MulDiv( 1061 FT_MulDiv( 1062 normalized[i] - av->correspondence[j - 1].fromCoord, 1063 0x10000L, 1064 av->correspondence[j].fromCoord - 1065 av->correspondence[j - 1].fromCoord ), 1066 av->correspondence[j].toCoord - 1067 av->correspondence[j - 1].toCoord, 1068 0x10000L ) + 1069 av->correspondence[j - 1].toCoord; 1070 break; 1071 } 1072 } 1073 } 1074 1075 error = TT_Set_MM_Blend( face, num_coords, normalized ); 1076 1077 Exit: 1078 FT_FREE( normalized ); 1079 return error; 1080 } 1081 1082 1083 /*************************************************************************/ 1084 /*************************************************************************/ 1085 /***** *****/ 1086 /***** GX VAR PARSING ROUTINES *****/ 1087 /***** *****/ 1088 /*************************************************************************/ 1089 /*************************************************************************/ 1090 1091 1092 /*************************************************************************/ 1093 /* */ 1094 /* <Function> */ 1095 /* tt_face_vary_cvt */ 1096 /* */ 1097 /* <Description> */ 1098 /* Modify the loaded cvt table according to the `cvar' table and the */ 1099 /* font's blend. */ 1100 /* */ 1101 /* <InOut> */ 1102 /* face :: A handle to the target face object. */ 1103 /* */ 1104 /* <Input> */ 1105 /* stream :: A handle to the input stream. */ 1106 /* */ 1107 /* <Return> */ 1108 /* FreeType error code. 0 means success. */ 1109 /* */ 1110 /* Most errors are ignored. It is perfectly valid not to have a */ 1111 /* `cvar' table even if there is a `gvar' and `fvar' table. */ 1112 /* */ 1113 FT_LOCAL_DEF( FT_Error ) 1114 tt_face_vary_cvt( TT_Face face, 1115 FT_Stream stream ) 1116 { 1117 FT_Error error; 1118 FT_Memory memory = stream->memory; 1119 FT_ULong table_start; 1120 FT_ULong table_len; 1121 FT_UInt tupleCount; 1122 FT_ULong offsetToData; 1123 FT_ULong here; 1124 FT_UInt i, j; 1125 FT_Fixed* tuple_coords = NULL; 1126 FT_Fixed* im_start_coords = NULL; 1127 FT_Fixed* im_end_coords = NULL; 1128 GX_Blend blend = face->blend; 1129 FT_UInt point_count; 1130 FT_UShort* localpoints; 1131 FT_Short* deltas; 1132 1133 1134 FT_TRACE2(( "CVAR " )); 1135 1136 if ( blend == NULL ) 1137 { 1138 FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" )); 1139 1140 error = TT_Err_Ok; 1141 goto Exit; 1142 } 1143 1144 if ( face->cvt == NULL ) 1145 { 1146 FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" )); 1147 1148 error = TT_Err_Ok; 1149 goto Exit; 1150 } 1151 1152 error = face->goto_table( face, TTAG_cvar, stream, &table_len ); 1153 if ( error ) 1154 { 1155 FT_TRACE2(( "is missing\n" )); 1156 1157 error = TT_Err_Ok; 1158 goto Exit; 1159 } 1160 1161 if ( FT_FRAME_ENTER( table_len ) ) 1162 { 1163 error = TT_Err_Ok; 1164 goto Exit; 1165 } 1166 1167 table_start = FT_Stream_FTell( stream ); 1168 if ( FT_GET_LONG() != 0x00010000L ) 1169 { 1170 FT_TRACE2(( "bad table version\n" )); 1171 1172 error = TT_Err_Ok; 1173 goto FExit; 1174 } 1175 1176 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 1177 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 1178 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 1179 goto FExit; 1180 1181 tupleCount = FT_GET_USHORT(); 1182 offsetToData = table_start + FT_GET_USHORT(); 1183 1184 /* The documentation implies there are flags packed into the */ 1185 /* tuplecount, but John Jenkins says that shared points don't apply */ 1186 /* to `cvar', and no other flags are defined. */ 1187 1188 for ( i = 0; i < ( tupleCount & 0xFFF ); ++i ) 1189 { 1190 FT_UInt tupleDataSize; 1191 FT_UInt tupleIndex; 1192 FT_Fixed apply; 1193 1194 1195 tupleDataSize = FT_GET_USHORT(); 1196 tupleIndex = FT_GET_USHORT(); 1197 1198 /* There is no provision here for a global tuple coordinate section, */ 1199 /* so John says. There are no tuple indices, just embedded tuples. */ 1200 1201 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 1202 { 1203 for ( j = 0; j < blend->num_axis; ++j ) 1204 tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from */ 1205 /* short frac to fixed */ 1206 } 1207 else 1208 { 1209 /* skip this tuple; it makes no sense */ 1210 1211 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1212 for ( j = 0; j < 2 * blend->num_axis; ++j ) 1213 (void)FT_GET_SHORT(); 1214 1215 offsetToData += tupleDataSize; 1216 continue; 1217 } 1218 1219 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1220 { 1221 for ( j = 0; j < blend->num_axis; ++j ) 1222 im_start_coords[j] = FT_GET_SHORT() << 2; 1223 for ( j = 0; j < blend->num_axis; ++j ) 1224 im_end_coords[j] = FT_GET_SHORT() << 2; 1225 } 1226 1227 apply = ft_var_apply_tuple( blend, 1228 (FT_UShort)tupleIndex, 1229 tuple_coords, 1230 im_start_coords, 1231 im_end_coords ); 1232 if ( /* tuple isn't active for our blend */ 1233 apply == 0 || 1234 /* global points not allowed, */ 1235 /* if they aren't local, makes no sense */ 1236 !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) ) 1237 { 1238 offsetToData += tupleDataSize; 1239 continue; 1240 } 1241 1242 here = FT_Stream_FTell( stream ); 1243 1244 FT_Stream_SeekSet( stream, offsetToData ); 1245 1246 localpoints = ft_var_readpackedpoints( stream, &point_count ); 1247 deltas = ft_var_readpackeddeltas( stream, 1248 point_count == 0 ? face->cvt_size 1249 : point_count ); 1250 if ( localpoints == NULL || deltas == NULL ) 1251 /* failure, ignore it */; 1252 1253 else if ( localpoints == ALL_POINTS ) 1254 { 1255 /* this means that there are deltas for every entry in cvt */ 1256 for ( j = 0; j < face->cvt_size; ++j ) 1257 face->cvt[j] = (FT_Short)( face->cvt[j] + 1258 FT_MulFix( deltas[j], apply ) ); 1259 } 1260 1261 else 1262 { 1263 for ( j = 0; j < point_count; ++j ) 1264 { 1265 int pindex = localpoints[j]; 1266 1267 face->cvt[pindex] = (FT_Short)( face->cvt[pindex] + 1268 FT_MulFix( deltas[j], apply ) ); 1269 } 1270 } 1271 1272 if ( localpoints != ALL_POINTS ) 1273 FT_FREE( localpoints ); 1274 FT_FREE( deltas ); 1275 1276 offsetToData += tupleDataSize; 1277 1278 FT_Stream_SeekSet( stream, here ); 1279 } 1280 1281 FExit: 1282 FT_FRAME_EXIT(); 1283 1284 Exit: 1285 FT_FREE( tuple_coords ); 1286 FT_FREE( im_start_coords ); 1287 FT_FREE( im_end_coords ); 1288 1289 return error; 1290 } 1291 1292 1293 /*************************************************************************/ 1294 /* */ 1295 /* <Function> */ 1296 /* TT_Vary_Get_Glyph_Deltas */ 1297 /* */ 1298 /* <Description> */ 1299 /* Load the appropriate deltas for the current glyph. */ 1300 /* */ 1301 /* <Input> */ 1302 /* face :: A handle to the target face object. */ 1303 /* */ 1304 /* glyph_index :: The index of the glyph being modified. */ 1305 /* */ 1306 /* n_points :: The number of the points in the glyph, including */ 1307 /* phantom points. */ 1308 /* */ 1309 /* <Output> */ 1310 /* deltas :: The array of points to change. */ 1311 /* */ 1312 /* <Return> */ 1313 /* FreeType error code. 0 means success. */ 1314 /* */ 1315 FT_LOCAL_DEF( FT_Error ) 1316 TT_Vary_Get_Glyph_Deltas( TT_Face face, 1317 FT_UInt glyph_index, 1318 FT_Vector* *deltas, 1319 FT_UInt n_points ) 1320 { 1321 FT_Stream stream = face->root.stream; 1322 FT_Memory memory = stream->memory; 1323 GX_Blend blend = face->blend; 1324 FT_Vector* delta_xy; 1325 1326 FT_Error error; 1327 FT_ULong glyph_start; 1328 FT_UInt tupleCount; 1329 FT_ULong offsetToData; 1330 FT_ULong here; 1331 FT_UInt i, j; 1332 FT_Fixed* tuple_coords = NULL; 1333 FT_Fixed* im_start_coords = NULL; 1334 FT_Fixed* im_end_coords = NULL; 1335 FT_UInt point_count, spoint_count = 0; 1336 FT_UShort* sharedpoints = NULL; 1337 FT_UShort* localpoints = NULL; 1338 FT_UShort* points; 1339 FT_Short *deltas_x, *deltas_y; 1340 1341 1342 if ( !face->doblend || blend == NULL ) 1343 return TT_Err_Invalid_Argument; 1344 1345 /* to be freed by the caller */ 1346 if ( FT_NEW_ARRAY( delta_xy, n_points ) ) 1347 goto Exit; 1348 *deltas = delta_xy; 1349 1350 if ( glyph_index >= blend->gv_glyphcnt || 1351 blend->glyphoffsets[glyph_index] == 1352 blend->glyphoffsets[glyph_index + 1] ) 1353 return TT_Err_Ok; /* no variation data for this glyph */ 1354 1355 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) || 1356 FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] - 1357 blend->glyphoffsets[glyph_index] ) ) 1358 goto Fail1; 1359 1360 glyph_start = FT_Stream_FTell( stream ); 1361 1362 /* each set of glyph variation data is formatted similarly to `cvar' */ 1363 /* (except we get shared points and global tuples) */ 1364 1365 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 1366 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 1367 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 1368 goto Fail2; 1369 1370 tupleCount = FT_GET_USHORT(); 1371 offsetToData = glyph_start + FT_GET_USHORT(); 1372 1373 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 1374 { 1375 here = FT_Stream_FTell( stream ); 1376 1377 FT_Stream_SeekSet( stream, offsetToData ); 1378 1379 sharedpoints = ft_var_readpackedpoints( stream, &spoint_count ); 1380 offsetToData = FT_Stream_FTell( stream ); 1381 1382 FT_Stream_SeekSet( stream, here ); 1383 } 1384 1385 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i ) 1386 { 1387 FT_UInt tupleDataSize; 1388 FT_UInt tupleIndex; 1389 FT_Fixed apply; 1390 1391 1392 tupleDataSize = FT_GET_USHORT(); 1393 tupleIndex = FT_GET_USHORT(); 1394 1395 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 1396 { 1397 for ( j = 0; j < blend->num_axis; ++j ) 1398 tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from */ 1399 /* short frac to fixed */ 1400 } 1401 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 1402 { 1403 error = TT_Err_Invalid_Table; 1404 goto Fail3; 1405 } 1406 else 1407 { 1408 FT_MEM_COPY( 1409 tuple_coords, 1410 &blend->tuplecoords[(tupleIndex & 0xFFF) * blend->num_axis], 1411 blend->num_axis * sizeof ( FT_Fixed ) ); 1412 } 1413 1414 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1415 { 1416 for ( j = 0; j < blend->num_axis; ++j ) 1417 im_start_coords[j] = FT_GET_SHORT() << 2; 1418 for ( j = 0; j < blend->num_axis; ++j ) 1419 im_end_coords[j] = FT_GET_SHORT() << 2; 1420 } 1421 1422 apply = ft_var_apply_tuple( blend, 1423 (FT_UShort)tupleIndex, 1424 tuple_coords, 1425 im_start_coords, 1426 im_end_coords ); 1427 1428 if ( apply == 0 ) /* tuple isn't active for our blend */ 1429 { 1430 offsetToData += tupleDataSize; 1431 continue; 1432 } 1433 1434 here = FT_Stream_FTell( stream ); 1435 1436 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 1437 { 1438 FT_Stream_SeekSet( stream, offsetToData ); 1439 1440 localpoints = ft_var_readpackedpoints( stream, &point_count ); 1441 points = localpoints; 1442 } 1443 else 1444 { 1445 points = sharedpoints; 1446 point_count = spoint_count; 1447 } 1448 1449 deltas_x = ft_var_readpackeddeltas( stream, 1450 point_count == 0 ? n_points 1451 : point_count ); 1452 deltas_y = ft_var_readpackeddeltas( stream, 1453 point_count == 0 ? n_points 1454 : point_count ); 1455 1456 if ( points == NULL || deltas_y == NULL || deltas_x == NULL ) 1457 ; /* failure, ignore it */ 1458 1459 else if ( points == ALL_POINTS ) 1460 { 1461 /* this means that there are deltas for every point in the glyph */ 1462 for ( j = 0; j < n_points; ++j ) 1463 { 1464 delta_xy[j].x += FT_MulFix( deltas_x[j], apply ); 1465 delta_xy[j].y += FT_MulFix( deltas_y[j], apply ); 1466 } 1467 } 1468 1469 else 1470 { 1471 for ( j = 0; j < point_count; ++j ) 1472 { 1473 delta_xy[localpoints[j]].x += FT_MulFix( deltas_x[j], apply ); 1474 delta_xy[localpoints[j]].y += FT_MulFix( deltas_y[j], apply ); 1475 } 1476 } 1477 1478 if ( localpoints != ALL_POINTS ) 1479 FT_FREE( localpoints ); 1480 FT_FREE( deltas_x ); 1481 FT_FREE( deltas_y ); 1482 1483 offsetToData += tupleDataSize; 1484 1485 FT_Stream_SeekSet( stream, here ); 1486 } 1487 1488 Fail3: 1489 FT_FREE( tuple_coords ); 1490 FT_FREE( im_start_coords ); 1491 FT_FREE( im_end_coords ); 1492 1493 Fail2: 1494 FT_FRAME_EXIT(); 1495 1496 Fail1: 1497 if ( error ) 1498 { 1499 FT_FREE( delta_xy ); 1500 *deltas = NULL; 1501 } 1502 1503 Exit: 1504 return error; 1505 } 1506 1507 1508 /*************************************************************************/ 1509 /* */ 1510 /* <Function> */ 1511 /* tt_done_blend */ 1512 /* */ 1513 /* <Description> */ 1514 /* Frees the blend internal data structure. */ 1515 /* */ 1516 FT_LOCAL_DEF( void ) 1517 tt_done_blend( FT_Memory memory, 1518 GX_Blend blend ) 1519 { 1520 if ( blend != NULL ) 1521 { 1522 FT_UInt i; 1523 1524 1525 FT_FREE( blend->normalizedcoords ); 1526 FT_FREE( blend->mmvar ); 1527 1528 if ( blend->avar_segment != NULL ) 1529 { 1530 for ( i = 0; i < blend->num_axis; ++i ) 1531 FT_FREE( blend->avar_segment[i].correspondence ); 1532 FT_FREE( blend->avar_segment ); 1533 } 1534 1535 FT_FREE( blend->tuplecoords ); 1536 FT_FREE( blend->glyphoffsets ); 1537 FT_FREE( blend ); 1538 } 1539 } 1540 1541 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 1542 1543 1544 /* END */ 1545