1 /***************************************************************************/ |
|
2 /* */ |
|
3 /* afhints.c */ |
|
4 /* */ |
|
5 /* Auto-fitter hinting routines (body). */ |
|
6 /* */ |
|
7 /* Copyright 2003-2007, 2009-2011 by */ |
|
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ |
|
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 #include "afhints.h" |
|
20 #include "aferrors.h" |
|
21 #include FT_INTERNAL_CALC_H |
|
22 |
|
23 |
|
24 /* Get new segment for given axis. */ |
|
25 |
|
26 FT_LOCAL_DEF( FT_Error ) |
|
27 af_axis_hints_new_segment( AF_AxisHints axis, |
|
28 FT_Memory memory, |
|
29 AF_Segment *asegment ) |
|
30 { |
|
31 FT_Error error = AF_Err_Ok; |
|
32 AF_Segment segment = NULL; |
|
33 |
|
34 |
|
35 if ( axis->num_segments >= axis->max_segments ) |
|
36 { |
|
37 FT_Int old_max = axis->max_segments; |
|
38 FT_Int new_max = old_max; |
|
39 FT_Int big_max = (FT_Int)( FT_INT_MAX / sizeof ( *segment ) ); |
|
40 |
|
41 |
|
42 if ( old_max >= big_max ) |
|
43 { |
|
44 error = AF_Err_Out_Of_Memory; |
|
45 goto Exit; |
|
46 } |
|
47 |
|
48 new_max += ( new_max >> 2 ) + 4; |
|
49 if ( new_max < old_max || new_max > big_max ) |
|
50 new_max = big_max; |
|
51 |
|
52 if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) ) |
|
53 goto Exit; |
|
54 |
|
55 axis->max_segments = new_max; |
|
56 } |
|
57 |
|
58 segment = axis->segments + axis->num_segments++; |
|
59 |
|
60 Exit: |
|
61 *asegment = segment; |
|
62 return error; |
|
63 } |
|
64 |
|
65 |
|
66 /* Get new edge for given axis, direction, and position. */ |
|
67 |
|
68 FT_LOCAL( FT_Error ) |
|
69 af_axis_hints_new_edge( AF_AxisHints axis, |
|
70 FT_Int fpos, |
|
71 AF_Direction dir, |
|
72 FT_Memory memory, |
|
73 AF_Edge *aedge ) |
|
74 { |
|
75 FT_Error error = AF_Err_Ok; |
|
76 AF_Edge edge = NULL; |
|
77 AF_Edge edges; |
|
78 |
|
79 |
|
80 if ( axis->num_edges >= axis->max_edges ) |
|
81 { |
|
82 FT_Int old_max = axis->max_edges; |
|
83 FT_Int new_max = old_max; |
|
84 FT_Int big_max = (FT_Int)( FT_INT_MAX / sizeof ( *edge ) ); |
|
85 |
|
86 |
|
87 if ( old_max >= big_max ) |
|
88 { |
|
89 error = AF_Err_Out_Of_Memory; |
|
90 goto Exit; |
|
91 } |
|
92 |
|
93 new_max += ( new_max >> 2 ) + 4; |
|
94 if ( new_max < old_max || new_max > big_max ) |
|
95 new_max = big_max; |
|
96 |
|
97 if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) ) |
|
98 goto Exit; |
|
99 |
|
100 axis->max_edges = new_max; |
|
101 } |
|
102 |
|
103 edges = axis->edges; |
|
104 edge = edges + axis->num_edges; |
|
105 |
|
106 while ( edge > edges ) |
|
107 { |
|
108 if ( edge[-1].fpos < fpos ) |
|
109 break; |
|
110 |
|
111 /* we want the edge with same position and minor direction */ |
|
112 /* to appear before those in the major one in the list */ |
|
113 if ( edge[-1].fpos == fpos && dir == axis->major_dir ) |
|
114 break; |
|
115 |
|
116 edge[0] = edge[-1]; |
|
117 edge--; |
|
118 } |
|
119 |
|
120 axis->num_edges++; |
|
121 |
|
122 FT_ZERO( edge ); |
|
123 edge->fpos = (FT_Short)fpos; |
|
124 edge->dir = (FT_Char)dir; |
|
125 |
|
126 Exit: |
|
127 *aedge = edge; |
|
128 return error; |
|
129 } |
|
130 |
|
131 |
|
132 #ifdef FT_DEBUG_AUTOFIT |
|
133 |
|
134 #include FT_CONFIG_STANDARD_LIBRARY_H |
|
135 |
|
136 static const char* |
|
137 af_dir_str( AF_Direction dir ) |
|
138 { |
|
139 const char* result; |
|
140 |
|
141 |
|
142 switch ( dir ) |
|
143 { |
|
144 case AF_DIR_UP: |
|
145 result = "up"; |
|
146 break; |
|
147 case AF_DIR_DOWN: |
|
148 result = "down"; |
|
149 break; |
|
150 case AF_DIR_LEFT: |
|
151 result = "left"; |
|
152 break; |
|
153 case AF_DIR_RIGHT: |
|
154 result = "right"; |
|
155 break; |
|
156 default: |
|
157 result = "none"; |
|
158 } |
|
159 |
|
160 return result; |
|
161 } |
|
162 |
|
163 |
|
164 #define AF_INDEX_NUM( ptr, base ) ( (ptr) ? ( (ptr) - (base) ) : -1 ) |
|
165 |
|
166 |
|
167 #ifdef __cplusplus |
|
168 extern "C" { |
|
169 #endif |
|
170 void |
|
171 af_glyph_hints_dump_points( AF_GlyphHints hints ) |
|
172 { |
|
173 AF_Point points = hints->points; |
|
174 AF_Point limit = points + hints->num_points; |
|
175 AF_Point point; |
|
176 |
|
177 |
|
178 printf( "Table of points:\n" ); |
|
179 printf( " [ index | xorg | yorg | xscale | yscale" |
|
180 " | xfit | yfit | flags ]\n" ); |
|
181 |
|
182 for ( point = points; point < limit; point++ ) |
|
183 { |
|
184 printf( " [ %5d | %5d | %5d | %6.2f | %6.2f" |
|
185 " | %5.2f | %5.2f | %c%c%c%c%c%c ]\n", |
|
186 point - points, |
|
187 point->fx, |
|
188 point->fy, |
|
189 point->ox / 64.0, |
|
190 point->oy / 64.0, |
|
191 point->x / 64.0, |
|
192 point->y / 64.0, |
|
193 ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) ? 'w' : ' ', |
|
194 ( point->flags & AF_FLAG_INFLECTION ) ? 'i' : ' ', |
|
195 ( point->flags & AF_FLAG_EXTREMA_X ) ? '<' : ' ', |
|
196 ( point->flags & AF_FLAG_EXTREMA_Y ) ? 'v' : ' ', |
|
197 ( point->flags & AF_FLAG_ROUND_X ) ? '(' : ' ', |
|
198 ( point->flags & AF_FLAG_ROUND_Y ) ? 'u' : ' '); |
|
199 } |
|
200 printf( "\n" ); |
|
201 } |
|
202 #ifdef __cplusplus |
|
203 } |
|
204 #endif |
|
205 |
|
206 |
|
207 static const char* |
|
208 af_edge_flags_to_string( AF_Edge_Flags flags ) |
|
209 { |
|
210 static char temp[32]; |
|
211 int pos = 0; |
|
212 |
|
213 |
|
214 if ( flags & AF_EDGE_ROUND ) |
|
215 { |
|
216 ft_memcpy( temp + pos, "round", 5 ); |
|
217 pos += 5; |
|
218 } |
|
219 if ( flags & AF_EDGE_SERIF ) |
|
220 { |
|
221 if ( pos > 0 ) |
|
222 temp[pos++] = ' '; |
|
223 ft_memcpy( temp + pos, "serif", 5 ); |
|
224 pos += 5; |
|
225 } |
|
226 if ( pos == 0 ) |
|
227 return "normal"; |
|
228 |
|
229 temp[pos] = 0; |
|
230 |
|
231 return temp; |
|
232 } |
|
233 |
|
234 |
|
235 /* Dump the array of linked segments. */ |
|
236 |
|
237 #ifdef __cplusplus |
|
238 extern "C" { |
|
239 #endif |
|
240 void |
|
241 af_glyph_hints_dump_segments( AF_GlyphHints hints ) |
|
242 { |
|
243 FT_Int dimension; |
|
244 |
|
245 |
|
246 for ( dimension = 1; dimension >= 0; dimension-- ) |
|
247 { |
|
248 AF_AxisHints axis = &hints->axis[dimension]; |
|
249 AF_Segment segments = axis->segments; |
|
250 AF_Segment limit = segments + axis->num_segments; |
|
251 AF_Segment seg; |
|
252 |
|
253 |
|
254 printf ( "Table of %s segments:\n", |
|
255 dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" ); |
|
256 printf ( " [ index | pos | dir | link | serif |" |
|
257 " height | extra | flags ]\n" ); |
|
258 |
|
259 for ( seg = segments; seg < limit; seg++ ) |
|
260 { |
|
261 printf ( " [ %5d | %5.2g | %5s | %4d | %5d | %6d | %5d | %11s ]\n", |
|
262 seg - segments, |
|
263 dimension == AF_DIMENSION_HORZ ? (int)seg->first->ox / 64.0 |
|
264 : (int)seg->first->oy / 64.0, |
|
265 af_dir_str( (AF_Direction)seg->dir ), |
|
266 AF_INDEX_NUM( seg->link, segments ), |
|
267 AF_INDEX_NUM( seg->serif, segments ), |
|
268 seg->height, |
|
269 seg->height - ( seg->max_coord - seg->min_coord ), |
|
270 af_edge_flags_to_string( seg->flags ) ); |
|
271 } |
|
272 printf( "\n" ); |
|
273 } |
|
274 } |
|
275 #ifdef __cplusplus |
|
276 } |
|
277 #endif |
|
278 |
|
279 |
|
280 /* Dump the array of linked edges. */ |
|
281 |
|
282 #ifdef __cplusplus |
|
283 extern "C" { |
|
284 #endif |
|
285 void |
|
286 af_glyph_hints_dump_edges( AF_GlyphHints hints ) |
|
287 { |
|
288 FT_Int dimension; |
|
289 |
|
290 |
|
291 for ( dimension = 1; dimension >= 0; dimension-- ) |
|
292 { |
|
293 AF_AxisHints axis = &hints->axis[dimension]; |
|
294 AF_Edge edges = axis->edges; |
|
295 AF_Edge limit = edges + axis->num_edges; |
|
296 AF_Edge edge; |
|
297 |
|
298 |
|
299 /* |
|
300 * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges |
|
301 * since they have a constant X coordinate. |
|
302 */ |
|
303 printf ( "Table of %s edges:\n", |
|
304 dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" ); |
|
305 printf ( " [ index | pos | dir | link |" |
|
306 " serif | blue | opos | pos | flags ]\n" ); |
|
307 |
|
308 for ( edge = edges; edge < limit; edge++ ) |
|
309 { |
|
310 printf ( " [ %5d | %5.2g | %5s | %4d |" |
|
311 " %5d | %c | %5.2f | %5.2f | %11s ]\n", |
|
312 edge - edges, |
|
313 (int)edge->opos / 64.0, |
|
314 af_dir_str( (AF_Direction)edge->dir ), |
|
315 AF_INDEX_NUM( edge->link, edges ), |
|
316 AF_INDEX_NUM( edge->serif, edges ), |
|
317 edge->blue_edge ? 'y' : 'n', |
|
318 edge->opos / 64.0, |
|
319 edge->pos / 64.0, |
|
320 af_edge_flags_to_string( edge->flags ) ); |
|
321 } |
|
322 printf( "\n" ); |
|
323 } |
|
324 } |
|
325 #ifdef __cplusplus |
|
326 } |
|
327 #endif |
|
328 |
|
329 #else /* !FT_DEBUG_AUTOFIT */ |
|
330 |
|
331 /* these empty stubs are only used to link the `ftgrid' test program */ |
|
332 /* if debugging is disabled */ |
|
333 |
|
334 #ifdef __cplusplus |
|
335 extern "C" { |
|
336 #endif |
|
337 |
|
338 void |
|
339 af_glyph_hints_dump_points( AF_GlyphHints hints ) |
|
340 { |
|
341 FT_UNUSED( hints ); |
|
342 } |
|
343 |
|
344 |
|
345 void |
|
346 af_glyph_hints_dump_segments( AF_GlyphHints hints ) |
|
347 { |
|
348 FT_UNUSED( hints ); |
|
349 } |
|
350 |
|
351 |
|
352 void |
|
353 af_glyph_hints_dump_edges( AF_GlyphHints hints ) |
|
354 { |
|
355 FT_UNUSED( hints ); |
|
356 } |
|
357 |
|
358 #ifdef __cplusplus |
|
359 } |
|
360 #endif |
|
361 |
|
362 #endif /* !FT_DEBUG_AUTOFIT */ |
|
363 |
|
364 |
|
365 /* Compute the direction value of a given vector. */ |
|
366 |
|
367 FT_LOCAL_DEF( AF_Direction ) |
|
368 af_direction_compute( FT_Pos dx, |
|
369 FT_Pos dy ) |
|
370 { |
|
371 FT_Pos ll, ss; /* long and short arm lengths */ |
|
372 AF_Direction dir; /* candidate direction */ |
|
373 |
|
374 |
|
375 if ( dy >= dx ) |
|
376 { |
|
377 if ( dy >= -dx ) |
|
378 { |
|
379 dir = AF_DIR_UP; |
|
380 ll = dy; |
|
381 ss = dx; |
|
382 } |
|
383 else |
|
384 { |
|
385 dir = AF_DIR_LEFT; |
|
386 ll = -dx; |
|
387 ss = dy; |
|
388 } |
|
389 } |
|
390 else /* dy < dx */ |
|
391 { |
|
392 if ( dy >= -dx ) |
|
393 { |
|
394 dir = AF_DIR_RIGHT; |
|
395 ll = dx; |
|
396 ss = dy; |
|
397 } |
|
398 else |
|
399 { |
|
400 dir = AF_DIR_DOWN; |
|
401 ll = dy; |
|
402 ss = dx; |
|
403 } |
|
404 } |
|
405 |
|
406 /* return no direction if arm lengths differ too much */ |
|
407 /* (value 14 is heuristic) */ |
|
408 ss *= 14; |
|
409 if ( FT_ABS( ll ) <= FT_ABS( ss ) ) |
|
410 dir = AF_DIR_NONE; |
|
411 |
|
412 return dir; |
|
413 } |
|
414 |
|
415 |
|
416 FT_LOCAL_DEF( void ) |
|
417 af_glyph_hints_init( AF_GlyphHints hints, |
|
418 FT_Memory memory ) |
|
419 { |
|
420 FT_ZERO( hints ); |
|
421 hints->memory = memory; |
|
422 } |
|
423 |
|
424 |
|
425 FT_LOCAL_DEF( void ) |
|
426 af_glyph_hints_done( AF_GlyphHints hints ) |
|
427 { |
|
428 if ( hints && hints->memory ) |
|
429 { |
|
430 FT_Memory memory = hints->memory; |
|
431 int dim; |
|
432 |
|
433 |
|
434 /* |
|
435 * note that we don't need to free the segment and edge |
|
436 * buffers since they are really within the hints->points array |
|
437 */ |
|
438 for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) |
|
439 { |
|
440 AF_AxisHints axis = &hints->axis[dim]; |
|
441 |
|
442 |
|
443 axis->num_segments = 0; |
|
444 axis->max_segments = 0; |
|
445 FT_FREE( axis->segments ); |
|
446 |
|
447 axis->num_edges = 0; |
|
448 axis->max_edges = 0; |
|
449 FT_FREE( axis->edges ); |
|
450 } |
|
451 |
|
452 FT_FREE( hints->contours ); |
|
453 hints->max_contours = 0; |
|
454 hints->num_contours = 0; |
|
455 |
|
456 FT_FREE( hints->points ); |
|
457 hints->num_points = 0; |
|
458 hints->max_points = 0; |
|
459 |
|
460 hints->memory = NULL; |
|
461 } |
|
462 } |
|
463 |
|
464 |
|
465 /* Reset metrics. */ |
|
466 |
|
467 FT_LOCAL_DEF( void ) |
|
468 af_glyph_hints_rescale( AF_GlyphHints hints, |
|
469 AF_ScriptMetrics metrics ) |
|
470 { |
|
471 hints->metrics = metrics; |
|
472 hints->scaler_flags = metrics->scaler.flags; |
|
473 } |
|
474 |
|
475 |
|
476 /* Recompute all AF_Point in AF_GlyphHints from the definitions */ |
|
477 /* in a source outline. */ |
|
478 |
|
479 FT_LOCAL_DEF( FT_Error ) |
|
480 af_glyph_hints_reload( AF_GlyphHints hints, |
|
481 FT_Outline* outline ) |
|
482 { |
|
483 FT_Error error = AF_Err_Ok; |
|
484 AF_Point points; |
|
485 FT_UInt old_max, new_max; |
|
486 FT_Fixed x_scale = hints->x_scale; |
|
487 FT_Fixed y_scale = hints->y_scale; |
|
488 FT_Pos x_delta = hints->x_delta; |
|
489 FT_Pos y_delta = hints->y_delta; |
|
490 FT_Memory memory = hints->memory; |
|
491 |
|
492 |
|
493 hints->num_points = 0; |
|
494 hints->num_contours = 0; |
|
495 |
|
496 hints->axis[0].num_segments = 0; |
|
497 hints->axis[0].num_edges = 0; |
|
498 hints->axis[1].num_segments = 0; |
|
499 hints->axis[1].num_edges = 0; |
|
500 |
|
501 /* first of all, reallocate the contours array if necessary */ |
|
502 new_max = (FT_UInt)outline->n_contours; |
|
503 old_max = hints->max_contours; |
|
504 if ( new_max > old_max ) |
|
505 { |
|
506 new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */ |
|
507 |
|
508 if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) ) |
|
509 goto Exit; |
|
510 |
|
511 hints->max_contours = new_max; |
|
512 } |
|
513 |
|
514 /* |
|
515 * then reallocate the points arrays if necessary -- |
|
516 * note that we reserve two additional point positions, used to |
|
517 * hint metrics appropriately |
|
518 */ |
|
519 new_max = (FT_UInt)( outline->n_points + 2 ); |
|
520 old_max = hints->max_points; |
|
521 if ( new_max > old_max ) |
|
522 { |
|
523 new_max = ( new_max + 2 + 7 ) & ~7; /* round up to a multiple of 8 */ |
|
524 |
|
525 if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) ) |
|
526 goto Exit; |
|
527 |
|
528 hints->max_points = new_max; |
|
529 } |
|
530 |
|
531 hints->num_points = outline->n_points; |
|
532 hints->num_contours = outline->n_contours; |
|
533 |
|
534 /* We can't rely on the value of `FT_Outline.flags' to know the fill */ |
|
535 /* direction used for a glyph, given that some fonts are broken (e.g., */ |
|
536 /* the Arphic ones). We thus recompute it each time we need to. */ |
|
537 /* */ |
|
538 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP; |
|
539 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT; |
|
540 |
|
541 if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT ) |
|
542 { |
|
543 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN; |
|
544 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT; |
|
545 } |
|
546 |
|
547 hints->x_scale = x_scale; |
|
548 hints->y_scale = y_scale; |
|
549 hints->x_delta = x_delta; |
|
550 hints->y_delta = y_delta; |
|
551 |
|
552 hints->xmin_delta = 0; |
|
553 hints->xmax_delta = 0; |
|
554 |
|
555 points = hints->points; |
|
556 if ( hints->num_points == 0 ) |
|
557 goto Exit; |
|
558 |
|
559 { |
|
560 AF_Point point; |
|
561 AF_Point point_limit = points + hints->num_points; |
|
562 |
|
563 |
|
564 /* compute coordinates & Bezier flags, next and prev */ |
|
565 { |
|
566 FT_Vector* vec = outline->points; |
|
567 char* tag = outline->tags; |
|
568 AF_Point end = points + outline->contours[0]; |
|
569 AF_Point prev = end; |
|
570 FT_Int contour_index = 0; |
|
571 |
|
572 |
|
573 for ( point = points; point < point_limit; point++, vec++, tag++ ) |
|
574 { |
|
575 point->fx = (FT_Short)vec->x; |
|
576 point->fy = (FT_Short)vec->y; |
|
577 point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta; |
|
578 point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta; |
|
579 |
|
580 switch ( FT_CURVE_TAG( *tag ) ) |
|
581 { |
|
582 case FT_CURVE_TAG_CONIC: |
|
583 point->flags = AF_FLAG_CONIC; |
|
584 break; |
|
585 case FT_CURVE_TAG_CUBIC: |
|
586 point->flags = AF_FLAG_CUBIC; |
|
587 break; |
|
588 default: |
|
589 point->flags = AF_FLAG_NONE; |
|
590 } |
|
591 |
|
592 point->prev = prev; |
|
593 prev->next = point; |
|
594 prev = point; |
|
595 |
|
596 if ( point == end ) |
|
597 { |
|
598 if ( ++contour_index < outline->n_contours ) |
|
599 { |
|
600 end = points + outline->contours[contour_index]; |
|
601 prev = end; |
|
602 } |
|
603 } |
|
604 } |
|
605 } |
|
606 |
|
607 /* set up the contours array */ |
|
608 { |
|
609 AF_Point* contour = hints->contours; |
|
610 AF_Point* contour_limit = contour + hints->num_contours; |
|
611 short* end = outline->contours; |
|
612 short idx = 0; |
|
613 |
|
614 |
|
615 for ( ; contour < contour_limit; contour++, end++ ) |
|
616 { |
|
617 contour[0] = points + idx; |
|
618 idx = (short)( end[0] + 1 ); |
|
619 } |
|
620 } |
|
621 |
|
622 /* compute directions of in & out vectors */ |
|
623 { |
|
624 AF_Point first = points; |
|
625 AF_Point prev = NULL; |
|
626 FT_Pos in_x = 0; |
|
627 FT_Pos in_y = 0; |
|
628 AF_Direction in_dir = AF_DIR_NONE; |
|
629 |
|
630 |
|
631 for ( point = points; point < point_limit; point++ ) |
|
632 { |
|
633 AF_Point next; |
|
634 FT_Pos out_x, out_y; |
|
635 |
|
636 |
|
637 if ( point == first ) |
|
638 { |
|
639 prev = first->prev; |
|
640 in_x = first->fx - prev->fx; |
|
641 in_y = first->fy - prev->fy; |
|
642 in_dir = af_direction_compute( in_x, in_y ); |
|
643 first = prev + 1; |
|
644 } |
|
645 |
|
646 point->in_dir = (FT_Char)in_dir; |
|
647 |
|
648 next = point->next; |
|
649 out_x = next->fx - point->fx; |
|
650 out_y = next->fy - point->fy; |
|
651 |
|
652 in_dir = af_direction_compute( out_x, out_y ); |
|
653 point->out_dir = (FT_Char)in_dir; |
|
654 |
|
655 /* check for weak points */ |
|
656 |
|
657 if ( point->flags & ( AF_FLAG_CONIC | AF_FLAG_CUBIC ) ) |
|
658 { |
|
659 Is_Weak_Point: |
|
660 point->flags |= AF_FLAG_WEAK_INTERPOLATION; |
|
661 } |
|
662 else if ( point->out_dir == point->in_dir ) |
|
663 { |
|
664 if ( point->out_dir != AF_DIR_NONE ) |
|
665 goto Is_Weak_Point; |
|
666 |
|
667 if ( ft_corner_is_flat( in_x, in_y, out_x, out_y ) ) |
|
668 goto Is_Weak_Point; |
|
669 } |
|
670 else if ( point->in_dir == -point->out_dir ) |
|
671 goto Is_Weak_Point; |
|
672 |
|
673 in_x = out_x; |
|
674 in_y = out_y; |
|
675 prev = point; |
|
676 } |
|
677 } |
|
678 } |
|
679 |
|
680 Exit: |
|
681 return error; |
|
682 } |
|
683 |
|
684 |
|
685 /* Store the hinted outline in an FT_Outline structure. */ |
|
686 |
|
687 FT_LOCAL_DEF( void ) |
|
688 af_glyph_hints_save( AF_GlyphHints hints, |
|
689 FT_Outline* outline ) |
|
690 { |
|
691 AF_Point point = hints->points; |
|
692 AF_Point limit = point + hints->num_points; |
|
693 FT_Vector* vec = outline->points; |
|
694 char* tag = outline->tags; |
|
695 |
|
696 |
|
697 for ( ; point < limit; point++, vec++, tag++ ) |
|
698 { |
|
699 vec->x = point->x; |
|
700 vec->y = point->y; |
|
701 |
|
702 if ( point->flags & AF_FLAG_CONIC ) |
|
703 tag[0] = FT_CURVE_TAG_CONIC; |
|
704 else if ( point->flags & AF_FLAG_CUBIC ) |
|
705 tag[0] = FT_CURVE_TAG_CUBIC; |
|
706 else |
|
707 tag[0] = FT_CURVE_TAG_ON; |
|
708 } |
|
709 } |
|
710 |
|
711 |
|
712 /**************************************************************** |
|
713 * |
|
714 * EDGE POINT GRID-FITTING |
|
715 * |
|
716 ****************************************************************/ |
|
717 |
|
718 |
|
719 /* Align all points of an edge to the same coordinate value, */ |
|
720 /* either horizontally or vertically. */ |
|
721 |
|
722 FT_LOCAL_DEF( void ) |
|
723 af_glyph_hints_align_edge_points( AF_GlyphHints hints, |
|
724 AF_Dimension dim ) |
|
725 { |
|
726 AF_AxisHints axis = & hints->axis[dim]; |
|
727 AF_Segment segments = axis->segments; |
|
728 AF_Segment segment_limit = segments + axis->num_segments; |
|
729 AF_Segment seg; |
|
730 |
|
731 |
|
732 if ( dim == AF_DIMENSION_HORZ ) |
|
733 { |
|
734 for ( seg = segments; seg < segment_limit; seg++ ) |
|
735 { |
|
736 AF_Edge edge = seg->edge; |
|
737 AF_Point point, first, last; |
|
738 |
|
739 |
|
740 if ( edge == NULL ) |
|
741 continue; |
|
742 |
|
743 first = seg->first; |
|
744 last = seg->last; |
|
745 point = first; |
|
746 for (;;) |
|
747 { |
|
748 point->x = edge->pos; |
|
749 point->flags |= AF_FLAG_TOUCH_X; |
|
750 |
|
751 if ( point == last ) |
|
752 break; |
|
753 |
|
754 point = point->next; |
|
755 } |
|
756 } |
|
757 } |
|
758 else |
|
759 { |
|
760 for ( seg = segments; seg < segment_limit; seg++ ) |
|
761 { |
|
762 AF_Edge edge = seg->edge; |
|
763 AF_Point point, first, last; |
|
764 |
|
765 |
|
766 if ( edge == NULL ) |
|
767 continue; |
|
768 |
|
769 first = seg->first; |
|
770 last = seg->last; |
|
771 point = first; |
|
772 for (;;) |
|
773 { |
|
774 point->y = edge->pos; |
|
775 point->flags |= AF_FLAG_TOUCH_Y; |
|
776 |
|
777 if ( point == last ) |
|
778 break; |
|
779 |
|
780 point = point->next; |
|
781 } |
|
782 } |
|
783 } |
|
784 } |
|
785 |
|
786 |
|
787 /**************************************************************** |
|
788 * |
|
789 * STRONG POINT INTERPOLATION |
|
790 * |
|
791 ****************************************************************/ |
|
792 |
|
793 |
|
794 /* Hint the strong points -- this is equivalent to the TrueType `IP' */ |
|
795 /* hinting instruction. */ |
|
796 |
|
797 FT_LOCAL_DEF( void ) |
|
798 af_glyph_hints_align_strong_points( AF_GlyphHints hints, |
|
799 AF_Dimension dim ) |
|
800 { |
|
801 AF_Point points = hints->points; |
|
802 AF_Point point_limit = points + hints->num_points; |
|
803 AF_AxisHints axis = &hints->axis[dim]; |
|
804 AF_Edge edges = axis->edges; |
|
805 AF_Edge edge_limit = edges + axis->num_edges; |
|
806 AF_Flags touch_flag; |
|
807 |
|
808 |
|
809 if ( dim == AF_DIMENSION_HORZ ) |
|
810 touch_flag = AF_FLAG_TOUCH_X; |
|
811 else |
|
812 touch_flag = AF_FLAG_TOUCH_Y; |
|
813 |
|
814 if ( edges < edge_limit ) |
|
815 { |
|
816 AF_Point point; |
|
817 AF_Edge edge; |
|
818 |
|
819 |
|
820 for ( point = points; point < point_limit; point++ ) |
|
821 { |
|
822 FT_Pos u, ou, fu; /* point position */ |
|
823 FT_Pos delta; |
|
824 |
|
825 |
|
826 if ( point->flags & touch_flag ) |
|
827 continue; |
|
828 |
|
829 /* if this point is candidate to weak interpolation, we */ |
|
830 /* interpolate it after all strong points have been processed */ |
|
831 |
|
832 if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) && |
|
833 !( point->flags & AF_FLAG_INFLECTION ) ) |
|
834 continue; |
|
835 |
|
836 if ( dim == AF_DIMENSION_VERT ) |
|
837 { |
|
838 u = point->fy; |
|
839 ou = point->oy; |
|
840 } |
|
841 else |
|
842 { |
|
843 u = point->fx; |
|
844 ou = point->ox; |
|
845 } |
|
846 |
|
847 fu = u; |
|
848 |
|
849 /* is the point before the first edge? */ |
|
850 edge = edges; |
|
851 delta = edge->fpos - u; |
|
852 if ( delta >= 0 ) |
|
853 { |
|
854 u = edge->pos - ( edge->opos - ou ); |
|
855 goto Store_Point; |
|
856 } |
|
857 |
|
858 /* is the point after the last edge? */ |
|
859 edge = edge_limit - 1; |
|
860 delta = u - edge->fpos; |
|
861 if ( delta >= 0 ) |
|
862 { |
|
863 u = edge->pos + ( ou - edge->opos ); |
|
864 goto Store_Point; |
|
865 } |
|
866 |
|
867 { |
|
868 FT_PtrDist min, max, mid; |
|
869 FT_Pos fpos; |
|
870 |
|
871 |
|
872 /* find enclosing edges */ |
|
873 min = 0; |
|
874 max = edge_limit - edges; |
|
875 |
|
876 #if 1 |
|
877 /* for a small number of edges, a linear search is better */ |
|
878 if ( max <= 8 ) |
|
879 { |
|
880 FT_PtrDist nn; |
|
881 |
|
882 |
|
883 for ( nn = 0; nn < max; nn++ ) |
|
884 if ( edges[nn].fpos >= u ) |
|
885 break; |
|
886 |
|
887 if ( edges[nn].fpos == u ) |
|
888 { |
|
889 u = edges[nn].pos; |
|
890 goto Store_Point; |
|
891 } |
|
892 min = nn; |
|
893 } |
|
894 else |
|
895 #endif |
|
896 while ( min < max ) |
|
897 { |
|
898 mid = ( max + min ) >> 1; |
|
899 edge = edges + mid; |
|
900 fpos = edge->fpos; |
|
901 |
|
902 if ( u < fpos ) |
|
903 max = mid; |
|
904 else if ( u > fpos ) |
|
905 min = mid + 1; |
|
906 else |
|
907 { |
|
908 /* we are on the edge */ |
|
909 u = edge->pos; |
|
910 goto Store_Point; |
|
911 } |
|
912 } |
|
913 |
|
914 /* point is not on an edge */ |
|
915 { |
|
916 AF_Edge before = edges + min - 1; |
|
917 AF_Edge after = edges + min + 0; |
|
918 |
|
919 |
|
920 /* assert( before && after && before != after ) */ |
|
921 if ( before->scale == 0 ) |
|
922 before->scale = FT_DivFix( after->pos - before->pos, |
|
923 after->fpos - before->fpos ); |
|
924 |
|
925 u = before->pos + FT_MulFix( fu - before->fpos, |
|
926 before->scale ); |
|
927 } |
|
928 } |
|
929 |
|
930 Store_Point: |
|
931 /* save the point position */ |
|
932 if ( dim == AF_DIMENSION_HORZ ) |
|
933 point->x = u; |
|
934 else |
|
935 point->y = u; |
|
936 |
|
937 point->flags |= touch_flag; |
|
938 } |
|
939 } |
|
940 } |
|
941 |
|
942 |
|
943 /**************************************************************** |
|
944 * |
|
945 * WEAK POINT INTERPOLATION |
|
946 * |
|
947 ****************************************************************/ |
|
948 |
|
949 |
|
950 /* Shift the original coordinates of all points between `p1' and */ |
|
951 /* `p2' to get hinted coordinates, using the same difference as */ |
|
952 /* given by `ref'. */ |
|
953 |
|
954 static void |
|
955 af_iup_shift( AF_Point p1, |
|
956 AF_Point p2, |
|
957 AF_Point ref ) |
|
958 { |
|
959 AF_Point p; |
|
960 FT_Pos delta = ref->u - ref->v; |
|
961 |
|
962 |
|
963 if ( delta == 0 ) |
|
964 return; |
|
965 |
|
966 for ( p = p1; p < ref; p++ ) |
|
967 p->u = p->v + delta; |
|
968 |
|
969 for ( p = ref + 1; p <= p2; p++ ) |
|
970 p->u = p->v + delta; |
|
971 } |
|
972 |
|
973 |
|
974 /* Interpolate the original coordinates of all points between `p1' and */ |
|
975 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */ |
|
976 /* reference points. The `u' and `v' members are the current and */ |
|
977 /* original coordinate values, respectively. */ |
|
978 /* */ |
|
979 /* Details can be found in the TrueType bytecode specification. */ |
|
980 |
|
981 static void |
|
982 af_iup_interp( AF_Point p1, |
|
983 AF_Point p2, |
|
984 AF_Point ref1, |
|
985 AF_Point ref2 ) |
|
986 { |
|
987 AF_Point p; |
|
988 FT_Pos u; |
|
989 FT_Pos v1 = ref1->v; |
|
990 FT_Pos v2 = ref2->v; |
|
991 FT_Pos d1 = ref1->u - v1; |
|
992 FT_Pos d2 = ref2->u - v2; |
|
993 |
|
994 |
|
995 if ( p1 > p2 ) |
|
996 return; |
|
997 |
|
998 if ( v1 == v2 ) |
|
999 { |
|
1000 for ( p = p1; p <= p2; p++ ) |
|
1001 { |
|
1002 u = p->v; |
|
1003 |
|
1004 if ( u <= v1 ) |
|
1005 u += d1; |
|
1006 else |
|
1007 u += d2; |
|
1008 |
|
1009 p->u = u; |
|
1010 } |
|
1011 return; |
|
1012 } |
|
1013 |
|
1014 if ( v1 < v2 ) |
|
1015 { |
|
1016 for ( p = p1; p <= p2; p++ ) |
|
1017 { |
|
1018 u = p->v; |
|
1019 |
|
1020 if ( u <= v1 ) |
|
1021 u += d1; |
|
1022 else if ( u >= v2 ) |
|
1023 u += d2; |
|
1024 else |
|
1025 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 ); |
|
1026 |
|
1027 p->u = u; |
|
1028 } |
|
1029 } |
|
1030 else |
|
1031 { |
|
1032 for ( p = p1; p <= p2; p++ ) |
|
1033 { |
|
1034 u = p->v; |
|
1035 |
|
1036 if ( u <= v2 ) |
|
1037 u += d2; |
|
1038 else if ( u >= v1 ) |
|
1039 u += d1; |
|
1040 else |
|
1041 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 ); |
|
1042 |
|
1043 p->u = u; |
|
1044 } |
|
1045 } |
|
1046 } |
|
1047 |
|
1048 |
|
1049 /* Hint the weak points -- this is equivalent to the TrueType `IUP' */ |
|
1050 /* hinting instruction. */ |
|
1051 |
|
1052 FT_LOCAL_DEF( void ) |
|
1053 af_glyph_hints_align_weak_points( AF_GlyphHints hints, |
|
1054 AF_Dimension dim ) |
|
1055 { |
|
1056 AF_Point points = hints->points; |
|
1057 AF_Point point_limit = points + hints->num_points; |
|
1058 AF_Point* contour = hints->contours; |
|
1059 AF_Point* contour_limit = contour + hints->num_contours; |
|
1060 AF_Flags touch_flag; |
|
1061 AF_Point point; |
|
1062 AF_Point end_point; |
|
1063 AF_Point first_point; |
|
1064 |
|
1065 |
|
1066 /* PASS 1: Move segment points to edge positions */ |
|
1067 |
|
1068 if ( dim == AF_DIMENSION_HORZ ) |
|
1069 { |
|
1070 touch_flag = AF_FLAG_TOUCH_X; |
|
1071 |
|
1072 for ( point = points; point < point_limit; point++ ) |
|
1073 { |
|
1074 point->u = point->x; |
|
1075 point->v = point->ox; |
|
1076 } |
|
1077 } |
|
1078 else |
|
1079 { |
|
1080 touch_flag = AF_FLAG_TOUCH_Y; |
|
1081 |
|
1082 for ( point = points; point < point_limit; point++ ) |
|
1083 { |
|
1084 point->u = point->y; |
|
1085 point->v = point->oy; |
|
1086 } |
|
1087 } |
|
1088 |
|
1089 point = points; |
|
1090 |
|
1091 for ( ; contour < contour_limit; contour++ ) |
|
1092 { |
|
1093 AF_Point first_touched, last_touched; |
|
1094 |
|
1095 |
|
1096 point = *contour; |
|
1097 end_point = point->prev; |
|
1098 first_point = point; |
|
1099 |
|
1100 /* find first touched point */ |
|
1101 for (;;) |
|
1102 { |
|
1103 if ( point > end_point ) /* no touched point in contour */ |
|
1104 goto NextContour; |
|
1105 |
|
1106 if ( point->flags & touch_flag ) |
|
1107 break; |
|
1108 |
|
1109 point++; |
|
1110 } |
|
1111 |
|
1112 first_touched = point; |
|
1113 last_touched = point; |
|
1114 |
|
1115 for (;;) |
|
1116 { |
|
1117 FT_ASSERT( point <= end_point && |
|
1118 ( point->flags & touch_flag ) != 0 ); |
|
1119 |
|
1120 /* skip any touched neighbours */ |
|
1121 while ( point < end_point && |
|
1122 ( point[1].flags & touch_flag ) != 0 ) |
|
1123 point++; |
|
1124 |
|
1125 last_touched = point; |
|
1126 |
|
1127 /* find the next touched point, if any */ |
|
1128 point++; |
|
1129 for (;;) |
|
1130 { |
|
1131 if ( point > end_point ) |
|
1132 goto EndContour; |
|
1133 |
|
1134 if ( ( point->flags & touch_flag ) != 0 ) |
|
1135 break; |
|
1136 |
|
1137 point++; |
|
1138 } |
|
1139 |
|
1140 /* interpolate between last_touched and point */ |
|
1141 af_iup_interp( last_touched + 1, point - 1, |
|
1142 last_touched, point ); |
|
1143 } |
|
1144 |
|
1145 EndContour: |
|
1146 /* special case: only one point was touched */ |
|
1147 if ( last_touched == first_touched ) |
|
1148 af_iup_shift( first_point, end_point, first_touched ); |
|
1149 |
|
1150 else /* interpolate the last part */ |
|
1151 { |
|
1152 if ( last_touched < end_point ) |
|
1153 af_iup_interp( last_touched + 1, end_point, |
|
1154 last_touched, first_touched ); |
|
1155 |
|
1156 if ( first_touched > points ) |
|
1157 af_iup_interp( first_point, first_touched - 1, |
|
1158 last_touched, first_touched ); |
|
1159 } |
|
1160 |
|
1161 NextContour: |
|
1162 ; |
|
1163 } |
|
1164 |
|
1165 /* now save the interpolated values back to x/y */ |
|
1166 if ( dim == AF_DIMENSION_HORZ ) |
|
1167 { |
|
1168 for ( point = points; point < point_limit; point++ ) |
|
1169 point->x = point->u; |
|
1170 } |
|
1171 else |
|
1172 { |
|
1173 for ( point = points; point < point_limit; point++ ) |
|
1174 point->y = point->u; |
|
1175 } |
|
1176 } |
|
1177 |
|
1178 |
|
1179 #ifdef AF_CONFIG_OPTION_USE_WARPER |
|
1180 |
|
1181 /* Apply (small) warp scale and warp delta for given dimension. */ |
|
1182 |
|
1183 FT_LOCAL_DEF( void ) |
|
1184 af_glyph_hints_scale_dim( AF_GlyphHints hints, |
|
1185 AF_Dimension dim, |
|
1186 FT_Fixed scale, |
|
1187 FT_Pos delta ) |
|
1188 { |
|
1189 AF_Point points = hints->points; |
|
1190 AF_Point points_limit = points + hints->num_points; |
|
1191 AF_Point point; |
|
1192 |
|
1193 |
|
1194 if ( dim == AF_DIMENSION_HORZ ) |
|
1195 { |
|
1196 for ( point = points; point < points_limit; point++ ) |
|
1197 point->x = FT_MulFix( point->fx, scale ) + delta; |
|
1198 } |
|
1199 else |
|
1200 { |
|
1201 for ( point = points; point < points_limit; point++ ) |
|
1202 point->y = FT_MulFix( point->fy, scale ) + delta; |
|
1203 } |
|
1204 } |
|
1205 |
|
1206 #endif /* AF_CONFIG_OPTION_USE_WARPER */ |
|
1207 |
|
1208 /* END */ |
|