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 |