xref: /inferno-os/libfreetype/pshglob.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1 /***************************************************************************/
2 /*                                                                         */
3 /*  pshglob.c                                                              */
4 /*                                                                         */
5 /*    PostScript hinter global hinting management (body).                  */
6 /*    Inspired by the new auto-hinter module.                              */
7 /*                                                                         */
8 /*  Copyright 2001, 2002 by                                                */
9 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
10 /*                                                                         */
11 /*  This file is part of the FreeType project, and may only be used        */
12 /*  modified and distributed under the terms of the FreeType project       */
13 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
14 /*  this file you indicate that you have read the license and              */
15 /*  understand and accept it fully.                                        */
16 /*                                                                         */
17 /***************************************************************************/
18 
19 
20 #include <ft2build.h>
21 #include FT_FREETYPE_H
22 #include FT_INTERNAL_OBJECTS_H
23 #include "pshglob.h"
24 
25 #ifdef DEBUG_HINTER
26   PSH_Globals  ps_debug_globals = 0;
27 #endif
28 
29 
30   /*************************************************************************/
31   /*************************************************************************/
32   /*****                                                               *****/
33   /*****                       STANDARD WIDTHS                         *****/
34   /*****                                                               *****/
35   /*************************************************************************/
36   /*************************************************************************/
37 
38 
39   /* scale the widths/heights table */
40   static void
psh_globals_scale_widths(PSH_Globals globals,FT_UInt direction)41   psh_globals_scale_widths( PSH_Globals  globals,
42                             FT_UInt      direction )
43   {
44     PSH_Dimension  dim   = &globals->dimension[direction];
45     PSH_Widths     stdw  = &dim->stdw;
46     FT_UInt        count = stdw->count;
47     PSH_Width      width = stdw->widths;
48     PSH_Width      stand = width;               /* standard width/height */
49     FT_Fixed       scale = dim->scale_mult;
50 
51 
52     if ( count > 0 )
53     {
54       width->cur = FT_MulFix( width->org, scale );
55       width->fit = FT_RoundFix( width->cur );
56 
57       width++;
58       count--;
59 
60       for ( ; count > 0; count--, width++ )
61       {
62         FT_Pos  w, dist;
63 
64 
65         w    = FT_MulFix( width->org, scale );
66         dist = w - stand->cur;
67 
68         if ( dist < 0 )
69           dist = -dist;
70 
71         if ( dist < 128 )
72           w = stand->cur;
73 
74         width->cur = w;
75         width->fit = FT_RoundFix( w );
76       }
77     }
78   }
79 
80 
81   /* org_width is is font units, result in device pixels, 26.6 format */
82   FT_LOCAL_DEF( FT_Pos )
psh_dimension_snap_width(PSH_Dimension dimension,FT_Int org_width)83   psh_dimension_snap_width( PSH_Dimension  dimension,
84                             FT_Int         org_width )
85   {
86     FT_UInt  n;
87     FT_Pos   width     = FT_MulFix( org_width, dimension->scale_mult );
88     FT_Pos   best      = 64 + 32 + 2;
89     FT_Pos   reference = width;
90 
91 
92     for ( n = 0; n < dimension->stdw.count; n++ )
93     {
94       FT_Pos  w;
95       FT_Pos  dist;
96 
97 
98       w = dimension->stdw.widths[n].cur;
99       dist = width - w;
100       if ( dist < 0 )
101         dist = -dist;
102       if ( dist < best )
103       {
104         best      = dist;
105         reference = w;
106       }
107     }
108 
109     if ( width >= reference )
110     {
111       width -= 0x21;
112       if ( width < reference )
113         width = reference;
114     }
115     else
116     {
117       width += 0x21;
118       if ( width > reference )
119         width = reference;
120     }
121 
122     return width;
123   }
124 
125 
126   /*************************************************************************/
127   /*************************************************************************/
128   /*****                                                               *****/
129   /*****                       BLUE ZONES                              *****/
130   /*****                                                               *****/
131   /*************************************************************************/
132   /*************************************************************************/
133 
134   static void
psh_blues_set_zones_0(PSH_Blues target,FT_Bool is_others,FT_UInt read_count,FT_Short * read,PSH_Blue_Table top_table,PSH_Blue_Table bot_table)135   psh_blues_set_zones_0( PSH_Blues       target,
136                          FT_Bool         is_others,
137                          FT_UInt         read_count,
138                          FT_Short*       read,
139                          PSH_Blue_Table  top_table,
140                          PSH_Blue_Table  bot_table )
141   {
142     FT_UInt  count_top = top_table->count;
143     FT_UInt  count_bot = bot_table->count;
144     FT_Bool  first     = 1;
145 
146     FT_UNUSED( target );
147 
148 
149     for ( ; read_count > 0; read_count -= 2 )
150     {
151       FT_Int         reference, delta;
152       FT_UInt        count;
153       PSH_Blue_Zone  zones, zone;
154       FT_Bool        top;
155 
156 
157       /* read blue zone entry, and select target top/bottom zone */
158       top = 0;
159       if ( first || is_others )
160       {
161         reference = read[1];
162         delta     = read[0] - reference;
163 
164         zones = bot_table->zones;
165         count = count_bot;
166         first = 0;
167       }
168       else
169       {
170         reference = read[0];
171         delta     = read[1] - reference;
172 
173         zones = top_table->zones;
174         count = count_top;
175         top   = 1;
176       }
177 
178       /* insert into sorted table */
179       zone = zones;
180       for ( ; count > 0; count--, zone++ )
181       {
182         if ( reference < zone->org_ref )
183           break;
184 
185         if ( reference == zone->org_ref )
186         {
187           FT_Int  delta0 = zone->org_delta;
188 
189 
190           /* we have two zones on the same reference position -- */
191           /* only keep the largest one                           */
192           if ( delta < 0 )
193           {
194             if ( delta < delta0 )
195               zone->org_delta = delta;
196           }
197           else
198           {
199             if ( delta > delta0 )
200               zone->org_delta = delta;
201           }
202           goto Skip;
203         }
204       }
205 
206       for ( ; count > 0; count-- )
207         zone[count] = zone[count-1];
208 
209       zone->org_ref   = reference;
210       zone->org_delta = delta;
211 
212       if ( top )
213         count_top++;
214       else
215         count_bot++;
216 
217     Skip:
218       read += 2;
219     }
220 
221     top_table->count = count_top;
222     bot_table->count = count_bot;
223   }
224 
225 
226   /* Re-read blue zones from the original fonts and store them into out */
227   /* private structure.  This function re-orders, sanitizes and         */
228   /* fuzz-expands the zones as well.                                    */
229   static void
psh_blues_set_zones(PSH_Blues target,FT_UInt count,FT_Short * blues,FT_UInt count_others,FT_Short * other_blues,FT_Int fuzz,FT_Int family)230   psh_blues_set_zones( PSH_Blues  target,
231                        FT_UInt    count,
232                        FT_Short*  blues,
233                        FT_UInt    count_others,
234                        FT_Short*  other_blues,
235                        FT_Int     fuzz,
236                        FT_Int     family )
237   {
238     PSH_Blue_Table  top_table, bot_table;
239     FT_Int          count_top, count_bot;
240 
241 
242     if ( family )
243     {
244       top_table = &target->family_top;
245       bot_table = &target->family_bottom;
246     }
247     else
248     {
249       top_table = &target->normal_top;
250       bot_table = &target->normal_bottom;
251     }
252 
253     /* read the input blue zones, and build two sorted tables  */
254     /* (one for the top zones, the other for the bottom zones) */
255     top_table->count = 0;
256     bot_table->count = 0;
257 
258     /* first, the blues */
259     psh_blues_set_zones_0( target, 0,
260                            count, blues, top_table, bot_table );
261     psh_blues_set_zones_0( target, 1,
262                            count_others, other_blues, top_table, bot_table );
263 
264     count_top = top_table->count;
265     count_bot = bot_table->count;
266 
267     /* sanitize top table */
268     if ( count_top > 0 )
269     {
270       PSH_Blue_Zone  zone = top_table->zones;
271 
272 
273       for ( count = count_top; count > 0; count--, zone++ )
274       {
275         FT_Int  delta;
276 
277 
278         if ( count > 1 )
279         {
280           delta = zone[1].org_ref - zone[0].org_ref;
281           if ( zone->org_delta > delta )
282             zone->org_delta = delta;
283         }
284 
285         zone->org_bottom = zone->org_ref;
286         zone->org_top    = zone->org_delta + zone->org_ref;
287       }
288     }
289 
290     /* sanitize bottom table */
291     if ( count_bot > 0 )
292     {
293       PSH_Blue_Zone  zone = bot_table->zones;
294 
295 
296       for ( count = count_bot; count > 0; count--, zone++ )
297       {
298         FT_Int  delta;
299 
300 
301         if ( count > 1 )
302         {
303           delta = zone[0].org_ref - zone[1].org_ref;
304           if ( zone->org_delta < delta )
305             zone->org_delta = delta;
306         }
307 
308         zone->org_top    = zone->org_ref;
309         zone->org_bottom = zone->org_delta + zone->org_ref;
310       }
311     }
312 
313     /* expand top and bottom tables with blue fuzz */
314     {
315       FT_Int         dim, top, bot, delta;
316       PSH_Blue_Zone  zone;
317 
318 
319       zone  = top_table->zones;
320       count = count_top;
321 
322       for ( dim = 1; dim >= 0; dim-- )
323       {
324         if ( count > 0 )
325         {
326           /* expand the bottom of the lowest zone normally */
327           zone->org_bottom -= fuzz;
328 
329           /* expand the top and bottom of intermediate zones;    */
330           /* checking that the interval is smaller than the fuzz */
331           top = zone->org_top;
332 
333           for ( count--; count > 0; count-- )
334           {
335             bot   = zone[1].org_bottom;
336             delta = bot - top;
337 
338             if ( delta < 2 * fuzz )
339               zone[0].org_top = zone[1].org_bottom = top + delta / 2;
340             else
341             {
342               zone[0].org_top    = top + fuzz;
343               zone[1].org_bottom = bot - fuzz;
344             }
345 
346             zone++;
347             top = zone->org_top;
348           }
349 
350           /* expand the top of the highest zone normally */
351           zone->org_top = top + fuzz;
352         }
353         zone  = bot_table->zones;
354         count = count_bot;
355       }
356     }
357   }
358 
359 
360   /* reset the blues table when the device transform changes */
361   static void
psh_blues_scale_zones(PSH_Blues blues,FT_Fixed scale,FT_Pos delta)362   psh_blues_scale_zones( PSH_Blues  blues,
363                          FT_Fixed   scale,
364                          FT_Pos     delta )
365   {
366     FT_UInt         count;
367     FT_UInt         num;
368     PSH_Blue_Table  table = 0;
369 
370     /*                                                        */
371     /* Determine whether we need to suppress overshoots or    */
372     /* not.  We simply need to compare the vertical scale     */
373     /* parameter to the raw bluescale value.  Here is why:    */
374     /*                                                        */
375     /*   We need to suppress overshoots for all pointsizes.   */
376     /*   At 300dpi that satisfy:                              */
377     /*                                                        */
378     /*      pointsize < 240*bluescale + 0.49                  */
379     /*                                                        */
380     /*   This corresponds to:                                 */
381     /*                                                        */
382     /*      pixelsize < 1000*bluescale + 49/24                */
383     /*                                                        */
384     /*      scale*EM_Size < 1000*bluescale + 49/24            */
385     /*                                                        */
386     /*   However, for normal Type 1 fonts, EM_Size is 1000!   */
387     /*   We thus only check:                                  */
388     /*                                                        */
389     /*      scale < bluescale + 49/24000                      */
390     /*                                                        */
391     /*   which we shorten to                                  */
392     /*                                                        */
393     /*      "scale < bluescale"                               */
394     /*                                                        */
395     blues->no_overshoots = FT_BOOL( scale < blues->blue_scale );
396 
397     /*                                                        */
398     /*  The blue threshold is the font units distance under   */
399     /*  which overshoots are suppressed due to the BlueShift  */
400     /*  even if the scale is greater than BlueScale.          */
401     /*                                                        */
402     /*  It is the smallest distance such that                 */
403     /*                                                        */
404     /*    dist <= BlueShift && dist*scale <= 0.5 pixels       */
405     /*                                                        */
406     {
407       FT_Int  threshold = blues->blue_shift;
408 
409 
410       while ( threshold > 0 && FT_MulFix( threshold, scale ) > 32 )
411         threshold --;
412 
413       blues->blue_threshold = threshold;
414     }
415 
416     for ( num = 0; num < 4; num++ )
417     {
418       PSH_Blue_Zone  zone;
419 
420 
421       switch ( num )
422       {
423       case 0:
424         table = &blues->normal_top;
425         break;
426       case 1:
427         table = &blues->normal_bottom;
428         break;
429       case 2:
430         table = &blues->family_top;
431         break;
432       default:
433         table = &blues->family_bottom;
434         break;
435       }
436 
437       zone  = table->zones;
438       count = table->count;
439       for ( ; count > 0; count--, zone++ )
440       {
441         zone->cur_top    = FT_MulFix( zone->org_top,    scale ) + delta;
442         zone->cur_bottom = FT_MulFix( zone->org_bottom, scale ) + delta;
443         zone->cur_ref    = FT_MulFix( zone->org_ref,    scale ) + delta;
444         zone->cur_delta  = FT_MulFix( zone->org_delta,  scale );
445 
446         /* round scaled reference position */
447         zone->cur_ref = ( zone->cur_ref + 32 ) & -64;
448 
449 #if 0
450         if ( zone->cur_ref > zone->cur_top )
451           zone->cur_ref -= 64;
452         else if ( zone->cur_ref < zone->cur_bottom )
453           zone->cur_ref += 64;
454 #endif
455       }
456     }
457 
458     /* process the families now */
459 
460     for ( num = 0; num < 2; num++ )
461     {
462       PSH_Blue_Zone   zone1, zone2;
463       FT_UInt         count1, count2;
464       PSH_Blue_Table  normal, family;
465 
466 
467       switch ( num )
468       {
469       case 0:
470         normal = &blues->normal_top;
471         family = &blues->family_top;
472         break;
473 
474       default:
475         normal = &blues->normal_bottom;
476         family = &blues->family_bottom;
477       }
478 
479       zone1  = normal->zones;
480       count1 = normal->count;
481 
482       for ( ; count1 > 0; count1--, zone1++ )
483       {
484         /* try to find a family zone whose reference position is less */
485         /* than 1 pixel far from the current zone                     */
486         zone2  = family->zones;
487         count2 = family->count;
488 
489         for ( ; count2 > 0; count2--, zone2++ )
490         {
491           FT_Pos  Delta;
492 
493 
494           Delta = zone1->org_ref - zone2->org_ref;
495           if ( Delta < 0 )
496             Delta = -Delta;
497 
498           if ( FT_MulFix( Delta, scale ) < 64 )
499           {
500             zone1->cur_top    = zone2->cur_top;
501             zone1->cur_bottom = zone2->cur_bottom;
502             zone1->cur_ref    = zone2->cur_ref;
503             zone1->cur_delta  = zone2->cur_delta;
504             break;
505           }
506         }
507       }
508     }
509   }
510 
511 
512   FT_LOCAL_DEF( void )
psh_blues_snap_stem(PSH_Blues blues,FT_Int stem_top,FT_Int stem_bot,PSH_Alignment alignment)513   psh_blues_snap_stem( PSH_Blues      blues,
514                        FT_Int         stem_top,
515                        FT_Int         stem_bot,
516                        PSH_Alignment  alignment )
517   {
518     PSH_Blue_Table  table;
519     FT_UInt         count;
520     FT_Pos          delta;
521     PSH_Blue_Zone   zone;
522     FT_Int          no_shoots;
523 
524 
525     alignment->align = PSH_BLUE_ALIGN_NONE;
526 
527     no_shoots = blues->no_overshoots;
528 
529     /* lookup stem top in top zones table */
530     table = &blues->normal_top;
531     count = table->count;
532     zone  = table->zones;
533 
534     for ( ; count > 0; count--, zone++ )
535     {
536       delta = stem_top - zone->org_bottom;
537       if ( delta < -blues->blue_fuzz )
538         break;
539 
540       if ( stem_top <= zone->org_top + blues->blue_fuzz )
541       {
542         if ( no_shoots || delta <= blues->blue_threshold )
543         {
544           alignment->align    |= PSH_BLUE_ALIGN_TOP;
545           alignment->align_top = zone->cur_ref;
546         }
547         break;
548       }
549     }
550 
551     /* look up stem bottom in bottom zones table */
552     table = &blues->normal_bottom;
553     count = table->count;
554     zone  = table->zones + count-1;
555 
556     for ( ; count > 0; count--, zone-- )
557     {
558       delta = zone->org_top - stem_bot;
559       if ( delta < -blues->blue_fuzz )
560         break;
561 
562       if ( stem_bot >= zone->org_bottom - blues->blue_fuzz )
563       {
564         if ( no_shoots || delta < blues->blue_shift )
565         {
566           alignment->align    |= PSH_BLUE_ALIGN_BOT;
567           alignment->align_bot = zone->cur_ref;
568         }
569         break;
570       }
571     }
572   }
573 
574 
575   /*************************************************************************/
576   /*************************************************************************/
577   /*****                                                               *****/
578   /*****                        GLOBAL HINTS                           *****/
579   /*****                                                               *****/
580   /*************************************************************************/
581   /*************************************************************************/
582 
583   static void
psh_globals_destroy(PSH_Globals globals)584   psh_globals_destroy( PSH_Globals  globals )
585   {
586     if ( globals )
587     {
588       FT_Memory  memory;
589 
590 
591       memory = globals->memory;
592       globals->dimension[0].stdw.count = 0;
593       globals->dimension[1].stdw.count = 0;
594 
595       globals->blues.normal_top.count    = 0;
596       globals->blues.normal_bottom.count = 0;
597       globals->blues.family_top.count    = 0;
598       globals->blues.family_bottom.count = 0;
599 
600       FT_FREE( globals );
601 
602 #ifdef DEBUG_HINTER
603       ps_debug_globals = 0;
604 #endif
605     }
606   }
607 
608 
609   static FT_Error
psh_globals_new(FT_Memory memory,T1_Private * priv,PSH_Globals * aglobals)610   psh_globals_new( FT_Memory     memory,
611                    T1_Private*   priv,
612                    PSH_Globals  *aglobals )
613   {
614     PSH_Globals  globals;
615     FT_Error     error;
616 
617 
618     if ( !FT_NEW( globals ) )
619     {
620       FT_UInt    count;
621       FT_Short*  read;
622 
623 
624       globals->memory = memory;
625 
626       /* copy standard widths */
627       {
628         PSH_Dimension  dim   = &globals->dimension[1];
629         PSH_Width      write = dim->stdw.widths;
630 
631 
632         write->org = priv->standard_width[0];
633         write++;
634 
635         read = priv->snap_widths;
636         for ( count = priv->num_snap_widths; count > 0; count-- )
637         {
638           write->org = *read;
639           write++;
640           read++;
641         }
642 
643         dim->stdw.count = write - dim->stdw.widths;
644       }
645 
646       /* copy standard heights */
647       {
648         PSH_Dimension  dim = &globals->dimension[0];
649         PSH_Width      write = dim->stdw.widths;
650 
651 
652         write->org = priv->standard_height[0];
653         write++;
654         read = priv->snap_heights;
655         for ( count = priv->num_snap_heights; count > 0; count-- )
656         {
657           write->org = *read;
658           write++;
659           read++;
660         }
661 
662         dim->stdw.count = write - dim->stdw.widths;
663       }
664 
665       /* copy blue zones */
666       psh_blues_set_zones( &globals->blues, priv->num_blue_values,
667                            priv->blue_values, priv->num_other_blues,
668                            priv->other_blues, priv->blue_fuzz, 0 );
669 
670       psh_blues_set_zones( &globals->blues, priv->num_family_blues,
671                            priv->family_blues, priv->num_family_other_blues,
672                            priv->family_other_blues, priv->blue_fuzz, 1 );
673 
674       globals->blues.blue_scale = priv->blue_scale
675                                   ? priv->blue_scale
676                                   : 0x28937L;   /* 0.039625 * 0x400000L */
677 
678       globals->blues.blue_shift = priv->blue_shift
679                                   ? priv->blue_shift
680                                   : 7;
681 
682       globals->blues.blue_fuzz = priv->blue_fuzz;
683 
684       globals->dimension[0].scale_mult  = 0;
685       globals->dimension[0].scale_delta = 0;
686       globals->dimension[1].scale_mult  = 0;
687       globals->dimension[1].scale_delta = 0;
688 
689 #ifdef DEBUG_HINTER
690       ps_debug_globals = globals;
691 #endif
692     }
693 
694     *aglobals = globals;
695     return error;
696   }
697 
698 
699   static FT_Error
psh_globals_set_scale(PSH_Globals globals,FT_Fixed x_scale,FT_Fixed y_scale,FT_Fixed x_delta,FT_Fixed y_delta)700   psh_globals_set_scale( PSH_Globals  globals,
701                          FT_Fixed     x_scale,
702                          FT_Fixed     y_scale,
703                          FT_Fixed     x_delta,
704                          FT_Fixed     y_delta )
705   {
706     PSH_Dimension  dim = &globals->dimension[0];
707 
708 
709     dim = &globals->dimension[0];
710     if ( x_scale != dim->scale_mult  ||
711          x_delta != dim->scale_delta )
712     {
713       dim->scale_mult  = x_scale;
714       dim->scale_delta = x_delta;
715 
716       psh_globals_scale_widths( globals, 0 );
717     }
718 
719     dim = &globals->dimension[1];
720     if ( y_scale != dim->scale_mult  ||
721          y_delta != dim->scale_delta )
722     {
723       dim->scale_mult  = y_scale;
724       dim->scale_delta = y_delta;
725 
726       psh_globals_scale_widths( globals, 1 );
727       psh_blues_scale_zones( &globals->blues, y_scale, y_delta );
728     }
729 
730     return 0;
731   }
732 
733 
734   FT_LOCAL_DEF( void )
psh_globals_funcs_init(PSH_Globals_FuncsRec * funcs)735   psh_globals_funcs_init( PSH_Globals_FuncsRec*  funcs )
736   {
737     funcs->create    = psh_globals_new;
738     funcs->set_scale = psh_globals_set_scale;
739     funcs->destroy   = psh_globals_destroy;
740   }
741 
742 
743 /* END */
744