| OLD | NEW |
| (Empty) |
| 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 "../../include/ft2build.h" | |
| 47 #include "../../include/freetype/internal/ftdebug.h" | |
| 48 #include "../../include/freetype/config/ftconfig.h" | |
| 49 #include "../../include/freetype/internal/ftstream.h" | |
| 50 #include "../../include/freetype/internal/sfnt.h" | |
| 51 #include "../../include/freetype/tttags.h" | |
| 52 #include "../../include/freetype/ftmm.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 */ | |
| OLD | NEW |