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