blob: 63aab22c1dbe9af77600231917a730b3afc11932 [file] [log] [blame]
David Turner3469d0d2000-07-19 20:02:14 +00001/***************************************************************************/
2/* */
3/* ahglobal.c */
4/* */
Werner Lembergdb578ae2000-07-23 21:27:52 +00005/* Routines used to compute global metrics automatically (body). */
David Turner3469d0d2000-07-19 20:02:14 +00006/* */
Werner Lembergd060a752000-07-20 06:57:41 +00007/* Copyright 2000 Catharon Productions Inc. */
David Turner3469d0d2000-07-19 20:02:14 +00008/* 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 */
Werner Lembergd060a752000-07-20 06:57:41 +000013/* `CatharonLicense.txt'. By continuing to use, modify, or distribute */
David Turner3469d0d2000-07-19 20:02:14 +000014/* this file you indicate that you have read the license and */
15/* understand and accept it fully. */
16/* */
Werner Lembergd060a752000-07-20 06:57:41 +000017/* Note that this license is compatible with the FreeType license. */
David Turner3469d0d2000-07-19 20:02:14 +000018/* */
19/***************************************************************************/
Werner Lembergd060a752000-07-20 06:57:41 +000020
Werner Lemberg90d99642000-12-09 00:45:38 +000021
David Turner19ed8af2000-12-08 02:42:29 +000022#include <ft2build.h>
23#include FT_SOURCE_FILE(autohint,ahglobal.h)
24#include FT_SOURCE_FILE(autohint,ahglyph.h)
David Turner3469d0d2000-07-19 20:02:14 +000025
David Turner3469d0d2000-07-19 20:02:14 +000026
Werner Lembergd060a752000-07-20 06:57:41 +000027#define MAX_TEST_CHARACTERS 12
28
29 static
30 const char* blue_chars[ah_blue_max] =
31 {
32 "THEZOCQS",
33 "HEZLOCUS",
34 "xzroesc",
35 "xzroesc",
36 "pqgjy"
37 };
38
David Turner3469d0d2000-07-19 20:02:14 +000039
40 /* simple insertion sort */
41 static
Werner Lembergd060a752000-07-20 06:57:41 +000042 void sort_values( FT_Int count,
43 FT_Pos* table )
David Turner3469d0d2000-07-19 20:02:14 +000044 {
45 FT_Int i, j, swap;
46
Werner Lembergd060a752000-07-20 06:57:41 +000047
David Turner3469d0d2000-07-19 20:02:14 +000048 for ( i = 1; i < count; i++ )
49 {
50 for ( j = i; j > 1; j-- )
51 {
Werner Lembergd060a752000-07-20 06:57:41 +000052 if ( table[j] > table[j - 1] )
David Turner3469d0d2000-07-19 20:02:14 +000053 break;
54
Werner Lembergd060a752000-07-20 06:57:41 +000055 swap = table[j];
56 table[j] = table[j - 1];
57 table[j - 1] = swap;
David Turner3469d0d2000-07-19 20:02:14 +000058 }
59 }
60 }
61
62
63 static
64 FT_Error ah_hinter_compute_blues( AH_Hinter* hinter )
65 {
Werner Lembergd060a752000-07-20 06:57:41 +000066 AH_Blue blue;
67 AH_Globals* globals = &hinter->globals->design;
68 FT_Pos flats [MAX_TEST_CHARACTERS];
69 FT_Pos rounds[MAX_TEST_CHARACTERS];
70 FT_Int num_flats;
71 FT_Int num_rounds;
David Turner3469d0d2000-07-19 20:02:14 +000072
73 FT_Face face;
74 FT_GlyphSlot glyph;
75 FT_Error error;
76 FT_CharMap charmap;
77
Werner Lembergd060a752000-07-20 06:57:41 +000078
David Turner3469d0d2000-07-19 20:02:14 +000079 face = hinter->face;
80 glyph = face->glyph;
81
82 /* save current charmap */
83 charmap = face->charmap;
84
Werner Lembergd060a752000-07-20 06:57:41 +000085 /* do we have a Unicode charmap in there? */
David Turner3469d0d2000-07-19 20:02:14 +000086 error = FT_Select_Charmap( face, ft_encoding_unicode );
Werner Lembergd060a752000-07-20 06:57:41 +000087 if ( error )
88 goto Exit;
David Turner3469d0d2000-07-19 20:02:14 +000089
Werner Lembergd060a752000-07-20 06:57:41 +000090 /* we compute the blues simply by loading each character from the */
91 /* 'blue_chars[blues]' string, then compute its top-most and */
92 /* bottom-most points */
David Turner3469d0d2000-07-19 20:02:14 +000093
94 AH_LOG(( "blue zones computation\n" ));
95 AH_LOG(( "------------------------------------------------\n" ));
96
Werner Lembergc3dd1512000-07-26 14:11:15 +000097 for ( blue = ah_blue_capital_top; blue < ah_blue_max; blue++ )
David Turner3469d0d2000-07-19 20:02:14 +000098 {
99 const char* p = blue_chars[blue];
100 const char* limit = p + MAX_TEST_CHARACTERS;
Werner Lembergd060a752000-07-20 06:57:41 +0000101 FT_Pos *blue_ref, *blue_shoot;
David Turner3469d0d2000-07-19 20:02:14 +0000102
Werner Lembergd060a752000-07-20 06:57:41 +0000103
Werner Lembergc3dd1512000-07-26 14:11:15 +0000104 AH_LOG(( "blue %3d: ", blue ));
David Turner3469d0d2000-07-19 20:02:14 +0000105
106 num_flats = 0;
107 num_rounds = 0;
Werner Lembergd060a752000-07-20 06:57:41 +0000108
David Turner3469d0d2000-07-19 20:02:14 +0000109 for ( ; p < limit; p++ )
110 {
Werner Lembergd060a752000-07-20 06:57:41 +0000111 FT_UInt glyph_index;
112 FT_Vector* extremum;
113 FT_Vector* points;
114 FT_Vector* point_limit;
115 FT_Vector* point;
116 FT_Bool round;
117
David Turner3469d0d2000-07-19 20:02:14 +0000118
119 /* exit if we reach the end of the string */
Werner Lembergd060a752000-07-20 06:57:41 +0000120 if ( !*p )
121 break;
David Turner3469d0d2000-07-19 20:02:14 +0000122
Werner Lembergd060a752000-07-20 06:57:41 +0000123 AH_LOG(( "`%c'", *p ));
David Turner3469d0d2000-07-19 20:02:14 +0000124
Werner Lembergd060a752000-07-20 06:57:41 +0000125 /* load the character in the face -- skip unknown or empty ones */
David Turner3469d0d2000-07-19 20:02:14 +0000126 glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p );
Werner Lembergd060a752000-07-20 06:57:41 +0000127 if ( glyph_index == 0 )
128 continue;
David Turner3469d0d2000-07-19 20:02:14 +0000129
130 error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
Werner Lembergd060a752000-07-20 06:57:41 +0000131 if ( error || glyph->outline.n_points <= 0 )
132 continue;
David Turner3469d0d2000-07-19 20:02:14 +0000133
134 /* now compute min or max point indices and coordinates */
135 points = glyph->outline.points;
136 point_limit = points + glyph->outline.n_points;
137 point = points;
138 extremum = point;
139 point++;
140
Werner Lembergd060a752000-07-20 06:57:41 +0000141 if ( AH_IS_TOP_BLUE( blue ) )
David Turner3469d0d2000-07-19 20:02:14 +0000142 {
143 for ( ; point < point_limit; point++ )
144 if ( point->y > extremum->y )
145 extremum = point;
146 }
147 else
148 {
149 for ( ; point < point_limit; point++ )
150 if ( point->y < extremum->y )
151 extremum = point;
152 }
153
154 AH_LOG(( "%5d", (int)extremum->y ));
155
Werner Lembergd060a752000-07-20 06:57:41 +0000156 /* now, check whether the point belongs to a straight or round */
157 /* segment; we first need to find in which contour the extremum */
158 /* lies, then see its previous and next points */
David Turner3469d0d2000-07-19 20:02:14 +0000159 {
160 FT_Int index = extremum - points;
161 FT_Int n;
Werner Lembergd060a752000-07-20 06:57:41 +0000162 FT_Int first, last, prev, next, end;
David Turner3469d0d2000-07-19 20:02:14 +0000163 FT_Pos dist;
164
Werner Lembergd060a752000-07-20 06:57:41 +0000165
David Turner3469d0d2000-07-19 20:02:14 +0000166 last = -1;
167 first = 0;
Werner Lembergd060a752000-07-20 06:57:41 +0000168
David Turner3469d0d2000-07-19 20:02:14 +0000169 for ( n = 0; n < glyph->outline.n_contours; n++ )
170 {
171 end = glyph->outline.contours[n];
172 if ( end >= index )
173 {
174 last = end;
175 break;
176 }
Werner Lembergd060a752000-07-20 06:57:41 +0000177 first = end + 1;
David Turner3469d0d2000-07-19 20:02:14 +0000178 }
179
Werner Lembergd060a752000-07-20 06:57:41 +0000180 /* XXX: should never happen! */
David Turner3469d0d2000-07-19 20:02:14 +0000181 if ( last < 0 )
182 continue;
183
184 /* now look for the previous and next points that are not on the */
Werner Lembergd060a752000-07-20 06:57:41 +0000185 /* same Y coordinate. Threshold the `closeness'... */
David Turner3469d0d2000-07-19 20:02:14 +0000186
187 prev = index;
188 next = prev;
189
190 do
191 {
Werner Lembergd060a752000-07-20 06:57:41 +0000192 if ( prev > first )
193 prev--;
194 else
195 prev = last;
David Turner3469d0d2000-07-19 20:02:14 +0000196
197 dist = points[prev].y - extremum->y;
198 if ( dist < -5 || dist > 5 )
199 break;
200
Werner Lembergd060a752000-07-20 06:57:41 +0000201 } while ( prev != index );
David Turner3469d0d2000-07-19 20:02:14 +0000202
203 do
204 {
Werner Lembergd060a752000-07-20 06:57:41 +0000205 if ( next < last )
206 next++;
207 else
208 next = first;
David Turner3469d0d2000-07-19 20:02:14 +0000209
210 dist = points[next].y - extremum->y;
211 if ( dist < -5 || dist > 5 )
212 break;
213
Werner Lembergd060a752000-07-20 06:57:41 +0000214 } while ( next != index );
David Turner3469d0d2000-07-19 20:02:14 +0000215
Werner Lembergd060a752000-07-20 06:57:41 +0000216 /* now, set the `round' flag depending on the segment's kind */
217 round =
218 FT_CURVE_TAG( glyph->outline.tags[prev] ) != FT_Curve_Tag_On ||
219 FT_CURVE_TAG( glyph->outline.tags[next] ) != FT_Curve_Tag_On ;
David Turner3469d0d2000-07-19 20:02:14 +0000220
221 AH_LOG(( "%c ", round ? 'r' : 'f' ));
222 }
223
Werner Lembergd060a752000-07-20 06:57:41 +0000224 if ( round )
225 rounds[num_rounds++] = extremum->y;
David Turner3469d0d2000-07-19 20:02:14 +0000226 else
Werner Lembergd060a752000-07-20 06:57:41 +0000227 flats[num_flats++] = extremum->y;
David Turner3469d0d2000-07-19 20:02:14 +0000228 }
229
230 AH_LOG(( "\n" ));
Werner Lembergd060a752000-07-20 06:57:41 +0000231
232 /* we have computed the contents of the `rounds' and `flats' tables, */
233 /* now determine the reference and overshoot position of the blue; */
234 /* we simply take the median value after a simple short */
David Turner3469d0d2000-07-19 20:02:14 +0000235 sort_values( num_rounds, rounds );
236 sort_values( num_flats, flats );
237
238 blue_ref = globals->blue_refs + blue;
239 blue_shoot = globals->blue_shoots + blue;
240 if ( num_flats == 0 && num_rounds == 0 )
241 {
242 *blue_ref = -10000;
243 *blue_shoot = -10000;
244 }
245 else if ( num_flats == 0 )
246 {
247 *blue_ref =
Werner Lembergd060a752000-07-20 06:57:41 +0000248 *blue_shoot = rounds[num_rounds / 2];
David Turner3469d0d2000-07-19 20:02:14 +0000249 }
250 else if ( num_rounds == 0 )
251 {
252 *blue_ref =
Werner Lembergd060a752000-07-20 06:57:41 +0000253 *blue_shoot = flats[num_flats / 2];
David Turner3469d0d2000-07-19 20:02:14 +0000254 }
255 else
256 {
Werner Lembergd060a752000-07-20 06:57:41 +0000257 *blue_ref = flats[num_flats / 2];
258 *blue_shoot = rounds[num_rounds / 2];
David Turner3469d0d2000-07-19 20:02:14 +0000259 }
260
Werner Lembergd060a752000-07-20 06:57:41 +0000261 /* there are sometimes problems: if the overshoot position of top */
262 /* zones is under its reference position, or the opposite for bottom */
263 /* zones. We must thus check everything there and correct the errors */
David Turner3469d0d2000-07-19 20:02:14 +0000264 if ( *blue_shoot != *blue_ref )
265 {
Werner Lembergd060a752000-07-20 06:57:41 +0000266 FT_Pos ref = *blue_ref;
267 FT_Pos shoot = *blue_shoot;
David Turner3469d0d2000-07-19 20:02:14 +0000268 FT_Bool over_ref = ( shoot > ref );
269
Werner Lembergd060a752000-07-20 06:57:41 +0000270
271 if ( AH_IS_TOP_BLUE( blue ) ^ over_ref )
272 *blue_shoot = *blue_ref = ( shoot + ref ) / 2;
David Turner3469d0d2000-07-19 20:02:14 +0000273 }
Werner Lembergd060a752000-07-20 06:57:41 +0000274
David Turner3469d0d2000-07-19 20:02:14 +0000275 AH_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot ));
276 }
277
278 /* reset original face charmap */
279 FT_Set_Charmap( face, charmap );
280 error = 0;
281
282 Exit:
283 return error;
284 }
285
286
287 static
288 FT_Error ah_hinter_compute_widths( AH_Hinter* hinter )
289 {
290 /* scan the array of segments in each direction */
Werner Lembergd060a752000-07-20 06:57:41 +0000291 AH_Outline* outline = hinter->glyph;
292 AH_Segment* segments;
293 AH_Segment* limit;
294 AH_Globals* globals = &hinter->globals->design;
295 FT_Pos* widths;
296 FT_Int dimension;
297 FT_Int* p_num_widths;
298 FT_Error error = 0;
299 FT_Pos edge_distance_threshold = 32000;
300
David Turner3469d0d2000-07-19 20:02:14 +0000301
302 globals->num_widths = 0;
303 globals->num_heights = 0;
304
Werner Lembergd060a752000-07-20 06:57:41 +0000305 /* For now, compute the standard width and height from the `o' */
306 /* character. I started computing the stem width of the `i' and the */
307 /* stem height of the "-", but it wasn't too good. Moreover, we now */
308 /* have a single character that gives us standard width and height. */
David Turner3469d0d2000-07-19 20:02:14 +0000309 {
310 FT_UInt glyph_index;
311
Werner Lembergd060a752000-07-20 06:57:41 +0000312
David Turner3469d0d2000-07-19 20:02:14 +0000313 glyph_index = FT_Get_Char_Index( hinter->face, 'o' );
Werner Lembergd060a752000-07-20 06:57:41 +0000314 if ( glyph_index == 0 )
315 return 0;
David Turner3469d0d2000-07-19 20:02:14 +0000316
317 error = FT_Load_Glyph( hinter->face, glyph_index, FT_LOAD_NO_SCALE );
Werner Lembergd060a752000-07-20 06:57:41 +0000318 if ( error )
319 goto Exit;
David Turner3469d0d2000-07-19 20:02:14 +0000320
321 error = ah_outline_load( hinter->glyph, hinter->face );
Werner Lembergd060a752000-07-20 06:57:41 +0000322 if ( error )
323 goto Exit;
David Turner3469d0d2000-07-19 20:02:14 +0000324
325 ah_outline_compute_segments( hinter->glyph );
326 ah_outline_link_segments( hinter->glyph );
327 }
328
329 segments = outline->horz_segments;
330 limit = segments + outline->num_hsegments;
331 widths = globals->heights;
332 p_num_widths = &globals->num_heights;
333
334 for ( dimension = 1; dimension >= 0; dimension-- )
335 {
336 AH_Segment* seg = segments;
337 AH_Segment* link;
338 FT_Int num_widths = 0;
339
Werner Lembergd060a752000-07-20 06:57:41 +0000340
David Turner3469d0d2000-07-19 20:02:14 +0000341 for ( ; seg < limit; seg++ )
342 {
343 link = seg->link;
Werner Lembergd060a752000-07-20 06:57:41 +0000344 /* we only consider stem segments there! */
345 if ( link && link->link == seg && link > seg )
David Turner3469d0d2000-07-19 20:02:14 +0000346 {
347 FT_Int dist;
348
Werner Lembergd060a752000-07-20 06:57:41 +0000349
David Turner3469d0d2000-07-19 20:02:14 +0000350 dist = seg->pos - link->pos;
Werner Lembergd060a752000-07-20 06:57:41 +0000351 if ( dist < 0 )
352 dist = -dist;
David Turner3469d0d2000-07-19 20:02:14 +0000353
354 if ( num_widths < 12 )
Werner Lembergd060a752000-07-20 06:57:41 +0000355 widths[num_widths++] = dist;
David Turner3469d0d2000-07-19 20:02:14 +0000356 }
357 }
358
359 sort_values( num_widths, widths );
360 *p_num_widths = num_widths;
361
362 /* we will now try to find the smallest width */
Werner Lembergd060a752000-07-20 06:57:41 +0000363 if ( num_widths > 0 && widths[0] < edge_distance_threshold )
David Turner3469d0d2000-07-19 20:02:14 +0000364 edge_distance_threshold = widths[0];
365
366 segments = outline->vert_segments;
367 limit = segments + outline->num_vsegments;
368 widths = globals->widths;
369 p_num_widths = &globals->num_widths;
370
371 }
372
Werner Lembergd060a752000-07-20 06:57:41 +0000373 /* Now, compute the edge distance threshold as a fraction of the */
374 /* smallest width in the font. Set it in `hinter.glyph' too! */
375 if ( edge_distance_threshold == 32000 )
David Turner3469d0d2000-07-19 20:02:14 +0000376 edge_distance_threshold = 50;
377
378 /* let's try 20% */
Werner Lembergd060a752000-07-20 06:57:41 +0000379 hinter->glyph->edge_distance_threshold = edge_distance_threshold / 5;
David Turner3469d0d2000-07-19 20:02:14 +0000380
381 Exit:
382 return error;
383 }
384
385
David Turner76a5f622000-11-04 01:55:49 +0000386 FT_LOCAL_DEF
David Turner3469d0d2000-07-19 20:02:14 +0000387 FT_Error ah_hinter_compute_globals( AH_Hinter* hinter )
388 {
389 return ah_hinter_compute_widths( hinter ) ||
390 ah_hinter_compute_blues ( hinter );
391 }
392
Werner Lembergd060a752000-07-20 06:57:41 +0000393
394/* END */