blob: 75fcbcfd4f9d9911b99b0e542b43636cb466e25a [file] [log] [blame]
David Turner3469d0d2000-07-19 20:02:14 +00001/***************************************************************************/
2/* */
3/* ahglyph.c */
4/* */
Werner Lembergdb578ae2000-07-23 21:27:52 +00005/* Routines used to load and analyze a given glyph before hinting */
6/* (body). */
David Turner3469d0d2000-07-19 20:02:14 +00007/* */
Werner Lemberg415235d2001-06-28 17:49:10 +00008/* Copyright 2000-2001 Catharon Productions Inc. */
David Turner3469d0d2000-07-19 20:02:14 +00009/* Author: David Turner */
10/* */
11/* This file is part of the Catharon Typography Project and shall only */
12/* be used, modified, and distributed under the terms of the Catharon */
13/* Open Source License that should come with this file under the name */
Werner Lembergc3dd1512000-07-26 14:11:15 +000014/* `CatharonLicense.txt'. By continuing to use, modify, or distribute */
David Turner3469d0d2000-07-19 20:02:14 +000015/* this file you indicate that you have read the license and */
16/* understand and accept it fully. */
17/* */
Werner Lembergdb578ae2000-07-23 21:27:52 +000018/* Note that this license is compatible with the FreeType license. */
David Turner3469d0d2000-07-19 20:02:14 +000019/* */
20/***************************************************************************/
21
Werner Lembergcc069be2000-12-08 16:17:16 +000022
23#include <ft2build.h>
David Turner8d3a4012001-03-20 11:14:24 +000024#include "ahglyph.h"
25#include "ahangles.h"
26#include "ahglobal.h"
Werner Lemberg1f7f0e82001-06-06 17:30:41 +000027#include "aherrors.h"
Werner Lembergcc069be2000-12-08 16:17:16 +000028
29#include <stdio.h>
David Turner3469d0d2000-07-19 20:02:14 +000030
Werner Lembergdb578ae2000-07-23 21:27:52 +000031
David Turner66cb4792001-05-14 14:04:23 +000032#ifdef AH_DEBUG
33
Werner Lembergf814d0f2001-06-27 16:18:10 +000034 void
35 ah_dump_edges( AH_Outline* outline )
David Turner66cb4792001-05-14 14:04:23 +000036 {
37 AH_Edge* edges;
38 AH_Edge* edge_limit;
39 AH_Segment* segments;
40 FT_Int dimension;
41
David Turner66cb4792001-05-14 14:04:23 +000042 edges = outline->horz_edges;
43 edge_limit = edges + outline->num_hedges;
44 segments = outline->horz_segments;
45
46 for ( dimension = 1; dimension >= 0; dimension-- )
47 {
48 AH_Edge* edge;
49
50
51 printf ( "Table of %s edges:\n",
52 !dimension ? "vertical" : "horizontal" );
53 printf ( " [ index | pos | dir | link |"
54 " serif | blue | opos | pos ]\n" );
55
56 for ( edge = edges; edge < edge_limit; edge++ )
57 {
58 printf ( " [ %5d | %4d | %5s | %4d | %5d | %c | %5.2f | %5.2f ]\n",
59 edge - edges,
60 (int)edge->fpos,
61 edge->dir == ah_dir_up
62 ? "up"
63 : ( edge->dir == ah_dir_down
64 ? "down"
65 : ( edge->dir == ah_dir_left
66 ? "left"
67 : ( edge->dir == ah_dir_right
68 ? "right"
69 : "none" ) ) ),
70 edge->link ? ( edge->link - edges ) : -1,
71 edge->serif ? ( edge->serif - edges ) : -1,
72 edge->blue_edge ? 'y' : 'n',
73 edge->opos / 64.0,
74 edge->pos / 64.0 );
75 }
76
77 edges = outline->vert_edges;
78 edge_limit = edges + outline->num_vedges;
79 segments = outline->vert_segments;
80 }
81 }
82
83
84 /* A function used to dump the array of linked segments */
Werner Lembergf814d0f2001-06-27 16:18:10 +000085 void
86 ah_dump_segments( AH_Outline* outline )
David Turner66cb4792001-05-14 14:04:23 +000087 {
88 AH_Segment* segments;
89 AH_Segment* segment_limit;
90 AH_Point* points;
91 FT_Int dimension;
92
93
94 points = outline->points;
95 segments = outline->horz_segments;
96 segment_limit = segments + outline->num_hsegments;
97
98 for ( dimension = 1; dimension >= 0; dimension-- )
99 {
100 AH_Segment* seg;
101
102
103 printf ( "Table of %s segments:\n",
104 !dimension ? "vertical" : "horizontal" );
105 printf ( " [ index | pos | dir | link | serif |"
106 " numl | first | start ]\n" );
107
108 for ( seg = segments; seg < segment_limit; seg++ )
109 {
110 printf ( " [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n",
111 seg - segments,
112 (int)seg->pos,
113 seg->dir == ah_dir_up
114 ? "up"
115 : ( seg->dir == ah_dir_down
116 ? "down"
117 : ( seg->dir == ah_dir_left
118 ? "left"
119 : ( seg->dir == ah_dir_right
120 ? "right"
121 : "none" ) ) ),
122 seg->link ? (seg->link-segments) : -1,
123 seg->serif ? (seg->serif-segments) : -1,
124 (int)seg->num_linked,
125 seg->first - points,
126 seg->last - points );
127 }
128
129 segments = outline->vert_segments;
130 segment_limit = segments + outline->num_vsegments;
131 }
132 }
133
134#endif /* AH_DEBUG */
David Turner3469d0d2000-07-19 20:02:14 +0000135
Werner Lembergdb578ae2000-07-23 21:27:52 +0000136
137 /* compute the direction value of a given vector.. */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000138 static AH_Direction
139 ah_compute_direction( FT_Pos dx,
140 FT_Pos dy )
David Turner3469d0d2000-07-19 20:02:14 +0000141 {
142 AH_Direction dir;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000143 FT_Pos ax = ABS( dx );
144 FT_Pos ay = ABS( dy );
145
David Turner3469d0d2000-07-19 20:02:14 +0000146
147 dir = ah_dir_none;
148
149 /* test for vertical direction */
Werner Lembergdb578ae2000-07-23 21:27:52 +0000150 if ( ax * 12 < ay )
David Turner3469d0d2000-07-19 20:02:14 +0000151 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000152 dir = dy > 0 ? ah_dir_up : ah_dir_down;
David Turner3469d0d2000-07-19 20:02:14 +0000153 }
154 /* test for horizontal direction */
Werner Lembergdb578ae2000-07-23 21:27:52 +0000155 else if ( ay * 12 < ax )
David Turner3469d0d2000-07-19 20:02:14 +0000156 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000157 dir = dx > 0 ? ah_dir_right : ah_dir_left;
David Turner3469d0d2000-07-19 20:02:14 +0000158 }
159
160 return dir;
161 }
162
David Turner3469d0d2000-07-19 20:02:14 +0000163
David Turner666b11d2000-07-27 15:59:08 +0000164 /* this function is used by ah_get_orientation (see below) to test */
Werner Lembergc703b1b2000-07-27 16:57:35 +0000165 /* the fill direction of a given bbox extrema */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000166 static int
167 ah_test_extrema( FT_Outline* outline,
168 int n )
David Turner666b11d2000-07-27 15:59:08 +0000169 {
170 FT_Vector *prev, *cur, *next;
171 FT_Pos product;
172 FT_Int first, last, c;
173
174
175 /* we need to compute the `previous' and `next' point */
Werner Lembergc703b1b2000-07-27 16:57:35 +0000176 /* for these extrema */
177 cur = outline->points + n;
178 prev = cur - 1;
179 next = cur + 1;
David Turner666b11d2000-07-27 15:59:08 +0000180
181 first = 0;
182 for ( c = 0; c < outline->n_contours; c++ )
183 {
184 last = outline->contours[c];
185
186 if ( n == first )
187 prev = outline->points + last;
188
189 if ( n == last )
190 next = outline->points + first;
191
192 first = last + 1;
193 }
194
195 product = FT_MulDiv( cur->x - prev->x, /* in.x */
196 next->y - cur->y, /* out.y */
197 0x40 )
198 -
199 FT_MulDiv( cur->y - prev->y, /* in.y */
200 next->x - cur->x, /* out.x */
201 0x40 );
202
203 if ( product )
204 product = product > 0 ? 2 : 1;
205
206 return product;
207 }
208
209
210 /* Compute the orientation of path filling. It differs between TrueType */
211 /* and Type1 formats. We could use the `ft_outline_reverse_fill' flag, */
212 /* but it is better to re-compute it directly (it seems that this flag */
213 /* isn't correctly set for some weird composite glyphs currently). */
214 /* */
215 /* We do this by computing bounding box points, and computing their */
216 /* curvature. */
217 /* */
218 /* The function returns either 1 or -1. */
219 /* */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000220 static int
221 ah_get_orientation( FT_Outline* outline )
David Turner666b11d2000-07-27 15:59:08 +0000222 {
223 FT_BBox box;
224 FT_BBox indices;
225 int n, last;
226
227
228 indices.xMin = -1;
229 indices.yMin = -1;
230 indices.xMax = -1;
231 indices.yMax = -1;
232
Werner Lembergcf24d512001-06-18 14:23:45 +0000233 box.xMin = box.yMin = 32767L;
234 box.xMax = box.yMax = -32768L;
David Turner666b11d2000-07-27 15:59:08 +0000235
Werner Lembergc703b1b2000-07-27 16:57:35 +0000236 /* is it empty? */
David Turner666b11d2000-07-27 15:59:08 +0000237 if ( outline->n_contours < 1 )
238 return 1;
239
240 last = outline->contours[outline->n_contours - 1];
241
242 for ( n = 0; n <= last; n++ )
243 {
244 FT_Pos x, y;
245
246
247 x = outline->points[n].x;
248 if ( x < box.xMin )
249 {
250 box.xMin = x;
251 indices.xMin = n;
252 }
253 if ( x > box.xMax )
254 {
255 box.xMax = x;
256 indices.xMax = n;
257 }
258
259 y = outline->points[n].y;
260 if ( y < box.yMin )
261 {
262 box.yMin = y;
263 indices.yMin = n;
264 }
265 if ( y > box.yMax )
266 {
267 box.yMax = y;
268 indices.yMax = n;
269 }
270 }
271
272 /* test orientation of the xmin */
273 n = ah_test_extrema( outline, indices.xMin );
Werner Lembergc703b1b2000-07-27 16:57:35 +0000274 if ( n )
275 goto Exit;
Werner Lemberge4b32a52000-10-31 20:42:18 +0000276
David Turner666b11d2000-07-27 15:59:08 +0000277 n = ah_test_extrema( outline, indices.yMin );
Werner Lembergc703b1b2000-07-27 16:57:35 +0000278 if ( n )
279 goto Exit;
Werner Lemberge4b32a52000-10-31 20:42:18 +0000280
David Turner666b11d2000-07-27 15:59:08 +0000281 n = ah_test_extrema( outline, indices.xMax );
Werner Lembergc703b1b2000-07-27 16:57:35 +0000282 if ( n )
283 goto Exit;
Werner Lemberge4b32a52000-10-31 20:42:18 +0000284
David Turner666b11d2000-07-27 15:59:08 +0000285 n = ah_test_extrema( outline, indices.yMax );
Werner Lembergc703b1b2000-07-27 16:57:35 +0000286 if ( !n )
David Turner666b11d2000-07-27 15:59:08 +0000287 n = 1;
288
289 Exit:
Werner Lemberge4b32a52000-10-31 20:42:18 +0000290 return n;
David Turner666b11d2000-07-27 15:59:08 +0000291 }
292
293
Werner Lembergdb578ae2000-07-23 21:27:52 +0000294 /*************************************************************************/
295 /* */
296 /* <Function> */
297 /* ah_outline_new */
298 /* */
299 /* <Description> */
300 /* Creates a new and empty AH_Outline object. */
301 /* */
David Turnerbc82f1b2002-03-01 02:26:22 +0000302 FT_LOCAL_DEF( FT_Error )
Werner Lembergf814d0f2001-06-27 16:18:10 +0000303 ah_outline_new( FT_Memory memory,
304 AH_Outline** aoutline )
David Turner3469d0d2000-07-19 20:02:14 +0000305 {
306 FT_Error error;
307 AH_Outline* outline;
308
Werner Lembergdb578ae2000-07-23 21:27:52 +0000309
David Turnere459d742002-03-22 13:52:37 +0000310 if ( !FT_NEW( outline ) )
David Turner3469d0d2000-07-19 20:02:14 +0000311 {
312 outline->memory = memory;
313 *aoutline = outline;
314 }
Werner Lembergdb578ae2000-07-23 21:27:52 +0000315
David Turner3469d0d2000-07-19 20:02:14 +0000316 return error;
317 }
318
319
Werner Lembergdb578ae2000-07-23 21:27:52 +0000320 /*************************************************************************/
321 /* */
322 /* <Function> */
323 /* ah_outline_done */
324 /* */
325 /* <Description> */
326 /* Destroys a given AH_Outline object. */
327 /* */
David Turnerbc82f1b2002-03-01 02:26:22 +0000328 FT_LOCAL_DEF( void )
Werner Lembergf814d0f2001-06-27 16:18:10 +0000329 ah_outline_done( AH_Outline* outline )
David Turner3469d0d2000-07-19 20:02:14 +0000330 {
331 FT_Memory memory = outline->memory;
332
Werner Lembergdb578ae2000-07-23 21:27:52 +0000333
David Turnere459d742002-03-22 13:52:37 +0000334 FT_FREE( outline->horz_edges );
335 FT_FREE( outline->horz_segments );
336 FT_FREE( outline->contours );
337 FT_FREE( outline->points );
David Turner3469d0d2000-07-19 20:02:14 +0000338
David Turnere459d742002-03-22 13:52:37 +0000339 FT_FREE( outline );
David Turner3469d0d2000-07-19 20:02:14 +0000340 }
341
342
Werner Lembergdb578ae2000-07-23 21:27:52 +0000343 /*************************************************************************/
344 /* */
345 /* <Function> */
346 /* ah_outline_save */
347 /* */
348 /* <Description> */
349 /* Saves the content of a given AH_Outline object into a face's glyph */
350 /* slot. */
351 /* */
David Turnerbc82f1b2002-03-01 02:26:22 +0000352 FT_LOCAL_DEF( void )
Werner Lembergf814d0f2001-06-27 16:18:10 +0000353 ah_outline_save( AH_Outline* outline,
David Turner4d570242002-02-24 02:59:24 +0000354 AH_Loader gloader )
David Turner3469d0d2000-07-19 20:02:14 +0000355 {
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000356 AH_Point* point = outline->points;
357 AH_Point* point_limit = point + outline->num_points;
358 FT_Vector* vec = gloader->current.outline.points;
359 char* tag = gloader->current.outline.tags;
David Turner3469d0d2000-07-19 20:02:14 +0000360
Werner Lembergdb578ae2000-07-23 21:27:52 +0000361
David Turner3469d0d2000-07-19 20:02:14 +0000362 /* we assume that the glyph loader has already been checked for storage */
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000363 for ( ; point < point_limit; point++, vec++, tag++ )
David Turner3469d0d2000-07-19 20:02:14 +0000364 {
365 vec->x = point->x;
366 vec->y = point->y;
367
David Turnerb92479b2001-10-29 10:45:57 +0000368 if ( point->flags & ah_flag_conic )
David Turner3469d0d2000-07-19 20:02:14 +0000369 tag[0] = FT_Curve_Tag_Conic;
David Turnerb92479b2001-10-29 10:45:57 +0000370 else if ( point->flags & ah_flag_cubic )
David Turner3469d0d2000-07-19 20:02:14 +0000371 tag[0] = FT_Curve_Tag_Cubic;
372 else
373 tag[0] = FT_Curve_Tag_On;
374 }
375 }
376
377
Werner Lembergdb578ae2000-07-23 21:27:52 +0000378 /*************************************************************************/
379 /* */
380 /* <Function> */
381 /* ah_outline_load */
382 /* */
383 /* <Description> */
384 /* Loads an unscaled outline from a glyph slot into an AH_Outline */
385 /* object. */
386 /* */
David Turnerbc82f1b2002-03-01 02:26:22 +0000387 FT_LOCAL_DEF( FT_Error )
Werner Lembergf814d0f2001-06-27 16:18:10 +0000388 ah_outline_load( AH_Outline* outline,
389 FT_Face face )
David Turner3469d0d2000-07-19 20:02:14 +0000390 {
391 FT_Memory memory = outline->memory;
Werner Lemberg1f7f0e82001-06-06 17:30:41 +0000392 FT_Error error = AH_Err_Ok;
David Turner3469d0d2000-07-19 20:02:14 +0000393 FT_Outline* source = &face->glyph->outline;
394 FT_Int num_points = source->n_points;
395 FT_Int num_contours = source->n_contours;
396 AH_Point* points;
397
Werner Lembergdb578ae2000-07-23 21:27:52 +0000398
David Turner3469d0d2000-07-19 20:02:14 +0000399 /* check arguments */
Werner Lembergdb578ae2000-07-23 21:27:52 +0000400 if ( !face ||
401 !face->size ||
402 face->glyph->format != ft_glyph_format_outline )
Werner Lemberg1f7f0e82001-06-06 17:30:41 +0000403 return AH_Err_Invalid_Argument;
David Turner3469d0d2000-07-19 20:02:14 +0000404
Werner Lembergdb578ae2000-07-23 21:27:52 +0000405 /* first of all, reallocate the contours array if necessary */
David Turner3469d0d2000-07-19 20:02:14 +0000406 if ( num_contours > outline->max_contours )
407 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000408 FT_Int new_contours = ( num_contours + 3 ) & -4;
409
David Turner3469d0d2000-07-19 20:02:14 +0000410
David Turnere459d742002-03-22 13:52:37 +0000411 if ( FT_RENEW_ARRAY( outline->contours,
412 outline->max_contours,
413 new_contours ) )
David Turner3469d0d2000-07-19 20:02:14 +0000414 goto Exit;
415
416 outline->max_contours = new_contours;
417 }
418
Werner Lemberg58b17f92000-07-27 23:29:08 +0000419 /* then, reallocate the points, segments & edges arrays if needed -- */
420 /* note that we reserved two additional point positions, used to */
421 /* hint metrics appropriately */
422 /* */
423 if ( num_points + 2 > outline->max_points )
David Turner3469d0d2000-07-19 20:02:14 +0000424 {
Werner Lemberg58b17f92000-07-27 23:29:08 +0000425 FT_Int news = ( num_points + 2 + 7 ) & -8;
David Turner3469d0d2000-07-19 20:02:14 +0000426 FT_Int max = outline->max_points;
427
Werner Lembergdb578ae2000-07-23 21:27:52 +0000428
David Turnere459d742002-03-22 13:52:37 +0000429 if ( FT_RENEW_ARRAY( outline->points, max, news ) ||
430 FT_RENEW_ARRAY( outline->horz_edges, max * 2, news * 2 ) ||
431 FT_RENEW_ARRAY( outline->horz_segments, max * 2, news * 2 ) )
David Turner3469d0d2000-07-19 20:02:14 +0000432 goto Exit;
433
434 /* readjust some pointers */
David Turnerf00a4de2000-10-03 22:03:09 +0000435 outline->vert_edges = outline->horz_edges + news;
436 outline->vert_segments = outline->horz_segments + news;
David Turner3469d0d2000-07-19 20:02:14 +0000437 outline->max_points = news;
438 }
439
440 outline->num_points = num_points;
441 outline->num_contours = num_contours;
442
443 outline->num_hedges = 0;
444 outline->num_vedges = 0;
445 outline->num_hsegments = 0;
446 outline->num_vsegments = 0;
447
Werner Lembergc703b1b2000-07-27 16:57:35 +0000448 /* We can't rely on the value of `FT_Outline.flags' to know the fill */
449 /* direction used for a glyph, given that some fonts are broken (e.g. */
450 /* the Arphic ones). We thus recompute it each time we need to. */
451 /* */
David Turner666b11d2000-07-27 15:59:08 +0000452 outline->vert_major_dir = ah_dir_up;
453 outline->horz_major_dir = ah_dir_left;
454
455 if ( ah_get_orientation( source ) > 1 )
456 {
457 outline->vert_major_dir = ah_dir_down;
458 outline->horz_major_dir = ah_dir_right;
Werner Lemberge4b32a52000-10-31 20:42:18 +0000459 }
Werner Lembergc703b1b2000-07-27 16:57:35 +0000460
David Turner3469d0d2000-07-19 20:02:14 +0000461 outline->x_scale = face->size->metrics.x_scale;
462 outline->y_scale = face->size->metrics.y_scale;
463
464 points = outline->points;
Werner Lembergc703b1b2000-07-27 16:57:35 +0000465 if ( outline->num_points == 0 )
David Turner666b11d2000-07-27 15:59:08 +0000466 goto Exit;
David Turner3469d0d2000-07-19 20:02:14 +0000467
468 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000469 /* do one thing at a time -- it is easier to understand, and */
470 /* the code is clearer */
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000471 AH_Point* point;
472 AH_Point* point_limit = points + outline->num_points;
David Turner3469d0d2000-07-19 20:02:14 +0000473
Werner Lembergdb578ae2000-07-23 21:27:52 +0000474
David Turner3469d0d2000-07-19 20:02:14 +0000475 /* compute coordinates */
476 {
477 FT_Vector* vec = source->points;
478 FT_Fixed x_scale = outline->x_scale;
479 FT_Fixed y_scale = outline->y_scale;
480
Werner Lembergdb578ae2000-07-23 21:27:52 +0000481
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000482 for ( point = points; point < point_limit; vec++, point++ )
David Turner3469d0d2000-07-19 20:02:14 +0000483 {
484 point->fx = vec->x;
485 point->fy = vec->y;
486 point->ox = point->x = FT_MulFix( vec->x, x_scale );
487 point->oy = point->y = FT_MulFix( vec->y, y_scale );
Werner Lembergdb578ae2000-07-23 21:27:52 +0000488
David Turner3469d0d2000-07-19 20:02:14 +0000489 point->flags = 0;
490 }
491 }
492
Werner Lembergdb578ae2000-07-23 21:27:52 +0000493 /* compute Bezier flags */
David Turner3469d0d2000-07-19 20:02:14 +0000494 {
495 char* tag = source->tags;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000496
497
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000498 for ( point = points; point < point_limit; point++, tag++ )
David Turner3469d0d2000-07-19 20:02:14 +0000499 {
500 switch ( FT_CURVE_TAG( *tag ) )
501 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000502 case FT_Curve_Tag_Conic:
David Turnerb92479b2001-10-29 10:45:57 +0000503 point->flags = ah_flag_conic; break;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000504 case FT_Curve_Tag_Cubic:
David Turnerb92479b2001-10-29 10:45:57 +0000505 point->flags = ah_flag_cubic; break;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000506 default:
507 ;
David Turner3469d0d2000-07-19 20:02:14 +0000508 }
509 }
510 }
511
Werner Lembergdb578ae2000-07-23 21:27:52 +0000512 /* compute `next' and `prev' */
David Turner3469d0d2000-07-19 20:02:14 +0000513 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000514 FT_Int contour_index;
515 AH_Point* prev;
516 AH_Point* first;
517 AH_Point* end;
518
David Turner3469d0d2000-07-19 20:02:14 +0000519
520 contour_index = 0;
521
522 first = points;
523 end = points + source->contours[0];
524 prev = end;
525
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000526 for ( point = points; point < point_limit; point++ )
David Turner3469d0d2000-07-19 20:02:14 +0000527 {
528 point->prev = prev;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000529 if ( point < end )
David Turner3469d0d2000-07-19 20:02:14 +0000530 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000531 point->next = point + 1;
David Turner3469d0d2000-07-19 20:02:14 +0000532 prev = point;
533 }
534 else
535 {
536 point->next = first;
537 contour_index++;
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000538 if ( point + 1 < point_limit )
Werner Lembergdb578ae2000-07-23 21:27:52 +0000539 {
David Turner3469d0d2000-07-19 20:02:14 +0000540 end = points + source->contours[contour_index];
Werner Lembergdb578ae2000-07-23 21:27:52 +0000541 first = point + 1;
David Turner3469d0d2000-07-19 20:02:14 +0000542 prev = end;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000543 }
David Turner3469d0d2000-07-19 20:02:14 +0000544 }
545 }
546 }
547
548 /* set-up the contours array */
549 {
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000550 AH_Point** contour = outline->contours;
551 AH_Point** contour_limit = contour + outline->num_contours;
552 short* end = source->contours;
Werner Lemberg0d9165e2002-03-07 21:59:59 +0000553 short idx = 0;
David Turner3469d0d2000-07-19 20:02:14 +0000554
Werner Lembergdb578ae2000-07-23 21:27:52 +0000555
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000556 for ( ; contour < contour_limit; contour++, end++ )
David Turner3469d0d2000-07-19 20:02:14 +0000557 {
Werner Lemberg0d9165e2002-03-07 21:59:59 +0000558 contour[0] = points + idx;
559 idx = (short)( end[0] + 1 );
David Turner3469d0d2000-07-19 20:02:14 +0000560 }
561 }
562
563 /* compute directions of in & out vectors */
564 {
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000565 for ( point = points; point < point_limit; point++ )
David Turner3469d0d2000-07-19 20:02:14 +0000566 {
567 AH_Point* prev;
568 AH_Point* next;
David Turnerb92479b2001-10-29 10:45:57 +0000569 FT_Vector ivec, ovec;
David Turner3469d0d2000-07-19 20:02:14 +0000570
Werner Lembergdb578ae2000-07-23 21:27:52 +0000571
David Turnerb92479b2001-10-29 10:45:57 +0000572 prev = point->prev;
573 ivec.x = point->fx - prev->fx;
574 ivec.y = point->fy - prev->fy;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000575
David Turnerb92479b2001-10-29 10:45:57 +0000576 point->in_dir = ah_compute_direction( ivec.x, ivec.y );
577
578 next = point->next;
579 ovec.x = next->fx - point->fx;
580 ovec.y = next->fy - point->fy;
581
582 point->out_dir = ah_compute_direction( ovec.x, ovec.y );
David Turner3469d0d2000-07-19 20:02:14 +0000583
584#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
David Turnerb92479b2001-10-29 10:45:57 +0000585 if ( point->flags & (ah_flag_conic | ah_flag_cubic) )
David Turner3469d0d2000-07-19 20:02:14 +0000586 {
David Turnerb92479b2001-10-29 10:45:57 +0000587 Is_Weak_Point:
588 point->flags |= ah_flag_weak_interpolation;
589 }
590 else if ( point->out_dir == point->in_dir )
591 {
592 AH_Angle angle_in, angle_out, delta;
David Turner3469d0d2000-07-19 20:02:14 +0000593
Werner Lembergdb578ae2000-07-23 21:27:52 +0000594
David Turnerb92479b2001-10-29 10:45:57 +0000595 if ( point->out_dir != ah_dir_none )
596 goto Is_Weak_Point;
Werner Lembergc3b21602001-12-05 01:22:05 +0000597
David Turnerb92479b2001-10-29 10:45:57 +0000598 angle_in = ah_angle( &ivec );
599 angle_out = ah_angle( &ovec );
600 delta = angle_in - angle_out;
Werner Lembergc3b21602001-12-05 01:22:05 +0000601
David Turnerb92479b2001-10-29 10:45:57 +0000602 if ( delta > AH_PI )
603 delta = AH_2PI - delta;
604
Werner Lembergdb578ae2000-07-23 21:27:52 +0000605 if ( delta < 0 )
606 delta = -delta;
David Turnerb92479b2001-10-29 10:45:57 +0000607
Werner Lembergdb578ae2000-07-23 21:27:52 +0000608 if ( delta < 2 )
David Turnerb92479b2001-10-29 10:45:57 +0000609 goto Is_Weak_Point;
David Turner3469d0d2000-07-19 20:02:14 +0000610 }
David Turnerb92479b2001-10-29 10:45:57 +0000611 else if ( point->in_dir == -point->out_dir )
612 goto Is_Weak_Point;
David Turner3469d0d2000-07-19 20:02:14 +0000613#endif
614 }
615 }
616 }
Werner Lembergdb578ae2000-07-23 21:27:52 +0000617
David Turner3469d0d2000-07-19 20:02:14 +0000618 Exit:
619 return error;
620 }
621
622
David Turnerbc82f1b2002-03-01 02:26:22 +0000623 FT_LOCAL_DEF( void )
Werner Lembergf814d0f2001-06-27 16:18:10 +0000624 ah_setup_uv( AH_Outline* outline,
625 AH_UV source )
David Turner3469d0d2000-07-19 20:02:14 +0000626 {
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000627 AH_Point* point = outline->points;
628 AH_Point* point_limit = point + outline->num_points;
David Turner3469d0d2000-07-19 20:02:14 +0000629
Werner Lembergdb578ae2000-07-23 21:27:52 +0000630
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000631 for ( ; point < point_limit; point++ )
David Turner3469d0d2000-07-19 20:02:14 +0000632 {
633 FT_Pos u, v;
634
Werner Lembergdb578ae2000-07-23 21:27:52 +0000635
636 switch ( source )
David Turner3469d0d2000-07-19 20:02:14 +0000637 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000638 case ah_uv_fxy:
639 u = point->fx;
640 v = point->fy;
641 break;
642 case ah_uv_fyx:
643 u = point->fy;
644 v = point->fx;
645 break;
646 case ah_uv_oxy:
647 u = point->ox;
648 v = point->oy;
649 break;
650 case ah_uv_oyx:
651 u = point->oy;
652 v = point->ox;
653 break;
654 case ah_uv_yx:
655 u = point->y;
656 v = point->x;
657 break;
658 case ah_uv_ox:
659 u = point->x;
660 v = point->ox;
661 break;
662 case ah_uv_oy:
663 u = point->y;
664 v = point->oy;
665 break;
666 default:
667 u = point->x;
668 v = point->y;
669 break;
David Turner3469d0d2000-07-19 20:02:14 +0000670 }
671 point->u = u;
672 point->v = v;
673 }
674 }
675
676
David Turnerbc82f1b2002-03-01 02:26:22 +0000677 FT_LOCAL_DEF( void )
Werner Lembergf814d0f2001-06-27 16:18:10 +0000678 ah_outline_compute_segments( AH_Outline* outline )
David Turner3469d0d2000-07-19 20:02:14 +0000679 {
680 int dimension;
681 AH_Segment* segments;
682 FT_Int* p_num_segments;
683 AH_Direction segment_dir;
684 AH_Direction major_dir;
685
Werner Lembergdb578ae2000-07-23 21:27:52 +0000686
David Turner3469d0d2000-07-19 20:02:14 +0000687 segments = outline->horz_segments;
688 p_num_segments = &outline->num_hsegments;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000689 major_dir = ah_dir_right; /* This value must be positive! */
David Turner3469d0d2000-07-19 20:02:14 +0000690 segment_dir = major_dir;
691
692 /* set up (u,v) in each point */
693 ah_setup_uv( outline, ah_uv_fyx );
694
695 for ( dimension = 1; dimension >= 0; dimension-- )
696 {
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000697 AH_Point** contour = outline->contours;
698 AH_Point** contour_limit = contour + outline->num_contours;
699 AH_Segment* segment = segments;
700 FT_Int num_segments = 0;
David Turner3469d0d2000-07-19 20:02:14 +0000701
702#ifdef AH_HINT_METRICS
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000703 AH_Point* min_point = 0;
704 AH_Point* max_point = 0;
705 FT_Pos min_coord = 32000;
706 FT_Pos max_coord = -32000;
David Turner3469d0d2000-07-19 20:02:14 +0000707#endif
Werner Lembergdb578ae2000-07-23 21:27:52 +0000708
709
David Turner3469d0d2000-07-19 20:02:14 +0000710 /* do each contour separately */
711 for ( ; contour < contour_limit; contour++ )
712 {
713 AH_Point* point = contour[0];
714 AH_Point* last = point->prev;
715 int on_edge = 0;
716 FT_Pos min_pos = +32000; /* minimum segment pos != min_coord */
717 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
718 FT_Bool passed;
719
Werner Lembergdb578ae2000-07-23 21:27:52 +0000720
David Turner3469d0d2000-07-19 20:02:14 +0000721#ifdef AH_HINT_METRICS
Werner Lembergdb578ae2000-07-23 21:27:52 +0000722 if ( point->u < min_coord )
David Turner3469d0d2000-07-19 20:02:14 +0000723 {
724 min_coord = point->u;
725 min_point = point;
726 }
Werner Lembergdb578ae2000-07-23 21:27:52 +0000727 if ( point->u > max_coord )
David Turner3469d0d2000-07-19 20:02:14 +0000728 {
729 max_coord = point->u;
730 max_point = point;
731 }
732#endif
733
Werner Lembergdb578ae2000-07-23 21:27:52 +0000734 if ( point == last ) /* skip singletons -- just in case? */
David Turner3469d0d2000-07-19 20:02:14 +0000735 continue;
736
Werner Lembergdb578ae2000-07-23 21:27:52 +0000737 if ( ABS( last->out_dir ) == major_dir &&
738 ABS( point->out_dir ) == major_dir )
David Turner3469d0d2000-07-19 20:02:14 +0000739 {
740 /* we are already on an edge, try to locate its start */
Werner Lembergdb578ae2000-07-23 21:27:52 +0000741 last = point;
742
David Turner3469d0d2000-07-19 20:02:14 +0000743 for (;;)
744 {
745 point = point->prev;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000746 if ( ABS( point->out_dir ) != major_dir )
David Turner3469d0d2000-07-19 20:02:14 +0000747 {
748 point = point->next;
749 break;
750 }
751 if ( point == last )
752 break;
753 }
David Turner3469d0d2000-07-19 20:02:14 +0000754 }
755
756 last = point;
757 passed = 0;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000758
David Turner3469d0d2000-07-19 20:02:14 +0000759 for (;;)
760 {
761 FT_Pos u, v;
762
Werner Lembergdb578ae2000-07-23 21:27:52 +0000763
764 if ( on_edge )
David Turner3469d0d2000-07-19 20:02:14 +0000765 {
766 u = point->u;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000767 if ( u < min_pos )
768 min_pos = u;
769 if ( u > max_pos )
770 max_pos = u;
David Turner3469d0d2000-07-19 20:02:14 +0000771
Werner Lembergdb578ae2000-07-23 21:27:52 +0000772 if ( point->out_dir != segment_dir || point == last )
David Turner3469d0d2000-07-19 20:02:14 +0000773 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000774 /* we are just leaving an edge; record a new segment! */
David Turner3469d0d2000-07-19 20:02:14 +0000775 segment->last = point;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000776 segment->pos = ( min_pos + max_pos ) >> 1;
David Turner3469d0d2000-07-19 20:02:14 +0000777
778 /* a segment is round if either its first or last point */
Werner Lembergdb578ae2000-07-23 21:27:52 +0000779 /* is a control point */
780 if ( ( segment->first->flags | point->flags ) &
David Turnerb92479b2001-10-29 10:45:57 +0000781 ah_flag_control )
David Turner3469d0d2000-07-19 20:02:14 +0000782 segment->flags |= ah_edge_round;
783
784 /* compute segment size */
Werner Lembergdb578ae2000-07-23 21:27:52 +0000785 min_pos = max_pos = point->v;
786
David Turner3469d0d2000-07-19 20:02:14 +0000787 v = segment->first->v;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000788 if ( v < min_pos )
789 min_pos = v;
790 if ( v > max_pos )
791 max_pos = v;
792
David Turner3469d0d2000-07-19 20:02:14 +0000793 segment->min_coord = min_pos;
794 segment->max_coord = max_pos;
795
796 on_edge = 0;
797 num_segments++;
798 segment++;
799 /* fallthrough */
800 }
801 }
802
Werner Lembergdb578ae2000-07-23 21:27:52 +0000803 /* now exit if we are at the start/end point */
804 if ( point == last )
David Turner3469d0d2000-07-19 20:02:14 +0000805 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000806 if ( passed )
David Turner3469d0d2000-07-19 20:02:14 +0000807 break;
808 passed = 1;
809 }
810
Werner Lembergdb578ae2000-07-23 21:27:52 +0000811 if ( !on_edge && ABS( point->out_dir ) == major_dir )
David Turner3469d0d2000-07-19 20:02:14 +0000812 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000813 /* this is the start of a new segment! */
814 segment_dir = point->out_dir;
David Turner3469d0d2000-07-19 20:02:14 +0000815
816 /* clear all segment fields */
David Turnere459d742002-03-22 13:52:37 +0000817 FT_MEM_SET( segment, 0, sizeof ( *segment ) );
David Turner3469d0d2000-07-19 20:02:14 +0000818
819 segment->dir = segment_dir;
820 segment->flags = ah_edge_normal;
821 min_pos = max_pos = point->u;
822 segment->first = point;
823 segment->last = point;
824 segment->contour = contour;
825 on_edge = 1;
826
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000827#ifdef AH_HINT_METRICS
Werner Lembergdb578ae2000-07-23 21:27:52 +0000828 if ( point == max_point )
David Turner3469d0d2000-07-19 20:02:14 +0000829 max_point = 0;
830
Werner Lembergdb578ae2000-07-23 21:27:52 +0000831 if ( point == min_point )
David Turner3469d0d2000-07-19 20:02:14 +0000832 min_point = 0;
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000833#endif
David Turner3469d0d2000-07-19 20:02:14 +0000834 }
835
836 point = point->next;
837 }
838
839 } /* contours */
840
David Turner3469d0d2000-07-19 20:02:14 +0000841#ifdef AH_HINT_METRICS
842 /* we need to ensure that there are edges on the left-most and */
Werner Lembergdb578ae2000-07-23 21:27:52 +0000843 /* right-most points of the glyph in order to hint the metrics; */
844 /* we do this by inserting fake segments when needed */
845 if ( dimension == 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000846 {
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000847 AH_Point* point = outline->points;
848 AH_Point* point_limit = point + outline->num_points;
David Turner3469d0d2000-07-19 20:02:14 +0000849
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000850 FT_Pos min_pos = 32000;
851 FT_Pos max_pos = -32000;
David Turner3469d0d2000-07-19 20:02:14 +0000852
Werner Lembergdb578ae2000-07-23 21:27:52 +0000853
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000854 min_point = 0;
855 max_point = 0;
856
David Turner3469d0d2000-07-19 20:02:14 +0000857 /* compute minimum and maximum points */
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000858 for ( ; point < point_limit; point++ )
David Turner3469d0d2000-07-19 20:02:14 +0000859 {
860 FT_Pos x = point->fx;
861
Werner Lembergdb578ae2000-07-23 21:27:52 +0000862
David Turner3469d0d2000-07-19 20:02:14 +0000863 if ( x < min_pos )
864 {
865 min_pos = x;
866 min_point = point;
867 }
868 if ( x > max_pos )
869 {
870 max_pos = x;
871 max_point = point;
872 }
873 }
874
875 /* insert minimum segment */
Werner Lembergdb578ae2000-07-23 21:27:52 +0000876 if ( min_point )
David Turner3469d0d2000-07-19 20:02:14 +0000877 {
878 /* clear all segment fields */
David Turnere459d742002-03-22 13:52:37 +0000879 FT_MEM_SET( segment, 0, sizeof ( *segment ) );
David Turner3469d0d2000-07-19 20:02:14 +0000880
881 segment->dir = segment_dir;
882 segment->flags = ah_edge_normal;
883 segment->first = min_point;
884 segment->last = min_point;
885 segment->pos = min_pos;
886
887 num_segments++;
888 segment++;
889 }
890
891 /* insert maximum segment */
Werner Lembergdb578ae2000-07-23 21:27:52 +0000892 if ( max_point )
David Turner3469d0d2000-07-19 20:02:14 +0000893 {
894 /* clear all segment fields */
David Turnere459d742002-03-22 13:52:37 +0000895 FT_MEM_SET( segment, 0, sizeof ( *segment ) );
David Turner3469d0d2000-07-19 20:02:14 +0000896
897 segment->dir = segment_dir;
898 segment->flags = ah_edge_normal;
899 segment->first = max_point;
900 segment->last = max_point;
901 segment->pos = max_pos;
902
903 num_segments++;
904 segment++;
905 }
906 }
Werner Lembergdb578ae2000-07-23 21:27:52 +0000907#endif /* AH_HINT_METRICS */
David Turner3469d0d2000-07-19 20:02:14 +0000908
909 *p_num_segments = num_segments;
910
911 segments = outline->vert_segments;
912 major_dir = ah_dir_up;
913 p_num_segments = &outline->num_vsegments;
914 ah_setup_uv( outline, ah_uv_fxy );
915 }
916 }
917
918
David Turnerbc82f1b2002-03-01 02:26:22 +0000919 FT_LOCAL_DEF( void )
Werner Lembergf814d0f2001-06-27 16:18:10 +0000920 ah_outline_link_segments( AH_Outline* outline )
David Turner3469d0d2000-07-19 20:02:14 +0000921 {
922 AH_Segment* segments;
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000923 AH_Segment* segment_limit;
David Turner3469d0d2000-07-19 20:02:14 +0000924 int dimension;
925
Werner Lembergdb578ae2000-07-23 21:27:52 +0000926
David Turner3469d0d2000-07-19 20:02:14 +0000927 ah_setup_uv( outline, ah_uv_fyx );
928
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000929 segments = outline->horz_segments;
930 segment_limit = segments + outline->num_hsegments;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000931
David Turner3469d0d2000-07-19 20:02:14 +0000932 for ( dimension = 1; dimension >= 0; dimension-- )
933 {
934 AH_Segment* seg1;
935 AH_Segment* seg2;
936
937 /* now compare each segment to the others */
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000938 for ( seg1 = segments; seg1 < segment_limit; seg1++ )
David Turner3469d0d2000-07-19 20:02:14 +0000939 {
David Turner9d7e5e82001-10-29 17:22:12 +0000940 FT_Pos best_score;
941 AH_Segment* best_segment;
Werner Lembergdb578ae2000-07-23 21:27:52 +0000942
943 /* the fake segments are introduced to hint the metrics -- */
944 /* we must never link them to anything */
945 if ( seg1->first == seg1->last )
David Turner3469d0d2000-07-19 20:02:14 +0000946 continue;
947
David Turner9d7e5e82001-10-29 17:22:12 +0000948 best_segment = seg1->link;
949 if ( best_segment )
950 best_score = seg1->score;
951 else
952 best_score = 32000;
Werner Lembergc3b21602001-12-05 01:22:05 +0000953
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000954 for ( seg2 = segments; seg2 < segment_limit; seg2++ )
David Turner3469d0d2000-07-19 20:02:14 +0000955 if ( seg1 != seg2 && seg1->dir + seg2->dir == 0 )
956 {
Werner Lembergdb578ae2000-07-23 21:27:52 +0000957 FT_Pos pos1 = seg1->pos;
958 FT_Pos pos2 = seg2->pos;
959 FT_Bool is_dir;
960 FT_Bool is_pos;
David Turner3469d0d2000-07-19 20:02:14 +0000961
Werner Lembergdb578ae2000-07-23 21:27:52 +0000962
963 /* check that the segments are correctly oriented and */
964 /* positioned to form a black distance */
David Turner3469d0d2000-07-19 20:02:14 +0000965
David Turner8edbcab2001-06-19 08:28:24 +0000966 is_dir = (FT_Bool)( seg1->dir == outline->horz_major_dir ||
967 seg1->dir == outline->vert_major_dir );
Werner Lemberg8eb03532001-06-19 23:03:41 +0000968 is_pos = (FT_Bool)( pos1 > pos2 );
David Turner3469d0d2000-07-19 20:02:14 +0000969
970 if ( pos1 == pos2 || !(is_dir ^ is_pos) )
971 continue;
972
David Turner3469d0d2000-07-19 20:02:14 +0000973 {
974 FT_Pos min = seg1->min_coord;
975 FT_Pos max = seg1->max_coord;
David Turner9d7e5e82001-10-29 17:22:12 +0000976 FT_Pos len, dist, score;
David Turner3469d0d2000-07-19 20:02:14 +0000977
Werner Lembergdb578ae2000-07-23 21:27:52 +0000978
David Turner3469d0d2000-07-19 20:02:14 +0000979 if ( min < seg2->min_coord )
980 min = seg2->min_coord;
981
David Turner9d7e5e82001-10-29 17:22:12 +0000982 if ( max > seg2->max_coord )
David Turner3469d0d2000-07-19 20:02:14 +0000983 max = seg2->max_coord;
984
985 len = max - min;
David Turner9d7e5e82001-10-29 17:22:12 +0000986 dist = seg2->pos - seg1->pos;
987 if ( dist < 0 )
988 dist = -dist;
David Turner3469d0d2000-07-19 20:02:14 +0000989
David Turner9d7e5e82001-10-29 17:22:12 +0000990 if ( len < 8 )
David Turneradf07a92001-10-30 23:51:24 +0000991 score = 300*8 + dist - len*3;
David Turner9d7e5e82001-10-29 17:22:12 +0000992 else
993 score = dist + 300/len;
994
995 if ( score < best_score )
996 {
997 best_score = score;
998 best_segment = seg2;
999 }
David Turner3469d0d2000-07-19 20:02:14 +00001000 }
1001 }
1002
Werner Lembergdb578ae2000-07-23 21:27:52 +00001003 if ( best_segment )
David Turner3469d0d2000-07-19 20:02:14 +00001004 {
1005 seg1->link = best_segment;
1006 seg1->score = best_score;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001007
David Turner3469d0d2000-07-19 20:02:14 +00001008 best_segment->num_linked++;
1009 }
1010
1011
1012 } /* edges 1 */
1013
Werner Lembergdb578ae2000-07-23 21:27:52 +00001014 /* now, compute the `serif' segments */
Werner Lemberge72c9fe2000-07-31 18:59:02 +00001015 for ( seg1 = segments; seg1 < segment_limit; seg1++ )
David Turner3469d0d2000-07-19 20:02:14 +00001016 {
1017 seg2 = seg1->link;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001018
1019 if ( seg2 && seg2->link != seg1 )
David Turner3469d0d2000-07-19 20:02:14 +00001020 {
1021 seg1->link = 0;
1022 seg1->serif = seg2->link;
1023 }
1024 }
1025
1026 ah_setup_uv( outline, ah_uv_fxy );
1027
Werner Lemberge72c9fe2000-07-31 18:59:02 +00001028 segments = outline->vert_segments;
1029 segment_limit = segments + outline->num_vsegments;
David Turner3469d0d2000-07-19 20:02:14 +00001030 }
1031 }
1032
1033
Werner Lembergf814d0f2001-06-27 16:18:10 +00001034 static void
1035 ah_outline_compute_edges( AH_Outline* outline )
David Turner3469d0d2000-07-19 20:02:14 +00001036 {
Werner Lembergdb578ae2000-07-23 21:27:52 +00001037 AH_Edge* edges;
1038 AH_Segment* segments;
1039 AH_Segment* segment_limit;
1040 AH_Direction up_dir;
1041 FT_Int* p_num_edges;
1042 FT_Int dimension;
1043 FT_Fixed scale;
1044 FT_Pos edge_distance_threshold;
1045
David Turner3469d0d2000-07-19 20:02:14 +00001046
1047 edges = outline->horz_edges;
1048 segments = outline->horz_segments;
1049 segment_limit = segments + outline->num_hsegments;
1050 p_num_edges = &outline->num_hedges;
1051 up_dir = ah_dir_right;
1052 scale = outline->y_scale;
1053
1054 for ( dimension = 1; dimension >= 0; dimension-- )
1055 {
1056 AH_Edge* edge;
1057 AH_Edge* edge_limit; /* really == edge + num_edges */
1058 AH_Segment* seg;
1059
Werner Lembergdb578ae2000-07-23 21:27:52 +00001060
1061 /*********************************************************************/
1062 /* */
1063 /* We will begin by generating a sorted table of edges for the */
1064 /* current direction. To do so, we simply scan each segment and try */
1065 /* to find an edge in our table that corresponds to its position. */
1066 /* */
1067 /* If no edge is found, we create and insert a new edge in the */
1068 /* sorted table. Otherwise, we simply add the segment to the edge's */
1069 /* list which will be processed in the second step to compute the */
1070 /* edge's properties. */
1071 /* */
1072 /* Note that the edges table is sorted along the segment/edge */
1073 /* position. */
1074 /* */
1075 /*********************************************************************/
David Turner3469d0d2000-07-19 20:02:14 +00001076
1077 edge_distance_threshold = FT_MulFix( outline->edge_distance_threshold,
1078 scale );
Werner Lembergdb578ae2000-07-23 21:27:52 +00001079 if ( edge_distance_threshold > 64 / 4 )
1080 edge_distance_threshold = 64 / 4;
David Turner3469d0d2000-07-19 20:02:14 +00001081
1082 edge_limit = edges;
1083 for ( seg = segments; seg < segment_limit; seg++ )
1084 {
1085 AH_Edge* found = 0;
1086
Werner Lembergdb578ae2000-07-23 21:27:52 +00001087
David Turner3469d0d2000-07-19 20:02:14 +00001088 /* look for an edge corresponding to the segment */
1089 for ( edge = edges; edge < edge_limit; edge++ )
1090 {
1091 FT_Pos dist;
1092
Werner Lembergdb578ae2000-07-23 21:27:52 +00001093
David Turner3469d0d2000-07-19 20:02:14 +00001094 dist = seg->pos - edge->fpos;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001095 if ( dist < 0 )
1096 dist = -dist;
David Turner3469d0d2000-07-19 20:02:14 +00001097
1098 dist = FT_MulFix( dist, scale );
1099 if ( dist < edge_distance_threshold )
1100 {
1101 found = edge;
1102 break;
1103 }
1104 }
1105
Werner Lembergdb578ae2000-07-23 21:27:52 +00001106 if ( !found )
David Turner3469d0d2000-07-19 20:02:14 +00001107 {
Werner Lembergdb578ae2000-07-23 21:27:52 +00001108 /* insert a new edge in the list and */
1109 /* sort according to the position */
David Turner3469d0d2000-07-19 20:02:14 +00001110 while ( edge > edges && edge[-1].fpos > seg->pos )
1111 {
1112 edge[0] = edge[-1];
1113 edge--;
1114 }
1115 edge_limit++;
1116
1117 /* clear all edge fields */
David Turnere459d742002-03-22 13:52:37 +00001118 FT_MEM_SET( edge, 0, sizeof ( *edge ) );
David Turner3469d0d2000-07-19 20:02:14 +00001119
1120 /* add the segment to the new edge's list */
1121 edge->first = seg;
1122 edge->last = seg;
1123 edge->fpos = seg->pos;
1124 edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
1125 seg->edge_next = seg;
1126 }
1127 else
1128 {
1129 /* if an edge was found, simply add the segment to the edge's */
1130 /* list */
1131 seg->edge_next = edge->first;
1132 edge->last->edge_next = seg;
1133 edge->last = seg;
1134 }
1135 }
1136
Werner Lemberg914b2892001-03-10 17:07:42 +00001137 *p_num_edges = (FT_Int)( edge_limit - edges );
David Turner3469d0d2000-07-19 20:02:14 +00001138
1139
Werner Lembergdb578ae2000-07-23 21:27:52 +00001140 /*********************************************************************/
1141 /* */
1142 /* Good, we will now compute each edge's properties according to */
1143 /* segments found on its position. Basically, these are: */
1144 /* */
1145 /* - edge's main direction */
1146 /* - stem edge, serif edge or both (which defaults to stem then) */
1147 /* - rounded edge, straigth or both (which defaults to straight) */
1148 /* - link for edge */
1149 /* */
1150 /*********************************************************************/
David Turner3469d0d2000-07-19 20:02:14 +00001151
Werner Lembergdb578ae2000-07-23 21:27:52 +00001152 /* first of all, set the `edge' field in each segment -- this is */
1153 /* required in order to compute edge links */
David Turner3469d0d2000-07-19 20:02:14 +00001154 for ( edge = edges; edge < edge_limit; edge++ )
1155 {
1156 seg = edge->first;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001157 if ( seg )
David Turner3469d0d2000-07-19 20:02:14 +00001158 do
1159 {
1160 seg->edge = edge;
1161 seg = seg->edge_next;
1162 }
1163 while ( seg != edge->first );
1164 }
1165
1166 /* now, compute each edge properties */
1167 for ( edge = edges; edge < edge_limit; edge++ )
1168 {
Werner Lembergdb578ae2000-07-23 21:27:52 +00001169 int is_round = 0; /* does it contain round segments? */
1170 int is_straight = 0; /* does it contain straight segments? */
1171 int ups = 0; /* number of upwards segments */
1172 int downs = 0; /* number of downwards segments */
1173
David Turner3469d0d2000-07-19 20:02:14 +00001174
1175 seg = edge->first;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001176
David Turner3469d0d2000-07-19 20:02:14 +00001177 do
1178 {
Werner Lembergdb578ae2000-07-23 21:27:52 +00001179 FT_Bool is_serif;
1180
David Turner3469d0d2000-07-19 20:02:14 +00001181
1182 /* check for roundness of segment */
Werner Lembergdb578ae2000-07-23 21:27:52 +00001183 if ( seg->flags & ah_edge_round )
1184 is_round++;
1185 else
1186 is_straight++;
David Turner3469d0d2000-07-19 20:02:14 +00001187
1188 /* check for segment direction */
Werner Lembergdb578ae2000-07-23 21:27:52 +00001189 if ( seg->dir == up_dir )
1190 ups += seg->max_coord-seg->min_coord;
1191 else
1192 downs += seg->max_coord-seg->min_coord;
David Turner3469d0d2000-07-19 20:02:14 +00001193
Werner Lembergdb578ae2000-07-23 21:27:52 +00001194 /* check for links -- if seg->serif is set, then seg->link must */
1195 /* be ignored */
Werner Lemberg8eb03532001-06-19 23:03:41 +00001196 is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
David Turner3469d0d2000-07-19 20:02:14 +00001197
1198 if ( seg->link || is_serif )
1199 {
1200 AH_Edge* edge2;
1201 AH_Segment* seg2;
1202
Werner Lembergdb578ae2000-07-23 21:27:52 +00001203
David Turner3469d0d2000-07-19 20:02:14 +00001204 edge2 = edge->link;
1205 seg2 = seg->link;
1206
Werner Lembergdb578ae2000-07-23 21:27:52 +00001207 if ( is_serif )
David Turner3469d0d2000-07-19 20:02:14 +00001208 {
1209 seg2 = seg->serif;
1210 edge2 = edge->serif;
1211 }
1212
Werner Lembergdb578ae2000-07-23 21:27:52 +00001213 if ( edge2 )
David Turner3469d0d2000-07-19 20:02:14 +00001214 {
1215 FT_Pos edge_delta;
1216 FT_Pos seg_delta;
1217
Werner Lembergdb578ae2000-07-23 21:27:52 +00001218
David Turner3469d0d2000-07-19 20:02:14 +00001219 edge_delta = edge->fpos - edge2->fpos;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001220 if ( edge_delta < 0 )
1221 edge_delta = -edge_delta;
David Turner3469d0d2000-07-19 20:02:14 +00001222
1223 seg_delta = seg->pos - seg2->pos;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001224 if ( seg_delta < 0 )
1225 seg_delta = -seg_delta;
David Turner3469d0d2000-07-19 20:02:14 +00001226
Werner Lembergdb578ae2000-07-23 21:27:52 +00001227 if ( seg_delta < edge_delta )
David Turner3469d0d2000-07-19 20:02:14 +00001228 edge2 = seg2->edge;
1229 }
1230 else
1231 edge2 = seg2->edge;
1232
Werner Lembergdb578ae2000-07-23 21:27:52 +00001233 if ( is_serif )
David Turner3469d0d2000-07-19 20:02:14 +00001234 edge->serif = edge2;
1235 else
1236 edge->link = edge2;
1237 }
1238
1239 seg = seg->edge_next;
1240
1241 } while ( seg != edge->first );
1242
1243 /* set the round/straight flags */
1244 edge->flags = ah_edge_normal;
1245
David Turner66cb4792001-05-14 14:04:23 +00001246 if ( is_round > 0 && is_round >= is_straight )
David Turner3469d0d2000-07-19 20:02:14 +00001247 edge->flags |= ah_edge_round;
1248
1249 /* set the edge's main direction */
1250 edge->dir = ah_dir_none;
1251
1252 if ( ups > downs )
1253 edge->dir = up_dir;
1254
1255 else if ( ups < downs )
1256 edge->dir = - up_dir;
1257
1258 else if ( ups == downs )
1259 edge->dir = 0; /* both up and down !! */
1260
Werner Lembergdb578ae2000-07-23 21:27:52 +00001261 /* gets rid of serifs if link is set */
1262 /* XXX: This gets rid of many unpleasant artefacts! */
1263 /* Example: the `c' in cour.pfa at size 13 */
David Turner3469d0d2000-07-19 20:02:14 +00001264
Werner Lembergdb578ae2000-07-23 21:27:52 +00001265 if ( edge->serif && edge->link )
David Turner3469d0d2000-07-19 20:02:14 +00001266 edge->serif = 0;
1267 }
1268
1269 edges = outline->vert_edges;
1270 segments = outline->vert_segments;
1271 segment_limit = segments + outline->num_vsegments;
1272 p_num_edges = &outline->num_vedges;
1273 up_dir = ah_dir_up;
1274 scale = outline->x_scale;
1275 }
1276 }
1277
1278
Werner Lembergdb578ae2000-07-23 21:27:52 +00001279 /*************************************************************************/
1280 /* */
1281 /* <Function> */
1282 /* ah_outline_detect_features */
1283 /* */
1284 /* <Description> */
1285 /* Performs feature detection on a given AH_Outline object. */
1286 /* */
David Turnerbc82f1b2002-03-01 02:26:22 +00001287 FT_LOCAL_DEF( void )
Werner Lembergf814d0f2001-06-27 16:18:10 +00001288 ah_outline_detect_features( AH_Outline* outline )
David Turner3469d0d2000-07-19 20:02:14 +00001289 {
1290 ah_outline_compute_segments( outline );
1291 ah_outline_link_segments ( outline );
1292 ah_outline_compute_edges ( outline );
1293 }
1294
1295
Werner Lembergdb578ae2000-07-23 21:27:52 +00001296 /*************************************************************************/
1297 /* */
1298 /* <Function> */
1299 /* ah_outline_compute_blue_edges */
1300 /* */
1301 /* <Description> */
1302 /* Computes the `blue edges' in a given outline (i.e. those that must */
1303 /* be snapped to a blue zone edge (top or bottom). */
1304 /* */
David Turnerbc82f1b2002-03-01 02:26:22 +00001305 FT_LOCAL_DEF( void )
Werner Lembergf814d0f2001-06-27 16:18:10 +00001306 ah_outline_compute_blue_edges( AH_Outline* outline,
1307 AH_Face_Globals* face_globals )
David Turner3469d0d2000-07-19 20:02:14 +00001308 {
Werner Lemberge72c9fe2000-07-31 18:59:02 +00001309 AH_Edge* edge = outline->horz_edges;
1310 AH_Edge* edge_limit = edge + outline->num_hedges;
1311 AH_Globals* globals = &face_globals->design;
1312 FT_Fixed y_scale = outline->y_scale;
David Turner3469d0d2000-07-19 20:02:14 +00001313
Werner Lemberg1f7f0e82001-06-06 17:30:41 +00001314 FT_Bool blue_active[ah_blue_max];
David Turner66cb4792001-05-14 14:04:23 +00001315
1316
1317 /* compute which blue zones are active, i.e. have their scaled */
1318 /* size < 3/4 pixels */
1319 {
1320 AH_Blue blue;
1321 FT_Bool check = 0;
1322
Werner Lemberg1f7f0e82001-06-06 17:30:41 +00001323
David Turner66cb4792001-05-14 14:04:23 +00001324 for ( blue = ah_blue_capital_top; blue < ah_blue_max; blue++ )
1325 {
1326 FT_Pos ref, shoot, dist;
1327
Werner Lemberg1f7f0e82001-06-06 17:30:41 +00001328
David Turner66cb4792001-05-14 14:04:23 +00001329 ref = globals->blue_refs[blue];
1330 shoot = globals->blue_shoots[blue];
1331 dist = ref-shoot;
Werner Lemberg1f7f0e82001-06-06 17:30:41 +00001332 if ( dist < 0 )
David Turner66cb4792001-05-14 14:04:23 +00001333 dist = -dist;
1334
1335 blue_active[blue] = 0;
1336
1337 if ( FT_MulFix( dist, y_scale ) < 48 )
1338 {
1339 blue_active[blue] = 1;
1340 check = 1;
1341 }
1342 }
1343
1344 /* return immediately if no blue zone is active */
Werner Lemberg1f7f0e82001-06-06 17:30:41 +00001345 if ( !check )
David Turner66cb4792001-05-14 14:04:23 +00001346 return;
1347 }
Werner Lembergdb578ae2000-07-23 21:27:52 +00001348
David Turner3469d0d2000-07-19 20:02:14 +00001349 /* compute for each horizontal edge, which blue zone is closer */
Werner Lemberge72c9fe2000-07-31 18:59:02 +00001350 for ( ; edge < edge_limit; edge++ )
David Turner3469d0d2000-07-19 20:02:14 +00001351 {
Werner Lembergdb578ae2000-07-23 21:27:52 +00001352 AH_Blue blue;
1353 FT_Pos* best_blue = 0;
1354 FT_Pos best_dist; /* initial threshold */
1355
David Turner3469d0d2000-07-19 20:02:14 +00001356
1357 /* compute the initial threshold as a fraction of the EM size */
1358 best_dist = FT_MulFix( face_globals->face->units_per_EM / 40, y_scale );
Werner Lembergdb578ae2000-07-23 21:27:52 +00001359 if ( best_dist > 64 / 4 )
1360 best_dist = 64 / 4;
David Turner3469d0d2000-07-19 20:02:14 +00001361
Werner Lembergc3dd1512000-07-26 14:11:15 +00001362 for ( blue = ah_blue_capital_top; blue < ah_blue_max; blue++ )
David Turner3469d0d2000-07-19 20:02:14 +00001363 {
Werner Lembergdb578ae2000-07-23 21:27:52 +00001364 /* if it is a top zone, check for right edges -- if it is a bottom */
1365 /* zone, check for left edges */
1366 /* */
1367 /* of course, that's for TrueType XXX */
Werner Lemberg8eb03532001-06-19 23:03:41 +00001368 FT_Bool is_top_blue =
1369 FT_BOOL( AH_IS_TOP_BLUE( blue ) );
1370 FT_Bool is_major_dir =
1371 FT_BOOL( edge->dir == outline->horz_major_dir );
David Turner3469d0d2000-07-19 20:02:14 +00001372
David Turner66cb4792001-05-14 14:04:23 +00001373 if ( !blue_active[blue] )
1374 continue;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001375
1376 /* if it is a top zone, the edge must be against the major */
1377 /* direction; if it is a bottom zone, it must be in the major */
1378 /* direction */
David Turner3469d0d2000-07-19 20:02:14 +00001379 if ( is_top_blue ^ is_major_dir )
1380 {
Werner Lembergdb578ae2000-07-23 21:27:52 +00001381 FT_Pos dist;
1382 FT_Pos* blue_pos = globals->blue_refs + blue;
1383
David Turner3469d0d2000-07-19 20:02:14 +00001384
1385 /* first of all, compare it to the reference position */
1386 dist = edge->fpos - *blue_pos;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001387 if ( dist < 0 )
1388 dist = -dist;
David Turner3469d0d2000-07-19 20:02:14 +00001389
1390 dist = FT_MulFix( dist, y_scale );
Werner Lembergdb578ae2000-07-23 21:27:52 +00001391 if ( dist < best_dist )
David Turner3469d0d2000-07-19 20:02:14 +00001392 {
1393 best_dist = dist;
1394 best_blue = blue_pos;
1395 }
1396
Werner Lembergdb578ae2000-07-23 21:27:52 +00001397 /* now, compare it to the overshoot position if the edge is */
1398 /* rounded, and if the edge is over the reference position of a */
1399 /* top zone, or under the reference position of a bottom zone */
David Turner3469d0d2000-07-19 20:02:14 +00001400 if ( edge->flags & ah_edge_round && dist != 0 )
1401 {
David Turner8edbcab2001-06-19 08:28:24 +00001402 FT_Bool is_under_ref = FT_BOOL( edge->fpos < *blue_pos );
David Turner3469d0d2000-07-19 20:02:14 +00001403
Werner Lembergdb578ae2000-07-23 21:27:52 +00001404
David Turner3469d0d2000-07-19 20:02:14 +00001405 if ( is_top_blue ^ is_under_ref )
1406 {
1407 blue_pos = globals->blue_shoots + blue;
1408 dist = edge->fpos - *blue_pos;
Werner Lembergdb578ae2000-07-23 21:27:52 +00001409 if ( dist < 0 )
1410 dist = -dist;
David Turner3469d0d2000-07-19 20:02:14 +00001411
1412 dist = FT_MulFix( dist, y_scale );
Werner Lembergdb578ae2000-07-23 21:27:52 +00001413 if ( dist < best_dist )
David Turner3469d0d2000-07-19 20:02:14 +00001414 {
1415 best_dist = dist;
1416 best_blue = blue_pos;
1417 }
1418 }
1419 }
1420 }
1421 }
1422
Werner Lembergdb578ae2000-07-23 21:27:52 +00001423 if ( best_blue )
David Turner3469d0d2000-07-19 20:02:14 +00001424 edge->blue_edge = best_blue;
1425 }
1426 }
1427
1428
Werner Lembergc3dd1512000-07-26 14:11:15 +00001429 /*************************************************************************/
1430 /* */
1431 /* <Function> */
1432 /* ah_outline_scale_blue_edges */
1433 /* */
1434 /* <Description> */
1435 /* This functions must be called before hinting in order to re-adjust */
1436 /* the contents of the detected edges (basically change the `blue */
1437 /* edge' pointer from `design units' to `scaled ones'). */
1438 /* */
David Turnerbc82f1b2002-03-01 02:26:22 +00001439 FT_LOCAL_DEF( void )
Werner Lembergf814d0f2001-06-27 16:18:10 +00001440 ah_outline_scale_blue_edges( AH_Outline* outline,
1441 AH_Face_Globals* globals )
David Turner3469d0d2000-07-19 20:02:14 +00001442 {
Werner Lemberge72c9fe2000-07-31 18:59:02 +00001443 AH_Edge* edge = outline->horz_edges;
1444 AH_Edge* edge_limit = edge + outline->num_hedges;
Werner Lembergc3dd1512000-07-26 14:11:15 +00001445 FT_Int delta;
1446
David Turner3469d0d2000-07-19 20:02:14 +00001447
1448 delta = globals->scaled.blue_refs - globals->design.blue_refs;
Werner Lembergc3dd1512000-07-26 14:11:15 +00001449
Werner Lemberge72c9fe2000-07-31 18:59:02 +00001450 for ( ; edge < edge_limit; edge++ )
David Turner3469d0d2000-07-19 20:02:14 +00001451 {
Werner Lembergc3dd1512000-07-26 14:11:15 +00001452 if ( edge->blue_edge )
David Turner3469d0d2000-07-19 20:02:14 +00001453 edge->blue_edge += delta;
1454 }
1455 }
1456
1457
Werner Lembergc3dd1512000-07-26 14:11:15 +00001458/* END */