1 /***************************************************************************/ |
|
2 /* */ |
|
3 /* pshalgo.c */ |
|
4 /* */ |
|
5 /* PostScript hinting algorithm (body). */ |
|
6 /* */ |
|
7 /* Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 */ |
|
8 /* by */ |
|
9 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ |
|
10 /* */ |
|
11 /* This file is part of the FreeType project, and may only be used */ |
|
12 /* modified and distributed under the terms of the FreeType project */ |
|
13 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ |
|
14 /* this file you indicate that you have read the license and */ |
|
15 /* understand and accept it fully. */ |
|
16 /* */ |
|
17 /***************************************************************************/ |
|
18 |
|
19 |
|
20 #include <ft2build.h> |
|
21 #include FT_INTERNAL_OBJECTS_H |
|
22 #include FT_INTERNAL_DEBUG_H |
|
23 #include FT_INTERNAL_CALC_H |
|
24 #include "pshalgo.h" |
|
25 |
|
26 #include "pshnterr.h" |
|
27 |
|
28 |
|
29 #undef FT_COMPONENT |
|
30 #define FT_COMPONENT trace_pshalgo2 |
|
31 |
|
32 |
|
33 #ifdef DEBUG_HINTER |
|
34 PSH_Hint_Table ps_debug_hint_table = 0; |
|
35 PSH_HintFunc ps_debug_hint_func = 0; |
|
36 PSH_Glyph ps_debug_glyph = 0; |
|
37 #endif |
|
38 |
|
39 |
|
40 #define COMPUTE_INFLEXS /* compute inflection points to optimize `S' */ |
|
41 /* and similar glyphs */ |
|
42 #define STRONGER /* slightly increase the contrast of smooth */ |
|
43 /* hinting */ |
|
44 |
|
45 |
|
46 /*************************************************************************/ |
|
47 /*************************************************************************/ |
|
48 /***** *****/ |
|
49 /***** BASIC HINTS RECORDINGS *****/ |
|
50 /***** *****/ |
|
51 /*************************************************************************/ |
|
52 /*************************************************************************/ |
|
53 |
|
54 /* return true if two stem hints overlap */ |
|
55 static FT_Int |
|
56 psh_hint_overlap( PSH_Hint hint1, |
|
57 PSH_Hint hint2 ) |
|
58 { |
|
59 return hint1->org_pos + hint1->org_len >= hint2->org_pos && |
|
60 hint2->org_pos + hint2->org_len >= hint1->org_pos; |
|
61 } |
|
62 |
|
63 |
|
64 /* destroy hints table */ |
|
65 static void |
|
66 psh_hint_table_done( PSH_Hint_Table table, |
|
67 FT_Memory memory ) |
|
68 { |
|
69 FT_FREE( table->zones ); |
|
70 table->num_zones = 0; |
|
71 table->zone = 0; |
|
72 |
|
73 FT_FREE( table->sort ); |
|
74 FT_FREE( table->hints ); |
|
75 table->num_hints = 0; |
|
76 table->max_hints = 0; |
|
77 table->sort_global = 0; |
|
78 } |
|
79 |
|
80 |
|
81 /* deactivate all hints in a table */ |
|
82 static void |
|
83 psh_hint_table_deactivate( PSH_Hint_Table table ) |
|
84 { |
|
85 FT_UInt count = table->max_hints; |
|
86 PSH_Hint hint = table->hints; |
|
87 |
|
88 |
|
89 for ( ; count > 0; count--, hint++ ) |
|
90 { |
|
91 psh_hint_deactivate( hint ); |
|
92 hint->order = -1; |
|
93 } |
|
94 } |
|
95 |
|
96 |
|
97 /* internal function to record a new hint */ |
|
98 static void |
|
99 psh_hint_table_record( PSH_Hint_Table table, |
|
100 FT_UInt idx ) |
|
101 { |
|
102 PSH_Hint hint = table->hints + idx; |
|
103 |
|
104 |
|
105 if ( idx >= table->max_hints ) |
|
106 { |
|
107 FT_TRACE0(( "psh_hint_table_record: invalid hint index %d\n", idx )); |
|
108 return; |
|
109 } |
|
110 |
|
111 /* ignore active hints */ |
|
112 if ( psh_hint_is_active( hint ) ) |
|
113 return; |
|
114 |
|
115 psh_hint_activate( hint ); |
|
116 |
|
117 /* now scan the current active hint set to check */ |
|
118 /* whether `hint' overlaps with another hint */ |
|
119 { |
|
120 PSH_Hint* sorted = table->sort_global; |
|
121 FT_UInt count = table->num_hints; |
|
122 PSH_Hint hint2; |
|
123 |
|
124 |
|
125 hint->parent = 0; |
|
126 for ( ; count > 0; count--, sorted++ ) |
|
127 { |
|
128 hint2 = sorted[0]; |
|
129 |
|
130 if ( psh_hint_overlap( hint, hint2 ) ) |
|
131 { |
|
132 hint->parent = hint2; |
|
133 break; |
|
134 } |
|
135 } |
|
136 } |
|
137 |
|
138 if ( table->num_hints < table->max_hints ) |
|
139 table->sort_global[table->num_hints++] = hint; |
|
140 else |
|
141 FT_TRACE0(( "psh_hint_table_record: too many sorted hints! BUG!\n" )); |
|
142 } |
|
143 |
|
144 |
|
145 static void |
|
146 psh_hint_table_record_mask( PSH_Hint_Table table, |
|
147 PS_Mask hint_mask ) |
|
148 { |
|
149 FT_Int mask = 0, val = 0; |
|
150 FT_Byte* cursor = hint_mask->bytes; |
|
151 FT_UInt idx, limit; |
|
152 |
|
153 |
|
154 limit = hint_mask->num_bits; |
|
155 |
|
156 for ( idx = 0; idx < limit; idx++ ) |
|
157 { |
|
158 if ( mask == 0 ) |
|
159 { |
|
160 val = *cursor++; |
|
161 mask = 0x80; |
|
162 } |
|
163 |
|
164 if ( val & mask ) |
|
165 psh_hint_table_record( table, idx ); |
|
166 |
|
167 mask >>= 1; |
|
168 } |
|
169 } |
|
170 |
|
171 |
|
172 /* create hints table */ |
|
173 static FT_Error |
|
174 psh_hint_table_init( PSH_Hint_Table table, |
|
175 PS_Hint_Table hints, |
|
176 PS_Mask_Table hint_masks, |
|
177 PS_Mask_Table counter_masks, |
|
178 FT_Memory memory ) |
|
179 { |
|
180 FT_UInt count; |
|
181 FT_Error error; |
|
182 |
|
183 FT_UNUSED( counter_masks ); |
|
184 |
|
185 |
|
186 count = hints->num_hints; |
|
187 |
|
188 /* allocate our tables */ |
|
189 if ( FT_NEW_ARRAY( table->sort, 2 * count ) || |
|
190 FT_NEW_ARRAY( table->hints, count ) || |
|
191 FT_NEW_ARRAY( table->zones, 2 * count + 1 ) ) |
|
192 goto Exit; |
|
193 |
|
194 table->max_hints = count; |
|
195 table->sort_global = table->sort + count; |
|
196 table->num_hints = 0; |
|
197 table->num_zones = 0; |
|
198 table->zone = 0; |
|
199 |
|
200 /* initialize the `table->hints' array */ |
|
201 { |
|
202 PSH_Hint write = table->hints; |
|
203 PS_Hint read = hints->hints; |
|
204 |
|
205 |
|
206 for ( ; count > 0; count--, write++, read++ ) |
|
207 { |
|
208 write->org_pos = read->pos; |
|
209 write->org_len = read->len; |
|
210 write->flags = read->flags; |
|
211 } |
|
212 } |
|
213 |
|
214 /* we now need to determine the initial `parent' stems; first */ |
|
215 /* activate the hints that are given by the initial hint masks */ |
|
216 if ( hint_masks ) |
|
217 { |
|
218 PS_Mask mask = hint_masks->masks; |
|
219 |
|
220 |
|
221 count = hint_masks->num_masks; |
|
222 table->hint_masks = hint_masks; |
|
223 |
|
224 for ( ; count > 0; count--, mask++ ) |
|
225 psh_hint_table_record_mask( table, mask ); |
|
226 } |
|
227 |
|
228 /* finally, do a linear parse in case some hints were left alone */ |
|
229 if ( table->num_hints != table->max_hints ) |
|
230 { |
|
231 FT_UInt idx; |
|
232 |
|
233 |
|
234 FT_TRACE0(( "psh_hint_table_init: missing/incorrect hint masks\n" )); |
|
235 |
|
236 count = table->max_hints; |
|
237 for ( idx = 0; idx < count; idx++ ) |
|
238 psh_hint_table_record( table, idx ); |
|
239 } |
|
240 |
|
241 Exit: |
|
242 return error; |
|
243 } |
|
244 |
|
245 |
|
246 static void |
|
247 psh_hint_table_activate_mask( PSH_Hint_Table table, |
|
248 PS_Mask hint_mask ) |
|
249 { |
|
250 FT_Int mask = 0, val = 0; |
|
251 FT_Byte* cursor = hint_mask->bytes; |
|
252 FT_UInt idx, limit, count; |
|
253 |
|
254 |
|
255 limit = hint_mask->num_bits; |
|
256 count = 0; |
|
257 |
|
258 psh_hint_table_deactivate( table ); |
|
259 |
|
260 for ( idx = 0; idx < limit; idx++ ) |
|
261 { |
|
262 if ( mask == 0 ) |
|
263 { |
|
264 val = *cursor++; |
|
265 mask = 0x80; |
|
266 } |
|
267 |
|
268 if ( val & mask ) |
|
269 { |
|
270 PSH_Hint hint = &table->hints[idx]; |
|
271 |
|
272 |
|
273 if ( !psh_hint_is_active( hint ) ) |
|
274 { |
|
275 FT_UInt count2; |
|
276 |
|
277 #if 0 |
|
278 PSH_Hint* sort = table->sort; |
|
279 PSH_Hint hint2; |
|
280 |
|
281 |
|
282 for ( count2 = count; count2 > 0; count2--, sort++ ) |
|
283 { |
|
284 hint2 = sort[0]; |
|
285 if ( psh_hint_overlap( hint, hint2 ) ) |
|
286 FT_TRACE0(( "psh_hint_table_activate_mask:" |
|
287 " found overlapping hints\n" )) |
|
288 } |
|
289 #else |
|
290 count2 = 0; |
|
291 #endif |
|
292 |
|
293 if ( count2 == 0 ) |
|
294 { |
|
295 psh_hint_activate( hint ); |
|
296 if ( count < table->max_hints ) |
|
297 table->sort[count++] = hint; |
|
298 else |
|
299 FT_TRACE0(( "psh_hint_tableactivate_mask:" |
|
300 " too many active hints\n" )); |
|
301 } |
|
302 } |
|
303 } |
|
304 |
|
305 mask >>= 1; |
|
306 } |
|
307 table->num_hints = count; |
|
308 |
|
309 /* now, sort the hints; they are guaranteed to not overlap */ |
|
310 /* so we can compare their "org_pos" field directly */ |
|
311 { |
|
312 FT_Int i1, i2; |
|
313 PSH_Hint hint1, hint2; |
|
314 PSH_Hint* sort = table->sort; |
|
315 |
|
316 |
|
317 /* a simple bubble sort will do, since in 99% of cases, the hints */ |
|
318 /* will be already sorted -- and the sort will be linear */ |
|
319 for ( i1 = 1; i1 < (FT_Int)count; i1++ ) |
|
320 { |
|
321 hint1 = sort[i1]; |
|
322 for ( i2 = i1 - 1; i2 >= 0; i2-- ) |
|
323 { |
|
324 hint2 = sort[i2]; |
|
325 |
|
326 if ( hint2->org_pos < hint1->org_pos ) |
|
327 break; |
|
328 |
|
329 sort[i2 + 1] = hint2; |
|
330 sort[i2] = hint1; |
|
331 } |
|
332 } |
|
333 } |
|
334 } |
|
335 |
|
336 |
|
337 /*************************************************************************/ |
|
338 /*************************************************************************/ |
|
339 /***** *****/ |
|
340 /***** HINTS GRID-FITTING AND OPTIMIZATION *****/ |
|
341 /***** *****/ |
|
342 /*************************************************************************/ |
|
343 /*************************************************************************/ |
|
344 |
|
345 #if 1 |
|
346 static FT_Pos |
|
347 psh_dimension_quantize_len( PSH_Dimension dim, |
|
348 FT_Pos len, |
|
349 FT_Bool do_snapping ) |
|
350 { |
|
351 if ( len <= 64 ) |
|
352 len = 64; |
|
353 else |
|
354 { |
|
355 FT_Pos delta = len - dim->stdw.widths[0].cur; |
|
356 |
|
357 |
|
358 if ( delta < 0 ) |
|
359 delta = -delta; |
|
360 |
|
361 if ( delta < 40 ) |
|
362 { |
|
363 len = dim->stdw.widths[0].cur; |
|
364 if ( len < 48 ) |
|
365 len = 48; |
|
366 } |
|
367 |
|
368 if ( len < 3 * 64 ) |
|
369 { |
|
370 delta = ( len & 63 ); |
|
371 len &= -64; |
|
372 |
|
373 if ( delta < 10 ) |
|
374 len += delta; |
|
375 |
|
376 else if ( delta < 32 ) |
|
377 len += 10; |
|
378 |
|
379 else if ( delta < 54 ) |
|
380 len += 54; |
|
381 |
|
382 else |
|
383 len += delta; |
|
384 } |
|
385 else |
|
386 len = FT_PIX_ROUND( len ); |
|
387 } |
|
388 |
|
389 if ( do_snapping ) |
|
390 len = FT_PIX_ROUND( len ); |
|
391 |
|
392 return len; |
|
393 } |
|
394 #endif /* 0 */ |
|
395 |
|
396 |
|
397 #ifdef DEBUG_HINTER |
|
398 |
|
399 static void |
|
400 ps_simple_scale( PSH_Hint_Table table, |
|
401 FT_Fixed scale, |
|
402 FT_Fixed delta, |
|
403 FT_Int dimension ) |
|
404 { |
|
405 PSH_Hint hint; |
|
406 FT_UInt count; |
|
407 |
|
408 |
|
409 for ( count = 0; count < table->max_hints; count++ ) |
|
410 { |
|
411 hint = table->hints + count; |
|
412 |
|
413 hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta; |
|
414 hint->cur_len = FT_MulFix( hint->org_len, scale ); |
|
415 |
|
416 if ( ps_debug_hint_func ) |
|
417 ps_debug_hint_func( hint, dimension ); |
|
418 } |
|
419 } |
|
420 |
|
421 #endif /* DEBUG_HINTER */ |
|
422 |
|
423 |
|
424 static FT_Fixed |
|
425 psh_hint_snap_stem_side_delta( FT_Fixed pos, |
|
426 FT_Fixed len ) |
|
427 { |
|
428 FT_Fixed delta1 = FT_PIX_ROUND( pos ) - pos; |
|
429 FT_Fixed delta2 = FT_PIX_ROUND( pos + len ) - pos - len; |
|
430 |
|
431 |
|
432 if ( FT_ABS( delta1 ) <= FT_ABS( delta2 ) ) |
|
433 return delta1; |
|
434 else |
|
435 return delta2; |
|
436 } |
|
437 |
|
438 |
|
439 static void |
|
440 psh_hint_align( PSH_Hint hint, |
|
441 PSH_Globals globals, |
|
442 FT_Int dimension, |
|
443 PSH_Glyph glyph ) |
|
444 { |
|
445 PSH_Dimension dim = &globals->dimension[dimension]; |
|
446 FT_Fixed scale = dim->scale_mult; |
|
447 FT_Fixed delta = dim->scale_delta; |
|
448 |
|
449 |
|
450 if ( !psh_hint_is_fitted( hint ) ) |
|
451 { |
|
452 FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; |
|
453 FT_Pos len = FT_MulFix( hint->org_len, scale ); |
|
454 |
|
455 FT_Int do_snapping; |
|
456 FT_Pos fit_len; |
|
457 PSH_AlignmentRec align; |
|
458 |
|
459 |
|
460 /* ignore stem alignments when requested through the hint flags */ |
|
461 if ( ( dimension == 0 && !glyph->do_horz_hints ) || |
|
462 ( dimension == 1 && !glyph->do_vert_hints ) ) |
|
463 { |
|
464 hint->cur_pos = pos; |
|
465 hint->cur_len = len; |
|
466 |
|
467 psh_hint_set_fitted( hint ); |
|
468 return; |
|
469 } |
|
470 |
|
471 /* perform stem snapping when requested - this is necessary |
|
472 * for monochrome and LCD hinting modes only |
|
473 */ |
|
474 do_snapping = ( dimension == 0 && glyph->do_horz_snapping ) || |
|
475 ( dimension == 1 && glyph->do_vert_snapping ); |
|
476 |
|
477 hint->cur_len = fit_len = len; |
|
478 |
|
479 /* check blue zones for horizontal stems */ |
|
480 align.align = PSH_BLUE_ALIGN_NONE; |
|
481 align.align_bot = align.align_top = 0; |
|
482 |
|
483 if ( dimension == 1 ) |
|
484 psh_blues_snap_stem( &globals->blues, |
|
485 hint->org_pos + hint->org_len, |
|
486 hint->org_pos, |
|
487 &align ); |
|
488 |
|
489 switch ( align.align ) |
|
490 { |
|
491 case PSH_BLUE_ALIGN_TOP: |
|
492 /* the top of the stem is aligned against a blue zone */ |
|
493 hint->cur_pos = align.align_top - fit_len; |
|
494 break; |
|
495 |
|
496 case PSH_BLUE_ALIGN_BOT: |
|
497 /* the bottom of the stem is aligned against a blue zone */ |
|
498 hint->cur_pos = align.align_bot; |
|
499 break; |
|
500 |
|
501 case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: |
|
502 /* both edges of the stem are aligned against blue zones */ |
|
503 hint->cur_pos = align.align_bot; |
|
504 hint->cur_len = align.align_top - align.align_bot; |
|
505 break; |
|
506 |
|
507 default: |
|
508 { |
|
509 PSH_Hint parent = hint->parent; |
|
510 |
|
511 |
|
512 if ( parent ) |
|
513 { |
|
514 FT_Pos par_org_center, par_cur_center; |
|
515 FT_Pos cur_org_center, cur_delta; |
|
516 |
|
517 |
|
518 /* ensure that parent is already fitted */ |
|
519 if ( !psh_hint_is_fitted( parent ) ) |
|
520 psh_hint_align( parent, globals, dimension, glyph ); |
|
521 |
|
522 /* keep original relation between hints, this is, use the */ |
|
523 /* scaled distance between the centers of the hints to */ |
|
524 /* compute the new position */ |
|
525 par_org_center = parent->org_pos + ( parent->org_len >> 1 ); |
|
526 par_cur_center = parent->cur_pos + ( parent->cur_len >> 1 ); |
|
527 cur_org_center = hint->org_pos + ( hint->org_len >> 1 ); |
|
528 |
|
529 cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); |
|
530 pos = par_cur_center + cur_delta - ( len >> 1 ); |
|
531 } |
|
532 |
|
533 hint->cur_pos = pos; |
|
534 hint->cur_len = fit_len; |
|
535 |
|
536 /* Stem adjustment tries to snap stem widths to standard |
|
537 * ones. This is important to prevent unpleasant rounding |
|
538 * artefacts. |
|
539 */ |
|
540 if ( glyph->do_stem_adjust ) |
|
541 { |
|
542 if ( len <= 64 ) |
|
543 { |
|
544 /* the stem is less than one pixel; we will center it |
|
545 * around the nearest pixel center |
|
546 */ |
|
547 if ( len >= 32 ) |
|
548 { |
|
549 /* This is a special case where we also widen the stem |
|
550 * and align it to the pixel grid. |
|
551 * |
|
552 * stem_center = pos + (len/2) |
|
553 * nearest_pixel_center = FT_ROUND(stem_center-32)+32 |
|
554 * new_pos = nearest_pixel_center-32 |
|
555 * = FT_ROUND(stem_center-32) |
|
556 * = FT_FLOOR(stem_center-32+32) |
|
557 * = FT_FLOOR(stem_center) |
|
558 * new_len = 64 |
|
559 */ |
|
560 pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ); |
|
561 len = 64; |
|
562 } |
|
563 else if ( len > 0 ) |
|
564 { |
|
565 /* This is a very small stem; we simply align it to the |
|
566 * pixel grid, trying to find the minimal displacement. |
|
567 * |
|
568 * left = pos |
|
569 * right = pos + len |
|
570 * left_nearest_edge = ROUND(pos) |
|
571 * right_nearest_edge = ROUND(right) |
|
572 * |
|
573 * if ( ABS(left_nearest_edge - left) <= |
|
574 * ABS(right_nearest_edge - right) ) |
|
575 * new_pos = left |
|
576 * else |
|
577 * new_pos = right |
|
578 */ |
|
579 FT_Pos left_nearest = FT_PIX_ROUND( pos ); |
|
580 FT_Pos right_nearest = FT_PIX_ROUND( pos + len ); |
|
581 FT_Pos left_disp = left_nearest - pos; |
|
582 FT_Pos right_disp = right_nearest - ( pos + len ); |
|
583 |
|
584 |
|
585 if ( left_disp < 0 ) |
|
586 left_disp = -left_disp; |
|
587 if ( right_disp < 0 ) |
|
588 right_disp = -right_disp; |
|
589 if ( left_disp <= right_disp ) |
|
590 pos = left_nearest; |
|
591 else |
|
592 pos = right_nearest; |
|
593 } |
|
594 else |
|
595 { |
|
596 /* this is a ghost stem; we simply round it */ |
|
597 pos = FT_PIX_ROUND( pos ); |
|
598 } |
|
599 } |
|
600 else |
|
601 { |
|
602 len = psh_dimension_quantize_len( dim, len, 0 ); |
|
603 } |
|
604 } |
|
605 |
|
606 /* now that we have a good hinted stem width, try to position */ |
|
607 /* the stem along a pixel grid integer coordinate */ |
|
608 hint->cur_pos = pos + psh_hint_snap_stem_side_delta( pos, len ); |
|
609 hint->cur_len = len; |
|
610 } |
|
611 } |
|
612 |
|
613 if ( do_snapping ) |
|
614 { |
|
615 pos = hint->cur_pos; |
|
616 len = hint->cur_len; |
|
617 |
|
618 if ( len < 64 ) |
|
619 len = 64; |
|
620 else |
|
621 len = FT_PIX_ROUND( len ); |
|
622 |
|
623 switch ( align.align ) |
|
624 { |
|
625 case PSH_BLUE_ALIGN_TOP: |
|
626 hint->cur_pos = align.align_top - len; |
|
627 hint->cur_len = len; |
|
628 break; |
|
629 |
|
630 case PSH_BLUE_ALIGN_BOT: |
|
631 hint->cur_len = len; |
|
632 break; |
|
633 |
|
634 case PSH_BLUE_ALIGN_BOT | PSH_BLUE_ALIGN_TOP: |
|
635 /* don't touch */ |
|
636 break; |
|
637 |
|
638 |
|
639 default: |
|
640 hint->cur_len = len; |
|
641 if ( len & 64 ) |
|
642 pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ) + 32; |
|
643 else |
|
644 pos = FT_PIX_ROUND( pos + ( len >> 1 ) ); |
|
645 |
|
646 hint->cur_pos = pos - ( len >> 1 ); |
|
647 hint->cur_len = len; |
|
648 } |
|
649 } |
|
650 |
|
651 psh_hint_set_fitted( hint ); |
|
652 |
|
653 #ifdef DEBUG_HINTER |
|
654 if ( ps_debug_hint_func ) |
|
655 ps_debug_hint_func( hint, dimension ); |
|
656 #endif |
|
657 } |
|
658 } |
|
659 |
|
660 |
|
661 #if 0 /* not used for now, experimental */ |
|
662 |
|
663 /* |
|
664 * A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT) |
|
665 * of stems |
|
666 */ |
|
667 static void |
|
668 psh_hint_align_light( PSH_Hint hint, |
|
669 PSH_Globals globals, |
|
670 FT_Int dimension, |
|
671 PSH_Glyph glyph ) |
|
672 { |
|
673 PSH_Dimension dim = &globals->dimension[dimension]; |
|
674 FT_Fixed scale = dim->scale_mult; |
|
675 FT_Fixed delta = dim->scale_delta; |
|
676 |
|
677 |
|
678 if ( !psh_hint_is_fitted( hint ) ) |
|
679 { |
|
680 FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; |
|
681 FT_Pos len = FT_MulFix( hint->org_len, scale ); |
|
682 |
|
683 FT_Pos fit_len; |
|
684 |
|
685 PSH_AlignmentRec align; |
|
686 |
|
687 |
|
688 /* ignore stem alignments when requested through the hint flags */ |
|
689 if ( ( dimension == 0 && !glyph->do_horz_hints ) || |
|
690 ( dimension == 1 && !glyph->do_vert_hints ) ) |
|
691 { |
|
692 hint->cur_pos = pos; |
|
693 hint->cur_len = len; |
|
694 |
|
695 psh_hint_set_fitted( hint ); |
|
696 return; |
|
697 } |
|
698 |
|
699 fit_len = len; |
|
700 |
|
701 hint->cur_len = fit_len; |
|
702 |
|
703 /* check blue zones for horizontal stems */ |
|
704 align.align = PSH_BLUE_ALIGN_NONE; |
|
705 align.align_bot = align.align_top = 0; |
|
706 |
|
707 if ( dimension == 1 ) |
|
708 psh_blues_snap_stem( &globals->blues, |
|
709 hint->org_pos + hint->org_len, |
|
710 hint->org_pos, |
|
711 &align ); |
|
712 |
|
713 switch ( align.align ) |
|
714 { |
|
715 case PSH_BLUE_ALIGN_TOP: |
|
716 /* the top of the stem is aligned against a blue zone */ |
|
717 hint->cur_pos = align.align_top - fit_len; |
|
718 break; |
|
719 |
|
720 case PSH_BLUE_ALIGN_BOT: |
|
721 /* the bottom of the stem is aligned against a blue zone */ |
|
722 hint->cur_pos = align.align_bot; |
|
723 break; |
|
724 |
|
725 case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: |
|
726 /* both edges of the stem are aligned against blue zones */ |
|
727 hint->cur_pos = align.align_bot; |
|
728 hint->cur_len = align.align_top - align.align_bot; |
|
729 break; |
|
730 |
|
731 default: |
|
732 { |
|
733 PSH_Hint parent = hint->parent; |
|
734 |
|
735 |
|
736 if ( parent ) |
|
737 { |
|
738 FT_Pos par_org_center, par_cur_center; |
|
739 FT_Pos cur_org_center, cur_delta; |
|
740 |
|
741 |
|
742 /* ensure that parent is already fitted */ |
|
743 if ( !psh_hint_is_fitted( parent ) ) |
|
744 psh_hint_align_light( parent, globals, dimension, glyph ); |
|
745 |
|
746 par_org_center = parent->org_pos + ( parent->org_len / 2 ); |
|
747 par_cur_center = parent->cur_pos + ( parent->cur_len / 2 ); |
|
748 cur_org_center = hint->org_pos + ( hint->org_len / 2 ); |
|
749 |
|
750 cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); |
|
751 pos = par_cur_center + cur_delta - ( len >> 1 ); |
|
752 } |
|
753 |
|
754 /* Stems less than one pixel wide are easy -- we want to |
|
755 * make them as dark as possible, so they must fall within |
|
756 * one pixel. If the stem is split between two pixels |
|
757 * then snap the edge that is nearer to the pixel boundary |
|
758 * to the pixel boundary. |
|
759 */ |
|
760 if ( len <= 64 ) |
|
761 { |
|
762 if ( ( pos + len + 63 ) / 64 != pos / 64 + 1 ) |
|
763 pos += psh_hint_snap_stem_side_delta ( pos, len ); |
|
764 } |
|
765 |
|
766 /* Position stems other to minimize the amount of mid-grays. |
|
767 * There are, in general, two positions that do this, |
|
768 * illustrated as A) and B) below. |
|
769 * |
|
770 * + + + + |
|
771 * |
|
772 * A) |--------------------------------| |
|
773 * B) |--------------------------------| |
|
774 * C) |--------------------------------| |
|
775 * |
|
776 * Position A) (split the excess stem equally) should be better |
|
777 * for stems of width N + f where f < 0.5. |
|
778 * |
|
779 * Position B) (split the deficiency equally) should be better |
|
780 * for stems of width N + f where f > 0.5. |
|
781 * |
|
782 * It turns out though that minimizing the total number of lit |
|
783 * pixels is also important, so position C), with one edge |
|
784 * aligned with a pixel boundary is actually preferable |
|
785 * to A). There are also more possibile positions for C) than |
|
786 * for A) or B), so it involves less distortion of the overall |
|
787 * character shape. |
|
788 */ |
|
789 else /* len > 64 */ |
|
790 { |
|
791 FT_Fixed frac_len = len & 63; |
|
792 FT_Fixed center = pos + ( len >> 1 ); |
|
793 FT_Fixed delta_a, delta_b; |
|
794 |
|
795 |
|
796 if ( ( len / 64 ) & 1 ) |
|
797 { |
|
798 delta_a = FT_PIX_FLOOR( center ) + 32 - center; |
|
799 delta_b = FT_PIX_ROUND( center ) - center; |
|
800 } |
|
801 else |
|
802 { |
|
803 delta_a = FT_PIX_ROUND( center ) - center; |
|
804 delta_b = FT_PIX_FLOOR( center ) + 32 - center; |
|
805 } |
|
806 |
|
807 /* We choose between B) and C) above based on the amount |
|
808 * of fractinal stem width; for small amounts, choose |
|
809 * C) always, for large amounts, B) always, and inbetween, |
|
810 * pick whichever one involves less stem movement. |
|
811 */ |
|
812 if ( frac_len < 32 ) |
|
813 { |
|
814 pos += psh_hint_snap_stem_side_delta ( pos, len ); |
|
815 } |
|
816 else if ( frac_len < 48 ) |
|
817 { |
|
818 FT_Fixed side_delta = psh_hint_snap_stem_side_delta ( pos, |
|
819 len ); |
|
820 |
|
821 if ( FT_ABS( side_delta ) < FT_ABS( delta_b ) ) |
|
822 pos += side_delta; |
|
823 else |
|
824 pos += delta_b; |
|
825 } |
|
826 else |
|
827 { |
|
828 pos += delta_b; |
|
829 } |
|
830 } |
|
831 |
|
832 hint->cur_pos = pos; |
|
833 } |
|
834 } /* switch */ |
|
835 |
|
836 psh_hint_set_fitted( hint ); |
|
837 |
|
838 #ifdef DEBUG_HINTER |
|
839 if ( ps_debug_hint_func ) |
|
840 ps_debug_hint_func( hint, dimension ); |
|
841 #endif |
|
842 } |
|
843 } |
|
844 |
|
845 #endif /* 0 */ |
|
846 |
|
847 |
|
848 static void |
|
849 psh_hint_table_align_hints( PSH_Hint_Table table, |
|
850 PSH_Globals globals, |
|
851 FT_Int dimension, |
|
852 PSH_Glyph glyph ) |
|
853 { |
|
854 PSH_Hint hint; |
|
855 FT_UInt count; |
|
856 |
|
857 #ifdef DEBUG_HINTER |
|
858 |
|
859 PSH_Dimension dim = &globals->dimension[dimension]; |
|
860 FT_Fixed scale = dim->scale_mult; |
|
861 FT_Fixed delta = dim->scale_delta; |
|
862 |
|
863 |
|
864 if ( ps_debug_no_vert_hints && dimension == 0 ) |
|
865 { |
|
866 ps_simple_scale( table, scale, delta, dimension ); |
|
867 return; |
|
868 } |
|
869 |
|
870 if ( ps_debug_no_horz_hints && dimension == 1 ) |
|
871 { |
|
872 ps_simple_scale( table, scale, delta, dimension ); |
|
873 return; |
|
874 } |
|
875 |
|
876 #endif /* DEBUG_HINTER*/ |
|
877 |
|
878 hint = table->hints; |
|
879 count = table->max_hints; |
|
880 |
|
881 for ( ; count > 0; count--, hint++ ) |
|
882 psh_hint_align( hint, globals, dimension, glyph ); |
|
883 } |
|
884 |
|
885 |
|
886 /*************************************************************************/ |
|
887 /*************************************************************************/ |
|
888 /***** *****/ |
|
889 /***** POINTS INTERPOLATION ROUTINES *****/ |
|
890 /***** *****/ |
|
891 /*************************************************************************/ |
|
892 /*************************************************************************/ |
|
893 |
|
894 #define PSH_ZONE_MIN -3200000L |
|
895 #define PSH_ZONE_MAX +3200000L |
|
896 |
|
897 #define xxDEBUG_ZONES |
|
898 |
|
899 |
|
900 #ifdef DEBUG_ZONES |
|
901 |
|
902 #include FT_CONFIG_STANDARD_LIBRARY_H |
|
903 |
|
904 static void |
|
905 psh_print_zone( PSH_Zone zone ) |
|
906 { |
|
907 printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n", |
|
908 zone->scale / 65536.0, |
|
909 zone->delta / 64.0, |
|
910 zone->min, |
|
911 zone->max ); |
|
912 } |
|
913 |
|
914 #else |
|
915 |
|
916 #define psh_print_zone( x ) do { } while ( 0 ) |
|
917 |
|
918 #endif /* DEBUG_ZONES */ |
|
919 |
|
920 |
|
921 /*************************************************************************/ |
|
922 /*************************************************************************/ |
|
923 /***** *****/ |
|
924 /***** HINTER GLYPH MANAGEMENT *****/ |
|
925 /***** *****/ |
|
926 /*************************************************************************/ |
|
927 /*************************************************************************/ |
|
928 |
|
929 #if 1 |
|
930 |
|
931 #define psh_corner_is_flat ft_corner_is_flat |
|
932 #define psh_corner_orientation ft_corner_orientation |
|
933 |
|
934 #else |
|
935 |
|
936 FT_LOCAL_DEF( FT_Int ) |
|
937 psh_corner_is_flat( FT_Pos x_in, |
|
938 FT_Pos y_in, |
|
939 FT_Pos x_out, |
|
940 FT_Pos y_out ) |
|
941 { |
|
942 FT_Pos ax = x_in; |
|
943 FT_Pos ay = y_in; |
|
944 |
|
945 FT_Pos d_in, d_out, d_corner; |
|
946 |
|
947 |
|
948 if ( ax < 0 ) |
|
949 ax = -ax; |
|
950 if ( ay < 0 ) |
|
951 ay = -ay; |
|
952 d_in = ax + ay; |
|
953 |
|
954 ax = x_out; |
|
955 if ( ax < 0 ) |
|
956 ax = -ax; |
|
957 ay = y_out; |
|
958 if ( ay < 0 ) |
|
959 ay = -ay; |
|
960 d_out = ax + ay; |
|
961 |
|
962 ax = x_out + x_in; |
|
963 if ( ax < 0 ) |
|
964 ax = -ax; |
|
965 ay = y_out + y_in; |
|
966 if ( ay < 0 ) |
|
967 ay = -ay; |
|
968 d_corner = ax + ay; |
|
969 |
|
970 return ( d_in + d_out - d_corner ) < ( d_corner >> 4 ); |
|
971 } |
|
972 |
|
973 static FT_Int |
|
974 psh_corner_orientation( FT_Pos in_x, |
|
975 FT_Pos in_y, |
|
976 FT_Pos out_x, |
|
977 FT_Pos out_y ) |
|
978 { |
|
979 FT_Int result; |
|
980 |
|
981 |
|
982 /* deal with the trivial cases quickly */ |
|
983 if ( in_y == 0 ) |
|
984 { |
|
985 if ( in_x >= 0 ) |
|
986 result = out_y; |
|
987 else |
|
988 result = -out_y; |
|
989 } |
|
990 else if ( in_x == 0 ) |
|
991 { |
|
992 if ( in_y >= 0 ) |
|
993 result = -out_x; |
|
994 else |
|
995 result = out_x; |
|
996 } |
|
997 else if ( out_y == 0 ) |
|
998 { |
|
999 if ( out_x >= 0 ) |
|
1000 result = in_y; |
|
1001 else |
|
1002 result = -in_y; |
|
1003 } |
|
1004 else if ( out_x == 0 ) |
|
1005 { |
|
1006 if ( out_y >= 0 ) |
|
1007 result = -in_x; |
|
1008 else |
|
1009 result = in_x; |
|
1010 } |
|
1011 else /* general case */ |
|
1012 { |
|
1013 long long delta = (long long)in_x * out_y - (long long)in_y * out_x; |
|
1014 |
|
1015 if ( delta == 0 ) |
|
1016 result = 0; |
|
1017 else |
|
1018 result = 1 - 2 * ( delta < 0 ); |
|
1019 } |
|
1020 |
|
1021 return result; |
|
1022 } |
|
1023 |
|
1024 #endif /* !1 */ |
|
1025 |
|
1026 |
|
1027 #ifdef COMPUTE_INFLEXS |
|
1028 |
|
1029 /* compute all inflex points in a given glyph */ |
|
1030 static void |
|
1031 psh_glyph_compute_inflections( PSH_Glyph glyph ) |
|
1032 { |
|
1033 FT_UInt n; |
|
1034 |
|
1035 |
|
1036 for ( n = 0; n < glyph->num_contours; n++ ) |
|
1037 { |
|
1038 PSH_Point first, start, end, before, after; |
|
1039 FT_Pos in_x, in_y, out_x, out_y; |
|
1040 FT_Int orient_prev, orient_cur; |
|
1041 FT_Int finished = 0; |
|
1042 |
|
1043 |
|
1044 /* we need at least 4 points to create an inflection point */ |
|
1045 if ( glyph->contours[n].count < 4 ) |
|
1046 continue; |
|
1047 |
|
1048 /* compute first segment in contour */ |
|
1049 first = glyph->contours[n].start; |
|
1050 |
|
1051 start = end = first; |
|
1052 do |
|
1053 { |
|
1054 end = end->next; |
|
1055 if ( end == first ) |
|
1056 goto Skip; |
|
1057 |
|
1058 in_x = end->org_u - start->org_u; |
|
1059 in_y = end->org_v - start->org_v; |
|
1060 |
|
1061 } while ( in_x == 0 && in_y == 0 ); |
|
1062 |
|
1063 /* extend the segment start whenever possible */ |
|
1064 before = start; |
|
1065 do |
|
1066 { |
|
1067 do |
|
1068 { |
|
1069 start = before; |
|
1070 before = before->prev; |
|
1071 if ( before == first ) |
|
1072 goto Skip; |
|
1073 |
|
1074 out_x = start->org_u - before->org_u; |
|
1075 out_y = start->org_v - before->org_v; |
|
1076 |
|
1077 } while ( out_x == 0 && out_y == 0 ); |
|
1078 |
|
1079 orient_prev = psh_corner_orientation( in_x, in_y, out_x, out_y ); |
|
1080 |
|
1081 } while ( orient_prev == 0 ); |
|
1082 |
|
1083 first = start; |
|
1084 in_x = out_x; |
|
1085 in_y = out_y; |
|
1086 |
|
1087 /* now, process all segments in the contour */ |
|
1088 do |
|
1089 { |
|
1090 /* first, extend current segment's end whenever possible */ |
|
1091 after = end; |
|
1092 do |
|
1093 { |
|
1094 do |
|
1095 { |
|
1096 end = after; |
|
1097 after = after->next; |
|
1098 if ( after == first ) |
|
1099 finished = 1; |
|
1100 |
|
1101 out_x = after->org_u - end->org_u; |
|
1102 out_y = after->org_v - end->org_v; |
|
1103 |
|
1104 } while ( out_x == 0 && out_y == 0 ); |
|
1105 |
|
1106 orient_cur = psh_corner_orientation( in_x, in_y, out_x, out_y ); |
|
1107 |
|
1108 } while ( orient_cur == 0 ); |
|
1109 |
|
1110 if ( ( orient_cur ^ orient_prev ) < 0 ) |
|
1111 { |
|
1112 do |
|
1113 { |
|
1114 psh_point_set_inflex( start ); |
|
1115 start = start->next; |
|
1116 } |
|
1117 while ( start != end ); |
|
1118 |
|
1119 psh_point_set_inflex( start ); |
|
1120 } |
|
1121 |
|
1122 start = end; |
|
1123 end = after; |
|
1124 orient_prev = orient_cur; |
|
1125 in_x = out_x; |
|
1126 in_y = out_y; |
|
1127 |
|
1128 } while ( !finished ); |
|
1129 |
|
1130 Skip: |
|
1131 ; |
|
1132 } |
|
1133 } |
|
1134 |
|
1135 #endif /* COMPUTE_INFLEXS */ |
|
1136 |
|
1137 |
|
1138 static void |
|
1139 psh_glyph_done( PSH_Glyph glyph ) |
|
1140 { |
|
1141 FT_Memory memory = glyph->memory; |
|
1142 |
|
1143 |
|
1144 psh_hint_table_done( &glyph->hint_tables[1], memory ); |
|
1145 psh_hint_table_done( &glyph->hint_tables[0], memory ); |
|
1146 |
|
1147 FT_FREE( glyph->points ); |
|
1148 FT_FREE( glyph->contours ); |
|
1149 |
|
1150 glyph->num_points = 0; |
|
1151 glyph->num_contours = 0; |
|
1152 |
|
1153 glyph->memory = 0; |
|
1154 } |
|
1155 |
|
1156 |
|
1157 static int |
|
1158 psh_compute_dir( FT_Pos dx, |
|
1159 FT_Pos dy ) |
|
1160 { |
|
1161 FT_Pos ax, ay; |
|
1162 int result = PSH_DIR_NONE; |
|
1163 |
|
1164 |
|
1165 ax = ( dx >= 0 ) ? dx : -dx; |
|
1166 ay = ( dy >= 0 ) ? dy : -dy; |
|
1167 |
|
1168 if ( ay * 12 < ax ) |
|
1169 { |
|
1170 /* |dy| <<< |dx| means a near-horizontal segment */ |
|
1171 result = ( dx >= 0 ) ? PSH_DIR_RIGHT : PSH_DIR_LEFT; |
|
1172 } |
|
1173 else if ( ax * 12 < ay ) |
|
1174 { |
|
1175 /* |dx| <<< |dy| means a near-vertical segment */ |
|
1176 result = ( dy >= 0 ) ? PSH_DIR_UP : PSH_DIR_DOWN; |
|
1177 } |
|
1178 |
|
1179 return result; |
|
1180 } |
|
1181 |
|
1182 |
|
1183 /* load outline point coordinates into hinter glyph */ |
|
1184 static void |
|
1185 psh_glyph_load_points( PSH_Glyph glyph, |
|
1186 FT_Int dimension ) |
|
1187 { |
|
1188 FT_Vector* vec = glyph->outline->points; |
|
1189 PSH_Point point = glyph->points; |
|
1190 FT_UInt count = glyph->num_points; |
|
1191 |
|
1192 |
|
1193 for ( ; count > 0; count--, point++, vec++ ) |
|
1194 { |
|
1195 point->flags2 = 0; |
|
1196 point->hint = NULL; |
|
1197 if ( dimension == 0 ) |
|
1198 { |
|
1199 point->org_u = vec->x; |
|
1200 point->org_v = vec->y; |
|
1201 } |
|
1202 else |
|
1203 { |
|
1204 point->org_u = vec->y; |
|
1205 point->org_v = vec->x; |
|
1206 } |
|
1207 |
|
1208 #ifdef DEBUG_HINTER |
|
1209 point->org_x = vec->x; |
|
1210 point->org_y = vec->y; |
|
1211 #endif |
|
1212 |
|
1213 } |
|
1214 } |
|
1215 |
|
1216 |
|
1217 /* save hinted point coordinates back to outline */ |
|
1218 static void |
|
1219 psh_glyph_save_points( PSH_Glyph glyph, |
|
1220 FT_Int dimension ) |
|
1221 { |
|
1222 FT_UInt n; |
|
1223 PSH_Point point = glyph->points; |
|
1224 FT_Vector* vec = glyph->outline->points; |
|
1225 char* tags = glyph->outline->tags; |
|
1226 |
|
1227 |
|
1228 for ( n = 0; n < glyph->num_points; n++ ) |
|
1229 { |
|
1230 if ( dimension == 0 ) |
|
1231 vec[n].x = point->cur_u; |
|
1232 else |
|
1233 vec[n].y = point->cur_u; |
|
1234 |
|
1235 if ( psh_point_is_strong( point ) ) |
|
1236 tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 ); |
|
1237 |
|
1238 #ifdef DEBUG_HINTER |
|
1239 |
|
1240 if ( dimension == 0 ) |
|
1241 { |
|
1242 point->cur_x = point->cur_u; |
|
1243 point->flags_x = point->flags2 | point->flags; |
|
1244 } |
|
1245 else |
|
1246 { |
|
1247 point->cur_y = point->cur_u; |
|
1248 point->flags_y = point->flags2 | point->flags; |
|
1249 } |
|
1250 |
|
1251 #endif |
|
1252 |
|
1253 point++; |
|
1254 } |
|
1255 } |
|
1256 |
|
1257 |
|
1258 static FT_Error |
|
1259 psh_glyph_init( PSH_Glyph glyph, |
|
1260 FT_Outline* outline, |
|
1261 PS_Hints ps_hints, |
|
1262 PSH_Globals globals ) |
|
1263 { |
|
1264 FT_Error error; |
|
1265 FT_Memory memory; |
|
1266 |
|
1267 |
|
1268 /* clear all fields */ |
|
1269 FT_MEM_ZERO( glyph, sizeof ( *glyph ) ); |
|
1270 |
|
1271 memory = glyph->memory = globals->memory; |
|
1272 |
|
1273 /* allocate and setup points + contours arrays */ |
|
1274 if ( FT_NEW_ARRAY( glyph->points, outline->n_points ) || |
|
1275 FT_NEW_ARRAY( glyph->contours, outline->n_contours ) ) |
|
1276 goto Exit; |
|
1277 |
|
1278 glyph->num_points = outline->n_points; |
|
1279 glyph->num_contours = outline->n_contours; |
|
1280 |
|
1281 { |
|
1282 FT_UInt first = 0, next, n; |
|
1283 PSH_Point points = glyph->points; |
|
1284 PSH_Contour contour = glyph->contours; |
|
1285 |
|
1286 |
|
1287 for ( n = 0; n < glyph->num_contours; n++ ) |
|
1288 { |
|
1289 FT_Int count; |
|
1290 PSH_Point point; |
|
1291 |
|
1292 |
|
1293 next = outline->contours[n] + 1; |
|
1294 count = next - first; |
|
1295 |
|
1296 contour->start = points + first; |
|
1297 contour->count = (FT_UInt)count; |
|
1298 |
|
1299 if ( count > 0 ) |
|
1300 { |
|
1301 point = points + first; |
|
1302 |
|
1303 point->prev = points + next - 1; |
|
1304 point->contour = contour; |
|
1305 |
|
1306 for ( ; count > 1; count-- ) |
|
1307 { |
|
1308 point[0].next = point + 1; |
|
1309 point[1].prev = point; |
|
1310 point++; |
|
1311 point->contour = contour; |
|
1312 } |
|
1313 point->next = points + first; |
|
1314 } |
|
1315 |
|
1316 contour++; |
|
1317 first = next; |
|
1318 } |
|
1319 } |
|
1320 |
|
1321 { |
|
1322 PSH_Point points = glyph->points; |
|
1323 PSH_Point point = points; |
|
1324 FT_Vector* vec = outline->points; |
|
1325 FT_UInt n; |
|
1326 |
|
1327 |
|
1328 for ( n = 0; n < glyph->num_points; n++, point++ ) |
|
1329 { |
|
1330 FT_Int n_prev = (FT_Int)( point->prev - points ); |
|
1331 FT_Int n_next = (FT_Int)( point->next - points ); |
|
1332 FT_Pos dxi, dyi, dxo, dyo; |
|
1333 |
|
1334 |
|
1335 if ( !( outline->tags[n] & FT_CURVE_TAG_ON ) ) |
|
1336 point->flags = PSH_POINT_OFF; |
|
1337 |
|
1338 dxi = vec[n].x - vec[n_prev].x; |
|
1339 dyi = vec[n].y - vec[n_prev].y; |
|
1340 |
|
1341 point->dir_in = (FT_Char)psh_compute_dir( dxi, dyi ); |
|
1342 |
|
1343 dxo = vec[n_next].x - vec[n].x; |
|
1344 dyo = vec[n_next].y - vec[n].y; |
|
1345 |
|
1346 point->dir_out = (FT_Char)psh_compute_dir( dxo, dyo ); |
|
1347 |
|
1348 /* detect smooth points */ |
|
1349 if ( point->flags & PSH_POINT_OFF ) |
|
1350 point->flags |= PSH_POINT_SMOOTH; |
|
1351 |
|
1352 else if ( point->dir_in == point->dir_out ) |
|
1353 { |
|
1354 if ( point->dir_out != PSH_DIR_NONE || |
|
1355 psh_corner_is_flat( dxi, dyi, dxo, dyo ) ) |
|
1356 point->flags |= PSH_POINT_SMOOTH; |
|
1357 } |
|
1358 } |
|
1359 } |
|
1360 |
|
1361 glyph->outline = outline; |
|
1362 glyph->globals = globals; |
|
1363 |
|
1364 #ifdef COMPUTE_INFLEXS |
|
1365 psh_glyph_load_points( glyph, 0 ); |
|
1366 psh_glyph_compute_inflections( glyph ); |
|
1367 #endif /* COMPUTE_INFLEXS */ |
|
1368 |
|
1369 /* now deal with hints tables */ |
|
1370 error = psh_hint_table_init( &glyph->hint_tables [0], |
|
1371 &ps_hints->dimension[0].hints, |
|
1372 &ps_hints->dimension[0].masks, |
|
1373 &ps_hints->dimension[0].counters, |
|
1374 memory ); |
|
1375 if ( error ) |
|
1376 goto Exit; |
|
1377 |
|
1378 error = psh_hint_table_init( &glyph->hint_tables [1], |
|
1379 &ps_hints->dimension[1].hints, |
|
1380 &ps_hints->dimension[1].masks, |
|
1381 &ps_hints->dimension[1].counters, |
|
1382 memory ); |
|
1383 if ( error ) |
|
1384 goto Exit; |
|
1385 |
|
1386 Exit: |
|
1387 return error; |
|
1388 } |
|
1389 |
|
1390 |
|
1391 /* compute all extrema in a glyph for a given dimension */ |
|
1392 static void |
|
1393 psh_glyph_compute_extrema( PSH_Glyph glyph ) |
|
1394 { |
|
1395 FT_UInt n; |
|
1396 |
|
1397 |
|
1398 /* first of all, compute all local extrema */ |
|
1399 for ( n = 0; n < glyph->num_contours; n++ ) |
|
1400 { |
|
1401 PSH_Point first = glyph->contours[n].start; |
|
1402 PSH_Point point, before, after; |
|
1403 |
|
1404 |
|
1405 if ( glyph->contours[n].count == 0 ) |
|
1406 continue; |
|
1407 |
|
1408 point = first; |
|
1409 before = point; |
|
1410 after = point; |
|
1411 |
|
1412 do |
|
1413 { |
|
1414 before = before->prev; |
|
1415 if ( before == first ) |
|
1416 goto Skip; |
|
1417 |
|
1418 } while ( before->org_u == point->org_u ); |
|
1419 |
|
1420 first = point = before->next; |
|
1421 |
|
1422 for (;;) |
|
1423 { |
|
1424 after = point; |
|
1425 do |
|
1426 { |
|
1427 after = after->next; |
|
1428 if ( after == first ) |
|
1429 goto Next; |
|
1430 |
|
1431 } while ( after->org_u == point->org_u ); |
|
1432 |
|
1433 if ( before->org_u < point->org_u ) |
|
1434 { |
|
1435 if ( after->org_u < point->org_u ) |
|
1436 { |
|
1437 /* local maximum */ |
|
1438 goto Extremum; |
|
1439 } |
|
1440 } |
|
1441 else /* before->org_u > point->org_u */ |
|
1442 { |
|
1443 if ( after->org_u > point->org_u ) |
|
1444 { |
|
1445 /* local minimum */ |
|
1446 Extremum: |
|
1447 do |
|
1448 { |
|
1449 psh_point_set_extremum( point ); |
|
1450 point = point->next; |
|
1451 |
|
1452 } while ( point != after ); |
|
1453 } |
|
1454 } |
|
1455 |
|
1456 before = after->prev; |
|
1457 point = after; |
|
1458 |
|
1459 } /* for */ |
|
1460 |
|
1461 Next: |
|
1462 ; |
|
1463 } |
|
1464 |
|
1465 /* for each extremum, determine its direction along the */ |
|
1466 /* orthogonal axis */ |
|
1467 for ( n = 0; n < glyph->num_points; n++ ) |
|
1468 { |
|
1469 PSH_Point point, before, after; |
|
1470 |
|
1471 |
|
1472 point = &glyph->points[n]; |
|
1473 before = point; |
|
1474 after = point; |
|
1475 |
|
1476 if ( psh_point_is_extremum( point ) ) |
|
1477 { |
|
1478 do |
|
1479 { |
|
1480 before = before->prev; |
|
1481 if ( before == point ) |
|
1482 goto Skip; |
|
1483 |
|
1484 } while ( before->org_v == point->org_v ); |
|
1485 |
|
1486 do |
|
1487 { |
|
1488 after = after->next; |
|
1489 if ( after == point ) |
|
1490 goto Skip; |
|
1491 |
|
1492 } while ( after->org_v == point->org_v ); |
|
1493 } |
|
1494 |
|
1495 if ( before->org_v < point->org_v && |
|
1496 after->org_v > point->org_v ) |
|
1497 { |
|
1498 psh_point_set_positive( point ); |
|
1499 } |
|
1500 else if ( before->org_v > point->org_v && |
|
1501 after->org_v < point->org_v ) |
|
1502 { |
|
1503 psh_point_set_negative( point ); |
|
1504 } |
|
1505 |
|
1506 Skip: |
|
1507 ; |
|
1508 } |
|
1509 } |
|
1510 |
|
1511 |
|
1512 /* major_dir is the direction for points on the bottom/left of the stem; */ |
|
1513 /* Points on the top/right of the stem will have a direction of */ |
|
1514 /* -major_dir. */ |
|
1515 |
|
1516 static void |
|
1517 psh_hint_table_find_strong_points( PSH_Hint_Table table, |
|
1518 PSH_Point point, |
|
1519 FT_UInt count, |
|
1520 FT_Int threshold, |
|
1521 FT_Int major_dir ) |
|
1522 { |
|
1523 PSH_Hint* sort = table->sort; |
|
1524 FT_UInt num_hints = table->num_hints; |
|
1525 |
|
1526 |
|
1527 for ( ; count > 0; count--, point++ ) |
|
1528 { |
|
1529 FT_Int point_dir = 0; |
|
1530 FT_Pos org_u = point->org_u; |
|
1531 |
|
1532 |
|
1533 if ( psh_point_is_strong( point ) ) |
|
1534 continue; |
|
1535 |
|
1536 if ( PSH_DIR_COMPARE( point->dir_in, major_dir ) ) |
|
1537 point_dir = point->dir_in; |
|
1538 |
|
1539 else if ( PSH_DIR_COMPARE( point->dir_out, major_dir ) ) |
|
1540 point_dir = point->dir_out; |
|
1541 |
|
1542 if ( point_dir ) |
|
1543 { |
|
1544 if ( point_dir == major_dir ) |
|
1545 { |
|
1546 FT_UInt nn; |
|
1547 |
|
1548 |
|
1549 for ( nn = 0; nn < num_hints; nn++ ) |
|
1550 { |
|
1551 PSH_Hint hint = sort[nn]; |
|
1552 FT_Pos d = org_u - hint->org_pos; |
|
1553 |
|
1554 |
|
1555 if ( d < threshold && -d < threshold ) |
|
1556 { |
|
1557 psh_point_set_strong( point ); |
|
1558 point->flags2 |= PSH_POINT_EDGE_MIN; |
|
1559 point->hint = hint; |
|
1560 break; |
|
1561 } |
|
1562 } |
|
1563 } |
|
1564 else if ( point_dir == -major_dir ) |
|
1565 { |
|
1566 FT_UInt nn; |
|
1567 |
|
1568 |
|
1569 for ( nn = 0; nn < num_hints; nn++ ) |
|
1570 { |
|
1571 PSH_Hint hint = sort[nn]; |
|
1572 FT_Pos d = org_u - hint->org_pos - hint->org_len; |
|
1573 |
|
1574 |
|
1575 if ( d < threshold && -d < threshold ) |
|
1576 { |
|
1577 psh_point_set_strong( point ); |
|
1578 point->flags2 |= PSH_POINT_EDGE_MAX; |
|
1579 point->hint = hint; |
|
1580 break; |
|
1581 } |
|
1582 } |
|
1583 } |
|
1584 } |
|
1585 |
|
1586 #if 1 |
|
1587 else if ( psh_point_is_extremum( point ) ) |
|
1588 { |
|
1589 /* treat extrema as special cases for stem edge alignment */ |
|
1590 FT_UInt nn, min_flag, max_flag; |
|
1591 |
|
1592 |
|
1593 if ( major_dir == PSH_DIR_HORIZONTAL ) |
|
1594 { |
|
1595 min_flag = PSH_POINT_POSITIVE; |
|
1596 max_flag = PSH_POINT_NEGATIVE; |
|
1597 } |
|
1598 else |
|
1599 { |
|
1600 min_flag = PSH_POINT_NEGATIVE; |
|
1601 max_flag = PSH_POINT_POSITIVE; |
|
1602 } |
|
1603 |
|
1604 if ( point->flags2 & min_flag ) |
|
1605 { |
|
1606 for ( nn = 0; nn < num_hints; nn++ ) |
|
1607 { |
|
1608 PSH_Hint hint = sort[nn]; |
|
1609 FT_Pos d = org_u - hint->org_pos; |
|
1610 |
|
1611 |
|
1612 if ( d < threshold && -d < threshold ) |
|
1613 { |
|
1614 point->flags2 |= PSH_POINT_EDGE_MIN; |
|
1615 point->hint = hint; |
|
1616 psh_point_set_strong( point ); |
|
1617 break; |
|
1618 } |
|
1619 } |
|
1620 } |
|
1621 else if ( point->flags2 & max_flag ) |
|
1622 { |
|
1623 for ( nn = 0; nn < num_hints; nn++ ) |
|
1624 { |
|
1625 PSH_Hint hint = sort[nn]; |
|
1626 FT_Pos d = org_u - hint->org_pos - hint->org_len; |
|
1627 |
|
1628 |
|
1629 if ( d < threshold && -d < threshold ) |
|
1630 { |
|
1631 point->flags2 |= PSH_POINT_EDGE_MAX; |
|
1632 point->hint = hint; |
|
1633 psh_point_set_strong( point ); |
|
1634 break; |
|
1635 } |
|
1636 } |
|
1637 } |
|
1638 |
|
1639 if ( point->hint == NULL ) |
|
1640 { |
|
1641 for ( nn = 0; nn < num_hints; nn++ ) |
|
1642 { |
|
1643 PSH_Hint hint = sort[nn]; |
|
1644 |
|
1645 |
|
1646 if ( org_u >= hint->org_pos && |
|
1647 org_u <= hint->org_pos + hint->org_len ) |
|
1648 { |
|
1649 point->hint = hint; |
|
1650 break; |
|
1651 } |
|
1652 } |
|
1653 } |
|
1654 } |
|
1655 |
|
1656 #endif /* 1 */ |
|
1657 } |
|
1658 } |
|
1659 |
|
1660 |
|
1661 /* the accepted shift for strong points in fractional pixels */ |
|
1662 #define PSH_STRONG_THRESHOLD 32 |
|
1663 |
|
1664 /* the maximum shift value in font units */ |
|
1665 #define PSH_STRONG_THRESHOLD_MAXIMUM 30 |
|
1666 |
|
1667 |
|
1668 /* find strong points in a glyph */ |
|
1669 static void |
|
1670 psh_glyph_find_strong_points( PSH_Glyph glyph, |
|
1671 FT_Int dimension ) |
|
1672 { |
|
1673 /* a point is `strong' if it is located on a stem edge and */ |
|
1674 /* has an `in' or `out' tangent parallel to the hint's direction */ |
|
1675 |
|
1676 PSH_Hint_Table table = &glyph->hint_tables[dimension]; |
|
1677 PS_Mask mask = table->hint_masks->masks; |
|
1678 FT_UInt num_masks = table->hint_masks->num_masks; |
|
1679 FT_UInt first = 0; |
|
1680 FT_Int major_dir = dimension == 0 ? PSH_DIR_VERTICAL |
|
1681 : PSH_DIR_HORIZONTAL; |
|
1682 PSH_Dimension dim = &glyph->globals->dimension[dimension]; |
|
1683 FT_Fixed scale = dim->scale_mult; |
|
1684 FT_Int threshold; |
|
1685 |
|
1686 |
|
1687 threshold = (FT_Int)FT_DivFix( PSH_STRONG_THRESHOLD, scale ); |
|
1688 if ( threshold > PSH_STRONG_THRESHOLD_MAXIMUM ) |
|
1689 threshold = PSH_STRONG_THRESHOLD_MAXIMUM; |
|
1690 |
|
1691 /* process secondary hints to `selected' points */ |
|
1692 if ( num_masks > 1 && glyph->num_points > 0 ) |
|
1693 { |
|
1694 /* the `endchar' op can reduce the number of points */ |
|
1695 first = mask->end_point > glyph->num_points |
|
1696 ? glyph->num_points |
|
1697 : mask->end_point; |
|
1698 mask++; |
|
1699 for ( ; num_masks > 1; num_masks--, mask++ ) |
|
1700 { |
|
1701 FT_UInt next; |
|
1702 FT_Int count; |
|
1703 |
|
1704 |
|
1705 next = mask->end_point > glyph->num_points |
|
1706 ? glyph->num_points |
|
1707 : mask->end_point; |
|
1708 count = next - first; |
|
1709 if ( count > 0 ) |
|
1710 { |
|
1711 PSH_Point point = glyph->points + first; |
|
1712 |
|
1713 |
|
1714 psh_hint_table_activate_mask( table, mask ); |
|
1715 |
|
1716 psh_hint_table_find_strong_points( table, point, count, |
|
1717 threshold, major_dir ); |
|
1718 } |
|
1719 first = next; |
|
1720 } |
|
1721 } |
|
1722 |
|
1723 /* process primary hints for all points */ |
|
1724 if ( num_masks == 1 ) |
|
1725 { |
|
1726 FT_UInt count = glyph->num_points; |
|
1727 PSH_Point point = glyph->points; |
|
1728 |
|
1729 |
|
1730 psh_hint_table_activate_mask( table, table->hint_masks->masks ); |
|
1731 |
|
1732 psh_hint_table_find_strong_points( table, point, count, |
|
1733 threshold, major_dir ); |
|
1734 } |
|
1735 |
|
1736 /* now, certain points may have been attached to a hint and */ |
|
1737 /* not marked as strong; update their flags then */ |
|
1738 { |
|
1739 FT_UInt count = glyph->num_points; |
|
1740 PSH_Point point = glyph->points; |
|
1741 |
|
1742 |
|
1743 for ( ; count > 0; count--, point++ ) |
|
1744 if ( point->hint && !psh_point_is_strong( point ) ) |
|
1745 psh_point_set_strong( point ); |
|
1746 } |
|
1747 } |
|
1748 |
|
1749 |
|
1750 /* find points in a glyph which are in a blue zone and have `in' or */ |
|
1751 /* `out' tangents parallel to the horizontal axis */ |
|
1752 static void |
|
1753 psh_glyph_find_blue_points( PSH_Blues blues, |
|
1754 PSH_Glyph glyph ) |
|
1755 { |
|
1756 PSH_Blue_Table table; |
|
1757 PSH_Blue_Zone zone; |
|
1758 FT_UInt glyph_count = glyph->num_points; |
|
1759 FT_UInt blue_count; |
|
1760 PSH_Point point = glyph->points; |
|
1761 |
|
1762 |
|
1763 for ( ; glyph_count > 0; glyph_count--, point++ ) |
|
1764 { |
|
1765 FT_Pos y; |
|
1766 |
|
1767 |
|
1768 /* check tangents */ |
|
1769 if ( !PSH_DIR_COMPARE( point->dir_in, PSH_DIR_HORIZONTAL ) && |
|
1770 !PSH_DIR_COMPARE( point->dir_out, PSH_DIR_HORIZONTAL ) ) |
|
1771 continue; |
|
1772 |
|
1773 /* skip strong points */ |
|
1774 if ( psh_point_is_strong( point ) ) |
|
1775 continue; |
|
1776 |
|
1777 y = point->org_u; |
|
1778 |
|
1779 /* look up top zones */ |
|
1780 table = &blues->normal_top; |
|
1781 blue_count = table->count; |
|
1782 zone = table->zones; |
|
1783 |
|
1784 for ( ; blue_count > 0; blue_count--, zone++ ) |
|
1785 { |
|
1786 FT_Pos delta = y - zone->org_bottom; |
|
1787 |
|
1788 |
|
1789 if ( delta < -blues->blue_fuzz ) |
|
1790 break; |
|
1791 |
|
1792 if ( y <= zone->org_top + blues->blue_fuzz ) |
|
1793 if ( blues->no_overshoots || delta <= blues->blue_threshold ) |
|
1794 { |
|
1795 point->cur_u = zone->cur_bottom; |
|
1796 psh_point_set_strong( point ); |
|
1797 psh_point_set_fitted( point ); |
|
1798 } |
|
1799 } |
|
1800 |
|
1801 /* look up bottom zones */ |
|
1802 table = &blues->normal_bottom; |
|
1803 blue_count = table->count; |
|
1804 zone = table->zones + blue_count - 1; |
|
1805 |
|
1806 for ( ; blue_count > 0; blue_count--, zone-- ) |
|
1807 { |
|
1808 FT_Pos delta = zone->org_top - y; |
|
1809 |
|
1810 |
|
1811 if ( delta < -blues->blue_fuzz ) |
|
1812 break; |
|
1813 |
|
1814 if ( y >= zone->org_bottom - blues->blue_fuzz ) |
|
1815 if ( blues->no_overshoots || delta < blues->blue_threshold ) |
|
1816 { |
|
1817 point->cur_u = zone->cur_top; |
|
1818 psh_point_set_strong( point ); |
|
1819 psh_point_set_fitted( point ); |
|
1820 } |
|
1821 } |
|
1822 } |
|
1823 } |
|
1824 |
|
1825 |
|
1826 /* interpolate strong points with the help of hinted coordinates */ |
|
1827 static void |
|
1828 psh_glyph_interpolate_strong_points( PSH_Glyph glyph, |
|
1829 FT_Int dimension ) |
|
1830 { |
|
1831 PSH_Dimension dim = &glyph->globals->dimension[dimension]; |
|
1832 FT_Fixed scale = dim->scale_mult; |
|
1833 |
|
1834 FT_UInt count = glyph->num_points; |
|
1835 PSH_Point point = glyph->points; |
|
1836 |
|
1837 |
|
1838 for ( ; count > 0; count--, point++ ) |
|
1839 { |
|
1840 PSH_Hint hint = point->hint; |
|
1841 |
|
1842 |
|
1843 if ( hint ) |
|
1844 { |
|
1845 FT_Pos delta; |
|
1846 |
|
1847 |
|
1848 if ( psh_point_is_edge_min( point ) ) |
|
1849 point->cur_u = hint->cur_pos; |
|
1850 |
|
1851 else if ( psh_point_is_edge_max( point ) ) |
|
1852 point->cur_u = hint->cur_pos + hint->cur_len; |
|
1853 |
|
1854 else |
|
1855 { |
|
1856 delta = point->org_u - hint->org_pos; |
|
1857 |
|
1858 if ( delta <= 0 ) |
|
1859 point->cur_u = hint->cur_pos + FT_MulFix( delta, scale ); |
|
1860 |
|
1861 else if ( delta >= hint->org_len ) |
|
1862 point->cur_u = hint->cur_pos + hint->cur_len + |
|
1863 FT_MulFix( delta - hint->org_len, scale ); |
|
1864 |
|
1865 else /* hint->org_len > 0 */ |
|
1866 point->cur_u = hint->cur_pos + |
|
1867 FT_MulDiv( delta, hint->cur_len, |
|
1868 hint->org_len ); |
|
1869 } |
|
1870 psh_point_set_fitted( point ); |
|
1871 } |
|
1872 } |
|
1873 } |
|
1874 |
|
1875 |
|
1876 #define PSH_MAX_STRONG_INTERNAL 16 |
|
1877 |
|
1878 static void |
|
1879 psh_glyph_interpolate_normal_points( PSH_Glyph glyph, |
|
1880 FT_Int dimension ) |
|
1881 { |
|
1882 |
|
1883 #if 1 |
|
1884 /* first technique: a point is strong if it is a local extremum */ |
|
1885 |
|
1886 PSH_Dimension dim = &glyph->globals->dimension[dimension]; |
|
1887 FT_Fixed scale = dim->scale_mult; |
|
1888 FT_Memory memory = glyph->memory; |
|
1889 |
|
1890 PSH_Point* strongs = NULL; |
|
1891 PSH_Point strongs_0[PSH_MAX_STRONG_INTERNAL]; |
|
1892 FT_UInt num_strongs = 0; |
|
1893 |
|
1894 PSH_Point points = glyph->points; |
|
1895 PSH_Point points_end = points + glyph->num_points; |
|
1896 PSH_Point point; |
|
1897 |
|
1898 |
|
1899 /* first count the number of strong points */ |
|
1900 for ( point = points; point < points_end; point++ ) |
|
1901 { |
|
1902 if ( psh_point_is_strong( point ) ) |
|
1903 num_strongs++; |
|
1904 } |
|
1905 |
|
1906 if ( num_strongs == 0 ) /* nothing to do here */ |
|
1907 return; |
|
1908 |
|
1909 /* allocate an array to store a list of points, */ |
|
1910 /* stored in increasing org_u order */ |
|
1911 if ( num_strongs <= PSH_MAX_STRONG_INTERNAL ) |
|
1912 strongs = strongs_0; |
|
1913 else |
|
1914 { |
|
1915 FT_Error error; |
|
1916 |
|
1917 |
|
1918 if ( FT_NEW_ARRAY( strongs, num_strongs ) ) |
|
1919 return; |
|
1920 } |
|
1921 |
|
1922 num_strongs = 0; |
|
1923 for ( point = points; point < points_end; point++ ) |
|
1924 { |
|
1925 PSH_Point* insert; |
|
1926 |
|
1927 |
|
1928 if ( !psh_point_is_strong( point ) ) |
|
1929 continue; |
|
1930 |
|
1931 for ( insert = strongs + num_strongs; insert > strongs; insert-- ) |
|
1932 { |
|
1933 if ( insert[-1]->org_u <= point->org_u ) |
|
1934 break; |
|
1935 |
|
1936 insert[0] = insert[-1]; |
|
1937 } |
|
1938 insert[0] = point; |
|
1939 num_strongs++; |
|
1940 } |
|
1941 |
|
1942 /* now try to interpolate all normal points */ |
|
1943 for ( point = points; point < points_end; point++ ) |
|
1944 { |
|
1945 if ( psh_point_is_strong( point ) ) |
|
1946 continue; |
|
1947 |
|
1948 /* sometimes, some local extrema are smooth points */ |
|
1949 if ( psh_point_is_smooth( point ) ) |
|
1950 { |
|
1951 if ( point->dir_in == PSH_DIR_NONE || |
|
1952 point->dir_in != point->dir_out ) |
|
1953 continue; |
|
1954 |
|
1955 if ( !psh_point_is_extremum( point ) && |
|
1956 !psh_point_is_inflex( point ) ) |
|
1957 continue; |
|
1958 |
|
1959 point->flags &= ~PSH_POINT_SMOOTH; |
|
1960 } |
|
1961 |
|
1962 /* find best enclosing point coordinates then interpolate */ |
|
1963 { |
|
1964 PSH_Point before, after; |
|
1965 FT_UInt nn; |
|
1966 |
|
1967 |
|
1968 for ( nn = 0; nn < num_strongs; nn++ ) |
|
1969 if ( strongs[nn]->org_u > point->org_u ) |
|
1970 break; |
|
1971 |
|
1972 if ( nn == 0 ) /* point before the first strong point */ |
|
1973 { |
|
1974 after = strongs[0]; |
|
1975 |
|
1976 point->cur_u = after->cur_u + |
|
1977 FT_MulFix( point->org_u - after->org_u, |
|
1978 scale ); |
|
1979 } |
|
1980 else |
|
1981 { |
|
1982 before = strongs[nn - 1]; |
|
1983 |
|
1984 for ( nn = num_strongs; nn > 0; nn-- ) |
|
1985 if ( strongs[nn - 1]->org_u < point->org_u ) |
|
1986 break; |
|
1987 |
|
1988 if ( nn == num_strongs ) /* point is after last strong point */ |
|
1989 { |
|
1990 before = strongs[nn - 1]; |
|
1991 |
|
1992 point->cur_u = before->cur_u + |
|
1993 FT_MulFix( point->org_u - before->org_u, |
|
1994 scale ); |
|
1995 } |
|
1996 else |
|
1997 { |
|
1998 FT_Pos u; |
|
1999 |
|
2000 |
|
2001 after = strongs[nn]; |
|
2002 |
|
2003 /* now interpolate point between before and after */ |
|
2004 u = point->org_u; |
|
2005 |
|
2006 if ( u == before->org_u ) |
|
2007 point->cur_u = before->cur_u; |
|
2008 |
|
2009 else if ( u == after->org_u ) |
|
2010 point->cur_u = after->cur_u; |
|
2011 |
|
2012 else |
|
2013 point->cur_u = before->cur_u + |
|
2014 FT_MulDiv( u - before->org_u, |
|
2015 after->cur_u - before->cur_u, |
|
2016 after->org_u - before->org_u ); |
|
2017 } |
|
2018 } |
|
2019 psh_point_set_fitted( point ); |
|
2020 } |
|
2021 } |
|
2022 |
|
2023 if ( strongs != strongs_0 ) |
|
2024 FT_FREE( strongs ); |
|
2025 |
|
2026 #endif /* 1 */ |
|
2027 |
|
2028 } |
|
2029 |
|
2030 |
|
2031 /* interpolate other points */ |
|
2032 static void |
|
2033 psh_glyph_interpolate_other_points( PSH_Glyph glyph, |
|
2034 FT_Int dimension ) |
|
2035 { |
|
2036 PSH_Dimension dim = &glyph->globals->dimension[dimension]; |
|
2037 FT_Fixed scale = dim->scale_mult; |
|
2038 FT_Fixed delta = dim->scale_delta; |
|
2039 PSH_Contour contour = glyph->contours; |
|
2040 FT_UInt num_contours = glyph->num_contours; |
|
2041 |
|
2042 |
|
2043 for ( ; num_contours > 0; num_contours--, contour++ ) |
|
2044 { |
|
2045 PSH_Point start = contour->start; |
|
2046 PSH_Point first, next, point; |
|
2047 FT_UInt fit_count; |
|
2048 |
|
2049 |
|
2050 /* count the number of strong points in this contour */ |
|
2051 next = start + contour->count; |
|
2052 fit_count = 0; |
|
2053 first = 0; |
|
2054 |
|
2055 for ( point = start; point < next; point++ ) |
|
2056 if ( psh_point_is_fitted( point ) ) |
|
2057 { |
|
2058 if ( !first ) |
|
2059 first = point; |
|
2060 |
|
2061 fit_count++; |
|
2062 } |
|
2063 |
|
2064 /* if there are less than 2 fitted points in the contour, we */ |
|
2065 /* simply scale and eventually translate the contour points */ |
|
2066 if ( fit_count < 2 ) |
|
2067 { |
|
2068 if ( fit_count == 1 ) |
|
2069 delta = first->cur_u - FT_MulFix( first->org_u, scale ); |
|
2070 |
|
2071 for ( point = start; point < next; point++ ) |
|
2072 if ( point != first ) |
|
2073 point->cur_u = FT_MulFix( point->org_u, scale ) + delta; |
|
2074 |
|
2075 goto Next_Contour; |
|
2076 } |
|
2077 |
|
2078 /* there are more than 2 strong points in this contour; we */ |
|
2079 /* need to interpolate weak points between them */ |
|
2080 start = first; |
|
2081 do |
|
2082 { |
|
2083 point = first; |
|
2084 |
|
2085 /* skip consecutive fitted points */ |
|
2086 for (;;) |
|
2087 { |
|
2088 next = first->next; |
|
2089 if ( next == start ) |
|
2090 goto Next_Contour; |
|
2091 |
|
2092 if ( !psh_point_is_fitted( next ) ) |
|
2093 break; |
|
2094 |
|
2095 first = next; |
|
2096 } |
|
2097 |
|
2098 /* find next fitted point after unfitted one */ |
|
2099 for (;;) |
|
2100 { |
|
2101 next = next->next; |
|
2102 if ( psh_point_is_fitted( next ) ) |
|
2103 break; |
|
2104 } |
|
2105 |
|
2106 /* now interpolate between them */ |
|
2107 { |
|
2108 FT_Pos org_a, org_ab, cur_a, cur_ab; |
|
2109 FT_Pos org_c, org_ac, cur_c; |
|
2110 FT_Fixed scale_ab; |
|
2111 |
|
2112 |
|
2113 if ( first->org_u <= next->org_u ) |
|
2114 { |
|
2115 org_a = first->org_u; |
|
2116 cur_a = first->cur_u; |
|
2117 org_ab = next->org_u - org_a; |
|
2118 cur_ab = next->cur_u - cur_a; |
|
2119 } |
|
2120 else |
|
2121 { |
|
2122 org_a = next->org_u; |
|
2123 cur_a = next->cur_u; |
|
2124 org_ab = first->org_u - org_a; |
|
2125 cur_ab = first->cur_u - cur_a; |
|
2126 } |
|
2127 |
|
2128 scale_ab = 0x10000L; |
|
2129 if ( org_ab > 0 ) |
|
2130 scale_ab = FT_DivFix( cur_ab, org_ab ); |
|
2131 |
|
2132 point = first->next; |
|
2133 do |
|
2134 { |
|
2135 org_c = point->org_u; |
|
2136 org_ac = org_c - org_a; |
|
2137 |
|
2138 if ( org_ac <= 0 ) |
|
2139 { |
|
2140 /* on the left of the interpolation zone */ |
|
2141 cur_c = cur_a + FT_MulFix( org_ac, scale ); |
|
2142 } |
|
2143 else if ( org_ac >= org_ab ) |
|
2144 { |
|
2145 /* on the right on the interpolation zone */ |
|
2146 cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale ); |
|
2147 } |
|
2148 else |
|
2149 { |
|
2150 /* within the interpolation zone */ |
|
2151 cur_c = cur_a + FT_MulFix( org_ac, scale_ab ); |
|
2152 } |
|
2153 |
|
2154 point->cur_u = cur_c; |
|
2155 |
|
2156 point = point->next; |
|
2157 |
|
2158 } while ( point != next ); |
|
2159 } |
|
2160 |
|
2161 /* keep going until all points in the contours have been processed */ |
|
2162 first = next; |
|
2163 |
|
2164 } while ( first != start ); |
|
2165 |
|
2166 Next_Contour: |
|
2167 ; |
|
2168 } |
|
2169 } |
|
2170 |
|
2171 |
|
2172 /*************************************************************************/ |
|
2173 /*************************************************************************/ |
|
2174 /***** *****/ |
|
2175 /***** HIGH-LEVEL INTERFACE *****/ |
|
2176 /***** *****/ |
|
2177 /*************************************************************************/ |
|
2178 /*************************************************************************/ |
|
2179 |
|
2180 FT_Error |
|
2181 ps_hints_apply( PS_Hints ps_hints, |
|
2182 FT_Outline* outline, |
|
2183 PSH_Globals globals, |
|
2184 FT_Render_Mode hint_mode ) |
|
2185 { |
|
2186 PSH_GlyphRec glyphrec; |
|
2187 PSH_Glyph glyph = &glyphrec; |
|
2188 FT_Error error; |
|
2189 #ifdef DEBUG_HINTER |
|
2190 FT_Memory memory; |
|
2191 #endif |
|
2192 FT_Int dimension; |
|
2193 |
|
2194 |
|
2195 /* something to do? */ |
|
2196 if ( outline->n_points == 0 || outline->n_contours == 0 ) |
|
2197 return PSH_Err_Ok; |
|
2198 |
|
2199 #ifdef DEBUG_HINTER |
|
2200 |
|
2201 memory = globals->memory; |
|
2202 |
|
2203 if ( ps_debug_glyph ) |
|
2204 { |
|
2205 psh_glyph_done( ps_debug_glyph ); |
|
2206 FT_FREE( ps_debug_glyph ); |
|
2207 } |
|
2208 |
|
2209 if ( FT_NEW( glyph ) ) |
|
2210 return error; |
|
2211 |
|
2212 ps_debug_glyph = glyph; |
|
2213 |
|
2214 #endif /* DEBUG_HINTER */ |
|
2215 |
|
2216 error = psh_glyph_init( glyph, outline, ps_hints, globals ); |
|
2217 if ( error ) |
|
2218 goto Exit; |
|
2219 |
|
2220 /* try to optimize the y_scale so that the top of non-capital letters |
|
2221 * is aligned on a pixel boundary whenever possible |
|
2222 */ |
|
2223 { |
|
2224 PSH_Dimension dim_x = &glyph->globals->dimension[0]; |
|
2225 PSH_Dimension dim_y = &glyph->globals->dimension[1]; |
|
2226 |
|
2227 FT_Fixed x_scale = dim_x->scale_mult; |
|
2228 FT_Fixed y_scale = dim_y->scale_mult; |
|
2229 |
|
2230 FT_Fixed old_x_scale = x_scale; |
|
2231 FT_Fixed old_y_scale = y_scale; |
|
2232 |
|
2233 FT_Fixed scaled; |
|
2234 FT_Fixed fitted; |
|
2235 |
|
2236 FT_Bool rescale = FALSE; |
|
2237 |
|
2238 |
|
2239 scaled = FT_MulFix( globals->blues.normal_top.zones->org_ref, y_scale ); |
|
2240 fitted = FT_PIX_ROUND( scaled ); |
|
2241 |
|
2242 if ( fitted != 0 && scaled != fitted ) |
|
2243 { |
|
2244 rescale = TRUE; |
|
2245 |
|
2246 y_scale = FT_MulDiv( y_scale, fitted, scaled ); |
|
2247 |
|
2248 if ( fitted < scaled ) |
|
2249 x_scale -= x_scale / 50; |
|
2250 |
|
2251 psh_globals_set_scale( glyph->globals, x_scale, y_scale, 0, 0 ); |
|
2252 } |
|
2253 |
|
2254 glyph->do_horz_hints = 1; |
|
2255 glyph->do_vert_hints = 1; |
|
2256 |
|
2257 glyph->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || |
|
2258 hint_mode == FT_RENDER_MODE_LCD ); |
|
2259 |
|
2260 glyph->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || |
|
2261 hint_mode == FT_RENDER_MODE_LCD_V ); |
|
2262 |
|
2263 glyph->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT ); |
|
2264 |
|
2265 for ( dimension = 0; dimension < 2; dimension++ ) |
|
2266 { |
|
2267 /* load outline coordinates into glyph */ |
|
2268 psh_glyph_load_points( glyph, dimension ); |
|
2269 |
|
2270 /* compute local extrema */ |
|
2271 psh_glyph_compute_extrema( glyph ); |
|
2272 |
|
2273 /* compute aligned stem/hints positions */ |
|
2274 psh_hint_table_align_hints( &glyph->hint_tables[dimension], |
|
2275 glyph->globals, |
|
2276 dimension, |
|
2277 glyph ); |
|
2278 |
|
2279 /* find strong points, align them, then interpolate others */ |
|
2280 psh_glyph_find_strong_points( glyph, dimension ); |
|
2281 if ( dimension == 1 ) |
|
2282 psh_glyph_find_blue_points( &globals->blues, glyph ); |
|
2283 psh_glyph_interpolate_strong_points( glyph, dimension ); |
|
2284 psh_glyph_interpolate_normal_points( glyph, dimension ); |
|
2285 psh_glyph_interpolate_other_points( glyph, dimension ); |
|
2286 |
|
2287 /* save hinted coordinates back to outline */ |
|
2288 psh_glyph_save_points( glyph, dimension ); |
|
2289 |
|
2290 if ( rescale ) |
|
2291 psh_globals_set_scale( glyph->globals, |
|
2292 old_x_scale, old_y_scale, 0, 0 ); |
|
2293 } |
|
2294 } |
|
2295 |
|
2296 Exit: |
|
2297 |
|
2298 #ifndef DEBUG_HINTER |
|
2299 psh_glyph_done( glyph ); |
|
2300 #endif |
|
2301 |
|
2302 return error; |
|
2303 } |
|
2304 |
|
2305 |
|
2306 /* END */ |
|