Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /***************************************************************************/ |
2 | /* */ |
||
3 | /* aflatin.c */ |
||
4 | /* */ |
||
5 | /* Auto-fitter hinting routines for latin script (body). */ |
||
6 | /* */ |
||
7 | /* Copyright 2003-2013 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 |
||
20 | #include FT_ADVANCES_H |
||
21 | #include FT_INTERNAL_DEBUG_H |
||
22 | |||
23 | #include "afglobal.h" |
||
24 | #include "aflatin.h" |
||
25 | #include "aferrors.h" |
||
26 | |||
27 | |||
28 | #ifdef AF_CONFIG_OPTION_USE_WARPER |
||
29 | #include "afwarp.h" |
||
30 | #endif |
||
31 | |||
32 | |||
33 | /*************************************************************************/ |
||
34 | /* */ |
||
35 | /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ |
||
36 | /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ |
||
37 | /* messages during execution. */ |
||
38 | /* */ |
||
39 | #undef FT_COMPONENT |
||
40 | #define FT_COMPONENT trace_aflatin |
||
41 | |||
42 | |||
43 | /*************************************************************************/ |
||
44 | /*************************************************************************/ |
||
45 | /***** *****/ |
||
46 | /***** L A T I N G L O B A L M E T R I C S *****/ |
||
47 | /***** *****/ |
||
48 | /*************************************************************************/ |
||
49 | /*************************************************************************/ |
||
50 | |||
51 | |||
52 | /* Find segments and links, compute all stem widths, and initialize */ |
||
53 | /* standard width and height for the glyph with given charcode. */ |
||
54 | |||
55 | FT_LOCAL_DEF( void ) |
||
56 | af_latin_metrics_init_widths( AF_LatinMetrics metrics, |
||
57 | FT_Face face ) |
||
58 | { |
||
59 | /* scan the array of segments in each direction */ |
||
60 | AF_GlyphHintsRec hints[1]; |
||
61 | |||
62 | |||
63 | FT_TRACE5(( "standard widths computation\n" |
||
64 | "===========================\n\n" )); |
||
65 | |||
66 | af_glyph_hints_init( hints, face->memory ); |
||
67 | |||
68 | metrics->axis[AF_DIMENSION_HORZ].width_count = 0; |
||
69 | metrics->axis[AF_DIMENSION_VERT].width_count = 0; |
||
70 | |||
71 | { |
||
72 | FT_Error error; |
||
73 | FT_UInt glyph_index; |
||
74 | int dim; |
||
75 | AF_LatinMetricsRec dummy[1]; |
||
76 | AF_Scaler scaler = &dummy->root.scaler; |
||
77 | |||
78 | |||
79 | glyph_index = FT_Get_Char_Index( face, |
||
80 | metrics->root.clazz->standard_char ); |
||
81 | if ( glyph_index == 0 ) |
||
82 | goto Exit; |
||
83 | |||
84 | FT_TRACE5(( "standard character: 0x%X (glyph index %d)\n", |
||
85 | metrics->root.clazz->standard_char, glyph_index )); |
||
86 | |||
87 | error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); |
||
88 | if ( error || face->glyph->outline.n_points <= 0 ) |
||
89 | goto Exit; |
||
90 | |||
91 | FT_ZERO( dummy ); |
||
92 | |||
93 | dummy->units_per_em = metrics->units_per_em; |
||
94 | |||
95 | scaler->x_scale = 0x10000L; |
||
96 | scaler->y_scale = 0x10000L; |
||
97 | scaler->x_delta = 0; |
||
98 | scaler->y_delta = 0; |
||
99 | |||
100 | scaler->face = face; |
||
101 | scaler->render_mode = FT_RENDER_MODE_NORMAL; |
||
102 | scaler->flags = 0; |
||
103 | |||
104 | af_glyph_hints_rescale( hints, (AF_ScriptMetrics)dummy ); |
||
105 | |||
106 | error = af_glyph_hints_reload( hints, &face->glyph->outline ); |
||
107 | if ( error ) |
||
108 | goto Exit; |
||
109 | |||
110 | for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) |
||
111 | { |
||
112 | AF_LatinAxis axis = &metrics->axis[dim]; |
||
113 | AF_AxisHints axhints = &hints->axis[dim]; |
||
114 | AF_Segment seg, limit, link; |
||
115 | FT_UInt num_widths = 0; |
||
116 | |||
117 | |||
118 | error = af_latin_hints_compute_segments( hints, |
||
119 | (AF_Dimension)dim ); |
||
120 | if ( error ) |
||
121 | goto Exit; |
||
122 | |||
123 | af_latin_hints_link_segments( hints, |
||
124 | (AF_Dimension)dim ); |
||
125 | |||
126 | seg = axhints->segments; |
||
127 | limit = seg + axhints->num_segments; |
||
128 | |||
129 | for ( ; seg < limit; seg++ ) |
||
130 | { |
||
131 | link = seg->link; |
||
132 | |||
133 | /* we only consider stem segments there! */ |
||
134 | if ( link && link->link == seg && link > seg ) |
||
135 | { |
||
136 | FT_Pos dist; |
||
137 | |||
138 | |||
139 | dist = seg->pos - link->pos; |
||
140 | if ( dist < 0 ) |
||
141 | dist = -dist; |
||
142 | |||
143 | if ( num_widths < AF_LATIN_MAX_WIDTHS ) |
||
144 | axis->widths[num_widths++].org = dist; |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /* this also replaces multiple almost identical stem widths */ |
||
149 | /* with a single one (the value 100 is heuristic) */ |
||
150 | af_sort_and_quantize_widths( &num_widths, axis->widths, |
||
151 | dummy->units_per_em / 100 ); |
||
152 | axis->width_count = num_widths; |
||
153 | } |
||
154 | |||
155 | Exit: |
||
156 | for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) |
||
157 | { |
||
158 | AF_LatinAxis axis = &metrics->axis[dim]; |
||
159 | FT_Pos stdw; |
||
160 | |||
161 | |||
162 | stdw = ( axis->width_count > 0 ) |
||
163 | ? axis->widths[0].org |
||
164 | : AF_LATIN_CONSTANT( metrics, 50 ); |
||
165 | |||
166 | /* let's try 20% of the smallest width */ |
||
167 | axis->edge_distance_threshold = stdw / 5; |
||
168 | axis->standard_width = stdw; |
||
169 | axis->extra_light = 0; |
||
170 | |||
171 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
172 | { |
||
173 | FT_UInt i; |
||
174 | |||
175 | |||
176 | FT_TRACE5(( "%s widths:\n", |
||
177 | dim == AF_DIMENSION_VERT ? "horizontal" |
||
178 | : "vertical" )); |
||
179 | |||
180 | FT_TRACE5(( " %d (standard)", axis->standard_width )); |
||
181 | for ( i = 1; i < axis->width_count; i++ ) |
||
182 | FT_TRACE5(( " %d", axis->widths[i].org )); |
||
183 | |||
184 | FT_TRACE5(( "\n" )); |
||
185 | } |
||
186 | #endif |
||
187 | } |
||
188 | } |
||
189 | |||
190 | FT_TRACE5(( "\n" )); |
||
191 | |||
192 | af_glyph_hints_done( hints ); |
||
193 | } |
||
194 | |||
195 | |||
196 | |||
197 | #define AF_LATIN_MAX_TEST_CHARACTERS 12 |
||
198 | |||
199 | |||
200 | static const char af_latin_blue_chars[AF_LATIN_MAX_BLUES] |
||
201 | [AF_LATIN_MAX_TEST_CHARACTERS + 1] = |
||
202 | { |
||
203 | "THEZOCQS", |
||
204 | "HEZLOCUS", |
||
205 | "fijkdbh", |
||
206 | "xzroesc", |
||
207 | "xzroesc", |
||
208 | "pqgjy" |
||
209 | }; |
||
210 | |||
211 | |||
212 | /* Find all blue zones. Flat segments give the reference points, */ |
||
213 | /* round segments the overshoot positions. */ |
||
214 | |||
215 | static void |
||
216 | af_latin_metrics_init_blues( AF_LatinMetrics metrics, |
||
217 | FT_Face face ) |
||
218 | { |
||
219 | FT_Pos flats [AF_LATIN_MAX_TEST_CHARACTERS]; |
||
220 | FT_Pos rounds[AF_LATIN_MAX_TEST_CHARACTERS]; |
||
221 | FT_Int num_flats; |
||
222 | FT_Int num_rounds; |
||
223 | FT_Int bb; |
||
224 | AF_LatinBlue blue; |
||
225 | FT_Error error; |
||
226 | AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT]; |
||
227 | FT_Outline outline; |
||
228 | |||
229 | |||
230 | /* we compute the blues simply by loading each character from the */ |
||
231 | /* `af_latin_blue_chars[blues]' string, then finding its top-most or */ |
||
232 | /* bottom-most points (depending on `AF_IS_TOP_BLUE') */ |
||
233 | |||
234 | FT_TRACE5(( "blue zones computation\n" |
||
235 | "======================\n\n" )); |
||
236 | |||
237 | for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ ) |
||
238 | { |
||
239 | const char* p = af_latin_blue_chars[bb]; |
||
240 | const char* limit = p + AF_LATIN_MAX_TEST_CHARACTERS; |
||
241 | FT_Pos* blue_ref; |
||
242 | FT_Pos* blue_shoot; |
||
243 | |||
244 | |||
245 | FT_TRACE5(( "blue zone %d:\n", bb )); |
||
246 | |||
247 | num_flats = 0; |
||
248 | num_rounds = 0; |
||
249 | |||
250 | for ( ; p < limit && *p; p++ ) |
||
251 | { |
||
252 | FT_UInt glyph_index; |
||
253 | FT_Pos best_y; /* same as points.y */ |
||
254 | FT_Int best_point, best_contour_first, best_contour_last; |
||
255 | FT_Vector* points; |
||
256 | FT_Bool round = 0; |
||
257 | |||
258 | |||
259 | /* load the character in the face -- skip unknown or empty ones */ |
||
260 | glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p ); |
||
261 | if ( glyph_index == 0 ) |
||
262 | continue; |
||
263 | |||
264 | error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); |
||
265 | outline = face->glyph->outline; |
||
266 | if ( error || outline.n_points <= 0 ) |
||
267 | continue; |
||
268 | |||
269 | /* now compute min or max point indices and coordinates */ |
||
270 | points = outline.points; |
||
271 | best_point = -1; |
||
272 | best_y = 0; /* make compiler happy */ |
||
273 | best_contour_first = 0; /* ditto */ |
||
274 | best_contour_last = 0; /* ditto */ |
||
275 | |||
276 | { |
||
277 | FT_Int nn; |
||
278 | FT_Int first = 0; |
||
279 | FT_Int last = -1; |
||
280 | |||
281 | |||
282 | for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ ) |
||
283 | { |
||
284 | FT_Int old_best_point = best_point; |
||
285 | FT_Int pp; |
||
286 | |||
287 | |||
288 | last = outline.contours[nn]; |
||
289 | |||
290 | /* Avoid single-point contours since they are never rasterized. */ |
||
291 | /* In some fonts, they correspond to mark attachment points */ |
||
292 | /* which are way outside of the glyph's real outline. */ |
||
293 | if ( last <= first ) |
||
294 | continue; |
||
295 | |||
296 | if ( AF_LATIN_IS_TOP_BLUE( bb ) ) |
||
297 | { |
||
298 | for ( pp = first; pp <= last; pp++ ) |
||
299 | if ( best_point < 0 || points[pp].y > best_y ) |
||
300 | { |
||
301 | best_point = pp; |
||
302 | best_y = points[pp].y; |
||
303 | } |
||
304 | } |
||
305 | else |
||
306 | { |
||
307 | for ( pp = first; pp <= last; pp++ ) |
||
308 | if ( best_point < 0 || points[pp].y < best_y ) |
||
309 | { |
||
310 | best_point = pp; |
||
311 | best_y = points[pp].y; |
||
312 | } |
||
313 | } |
||
314 | |||
315 | if ( best_point != old_best_point ) |
||
316 | { |
||
317 | best_contour_first = first; |
||
318 | best_contour_last = last; |
||
319 | } |
||
320 | } |
||
321 | FT_TRACE5(( " %c %ld", *p, best_y )); |
||
322 | } |
||
323 | |||
324 | /* now check whether the point belongs to a straight or round */ |
||
325 | /* segment; we first need to find in which contour the extremum */ |
||
326 | /* lies, then inspect its previous and next points */ |
||
327 | if ( best_point >= 0 ) |
||
328 | { |
||
329 | FT_Pos best_x = points[best_point].x; |
||
330 | FT_Int prev, next; |
||
331 | FT_Int best_on_point_first, best_on_point_last; |
||
332 | FT_Pos dist; |
||
333 | |||
334 | |||
335 | if ( FT_CURVE_TAG( outline.tags[best_point] ) == FT_CURVE_TAG_ON ) |
||
336 | { |
||
337 | best_on_point_first = best_point; |
||
338 | best_on_point_last = best_point; |
||
339 | } |
||
340 | else |
||
341 | { |
||
342 | best_on_point_first = -1; |
||
343 | best_on_point_last = -1; |
||
344 | } |
||
345 | |||
346 | /* look for the previous and next points that are not on the */ |
||
347 | /* same Y coordinate, then threshold the `closeness'... */ |
||
348 | prev = best_point; |
||
349 | next = prev; |
||
350 | |||
351 | do |
||
352 | { |
||
353 | if ( prev > best_contour_first ) |
||
354 | prev--; |
||
355 | else |
||
356 | prev = best_contour_last; |
||
357 | |||
358 | dist = FT_ABS( points[prev].y - best_y ); |
||
359 | /* accept a small distance or a small angle (both values are */ |
||
360 | /* heuristic; value 20 corresponds to approx. 2.9 degrees) */ |
||
361 | if ( dist > 5 ) |
||
362 | if ( FT_ABS( points[prev].x - best_x ) <= 20 * dist ) |
||
363 | break; |
||
364 | |||
365 | if ( FT_CURVE_TAG( outline.tags[prev] ) == FT_CURVE_TAG_ON ) |
||
366 | { |
||
367 | best_on_point_first = prev; |
||
368 | if ( best_on_point_last < 0 ) |
||
369 | best_on_point_last = prev; |
||
370 | } |
||
371 | |||
372 | } while ( prev != best_point ); |
||
373 | |||
374 | do |
||
375 | { |
||
376 | if ( next < best_contour_last ) |
||
377 | next++; |
||
378 | else |
||
379 | next = best_contour_first; |
||
380 | |||
381 | dist = FT_ABS( points[next].y - best_y ); |
||
382 | if ( dist > 5 ) |
||
383 | if ( FT_ABS( points[next].x - best_x ) <= 20 * dist ) |
||
384 | break; |
||
385 | |||
386 | if ( FT_CURVE_TAG( outline.tags[next] ) == FT_CURVE_TAG_ON ) |
||
387 | { |
||
388 | best_on_point_last = next; |
||
389 | if ( best_on_point_first < 0 ) |
||
390 | best_on_point_first = next; |
||
391 | } |
||
392 | |||
393 | } while ( next != best_point ); |
||
394 | |||
395 | /* now set the `round' flag depending on the segment's kind */ |
||
396 | /* (value 8 is heuristic) */ |
||
397 | if ( best_on_point_first >= 0 && |
||
398 | best_on_point_last >= 0 && |
||
399 | (FT_UInt)( FT_ABS( points[best_on_point_last].x - |
||
400 | points[best_on_point_first].x ) ) > |
||
401 | metrics->units_per_em / 8 ) |
||
402 | round = 0; |
||
403 | else |
||
404 | round = FT_BOOL( |
||
405 | FT_CURVE_TAG( outline.tags[prev] ) != FT_CURVE_TAG_ON || |
||
406 | FT_CURVE_TAG( outline.tags[next] ) != FT_CURVE_TAG_ON ); |
||
407 | |||
408 | FT_TRACE5(( " (%s)\n", round ? "round" : "flat" )); |
||
409 | } |
||
410 | |||
411 | if ( round ) |
||
412 | rounds[num_rounds++] = best_y; |
||
413 | else |
||
414 | flats[num_flats++] = best_y; |
||
415 | } |
||
416 | |||
417 | if ( num_flats == 0 && num_rounds == 0 ) |
||
418 | { |
||
419 | /* |
||
420 | * we couldn't find a single glyph to compute this blue zone, |
||
421 | * we will simply ignore it then |
||
422 | */ |
||
423 | FT_TRACE5(( " empty\n" )); |
||
424 | continue; |
||
425 | } |
||
426 | |||
427 | /* we have computed the contents of the `rounds' and `flats' tables, */ |
||
428 | /* now determine the reference and overshoot position of the blue -- */ |
||
429 | /* we simply take the median value after a simple sort */ |
||
430 | af_sort_pos( num_rounds, rounds ); |
||
431 | af_sort_pos( num_flats, flats ); |
||
432 | |||
433 | blue = &axis->blues[axis->blue_count]; |
||
434 | blue_ref = &blue->ref.org; |
||
435 | blue_shoot = &blue->shoot.org; |
||
436 | |||
437 | axis->blue_count++; |
||
438 | |||
439 | if ( num_flats == 0 ) |
||
440 | { |
||
441 | *blue_ref = |
||
442 | *blue_shoot = rounds[num_rounds / 2]; |
||
443 | } |
||
444 | else if ( num_rounds == 0 ) |
||
445 | { |
||
446 | *blue_ref = |
||
447 | *blue_shoot = flats[num_flats / 2]; |
||
448 | } |
||
449 | else |
||
450 | { |
||
451 | *blue_ref = flats[num_flats / 2]; |
||
452 | *blue_shoot = rounds[num_rounds / 2]; |
||
453 | } |
||
454 | |||
455 | /* there are sometimes problems: if the overshoot position of top */ |
||
456 | /* zones is under its reference position, or the opposite for bottom */ |
||
457 | /* zones. We must thus check everything there and correct the errors */ |
||
458 | if ( *blue_shoot != *blue_ref ) |
||
459 | { |
||
460 | FT_Pos ref = *blue_ref; |
||
461 | FT_Pos shoot = *blue_shoot; |
||
462 | FT_Bool over_ref = FT_BOOL( shoot > ref ); |
||
463 | |||
464 | |||
465 | if ( AF_LATIN_IS_TOP_BLUE( bb ) ^ over_ref ) |
||
466 | { |
||
467 | *blue_ref = |
||
468 | *blue_shoot = ( shoot + ref ) / 2; |
||
469 | |||
470 | FT_TRACE5(( " [overshoot smaller than reference," |
||
471 | " taking mean value]\n" )); |
||
472 | } |
||
473 | } |
||
474 | |||
475 | blue->flags = 0; |
||
476 | if ( AF_LATIN_IS_TOP_BLUE( bb ) ) |
||
477 | blue->flags |= AF_LATIN_BLUE_TOP; |
||
478 | |||
479 | /* |
||
480 | * The following flag is used later to adjust the y and x scales |
||
481 | * in order to optimize the pixel grid alignment of the top of small |
||
482 | * letters. |
||
483 | */ |
||
484 | if ( bb == AF_LATIN_BLUE_SMALL_TOP ) |
||
485 | blue->flags |= AF_LATIN_BLUE_ADJUSTMENT; |
||
486 | |||
487 | FT_TRACE5(( " -> reference = %ld\n" |
||
488 | " overshoot = %ld\n", |
||
489 | *blue_ref, *blue_shoot )); |
||
490 | } |
||
491 | |||
492 | FT_TRACE5(( "\n" )); |
||
493 | |||
494 | return; |
||
495 | } |
||
496 | |||
497 | |||
498 | /* Check whether all ASCII digits have the same advance width. */ |
||
499 | |||
500 | FT_LOCAL_DEF( void ) |
||
501 | af_latin_metrics_check_digits( AF_LatinMetrics metrics, |
||
502 | FT_Face face ) |
||
503 | { |
||
504 | FT_UInt i; |
||
505 | FT_Bool started = 0, same_width = 1; |
||
506 | FT_Fixed advance, old_advance = 0; |
||
507 | |||
508 | |||
509 | /* digit `0' is 0x30 in all supported charmaps */ |
||
510 | for ( i = 0x30; i <= 0x39; i++ ) |
||
511 | { |
||
512 | FT_UInt glyph_index; |
||
513 | |||
514 | |||
515 | glyph_index = FT_Get_Char_Index( face, i ); |
||
516 | if ( glyph_index == 0 ) |
||
517 | continue; |
||
518 | |||
519 | if ( FT_Get_Advance( face, glyph_index, |
||
520 | FT_LOAD_NO_SCALE | |
||
521 | FT_LOAD_NO_HINTING | |
||
522 | FT_LOAD_IGNORE_TRANSFORM, |
||
523 | &advance ) ) |
||
524 | continue; |
||
525 | |||
526 | if ( started ) |
||
527 | { |
||
528 | if ( advance != old_advance ) |
||
529 | { |
||
530 | same_width = 0; |
||
531 | break; |
||
532 | } |
||
533 | } |
||
534 | else |
||
535 | { |
||
536 | old_advance = advance; |
||
537 | started = 1; |
||
538 | } |
||
539 | } |
||
540 | |||
541 | metrics->root.digits_have_same_width = same_width; |
||
542 | } |
||
543 | |||
544 | |||
545 | /* Initialize global metrics. */ |
||
546 | |||
547 | FT_LOCAL_DEF( FT_Error ) |
||
548 | af_latin_metrics_init( AF_LatinMetrics metrics, |
||
549 | FT_Face face ) |
||
550 | { |
||
551 | FT_CharMap oldmap = face->charmap; |
||
552 | |||
553 | |||
554 | metrics->units_per_em = face->units_per_EM; |
||
555 | |||
556 | if ( !FT_Select_Charmap( face, FT_ENCODING_UNICODE ) ) |
||
557 | { |
||
558 | af_latin_metrics_init_widths( metrics, face ); |
||
559 | af_latin_metrics_init_blues( metrics, face ); |
||
560 | af_latin_metrics_check_digits( metrics, face ); |
||
561 | } |
||
562 | |||
563 | FT_Set_Charmap( face, oldmap ); |
||
564 | return FT_Err_Ok; |
||
565 | } |
||
566 | |||
567 | |||
568 | /* Adjust scaling value, then scale and shift widths */ |
||
569 | /* and blue zones (if applicable) for given dimension. */ |
||
570 | |||
571 | static void |
||
572 | af_latin_metrics_scale_dim( AF_LatinMetrics metrics, |
||
573 | AF_Scaler scaler, |
||
574 | AF_Dimension dim ) |
||
575 | { |
||
576 | FT_Fixed scale; |
||
577 | FT_Pos delta; |
||
578 | AF_LatinAxis axis; |
||
579 | FT_UInt nn; |
||
580 | |||
581 | |||
582 | if ( dim == AF_DIMENSION_HORZ ) |
||
583 | { |
||
584 | scale = scaler->x_scale; |
||
585 | delta = scaler->x_delta; |
||
586 | } |
||
587 | else |
||
588 | { |
||
589 | scale = scaler->y_scale; |
||
590 | delta = scaler->y_delta; |
||
591 | } |
||
592 | |||
593 | axis = &metrics->axis[dim]; |
||
594 | |||
595 | if ( axis->org_scale == scale && axis->org_delta == delta ) |
||
596 | return; |
||
597 | |||
598 | axis->org_scale = scale; |
||
599 | axis->org_delta = delta; |
||
600 | |||
601 | /* |
||
602 | * correct X and Y scale to optimize the alignment of the top of small |
||
603 | * letters to the pixel grid |
||
604 | */ |
||
605 | { |
||
606 | AF_LatinAxis Axis = &metrics->axis[AF_DIMENSION_VERT]; |
||
607 | AF_LatinBlue blue = NULL; |
||
608 | |||
609 | |||
610 | for ( nn = 0; nn < Axis->blue_count; nn++ ) |
||
611 | { |
||
612 | if ( Axis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT ) |
||
613 | { |
||
614 | blue = &Axis->blues[nn]; |
||
615 | break; |
||
616 | } |
||
617 | } |
||
618 | |||
619 | if ( blue ) |
||
620 | { |
||
621 | FT_Pos scaled; |
||
622 | FT_Pos threshold; |
||
623 | FT_Pos fitted; |
||
624 | FT_UInt limit; |
||
625 | FT_UInt ppem; |
||
626 | |||
627 | |||
628 | scaled = FT_MulFix( blue->shoot.org, scaler->y_scale ); |
||
629 | ppem = metrics->root.scaler.face->size->metrics.x_ppem; |
||
630 | limit = metrics->root.globals->increase_x_height; |
||
631 | threshold = 40; |
||
632 | |||
633 | /* if the `increase-x-height' property is active, */ |
||
634 | /* we round up much more often */ |
||
635 | if ( limit && |
||
636 | ppem <= limit && |
||
637 | ppem >= AF_PROP_INCREASE_X_HEIGHT_MIN ) |
||
638 | threshold = 52; |
||
639 | |||
640 | fitted = ( scaled + threshold ) & ~63; |
||
641 | |||
642 | if ( scaled != fitted ) |
||
643 | { |
||
644 | #if 0 |
||
645 | if ( dim == AF_DIMENSION_HORZ ) |
||
646 | { |
||
647 | if ( fitted < scaled ) |
||
648 | scale -= scale / 50; /* scale *= 0.98 */ |
||
649 | } |
||
650 | else |
||
651 | #endif |
||
652 | if ( dim == AF_DIMENSION_VERT ) |
||
653 | scale = FT_MulDiv( scale, fitted, scaled ); |
||
654 | } |
||
655 | } |
||
656 | } |
||
657 | |||
658 | axis->scale = scale; |
||
659 | axis->delta = delta; |
||
660 | |||
661 | if ( dim == AF_DIMENSION_HORZ ) |
||
662 | { |
||
663 | metrics->root.scaler.x_scale = scale; |
||
664 | metrics->root.scaler.x_delta = delta; |
||
665 | } |
||
666 | else |
||
667 | { |
||
668 | metrics->root.scaler.y_scale = scale; |
||
669 | metrics->root.scaler.y_delta = delta; |
||
670 | } |
||
671 | |||
672 | /* scale the widths */ |
||
673 | for ( nn = 0; nn < axis->width_count; nn++ ) |
||
674 | { |
||
675 | AF_Width width = axis->widths + nn; |
||
676 | |||
677 | |||
678 | width->cur = FT_MulFix( width->org, scale ); |
||
679 | width->fit = width->cur; |
||
680 | } |
||
681 | |||
682 | /* an extra-light axis corresponds to a standard width that is */ |
||
683 | /* smaller than 5/8 pixels */ |
||
684 | axis->extra_light = |
||
685 | (FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 ); |
||
686 | |||
687 | if ( dim == AF_DIMENSION_VERT ) |
||
688 | { |
||
689 | /* scale the blue zones */ |
||
690 | for ( nn = 0; nn < axis->blue_count; nn++ ) |
||
691 | { |
||
692 | AF_LatinBlue blue = &axis->blues[nn]; |
||
693 | FT_Pos dist; |
||
694 | |||
695 | |||
696 | blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta; |
||
697 | blue->ref.fit = blue->ref.cur; |
||
698 | blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta; |
||
699 | blue->shoot.fit = blue->shoot.cur; |
||
700 | blue->flags &= ~AF_LATIN_BLUE_ACTIVE; |
||
701 | |||
702 | /* a blue zone is only active if it is less than 3/4 pixels tall */ |
||
703 | dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale ); |
||
704 | if ( dist <= 48 && dist >= -48 ) |
||
705 | { |
||
706 | #if 0 |
||
707 | FT_Pos delta1; |
||
708 | #endif |
||
709 | FT_Pos delta2; |
||
710 | |||
711 | |||
712 | /* use discrete values for blue zone widths */ |
||
713 | |||
714 | #if 0 |
||
715 | |||
716 | /* generic, original code */ |
||
717 | delta1 = blue->shoot.org - blue->ref.org; |
||
718 | delta2 = delta1; |
||
719 | if ( delta1 < 0 ) |
||
720 | delta2 = -delta2; |
||
721 | |||
722 | delta2 = FT_MulFix( delta2, scale ); |
||
723 | |||
724 | if ( delta2 < 32 ) |
||
725 | delta2 = 0; |
||
726 | else if ( delta2 < 64 ) |
||
727 | delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 ); |
||
728 | else |
||
729 | delta2 = FT_PIX_ROUND( delta2 ); |
||
730 | |||
731 | if ( delta1 < 0 ) |
||
732 | delta2 = -delta2; |
||
733 | |||
734 | blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); |
||
735 | blue->shoot.fit = blue->ref.fit + delta2; |
||
736 | |||
737 | #else |
||
738 | |||
739 | /* simplified version due to abs(dist) <= 48 */ |
||
740 | delta2 = dist; |
||
741 | if ( dist < 0 ) |
||
742 | delta2 = -delta2; |
||
743 | |||
744 | if ( delta2 < 32 ) |
||
745 | delta2 = 0; |
||
746 | else if ( delta2 < 48 ) |
||
747 | delta2 = 32; |
||
748 | else |
||
749 | delta2 = 64; |
||
750 | |||
751 | if ( dist < 0 ) |
||
752 | delta2 = -delta2; |
||
753 | |||
754 | blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); |
||
755 | blue->shoot.fit = blue->ref.fit - delta2; |
||
756 | |||
757 | #endif |
||
758 | |||
759 | blue->flags |= AF_LATIN_BLUE_ACTIVE; |
||
760 | } |
||
761 | } |
||
762 | } |
||
763 | } |
||
764 | |||
765 | |||
766 | /* Scale global values in both directions. */ |
||
767 | |||
768 | FT_LOCAL_DEF( void ) |
||
769 | af_latin_metrics_scale( AF_LatinMetrics metrics, |
||
770 | AF_Scaler scaler ) |
||
771 | { |
||
772 | metrics->root.scaler.render_mode = scaler->render_mode; |
||
773 | metrics->root.scaler.face = scaler->face; |
||
774 | metrics->root.scaler.flags = scaler->flags; |
||
775 | |||
776 | af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ ); |
||
777 | af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT ); |
||
778 | } |
||
779 | |||
780 | |||
781 | /*************************************************************************/ |
||
782 | /*************************************************************************/ |
||
783 | /***** *****/ |
||
784 | /***** L A T I N G L Y P H A N A L Y S I S *****/ |
||
785 | /***** *****/ |
||
786 | /*************************************************************************/ |
||
787 | /*************************************************************************/ |
||
788 | |||
789 | |||
790 | /* Walk over all contours and compute its segments. */ |
||
791 | |||
792 | FT_LOCAL_DEF( FT_Error ) |
||
793 | af_latin_hints_compute_segments( AF_GlyphHints hints, |
||
794 | AF_Dimension dim ) |
||
795 | { |
||
796 | AF_AxisHints axis = &hints->axis[dim]; |
||
797 | FT_Memory memory = hints->memory; |
||
798 | FT_Error error = FT_Err_Ok; |
||
799 | AF_Segment segment = NULL; |
||
800 | AF_SegmentRec seg0; |
||
801 | AF_Point* contour = hints->contours; |
||
802 | AF_Point* contour_limit = contour + hints->num_contours; |
||
803 | AF_Direction major_dir, segment_dir; |
||
804 | |||
805 | |||
806 | FT_ZERO( &seg0 ); |
||
807 | seg0.score = 32000; |
||
808 | seg0.flags = AF_EDGE_NORMAL; |
||
809 | |||
810 | major_dir = (AF_Direction)FT_ABS( axis->major_dir ); |
||
811 | segment_dir = major_dir; |
||
812 | |||
813 | axis->num_segments = 0; |
||
814 | |||
815 | /* set up (u,v) in each point */ |
||
816 | if ( dim == AF_DIMENSION_HORZ ) |
||
817 | { |
||
818 | AF_Point point = hints->points; |
||
819 | AF_Point limit = point + hints->num_points; |
||
820 | |||
821 | |||
822 | for ( ; point < limit; point++ ) |
||
823 | { |
||
824 | point->u = point->fx; |
||
825 | point->v = point->fy; |
||
826 | } |
||
827 | } |
||
828 | else |
||
829 | { |
||
830 | AF_Point point = hints->points; |
||
831 | AF_Point limit = point + hints->num_points; |
||
832 | |||
833 | |||
834 | for ( ; point < limit; point++ ) |
||
835 | { |
||
836 | point->u = point->fy; |
||
837 | point->v = point->fx; |
||
838 | } |
||
839 | } |
||
840 | |||
841 | /* do each contour separately */ |
||
842 | for ( ; contour < contour_limit; contour++ ) |
||
843 | { |
||
844 | AF_Point point = contour[0]; |
||
845 | AF_Point last = point->prev; |
||
846 | int on_edge = 0; |
||
847 | FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */ |
||
848 | FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */ |
||
849 | FT_Bool passed; |
||
850 | |||
851 | |||
852 | if ( point == last ) /* skip singletons -- just in case */ |
||
853 | continue; |
||
854 | |||
855 | if ( FT_ABS( last->out_dir ) == major_dir && |
||
856 | FT_ABS( point->out_dir ) == major_dir ) |
||
857 | { |
||
858 | /* we are already on an edge, try to locate its start */ |
||
859 | last = point; |
||
860 | |||
861 | for (;;) |
||
862 | { |
||
863 | point = point->prev; |
||
864 | if ( FT_ABS( point->out_dir ) != major_dir ) |
||
865 | { |
||
866 | point = point->next; |
||
867 | break; |
||
868 | } |
||
869 | if ( point == last ) |
||
870 | break; |
||
871 | } |
||
872 | } |
||
873 | |||
874 | last = point; |
||
875 | passed = 0; |
||
876 | |||
877 | for (;;) |
||
878 | { |
||
879 | FT_Pos u, v; |
||
880 | |||
881 | |||
882 | if ( on_edge ) |
||
883 | { |
||
884 | u = point->u; |
||
885 | if ( u < min_pos ) |
||
886 | min_pos = u; |
||
887 | if ( u > max_pos ) |
||
888 | max_pos = u; |
||
889 | |||
890 | if ( point->out_dir != segment_dir || point == last ) |
||
891 | { |
||
892 | /* we are just leaving an edge; record a new segment! */ |
||
893 | segment->last = point; |
||
894 | segment->pos = (FT_Short)( ( min_pos + max_pos ) >> 1 ); |
||
895 | |||
896 | /* a segment is round if either its first or last point */ |
||
897 | /* is a control point */ |
||
898 | if ( ( segment->first->flags | point->flags ) & |
||
899 | AF_FLAG_CONTROL ) |
||
900 | segment->flags |= AF_EDGE_ROUND; |
||
901 | |||
902 | /* compute segment size */ |
||
903 | min_pos = max_pos = point->v; |
||
904 | |||
905 | v = segment->first->v; |
||
906 | if ( v < min_pos ) |
||
907 | min_pos = v; |
||
908 | if ( v > max_pos ) |
||
909 | max_pos = v; |
||
910 | |||
911 | segment->min_coord = (FT_Short)min_pos; |
||
912 | segment->max_coord = (FT_Short)max_pos; |
||
913 | segment->height = (FT_Short)( segment->max_coord - |
||
914 | segment->min_coord ); |
||
915 | |||
916 | on_edge = 0; |
||
917 | segment = NULL; |
||
918 | /* fall through */ |
||
919 | } |
||
920 | } |
||
921 | |||
922 | /* now exit if we are at the start/end point */ |
||
923 | if ( point == last ) |
||
924 | { |
||
925 | if ( passed ) |
||
926 | break; |
||
927 | passed = 1; |
||
928 | } |
||
929 | |||
930 | if ( !on_edge && FT_ABS( point->out_dir ) == major_dir ) |
||
931 | { |
||
932 | /* this is the start of a new segment! */ |
||
933 | segment_dir = (AF_Direction)point->out_dir; |
||
934 | |||
935 | /* clear all segment fields */ |
||
936 | error = af_axis_hints_new_segment( axis, memory, &segment ); |
||
937 | if ( error ) |
||
938 | goto Exit; |
||
939 | |||
940 | segment[0] = seg0; |
||
941 | segment->dir = (FT_Char)segment_dir; |
||
942 | min_pos = max_pos = point->u; |
||
943 | segment->first = point; |
||
944 | segment->last = point; |
||
945 | on_edge = 1; |
||
946 | } |
||
947 | |||
948 | point = point->next; |
||
949 | } |
||
950 | |||
951 | } /* contours */ |
||
952 | |||
953 | |||
954 | /* now slightly increase the height of segments if this makes */ |
||
955 | /* sense -- this is used to better detect and ignore serifs */ |
||
956 | { |
||
957 | AF_Segment segments = axis->segments; |
||
958 | AF_Segment segments_end = segments + axis->num_segments; |
||
959 | |||
960 | |||
961 | for ( segment = segments; segment < segments_end; segment++ ) |
||
962 | { |
||
963 | AF_Point first = segment->first; |
||
964 | AF_Point last = segment->last; |
||
965 | FT_Pos first_v = first->v; |
||
966 | FT_Pos last_v = last->v; |
||
967 | |||
968 | |||
969 | if ( first == last ) |
||
970 | continue; |
||
971 | |||
972 | if ( first_v < last_v ) |
||
973 | { |
||
974 | AF_Point p; |
||
975 | |||
976 | |||
977 | p = first->prev; |
||
978 | if ( p->v < first_v ) |
||
979 | segment->height = (FT_Short)( segment->height + |
||
980 | ( ( first_v - p->v ) >> 1 ) ); |
||
981 | |||
982 | p = last->next; |
||
983 | if ( p->v > last_v ) |
||
984 | segment->height = (FT_Short)( segment->height + |
||
985 | ( ( p->v - last_v ) >> 1 ) ); |
||
986 | } |
||
987 | else |
||
988 | { |
||
989 | AF_Point p; |
||
990 | |||
991 | |||
992 | p = first->prev; |
||
993 | if ( p->v > first_v ) |
||
994 | segment->height = (FT_Short)( segment->height + |
||
995 | ( ( p->v - first_v ) >> 1 ) ); |
||
996 | |||
997 | p = last->next; |
||
998 | if ( p->v < last_v ) |
||
999 | segment->height = (FT_Short)( segment->height + |
||
1000 | ( ( last_v - p->v ) >> 1 ) ); |
||
1001 | } |
||
1002 | } |
||
1003 | } |
||
1004 | |||
1005 | Exit: |
||
1006 | return error; |
||
1007 | } |
||
1008 | |||
1009 | |||
1010 | /* Link segments to form stems and serifs. */ |
||
1011 | |||
1012 | FT_LOCAL_DEF( void ) |
||
1013 | af_latin_hints_link_segments( AF_GlyphHints hints, |
||
1014 | AF_Dimension dim ) |
||
1015 | { |
||
1016 | AF_AxisHints axis = &hints->axis[dim]; |
||
1017 | AF_Segment segments = axis->segments; |
||
1018 | AF_Segment segment_limit = segments + axis->num_segments; |
||
1019 | FT_Pos len_threshold, len_score; |
||
1020 | AF_Segment seg1, seg2; |
||
1021 | |||
1022 | |||
1023 | len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 ); |
||
1024 | if ( len_threshold == 0 ) |
||
1025 | len_threshold = 1; |
||
1026 | |||
1027 | len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 ); |
||
1028 | |||
1029 | /* now compare each segment to the others */ |
||
1030 | for ( seg1 = segments; seg1 < segment_limit; seg1++ ) |
||
1031 | { |
||
1032 | /* the fake segments are introduced to hint the metrics -- */ |
||
1033 | /* we must never link them to anything */ |
||
1034 | if ( seg1->dir != axis->major_dir || seg1->first == seg1->last ) |
||
1035 | continue; |
||
1036 | |||
1037 | /* search for stems having opposite directions, */ |
||
1038 | /* with seg1 to the `left' of seg2 */ |
||
1039 | for ( seg2 = segments; seg2 < segment_limit; seg2++ ) |
||
1040 | { |
||
1041 | FT_Pos pos1 = seg1->pos; |
||
1042 | FT_Pos pos2 = seg2->pos; |
||
1043 | |||
1044 | |||
1045 | if ( seg1->dir + seg2->dir == 0 && pos2 > pos1 ) |
||
1046 | { |
||
1047 | /* compute distance between the two segments */ |
||
1048 | FT_Pos dist = pos2 - pos1; |
||
1049 | FT_Pos min = seg1->min_coord; |
||
1050 | FT_Pos max = seg1->max_coord; |
||
1051 | FT_Pos len, score; |
||
1052 | |||
1053 | |||
1054 | if ( min < seg2->min_coord ) |
||
1055 | min = seg2->min_coord; |
||
1056 | |||
1057 | if ( max > seg2->max_coord ) |
||
1058 | max = seg2->max_coord; |
||
1059 | |||
1060 | /* compute maximum coordinate difference of the two segments */ |
||
1061 | len = max - min; |
||
1062 | if ( len >= len_threshold ) |
||
1063 | { |
||
1064 | /* small coordinate differences cause a higher score, and */ |
||
1065 | /* segments with a greater distance cause a higher score also */ |
||
1066 | score = dist + len_score / len; |
||
1067 | |||
1068 | /* and we search for the smallest score */ |
||
1069 | /* of the sum of the two values */ |
||
1070 | if ( score < seg1->score ) |
||
1071 | { |
||
1072 | seg1->score = score; |
||
1073 | seg1->link = seg2; |
||
1074 | } |
||
1075 | |||
1076 | if ( score < seg2->score ) |
||
1077 | { |
||
1078 | seg2->score = score; |
||
1079 | seg2->link = seg1; |
||
1080 | } |
||
1081 | } |
||
1082 | } |
||
1083 | } |
||
1084 | } |
||
1085 | |||
1086 | /* now compute the `serif' segments, cf. explanations in `afhints.h' */ |
||
1087 | for ( seg1 = segments; seg1 < segment_limit; seg1++ ) |
||
1088 | { |
||
1089 | seg2 = seg1->link; |
||
1090 | |||
1091 | if ( seg2 ) |
||
1092 | { |
||
1093 | if ( seg2->link != seg1 ) |
||
1094 | { |
||
1095 | seg1->link = 0; |
||
1096 | seg1->serif = seg2->link; |
||
1097 | } |
||
1098 | } |
||
1099 | } |
||
1100 | } |
||
1101 | |||
1102 | |||
1103 | /* Link segments to edges, using feature analysis for selection. */ |
||
1104 | |||
1105 | FT_LOCAL_DEF( FT_Error ) |
||
1106 | af_latin_hints_compute_edges( AF_GlyphHints hints, |
||
1107 | AF_Dimension dim ) |
||
1108 | { |
||
1109 | AF_AxisHints axis = &hints->axis[dim]; |
||
1110 | FT_Error error = FT_Err_Ok; |
||
1111 | FT_Memory memory = hints->memory; |
||
1112 | AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim]; |
||
1113 | |||
1114 | AF_Segment segments = axis->segments; |
||
1115 | AF_Segment segment_limit = segments + axis->num_segments; |
||
1116 | AF_Segment seg; |
||
1117 | |||
1118 | #if 0 |
||
1119 | AF_Direction up_dir; |
||
1120 | #endif |
||
1121 | FT_Fixed scale; |
||
1122 | FT_Pos edge_distance_threshold; |
||
1123 | FT_Pos segment_length_threshold; |
||
1124 | |||
1125 | |||
1126 | axis->num_edges = 0; |
||
1127 | |||
1128 | scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale |
||
1129 | : hints->y_scale; |
||
1130 | |||
1131 | #if 0 |
||
1132 | up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP |
||
1133 | : AF_DIR_RIGHT; |
||
1134 | #endif |
||
1135 | |||
1136 | /* |
||
1137 | * We ignore all segments that are less than 1 pixel in length |
||
1138 | * to avoid many problems with serif fonts. We compute the |
||
1139 | * corresponding threshold in font units. |
||
1140 | */ |
||
1141 | if ( dim == AF_DIMENSION_HORZ ) |
||
1142 | segment_length_threshold = FT_DivFix( 64, hints->y_scale ); |
||
1143 | else |
||
1144 | segment_length_threshold = 0; |
||
1145 | |||
1146 | /*********************************************************************/ |
||
1147 | /* */ |
||
1148 | /* We begin by generating a sorted table of edges for the current */ |
||
1149 | /* direction. To do so, we simply scan each segment and try to find */ |
||
1150 | /* an edge in our table that corresponds to its position. */ |
||
1151 | /* */ |
||
1152 | /* If no edge is found, we create and insert a new edge in the */ |
||
1153 | /* sorted table. Otherwise, we simply add the segment to the edge's */ |
||
1154 | /* list which gets processed in the second step to compute the */ |
||
1155 | /* edge's properties. */ |
||
1156 | /* */ |
||
1157 | /* Note that the table of edges is sorted along the segment/edge */ |
||
1158 | /* position. */ |
||
1159 | /* */ |
||
1160 | /*********************************************************************/ |
||
1161 | |||
1162 | /* assure that edge distance threshold is at most 0.25px */ |
||
1163 | edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold, |
||
1164 | scale ); |
||
1165 | if ( edge_distance_threshold > 64 / 4 ) |
||
1166 | edge_distance_threshold = 64 / 4; |
||
1167 | |||
1168 | edge_distance_threshold = FT_DivFix( edge_distance_threshold, |
||
1169 | scale ); |
||
1170 | |||
1171 | for ( seg = segments; seg < segment_limit; seg++ ) |
||
1172 | { |
||
1173 | AF_Edge found = NULL; |
||
1174 | FT_Int ee; |
||
1175 | |||
1176 | |||
1177 | if ( seg->height < segment_length_threshold ) |
||
1178 | continue; |
||
1179 | |||
1180 | /* A special case for serif edges: If they are smaller than */ |
||
1181 | /* 1.5 pixels we ignore them. */ |
||
1182 | if ( seg->serif && |
||
1183 | 2 * seg->height < 3 * segment_length_threshold ) |
||
1184 | continue; |
||
1185 | |||
1186 | /* look for an edge corresponding to the segment */ |
||
1187 | for ( ee = 0; ee < axis->num_edges; ee++ ) |
||
1188 | { |
||
1189 | AF_Edge edge = axis->edges + ee; |
||
1190 | FT_Pos dist; |
||
1191 | |||
1192 | |||
1193 | dist = seg->pos - edge->fpos; |
||
1194 | if ( dist < 0 ) |
||
1195 | dist = -dist; |
||
1196 | |||
1197 | if ( dist < edge_distance_threshold && edge->dir == seg->dir ) |
||
1198 | { |
||
1199 | found = edge; |
||
1200 | break; |
||
1201 | } |
||
1202 | } |
||
1203 | |||
1204 | if ( !found ) |
||
1205 | { |
||
1206 | AF_Edge edge; |
||
1207 | |||
1208 | |||
1209 | /* insert a new edge in the list and */ |
||
1210 | /* sort according to the position */ |
||
1211 | error = af_axis_hints_new_edge( axis, seg->pos, |
||
1212 | (AF_Direction)seg->dir, |
||
1213 | memory, &edge ); |
||
1214 | if ( error ) |
||
1215 | goto Exit; |
||
1216 | |||
1217 | /* add the segment to the new edge's list */ |
||
1218 | FT_ZERO( edge ); |
||
1219 | |||
1220 | edge->first = seg; |
||
1221 | edge->last = seg; |
||
1222 | edge->dir = seg->dir; |
||
1223 | edge->fpos = seg->pos; |
||
1224 | edge->opos = FT_MulFix( seg->pos, scale ); |
||
1225 | edge->pos = edge->opos; |
||
1226 | seg->edge_next = seg; |
||
1227 | } |
||
1228 | else |
||
1229 | { |
||
1230 | /* if an edge was found, simply add the segment to the edge's */ |
||
1231 | /* list */ |
||
1232 | seg->edge_next = found->first; |
||
1233 | found->last->edge_next = seg; |
||
1234 | found->last = seg; |
||
1235 | } |
||
1236 | } |
||
1237 | |||
1238 | |||
1239 | /******************************************************************/ |
||
1240 | /* */ |
||
1241 | /* Good, we now compute each edge's properties according to the */ |
||
1242 | /* segments found on its position. Basically, these are */ |
||
1243 | /* */ |
||
1244 | /* - the edge's main direction */ |
||
1245 | /* - stem edge, serif edge or both (which defaults to stem then) */ |
||
1246 | /* - rounded edge, straight or both (which defaults to straight) */ |
||
1247 | /* - link for edge */ |
||
1248 | /* */ |
||
1249 | /******************************************************************/ |
||
1250 | |||
1251 | /* first of all, set the `edge' field in each segment -- this is */ |
||
1252 | /* required in order to compute edge links */ |
||
1253 | |||
1254 | /* |
||
1255 | * Note that removing this loop and setting the `edge' field of each |
||
1256 | * segment directly in the code above slows down execution speed for |
||
1257 | * some reasons on platforms like the Sun. |
||
1258 | */ |
||
1259 | { |
||
1260 | AF_Edge edges = axis->edges; |
||
1261 | AF_Edge edge_limit = edges + axis->num_edges; |
||
1262 | AF_Edge edge; |
||
1263 | |||
1264 | |||
1265 | for ( edge = edges; edge < edge_limit; edge++ ) |
||
1266 | { |
||
1267 | seg = edge->first; |
||
1268 | if ( seg ) |
||
1269 | do |
||
1270 | { |
||
1271 | seg->edge = edge; |
||
1272 | seg = seg->edge_next; |
||
1273 | |||
1274 | } while ( seg != edge->first ); |
||
1275 | } |
||
1276 | |||
1277 | /* now compute each edge properties */ |
||
1278 | for ( edge = edges; edge < edge_limit; edge++ ) |
||
1279 | { |
||
1280 | FT_Int is_round = 0; /* does it contain round segments? */ |
||
1281 | FT_Int is_straight = 0; /* does it contain straight segments? */ |
||
1282 | #if 0 |
||
1283 | FT_Pos ups = 0; /* number of upwards segments */ |
||
1284 | FT_Pos downs = 0; /* number of downwards segments */ |
||
1285 | #endif |
||
1286 | |||
1287 | |||
1288 | seg = edge->first; |
||
1289 | |||
1290 | do |
||
1291 | { |
||
1292 | FT_Bool is_serif; |
||
1293 | |||
1294 | |||
1295 | /* check for roundness of segment */ |
||
1296 | if ( seg->flags & AF_EDGE_ROUND ) |
||
1297 | is_round++; |
||
1298 | else |
||
1299 | is_straight++; |
||
1300 | |||
1301 | #if 0 |
||
1302 | /* check for segment direction */ |
||
1303 | if ( seg->dir == up_dir ) |
||
1304 | ups += seg->max_coord - seg->min_coord; |
||
1305 | else |
||
1306 | downs += seg->max_coord - seg->min_coord; |
||
1307 | #endif |
||
1308 | |||
1309 | /* check for links -- if seg->serif is set, then seg->link must */ |
||
1310 | /* be ignored */ |
||
1311 | is_serif = (FT_Bool)( seg->serif && |
||
1312 | seg->serif->edge && |
||
1313 | seg->serif->edge != edge ); |
||
1314 | |||
1315 | if ( ( seg->link && seg->link->edge != NULL ) || is_serif ) |
||
1316 | { |
||
1317 | AF_Edge edge2; |
||
1318 | AF_Segment seg2; |
||
1319 | |||
1320 | |||
1321 | edge2 = edge->link; |
||
1322 | seg2 = seg->link; |
||
1323 | |||
1324 | if ( is_serif ) |
||
1325 | { |
||
1326 | seg2 = seg->serif; |
||
1327 | edge2 = edge->serif; |
||
1328 | } |
||
1329 | |||
1330 | if ( edge2 ) |
||
1331 | { |
||
1332 | FT_Pos edge_delta; |
||
1333 | FT_Pos seg_delta; |
||
1334 | |||
1335 | |||
1336 | edge_delta = edge->fpos - edge2->fpos; |
||
1337 | if ( edge_delta < 0 ) |
||
1338 | edge_delta = -edge_delta; |
||
1339 | |||
1340 | seg_delta = seg->pos - seg2->pos; |
||
1341 | if ( seg_delta < 0 ) |
||
1342 | seg_delta = -seg_delta; |
||
1343 | |||
1344 | if ( seg_delta < edge_delta ) |
||
1345 | edge2 = seg2->edge; |
||
1346 | } |
||
1347 | else |
||
1348 | edge2 = seg2->edge; |
||
1349 | |||
1350 | if ( is_serif ) |
||
1351 | { |
||
1352 | edge->serif = edge2; |
||
1353 | edge2->flags |= AF_EDGE_SERIF; |
||
1354 | } |
||
1355 | else |
||
1356 | edge->link = edge2; |
||
1357 | } |
||
1358 | |||
1359 | seg = seg->edge_next; |
||
1360 | |||
1361 | } while ( seg != edge->first ); |
||
1362 | |||
1363 | /* set the round/straight flags */ |
||
1364 | edge->flags = AF_EDGE_NORMAL; |
||
1365 | |||
1366 | if ( is_round > 0 && is_round >= is_straight ) |
||
1367 | edge->flags |= AF_EDGE_ROUND; |
||
1368 | |||
1369 | #if 0 |
||
1370 | /* set the edge's main direction */ |
||
1371 | edge->dir = AF_DIR_NONE; |
||
1372 | |||
1373 | if ( ups > downs ) |
||
1374 | edge->dir = (FT_Char)up_dir; |
||
1375 | |||
1376 | else if ( ups < downs ) |
||
1377 | edge->dir = (FT_Char)-up_dir; |
||
1378 | |||
1379 | else if ( ups == downs ) |
||
1380 | edge->dir = 0; /* both up and down! */ |
||
1381 | #endif |
||
1382 | |||
1383 | /* get rid of serifs if link is set */ |
||
1384 | /* XXX: This gets rid of many unpleasant artefacts! */ |
||
1385 | /* Example: the `c' in cour.pfa at size 13 */ |
||
1386 | |||
1387 | if ( edge->serif && edge->link ) |
||
1388 | edge->serif = 0; |
||
1389 | } |
||
1390 | } |
||
1391 | |||
1392 | Exit: |
||
1393 | return error; |
||
1394 | } |
||
1395 | |||
1396 | |||
1397 | /* Detect segments and edges for given dimension. */ |
||
1398 | |||
1399 | FT_LOCAL_DEF( FT_Error ) |
||
1400 | af_latin_hints_detect_features( AF_GlyphHints hints, |
||
1401 | AF_Dimension dim ) |
||
1402 | { |
||
1403 | FT_Error error; |
||
1404 | |||
1405 | |||
1406 | error = af_latin_hints_compute_segments( hints, dim ); |
||
1407 | if ( !error ) |
||
1408 | { |
||
1409 | af_latin_hints_link_segments( hints, dim ); |
||
1410 | |||
1411 | error = af_latin_hints_compute_edges( hints, dim ); |
||
1412 | } |
||
1413 | |||
1414 | return error; |
||
1415 | } |
||
1416 | |||
1417 | |||
1418 | /* Compute all edges which lie within blue zones. */ |
||
1419 | |||
1420 | FT_LOCAL_DEF( void ) |
||
1421 | af_latin_hints_compute_blue_edges( AF_GlyphHints hints, |
||
1422 | AF_LatinMetrics metrics ) |
||
1423 | { |
||
1424 | AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT]; |
||
1425 | AF_Edge edge = axis->edges; |
||
1426 | AF_Edge edge_limit = edge + axis->num_edges; |
||
1427 | AF_LatinAxis latin = &metrics->axis[AF_DIMENSION_VERT]; |
||
1428 | FT_Fixed scale = latin->scale; |
||
1429 | |||
1430 | |||
1431 | /* compute which blue zones are active, i.e. have their scaled */ |
||
1432 | /* size < 3/4 pixels */ |
||
1433 | |||
1434 | /* for each horizontal edge search the blue zone which is closest */ |
||
1435 | for ( ; edge < edge_limit; edge++ ) |
||
1436 | { |
||
1437 | FT_UInt bb; |
||
1438 | AF_Width best_blue = NULL; |
||
1439 | FT_Pos best_dist; /* initial threshold */ |
||
1440 | |||
1441 | |||
1442 | /* compute the initial threshold as a fraction of the EM size */ |
||
1443 | /* (the value 40 is heuristic) */ |
||
1444 | best_dist = FT_MulFix( metrics->units_per_em / 40, scale ); |
||
1445 | |||
1446 | /* assure a minimum distance of 0.5px */ |
||
1447 | if ( best_dist > 64 / 2 ) |
||
1448 | best_dist = 64 / 2; |
||
1449 | |||
1450 | for ( bb = 0; bb < latin->blue_count; bb++ ) |
||
1451 | { |
||
1452 | AF_LatinBlue blue = latin->blues + bb; |
||
1453 | FT_Bool is_top_blue, is_major_dir; |
||
1454 | |||
1455 | |||
1456 | /* skip inactive blue zones (i.e., those that are too large) */ |
||
1457 | if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) ) |
||
1458 | continue; |
||
1459 | |||
1460 | /* if it is a top zone, check for right edges -- if it is a bottom */ |
||
1461 | /* zone, check for left edges */ |
||
1462 | /* */ |
||
1463 | /* of course, that's for TrueType */ |
||
1464 | is_top_blue = (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 ); |
||
1465 | is_major_dir = FT_BOOL( edge->dir == axis->major_dir ); |
||
1466 | |||
1467 | /* if it is a top zone, the edge must be against the major */ |
||
1468 | /* direction; if it is a bottom zone, it must be in the major */ |
||
1469 | /* direction */ |
||
1470 | if ( is_top_blue ^ is_major_dir ) |
||
1471 | { |
||
1472 | FT_Pos dist; |
||
1473 | |||
1474 | |||
1475 | /* first of all, compare it to the reference position */ |
||
1476 | dist = edge->fpos - blue->ref.org; |
||
1477 | if ( dist < 0 ) |
||
1478 | dist = -dist; |
||
1479 | |||
1480 | dist = FT_MulFix( dist, scale ); |
||
1481 | if ( dist < best_dist ) |
||
1482 | { |
||
1483 | best_dist = dist; |
||
1484 | best_blue = &blue->ref; |
||
1485 | } |
||
1486 | |||
1487 | /* now compare it to the overshoot position and check whether */ |
||
1488 | /* the edge is rounded, and whether the edge is over the */ |
||
1489 | /* reference position of a top zone, or under the reference */ |
||
1490 | /* position of a bottom zone */ |
||
1491 | if ( edge->flags & AF_EDGE_ROUND && dist != 0 ) |
||
1492 | { |
||
1493 | FT_Bool is_under_ref = FT_BOOL( edge->fpos < blue->ref.org ); |
||
1494 | |||
1495 | |||
1496 | if ( is_top_blue ^ is_under_ref ) |
||
1497 | { |
||
1498 | dist = edge->fpos - blue->shoot.org; |
||
1499 | if ( dist < 0 ) |
||
1500 | dist = -dist; |
||
1501 | |||
1502 | dist = FT_MulFix( dist, scale ); |
||
1503 | if ( dist < best_dist ) |
||
1504 | { |
||
1505 | best_dist = dist; |
||
1506 | best_blue = &blue->shoot; |
||
1507 | } |
||
1508 | } |
||
1509 | } |
||
1510 | } |
||
1511 | } |
||
1512 | |||
1513 | if ( best_blue ) |
||
1514 | edge->blue_edge = best_blue; |
||
1515 | } |
||
1516 | } |
||
1517 | |||
1518 | |||
1519 | /* Initalize hinting engine. */ |
||
1520 | |||
1521 | static FT_Error |
||
1522 | af_latin_hints_init( AF_GlyphHints hints, |
||
1523 | AF_LatinMetrics metrics ) |
||
1524 | { |
||
1525 | FT_Render_Mode mode; |
||
1526 | FT_UInt32 scaler_flags, other_flags; |
||
1527 | FT_Face face = metrics->root.scaler.face; |
||
1528 | |||
1529 | |||
1530 | af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics ); |
||
1531 | |||
1532 | /* |
||
1533 | * correct x_scale and y_scale if needed, since they may have |
||
1534 | * been modified by `af_latin_metrics_scale_dim' above |
||
1535 | */ |
||
1536 | hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale; |
||
1537 | hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta; |
||
1538 | hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale; |
||
1539 | hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta; |
||
1540 | |||
1541 | /* compute flags depending on render mode, etc. */ |
||
1542 | mode = metrics->root.scaler.render_mode; |
||
1543 | |||
1544 | #if 0 /* #ifdef AF_CONFIG_OPTION_USE_WARPER */ |
||
1545 | if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V ) |
||
1546 | metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL; |
||
1547 | #endif |
||
1548 | |||
1549 | scaler_flags = hints->scaler_flags; |
||
1550 | other_flags = 0; |
||
1551 | |||
1552 | /* |
||
1553 | * We snap the width of vertical stems for the monochrome and |
||
1554 | * horizontal LCD rendering targets only. |
||
1555 | */ |
||
1556 | if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD ) |
||
1557 | other_flags |= AF_LATIN_HINTS_HORZ_SNAP; |
||
1558 | |||
1559 | /* |
||
1560 | * We snap the width of horizontal stems for the monochrome and |
||
1561 | * vertical LCD rendering targets only. |
||
1562 | */ |
||
1563 | if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V ) |
||
1564 | other_flags |= AF_LATIN_HINTS_VERT_SNAP; |
||
1565 | |||
1566 | /* |
||
1567 | * We adjust stems to full pixels only if we don't use the `light' mode. |
||
1568 | */ |
||
1569 | if ( mode != FT_RENDER_MODE_LIGHT ) |
||
1570 | other_flags |= AF_LATIN_HINTS_STEM_ADJUST; |
||
1571 | |||
1572 | if ( mode == FT_RENDER_MODE_MONO ) |
||
1573 | other_flags |= AF_LATIN_HINTS_MONO; |
||
1574 | |||
1575 | /* |
||
1576 | * In `light' hinting mode we disable horizontal hinting completely. |
||
1577 | * We also do it if the face is italic. |
||
1578 | */ |
||
1579 | if ( mode == FT_RENDER_MODE_LIGHT || |
||
1580 | ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 ) |
||
1581 | scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL; |
||
1582 | |||
1583 | hints->scaler_flags = scaler_flags; |
||
1584 | hints->other_flags = other_flags; |
||
1585 | |||
1586 | return FT_Err_Ok; |
||
1587 | } |
||
1588 | |||
1589 | |||
1590 | /*************************************************************************/ |
||
1591 | /*************************************************************************/ |
||
1592 | /***** *****/ |
||
1593 | /***** L A T I N G L Y P H G R I D - F I T T I N G *****/ |
||
1594 | /***** *****/ |
||
1595 | /*************************************************************************/ |
||
1596 | /*************************************************************************/ |
||
1597 | |||
1598 | /* Snap a given width in scaled coordinates to one of the */ |
||
1599 | /* current standard widths. */ |
||
1600 | |||
1601 | static FT_Pos |
||
1602 | af_latin_snap_width( AF_Width widths, |
||
1603 | FT_Int count, |
||
1604 | FT_Pos width ) |
||
1605 | { |
||
1606 | int n; |
||
1607 | FT_Pos best = 64 + 32 + 2; |
||
1608 | FT_Pos reference = width; |
||
1609 | FT_Pos scaled; |
||
1610 | |||
1611 | |||
1612 | for ( n = 0; n < count; n++ ) |
||
1613 | { |
||
1614 | FT_Pos w; |
||
1615 | FT_Pos dist; |
||
1616 | |||
1617 | |||
1618 | w = widths[n].cur; |
||
1619 | dist = width - w; |
||
1620 | if ( dist < 0 ) |
||
1621 | dist = -dist; |
||
1622 | if ( dist < best ) |
||
1623 | { |
||
1624 | best = dist; |
||
1625 | reference = w; |
||
1626 | } |
||
1627 | } |
||
1628 | |||
1629 | scaled = FT_PIX_ROUND( reference ); |
||
1630 | |||
1631 | if ( width >= reference ) |
||
1632 | { |
||
1633 | if ( width < scaled + 48 ) |
||
1634 | width = reference; |
||
1635 | } |
||
1636 | else |
||
1637 | { |
||
1638 | if ( width > scaled - 48 ) |
||
1639 | width = reference; |
||
1640 | } |
||
1641 | |||
1642 | return width; |
||
1643 | } |
||
1644 | |||
1645 | |||
1646 | /* Compute the snapped width of a given stem, ignoring very thin ones. */ |
||
1647 | /* There is a lot of voodoo in this function; changing the hard-coded */ |
||
1648 | /* parameters influence the whole hinting process. */ |
||
1649 | |||
1650 | static FT_Pos |
||
1651 | af_latin_compute_stem_width( AF_GlyphHints hints, |
||
1652 | AF_Dimension dim, |
||
1653 | FT_Pos width, |
||
1654 | AF_Edge_Flags base_flags, |
||
1655 | AF_Edge_Flags stem_flags ) |
||
1656 | { |
||
1657 | AF_LatinMetrics metrics = (AF_LatinMetrics) hints->metrics; |
||
1658 | AF_LatinAxis axis = & metrics->axis[dim]; |
||
1659 | FT_Pos dist = width; |
||
1660 | FT_Int sign = 0; |
||
1661 | FT_Int vertical = ( dim == AF_DIMENSION_VERT ); |
||
1662 | |||
1663 | |||
1664 | if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) || |
||
1665 | axis->extra_light ) |
||
1666 | return width; |
||
1667 | |||
1668 | if ( dist < 0 ) |
||
1669 | { |
||
1670 | dist = -width; |
||
1671 | sign = 1; |
||
1672 | } |
||
1673 | |||
1674 | if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || |
||
1675 | ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ) |
||
1676 | { |
||
1677 | /* smooth hinting process: very lightly quantize the stem width */ |
||
1678 | |||
1679 | /* leave the widths of serifs alone */ |
||
1680 | if ( ( stem_flags & AF_EDGE_SERIF ) && |
||
1681 | vertical && |
||
1682 | ( dist < 3 * 64 ) ) |
||
1683 | goto Done_Width; |
||
1684 | |||
1685 | else if ( base_flags & AF_EDGE_ROUND ) |
||
1686 | { |
||
1687 | if ( dist < 80 ) |
||
1688 | dist = 64; |
||
1689 | } |
||
1690 | else if ( dist < 56 ) |
||
1691 | dist = 56; |
||
1692 | |||
1693 | if ( axis->width_count > 0 ) |
||
1694 | { |
||
1695 | FT_Pos delta; |
||
1696 | |||
1697 | |||
1698 | /* compare to standard width */ |
||
1699 | delta = dist - axis->widths[0].cur; |
||
1700 | |||
1701 | if ( delta < 0 ) |
||
1702 | delta = -delta; |
||
1703 | |||
1704 | if ( delta < 40 ) |
||
1705 | { |
||
1706 | dist = axis->widths[0].cur; |
||
1707 | if ( dist < 48 ) |
||
1708 | dist = 48; |
||
1709 | |||
1710 | goto Done_Width; |
||
1711 | } |
||
1712 | |||
1713 | if ( dist < 3 * 64 ) |
||
1714 | { |
||
1715 | delta = dist & 63; |
||
1716 | dist &= -64; |
||
1717 | |||
1718 | if ( delta < 10 ) |
||
1719 | dist += delta; |
||
1720 | |||
1721 | else if ( delta < 32 ) |
||
1722 | dist += 10; |
||
1723 | |||
1724 | else if ( delta < 54 ) |
||
1725 | dist += 54; |
||
1726 | |||
1727 | else |
||
1728 | dist += delta; |
||
1729 | } |
||
1730 | else |
||
1731 | dist = ( dist + 32 ) & ~63; |
||
1732 | } |
||
1733 | } |
||
1734 | else |
||
1735 | { |
||
1736 | /* strong hinting process: snap the stem width to integer pixels */ |
||
1737 | |||
1738 | FT_Pos org_dist = dist; |
||
1739 | |||
1740 | |||
1741 | dist = af_latin_snap_width( axis->widths, axis->width_count, dist ); |
||
1742 | |||
1743 | if ( vertical ) |
||
1744 | { |
||
1745 | /* in the case of vertical hinting, always round */ |
||
1746 | /* the stem heights to integer pixels */ |
||
1747 | |||
1748 | if ( dist >= 64 ) |
||
1749 | dist = ( dist + 16 ) & ~63; |
||
1750 | else |
||
1751 | dist = 64; |
||
1752 | } |
||
1753 | else |
||
1754 | { |
||
1755 | if ( AF_LATIN_HINTS_DO_MONO( hints ) ) |
||
1756 | { |
||
1757 | /* monochrome horizontal hinting: snap widths to integer pixels */ |
||
1758 | /* with a different threshold */ |
||
1759 | |||
1760 | if ( dist < 64 ) |
||
1761 | dist = 64; |
||
1762 | else |
||
1763 | dist = ( dist + 32 ) & ~63; |
||
1764 | } |
||
1765 | else |
||
1766 | { |
||
1767 | /* for horizontal anti-aliased hinting, we adopt a more subtle */ |
||
1768 | /* approach: we strengthen small stems, round stems whose size */ |
||
1769 | /* is between 1 and 2 pixels to an integer, otherwise nothing */ |
||
1770 | |||
1771 | if ( dist < 48 ) |
||
1772 | dist = ( dist + 64 ) >> 1; |
||
1773 | |||
1774 | else if ( dist < 128 ) |
||
1775 | { |
||
1776 | /* We only round to an integer width if the corresponding */ |
||
1777 | /* distortion is less than 1/4 pixel. Otherwise this */ |
||
1778 | /* makes everything worse since the diagonals, which are */ |
||
1779 | /* not hinted, appear a lot bolder or thinner than the */ |
||
1780 | /* vertical stems. */ |
||
1781 | |||
1782 | FT_Pos delta; |
||
1783 | |||
1784 | |||
1785 | dist = ( dist + 22 ) & ~63; |
||
1786 | delta = dist - org_dist; |
||
1787 | if ( delta < 0 ) |
||
1788 | delta = -delta; |
||
1789 | |||
1790 | if ( delta >= 16 ) |
||
1791 | { |
||
1792 | dist = org_dist; |
||
1793 | if ( dist < 48 ) |
||
1794 | dist = ( dist + 64 ) >> 1; |
||
1795 | } |
||
1796 | } |
||
1797 | else |
||
1798 | /* round otherwise to prevent color fringes in LCD mode */ |
||
1799 | dist = ( dist + 32 ) & ~63; |
||
1800 | } |
||
1801 | } |
||
1802 | } |
||
1803 | |||
1804 | Done_Width: |
||
1805 | if ( sign ) |
||
1806 | dist = -dist; |
||
1807 | |||
1808 | return dist; |
||
1809 | } |
||
1810 | |||
1811 | |||
1812 | /* Align one stem edge relative to the previous stem edge. */ |
||
1813 | |||
1814 | static void |
||
1815 | af_latin_align_linked_edge( AF_GlyphHints hints, |
||
1816 | AF_Dimension dim, |
||
1817 | AF_Edge base_edge, |
||
1818 | AF_Edge stem_edge ) |
||
1819 | { |
||
1820 | FT_Pos dist = stem_edge->opos - base_edge->opos; |
||
1821 | |||
1822 | FT_Pos fitted_width = af_latin_compute_stem_width( |
||
1823 | hints, dim, dist, |
||
1824 | (AF_Edge_Flags)base_edge->flags, |
||
1825 | (AF_Edge_Flags)stem_edge->flags ); |
||
1826 | |||
1827 | |||
1828 | stem_edge->pos = base_edge->pos + fitted_width; |
||
1829 | |||
1830 | FT_TRACE5(( " LINK: edge %d (opos=%.2f) linked to %.2f," |
||
1831 | " dist was %.2f, now %.2f\n", |
||
1832 | stem_edge-hints->axis[dim].edges, stem_edge->opos / 64.0, |
||
1833 | stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 )); |
||
1834 | } |
||
1835 | |||
1836 | |||
1837 | /* Shift the coordinates of the `serif' edge by the same amount */ |
||
1838 | /* as the corresponding `base' edge has been moved already. */ |
||
1839 | |||
1840 | static void |
||
1841 | af_latin_align_serif_edge( AF_GlyphHints hints, |
||
1842 | AF_Edge base, |
||
1843 | AF_Edge serif ) |
||
1844 | { |
||
1845 | FT_UNUSED( hints ); |
||
1846 | |||
1847 | serif->pos = base->pos + ( serif->opos - base->opos ); |
||
1848 | } |
||
1849 | |||
1850 | |||
1851 | /*************************************************************************/ |
||
1852 | /*************************************************************************/ |
||
1853 | /*************************************************************************/ |
||
1854 | /**** ****/ |
||
1855 | /**** E D G E H I N T I N G ****/ |
||
1856 | /**** ****/ |
||
1857 | /*************************************************************************/ |
||
1858 | /*************************************************************************/ |
||
1859 | /*************************************************************************/ |
||
1860 | |||
1861 | |||
1862 | /* The main grid-fitting routine. */ |
||
1863 | |||
1864 | FT_LOCAL_DEF( void ) |
||
1865 | af_latin_hint_edges( AF_GlyphHints hints, |
||
1866 | AF_Dimension dim ) |
||
1867 | { |
||
1868 | AF_AxisHints axis = &hints->axis[dim]; |
||
1869 | AF_Edge edges = axis->edges; |
||
1870 | AF_Edge edge_limit = edges + axis->num_edges; |
||
1871 | FT_PtrDist n_edges; |
||
1872 | AF_Edge edge; |
||
1873 | AF_Edge anchor = NULL; |
||
1874 | FT_Int has_serifs = 0; |
||
1875 | |||
1876 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
1877 | FT_UInt num_actions = 0; |
||
1878 | #endif |
||
1879 | |||
1880 | |||
1881 | FT_TRACE5(( "%s edge hinting\n", |
||
1882 | dim == AF_DIMENSION_VERT ? "horizontal" : "vertical" )); |
||
1883 | |||
1884 | /* we begin by aligning all stems relative to the blue zone */ |
||
1885 | /* if needed -- that's only for horizontal edges */ |
||
1886 | |||
1887 | if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) ) |
||
1888 | { |
||
1889 | for ( edge = edges; edge < edge_limit; edge++ ) |
||
1890 | { |
||
1891 | AF_Width blue; |
||
1892 | AF_Edge edge1, edge2; /* these edges form the stem to check */ |
||
1893 | |||
1894 | |||
1895 | if ( edge->flags & AF_EDGE_DONE ) |
||
1896 | continue; |
||
1897 | |||
1898 | blue = edge->blue_edge; |
||
1899 | edge1 = NULL; |
||
1900 | edge2 = edge->link; |
||
1901 | |||
1902 | if ( blue ) |
||
1903 | edge1 = edge; |
||
1904 | |||
1905 | /* flip edges if the other stem is aligned to a blue zone */ |
||
1906 | else if ( edge2 && edge2->blue_edge ) |
||
1907 | { |
||
1908 | blue = edge2->blue_edge; |
||
1909 | edge1 = edge2; |
||
1910 | edge2 = edge; |
||
1911 | } |
||
1912 | |||
1913 | if ( !edge1 ) |
||
1914 | continue; |
||
1915 | |||
1916 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
1917 | if ( !anchor ) |
||
1918 | FT_TRACE5(( " BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f," |
||
1919 | " was %.2f (anchor=edge %d)\n", |
||
1920 | edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0, |
||
1921 | edge1->pos / 64.0, edge - edges )); |
||
1922 | else |
||
1923 | FT_TRACE5(( " BLUE: edge %d (opos=%.2f) snapped to %.2f," |
||
1924 | " was %.2f\n", |
||
1925 | edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0, |
||
1926 | edge1->pos / 64.0 )); |
||
1927 | |||
1928 | num_actions++; |
||
1929 | #endif |
||
1930 | |||
1931 | edge1->pos = blue->fit; |
||
1932 | edge1->flags |= AF_EDGE_DONE; |
||
1933 | |||
1934 | if ( edge2 && !edge2->blue_edge ) |
||
1935 | { |
||
1936 | af_latin_align_linked_edge( hints, dim, edge1, edge2 ); |
||
1937 | edge2->flags |= AF_EDGE_DONE; |
||
1938 | |||
1939 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
1940 | num_actions++; |
||
1941 | #endif |
||
1942 | } |
||
1943 | |||
1944 | if ( !anchor ) |
||
1945 | anchor = edge; |
||
1946 | } |
||
1947 | } |
||
1948 | |||
1949 | /* now we align all other stem edges, trying to maintain the */ |
||
1950 | /* relative order of stems in the glyph */ |
||
1951 | for ( edge = edges; edge < edge_limit; edge++ ) |
||
1952 | { |
||
1953 | AF_Edge edge2; |
||
1954 | |||
1955 | |||
1956 | if ( edge->flags & AF_EDGE_DONE ) |
||
1957 | continue; |
||
1958 | |||
1959 | /* skip all non-stem edges */ |
||
1960 | edge2 = edge->link; |
||
1961 | if ( !edge2 ) |
||
1962 | { |
||
1963 | has_serifs++; |
||
1964 | continue; |
||
1965 | } |
||
1966 | |||
1967 | /* now align the stem */ |
||
1968 | |||
1969 | /* this should not happen, but it's better to be safe */ |
||
1970 | if ( edge2->blue_edge ) |
||
1971 | { |
||
1972 | FT_TRACE5(( " ASSERTION FAILED for edge %d\n", edge2-edges )); |
||
1973 | |||
1974 | af_latin_align_linked_edge( hints, dim, edge2, edge ); |
||
1975 | edge->flags |= AF_EDGE_DONE; |
||
1976 | |||
1977 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
1978 | num_actions++; |
||
1979 | #endif |
||
1980 | continue; |
||
1981 | } |
||
1982 | |||
1983 | if ( !anchor ) |
||
1984 | { |
||
1985 | /* if we reach this if clause, no stem has been aligned yet */ |
||
1986 | |||
1987 | FT_Pos org_len, org_center, cur_len; |
||
1988 | FT_Pos cur_pos1, error1, error2, u_off, d_off; |
||
1989 | |||
1990 | |||
1991 | org_len = edge2->opos - edge->opos; |
||
1992 | cur_len = af_latin_compute_stem_width( |
||
1993 | hints, dim, org_len, |
||
1994 | (AF_Edge_Flags)edge->flags, |
||
1995 | (AF_Edge_Flags)edge2->flags ); |
||
1996 | |||
1997 | /* some voodoo to specially round edges for small stem widths; */ |
||
1998 | /* the idea is to align the center of a stem, then shifting */ |
||
1999 | /* the stem edges to suitable positions */ |
||
2000 | if ( cur_len <= 64 ) |
||
2001 | { |
||
2002 | /* width <= 1px */ |
||
2003 | u_off = 32; |
||
2004 | d_off = 32; |
||
2005 | } |
||
2006 | else |
||
2007 | { |
||
2008 | /* 1px < width < 1.5px */ |
||
2009 | u_off = 38; |
||
2010 | d_off = 26; |
||
2011 | } |
||
2012 | |||
2013 | if ( cur_len < 96 ) |
||
2014 | { |
||
2015 | org_center = edge->opos + ( org_len >> 1 ); |
||
2016 | cur_pos1 = FT_PIX_ROUND( org_center ); |
||
2017 | |||
2018 | error1 = org_center - ( cur_pos1 - u_off ); |
||
2019 | if ( error1 < 0 ) |
||
2020 | error1 = -error1; |
||
2021 | |||
2022 | error2 = org_center - ( cur_pos1 + d_off ); |
||
2023 | if ( error2 < 0 ) |
||
2024 | error2 = -error2; |
||
2025 | |||
2026 | if ( error1 < error2 ) |
||
2027 | cur_pos1 -= u_off; |
||
2028 | else |
||
2029 | cur_pos1 += d_off; |
||
2030 | |||
2031 | edge->pos = cur_pos1 - cur_len / 2; |
||
2032 | edge2->pos = edge->pos + cur_len; |
||
2033 | } |
||
2034 | else |
||
2035 | edge->pos = FT_PIX_ROUND( edge->opos ); |
||
2036 | |||
2037 | anchor = edge; |
||
2038 | edge->flags |= AF_EDGE_DONE; |
||
2039 | |||
2040 | FT_TRACE5(( " ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)" |
||
2041 | " snapped to %.2f and %.2f\n", |
||
2042 | edge - edges, edge->opos / 64.0, |
||
2043 | edge2 - edges, edge2->opos / 64.0, |
||
2044 | edge->pos / 64.0, edge2->pos / 64.0 )); |
||
2045 | |||
2046 | af_latin_align_linked_edge( hints, dim, edge, edge2 ); |
||
2047 | |||
2048 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
2049 | num_actions += 2; |
||
2050 | #endif |
||
2051 | } |
||
2052 | else |
||
2053 | { |
||
2054 | FT_Pos org_pos, org_len, org_center, cur_len; |
||
2055 | FT_Pos cur_pos1, cur_pos2, delta1, delta2; |
||
2056 | |||
2057 | |||
2058 | org_pos = anchor->pos + ( edge->opos - anchor->opos ); |
||
2059 | org_len = edge2->opos - edge->opos; |
||
2060 | org_center = org_pos + ( org_len >> 1 ); |
||
2061 | |||
2062 | cur_len = af_latin_compute_stem_width( |
||
2063 | hints, dim, org_len, |
||
2064 | (AF_Edge_Flags)edge->flags, |
||
2065 | (AF_Edge_Flags)edge2->flags ); |
||
2066 | |||
2067 | if ( edge2->flags & AF_EDGE_DONE ) |
||
2068 | { |
||
2069 | FT_TRACE5(( " ADJUST: edge %d (pos=%.2f) moved to %.2f\n", |
||
2070 | edge - edges, edge->pos / 64.0, |
||
2071 | ( edge2->pos - cur_len ) / 64.0 )); |
||
2072 | |||
2073 | edge->pos = edge2->pos - cur_len; |
||
2074 | } |
||
2075 | |||
2076 | else if ( cur_len < 96 ) |
||
2077 | { |
||
2078 | FT_Pos u_off, d_off; |
||
2079 | |||
2080 | |||
2081 | cur_pos1 = FT_PIX_ROUND( org_center ); |
||
2082 | |||
2083 | if ( cur_len <= 64 ) |
||
2084 | { |
||
2085 | u_off = 32; |
||
2086 | d_off = 32; |
||
2087 | } |
||
2088 | else |
||
2089 | { |
||
2090 | u_off = 38; |
||
2091 | d_off = 26; |
||
2092 | } |
||
2093 | |||
2094 | delta1 = org_center - ( cur_pos1 - u_off ); |
||
2095 | if ( delta1 < 0 ) |
||
2096 | delta1 = -delta1; |
||
2097 | |||
2098 | delta2 = org_center - ( cur_pos1 + d_off ); |
||
2099 | if ( delta2 < 0 ) |
||
2100 | delta2 = -delta2; |
||
2101 | |||
2102 | if ( delta1 < delta2 ) |
||
2103 | cur_pos1 -= u_off; |
||
2104 | else |
||
2105 | cur_pos1 += d_off; |
||
2106 | |||
2107 | edge->pos = cur_pos1 - cur_len / 2; |
||
2108 | edge2->pos = cur_pos1 + cur_len / 2; |
||
2109 | |||
2110 | FT_TRACE5(( " STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)" |
||
2111 | " snapped to %.2f and %.2f\n", |
||
2112 | edge - edges, edge->opos / 64.0, |
||
2113 | edge2 - edges, edge2->opos / 64.0, |
||
2114 | edge->pos / 64.0, edge2->pos / 64.0 )); |
||
2115 | } |
||
2116 | |||
2117 | else |
||
2118 | { |
||
2119 | org_pos = anchor->pos + ( edge->opos - anchor->opos ); |
||
2120 | org_len = edge2->opos - edge->opos; |
||
2121 | org_center = org_pos + ( org_len >> 1 ); |
||
2122 | |||
2123 | cur_len = af_latin_compute_stem_width( |
||
2124 | hints, dim, org_len, |
||
2125 | (AF_Edge_Flags)edge->flags, |
||
2126 | (AF_Edge_Flags)edge2->flags ); |
||
2127 | |||
2128 | cur_pos1 = FT_PIX_ROUND( org_pos ); |
||
2129 | delta1 = cur_pos1 + ( cur_len >> 1 ) - org_center; |
||
2130 | if ( delta1 < 0 ) |
||
2131 | delta1 = -delta1; |
||
2132 | |||
2133 | cur_pos2 = FT_PIX_ROUND( org_pos + org_len ) - cur_len; |
||
2134 | delta2 = cur_pos2 + ( cur_len >> 1 ) - org_center; |
||
2135 | if ( delta2 < 0 ) |
||
2136 | delta2 = -delta2; |
||
2137 | |||
2138 | edge->pos = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2; |
||
2139 | edge2->pos = edge->pos + cur_len; |
||
2140 | |||
2141 | FT_TRACE5(( " STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)" |
||
2142 | " snapped to %.2f and %.2f\n", |
||
2143 | edge - edges, edge->opos / 64.0, |
||
2144 | edge2 - edges, edge2->opos / 64.0, |
||
2145 | edge->pos / 64.0, edge2->pos / 64.0 )); |
||
2146 | } |
||
2147 | |||
2148 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
2149 | num_actions++; |
||
2150 | #endif |
||
2151 | |||
2152 | edge->flags |= AF_EDGE_DONE; |
||
2153 | edge2->flags |= AF_EDGE_DONE; |
||
2154 | |||
2155 | if ( edge > edges && edge->pos < edge[-1].pos ) |
||
2156 | { |
||
2157 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
2158 | FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n", |
||
2159 | edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0 )); |
||
2160 | |||
2161 | num_actions++; |
||
2162 | #endif |
||
2163 | |||
2164 | edge->pos = edge[-1].pos; |
||
2165 | } |
||
2166 | } |
||
2167 | } |
||
2168 | |||
2169 | /* make sure that lowercase m's maintain their symmetry */ |
||
2170 | |||
2171 | /* In general, lowercase m's have six vertical edges if they are sans */ |
||
2172 | /* serif, or twelve if they are with serifs. This implementation is */ |
||
2173 | /* based on that assumption, and seems to work very well with most */ |
||
2174 | /* faces. However, if for a certain face this assumption is not */ |
||
2175 | /* true, the m is just rendered like before. In addition, any stem */ |
||
2176 | /* correction will only be applied to symmetrical glyphs (even if the */ |
||
2177 | /* glyph is not an m), so the potential for unwanted distortion is */ |
||
2178 | /* relatively low. */ |
||
2179 | |||
2180 | /* We don't handle horizontal edges since we can't easily assure that */ |
||
2181 | /* the third (lowest) stem aligns with the base line; it might end up */ |
||
2182 | /* one pixel higher or lower. */ |
||
2183 | |||
2184 | n_edges = edge_limit - edges; |
||
2185 | if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) ) |
||
2186 | { |
||
2187 | AF_Edge edge1, edge2, edge3; |
||
2188 | FT_Pos dist1, dist2, span, delta; |
||
2189 | |||
2190 | |||
2191 | if ( n_edges == 6 ) |
||
2192 | { |
||
2193 | edge1 = edges; |
||
2194 | edge2 = edges + 2; |
||
2195 | edge3 = edges + 4; |
||
2196 | } |
||
2197 | else |
||
2198 | { |
||
2199 | edge1 = edges + 1; |
||
2200 | edge2 = edges + 5; |
||
2201 | edge3 = edges + 9; |
||
2202 | } |
||
2203 | |||
2204 | dist1 = edge2->opos - edge1->opos; |
||
2205 | dist2 = edge3->opos - edge2->opos; |
||
2206 | |||
2207 | span = dist1 - dist2; |
||
2208 | if ( span < 0 ) |
||
2209 | span = -span; |
||
2210 | |||
2211 | if ( span < 8 ) |
||
2212 | { |
||
2213 | delta = edge3->pos - ( 2 * edge2->pos - edge1->pos ); |
||
2214 | edge3->pos -= delta; |
||
2215 | if ( edge3->link ) |
||
2216 | edge3->link->pos -= delta; |
||
2217 | |||
2218 | /* move the serifs along with the stem */ |
||
2219 | if ( n_edges == 12 ) |
||
2220 | { |
||
2221 | ( edges + 8 )->pos -= delta; |
||
2222 | ( edges + 11 )->pos -= delta; |
||
2223 | } |
||
2224 | |||
2225 | edge3->flags |= AF_EDGE_DONE; |
||
2226 | if ( edge3->link ) |
||
2227 | edge3->link->flags |= AF_EDGE_DONE; |
||
2228 | } |
||
2229 | } |
||
2230 | |||
2231 | if ( has_serifs || !anchor ) |
||
2232 | { |
||
2233 | /* |
||
2234 | * now hint the remaining edges (serifs and single) in order |
||
2235 | * to complete our processing |
||
2236 | */ |
||
2237 | for ( edge = edges; edge < edge_limit; edge++ ) |
||
2238 | { |
||
2239 | FT_Pos delta; |
||
2240 | |||
2241 | |||
2242 | if ( edge->flags & AF_EDGE_DONE ) |
||
2243 | continue; |
||
2244 | |||
2245 | delta = 1000; |
||
2246 | |||
2247 | if ( edge->serif ) |
||
2248 | { |
||
2249 | delta = edge->serif->opos - edge->opos; |
||
2250 | if ( delta < 0 ) |
||
2251 | delta = -delta; |
||
2252 | } |
||
2253 | |||
2254 | if ( delta < 64 + 16 ) |
||
2255 | { |
||
2256 | af_latin_align_serif_edge( hints, edge->serif, edge ); |
||
2257 | FT_TRACE5(( " SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)" |
||
2258 | " aligned to %.2f\n", |
||
2259 | edge - edges, edge->opos / 64.0, |
||
2260 | edge->serif - edges, edge->serif->opos / 64.0, |
||
2261 | edge->pos / 64.0 )); |
||
2262 | } |
||
2263 | else if ( !anchor ) |
||
2264 | { |
||
2265 | edge->pos = FT_PIX_ROUND( edge->opos ); |
||
2266 | anchor = edge; |
||
2267 | FT_TRACE5(( " SERIF_ANCHOR: edge %d (opos=%.2f)" |
||
2268 | " snapped to %.2f\n", |
||
2269 | edge-edges, edge->opos / 64.0, edge->pos / 64.0 )); |
||
2270 | } |
||
2271 | else |
||
2272 | { |
||
2273 | AF_Edge before, after; |
||
2274 | |||
2275 | |||
2276 | for ( before = edge - 1; before >= edges; before-- ) |
||
2277 | if ( before->flags & AF_EDGE_DONE ) |
||
2278 | break; |
||
2279 | |||
2280 | for ( after = edge + 1; after < edge_limit; after++ ) |
||
2281 | if ( after->flags & AF_EDGE_DONE ) |
||
2282 | break; |
||
2283 | |||
2284 | if ( before >= edges && before < edge && |
||
2285 | after < edge_limit && after > edge ) |
||
2286 | { |
||
2287 | if ( after->opos == before->opos ) |
||
2288 | edge->pos = before->pos; |
||
2289 | else |
||
2290 | edge->pos = before->pos + |
||
2291 | FT_MulDiv( edge->opos - before->opos, |
||
2292 | after->pos - before->pos, |
||
2293 | after->opos - before->opos ); |
||
2294 | |||
2295 | FT_TRACE5(( " SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f" |
||
2296 | " from %d (opos=%.2f)\n", |
||
2297 | edge - edges, edge->opos / 64.0, |
||
2298 | edge->pos / 64.0, |
||
2299 | before - edges, before->opos / 64.0 )); |
||
2300 | } |
||
2301 | else |
||
2302 | { |
||
2303 | edge->pos = anchor->pos + |
||
2304 | ( ( edge->opos - anchor->opos + 16 ) & ~31 ); |
||
2305 | FT_TRACE5(( " SERIF_LINK2: edge %d (opos=%.2f)" |
||
2306 | " snapped to %.2f\n", |
||
2307 | edge - edges, edge->opos / 64.0, edge->pos / 64.0 )); |
||
2308 | } |
||
2309 | } |
||
2310 | |||
2311 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
2312 | num_actions++; |
||
2313 | #endif |
||
2314 | edge->flags |= AF_EDGE_DONE; |
||
2315 | |||
2316 | if ( edge > edges && edge->pos < edge[-1].pos ) |
||
2317 | { |
||
2318 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
2319 | FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n", |
||
2320 | edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0 )); |
||
2321 | |||
2322 | num_actions++; |
||
2323 | #endif |
||
2324 | edge->pos = edge[-1].pos; |
||
2325 | } |
||
2326 | |||
2327 | if ( edge + 1 < edge_limit && |
||
2328 | edge[1].flags & AF_EDGE_DONE && |
||
2329 | edge->pos > edge[1].pos ) |
||
2330 | { |
||
2331 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
2332 | FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n", |
||
2333 | edge - edges, edge->pos / 64.0, edge[1].pos / 64.0 )); |
||
2334 | |||
2335 | num_actions++; |
||
2336 | #endif |
||
2337 | |||
2338 | edge->pos = edge[1].pos; |
||
2339 | } |
||
2340 | } |
||
2341 | } |
||
2342 | |||
2343 | #ifdef FT_DEBUG_LEVEL_TRACE |
||
2344 | if ( !num_actions ) |
||
2345 | FT_TRACE5(( " (none)\n" )); |
||
2346 | FT_TRACE5(( "\n" )); |
||
2347 | #endif |
||
2348 | } |
||
2349 | |||
2350 | |||
2351 | /* Apply the complete hinting algorithm to a latin glyph. */ |
||
2352 | |||
2353 | static FT_Error |
||
2354 | af_latin_hints_apply( AF_GlyphHints hints, |
||
2355 | FT_Outline* outline, |
||
2356 | AF_LatinMetrics metrics ) |
||
2357 | { |
||
2358 | FT_Error error; |
||
2359 | int dim; |
||
2360 | |||
2361 | |||
2362 | error = af_glyph_hints_reload( hints, outline ); |
||
2363 | if ( error ) |
||
2364 | goto Exit; |
||
2365 | |||
2366 | /* analyze glyph outline */ |
||
2367 | #ifdef AF_CONFIG_OPTION_USE_WARPER |
||
2368 | if ( metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT || |
||
2369 | AF_HINTS_DO_HORIZONTAL( hints ) ) |
||
2370 | #else |
||
2371 | if ( AF_HINTS_DO_HORIZONTAL( hints ) ) |
||
2372 | #endif |
||
2373 | { |
||
2374 | error = af_latin_hints_detect_features( hints, AF_DIMENSION_HORZ ); |
||
2375 | if ( error ) |
||
2376 | goto Exit; |
||
2377 | } |
||
2378 | |||
2379 | if ( AF_HINTS_DO_VERTICAL( hints ) ) |
||
2380 | { |
||
2381 | error = af_latin_hints_detect_features( hints, AF_DIMENSION_VERT ); |
||
2382 | if ( error ) |
||
2383 | goto Exit; |
||
2384 | |||
2385 | af_latin_hints_compute_blue_edges( hints, metrics ); |
||
2386 | } |
||
2387 | |||
2388 | /* grid-fit the outline */ |
||
2389 | for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) |
||
2390 | { |
||
2391 | #ifdef AF_CONFIG_OPTION_USE_WARPER |
||
2392 | if ( dim == AF_DIMENSION_HORZ && |
||
2393 | metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT ) |
||
2394 | { |
||
2395 | AF_WarperRec warper; |
||
2396 | FT_Fixed scale; |
||
2397 | FT_Pos delta; |
||
2398 | |||
2399 | |||
2400 | af_warper_compute( &warper, hints, (AF_Dimension)dim, |
||
2401 | &scale, &delta ); |
||
2402 | af_glyph_hints_scale_dim( hints, (AF_Dimension)dim, |
||
2403 | scale, delta ); |
||
2404 | continue; |
||
2405 | } |
||
2406 | #endif |
||
2407 | |||
2408 | if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) || |
||
2409 | ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) ) |
||
2410 | { |
||
2411 | af_latin_hint_edges( hints, (AF_Dimension)dim ); |
||
2412 | af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim ); |
||
2413 | af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim ); |
||
2414 | af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim ); |
||
2415 | } |
||
2416 | } |
||
2417 | af_glyph_hints_save( hints, outline ); |
||
2418 | |||
2419 | Exit: |
||
2420 | return error; |
||
2421 | } |
||
2422 | |||
2423 | |||
2424 | /*************************************************************************/ |
||
2425 | /*************************************************************************/ |
||
2426 | /***** *****/ |
||
2427 | /***** L A T I N S C R I P T C L A S S *****/ |
||
2428 | /***** *****/ |
||
2429 | /*************************************************************************/ |
||
2430 | /*************************************************************************/ |
||
2431 | |||
2432 | |||
2433 | /* XXX: this should probably fine tuned to differentiate better between */ |
||
2434 | /* scripts... */ |
||
2435 | |||
2436 | static const AF_Script_UniRangeRec af_latin_uniranges[] = |
||
2437 | { |
||
2438 | AF_UNIRANGE_REC( 0x0020UL, 0x007FUL ), /* Basic Latin (no control chars) */ |
||
2439 | AF_UNIRANGE_REC( 0x00A0UL, 0x00FFUL ), /* Latin-1 Supplement (no control chars) */ |
||
2440 | AF_UNIRANGE_REC( 0x0100UL, 0x017FUL ), /* Latin Extended-A */ |
||
2441 | AF_UNIRANGE_REC( 0x0180UL, 0x024FUL ), /* Latin Extended-B */ |
||
2442 | AF_UNIRANGE_REC( 0x0250UL, 0x02AFUL ), /* IPA Extensions */ |
||
2443 | AF_UNIRANGE_REC( 0x02B0UL, 0x02FFUL ), /* Spacing Modifier Letters */ |
||
2444 | AF_UNIRANGE_REC( 0x0300UL, 0x036FUL ), /* Combining Diacritical Marks */ |
||
2445 | AF_UNIRANGE_REC( 0x0370UL, 0x03FFUL ), /* Greek and Coptic */ |
||
2446 | AF_UNIRANGE_REC( 0x0400UL, 0x04FFUL ), /* Cyrillic */ |
||
2447 | AF_UNIRANGE_REC( 0x0500UL, 0x052FUL ), /* Cyrillic Supplement */ |
||
2448 | AF_UNIRANGE_REC( 0x1D00UL, 0x1D7FUL ), /* Phonetic Extensions */ |
||
2449 | AF_UNIRANGE_REC( 0x1D80UL, 0x1DBFUL ), /* Phonetic Extensions Supplement */ |
||
2450 | AF_UNIRANGE_REC( 0x1DC0UL, 0x1DFFUL ), /* Combining Diacritical Marks Supplement */ |
||
2451 | AF_UNIRANGE_REC( 0x1E00UL, 0x1EFFUL ), /* Latin Extended Additional */ |
||
2452 | AF_UNIRANGE_REC( 0x1F00UL, 0x1FFFUL ), /* Greek Extended */ |
||
2453 | AF_UNIRANGE_REC( 0x2000UL, 0x206FUL ), /* General Punctuation */ |
||
2454 | AF_UNIRANGE_REC( 0x2070UL, 0x209FUL ), /* Superscripts and Subscripts */ |
||
2455 | AF_UNIRANGE_REC( 0x20A0UL, 0x20CFUL ), /* Currency Symbols */ |
||
2456 | AF_UNIRANGE_REC( 0x2150UL, 0x218FUL ), /* Number Forms */ |
||
2457 | AF_UNIRANGE_REC( 0x2460UL, 0x24FFUL ), /* Enclosed Alphanumerics */ |
||
2458 | AF_UNIRANGE_REC( 0x2C60UL, 0x2C7FUL ), /* Latin Extended-C */ |
||
2459 | AF_UNIRANGE_REC( 0x2DE0UL, 0x2DFFUL ), /* Cyrillic Extended-A */ |
||
2460 | AF_UNIRANGE_REC( 0x2E00UL, 0x2E7FUL ), /* Supplemental Punctuation */ |
||
2461 | AF_UNIRANGE_REC( 0xA640UL, 0xA69FUL ), /* Cyrillic Extended-B */ |
||
2462 | AF_UNIRANGE_REC( 0xA720UL, 0xA7FFUL ), /* Latin Extended-D */ |
||
2463 | AF_UNIRANGE_REC( 0xFB00UL, 0xFB06UL ), /* Alphab. Present. Forms (Latin Ligs) */ |
||
2464 | AF_UNIRANGE_REC( 0x1D400UL, 0x1D7FFUL ), /* Mathematical Alphanumeric Symbols */ |
||
2465 | AF_UNIRANGE_REC( 0x1F100UL, 0x1F1FFUL ), /* Enclosed Alphanumeric Supplement */ |
||
2466 | AF_UNIRANGE_REC( 0UL, 0UL ) |
||
2467 | }; |
||
2468 | |||
2469 | |||
2470 | AF_DEFINE_SCRIPT_CLASS( af_latin_script_class, |
||
2471 | AF_SCRIPT_LATIN, |
||
2472 | af_latin_uniranges, |
||
2473 | 'o', |
||
2474 | |||
2475 | sizeof ( AF_LatinMetricsRec ), |
||
2476 | |||
2477 | (AF_Script_InitMetricsFunc) af_latin_metrics_init, |
||
2478 | (AF_Script_ScaleMetricsFunc)af_latin_metrics_scale, |
||
2479 | (AF_Script_DoneMetricsFunc) NULL, |
||
2480 | |||
2481 | (AF_Script_InitHintsFunc) af_latin_hints_init, |
||
2482 | (AF_Script_ApplyHintsFunc) af_latin_hints_apply |
||
2483 | ) |
||
2484 | |||
2485 | |||
2486 | /* END */>>>>>>>>>>>>>>>>>>=>>>>>>>>=>=>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>=>>>>>=>>>>>=>>=>>=>>>=>>>=>>=>=>>=>>>>>>>>>=> |