]> git.saurik.com Git - wxWidgets.git/blob - src/freetype/type1/t1hinter.c
cleanup
[wxWidgets.git] / src / freetype / type1 / t1hinter.c
1 /***************************************************************************/
2 /* */
3 /* t1hinter.c */
4 /* */
5 /* Type 1 hinter (body). */
6 /* */
7 /* Copyright 1996-2000 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 /*************************************************************************/
20 /* */
21 /* The Hinter is in charge of fitting th scaled outline to the pixel */
22 /* grid in order to considerably improve the quality of the Type 1 font */
23 /* driver's output. */
24 /* */
25 /*************************************************************************/
26
27
28 #include <freetype/internal/ftdebug.h>
29
30
31 #ifdef FT_FLAT_COMPILE
32
33 #include "t1objs.h"
34 #include "t1hinter.h"
35
36 #else
37
38 #include <type1/t1objs.h>
39 #include <type1/t1hinter.h>
40
41 #endif
42
43
44 /*************************************************************************/
45 /* */
46 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
47 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
48 /* messages during execution. */
49 /* */
50 #undef FT_COMPONENT
51 #define FT_COMPONENT trace_t1hint
52
53
54 #undef ONE_PIXEL
55 #define ONE_PIXEL 64
56
57 #undef ROUND
58 #define ROUND( x ) ( ( x + ONE_PIXEL / 2 ) & -ONE_PIXEL )
59
60 #undef SCALE
61 #define SCALE( val ) FT_MulFix( val, scale )
62
63 /* various constants used to describe the alignment of a horizontal */
64 /* stem with regards to the blue zones */
65
66 #define T1_ALIGN_NONE 0
67 #define T1_ALIGN_BOTTOM 1
68 #define T1_ALIGN_TOP 2
69 #define T1_ALIGN_BOTH 3
70
71
72 /* very simple bubble sort (not a lot of elements, mostly */
73 /* pre-sorted, no need for quicksort) */
74
75 static
76 void t1_sort_blues( FT_Int* blues,
77 FT_Int count )
78 {
79 FT_Int i, swap;
80 FT_Int* cur;
81
82
83 for ( i = 2; i < count; i += 2 )
84 {
85 cur = blues + i;
86 do
87 {
88 if ( cur[-1] < cur[0] )
89 break;
90
91 swap = cur[-2]; cur[-2] = cur[0]; cur[0] = swap;
92 swap = cur[-1]; cur[-1] = cur[1]; cur[1] = swap;
93 cur -= 2;
94
95 } while ( cur > blues );
96 }
97 }
98
99
100 /*************************************************************************/
101 /* */
102 /* <Function> */
103 /* t1_set_blue_zones */
104 /* */
105 /* <Description> */
106 /* Sets a size object's blue zones during reset. This will compute */
107 /* the `snap' zone corresponding to each blue zone. */
108 /* */
109 /* <InOut> */
110 /* size :: A handle to target size object. */
111 /* */
112 /* <Return> */
113 /* FreeType error code. 0 means success. */
114 /* */
115 /* <Note> */
116 /* This functions does the following: */
117 /* */
118 /* 1. It extracts the bottom and top blue zones from the face object. */
119 /* */
120 /* 2. Each zone is then grown by `BlueFuzz', overlapping is */
121 /* eliminated by adjusting the zone edges appropriately. */
122 /* */
123 /* 3. For each zone, we keep its original font units position, its */
124 /* original scaled position, as well as its grown/adjusted edges. */
125 /* */
126 static
127 FT_Error t1_set_blue_zones( T1_Size size )
128 {
129 T1_Face face = (T1_Face)size->root.face;
130 T1_Private* priv = &face->type1.private_dict;
131 FT_Int n;
132 FT_Int blues[24];
133 FT_Int num_bottom;
134 FT_Int num_top;
135 FT_Int num_blues;
136 T1_Size_Hints* hints = size->hints;
137 T1_Snap_Zone* zone;
138 FT_Pos pix, orus;
139 FT_Pos min, max, threshold;
140 FT_Fixed scale;
141 FT_Bool is_bottom;
142
143
144 /***********************************************************************/
145 /* */
146 /* copy bottom and top blue zones in local arrays */
147 /* */
148
149 /* First of all, check the sizes of the /BlueValues and /OtherBlues */
150 /* tables. They all must contain an even number of arguments. */
151 if ( priv->num_other_blues & 1 ||
152 priv->num_blue_values & 1 )
153 {
154 FT_ERROR(( "t1_set_blue_zones: odd number of blue values\n" ));
155 return T1_Err_Syntax_Error;
156 }
157
158 /* copy the bottom blue zones from /OtherBlues */
159 num_top = 0;
160 num_bottom = priv->num_other_blues;
161
162 for ( n = 0; n < num_bottom; n++ )
163 blues[n] = priv->other_blues[n];
164
165 /* add the first blue zone in /BlueValues to the table */
166 num_top = priv->num_blue_values - 2;
167 if ( num_top >= 0 )
168 {
169 blues[num_bottom ] = priv->blue_values[0];
170 blues[num_bottom + 1] = priv->blue_values[1];
171
172 num_bottom += 2;
173 }
174
175 /* sort the bottom blue zones */
176 t1_sort_blues( blues, num_bottom );
177
178 hints->num_bottom_zones = num_bottom >> 1;
179
180 /* now copy the /BlueValues to the top of the blues array */
181 if ( num_top > 0 )
182 {
183 for ( n = 0; n < num_top; n++ )
184 blues[num_bottom + n] = priv->blue_values[n + 2];
185
186 /* sort the top blue zones */
187 t1_sort_blues( blues + num_bottom, num_top );
188 }
189 else
190 num_top = 0;
191
192 num_blues = num_top + num_bottom;
193 hints->num_blue_zones = ( num_blues ) >> 1;
194
195 /***********************************************************************/
196 /* */
197 /* build blue snap zones from the local blues arrays */
198 /* */
199
200 scale = size->root.metrics.y_scale;
201 zone = hints->blue_zones;
202 threshold = ONE_PIXEL / 4; /* 0.25 pixels */
203
204 for ( n = 0; n < num_blues; n += 2, zone++ )
205 {
206 is_bottom = n < num_bottom ? 1 : 0;
207
208 orus = blues[n + is_bottom]; /* get alignement coordinate */
209 pix = SCALE( orus ); /* scale it */
210
211 min = SCALE( blues[n ] - priv->blue_fuzz );
212 max = SCALE( blues[n + 1] + priv->blue_fuzz );
213
214 if ( min > pix - threshold )
215 min = pix - threshold;
216 if ( max < pix + threshold )
217 max = pix + threshold;
218
219 zone->orus = orus;
220 zone->pix = pix;
221 zone->min = min;
222 zone->max = max;
223 }
224
225 /* adjust edges in case of overlap */
226 zone = hints->blue_zones;
227 for ( n = 0; n < num_blues - 2; n += 2, zone++ )
228 {
229 if ( n != num_bottom - 2 &&
230 zone[0].max > zone[1].min )
231 zone[0].max = zone[1].min = ( zone[0].pix + zone[1].pix ) / 2;
232 }
233
234 /* compare the current pixel size with the BlueScale value */
235 /* to know whether to supress overshoots */
236
237 hints->supress_overshoots =
238 size->root.metrics.y_ppem < FT_MulFix( 1000, priv->blue_scale );
239
240 #ifdef FT_DEBUG_LEVEL_TRACE
241
242 /* now print the new blue values in tracing mode */
243
244 FT_TRACE2(( "Blue Zones for size object at $%08lx:\n", (long)size ));
245 FT_TRACE2(( " orus pix min max\n" ));
246 FT_TRACE2(( "-------------------------------\n" ));
247
248 zone = hints->blue_zones;
249 for ( n = 0; n < hints->num_blue_zones; n++ )
250 {
251 FT_TRACE2(( " %3d %.2f %.2f %.2f\n",
252 zone->orus,
253 zone->pix / 64.0,
254 zone->min / 64.0,
255 zone->max / 64.0 ));
256 zone++;
257 }
258 FT_TRACE2(( "\nOvershoots are %s\n\n",
259 hints->supress_overshoots ? "supressed" : "active" ));
260
261 #endif /* DEBUG_LEVEL_TRACE */
262
263 return T1_Err_Ok;
264 }
265
266
267 /*************************************************************************/
268 /* */
269 /* <Function> */
270 /* t1_set_snap_zones */
271 /* */
272 /* <Description> */
273 /* This function set a size object's stem snap zones. */
274 /* */
275 /* <InOut> */
276 /* size :: A handle to the target size object. */
277 /* */
278 /* <Return> */
279 /* FreeType error code. 0 means success. */
280 /* */
281 /* <Note> */
282 /* This function performs the following: */
283 /* */
284 /* 1. It reads and scales the stem snap widths from the parent face. */
285 /* */
286 /* 2. A `snap zone' is computed for each snap width, by `growing' it */
287 /* with a threshold of 1/2 pixel. Overlapping is avoided through */
288 /* proper edge adjustment. */
289 /* */
290 /* 3. Each width whose zone contain the scaled standard set width is */
291 /* removed from the table. */
292 /* */
293 /* 4. Finally, the standard set width is scaled, and its correponding */
294 /* `snap zone' is inserted into the sorted snap zones table. */
295 /* */
296 static
297 FT_Error t1_set_snap_zones( T1_Size size )
298 {
299 FT_Int n, direction, n_zones, num_zones;
300 T1_Snap_Zone* zone;
301 T1_Snap_Zone* base_zone;
302 FT_Short* orgs;
303 FT_Pos standard_width;
304 FT_Fixed scale;
305
306 T1_Face face = (T1_Face)size->root.face;
307 T1_Private* priv = &face->type1.private_dict;
308 T1_Size_Hints* hints = size->hints;
309
310
311 /* start with horizontal snap zones */
312 direction = 0;
313 standard_width = priv->standard_width[0];
314 n_zones = priv->num_snap_widths;
315 base_zone = hints->snap_widths;
316 orgs = priv->snap_widths;
317 scale = size->root.metrics.x_scale;
318
319 while ( direction < 2 )
320 {
321 /*********************************************************************/
322 /* */
323 /* Read and scale stem snap widths table from the physical font */
324 /* record. */
325 /* */
326
327 FT_Pos prev, orus, pix, min, max, threshold;
328
329
330 threshold = ONE_PIXEL / 4;
331 zone = base_zone;
332
333 if ( n_zones > 0 )
334 {
335 orus = *orgs++;
336 pix = SCALE( orus );
337 min = pix - threshold;
338 max = pix + threshold;
339
340 zone->orus = orus;
341 zone->pix = pix;
342 zone->min = min;
343 prev = pix;
344
345 for ( n = 1; n < n_zones; n++ )
346 {
347 orus = *orgs++;
348 pix = SCALE( orus );
349
350 if ( pix - prev < 2 * threshold )
351 {
352 min = max = ( pix + prev ) / 2;
353 }
354 else
355 min = pix - threshold;
356
357 zone->max = max;
358 zone++;
359 zone->orus = orus;
360 zone->pix = pix;
361 zone->min = min;
362
363 max = pix + threshold;
364 prev = pix;
365 }
366 zone->max = max;
367 }
368
369 #ifdef FT_DEBUG_LEVEL_TRACE
370
371 /* print the scaled stem snap values in tracing mode */
372
373 FT_TRACE2(( "Set_Snap_Zones: first %s pass\n",
374 direction ? "vertical" : "horizontal" ));
375
376 FT_TRACE2(( "Scaled original stem snap zones:\n" ));
377 FT_TRACE2(( " orus pix min max\n" ));
378 FT_TRACE2(( "-----------------------------\n" ));
379
380 zone = base_zone;
381 for ( n = 0; n < n_zones; n++, zone++ )
382 FT_TRACE2(( " %3d %.2f %.2f %.2f\n",
383 zone->orus,
384 zone->pix / 64.0,
385 zone->min / 64.0,
386 zone->max / 64.0 ));
387 FT_TRACE2(( "\n" ));
388
389 FT_TRACE2(( "Standard width = %d\n", standard_width ));
390
391 #endif /* FT_DEBUG_LEVEL_TRACE */
392
393 /*********************************************************************/
394 /* */
395 /* Now, each snap width which is in the range of the standard set */
396 /* width will be removed from the list. */
397 /* */
398
399 if ( standard_width > 0 )
400 {
401 T1_Snap_Zone* parent;
402 FT_Pos std_pix, std_min, std_max;
403
404
405 std_pix = SCALE( standard_width );
406
407 std_min = std_pix - threshold;
408 std_max = std_pix + threshold;
409
410 num_zones = 0;
411 zone = base_zone;
412 parent = base_zone;
413
414 for ( n = 0; n < n_zones; n++ )
415 {
416 if ( zone->pix >= std_min && zone->pix <= std_max )
417 {
418 /* this zone must be removed from the list */
419 if ( std_min > zone->min )
420 std_min = zone->min;
421 if ( std_max < zone->max )
422 std_max = zone->max;
423 }
424 else
425 {
426 *parent++ = *zone;
427 num_zones++;
428 }
429 zone++;
430 }
431
432 /*******************************************************************/
433 /* */
434 /* Now, insert the standard width zone */
435 /* */
436
437 zone = base_zone + num_zones;
438 while ( zone > base_zone && zone[-1].pix > std_max )
439 {
440 zone[0] = zone[-1];
441 zone--;
442 }
443
444 /* check border zones */
445 if ( zone > base_zone && zone[-1].max > std_min )
446 zone[-1].max = std_min;
447
448 if ( zone < base_zone + num_zones && zone[1].min < std_max )
449 zone[1].min = std_max;
450
451 zone->orus = standard_width;
452 zone->pix = std_pix;
453 zone->min = std_min;
454 zone->max = std_max;
455
456 num_zones++;
457 }
458 else
459 num_zones = n_zones;
460
461 /* save total number of stem snaps now */
462 if ( direction )
463 hints->num_snap_heights = num_zones;
464 else
465 hints->num_snap_widths = num_zones;
466
467 #ifdef FT_DEBUG_LEVEL_TRACE
468
469 /* print the scaled stem snap values in tracing mode */
470
471 FT_TRACE2(( "Set_Snap_Zones: second %s pass\n",
472 direction ? "vertical" : "horizontal" ));
473
474 FT_TRACE2(( "Scaled clipped stem snap zones:\n" ));
475 FT_TRACE2(( " orus pix min max\n" ));
476 FT_TRACE2(( "-----------------------------\n" ));
477
478 zone = base_zone;
479 for ( n = 0; n < num_zones; n++, zone++ )
480 FT_TRACE2(( " %3d %.2f %.2f %.2f\n",
481 zone->orus,
482 zone->pix / 64.0,
483 zone->min / 64.0,
484 zone->max / 64.0 ));
485 FT_TRACE2(( "\n" ));
486
487 FT_TRACE2(( "Standard width = %d\n", standard_width ));
488
489 #endif /* FT_DEBUG_LEVEL_TRACE */
490
491 /* continue with vertical snap zone */
492 direction++;
493 standard_width = priv->standard_height[0];
494 n_zones = priv->num_snap_heights;
495 base_zone = hints->snap_heights;
496 orgs = priv->snap_heights;
497 scale = size->root.metrics.y_scale;
498 }
499
500 return T1_Err_Ok;
501 }
502
503
504 /*************************************************************************/
505 /* */
506 /* <Function> */
507 /* T1_New_Size_Hinter */
508 /* */
509 /* <Description> */
510 /* Allocates a new hinter structure for a given size object. */
511 /* */
512 /* <InOut> */
513 /* size :: A handle to the target size object. */
514 /* */
515 /* <Return> */
516 /* FreeType Error code. 0 means success. */
517 /* */
518 LOCAL_FUNC
519 FT_Error T1_New_Size_Hinter( T1_Size size )
520 {
521 FT_Memory memory = size->root.face->memory;
522
523
524 return MEM_Alloc( size->hints, sizeof ( *size->hints ) );
525 }
526
527
528 /*************************************************************************/
529 /* */
530 /* <Function> */
531 /* T1_Done_Size_Hinter */
532 /* */
533 /* <Description> */
534 /* Releases a given size object's hinter structure. */
535 /* */
536 /* <Input> */
537 /* size :: A handle to the target size object. */
538 /* */
539 LOCAL_FUNC
540 void T1_Done_Size_Hinter( T1_Size size )
541 {
542 FT_Memory memory = size->root.face->memory;
543
544
545 FREE( size->hints );
546 }
547
548
549 /*************************************************************************/
550 /* */
551 /* <Function> */
552 /* T1_Reset_Size_Hinter */
553 /* */
554 /* <Description> */
555 /* Recomputes hinting information when a given size object has */
556 /* changed its resolutions/char sizes/pixel sizes. */
557 /* */
558 /* <InOut> */
559 /* size :: A handle to the size object. */
560 /* */
561 /* <Return> */
562 /* FreeType error code. 0 means success. */
563 /* */
564 LOCAL_FUNC
565 FT_Error T1_Reset_Size_Hinter( T1_Size size )
566 {
567 return t1_set_blue_zones( size ) || t1_set_snap_zones( size );
568 }
569
570
571 /*************************************************************************/
572 /* */
573 /* <Function> */
574 /* T1_New_Glyph_Hinter */
575 /* */
576 /* <Description> */
577 /* Allocates a new hinter structure for a given glyph slot. */
578 /* */
579 /* <InOut> */
580 /* glyph :: A handle to the target glyph slot. */
581 /* */
582 /* <Return> */
583 /* FreeType error code. 0 means success. */
584 /* */
585 LOCAL_FUNC
586 FT_Error T1_New_Glyph_Hinter( T1_GlyphSlot glyph )
587 {
588 FT_Memory memory = glyph->root.face->memory;
589
590
591 return MEM_Alloc( glyph->hints, sizeof ( *glyph->hints ) );
592 }
593
594
595 /*************************************************************************/
596 /* */
597 /* <Function> */
598 /* T1_Done_Glyph_Hinter */
599 /* */
600 /* <Description> */
601 /* Releases a given glyph slot's hinter structure. */
602 /* */
603 /* <Input> */
604 /* glyph :: A handle to the glyph slot. */
605 /* */
606 LOCAL_FUNC
607 void T1_Done_Glyph_Hinter( T1_GlyphSlot glyph )
608 {
609 FT_Memory memory = glyph->root.face->memory;
610
611
612 FREE( glyph->hints );
613 }
614
615
616 /*************************************************************************/
617 /*************************************************************************/
618 /*************************************************************************/
619 /********** **********/
620 /********** HINTED GLYPH LOADER **********/
621 /********** **********/
622 /********** The following code is in charge of the first **********/
623 /********** and second pass when loading a single outline **********/
624 /********** **********/
625 /*************************************************************************/
626 /*************************************************************************/
627 /*************************************************************************/
628
629
630 static
631 FT_Error t1_hinter_ignore( void )
632 {
633 /* do nothing, used for `dotsection' which is unsupported for now */
634 return 0;
635 }
636
637
638 static
639 FT_Error t1_hinter_stem( T1_Builder* builder,
640 FT_Pos pos,
641 FT_Int width,
642 FT_Bool vertical )
643 {
644 T1_Stem_Table* stem_table;
645 T1_Stem_Hint* stems;
646 T1_Stem_Hint* cur_stem;
647 FT_Int min, max, n, num_stems;
648 FT_Bool new_stem;
649 T1_Glyph_Hints* hinter = builder->glyph->hints;
650
651
652 /* select the appropriate stem array */
653 stem_table = vertical ? &hinter->vert_stems : &hinter->hori_stems;
654 stems = stem_table->stems;
655 num_stems = stem_table->num_stems;
656
657 /* Compute minimum and maximum coord for the stem */
658 min = pos + ( vertical
659 ? builder->left_bearing.x
660 : builder->left_bearing.y );
661
662 if ( width >= 0 )
663 max = min + width;
664 else
665 {
666 /* a negative width indicates a `ghost' stem */
667 if ( width == -21 )
668 min += width;
669
670 max = min;
671 }
672
673 /* Now scan the array. If we find a stem with the same borders */
674 /* simply activate it. */
675 cur_stem = stems;
676 new_stem = 1;
677
678 for ( n = 0; n < num_stems; n++, cur_stem++ )
679 {
680 if ( cur_stem->min_edge.orus == min &&
681 cur_stem->max_edge.orus == max )
682 {
683 /* This stem is already in the table, simply activate it */
684 if ( ( cur_stem->hint_flags & T1_HINT_FLAG_ACTIVE ) == 0 )
685 {
686 cur_stem->hint_flags |= T1_HINT_FLAG_ACTIVE;
687 stem_table->num_active++;
688 }
689 new_stem = 0;
690 break;
691 }
692 }
693
694 /* add a new stem to the array if necessary */
695 if ( new_stem )
696 {
697 if ( cur_stem >= stems + T1_HINTER_MAX_EDGES )
698 {
699 FT_ERROR(( "t1_hinter_stem: too many stems in glyph charstring\n" ));
700 return T1_Err_Syntax_Error;
701 }
702
703 /* on the first pass, we record the stem, otherwise, this is */
704 /* a bug in the glyph loader! */
705 if ( builder->pass == 0 )
706 {
707 cur_stem->min_edge.orus = min;
708 cur_stem->max_edge.orus = max;
709 cur_stem->hint_flags = T1_HINT_FLAG_ACTIVE;
710
711 stem_table->num_stems++;
712 stem_table->num_active++;
713 }
714 else
715 {
716 FT_ERROR(( "t1_hinter_stem:" ));
717 FT_ERROR(( " fatal glyph loader bug -- pass2-stem\n" ));
718 return T1_Err_Syntax_Error;
719 }
720 }
721
722 return T1_Err_Ok;
723 }
724
725
726 static
727 FT_Error t1_hinter_stem3( T1_Builder* builder,
728 FT_Pos pos0,
729 FT_Int width0,
730 FT_Pos pos1,
731 FT_Int width1,
732 FT_Pos pos2,
733 FT_Int width2,
734 FT_Bool vertical )
735 {
736 /* For now, simply call `stem' 3 times */
737 return t1_hinter_stem( builder, pos0, width0, vertical ) ||
738 t1_hinter_stem( builder, pos1, width1, vertical ) ||
739 t1_hinter_stem( builder, pos2, width2, vertical );
740 }
741
742
743 static
744 FT_Error t1_hinter_changehints( T1_Builder* builder )
745 {
746 FT_Int dimension;
747 T1_Stem_Table* stem_table;
748 T1_Glyph_Hints* hinter = builder->glyph->hints;
749
750
751 /* If we are in the second pass of glyph hinting, we must */
752 /* call the function T1_Hint_Points() on the builder in order */
753 /* to force the fit the latest points to the pixel grid. */
754 if ( builder->pass == 1 )
755 T1_Hint_Points( builder );
756
757 /* Simply de-activate all hints in all arrays */
758 stem_table = &hinter->hori_stems;
759
760 for ( dimension = 2; dimension > 0; dimension-- )
761 {
762 T1_Stem_Hint* cur = stem_table->stems;
763 T1_Stem_Hint* limit = cur + stem_table->num_stems;
764
765
766 for ( ; cur < limit; cur++ )
767 cur->hint_flags &= ~T1_HINT_FLAG_ACTIVE;
768
769 stem_table->num_active = 0;
770 stem_table = &hinter->vert_stems;
771 }
772
773 return T1_Err_Ok;
774 }
775
776
777 const T1_Hinter_Funcs t1_hinter_funcs =
778 {
779 (T1_Hinter_ChangeHints)t1_hinter_changehints,
780 (T1_Hinter_DotSection) t1_hinter_ignore,
781 (T1_Hinter_Stem) t1_hinter_stem,
782 (T1_Hinter_Stem3) t1_hinter_stem3
783 };
784
785
786 /*************************************************************************/
787 /*************************************************************************/
788 /*************************************************************************/
789 /********** *********/
790 /********** *********/
791 /********** STEM HINTS MANAGEMENT *********/
792 /********** *********/
793 /********** The following code is in charge of computing *********/
794 /********** the placement of each scaled stem hint. *********/
795 /********** *********/
796 /*************************************************************************/
797 /*************************************************************************/
798 /*************************************************************************/
799
800
801 /*************************************************************************/
802 /* */
803 /* <Function> */
804 /* t1_sort_hints */
805 /* */
806 /* <Description> */
807 /* Sorta the list of active stems in increasing order, through the */
808 /* `sort' indexing table. */
809 /* */
810 /* <InOut> */
811 /* table :: A stem hints table. */
812 /* */
813 static
814 void t1_sort_hints( T1_Stem_Table* table )
815 {
816 FT_Int num_stems = table->num_stems;
817 FT_Int num_active = 0;
818 FT_Int* sort = table->sort;
819 T1_Stem_Hint* stems = table->stems;
820 FT_Int n;
821
822
823 /* record active stems in sort table */
824 for ( n = 0; n < num_stems; n++ )
825 {
826 if ( stems[n].hint_flags & T1_HINT_FLAG_ACTIVE )
827 sort[num_active++] = n;
828 }
829
830 /* Now sort the indices. There are usually very few stems, */
831 /* and they are pre-sorted in 90% cases, so we choose a */
832 /* simple bubble sort (quicksort would be slower). */
833 for ( n = 1; n < num_active; n++ )
834 {
835 FT_Int p = n - 1;
836 T1_Stem_Hint* cur = stems + sort[n];
837
838
839 do
840 {
841 FT_Int swap;
842 T1_Stem_Hint* prev = stems + sort[p];
843
844
845 /* note that by definition, the active stems cannot overlap */
846 /* so we simply compare their `min' to sort them (we could compare */
847 /* their max values also; this wouldn't change anything). */
848 if ( prev->min_edge.orus <= cur->min_edge.orus )
849 break;
850
851 /* swap elements */
852 swap = sort[p ];
853 sort[p ] = sort[p + 1];
854 sort[p + 1] = swap;
855 p--;
856
857 } while ( p >= 0 );
858 }
859
860 table->num_active = num_active;
861 }
862
863
864 /*************************************************************************/
865 /* */
866 /* <Function> */
867 /* t1_hint_horizontal_stems */
868 /* */
869 /* <Description> */
870 /* Computes the location of each scaled horizontal stem hint. This */
871 /* takes care of the blue zones and the horizontal stem snap table. */
872 /* */
873 /* <Input> */
874 /* table :: The horizontal stem hints table. */
875 /* */
876 /* hints :: The current size's hint structure. */
877 /* */
878 /* blueShift :: The value of the /BlueShift as taken from the face */
879 /* object. */
880 /* */
881 /* scale :: The 16.16 scale used to convert outline units to */
882 /* 26.6 pixels. */
883 /* */
884 /* <Note> */
885 /* For now, all stems are hinted independently from each other. It */
886 /* might be necessary, for better performance, to introduce the */
887 /* notion of `controlled' hints describing things like counter-stems, */
888 /* stem3, as well as overlapping stems control. */
889 /* */
890 static
891 void t1_hint_horizontal_stems( T1_Stem_Table* table,
892 T1_Size_Hints* hints,
893 FT_Pos blueShift,
894 FT_Fixed scale )
895 {
896 T1_Stem_Hint* stem = table->stems;
897 T1_Stem_Hint* limit = stem + table->num_stems;
898
899
900 /* first of all, scale the blueShift */
901 blueShift = SCALE( blueShift );
902
903 /* then scan the horizontal stem table */
904 for ( ; stem < limit; stem++ )
905 {
906 FT_Pos bottom_orus = stem->min_edge.orus;
907 FT_Pos top_orus = stem->max_edge.orus;
908
909 FT_Pos top_pix = SCALE( top_orus );
910 FT_Pos bottom_pix = SCALE( bottom_orus );
911 FT_Pos width_pix = top_pix - bottom_pix;
912
913 FT_Pos bottom = bottom_pix;
914 FT_Pos top = top_pix;
915 FT_Int align = T1_ALIGN_NONE;
916
917
918 /*********************************************************************/
919 /* */
920 /* Snap pixel width if in stem snap range */
921 /* */
922
923 {
924 T1_Snap_Zone* zone = hints->snap_heights;
925 T1_Snap_Zone* zone_limit = zone + hints->num_snap_heights;
926 FT_Pos best_dist = 32000;
927 T1_Snap_Zone* best_zone = 0;
928
929
930 for ( ; zone < zone_limit; zone++ )
931 {
932 FT_Pos dist;
933
934
935 dist = width_pix - zone->min;
936 if ( dist < 0 )
937 dist = -dist;
938 if ( dist < best_dist )
939 {
940 best_zone = zone;
941 best_dist = dist;
942 }
943 }
944
945 if ( best_zone )
946 {
947 if ( width_pix > best_zone->pix )
948 {
949 width_pix -= 0x20;
950 if ( width_pix < best_zone->pix )
951 width_pix = best_zone->pix;
952 }
953 else
954 {
955 width_pix += 0x20;
956 if ( width_pix > best_zone->pix )
957 width_pix = best_zone->pix;
958 }
959 }
960 }
961
962 /*********************************************************************/
963 /* */
964 /* round width - minimum 1 pixel if this isn't a ghost stem */
965 /* */
966
967 if ( width_pix > 0 )
968 width_pix = width_pix < ONE_PIXEL ? ONE_PIXEL : ROUND( width_pix );
969
970
971 /*********************************************************************/
972 /* */
973 /* Now check for bottom blue zones alignement */
974 /* */
975
976 {
977 FT_Int num_blues = hints->num_bottom_zones;
978 T1_Snap_Zone* blue = hints->blue_zones;
979 T1_Snap_Zone* blue_limit = blue + num_blues;
980
981
982 for ( ; blue < blue_limit; blue++ )
983 {
984 if ( bottom_pix < blue->min )
985 break;
986
987 if ( bottom_pix <= blue->max )
988 {
989 align = T1_ALIGN_BOTTOM;
990 bottom = ROUND( blue->pix );
991
992 /* implement blue shift */
993 if ( !hints->supress_overshoots )
994 {
995 FT_Pos delta = blue->pix - bottom_pix;
996
997
998 delta = delta < blueShift ? 0 : ROUND( delta );
999 bottom -= delta;
1000 }
1001 }
1002 }
1003 }
1004
1005 /*********************************************************************/
1006 /* */
1007 /* check for top blue zones alignement */
1008 /* */
1009
1010 {
1011 FT_Int num_blues = hints->num_blue_zones -
1012 hints->num_bottom_zones;
1013
1014 T1_Snap_Zone* blue = hints->blue_zones +
1015 hints->num_bottom_zones;
1016
1017 T1_Snap_Zone* blue_limit = blue + num_blues;
1018
1019
1020 for ( ; blue < blue_limit; blue++ )
1021 {
1022 if ( top_pix < blue->min )
1023 break;
1024
1025 if ( top_pix <= blue->max )
1026 {
1027 align |= T1_ALIGN_TOP;
1028 top = ROUND( blue->pix );
1029
1030 /* implement blue shift */
1031 if ( !hints->supress_overshoots )
1032 {
1033 FT_Pos delta = top - blue->pix;
1034
1035
1036 delta = delta < blueShift ? 0 : ROUND( delta );
1037 top += delta;
1038 }
1039 }
1040 }
1041 }
1042
1043 /*********************************************************************/
1044 /* */
1045 /* compute the hinted stem position, according to its alignment */
1046 /* */
1047
1048 switch ( align )
1049 {
1050 case T1_ALIGN_BOTTOM: /* bottom zone alignment */
1051 bottom_pix = bottom;
1052 top_pix = bottom + width_pix;
1053 break;
1054
1055 case T1_ALIGN_TOP: /* top zone alignment */
1056 top_pix = top;
1057 bottom_pix = top - width_pix;
1058 break;
1059
1060 case T1_ALIGN_BOTH: /* bottom+top zone alignment */
1061 bottom_pix = bottom;
1062 top_pix = top;
1063 break;
1064
1065 default: /* no alignment */
1066 /* XXX TODO: Add management of controlled stems */
1067 bottom = ( SCALE( bottom_orus + top_orus ) - width_pix ) / 2;
1068
1069 bottom_pix = ROUND( bottom );
1070 top_pix = bottom_pix + width_pix;
1071 }
1072
1073 stem->min_edge.pix = bottom_pix;
1074 stem->max_edge.pix = top_pix;
1075 }
1076 }
1077
1078
1079 /*************************************************************************/
1080 /* */
1081 /* <Function> */
1082 /* t1_hint_vertical_stems */
1083 /* */
1084 /* <Description> */
1085 /* Computes the location of each scaled vertical stem hint. This */
1086 /* takes care of the vertical stem snap table. */
1087 /* */
1088 /* <Input> */
1089 /* table :: The vertical stem hints table. */
1090 /* hints :: The current size's hint structure. */
1091 /* scale :: The 16.16 scale used to convert outline units to */
1092 /* 26.6 pixels. */
1093 /* */
1094 /* <Note> */
1095 /* For now, all stems are hinted independently from each other. It */
1096 /* might be necessary, for better performance, to introduce the */
1097 /* notion of `controlled' hints describing things like counter-stems, */
1098 /* stem3 as well as overlapping stems control. */
1099 /* */
1100 static
1101 void t1_hint_vertical_stems( T1_Stem_Table* table,
1102 T1_Size_Hints* hints,
1103 FT_Fixed scale )
1104 {
1105 T1_Stem_Hint* stem = table->stems;
1106 T1_Stem_Hint* limit = stem + table->num_stems;
1107
1108
1109 for ( ; stem < limit; stem++ )
1110 {
1111 FT_Pos stem_left = stem->min_edge.orus;
1112 FT_Pos stem_right = stem->max_edge.orus;
1113 FT_Pos width_pix, left;
1114
1115
1116 width_pix = SCALE( stem_right - stem_left );
1117
1118 /* Snap pixel width if in stem snap range */
1119 {
1120 T1_Snap_Zone* zone = hints->snap_heights;
1121 T1_Snap_Zone* zone_limit = zone + hints->num_snap_heights;
1122 FT_Pos best_dist = 32000;
1123 T1_Snap_Zone* best_zone = 0;
1124
1125
1126 for ( ; zone < zone_limit; zone++ )
1127 {
1128 FT_Pos dist;
1129
1130
1131 dist = width_pix - zone->min;
1132 if ( dist < 0 )
1133 dist = -dist;
1134 if ( dist < best_dist )
1135 {
1136 best_zone = zone;
1137 best_dist = dist;
1138 }
1139 }
1140
1141 if ( best_zone )
1142 {
1143 if ( width_pix > best_zone->pix )
1144 {
1145 width_pix -= 0x20;
1146 if ( width_pix < best_zone->pix )
1147 width_pix = best_zone->pix;
1148 }
1149 else
1150 {
1151 width_pix += 0x20;
1152 if ( width_pix > best_zone->pix )
1153 width_pix = best_zone->pix;
1154 }
1155 }
1156 }
1157
1158 /* round width - minimum 1 pixel if this isn't a ghost stem */
1159 if ( width_pix > 0 )
1160 width_pix = width_pix < ONE_PIXEL ? ONE_PIXEL
1161 : ROUND( width_pix );
1162
1163 /* now place the snapped and rounded stem */
1164
1165 /* XXX TODO: implement controlled stems for the overlapping */
1166 /* cases */
1167
1168 left = ( SCALE( stem_left + stem_right ) - width_pix ) / 2;
1169
1170 stem->min_edge.pix = ROUND( left );
1171 stem->max_edge.pix = stem->min_edge.pix + width_pix;
1172 }
1173 }
1174
1175
1176 /*************************************************************************/
1177 /* */
1178 /* <Function> */
1179 /* t1_hint_point */
1180 /* */
1181 /* <Description> */
1182 /* Grid-fit a coordinate with regards to a given stem hints table. */
1183 /* */
1184 /* <Input> */
1185 /* table :: The source stem hints table. */
1186 /* coord :: The original coordinate, expressed in font units. */
1187 /* scale :: The 16.16 scale used to convert font units into */
1188 /* 26.6 pixels. */
1189 /* */
1190 /* <Return> */
1191 /* The hinted/scaled value in 26.6 pixels. */
1192 /* */
1193 /* <Note> */
1194 /* For now, all stems are hinted independently from each other. It */
1195 /* might be necessary, for better performance, to introduce the */
1196 /* notion of `controlled' hints describing things like counter-stems, */
1197 /* stem3 as well as overlapping stems control. */
1198 /* */
1199 static
1200 FT_Pos t1_hint_point( T1_Stem_Table* table,
1201 FT_Pos coord,
1202 FT_Fixed scale )
1203 {
1204 FT_Int num_active = table->num_active;
1205 FT_Int n;
1206 T1_Stem_Hint* prev = 0;
1207 T1_Stem_Hint* cur = 0;
1208 T1_Edge* min;
1209 T1_Edge* max;
1210 FT_Pos delta;
1211
1212
1213 /* only hint when there is at least one stem defined */
1214 if ( num_active <= 0 )
1215 return SCALE( coord );
1216
1217 /* scan the stem table to determine placement of coordinate */
1218 /* relative to the list of sorted and stems */
1219 for ( n = 0; n < num_active; n++, prev = cur )
1220 {
1221 cur = table->stems + table->sort[n];
1222
1223 /* is it on the left of the current edge? */
1224 delta = cur->min_edge.orus - coord;
1225 if ( delta == 0 )
1226 return cur->min_edge.pix;
1227
1228 if ( delta > 0 )
1229 {
1230 /* if this is the left of the first edge, simply shift */
1231 if ( !prev )
1232 return cur->min_edge.pix - SCALE( delta );
1233
1234 /* otherwise, interpolate between the maximum of the */
1235 /* previous stem, and the minimum of the current one */
1236 min = &prev->max_edge;
1237 max = &cur->min_edge;
1238
1239 goto Interpolate;
1240 }
1241
1242 /* is it within the current edge? */
1243 delta = cur->max_edge.orus - coord;
1244 if ( delta == 0 )
1245 return cur->max_edge.pix;
1246
1247 if ( delta > 0 )
1248 {
1249 /* interpolate within the stem */
1250 min = &cur->min_edge;
1251 max = &cur->max_edge;
1252
1253 goto Interpolate;
1254 }
1255 }
1256
1257 /* apparently, this coordinate is on the right of the last stem */
1258 delta = coord - cur->max_edge.orus;
1259 return cur->max_edge.pix + SCALE( delta );
1260
1261 Interpolate:
1262 return min->pix + FT_MulDiv( coord - min->orus,
1263 max->pix - min->pix,
1264 max->orus - min->orus );
1265 }
1266
1267
1268 /*************************************************************************/
1269 /* */
1270 /* <Function> */
1271 /* T1_Hint_Points */
1272 /* */
1273 /* <Description> */
1274 /* This function grid-fits several points in a given Type 1 builder */
1275 /* at once. */
1276 /* */
1277 /* <Input> */
1278 /* builder :: A handle to target Type 1 builder. */
1279 /* */
1280 LOCAL_FUNC
1281 void T1_Hint_Points( T1_Builder* builder )
1282 {
1283 FT_Int first = builder->hint_point;
1284 FT_Int last = builder->current->n_points - 1;
1285
1286 T1_Size size = builder->size;
1287 FT_Fixed scale_x = size->root.metrics.x_scale;
1288 FT_Fixed scale_y = size->root.metrics.y_scale;
1289
1290 T1_Glyph_Hints* hints = builder->glyph->hints;
1291 T1_Stem_Table* hori_stems = &hints->hori_stems;
1292 T1_Stem_Table* vert_stems = &hints->vert_stems;
1293
1294 FT_Vector* cur = builder->current->points + first;
1295 FT_Vector* limit = cur + last - first + 1;
1296
1297
1298 /* first of all, sort the active stem hints */
1299 t1_sort_hints( hori_stems );
1300 t1_sort_hints( vert_stems );
1301
1302 for ( ; cur < limit; cur++ )
1303 {
1304 cur->x = t1_hint_point( vert_stems, cur->x, scale_x );
1305 cur->y = t1_hint_point( hori_stems, cur->y, scale_y );
1306 }
1307
1308 builder->hint_point = builder->current->n_points;
1309 }
1310
1311
1312 /*************************************************************************/
1313 /* */
1314 /* <Function> */
1315 /* T1_Hint_Stems */
1316 /* */
1317 /* <Description> */
1318 /* This function is used to compute the location of each stem hint */
1319 /* between the first and second passes of the glyph loader on the */
1320 /* charstring. */
1321 /* */
1322 /* <Input> */
1323 /* builder :: A handle to the target builder. */
1324 /* */
1325 LOCAL_FUNC
1326 void T1_Hint_Stems( T1_Builder* builder )
1327 {
1328 T1_Glyph_Hints* hints = builder->glyph->hints;
1329 T1_Private* priv = &builder->face->type1.private_dict;
1330
1331 T1_Size size = builder->size;
1332 FT_Fixed scale_x = size->root.metrics.x_scale;
1333 FT_Fixed scale_y = size->root.metrics.y_scale;
1334
1335
1336 t1_hint_horizontal_stems( &hints->hori_stems,
1337 builder->size->hints,
1338 priv->blue_shift,
1339 scale_y );
1340
1341 t1_hint_vertical_stems( &hints->vert_stems,
1342 builder->size->hints,
1343 scale_x );
1344 }
1345
1346
1347 /* END */