1 | /***************************************************************************/ |
---|
2 | /* */ |
---|
3 | /* afcjk.c */ |
---|
4 | /* */ |
---|
5 | /* Auto-fitter hinting routines for CJK script (body). */ |
---|
6 | /* */ |
---|
7 | /* Copyright 2006, 2007, 2008 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 | * The algorithm is based on akito's autohint patch, available here: |
---|
20 | * |
---|
21 | * http://www.kde.gr.jp/~akito/patch/freetype2/ |
---|
22 | * |
---|
23 | */ |
---|
24 | |
---|
25 | #include "aftypes.h" |
---|
26 | #include "aflatin.h" |
---|
27 | |
---|
28 | |
---|
29 | #ifdef AF_CONFIG_OPTION_CJK |
---|
30 | |
---|
31 | #include "afcjk.h" |
---|
32 | #include "aferrors.h" |
---|
33 | |
---|
34 | |
---|
35 | #ifdef AF_USE_WARPER |
---|
36 | #include "afwarp.h" |
---|
37 | #endif |
---|
38 | |
---|
39 | |
---|
40 | /*************************************************************************/ |
---|
41 | /*************************************************************************/ |
---|
42 | /***** *****/ |
---|
43 | /***** C J K G L O B A L M E T R I C S *****/ |
---|
44 | /***** *****/ |
---|
45 | /*************************************************************************/ |
---|
46 | /*************************************************************************/ |
---|
47 | |
---|
48 | FT_LOCAL_DEF( FT_Error ) |
---|
49 | af_cjk_metrics_init( AF_LatinMetrics metrics, |
---|
50 | FT_Face face ) |
---|
51 | { |
---|
52 | FT_CharMap oldmap = face->charmap; |
---|
53 | |
---|
54 | |
---|
55 | metrics->units_per_em = face->units_per_EM; |
---|
56 | |
---|
57 | /* TODO are there blues? */ |
---|
58 | |
---|
59 | if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) ) |
---|
60 | face->charmap = NULL; |
---|
61 | |
---|
62 | /* latin's version would suffice */ |
---|
63 | af_latin_metrics_init_widths( metrics, face, 0x7530 ); |
---|
64 | |
---|
65 | FT_Set_Charmap( face, oldmap ); |
---|
66 | |
---|
67 | return AF_Err_Ok; |
---|
68 | } |
---|
69 | |
---|
70 | |
---|
71 | static void |
---|
72 | af_cjk_metrics_scale_dim( AF_LatinMetrics metrics, |
---|
73 | AF_Scaler scaler, |
---|
74 | AF_Dimension dim ) |
---|
75 | { |
---|
76 | AF_LatinAxis axis; |
---|
77 | |
---|
78 | |
---|
79 | axis = &metrics->axis[dim]; |
---|
80 | |
---|
81 | if ( dim == AF_DIMENSION_HORZ ) |
---|
82 | { |
---|
83 | axis->scale = scaler->x_scale; |
---|
84 | axis->delta = scaler->x_delta; |
---|
85 | } |
---|
86 | else |
---|
87 | { |
---|
88 | axis->scale = scaler->y_scale; |
---|
89 | axis->delta = scaler->y_delta; |
---|
90 | } |
---|
91 | } |
---|
92 | |
---|
93 | |
---|
94 | FT_LOCAL_DEF( void ) |
---|
95 | af_cjk_metrics_scale( AF_LatinMetrics metrics, |
---|
96 | AF_Scaler scaler ) |
---|
97 | { |
---|
98 | metrics->root.scaler = *scaler; |
---|
99 | |
---|
100 | af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ ); |
---|
101 | af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT ); |
---|
102 | } |
---|
103 | |
---|
104 | |
---|
105 | /*************************************************************************/ |
---|
106 | /*************************************************************************/ |
---|
107 | /***** *****/ |
---|
108 | /***** C J K G L Y P H A N A L Y S I S *****/ |
---|
109 | /***** *****/ |
---|
110 | /*************************************************************************/ |
---|
111 | /*************************************************************************/ |
---|
112 | |
---|
113 | static FT_Error |
---|
114 | af_cjk_hints_compute_segments( AF_GlyphHints hints, |
---|
115 | AF_Dimension dim ) |
---|
116 | { |
---|
117 | AF_AxisHints axis = &hints->axis[dim]; |
---|
118 | AF_Segment segments = axis->segments; |
---|
119 | AF_Segment segment_limit = segments + axis->num_segments; |
---|
120 | FT_Error error; |
---|
121 | AF_Segment seg; |
---|
122 | |
---|
123 | |
---|
124 | error = af_latin_hints_compute_segments( hints, dim ); |
---|
125 | if ( error ) |
---|
126 | return error; |
---|
127 | |
---|
128 | /* a segment is round if it doesn't have successive */ |
---|
129 | /* on-curve points. */ |
---|
130 | for ( seg = segments; seg < segment_limit; seg++ ) |
---|
131 | { |
---|
132 | AF_Point pt = seg->first; |
---|
133 | AF_Point last = seg->last; |
---|
134 | AF_Flags f0 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL); |
---|
135 | AF_Flags f1; |
---|
136 | |
---|
137 | |
---|
138 | seg->flags &= ~AF_EDGE_ROUND; |
---|
139 | |
---|
140 | for ( ; pt != last; f0 = f1 ) |
---|
141 | { |
---|
142 | pt = pt->next; |
---|
143 | f1 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL); |
---|
144 | |
---|
145 | if ( !f0 && !f1 ) |
---|
146 | break; |
---|
147 | |
---|
148 | if ( pt == last ) |
---|
149 | seg->flags |= AF_EDGE_ROUND; |
---|
150 | } |
---|
151 | } |
---|
152 | |
---|
153 | return AF_Err_Ok; |
---|
154 | } |
---|
155 | |
---|
156 | |
---|
157 | static void |
---|
158 | af_cjk_hints_link_segments( AF_GlyphHints hints, |
---|
159 | AF_Dimension dim ) |
---|
160 | { |
---|
161 | AF_AxisHints axis = &hints->axis[dim]; |
---|
162 | AF_Segment segments = axis->segments; |
---|
163 | AF_Segment segment_limit = segments + axis->num_segments; |
---|
164 | AF_Direction major_dir = axis->major_dir; |
---|
165 | AF_Segment seg1, seg2; |
---|
166 | FT_Pos len_threshold; |
---|
167 | FT_Pos dist_threshold; |
---|
168 | |
---|
169 | |
---|
170 | len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 ); |
---|
171 | |
---|
172 | dist_threshold = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale |
---|
173 | : hints->y_scale; |
---|
174 | dist_threshold = FT_DivFix( 64 * 3, dist_threshold ); |
---|
175 | |
---|
176 | /* now compare each segment to the others */ |
---|
177 | for ( seg1 = segments; seg1 < segment_limit; seg1++ ) |
---|
178 | { |
---|
179 | /* the fake segments are for metrics hinting only */ |
---|
180 | if ( seg1->first == seg1->last ) |
---|
181 | continue; |
---|
182 | |
---|
183 | if ( seg1->dir != major_dir ) |
---|
184 | continue; |
---|
185 | |
---|
186 | for ( seg2 = segments; seg2 < segment_limit; seg2++ ) |
---|
187 | if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 ) |
---|
188 | { |
---|
189 | FT_Pos dist = seg2->pos - seg1->pos; |
---|
190 | |
---|
191 | |
---|
192 | if ( dist < 0 ) |
---|
193 | continue; |
---|
194 | |
---|
195 | { |
---|
196 | FT_Pos min = seg1->min_coord; |
---|
197 | FT_Pos max = seg1->max_coord; |
---|
198 | FT_Pos len; |
---|
199 | |
---|
200 | |
---|
201 | if ( min < seg2->min_coord ) |
---|
202 | min = seg2->min_coord; |
---|
203 | |
---|
204 | if ( max > seg2->max_coord ) |
---|
205 | max = seg2->max_coord; |
---|
206 | |
---|
207 | len = max - min; |
---|
208 | if ( len >= len_threshold ) |
---|
209 | { |
---|
210 | if ( dist * 8 < seg1->score * 9 && |
---|
211 | ( dist * 8 < seg1->score * 7 || seg1->len < len ) ) |
---|
212 | { |
---|
213 | seg1->score = dist; |
---|
214 | seg1->len = len; |
---|
215 | seg1->link = seg2; |
---|
216 | } |
---|
217 | |
---|
218 | if ( dist * 8 < seg2->score * 9 && |
---|
219 | ( dist * 8 < seg2->score * 7 || seg2->len < len ) ) |
---|
220 | { |
---|
221 | seg2->score = dist; |
---|
222 | seg2->len = len; |
---|
223 | seg2->link = seg1; |
---|
224 | } |
---|
225 | } |
---|
226 | } |
---|
227 | } |
---|
228 | } |
---|
229 | |
---|
230 | /* |
---|
231 | * now compute the `serif' segments |
---|
232 | * |
---|
233 | * In Hanzi, some strokes are wider on one or both of the ends. |
---|
234 | * We either identify the stems on the ends as serifs or remove |
---|
235 | * the linkage, depending on the length of the stems. |
---|
236 | * |
---|
237 | */ |
---|
238 | |
---|
239 | { |
---|
240 | AF_Segment link1, link2; |
---|
241 | |
---|
242 | |
---|
243 | for ( seg1 = segments; seg1 < segment_limit; seg1++ ) |
---|
244 | { |
---|
245 | link1 = seg1->link; |
---|
246 | if ( !link1 || link1->link != seg1 || link1->pos <= seg1->pos ) |
---|
247 | continue; |
---|
248 | |
---|
249 | if ( seg1->score >= dist_threshold ) |
---|
250 | continue; |
---|
251 | |
---|
252 | for ( seg2 = segments; seg2 < segment_limit; seg2++ ) |
---|
253 | { |
---|
254 | if ( seg2->pos > seg1->pos || seg1 == seg2 ) |
---|
255 | continue; |
---|
256 | |
---|
257 | link2 = seg2->link; |
---|
258 | if ( !link2 || link2->link != seg2 || link2->pos < link1->pos ) |
---|
259 | continue; |
---|
260 | |
---|
261 | if ( seg1->pos == seg2->pos && link1->pos == link2->pos ) |
---|
262 | continue; |
---|
263 | |
---|
264 | if ( seg2->score <= seg1->score || seg1->score * 4 <= seg2->score ) |
---|
265 | continue; |
---|
266 | |
---|
267 | /* seg2 < seg1 < link1 < link2 */ |
---|
268 | |
---|
269 | if ( seg1->len >= seg2->len * 3 ) |
---|
270 | { |
---|
271 | AF_Segment seg; |
---|
272 | |
---|
273 | |
---|
274 | for ( seg = segments; seg < segment_limit; seg++ ) |
---|
275 | { |
---|
276 | AF_Segment link = seg->link; |
---|
277 | |
---|
278 | |
---|
279 | if ( link == seg2 ) |
---|
280 | { |
---|
281 | seg->link = 0; |
---|
282 | seg->serif = link1; |
---|
283 | } |
---|
284 | else if ( link == link2 ) |
---|
285 | { |
---|
286 | seg->link = 0; |
---|
287 | seg->serif = seg1; |
---|
288 | } |
---|
289 | } |
---|
290 | } |
---|
291 | else |
---|
292 | { |
---|
293 | seg1->link = link1->link = 0; |
---|
294 | |
---|
295 | break; |
---|
296 | } |
---|
297 | } |
---|
298 | } |
---|
299 | } |
---|
300 | |
---|
301 | for ( seg1 = segments; seg1 < segment_limit; seg1++ ) |
---|
302 | { |
---|
303 | seg2 = seg1->link; |
---|
304 | |
---|
305 | if ( seg2 ) |
---|
306 | { |
---|
307 | seg2->num_linked++; |
---|
308 | if ( seg2->link != seg1 ) |
---|
309 | { |
---|
310 | seg1->link = 0; |
---|
311 | |
---|
312 | if ( seg2->score < dist_threshold || seg1->score < seg2->score * 4 ) |
---|
313 | seg1->serif = seg2->link; |
---|
314 | else |
---|
315 | seg2->num_linked--; |
---|
316 | } |
---|
317 | } |
---|
318 | } |
---|
319 | } |
---|
320 | |
---|
321 | |
---|
322 | static FT_Error |
---|
323 | af_cjk_hints_compute_edges( AF_GlyphHints hints, |
---|
324 | AF_Dimension dim ) |
---|
325 | { |
---|
326 | AF_AxisHints axis = &hints->axis[dim]; |
---|
327 | FT_Error error = AF_Err_Ok; |
---|
328 | FT_Memory memory = hints->memory; |
---|
329 | AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim]; |
---|
330 | |
---|
331 | AF_Segment segments = axis->segments; |
---|
332 | AF_Segment segment_limit = segments + axis->num_segments; |
---|
333 | AF_Segment seg; |
---|
334 | |
---|
335 | FT_Fixed scale; |
---|
336 | FT_Pos edge_distance_threshold; |
---|
337 | |
---|
338 | |
---|
339 | axis->num_edges = 0; |
---|
340 | |
---|
341 | scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale |
---|
342 | : hints->y_scale; |
---|
343 | |
---|
344 | /*********************************************************************/ |
---|
345 | /* */ |
---|
346 | /* We begin by generating a sorted table of edges for the current */ |
---|
347 | /* direction. To do so, we simply scan each segment and try to find */ |
---|
348 | /* an edge in our table that corresponds to its position. */ |
---|
349 | /* */ |
---|
350 | /* If no edge is found, we create and insert a new edge in the */ |
---|
351 | /* sorted table. Otherwise, we simply add the segment to the edge's */ |
---|
352 | /* list which is then processed in the second step to compute the */ |
---|
353 | /* edge's properties. */ |
---|
354 | /* */ |
---|
355 | /* Note that the edges table is sorted along the segment/edge */ |
---|
356 | /* position. */ |
---|
357 | /* */ |
---|
358 | /*********************************************************************/ |
---|
359 | |
---|
360 | edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold, |
---|
361 | scale ); |
---|
362 | if ( edge_distance_threshold > 64 / 4 ) |
---|
363 | edge_distance_threshold = FT_DivFix( 64 / 4, scale ); |
---|
364 | else |
---|
365 | edge_distance_threshold = laxis->edge_distance_threshold; |
---|
366 | |
---|
367 | for ( seg = segments; seg < segment_limit; seg++ ) |
---|
368 | { |
---|
369 | AF_Edge found = 0; |
---|
370 | FT_Pos best = 0xFFFFU; |
---|
371 | FT_Int ee; |
---|
372 | |
---|
373 | |
---|
374 | /* look for an edge corresponding to the segment */ |
---|
375 | for ( ee = 0; ee < axis->num_edges; ee++ ) |
---|
376 | { |
---|
377 | AF_Edge edge = axis->edges + ee; |
---|
378 | FT_Pos dist; |
---|
379 | |
---|
380 | |
---|
381 | if ( edge->dir != seg->dir ) |
---|
382 | continue; |
---|
383 | |
---|
384 | dist = seg->pos - edge->fpos; |
---|
385 | if ( dist < 0 ) |
---|
386 | dist = -dist; |
---|
387 | |
---|
388 | if ( dist < edge_distance_threshold && dist < best ) |
---|
389 | { |
---|
390 | AF_Segment link = seg->link; |
---|
391 | |
---|
392 | |
---|
393 | /* check whether all linked segments of the candidate edge */ |
---|
394 | /* can make a single edge. */ |
---|
395 | if ( link ) |
---|
396 | { |
---|
397 | AF_Segment seg1 = edge->first; |
---|
398 | AF_Segment link1; |
---|
399 | FT_Pos dist2 = 0; |
---|
400 | |
---|
401 | |
---|
402 | do |
---|
403 | { |
---|
404 | link1 = seg1->link; |
---|
405 | if ( link1 ) |
---|
406 | { |
---|
407 | dist2 = AF_SEGMENT_DIST( link, link1 ); |
---|
408 | if ( dist2 >= edge_distance_threshold ) |
---|
409 | break; |
---|
410 | } |
---|
411 | |
---|
412 | } while ( ( seg1 = seg1->edge_next ) != edge->first ); |
---|
413 | |
---|
414 | if ( dist2 >= edge_distance_threshold ) |
---|
415 | continue; |
---|
416 | } |
---|
417 | |
---|
418 | best = dist; |
---|
419 | found = edge; |
---|
420 | } |
---|
421 | } |
---|
422 | |
---|
423 | if ( !found ) |
---|
424 | { |
---|
425 | AF_Edge edge; |
---|
426 | |
---|
427 | |
---|
428 | /* insert a new edge in the list and */ |
---|
429 | /* sort according to the position */ |
---|
430 | error = af_axis_hints_new_edge( axis, seg->pos, |
---|
431 | (AF_Direction)seg->dir, |
---|
432 | memory, &edge ); |
---|
433 | if ( error ) |
---|
434 | goto Exit; |
---|
435 | |
---|
436 | /* add the segment to the new edge's list */ |
---|
437 | FT_ZERO( edge ); |
---|
438 | |
---|
439 | edge->first = seg; |
---|
440 | edge->last = seg; |
---|
441 | edge->fpos = seg->pos; |
---|
442 | edge->opos = edge->pos = FT_MulFix( seg->pos, scale ); |
---|
443 | seg->edge_next = seg; |
---|
444 | edge->dir = seg->dir; |
---|
445 | } |
---|
446 | else |
---|
447 | { |
---|
448 | /* if an edge was found, simply add the segment to the edge's */ |
---|
449 | /* list */ |
---|
450 | seg->edge_next = found->first; |
---|
451 | found->last->edge_next = seg; |
---|
452 | found->last = seg; |
---|
453 | } |
---|
454 | } |
---|
455 | |
---|
456 | /*********************************************************************/ |
---|
457 | /* */ |
---|
458 | /* Good, we now compute each edge's properties according to segments */ |
---|
459 | /* found on its position. Basically, these are as follows. */ |
---|
460 | /* */ |
---|
461 | /* - edge's main direction */ |
---|
462 | /* - stem edge, serif edge or both (which defaults to stem then) */ |
---|
463 | /* - rounded edge, straight or both (which defaults to straight) */ |
---|
464 | /* - link for edge */ |
---|
465 | /* */ |
---|
466 | /*********************************************************************/ |
---|
467 | |
---|
468 | /* first of all, set the `edge' field in each segment -- this is */ |
---|
469 | /* required in order to compute edge links */ |
---|
470 | /* */ |
---|
471 | /* Note that removing this loop and setting the `edge' field of each */ |
---|
472 | /* segment directly in the code above slows down execution speed for */ |
---|
473 | /* some reasons on platforms like the Sun. */ |
---|
474 | |
---|
475 | { |
---|
476 | AF_Edge edges = axis->edges; |
---|
477 | AF_Edge edge_limit = edges + axis->num_edges; |
---|
478 | AF_Edge edge; |
---|
479 | |
---|
480 | |
---|
481 | for ( edge = edges; edge < edge_limit; edge++ ) |
---|
482 | { |
---|
483 | seg = edge->first; |
---|
484 | if ( seg ) |
---|
485 | do |
---|
486 | { |
---|
487 | seg->edge = edge; |
---|
488 | seg = seg->edge_next; |
---|
489 | |
---|
490 | } while ( seg != edge->first ); |
---|
491 | } |
---|
492 | |
---|
493 | /* now compute each edge properties */ |
---|
494 | for ( edge = edges; edge < edge_limit; edge++ ) |
---|
495 | { |
---|
496 | FT_Int is_round = 0; /* does it contain round segments? */ |
---|
497 | FT_Int is_straight = 0; /* does it contain straight segments? */ |
---|
498 | |
---|
499 | |
---|
500 | seg = edge->first; |
---|
501 | |
---|
502 | do |
---|
503 | { |
---|
504 | FT_Bool is_serif; |
---|
505 | |
---|
506 | |
---|
507 | /* check for roundness of segment */ |
---|
508 | if ( seg->flags & AF_EDGE_ROUND ) |
---|
509 | is_round++; |
---|
510 | else |
---|
511 | is_straight++; |
---|
512 | |
---|
513 | /* check for links -- if seg->serif is set, then seg->link must */ |
---|
514 | /* be ignored */ |
---|
515 | is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge ); |
---|
516 | |
---|
517 | if ( seg->link || is_serif ) |
---|
518 | { |
---|
519 | AF_Edge edge2; |
---|
520 | AF_Segment seg2; |
---|
521 | |
---|
522 | |
---|
523 | edge2 = edge->link; |
---|
524 | seg2 = seg->link; |
---|
525 | |
---|
526 | if ( is_serif ) |
---|
527 | { |
---|
528 | seg2 = seg->serif; |
---|
529 | edge2 = edge->serif; |
---|
530 | } |
---|
531 | |
---|
532 | if ( edge2 ) |
---|
533 | { |
---|
534 | FT_Pos edge_delta; |
---|
535 | FT_Pos seg_delta; |
---|
536 | |
---|
537 | |
---|
538 | edge_delta = edge->fpos - edge2->fpos; |
---|
539 | if ( edge_delta < 0 ) |
---|
540 | edge_delta = -edge_delta; |
---|
541 | |
---|
542 | seg_delta = AF_SEGMENT_DIST( seg, seg2 ); |
---|
543 | |
---|
544 | if ( seg_delta < edge_delta ) |
---|
545 | edge2 = seg2->edge; |
---|
546 | } |
---|
547 | else |
---|
548 | edge2 = seg2->edge; |
---|
549 | |
---|
550 | if ( is_serif ) |
---|
551 | { |
---|
552 | edge->serif = edge2; |
---|
553 | edge2->flags |= AF_EDGE_SERIF; |
---|
554 | } |
---|
555 | else |
---|
556 | edge->link = edge2; |
---|
557 | } |
---|
558 | |
---|
559 | seg = seg->edge_next; |
---|
560 | |
---|
561 | } while ( seg != edge->first ); |
---|
562 | |
---|
563 | /* set the round/straight flags */ |
---|
564 | edge->flags = AF_EDGE_NORMAL; |
---|
565 | |
---|
566 | if ( is_round > 0 && is_round >= is_straight ) |
---|
567 | edge->flags |= AF_EDGE_ROUND; |
---|
568 | |
---|
569 | /* get rid of serifs if link is set */ |
---|
570 | /* XXX: This gets rid of many unpleasant artefacts! */ |
---|
571 | /* Example: the `c' in cour.pfa at size 13 */ |
---|
572 | |
---|
573 | if ( edge->serif && edge->link ) |
---|
574 | edge->serif = 0; |
---|
575 | } |
---|
576 | } |
---|
577 | |
---|
578 | Exit: |
---|
579 | return error; |
---|
580 | } |
---|
581 | |
---|
582 | |
---|
583 | static FT_Error |
---|
584 | af_cjk_hints_detect_features( AF_GlyphHints hints, |
---|
585 | AF_Dimension dim ) |
---|
586 | { |
---|
587 | FT_Error error; |
---|
588 | |
---|
589 | |
---|
590 | error = af_cjk_hints_compute_segments( hints, dim ); |
---|
591 | if ( !error ) |
---|
592 | { |
---|
593 | af_cjk_hints_link_segments( hints, dim ); |
---|
594 | |
---|
595 | error = af_cjk_hints_compute_edges( hints, dim ); |
---|
596 | } |
---|
597 | return error; |
---|
598 | } |
---|
599 | |
---|
600 | |
---|
601 | FT_LOCAL_DEF( FT_Error ) |
---|
602 | af_cjk_hints_init( AF_GlyphHints hints, |
---|
603 | AF_LatinMetrics metrics ) |
---|
604 | { |
---|
605 | FT_Render_Mode mode; |
---|
606 | FT_UInt32 scaler_flags, other_flags; |
---|
607 | |
---|
608 | |
---|
609 | af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics ); |
---|
610 | |
---|
611 | /* |
---|
612 | * correct x_scale and y_scale when needed, since they may have |
---|
613 | * been modified af_cjk_scale_dim above |
---|
614 | */ |
---|
615 | hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale; |
---|
616 | hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta; |
---|
617 | hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale; |
---|
618 | hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta; |
---|
619 | |
---|
620 | /* compute flags depending on render mode, etc. */ |
---|
621 | mode = metrics->root.scaler.render_mode; |
---|
622 | |
---|
623 | #ifdef AF_USE_WARPER |
---|
624 | if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V ) |
---|
625 | metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL; |
---|
626 | #endif |
---|
627 | |
---|
628 | scaler_flags = hints->scaler_flags; |
---|
629 | other_flags = 0; |
---|
630 | |
---|
631 | /* |
---|
632 | * We snap the width of vertical stems for the monochrome and |
---|
633 | * horizontal LCD rendering targets only. |
---|
634 | */ |
---|
635 | if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD ) |
---|
636 | other_flags |= AF_LATIN_HINTS_HORZ_SNAP; |
---|
637 | |
---|
638 | /* |
---|
639 | * We snap the width of horizontal stems for the monochrome and |
---|
640 | * vertical LCD rendering targets only. |
---|
641 | */ |
---|
642 | if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V ) |
---|
643 | other_flags |= AF_LATIN_HINTS_VERT_SNAP; |
---|
644 | |
---|
645 | /* |
---|
646 | * We adjust stems to full pixels only if we don't use the `light' mode. |
---|
647 | */ |
---|
648 | if ( mode != FT_RENDER_MODE_LIGHT ) |
---|
649 | other_flags |= AF_LATIN_HINTS_STEM_ADJUST; |
---|
650 | |
---|
651 | if ( mode == FT_RENDER_MODE_MONO ) |
---|
652 | other_flags |= AF_LATIN_HINTS_MONO; |
---|
653 | |
---|
654 | scaler_flags |= AF_SCALER_FLAG_NO_ADVANCE; |
---|
655 | |
---|
656 | hints->scaler_flags = scaler_flags; |
---|
657 | hints->other_flags = other_flags; |
---|
658 | |
---|
659 | return 0; |
---|
660 | } |
---|
661 | |
---|
662 | |
---|
663 | /*************************************************************************/ |
---|
664 | /*************************************************************************/ |
---|
665 | /***** *****/ |
---|
666 | /***** C J K G L Y P H G R I D - F I T T I N G *****/ |
---|
667 | /***** *****/ |
---|
668 | /*************************************************************************/ |
---|
669 | /*************************************************************************/ |
---|
670 | |
---|
671 | /* snap a given width in scaled coordinates to one of the */ |
---|
672 | /* current standard widths */ |
---|
673 | |
---|
674 | static FT_Pos |
---|
675 | af_cjk_snap_width( AF_Width widths, |
---|
676 | FT_Int count, |
---|
677 | FT_Pos width ) |
---|
678 | { |
---|
679 | int n; |
---|
680 | FT_Pos best = 64 + 32 + 2; |
---|
681 | FT_Pos reference = width; |
---|
682 | FT_Pos scaled; |
---|
683 | |
---|
684 | |
---|
685 | for ( n = 0; n < count; n++ ) |
---|
686 | { |
---|
687 | FT_Pos w; |
---|
688 | FT_Pos dist; |
---|
689 | |
---|
690 | |
---|
691 | w = widths[n].cur; |
---|
692 | dist = width - w; |
---|
693 | if ( dist < 0 ) |
---|
694 | dist = -dist; |
---|
695 | if ( dist < best ) |
---|
696 | { |
---|
697 | best = dist; |
---|
698 | reference = w; |
---|
699 | } |
---|
700 | } |
---|
701 | |
---|
702 | scaled = FT_PIX_ROUND( reference ); |
---|
703 | |
---|
704 | if ( width >= reference ) |
---|
705 | { |
---|
706 | if ( width < scaled + 48 ) |
---|
707 | width = reference; |
---|
708 | } |
---|
709 | else |
---|
710 | { |
---|
711 | if ( width > scaled - 48 ) |
---|
712 | width = reference; |
---|
713 | } |
---|
714 | |
---|
715 | return width; |
---|
716 | } |
---|
717 | |
---|
718 | |
---|
719 | /* compute the snapped width of a given stem */ |
---|
720 | |
---|
721 | static FT_Pos |
---|
722 | af_cjk_compute_stem_width( AF_GlyphHints hints, |
---|
723 | AF_Dimension dim, |
---|
724 | FT_Pos width, |
---|
725 | AF_Edge_Flags base_flags, |
---|
726 | AF_Edge_Flags stem_flags ) |
---|
727 | { |
---|
728 | AF_LatinMetrics metrics = (AF_LatinMetrics) hints->metrics; |
---|
729 | AF_LatinAxis axis = & metrics->axis[dim]; |
---|
730 | FT_Pos dist = width; |
---|
731 | FT_Int sign = 0; |
---|
732 | FT_Int vertical = ( dim == AF_DIMENSION_VERT ); |
---|
733 | |
---|
734 | FT_UNUSED( base_flags ); |
---|
735 | FT_UNUSED( stem_flags ); |
---|
736 | |
---|
737 | |
---|
738 | if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ) |
---|
739 | return width; |
---|
740 | |
---|
741 | if ( dist < 0 ) |
---|
742 | { |
---|
743 | dist = -width; |
---|
744 | sign = 1; |
---|
745 | } |
---|
746 | |
---|
747 | if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || |
---|
748 | ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ) |
---|
749 | { |
---|
750 | /* smooth hinting process: very lightly quantize the stem width */ |
---|
751 | |
---|
752 | if ( axis->width_count > 0 ) |
---|
753 | { |
---|
754 | if ( FT_ABS( dist - axis->widths[0].cur ) < 40 ) |
---|
755 | { |
---|
756 | dist = axis->widths[0].cur; |
---|
757 | if ( dist < 48 ) |
---|
758 | dist = 48; |
---|
759 | |
---|
760 | goto Done_Width; |
---|
761 | } |
---|
762 | } |
---|
763 | |
---|
764 | if ( dist < 54 ) |
---|
765 | dist += ( 54 - dist ) / 2 ; |
---|
766 | else if ( dist < 3 * 64 ) |
---|
767 | { |
---|
768 | FT_Pos delta; |
---|
769 | |
---|
770 | |
---|
771 | delta = dist & 63; |
---|
772 | dist &= -64; |
---|
773 | |
---|
774 | if ( delta < 10 ) |
---|
775 | dist += delta; |
---|
776 | else if ( delta < 22 ) |
---|
777 | dist += 10; |
---|
778 | else if ( delta < 42 ) |
---|
779 | dist += delta; |
---|
780 | else if ( delta < 54 ) |
---|
781 | dist += 54; |
---|
782 | else |
---|
783 | dist += delta; |
---|
784 | } |
---|
785 | } |
---|
786 | else |
---|
787 | { |
---|
788 | /* strong hinting process: snap the stem width to integer pixels */ |
---|
789 | |
---|
790 | dist = af_cjk_snap_width( axis->widths, axis->width_count, dist ); |
---|
791 | |
---|
792 | if ( vertical ) |
---|
793 | { |
---|
794 | /* in the case of vertical hinting, always round */ |
---|
795 | /* the stem heights to integer pixels */ |
---|
796 | |
---|
797 | if ( dist >= 64 ) |
---|
798 | dist = ( dist + 16 ) & ~63; |
---|
799 | else |
---|
800 | dist = 64; |
---|
801 | } |
---|
802 | else |
---|
803 | { |
---|
804 | if ( AF_LATIN_HINTS_DO_MONO( hints ) ) |
---|
805 | { |
---|
806 | /* monochrome horizontal hinting: snap widths to integer pixels */ |
---|
807 | /* with a different threshold */ |
---|
808 | |
---|
809 | if ( dist < 64 ) |
---|
810 | dist = 64; |
---|
811 | else |
---|
812 | dist = ( dist + 32 ) & ~63; |
---|
813 | } |
---|
814 | else |
---|
815 | { |
---|
816 | /* for horizontal anti-aliased hinting, we adopt a more subtle */ |
---|
817 | /* approach: we strengthen small stems, round stems whose size */ |
---|
818 | /* is between 1 and 2 pixels to an integer, otherwise nothing */ |
---|
819 | |
---|
820 | if ( dist < 48 ) |
---|
821 | dist = ( dist + 64 ) >> 1; |
---|
822 | |
---|
823 | else if ( dist < 128 ) |
---|
824 | dist = ( dist + 22 ) & ~63; |
---|
825 | else |
---|
826 | /* round otherwise to prevent color fringes in LCD mode */ |
---|
827 | dist = ( dist + 32 ) & ~63; |
---|
828 | } |
---|
829 | } |
---|
830 | } |
---|
831 | |
---|
832 | Done_Width: |
---|
833 | if ( sign ) |
---|
834 | dist = -dist; |
---|
835 | |
---|
836 | return dist; |
---|
837 | } |
---|
838 | |
---|
839 | |
---|
840 | /* align one stem edge relative to the previous stem edge */ |
---|
841 | |
---|
842 | static void |
---|
843 | af_cjk_align_linked_edge( AF_GlyphHints hints, |
---|
844 | AF_Dimension dim, |
---|
845 | AF_Edge base_edge, |
---|
846 | AF_Edge stem_edge ) |
---|
847 | { |
---|
848 | FT_Pos dist = stem_edge->opos - base_edge->opos; |
---|
849 | |
---|
850 | FT_Pos fitted_width = af_cjk_compute_stem_width( |
---|
851 | hints, dim, dist, |
---|
852 | (AF_Edge_Flags)base_edge->flags, |
---|
853 | (AF_Edge_Flags)stem_edge->flags ); |
---|
854 | |
---|
855 | |
---|
856 | stem_edge->pos = base_edge->pos + fitted_width; |
---|
857 | } |
---|
858 | |
---|
859 | |
---|
860 | static void |
---|
861 | af_cjk_align_serif_edge( AF_GlyphHints hints, |
---|
862 | AF_Edge base, |
---|
863 | AF_Edge serif ) |
---|
864 | { |
---|
865 | FT_UNUSED( hints ); |
---|
866 | |
---|
867 | serif->pos = base->pos + ( serif->opos - base->opos ); |
---|
868 | } |
---|
869 | |
---|
870 | |
---|
871 | /*************************************************************************/ |
---|
872 | /*************************************************************************/ |
---|
873 | /*************************************************************************/ |
---|
874 | /**** ****/ |
---|
875 | /**** E D G E H I N T I N G ****/ |
---|
876 | /**** ****/ |
---|
877 | /*************************************************************************/ |
---|
878 | /*************************************************************************/ |
---|
879 | /*************************************************************************/ |
---|
880 | |
---|
881 | |
---|
882 | #define AF_LIGHT_MODE_MAX_HORZ_GAP 9 |
---|
883 | #define AF_LIGHT_MODE_MAX_VERT_GAP 15 |
---|
884 | #define AF_LIGHT_MODE_MAX_DELTA_ABS 14 |
---|
885 | |
---|
886 | |
---|
887 | static FT_Pos |
---|
888 | af_hint_normal_stem( AF_GlyphHints hints, |
---|
889 | AF_Edge edge, |
---|
890 | AF_Edge edge2, |
---|
891 | FT_Pos anchor, |
---|
892 | AF_Dimension dim ) |
---|
893 | { |
---|
894 | FT_Pos org_len, cur_len, org_center; |
---|
895 | FT_Pos cur_pos1, cur_pos2; |
---|
896 | FT_Pos d_off1, u_off1, d_off2, u_off2, delta; |
---|
897 | FT_Pos offset; |
---|
898 | FT_Pos threshold = 64; |
---|
899 | |
---|
900 | |
---|
901 | if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ) |
---|
902 | { |
---|
903 | if ( ( edge->flags & AF_EDGE_ROUND ) && |
---|
904 | ( edge2->flags & AF_EDGE_ROUND ) ) |
---|
905 | { |
---|
906 | if ( dim == AF_DIMENSION_VERT ) |
---|
907 | threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP; |
---|
908 | else |
---|
909 | threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP; |
---|
910 | } |
---|
911 | else |
---|
912 | { |
---|
913 | if ( dim == AF_DIMENSION_VERT ) |
---|
914 | threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP / 3; |
---|
915 | else |
---|
916 | threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP / 3; |
---|
917 | } |
---|
918 | } |
---|
919 | |
---|
920 | org_len = edge2->opos - edge->opos; |
---|
921 | cur_len = af_cjk_compute_stem_width( hints, dim, org_len, |
---|
922 | (AF_Edge_Flags)edge->flags, |
---|
923 | (AF_Edge_Flags)edge2->flags ); |
---|
924 | |
---|
925 | org_center = ( edge->opos + edge2->opos ) / 2 + anchor; |
---|
926 | cur_pos1 = org_center - cur_len / 2; |
---|
927 | cur_pos2 = cur_pos1 + cur_len; |
---|
928 | d_off1 = cur_pos1 - FT_PIX_FLOOR( cur_pos1 ); |
---|
929 | d_off2 = cur_pos2 - FT_PIX_FLOOR( cur_pos2 ); |
---|
930 | u_off1 = 64 - d_off1; |
---|
931 | u_off2 = 64 - d_off2; |
---|
932 | delta = 0; |
---|
933 | |
---|
934 | |
---|
935 | if ( d_off1 == 0 || d_off2 == 0 ) |
---|
936 | goto Exit; |
---|
937 | |
---|
938 | if ( cur_len <= threshold ) |
---|
939 | { |
---|
940 | if ( d_off2 < cur_len ) |
---|
941 | { |
---|
942 | if ( u_off1 <= d_off2 ) |
---|
943 | delta = u_off1; |
---|
944 | else |
---|
945 | delta = -d_off2; |
---|
946 | } |
---|
947 | |
---|
948 | goto Exit; |
---|
949 | } |
---|
950 | |
---|
951 | if ( threshold < 64 ) |
---|
952 | { |
---|
953 | if ( d_off1 >= threshold || u_off1 >= threshold || |
---|
954 | d_off2 >= threshold || u_off2 >= threshold ) |
---|
955 | goto Exit; |
---|
956 | } |
---|
957 | |
---|
958 | offset = cur_len % 64; |
---|
959 | |
---|
960 | if ( offset < 32 ) |
---|
961 | { |
---|
962 | if ( u_off1 <= offset || d_off2 <= offset ) |
---|
963 | goto Exit; |
---|
964 | } |
---|
965 | else |
---|
966 | offset = 64 - threshold; |
---|
967 | |
---|
968 | d_off1 = threshold - u_off1; |
---|
969 | u_off1 = u_off1 - offset; |
---|
970 | u_off2 = threshold - d_off2; |
---|
971 | d_off2 = d_off2 - offset; |
---|
972 | |
---|
973 | if ( d_off1 <= u_off1 ) |
---|
974 | u_off1 = -d_off1; |
---|
975 | |
---|
976 | if ( d_off2 <= u_off2 ) |
---|
977 | u_off2 = -d_off2; |
---|
978 | |
---|
979 | if ( FT_ABS( u_off1 ) <= FT_ABS( u_off2 ) ) |
---|
980 | delta = u_off1; |
---|
981 | else |
---|
982 | delta = u_off2; |
---|
983 | |
---|
984 | Exit: |
---|
985 | |
---|
986 | #if 1 |
---|
987 | if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ) |
---|
988 | { |
---|
989 | if ( delta > AF_LIGHT_MODE_MAX_DELTA_ABS ) |
---|
990 | delta = AF_LIGHT_MODE_MAX_DELTA_ABS; |
---|
991 | else if ( delta < -AF_LIGHT_MODE_MAX_DELTA_ABS ) |
---|
992 | delta = -AF_LIGHT_MODE_MAX_DELTA_ABS; |
---|
993 | } |
---|
994 | #endif |
---|
995 | |
---|
996 | cur_pos1 += delta; |
---|
997 | |
---|
998 | if ( edge->opos < edge2->opos ) |
---|
999 | { |
---|
1000 | edge->pos = cur_pos1; |
---|
1001 | edge2->pos = cur_pos1 + cur_len; |
---|
1002 | } |
---|
1003 | else |
---|
1004 | { |
---|
1005 | edge->pos = cur_pos1 + cur_len; |
---|
1006 | edge2->pos = cur_pos1; |
---|
1007 | } |
---|
1008 | |
---|
1009 | return delta; |
---|
1010 | } |
---|
1011 | |
---|
1012 | |
---|
1013 | static void |
---|
1014 | af_cjk_hint_edges( AF_GlyphHints hints, |
---|
1015 | AF_Dimension dim ) |
---|
1016 | { |
---|
1017 | AF_AxisHints axis = &hints->axis[dim]; |
---|
1018 | AF_Edge edges = axis->edges; |
---|
1019 | AF_Edge edge_limit = edges + axis->num_edges; |
---|
1020 | FT_Int n_edges; |
---|
1021 | AF_Edge edge; |
---|
1022 | AF_Edge anchor = 0; |
---|
1023 | FT_Pos delta = 0; |
---|
1024 | FT_Int skipped = 0; |
---|
1025 | |
---|
1026 | |
---|
1027 | /* now we align all stem edges. */ |
---|
1028 | for ( edge = edges; edge < edge_limit; edge++ ) |
---|
1029 | { |
---|
1030 | AF_Edge edge2; |
---|
1031 | |
---|
1032 | |
---|
1033 | if ( edge->flags & AF_EDGE_DONE ) |
---|
1034 | continue; |
---|
1035 | |
---|
1036 | /* skip all non-stem edges */ |
---|
1037 | edge2 = edge->link; |
---|
1038 | if ( !edge2 ) |
---|
1039 | { |
---|
1040 | skipped++; |
---|
1041 | continue; |
---|
1042 | } |
---|
1043 | |
---|
1044 | /* now align the stem */ |
---|
1045 | |
---|
1046 | if ( edge2 < edge ) |
---|
1047 | { |
---|
1048 | af_cjk_align_linked_edge( hints, dim, edge2, edge ); |
---|
1049 | edge->flags |= AF_EDGE_DONE; |
---|
1050 | continue; |
---|
1051 | } |
---|
1052 | |
---|
1053 | if ( dim != AF_DIMENSION_VERT && !anchor ) |
---|
1054 | { |
---|
1055 | |
---|
1056 | #if 0 |
---|
1057 | if ( fixedpitch ) |
---|
1058 | { |
---|
1059 | AF_Edge left = edge; |
---|
1060 | AF_Edge right = edge_limit - 1; |
---|
1061 | AF_EdgeRec left1, left2, right1, right2; |
---|
1062 | FT_Pos target, center1, center2; |
---|
1063 | FT_Pos delta1, delta2, d1, d2; |
---|
1064 | |
---|
1065 | |
---|
1066 | while ( right > left && !right->link ) |
---|
1067 | right--; |
---|
1068 | |
---|
1069 | left1 = *left; |
---|
1070 | left2 = *left->link; |
---|
1071 | right1 = *right->link; |
---|
1072 | right2 = *right; |
---|
1073 | |
---|
1074 | delta = ( ( ( hinter->pp2.x + 32 ) & -64 ) - hinter->pp2.x ) / 2; |
---|
1075 | target = left->opos + ( right->opos - left->opos ) / 2 + delta - 16; |
---|
1076 | |
---|
1077 | delta1 = delta; |
---|
1078 | delta1 += af_hint_normal_stem( hints, left, left->link, |
---|
1079 | delta1, 0 ); |
---|
1080 | |
---|
1081 | if ( left->link != right ) |
---|
1082 | af_hint_normal_stem( hints, right->link, right, delta1, 0 ); |
---|
1083 | |
---|
1084 | center1 = left->pos + ( right->pos - left->pos ) / 2; |
---|
1085 | |
---|
1086 | if ( center1 >= target ) |
---|
1087 | delta2 = delta - 32; |
---|
1088 | else |
---|
1089 | delta2 = delta + 32; |
---|
1090 | |
---|
1091 | delta2 += af_hint_normal_stem( hints, &left1, &left2, delta2, 0 ); |
---|
1092 | |
---|
1093 | if ( delta1 != delta2 ) |
---|
1094 | { |
---|
1095 | if ( left->link != right ) |
---|
1096 | af_hint_normal_stem( hints, &right1, &right2, delta2, 0 ); |
---|
1097 | |
---|
1098 | center2 = left1.pos + ( right2.pos - left1.pos ) / 2; |
---|
1099 | |
---|
1100 | d1 = center1 - target; |
---|
1101 | d2 = center2 - target; |
---|
1102 | |
---|
1103 | if ( FT_ABS( d2 ) < FT_ABS( d1 ) ) |
---|
1104 | { |
---|
1105 | left->pos = left1.pos; |
---|
1106 | left->link->pos = left2.pos; |
---|
1107 | |
---|
1108 | if ( left->link != right ) |
---|
1109 | { |
---|
1110 | right->link->pos = right1.pos; |
---|
1111 | right->pos = right2.pos; |
---|
1112 | } |
---|
1113 | |
---|
1114 | delta1 = delta2; |
---|
1115 | } |
---|
1116 | } |
---|
1117 | |
---|
1118 | delta = delta1; |
---|
1119 | right->link->flags |= AF_EDGE_DONE; |
---|
1120 | right->flags |= AF_EDGE_DONE; |
---|
1121 | } |
---|
1122 | else |
---|
1123 | |
---|
1124 | #endif /* 0 */ |
---|
1125 | |
---|
1126 | delta = af_hint_normal_stem( hints, edge, edge2, 0, |
---|
1127 | AF_DIMENSION_HORZ ); |
---|
1128 | } |
---|
1129 | else |
---|
1130 | af_hint_normal_stem( hints, edge, edge2, delta, dim ); |
---|
1131 | |
---|
1132 | #if 0 |
---|
1133 | printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n", |
---|
1134 | edge - edges, edge2 - edges, |
---|
1135 | ( edge->pos - edge->opos ) / 64.0, |
---|
1136 | ( edge2->pos - edge2->opos ) / 64.0 ); |
---|
1137 | #endif |
---|
1138 | |
---|
1139 | anchor = edge; |
---|
1140 | edge->flags |= AF_EDGE_DONE; |
---|
1141 | edge2->flags |= AF_EDGE_DONE; |
---|
1142 | } |
---|
1143 | |
---|
1144 | /* make sure that lowercase m's maintain their symmetry */ |
---|
1145 | |
---|
1146 | /* In general, lowercase m's have six vertical edges if they are sans */ |
---|
1147 | /* serif, or twelve if they are with serifs. This implementation is */ |
---|
1148 | /* based on that assumption, and seems to work very well with most */ |
---|
1149 | /* faces. However, if for a certain face this assumption is not */ |
---|
1150 | /* true, the m is just rendered like before. In addition, any stem */ |
---|
1151 | /* correction will only be applied to symmetrical glyphs (even if the */ |
---|
1152 | /* glyph is not an m), so the potential for unwanted distortion is */ |
---|
1153 | /* relatively low. */ |
---|
1154 | |
---|
1155 | /* We don't handle horizontal edges since we can't easily assure that */ |
---|
1156 | /* the third (lowest) stem aligns with the base line; it might end up */ |
---|
1157 | /* one pixel higher or lower. */ |
---|
1158 | |
---|
1159 | n_edges = edge_limit - edges; |
---|
1160 | if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) ) |
---|
1161 | { |
---|
1162 | AF_Edge edge1, edge2, edge3; |
---|
1163 | FT_Pos dist1, dist2, span; |
---|
1164 | |
---|
1165 | |
---|
1166 | if ( n_edges == 6 ) |
---|
1167 | { |
---|
1168 | edge1 = edges; |
---|
1169 | edge2 = edges + 2; |
---|
1170 | edge3 = edges + 4; |
---|
1171 | } |
---|
1172 | else |
---|
1173 | { |
---|
1174 | edge1 = edges + 1; |
---|
1175 | edge2 = edges + 5; |
---|
1176 | edge3 = edges + 9; |
---|
1177 | } |
---|
1178 | |
---|
1179 | dist1 = edge2->opos - edge1->opos; |
---|
1180 | dist2 = edge3->opos - edge2->opos; |
---|
1181 | |
---|
1182 | span = dist1 - dist2; |
---|
1183 | if ( span < 0 ) |
---|
1184 | span = -span; |
---|
1185 | |
---|
1186 | if ( edge1->link == edge1 + 1 && |
---|
1187 | edge2->link == edge2 + 1 && |
---|
1188 | edge3->link == edge3 + 1 && span < 8 ) |
---|
1189 | { |
---|
1190 | delta = edge3->pos - ( 2 * edge2->pos - edge1->pos ); |
---|
1191 | edge3->pos -= delta; |
---|
1192 | if ( edge3->link ) |
---|
1193 | edge3->link->pos -= delta; |
---|
1194 | |
---|
1195 | /* move the serifs along with the stem */ |
---|
1196 | if ( n_edges == 12 ) |
---|
1197 | { |
---|
1198 | ( edges + 8 )->pos -= delta; |
---|
1199 | ( edges + 11 )->pos -= delta; |
---|
1200 | } |
---|
1201 | |
---|
1202 | edge3->flags |= AF_EDGE_DONE; |
---|
1203 | if ( edge3->link ) |
---|
1204 | edge3->link->flags |= AF_EDGE_DONE; |
---|
1205 | } |
---|
1206 | } |
---|
1207 | |
---|
1208 | if ( !skipped ) |
---|
1209 | return; |
---|
1210 | |
---|
1211 | /* |
---|
1212 | * now hint the remaining edges (serifs and single) in order |
---|
1213 | * to complete our processing |
---|
1214 | */ |
---|
1215 | for ( edge = edges; edge < edge_limit; edge++ ) |
---|
1216 | { |
---|
1217 | if ( edge->flags & AF_EDGE_DONE ) |
---|
1218 | continue; |
---|
1219 | |
---|
1220 | if ( edge->serif ) |
---|
1221 | { |
---|
1222 | af_cjk_align_serif_edge( hints, edge->serif, edge ); |
---|
1223 | edge->flags |= AF_EDGE_DONE; |
---|
1224 | skipped--; |
---|
1225 | } |
---|
1226 | } |
---|
1227 | |
---|
1228 | if ( !skipped ) |
---|
1229 | return; |
---|
1230 | |
---|
1231 | for ( edge = edges; edge < edge_limit; edge++ ) |
---|
1232 | { |
---|
1233 | AF_Edge before, after; |
---|
1234 | |
---|
1235 | |
---|
1236 | if ( edge->flags & AF_EDGE_DONE ) |
---|
1237 | continue; |
---|
1238 | |
---|
1239 | before = after = edge; |
---|
1240 | |
---|
1241 | while ( --before >= edges ) |
---|
1242 | if ( before->flags & AF_EDGE_DONE ) |
---|
1243 | break; |
---|
1244 | |
---|
1245 | while ( ++after < edge_limit ) |
---|
1246 | if ( after->flags & AF_EDGE_DONE ) |
---|
1247 | break; |
---|
1248 | |
---|
1249 | if ( before >= edges || after < edge_limit ) |
---|
1250 | { |
---|
1251 | if ( before < edges ) |
---|
1252 | af_cjk_align_serif_edge( hints, after, edge ); |
---|
1253 | else if ( after >= edge_limit ) |
---|
1254 | af_cjk_align_serif_edge( hints, before, edge ); |
---|
1255 | else |
---|
1256 | { |
---|
1257 | if ( after->fpos == before->fpos ) |
---|
1258 | edge->pos = before->pos; |
---|
1259 | else |
---|
1260 | edge->pos = before->pos + |
---|
1261 | FT_MulDiv( edge->fpos - before->fpos, |
---|
1262 | after->pos - before->pos, |
---|
1263 | after->fpos - before->fpos ); |
---|
1264 | } |
---|
1265 | } |
---|
1266 | } |
---|
1267 | } |
---|
1268 | |
---|
1269 | |
---|
1270 | static void |
---|
1271 | af_cjk_align_edge_points( AF_GlyphHints hints, |
---|
1272 | AF_Dimension dim ) |
---|
1273 | { |
---|
1274 | AF_AxisHints axis = & hints->axis[dim]; |
---|
1275 | AF_Edge edges = axis->edges; |
---|
1276 | AF_Edge edge_limit = edges + axis->num_edges; |
---|
1277 | AF_Edge edge; |
---|
1278 | FT_Bool snapping; |
---|
1279 | |
---|
1280 | |
---|
1281 | snapping = FT_BOOL( ( dim == AF_DIMENSION_HORZ && |
---|
1282 | AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) || |
---|
1283 | ( dim == AF_DIMENSION_VERT && |
---|
1284 | AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ); |
---|
1285 | |
---|
1286 | for ( edge = edges; edge < edge_limit; edge++ ) |
---|
1287 | { |
---|
1288 | /* move the points of each segment */ |
---|
1289 | /* in each edge to the edge's position */ |
---|
1290 | AF_Segment seg = edge->first; |
---|
1291 | |
---|
1292 | |
---|
1293 | if ( snapping ) |
---|
1294 | { |
---|
1295 | do |
---|
1296 | { |
---|
1297 | AF_Point point = seg->first; |
---|
1298 | |
---|
1299 | |
---|
1300 | for (;;) |
---|
1301 | { |
---|
1302 | if ( dim == AF_DIMENSION_HORZ ) |
---|
1303 | { |
---|
1304 | point->x = edge->pos; |
---|
1305 | point->flags |= AF_FLAG_TOUCH_X; |
---|
1306 | } |
---|
1307 | else |
---|
1308 | { |
---|
1309 | point->y = edge->pos; |
---|
1310 | point->flags |= AF_FLAG_TOUCH_Y; |
---|
1311 | } |
---|
1312 | |
---|
1313 | if ( point == seg->last ) |
---|
1314 | break; |
---|
1315 | |
---|
1316 | point = point->next; |
---|
1317 | } |
---|
1318 | |
---|
1319 | seg = seg->edge_next; |
---|
1320 | |
---|
1321 | } while ( seg != edge->first ); |
---|
1322 | } |
---|
1323 | else |
---|
1324 | { |
---|
1325 | FT_Pos delta = edge->pos - edge->opos; |
---|
1326 | |
---|
1327 | |
---|
1328 | do |
---|
1329 | { |
---|
1330 | AF_Point point = seg->first; |
---|
1331 | |
---|
1332 | |
---|
1333 | for (;;) |
---|
1334 | { |
---|
1335 | if ( dim == AF_DIMENSION_HORZ ) |
---|
1336 | { |
---|
1337 | point->x += delta; |
---|
1338 | point->flags |= AF_FLAG_TOUCH_X; |
---|
1339 | } |
---|
1340 | else |
---|
1341 | { |
---|
1342 | point->y += delta; |
---|
1343 | point->flags |= AF_FLAG_TOUCH_Y; |
---|
1344 | } |
---|
1345 | |
---|
1346 | if ( point == seg->last ) |
---|
1347 | break; |
---|
1348 | |
---|
1349 | point = point->next; |
---|
1350 | } |
---|
1351 | |
---|
1352 | seg = seg->edge_next; |
---|
1353 | |
---|
1354 | } while ( seg != edge->first ); |
---|
1355 | } |
---|
1356 | } |
---|
1357 | } |
---|
1358 | |
---|
1359 | |
---|
1360 | FT_LOCAL_DEF( FT_Error ) |
---|
1361 | af_cjk_hints_apply( AF_GlyphHints hints, |
---|
1362 | FT_Outline* outline, |
---|
1363 | AF_LatinMetrics metrics ) |
---|
1364 | { |
---|
1365 | FT_Error error; |
---|
1366 | int dim; |
---|
1367 | |
---|
1368 | FT_UNUSED( metrics ); |
---|
1369 | |
---|
1370 | |
---|
1371 | error = af_glyph_hints_reload( hints, outline, 0 ); |
---|
1372 | if ( error ) |
---|
1373 | goto Exit; |
---|
1374 | |
---|
1375 | /* analyze glyph outline */ |
---|
1376 | if ( AF_HINTS_DO_HORIZONTAL( hints ) ) |
---|
1377 | { |
---|
1378 | error = af_cjk_hints_detect_features( hints, AF_DIMENSION_HORZ ); |
---|
1379 | if ( error ) |
---|
1380 | goto Exit; |
---|
1381 | } |
---|
1382 | |
---|
1383 | if ( AF_HINTS_DO_VERTICAL( hints ) ) |
---|
1384 | { |
---|
1385 | error = af_cjk_hints_detect_features( hints, AF_DIMENSION_VERT ); |
---|
1386 | if ( error ) |
---|
1387 | goto Exit; |
---|
1388 | } |
---|
1389 | |
---|
1390 | /* grid-fit the outline */ |
---|
1391 | for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) |
---|
1392 | { |
---|
1393 | if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) || |
---|
1394 | ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) ) |
---|
1395 | { |
---|
1396 | |
---|
1397 | #ifdef AF_USE_WARPER |
---|
1398 | if ( dim == AF_DIMENSION_HORZ && |
---|
1399 | metrics->root.scaler.render_mode == FT_RENDER_MODE_NORMAL ) |
---|
1400 | { |
---|
1401 | AF_WarperRec warper; |
---|
1402 | FT_Fixed scale; |
---|
1403 | FT_Pos delta; |
---|
1404 | |
---|
1405 | |
---|
1406 | af_warper_compute( &warper, hints, dim, &scale, &delta ); |
---|
1407 | af_glyph_hints_scale_dim( hints, dim, scale, delta ); |
---|
1408 | continue; |
---|
1409 | } |
---|
1410 | #endif /* AF_USE_WARPER */ |
---|
1411 | |
---|
1412 | af_cjk_hint_edges( hints, (AF_Dimension)dim ); |
---|
1413 | af_cjk_align_edge_points( hints, (AF_Dimension)dim ); |
---|
1414 | af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim ); |
---|
1415 | af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim ); |
---|
1416 | } |
---|
1417 | } |
---|
1418 | |
---|
1419 | #if 0 |
---|
1420 | af_glyph_hints_dump_points( hints ); |
---|
1421 | af_glyph_hints_dump_segments( hints ); |
---|
1422 | af_glyph_hints_dump_edges( hints ); |
---|
1423 | #endif |
---|
1424 | |
---|
1425 | af_glyph_hints_save( hints, outline ); |
---|
1426 | |
---|
1427 | Exit: |
---|
1428 | return error; |
---|
1429 | } |
---|
1430 | |
---|
1431 | |
---|
1432 | /*************************************************************************/ |
---|
1433 | /*************************************************************************/ |
---|
1434 | /***** *****/ |
---|
1435 | /***** C J K S C R I P T C L A S S *****/ |
---|
1436 | /***** *****/ |
---|
1437 | /*************************************************************************/ |
---|
1438 | /*************************************************************************/ |
---|
1439 | |
---|
1440 | |
---|
1441 | static const AF_Script_UniRangeRec af_cjk_uniranges[] = |
---|
1442 | { |
---|
1443 | #if 0 |
---|
1444 | { 0x0100UL, 0xFFFFUL }, /* why this? */ |
---|
1445 | #endif |
---|
1446 | { 0x2E80UL, 0x2EFFUL }, /* CJK Radicals Supplement */ |
---|
1447 | { 0x2F00UL, 0x2FDFUL }, /* Kangxi Radicals */ |
---|
1448 | { 0x3000UL, 0x303FUL }, /* CJK Symbols and Punctuation */ |
---|
1449 | { 0x3040UL, 0x309FUL }, /* Hiragana */ |
---|
1450 | { 0x30A0UL, 0x30FFUL }, /* Katakana */ |
---|
1451 | { 0x3100UL, 0x312FUL }, /* Bopomofo */ |
---|
1452 | { 0x3130UL, 0x318FUL }, /* Hangul Compatibility Jamo */ |
---|
1453 | { 0x31A0UL, 0x31BFUL }, /* Bopomofo Extended */ |
---|
1454 | { 0x31C0UL, 0x31EFUL }, /* CJK Strokes */ |
---|
1455 | { 0x31F0UL, 0x31FFUL }, /* Katakana Phonetic Extensions */ |
---|
1456 | { 0x3200UL, 0x32FFUL }, /* Enclosed CJK Letters and Months */ |
---|
1457 | { 0x3300UL, 0x33FFUL }, /* CJK Compatibility */ |
---|
1458 | { 0x3400UL, 0x4DBFUL }, /* CJK Unified Ideographs Extension A */ |
---|
1459 | { 0x4DC0UL, 0x4DFFUL }, /* Yijing Hexagram Symbols */ |
---|
1460 | { 0x4E00UL, 0x9FFFUL }, /* CJK Unified Ideographs */ |
---|
1461 | { 0xF900UL, 0xFAFFUL }, /* CJK Compatibility Ideographs */ |
---|
1462 | { 0xFE30UL, 0xFE4FUL }, /* CJK Compatibility Forms */ |
---|
1463 | { 0xFF00UL, 0xFFEFUL }, /* Halfwidth and Fullwidth Forms */ |
---|
1464 | { 0x20000UL, 0x2A6DFUL }, /* CJK Unified Ideographs Extension B */ |
---|
1465 | { 0x2F800UL, 0x2FA1FUL }, /* CJK Compatibility Ideographs Supplement */ |
---|
1466 | { 0UL, 0UL } |
---|
1467 | }; |
---|
1468 | |
---|
1469 | |
---|
1470 | FT_CALLBACK_TABLE_DEF const AF_ScriptClassRec |
---|
1471 | af_cjk_script_class = |
---|
1472 | { |
---|
1473 | AF_SCRIPT_CJK, |
---|
1474 | af_cjk_uniranges, |
---|
1475 | |
---|
1476 | sizeof( AF_LatinMetricsRec ), |
---|
1477 | |
---|
1478 | (AF_Script_InitMetricsFunc) af_cjk_metrics_init, |
---|
1479 | (AF_Script_ScaleMetricsFunc)af_cjk_metrics_scale, |
---|
1480 | (AF_Script_DoneMetricsFunc) NULL, |
---|
1481 | |
---|
1482 | (AF_Script_InitHintsFunc) af_cjk_hints_init, |
---|
1483 | (AF_Script_ApplyHintsFunc) af_cjk_hints_apply |
---|
1484 | }; |
---|
1485 | |
---|
1486 | #else /* !AF_CONFIG_OPTION_CJK */ |
---|
1487 | |
---|
1488 | static const AF_Script_UniRangeRec af_cjk_uniranges[] = |
---|
1489 | { |
---|
1490 | { 0, 0 } |
---|
1491 | }; |
---|
1492 | |
---|
1493 | |
---|
1494 | FT_CALLBACK_TABLE_DEF const AF_ScriptClassRec |
---|
1495 | af_cjk_script_class = |
---|
1496 | { |
---|
1497 | AF_SCRIPT_CJK, |
---|
1498 | af_cjk_uniranges, |
---|
1499 | |
---|
1500 | sizeof( AF_LatinMetricsRec ), |
---|
1501 | |
---|
1502 | (AF_Script_InitMetricsFunc) NULL, |
---|
1503 | (AF_Script_ScaleMetricsFunc)NULL, |
---|
1504 | (AF_Script_DoneMetricsFunc) NULL, |
---|
1505 | |
---|
1506 | (AF_Script_InitHintsFunc) NULL, |
---|
1507 | (AF_Script_ApplyHintsFunc) NULL |
---|
1508 | }; |
---|
1509 | |
---|
1510 | #endif /* !AF_CONFIG_OPTION_CJK */ |
---|
1511 | |
---|
1512 | |
---|
1513 | /* END */ |
---|