xref: /inferno-os/libfreetype/ahglobal.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ahglobal.c                                                             */
4 /*                                                                         */
5 /*    Routines used to compute global metrics automatically (body).        */
6 /*                                                                         */
7 /*  Copyright 2000-2001, 2002 Catharon Productions Inc.                    */
8 /*  Author: David Turner                                                   */
9 /*                                                                         */
10 /*  This file is part of the Catharon Typography Project and shall only    */
11 /*  be used, modified, and distributed under the terms of the Catharon     */
12 /*  Open Source License that should come with this file under the name     */
13 /*  `CatharonLicense.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 /*  Note that this license is compatible with the FreeType license.        */
18 /*                                                                         */
19 /***************************************************************************/
20 
21 
22 #include <ft2build.h>
23 #include FT_INTERNAL_DEBUG_H
24 #include "ahglobal.h"
25 #include "ahglyph.h"
26 
27 
28 #define MAX_TEST_CHARACTERS  12
29 
30   static
31   const char*  blue_chars[AH_BLUE_MAX] =
32   {
33     "THEZOCQS",
34     "HEZLOCUS",
35     "xzroesc",
36     "xzroesc",
37     "pqgjy"
38   };
39 
40 
41   /* simple insertion sort */
42   static void
sort_values(FT_Int count,FT_Pos * table)43   sort_values( FT_Int   count,
44                FT_Pos*  table )
45   {
46     FT_Int  i, j;
47     FT_Pos  swap;
48 
49 
50     for ( i = 1; i < count; i++ )
51     {
52       for ( j = i; j > 0; j-- )
53       {
54         if ( table[j] > table[j - 1] )
55           break;
56 
57         swap         = table[j];
58         table[j]     = table[j - 1];
59         table[j - 1] = swap;
60       }
61     }
62   }
63 
64 
65   static FT_Error
ah_hinter_compute_blues(AH_Hinter hinter)66   ah_hinter_compute_blues( AH_Hinter  hinter )
67   {
68     AH_Blue       blue;
69     AH_Globals    globals = &hinter->globals->design;
70     FT_Pos        flats [MAX_TEST_CHARACTERS];
71     FT_Pos        rounds[MAX_TEST_CHARACTERS];
72     FT_Int        num_flats;
73     FT_Int        num_rounds;
74 
75     FT_Face       face;
76     FT_GlyphSlot  glyph;
77     FT_Error      error;
78     FT_CharMap    charmap;
79 
80 
81     face  = hinter->face;
82     glyph = face->glyph;
83 
84     /* save current charmap */
85     charmap = face->charmap;
86 
87     /* do we have a Unicode charmap in there? */
88     error = FT_Select_Charmap( face, FT_ENCODING_UNICODE );
89     if ( error )
90       goto Exit;
91 
92     /* we compute the blues simply by loading each character from the */
93     /* 'blue_chars[blues]' string, then compute its top-most and      */
94     /* bottom-most points                                             */
95 
96     AH_LOG(( "blue zones computation\n" ));
97     AH_LOG(( "------------------------------------------------\n" ));
98 
99     for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ )
100     {
101       const char*  p     = blue_chars[blue];
102       const char*  limit = p + MAX_TEST_CHARACTERS;
103       FT_Pos       *blue_ref, *blue_shoot;
104 
105 
106       AH_LOG(( "blue %3d: ", blue ));
107 
108       num_flats  = 0;
109       num_rounds = 0;
110 
111       for ( ; p < limit; p++ )
112       {
113         FT_UInt     glyph_index;
114         FT_Vector*  extremum;
115         FT_Vector*  points;
116         FT_Vector*  point_limit;
117         FT_Vector*  point;
118         FT_Bool     round;
119 
120 
121         /* exit if we reach the end of the string */
122         if ( !*p )
123           break;
124 
125         AH_LOG(( "`%c'", *p ));
126 
127         /* load the character in the face -- skip unknown or empty ones */
128         glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p );
129         if ( glyph_index == 0 )
130           continue;
131 
132         error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
133         if ( error || glyph->outline.n_points <= 0 )
134           continue;
135 
136         /* now compute min or max point indices and coordinates */
137         points      = glyph->outline.points;
138         point_limit = points + glyph->outline.n_points;
139         point       = points;
140         extremum    = point;
141         point++;
142 
143         if ( AH_IS_TOP_BLUE( blue ) )
144         {
145           for ( ; point < point_limit; point++ )
146             if ( point->y > extremum->y )
147               extremum = point;
148         }
149         else
150         {
151           for ( ; point < point_limit; point++ )
152             if ( point->y < extremum->y )
153               extremum = point;
154         }
155 
156         AH_LOG(( "%5d", (int)extremum->y ));
157 
158         /* now, check whether the point belongs to a straight or round  */
159         /* segment; we first need to find in which contour the extremum */
160         /* lies, then see its previous and next points                  */
161         {
162           FT_Int  idx = (FT_Int)( extremum - points );
163           FT_Int  n;
164           FT_Int  first, last, prev, next, end;
165           FT_Pos  dist;
166 
167 
168           last  = -1;
169           first = 0;
170 
171           for ( n = 0; n < glyph->outline.n_contours; n++ )
172           {
173             end = glyph->outline.contours[n];
174             if ( end >= idx )
175             {
176               last = end;
177               break;
178             }
179             first = end + 1;
180           }
181 
182           /* XXX: should never happen! */
183           if ( last < 0 )
184             continue;
185 
186           /* now look for the previous and next points that are not on the */
187           /* same Y coordinate.  Threshold the `closeness'...              */
188 
189           prev = idx;
190           next = prev;
191 
192           do
193           {
194             if ( prev > first )
195               prev--;
196             else
197               prev = last;
198 
199             dist = points[prev].y - extremum->y;
200             if ( dist < -5 || dist > 5 )
201               break;
202 
203           } while ( prev != idx );
204 
205           do
206           {
207             if ( next < last )
208               next++;
209             else
210               next = first;
211 
212             dist = points[next].y - extremum->y;
213             if ( dist < -5 || dist > 5 )
214               break;
215 
216           } while ( next != idx );
217 
218           /* now, set the `round' flag depending on the segment's kind */
219           round = FT_BOOL(
220             FT_CURVE_TAG( glyph->outline.tags[prev] ) != FT_CURVE_TAG_ON ||
221             FT_CURVE_TAG( glyph->outline.tags[next] ) != FT_CURVE_TAG_ON );
222 
223           AH_LOG(( "%c ", round ? 'r' : 'f' ));
224         }
225 
226         if ( round )
227           rounds[num_rounds++] = extremum->y;
228         else
229           flats[num_flats++] = extremum->y;
230       }
231 
232       AH_LOG(( "\n" ));
233 
234       /* we have computed the contents of the `rounds' and `flats' tables, */
235       /* now determine the reference and overshoot position of the blue;   */
236       /* we simply take the median value after a simple short              */
237       sort_values( num_rounds, rounds );
238       sort_values( num_flats,  flats  );
239 
240       blue_ref   = globals->blue_refs + blue;
241       blue_shoot = globals->blue_shoots + blue;
242       if ( num_flats == 0 && num_rounds == 0 )
243       {
244         *blue_ref   = -10000;
245         *blue_shoot = -10000;
246       }
247       else if ( num_flats == 0 )
248       {
249         *blue_ref   =
250         *blue_shoot = rounds[num_rounds / 2];
251       }
252       else if ( num_rounds == 0 )
253       {
254         *blue_ref   =
255         *blue_shoot = flats[num_flats / 2];
256       }
257       else
258       {
259         *blue_ref   = flats[num_flats / 2];
260         *blue_shoot = rounds[num_rounds / 2];
261       }
262 
263       /* there are sometimes problems: if the overshoot position of top     */
264       /* zones is under its reference position, or the opposite for bottom  */
265       /* zones.  We must thus check everything there and correct the errors */
266       if ( *blue_shoot != *blue_ref )
267       {
268         FT_Pos   ref      = *blue_ref;
269         FT_Pos   shoot    = *blue_shoot;
270         FT_Bool  over_ref = FT_BOOL( shoot > ref );
271 
272 
273         if ( AH_IS_TOP_BLUE( blue ) ^ over_ref )
274           *blue_shoot = *blue_ref = ( shoot + ref ) / 2;
275       }
276 
277       AH_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot ));
278     }
279 
280     /* reset original face charmap */
281     FT_Set_Charmap( face, charmap );
282     error = 0;
283 
284   Exit:
285     return error;
286   }
287 
288 
289   static FT_Error
ah_hinter_compute_widths(AH_Hinter hinter)290   ah_hinter_compute_widths( AH_Hinter  hinter )
291   {
292     /* scan the array of segments in each direction */
293     AH_Outline  outline = hinter->glyph;
294     AH_Segment  segments;
295     AH_Segment  limit;
296     AH_Globals  globals = &hinter->globals->design;
297     FT_Pos*     widths;
298     FT_Int      dimension;
299     FT_Int*     p_num_widths;
300     FT_Error    error = 0;
301     FT_Pos      edge_distance_threshold = 32000;
302 
303 
304     globals->num_widths  = 0;
305     globals->num_heights = 0;
306 
307     /* For now, compute the standard width and height from the `o'       */
308     /* character.  I started computing the stem width of the `i' and the */
309     /* stem height of the "-", but it wasn't too good.  Moreover, we now */
310     /* have a single character that gives us standard width and height.  */
311     {
312       FT_UInt   glyph_index;
313 
314 
315       glyph_index = FT_Get_Char_Index( hinter->face, 'o' );
316       if ( glyph_index == 0 )
317         return 0;
318 
319       error = FT_Load_Glyph( hinter->face, glyph_index, FT_LOAD_NO_SCALE );
320       if ( error )
321         goto Exit;
322 
323       error = ah_outline_load( hinter->glyph, hinter->face );
324       if ( error )
325         goto Exit;
326 
327       ah_outline_compute_segments( hinter->glyph );
328       ah_outline_link_segments( hinter->glyph );
329     }
330 
331     segments     = outline->horz_segments;
332     limit        = segments + outline->num_hsegments;
333     widths       = globals->heights;
334     p_num_widths = &globals->num_heights;
335 
336     for ( dimension = 1; dimension >= 0; dimension-- )
337     {
338       AH_Segment  seg = segments;
339       AH_Segment  link;
340       FT_Int      num_widths = 0;
341 
342 
343       for ( ; seg < limit; seg++ )
344       {
345         link = seg->link;
346         /* we only consider stem segments there! */
347         if ( link && link->link == seg && link > seg )
348         {
349           FT_Pos  dist;
350 
351 
352           dist = seg->pos - link->pos;
353           if ( dist < 0 )
354             dist = -dist;
355 
356           if ( num_widths < AH_MAX_WIDTHS )
357             widths[num_widths++] = dist;
358         }
359       }
360 
361       sort_values( num_widths, widths );
362       *p_num_widths = num_widths;
363 
364       /* we will now try to find the smallest width */
365       if ( num_widths > 0 && widths[0] < edge_distance_threshold )
366         edge_distance_threshold = widths[0];
367 
368       segments     = outline->vert_segments;
369       limit        = segments + outline->num_vsegments;
370       widths       = globals->widths;
371       p_num_widths = &globals->num_widths;
372     }
373 
374     /* Now, compute the edge distance threshold as a fraction of the */
375     /* smallest width in the font. Set it in `hinter.glyph' too!     */
376     if ( edge_distance_threshold == 32000 )
377       edge_distance_threshold = 50;
378 
379     /* let's try 20% */
380     hinter->glyph->edge_distance_threshold = edge_distance_threshold / 5;
381 
382   Exit:
383     return error;
384   }
385 
386 
387   FT_LOCAL_DEF( FT_Error )
ah_hinter_compute_globals(AH_Hinter hinter)388   ah_hinter_compute_globals( AH_Hinter  hinter )
389   {
390     return ah_hinter_compute_widths( hinter ) ||
391            ah_hinter_compute_blues ( hinter );
392   }
393 
394 
395 /* END */
396