1 /***************************************************************************/ 2 /* */ 3 /* ttgxvar.c */ 4 /* */ 5 /* TrueType GX Font Variation loader */ 6 /* */ 7 /* Copyright 2004, 2005, 2006, 2007, 2008 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 be 2. */ 27 /* */ 28 /* The documentation for `gvar' is not intelligible; `cvar' refers you to */ 29 /* `gvar' and is thus also incomprehensible. */ 30 /* */ 31 /* The documentation for `avar' appears correct, but Apple has no fonts */ 32 /* with an `avar' table, so it is hard to test. */ 33 /* */ 34 /* Many thanks to John Jenkins (at Apple) in figuring this out. */ 35 /* */ 36 /* */ 37 /* Apple's `kern' table has some references to tuple indices, but as there */ 38 /* is no indication where these indices are defined, nor how to */ 39 /* interpolate the kerning values (different tuples have different */ 40 /* classes) this issue is ignored. */ 41 /* */ 42 /***************************************************************************/ 43 44 45 #include <ft2build.h> 46 #include FT_INTERNAL_DEBUG_H 47 #include FT_CONFIG_CONFIG_H 48 #include FT_INTERNAL_STREAM_H 49 #include FT_INTERNAL_SFNT_H 50 #include FT_TRUETYPE_IDS_H 51 #include FT_TRUETYPE_TAGS_H 52 #include FT_MULTIPLE_MASTERS_H 53 54 #include "ttdriver.h" 55 #include "ttpload.h" 56 #include "ttgxvar.h" 57 58 #include "tterrors.h" 59 60 61 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT 62 63 64 #define FT_Stream_FTell( stream ) \ 65 ( (stream)->cursor - (stream)->base ) 66 #define FT_Stream_SeekSet( stream, off ) \ 67 ( (stream)->cursor = (stream)->base+(off) ) 68 69 70 /*************************************************************************/ 71 /* */ 72 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 73 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 74 /* messages during execution. */ 75 /* */ 76 #undef FT_COMPONENT 77 #define FT_COMPONENT trace_ttgxvar 78 79 80 /*************************************************************************/ 81 /*************************************************************************/ 82 /***** *****/ 83 /***** Internal Routines *****/ 84 /***** *****/ 85 /*************************************************************************/ 86 /*************************************************************************/ 87 88 89 /*************************************************************************/ 90 /* */ 91 /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'. It */ 92 /* indicates that there is a delta for every point without needing to */ 93 /* enumerate all of them. */ 94 /* */ 95 #define ALL_POINTS (FT_UShort*)( -1 ) 96 97 98 enum 99 { 100 GX_PT_POINTS_ARE_WORDS = 0x80, 101 GX_PT_POINT_RUN_COUNT_MASK = 0x7F 102 }; 103 104 105 /*************************************************************************/ 106 /* */ 107 /* <Function> */ 108 /* ft_var_readpackedpoints */ 109 /* */ 110 /* <Description> */ 111 /* Read a set of points to which the following deltas will apply. */ 112 /* Points are packed with a run length encoding. */ 113 /* */ 114 /* <Input> */ 115 /* stream :: The data stream. */ 116 /* */ 117 /* <Output> */ 118 /* point_cnt :: The number of points read. A zero value means that */ 119 /* all points in the glyph will be affected, without */ 120 /* enumerating them individually. */ 121 /* */ 122 /* <Return> */ 123 /* An array of FT_UShort containing the affected points or the */ 124 /* special value ALL_POINTS. */ 125 /* */ 126 static FT_UShort* ft_var_readpackedpoints(FT_Stream stream,FT_UInt * point_cnt)127 ft_var_readpackedpoints( FT_Stream stream, 128 FT_UInt *point_cnt ) 129 { 130 FT_UShort *points; 131 FT_Int n; 132 FT_Int runcnt; 133 FT_Int i; 134 FT_Int j; 135 FT_Int first; 136 FT_Memory memory = stream->memory; 137 FT_Error error = TT_Err_Ok; 138 139 FT_UNUSED( error ); 140 141 142 *point_cnt = n = FT_GET_BYTE(); 143 if ( n == 0 ) 144 return ALL_POINTS; 145 146 if ( n & GX_PT_POINTS_ARE_WORDS ) 147 n = FT_GET_BYTE() | ( ( n & GX_PT_POINT_RUN_COUNT_MASK ) << 8 ); 148 149 if ( FT_NEW_ARRAY( points, n ) ) 150 return NULL; 151 152 i = 0; 153 while ( i < n ) 154 { 155 runcnt = FT_GET_BYTE(); 156 if ( runcnt & GX_PT_POINTS_ARE_WORDS ) 157 { 158 runcnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK; 159 first = points[i++] = FT_GET_USHORT(); 160 161 /* first point not included in runcount */ 162 for ( j = 0; j < runcnt; ++j ) 163 points[i++] = (FT_UShort)( first += FT_GET_USHORT() ); 164 } 165 else 166 { 167 first = points[i++] = FT_GET_BYTE(); 168 169 for ( j = 0; j < runcnt; ++j ) 170 points[i++] = (FT_UShort)( first += FT_GET_BYTE() ); 171 } 172 } 173 174 return points; 175 } 176 177 178 enum 179 { 180 GX_DT_DELTAS_ARE_ZERO = 0x80, 181 GX_DT_DELTAS_ARE_WORDS = 0x40, 182 GX_DT_DELTA_RUN_COUNT_MASK = 0x3F 183 }; 184 185 186 /*************************************************************************/ 187 /* */ 188 /* <Function> */ 189 /* ft_var_readpackeddeltas */ 190 /* */ 191 /* <Description> */ 192 /* Read a set of deltas. These are packed slightly differently than */ 193 /* points. In particular there is no overall count. */ 194 /* */ 195 /* <Input> */ 196 /* stream :: The data stream. */ 197 /* */ 198 /* delta_cnt :: The number of to be read. */ 199 /* */ 200 /* <Return> */ 201 /* An array of FT_Short containing the deltas for the affected */ 202 /* points. (This only gets the deltas for one dimension. It will */ 203 /* generally be called twice, once for x, once for y. When used in */ 204 /* cvt table, it will only be called once.) */ 205 /* */ 206 static FT_Short* ft_var_readpackeddeltas(FT_Stream stream,FT_Int delta_cnt)207 ft_var_readpackeddeltas( FT_Stream stream, 208 FT_Int delta_cnt ) 209 { 210 FT_Short *deltas; 211 FT_Int runcnt; 212 FT_Int i; 213 FT_Int j; 214 FT_Memory memory = stream->memory; 215 FT_Error error = TT_Err_Ok; 216 217 FT_UNUSED( error ); 218 219 220 if ( FT_NEW_ARRAY( deltas, delta_cnt ) ) 221 return NULL; 222 223 i = 0; 224 while ( i < delta_cnt ) 225 { 226 runcnt = FT_GET_BYTE(); 227 if ( runcnt & GX_DT_DELTAS_ARE_ZERO ) 228 { 229 /* runcnt zeroes get added */ 230 for ( j = 0; 231 j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt; 232 ++j ) 233 deltas[i++] = 0; 234 } 235 else if ( runcnt & GX_DT_DELTAS_ARE_WORDS ) 236 { 237 /* runcnt shorts from the stack */ 238 for ( j = 0; 239 j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt; 240 ++j ) 241 deltas[i++] = FT_GET_SHORT(); 242 } 243 else 244 { 245 /* runcnt signed bytes from the stack */ 246 for ( j = 0; 247 j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt; 248 ++j ) 249 deltas[i++] = FT_GET_CHAR(); 250 } 251 252 if ( j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) ) 253 { 254 /* Bad format */ 255 FT_FREE( deltas ); 256 return NULL; 257 } 258 } 259 260 return deltas; 261 } 262 263 264 /*************************************************************************/ 265 /* */ 266 /* <Function> */ 267 /* ft_var_load_avar */ 268 /* */ 269 /* <Description> */ 270 /* Parse the `avar' table if present. It need not be, so we return */ 271 /* nothing. */ 272 /* */ 273 /* <InOut> */ 274 /* face :: The font face. */ 275 /* */ 276 static void ft_var_load_avar(TT_Face face)277 ft_var_load_avar( TT_Face face ) 278 { 279 FT_Stream stream = FT_FACE_STREAM(face); 280 FT_Memory memory = stream->memory; 281 GX_Blend blend = face->blend; 282 GX_AVarSegment segment; 283 FT_Error error = TT_Err_Ok; 284 FT_ULong version; 285 FT_Long axisCount; 286 FT_Int i, j; 287 FT_ULong table_len; 288 289 FT_UNUSED( error ); 290 291 292 blend->avar_checked = TRUE; 293 if ( (error = face->goto_table( face, TTAG_avar, stream, &table_len )) != 0 ) 294 return; 295 296 if ( FT_FRAME_ENTER( table_len ) ) 297 return; 298 299 version = FT_GET_LONG(); 300 axisCount = FT_GET_LONG(); 301 302 if ( version != 0x00010000L || 303 axisCount != (FT_Long)blend->mmvar->num_axis ) 304 goto Exit; 305 306 if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) ) 307 goto Exit; 308 309 segment = &blend->avar_segment[0]; 310 for ( i = 0; i < axisCount; ++i, ++segment ) 311 { 312 segment->pairCount = FT_GET_USHORT(); 313 if ( FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) ) 314 { 315 /* Failure. Free everything we have done so far. We must do */ 316 /* it right now since loading the `avar' table is optional. */ 317 318 for ( j = i - 1; j >= 0; --j ) 319 FT_FREE( blend->avar_segment[j].correspondence ); 320 321 FT_FREE( blend->avar_segment ); 322 blend->avar_segment = NULL; 323 goto Exit; 324 } 325 326 for ( j = 0; j < segment->pairCount; ++j ) 327 { 328 segment->correspondence[j].fromCoord = 329 FT_GET_SHORT() << 2; /* convert to Fixed */ 330 segment->correspondence[j].toCoord = 331 FT_GET_SHORT()<<2; /* convert to Fixed */ 332 } 333 } 334 335 Exit: 336 FT_FRAME_EXIT(); 337 } 338 339 340 typedef struct GX_GVar_Head_ 341 { 342 FT_Long version; 343 FT_UShort axisCount; 344 FT_UShort globalCoordCount; 345 FT_ULong offsetToCoord; 346 FT_UShort glyphCount; 347 FT_UShort flags; 348 FT_ULong offsetToData; 349 350 } GX_GVar_Head; 351 352 353 /*************************************************************************/ 354 /* */ 355 /* <Function> */ 356 /* ft_var_load_gvar */ 357 /* */ 358 /* <Description> */ 359 /* Parses the `gvar' table if present. If `fvar' is there, `gvar' */ 360 /* had better be there too. */ 361 /* */ 362 /* <InOut> */ 363 /* face :: The font face. */ 364 /* */ 365 /* <Return> */ 366 /* FreeType error code. 0 means success. */ 367 /* */ 368 static FT_Error ft_var_load_gvar(TT_Face face)369 ft_var_load_gvar( TT_Face face ) 370 { 371 FT_Stream stream = FT_FACE_STREAM(face); 372 FT_Memory memory = stream->memory; 373 GX_Blend blend = face->blend; 374 FT_Error error; 375 FT_UInt i, j; 376 FT_ULong table_len; 377 FT_ULong gvar_start; 378 FT_ULong offsetToData; 379 GX_GVar_Head gvar_head; 380 381 static const FT_Frame_Field gvar_fields[] = 382 { 383 384 #undef FT_STRUCTURE 385 #define FT_STRUCTURE GX_GVar_Head 386 387 FT_FRAME_START( 20 ), 388 FT_FRAME_LONG ( version ), 389 FT_FRAME_USHORT( axisCount ), 390 FT_FRAME_USHORT( globalCoordCount ), 391 FT_FRAME_ULONG ( offsetToCoord ), 392 FT_FRAME_USHORT( glyphCount ), 393 FT_FRAME_USHORT( flags ), 394 FT_FRAME_ULONG ( offsetToData ), 395 FT_FRAME_END 396 }; 397 398 if ( (error = face->goto_table( face, TTAG_gvar, stream, &table_len )) != 0 ) 399 goto Exit; 400 401 gvar_start = FT_STREAM_POS( ); 402 if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) ) 403 goto Exit; 404 405 blend->tuplecount = gvar_head.globalCoordCount; 406 blend->gv_glyphcnt = gvar_head.glyphCount; 407 offsetToData = gvar_start + gvar_head.offsetToData; 408 409 if ( gvar_head.version != (FT_Long)0x00010000L || 410 gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis ) 411 { 412 error = TT_Err_Invalid_Table; 413 goto Exit; 414 } 415 416 if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) ) 417 goto Exit; 418 419 if ( gvar_head.flags & 1 ) 420 { 421 /* long offsets (one more offset than glyphs, to mark size of last) */ 422 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) ) 423 goto Exit; 424 425 for ( i = 0; i <= blend->gv_glyphcnt; ++i ) 426 blend->glyphoffsets[i] = offsetToData + FT_GET_LONG(); 427 428 FT_FRAME_EXIT(); 429 } 430 else 431 { 432 /* short offsets (one more offset than glyphs, to mark size of last) */ 433 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) ) 434 goto Exit; 435 436 for ( i = 0; i <= blend->gv_glyphcnt; ++i ) 437 blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2; 438 /* XXX: Undocumented: `*2'! */ 439 440 FT_FRAME_EXIT(); 441 } 442 443 if ( blend->tuplecount != 0 ) 444 { 445 if ( FT_NEW_ARRAY( blend->tuplecoords, 446 gvar_head.axisCount * blend->tuplecount ) ) 447 goto Exit; 448 449 if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) || 450 FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) ) 451 goto Exit; 452 453 for ( i = 0; i < blend->tuplecount; ++i ) 454 for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; ++j ) 455 blend->tuplecoords[i * gvar_head.axisCount + j] = 456 FT_GET_SHORT() << 2; /* convert to FT_Fixed */ 457 458 FT_FRAME_EXIT(); 459 } 460 461 Exit: 462 return error; 463 } 464 465 466 /*************************************************************************/ 467 /* */ 468 /* <Function> */ 469 /* ft_var_apply_tuple */ 470 /* */ 471 /* <Description> */ 472 /* Figure out whether a given tuple (design) applies to the current */ 473 /* blend, and if so, what is the scaling factor. */ 474 /* */ 475 /* <Input> */ 476 /* blend :: The current blend of the font. */ 477 /* */ 478 /* tupleIndex :: A flag saying whether this is an intermediate */ 479 /* tuple or not. */ 480 /* */ 481 /* tuple_coords :: The coordinates of the tuple in normalized axis */ 482 /* units. */ 483 /* */ 484 /* im_start_coords :: The initial coordinates where this tuple starts */ 485 /* to apply (for intermediate coordinates). */ 486 /* */ 487 /* im_end_coords :: The final coordinates after which this tuple no */ 488 /* longer applies (for intermediate coordinates). */ 489 /* */ 490 /* <Return> */ 491 /* An FT_Fixed value containing the scaling factor. */ 492 /* */ 493 static FT_Fixed ft_var_apply_tuple(GX_Blend blend,FT_UShort tupleIndex,FT_Fixed * tuple_coords,FT_Fixed * im_start_coords,FT_Fixed * im_end_coords)494 ft_var_apply_tuple( GX_Blend blend, 495 FT_UShort tupleIndex, 496 FT_Fixed* tuple_coords, 497 FT_Fixed* im_start_coords, 498 FT_Fixed* im_end_coords ) 499 { 500 FT_UInt i; 501 FT_Fixed apply; 502 FT_Fixed temp; 503 504 505 apply = 0x10000L; 506 for ( i = 0; i < blend->num_axis; ++i ) 507 { 508 if ( tuple_coords[i] == 0 ) 509 /* It's not clear why (for intermediate tuples) we don't need */ 510 /* to check against start/end -- the documentation says we don't. */ 511 /* Similarly, it's unclear why we don't need to scale along the */ 512 /* axis. */ 513 continue; 514 515 else if ( blend->normalizedcoords[i] == 0 || 516 ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) || 517 ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) ) 518 { 519 apply = 0; 520 break; 521 } 522 523 else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) ) 524 /* not an intermediate tuple */ 525 apply = FT_MulDiv( apply, 526 blend->normalizedcoords[i] > 0 527 ? blend->normalizedcoords[i] 528 : -blend->normalizedcoords[i], 529 0x10000L ); 530 531 else if ( blend->normalizedcoords[i] <= im_start_coords[i] || 532 blend->normalizedcoords[i] >= im_end_coords[i] ) 533 { 534 apply = 0; 535 break; 536 } 537 538 else if ( blend->normalizedcoords[i] < tuple_coords[i] ) 539 { 540 temp = FT_MulDiv( blend->normalizedcoords[i] - im_start_coords[i], 541 0x10000L, 542 tuple_coords[i] - im_start_coords[i]); 543 apply = FT_MulDiv( apply, temp, 0x10000L ); 544 } 545 546 else 547 { 548 temp = FT_MulDiv( im_end_coords[i] - blend->normalizedcoords[i], 549 0x10000L, 550 im_end_coords[i] - tuple_coords[i] ); 551 apply = FT_MulDiv( apply, temp, 0x10000L ); 552 } 553 } 554 555 return apply; 556 } 557 558 559 /*************************************************************************/ 560 /*************************************************************************/ 561 /***** *****/ 562 /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/ 563 /***** *****/ 564 /*************************************************************************/ 565 /*************************************************************************/ 566 567 568 typedef struct GX_FVar_Head_ 569 { 570 FT_Long version; 571 FT_UShort offsetToData; 572 FT_UShort countSizePairs; 573 FT_UShort axisCount; 574 FT_UShort axisSize; 575 FT_UShort instanceCount; 576 FT_UShort instanceSize; 577 578 } GX_FVar_Head; 579 580 581 typedef struct fvar_axis_ 582 { 583 FT_ULong axisTag; 584 FT_ULong minValue; 585 FT_ULong defaultValue; 586 FT_ULong maxValue; 587 FT_UShort flags; 588 FT_UShort nameID; 589 590 } GX_FVar_Axis; 591 592 593 /*************************************************************************/ 594 /* */ 595 /* <Function> */ 596 /* TT_Get_MM_Var */ 597 /* */ 598 /* <Description> */ 599 /* Check that the font's `fvar' table is valid, parse it, and return */ 600 /* those data. */ 601 /* */ 602 /* <InOut> */ 603 /* face :: The font face. */ 604 /* TT_Get_MM_Var initializes the blend structure. */ 605 /* */ 606 /* <Output> */ 607 /* master :: The `fvar' data (must be freed by caller). */ 608 /* */ 609 /* <Return> */ 610 /* FreeType error code. 0 means success. */ 611 /* */ 612 FT_LOCAL_DEF( FT_Error ) TT_Get_MM_Var(TT_Face face,FT_MM_Var ** master)613 TT_Get_MM_Var( TT_Face face, 614 FT_MM_Var* *master ) 615 { 616 FT_Stream stream = face->root.stream; 617 FT_Memory memory = face->root.memory; 618 FT_ULong table_len; 619 FT_Error error = TT_Err_Ok; 620 FT_ULong fvar_start; 621 FT_Int i, j; 622 FT_MM_Var* mmvar; 623 FT_Fixed* next_coords; 624 FT_String* next_name; 625 FT_Var_Axis* a; 626 FT_Var_Named_Style* ns; 627 GX_FVar_Head fvar_head; 628 629 static const FT_Frame_Field fvar_fields[] = 630 { 631 632 #undef FT_STRUCTURE 633 #define FT_STRUCTURE GX_FVar_Head 634 635 FT_FRAME_START( 16 ), 636 FT_FRAME_LONG ( version ), 637 FT_FRAME_USHORT( offsetToData ), 638 FT_FRAME_USHORT( countSizePairs ), 639 FT_FRAME_USHORT( axisCount ), 640 FT_FRAME_USHORT( axisSize ), 641 FT_FRAME_USHORT( instanceCount ), 642 FT_FRAME_USHORT( instanceSize ), 643 FT_FRAME_END 644 }; 645 646 static const FT_Frame_Field fvaraxis_fields[] = 647 { 648 649 #undef FT_STRUCTURE 650 #define FT_STRUCTURE GX_FVar_Axis 651 652 FT_FRAME_START( 20 ), 653 FT_FRAME_ULONG ( axisTag ), 654 FT_FRAME_ULONG ( minValue ), 655 FT_FRAME_ULONG ( defaultValue ), 656 FT_FRAME_ULONG ( maxValue ), 657 FT_FRAME_USHORT( flags ), 658 FT_FRAME_USHORT( nameID ), 659 FT_FRAME_END 660 }; 661 662 663 if ( face->blend == NULL ) 664 { 665 /* both `fvar' and `gvar' must be present */ 666 if ( (error = face->goto_table( face, TTAG_gvar, 667 stream, &table_len )) != 0 ) 668 goto Exit; 669 670 if ( (error = face->goto_table( face, TTAG_fvar, 671 stream, &table_len )) != 0 ) 672 goto Exit; 673 674 fvar_start = FT_STREAM_POS( ); 675 676 if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) ) 677 goto Exit; 678 679 if ( fvar_head.version != (FT_Long)0x00010000L || 680 fvar_head.countSizePairs != 2 || 681 fvar_head.axisSize != 20 || 682 fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount || 683 fvar_head.offsetToData + fvar_head.axisCount * 20U + 684 fvar_head.instanceCount * fvar_head.instanceSize > table_len ) 685 { 686 error = TT_Err_Invalid_Table; 687 goto Exit; 688 } 689 690 if ( FT_NEW( face->blend ) ) 691 goto Exit; 692 693 /* XXX: TODO - check for overflows */ 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 (FT_UInt)-1; /* 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 ) TT_Set_MM_Blend(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)848 TT_Set_MM_Blend( TT_Face face, 849 FT_UInt num_coords, 850 FT_Fixed* coords ) 851 { 852 FT_Error error = TT_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 = TT_Err_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 = TT_Err_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 ) TT_Set_Var_Design(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)984 TT_Set_Var_Design( TT_Face face, 985 FT_UInt num_coords, 986 FT_Fixed* coords ) 987 { 988 FT_Error error = TT_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 = TT_Err_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 = TT_Err_Invalid_Argument; 1026 goto Exit; 1027 } 1028 1029 if ( coords[i] < a->def ) 1030 { 1031 normalized[i] = -FT_MulDiv( coords[i] - a->def, 1032 0x10000L, 1033 a->minimum - a->def ); 1034 } 1035 else if ( a->maximum == a->def ) 1036 normalized[i] = 0; 1037 else 1038 { 1039 normalized[i] = FT_MulDiv( coords[i] - a->def, 1040 0x10000L, 1041 a->maximum - a->def ); 1042 } 1043 } 1044 1045 if ( !blend->avar_checked ) 1046 ft_var_load_avar( face ); 1047 1048 if ( blend->avar_segment != NULL ) 1049 { 1050 av = blend->avar_segment; 1051 for ( i = 0; i < mmvar->num_axis; ++i, ++av ) 1052 { 1053 for ( j = 1; j < (FT_UInt)av->pairCount; ++j ) 1054 if ( normalized[i] < av->correspondence[j].fromCoord ) 1055 { 1056 normalized[i] = 1057 FT_MulDiv( 1058 FT_MulDiv( 1059 normalized[i] - av->correspondence[j - 1].fromCoord, 1060 0x10000L, 1061 av->correspondence[j].fromCoord - 1062 av->correspondence[j - 1].fromCoord ), 1063 av->correspondence[j].toCoord - 1064 av->correspondence[j - 1].toCoord, 1065 0x10000L ) + 1066 av->correspondence[j - 1].toCoord; 1067 break; 1068 } 1069 } 1070 } 1071 1072 error = TT_Set_MM_Blend( face, num_coords, normalized ); 1073 1074 Exit: 1075 FT_FREE( normalized ); 1076 return error; 1077 } 1078 1079 1080 /*************************************************************************/ 1081 /*************************************************************************/ 1082 /***** *****/ 1083 /***** GX VAR PARSING ROUTINES *****/ 1084 /***** *****/ 1085 /*************************************************************************/ 1086 /*************************************************************************/ 1087 1088 1089 /*************************************************************************/ 1090 /* */ 1091 /* <Function> */ 1092 /* tt_face_vary_cvt */ 1093 /* */ 1094 /* <Description> */ 1095 /* Modify the loaded cvt table according to the `cvar' table and the */ 1096 /* font's blend. */ 1097 /* */ 1098 /* <InOut> */ 1099 /* face :: A handle to the target face object. */ 1100 /* */ 1101 /* <Input> */ 1102 /* stream :: A handle to the input stream. */ 1103 /* */ 1104 /* <Return> */ 1105 /* FreeType error code. 0 means success. */ 1106 /* */ 1107 /* Most errors are ignored. It is perfectly valid not to have a */ 1108 /* `cvar' table even if there is a `gvar' and `fvar' table. */ 1109 /* */ 1110 FT_LOCAL_DEF( FT_Error ) tt_face_vary_cvt(TT_Face face,FT_Stream stream)1111 tt_face_vary_cvt( TT_Face face, 1112 FT_Stream stream ) 1113 { 1114 FT_Error error; 1115 FT_Memory memory = stream->memory; 1116 FT_ULong table_start; 1117 FT_ULong table_len; 1118 FT_UInt tupleCount; 1119 FT_ULong offsetToData; 1120 FT_ULong here; 1121 FT_UInt i, j; 1122 FT_Fixed* tuple_coords = NULL; 1123 FT_Fixed* im_start_coords = NULL; 1124 FT_Fixed* im_end_coords = NULL; 1125 GX_Blend blend = face->blend; 1126 FT_UInt point_count; 1127 FT_UShort* localpoints; 1128 FT_Short* deltas; 1129 1130 1131 FT_TRACE2(( "CVAR " )); 1132 1133 if ( blend == NULL ) 1134 { 1135 FT_TRACE2(( "no blend specified!\n" )); 1136 1137 error = TT_Err_Ok; 1138 goto Exit; 1139 } 1140 1141 if ( face->cvt == NULL ) 1142 { 1143 FT_TRACE2(( "no `cvt ' table!\n" )); 1144 1145 error = TT_Err_Ok; 1146 goto Exit; 1147 } 1148 1149 error = face->goto_table( face, TTAG_cvar, stream, &table_len ); 1150 if ( error ) 1151 { 1152 FT_TRACE2(( "is missing!\n" )); 1153 1154 error = TT_Err_Ok; 1155 goto Exit; 1156 } 1157 1158 if ( FT_FRAME_ENTER( table_len ) ) 1159 { 1160 error = TT_Err_Ok; 1161 goto Exit; 1162 } 1163 1164 table_start = FT_Stream_FTell( stream ); 1165 if ( FT_GET_LONG() != 0x00010000L ) 1166 { 1167 FT_TRACE2(( "bad table version!\n" )); 1168 1169 error = TT_Err_Ok; 1170 goto FExit; 1171 } 1172 1173 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 1174 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 1175 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 1176 goto FExit; 1177 1178 tupleCount = FT_GET_USHORT(); 1179 offsetToData = table_start + FT_GET_USHORT(); 1180 1181 /* The documentation implies there are flags packed into the */ 1182 /* tuplecount, but John Jenkins says that shared points don't apply */ 1183 /* to `cvar', and no other flags are defined. */ 1184 1185 for ( i = 0; i < ( tupleCount & 0xFFF ); ++i ) 1186 { 1187 FT_UInt tupleDataSize; 1188 FT_UInt tupleIndex; 1189 FT_Fixed apply; 1190 1191 1192 tupleDataSize = FT_GET_USHORT(); 1193 tupleIndex = FT_GET_USHORT(); 1194 1195 /* There is no provision here for a global tuple coordinate section, */ 1196 /* so John says. There are no tuple indices, just embedded tuples. */ 1197 1198 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 1199 { 1200 for ( j = 0; j < blend->num_axis; ++j ) 1201 tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from */ 1202 /* short frac to fixed */ 1203 } 1204 else 1205 { 1206 /* skip this tuple; it makes no sense */ 1207 1208 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1209 for ( j = 0; j < 2 * blend->num_axis; ++j ) 1210 (void)FT_GET_SHORT(); 1211 1212 offsetToData += tupleDataSize; 1213 continue; 1214 } 1215 1216 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1217 { 1218 for ( j = 0; j < blend->num_axis; ++j ) 1219 im_start_coords[j] = FT_GET_SHORT() << 2; 1220 for ( j = 0; j < blend->num_axis; ++j ) 1221 im_end_coords[j] = FT_GET_SHORT() << 2; 1222 } 1223 1224 apply = ft_var_apply_tuple( blend, 1225 (FT_UShort)tupleIndex, 1226 tuple_coords, 1227 im_start_coords, 1228 im_end_coords ); 1229 if ( /* tuple isn't active for our blend */ 1230 apply == 0 || 1231 /* global points not allowed, */ 1232 /* if they aren't local, makes no sense */ 1233 !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) ) 1234 { 1235 offsetToData += tupleDataSize; 1236 continue; 1237 } 1238 1239 here = FT_Stream_FTell( stream ); 1240 1241 FT_Stream_SeekSet( stream, offsetToData ); 1242 1243 localpoints = ft_var_readpackedpoints( stream, &point_count ); 1244 deltas = ft_var_readpackeddeltas( stream, 1245 point_count == 0 ? face->cvt_size 1246 : point_count ); 1247 if ( localpoints == NULL || deltas == NULL ) 1248 /* failure, ignore it */; 1249 1250 else if ( localpoints == ALL_POINTS ) 1251 { 1252 /* this means that there are deltas for every entry in cvt */ 1253 for ( j = 0; j < face->cvt_size; ++j ) 1254 face->cvt[j] = (FT_Short)( face->cvt[j] + 1255 FT_MulFix( deltas[j], apply ) ); 1256 } 1257 1258 else 1259 { 1260 for ( j = 0; j < point_count; ++j ) 1261 { 1262 int pindex = localpoints[j]; 1263 1264 face->cvt[pindex] = (FT_Short)( face->cvt[pindex] + 1265 FT_MulFix( deltas[j], apply ) ); 1266 } 1267 } 1268 1269 if ( localpoints != ALL_POINTS ) 1270 FT_FREE( localpoints ); 1271 FT_FREE( deltas ); 1272 1273 offsetToData += tupleDataSize; 1274 1275 FT_Stream_SeekSet( stream, here ); 1276 } 1277 1278 FExit: 1279 FT_FRAME_EXIT(); 1280 1281 Exit: 1282 FT_FREE( tuple_coords ); 1283 FT_FREE( im_start_coords ); 1284 FT_FREE( im_end_coords ); 1285 1286 return error; 1287 } 1288 1289 1290 /*************************************************************************/ 1291 /* */ 1292 /* <Function> */ 1293 /* TT_Vary_Get_Glyph_Deltas */ 1294 /* */ 1295 /* <Description> */ 1296 /* Load the appropriate deltas for the current glyph. */ 1297 /* */ 1298 /* <Input> */ 1299 /* face :: A handle to the target face object. */ 1300 /* */ 1301 /* glyph_index :: The index of the glyph being modified. */ 1302 /* */ 1303 /* n_points :: The number of the points in the glyph, including */ 1304 /* phantom points. */ 1305 /* */ 1306 /* <Output> */ 1307 /* deltas :: The array of points to change. */ 1308 /* */ 1309 /* <Return> */ 1310 /* FreeType error code. 0 means success. */ 1311 /* */ 1312 FT_LOCAL_DEF( FT_Error ) TT_Vary_Get_Glyph_Deltas(TT_Face face,FT_UInt glyph_index,FT_Vector ** deltas,FT_UInt n_points)1313 TT_Vary_Get_Glyph_Deltas( TT_Face face, 1314 FT_UInt glyph_index, 1315 FT_Vector* *deltas, 1316 FT_UInt n_points ) 1317 { 1318 FT_Stream stream = face->root.stream; 1319 FT_Memory memory = stream->memory; 1320 GX_Blend blend = face->blend; 1321 FT_Vector* delta_xy; 1322 1323 FT_Error error; 1324 FT_ULong glyph_start; 1325 FT_UInt tupleCount; 1326 FT_ULong offsetToData; 1327 FT_ULong here; 1328 FT_UInt i, j; 1329 FT_Fixed* tuple_coords = NULL; 1330 FT_Fixed* im_start_coords = NULL; 1331 FT_Fixed* im_end_coords = NULL; 1332 FT_UInt point_count, spoint_count = 0; 1333 FT_UShort* sharedpoints = NULL; 1334 FT_UShort* localpoints = NULL; 1335 FT_UShort* points; 1336 FT_Short *deltas_x, *deltas_y; 1337 1338 1339 if ( !face->doblend || blend == NULL ) 1340 return TT_Err_Invalid_Argument; 1341 1342 /* to be freed by the caller */ 1343 if ( FT_NEW_ARRAY( delta_xy, n_points ) ) 1344 goto Exit; 1345 *deltas = delta_xy; 1346 1347 if ( glyph_index >= blend->gv_glyphcnt || 1348 blend->glyphoffsets[glyph_index] == 1349 blend->glyphoffsets[glyph_index + 1] ) 1350 return TT_Err_Ok; /* no variation data for this glyph */ 1351 1352 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) || 1353 FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] - 1354 blend->glyphoffsets[glyph_index] ) ) 1355 goto Fail1; 1356 1357 glyph_start = FT_Stream_FTell( stream ); 1358 1359 /* each set of glyph variation data is formatted similarly to `cvar' */ 1360 /* (except we get shared points and global tuples) */ 1361 1362 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 1363 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 1364 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 1365 goto Fail2; 1366 1367 tupleCount = FT_GET_USHORT(); 1368 offsetToData = glyph_start + FT_GET_USHORT(); 1369 1370 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 1371 { 1372 here = FT_Stream_FTell( stream ); 1373 1374 FT_Stream_SeekSet( stream, offsetToData ); 1375 1376 sharedpoints = ft_var_readpackedpoints( stream, &spoint_count ); 1377 offsetToData = FT_Stream_FTell( stream ); 1378 1379 FT_Stream_SeekSet( stream, here ); 1380 } 1381 1382 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i ) 1383 { 1384 FT_UInt tupleDataSize; 1385 FT_UInt tupleIndex; 1386 FT_Fixed apply; 1387 1388 1389 tupleDataSize = FT_GET_USHORT(); 1390 tupleIndex = FT_GET_USHORT(); 1391 1392 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 1393 { 1394 for ( j = 0; j < blend->num_axis; ++j ) 1395 tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from */ 1396 /* short frac to fixed */ 1397 } 1398 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 1399 { 1400 error = TT_Err_Invalid_Table; 1401 goto Fail3; 1402 } 1403 else 1404 { 1405 FT_MEM_COPY( 1406 tuple_coords, 1407 &blend->tuplecoords[(tupleIndex & 0xFFF) * blend->num_axis], 1408 blend->num_axis * sizeof ( FT_Fixed ) ); 1409 } 1410 1411 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1412 { 1413 for ( j = 0; j < blend->num_axis; ++j ) 1414 im_start_coords[j] = FT_GET_SHORT() << 2; 1415 for ( j = 0; j < blend->num_axis; ++j ) 1416 im_end_coords[j] = FT_GET_SHORT() << 2; 1417 } 1418 1419 apply = ft_var_apply_tuple( blend, 1420 (FT_UShort)tupleIndex, 1421 tuple_coords, 1422 im_start_coords, 1423 im_end_coords ); 1424 1425 if ( apply == 0 ) /* tuple isn't active for our blend */ 1426 { 1427 offsetToData += tupleDataSize; 1428 continue; 1429 } 1430 1431 here = FT_Stream_FTell( stream ); 1432 1433 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 1434 { 1435 FT_Stream_SeekSet( stream, offsetToData ); 1436 1437 localpoints = ft_var_readpackedpoints( stream, &point_count ); 1438 points = localpoints; 1439 } 1440 else 1441 { 1442 points = sharedpoints; 1443 point_count = spoint_count; 1444 } 1445 1446 deltas_x = ft_var_readpackeddeltas( stream, 1447 point_count == 0 ? n_points 1448 : point_count ); 1449 deltas_y = ft_var_readpackeddeltas( stream, 1450 point_count == 0 ? n_points 1451 : point_count ); 1452 1453 if ( points == NULL || deltas_y == NULL || deltas_x == NULL ) 1454 ; /* failure, ignore it */ 1455 1456 else if ( points == ALL_POINTS ) 1457 { 1458 /* this means that there are deltas for every point in the glyph */ 1459 for ( j = 0; j < n_points; ++j ) 1460 { 1461 delta_xy[j].x += FT_MulFix( deltas_x[j], apply ); 1462 delta_xy[j].y += FT_MulFix( deltas_y[j], apply ); 1463 } 1464 } 1465 1466 else 1467 { 1468 for ( j = 0; j < point_count; ++j ) 1469 { 1470 delta_xy[localpoints[j]].x += FT_MulFix( deltas_x[j], apply ); 1471 delta_xy[localpoints[j]].y += FT_MulFix( deltas_y[j], apply ); 1472 } 1473 } 1474 1475 if ( localpoints != ALL_POINTS ) 1476 FT_FREE( localpoints ); 1477 FT_FREE( deltas_x ); 1478 FT_FREE( deltas_y ); 1479 1480 offsetToData += tupleDataSize; 1481 1482 FT_Stream_SeekSet( stream, here ); 1483 } 1484 1485 Fail3: 1486 FT_FREE( tuple_coords ); 1487 FT_FREE( im_start_coords ); 1488 FT_FREE( im_end_coords ); 1489 1490 Fail2: 1491 FT_FRAME_EXIT(); 1492 1493 Fail1: 1494 if ( error ) 1495 { 1496 FT_FREE( delta_xy ); 1497 *deltas = NULL; 1498 } 1499 1500 Exit: 1501 return error; 1502 } 1503 1504 1505 /*************************************************************************/ 1506 /* */ 1507 /* <Function> */ 1508 /* tt_done_blend */ 1509 /* */ 1510 /* <Description> */ 1511 /* Frees the blend internal data structure. */ 1512 /* */ 1513 FT_LOCAL_DEF( void ) tt_done_blend(FT_Memory memory,GX_Blend blend)1514 tt_done_blend( FT_Memory memory, 1515 GX_Blend blend ) 1516 { 1517 if ( blend != NULL ) 1518 { 1519 FT_UInt i; 1520 1521 1522 FT_FREE( blend->normalizedcoords ); 1523 FT_FREE( blend->mmvar ); 1524 1525 if ( blend->avar_segment != NULL ) 1526 { 1527 for ( i = 0; i < blend->num_axis; ++i ) 1528 FT_FREE( blend->avar_segment[i].correspondence ); 1529 FT_FREE( blend->avar_segment ); 1530 } 1531 1532 FT_FREE( blend->tuplecoords ); 1533 FT_FREE( blend->glyphoffsets ); 1534 FT_FREE( blend ); 1535 } 1536 } 1537 1538 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 1539 1540 1541 /* END */ 1542