ahglyph.c 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579
  1. /***************************************************************************/
  2. /* */
  3. /* ahglyph.c */
  4. /* */
  5. /* Routines used to load and analyze a given glyph before hinting */
  6. /* (body). */
  7. /* */
  8. /* Copyright 2000-2001, 2002 Catharon Productions Inc. */
  9. /* 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 */
  14. /* `CatharonLicense.txt'. By continuing to use, modify, or distribute */
  15. /* this file you indicate that you have read the license and */
  16. /* understand and accept it fully. */
  17. /* */
  18. /* Note that this license is compatible with the FreeType license. */
  19. /* */
  20. /***************************************************************************/
  21. #include <ft2build.h>
  22. #include "ahglyph.h"
  23. #include "ahangles.h"
  24. #include "ahglobal.h"
  25. #include "aherrors.h"
  26. #ifdef AH_DEBUG
  27. #include <stdio.h>
  28. void
  29. ah_dump_edges( AH_Outline outline )
  30. {
  31. AH_Edge edges;
  32. AH_Edge edge_limit;
  33. AH_Segment segments;
  34. FT_Int dimension;
  35. edges = outline->horz_edges;
  36. edge_limit = edges + outline->num_hedges;
  37. segments = outline->horz_segments;
  38. for ( dimension = 1; dimension >= 0; dimension-- )
  39. {
  40. AH_Edge edge;
  41. printf ( "Table of %s edges:\n",
  42. !dimension ? "vertical" : "horizontal" );
  43. printf ( " [ index | pos | dir | link |"
  44. " serif | blue | opos | pos ]\n" );
  45. for ( edge = edges; edge < edge_limit; edge++ )
  46. {
  47. printf ( " [ %5d | %4d | %5s | %4d | %5d | %c | %5.2f | %5.2f ]\n",
  48. edge - edges,
  49. (int)edge->fpos,
  50. edge->dir == AH_DIR_UP
  51. ? "up"
  52. : ( edge->dir == AH_DIR_DOWN
  53. ? "down"
  54. : ( edge->dir == AH_DIR_LEFT
  55. ? "left"
  56. : ( edge->dir == AH_DIR_RIGHT
  57. ? "right"
  58. : "none" ) ) ),
  59. edge->link ? ( edge->link - edges ) : -1,
  60. edge->serif ? ( edge->serif - edges ) : -1,
  61. edge->blue_edge ? 'y' : 'n',
  62. edge->opos / 64.0,
  63. edge->pos / 64.0 );
  64. }
  65. edges = outline->vert_edges;
  66. edge_limit = edges + outline->num_vedges;
  67. segments = outline->vert_segments;
  68. }
  69. }
  70. /* A function used to dump the array of linked segments */
  71. void
  72. ah_dump_segments( AH_Outline outline )
  73. {
  74. AH_Segment segments;
  75. AH_Segment segment_limit;
  76. AH_Point points;
  77. FT_Int dimension;
  78. points = outline->points;
  79. segments = outline->horz_segments;
  80. segment_limit = segments + outline->num_hsegments;
  81. for ( dimension = 1; dimension >= 0; dimension-- )
  82. {
  83. AH_Segment seg;
  84. printf ( "Table of %s segments:\n",
  85. !dimension ? "vertical" : "horizontal" );
  86. printf ( " [ index | pos | dir | link | serif |"
  87. " numl | first | start ]\n" );
  88. for ( seg = segments; seg < segment_limit; seg++ )
  89. {
  90. printf ( " [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n",
  91. seg - segments,
  92. (int)seg->pos,
  93. seg->dir == AH_DIR_UP
  94. ? "up"
  95. : ( seg->dir == AH_DIR_DOWN
  96. ? "down"
  97. : ( seg->dir == AH_DIR_LEFT
  98. ? "left"
  99. : ( seg->dir == AH_DIR_RIGHT
  100. ? "right"
  101. : "none" ) ) ),
  102. seg->link ? (seg->link-segments) : -1,
  103. seg->serif ? (seg->serif-segments) : -1,
  104. (int)seg->num_linked,
  105. seg->first - points,
  106. seg->last - points );
  107. }
  108. segments = outline->vert_segments;
  109. segment_limit = segments + outline->num_vsegments;
  110. }
  111. }
  112. #endif /* AH_DEBUG */
  113. /* compute the direction value of a given vector.. */
  114. static AH_Direction
  115. ah_compute_direction( FT_Pos dx,
  116. FT_Pos dy )
  117. {
  118. AH_Direction dir;
  119. FT_Pos ax = ABS( dx );
  120. FT_Pos ay = ABS( dy );
  121. dir = AH_DIR_NONE;
  122. /* test for vertical direction */
  123. if ( ax * 12 < ay )
  124. {
  125. dir = dy > 0 ? AH_DIR_UP : AH_DIR_DOWN;
  126. }
  127. /* test for horizontal direction */
  128. else if ( ay * 12 < ax )
  129. {
  130. dir = dx > 0 ? AH_DIR_RIGHT : AH_DIR_LEFT;
  131. }
  132. return dir;
  133. }
  134. /* this function is used by ah_get_orientation (see below) to test */
  135. /* the fill direction of a given bbox extrema */
  136. static FT_Int
  137. ah_test_extrema( FT_Outline* outline,
  138. FT_Int n )
  139. {
  140. FT_Vector *prev, *cur, *next;
  141. FT_Pos product;
  142. FT_Int first, last, c;
  143. FT_Int retval;
  144. /* we need to compute the `previous' and `next' point */
  145. /* for these extrema */
  146. cur = outline->points + n;
  147. prev = cur - 1;
  148. next = cur + 1;
  149. first = 0;
  150. for ( c = 0; c < outline->n_contours; c++ )
  151. {
  152. last = outline->contours[c];
  153. if ( n == first )
  154. prev = outline->points + last;
  155. if ( n == last )
  156. next = outline->points + first;
  157. first = last + 1;
  158. }
  159. product = FT_MulDiv( cur->x - prev->x, /* in.x */
  160. next->y - cur->y, /* out.y */
  161. 0x40 )
  162. -
  163. FT_MulDiv( cur->y - prev->y, /* in.y */
  164. next->x - cur->x, /* out.x */
  165. 0x40 );
  166. retval = 0;
  167. if ( product )
  168. retval = product > 0 ? 2 : 1;
  169. return retval;
  170. }
  171. /* Compute the orientation of path filling. It differs between TrueType */
  172. /* and Type1 formats. We could use the `FT_OUTLINE_REVERSE_FILL' flag, */
  173. /* but it is better to re-compute it directly (it seems that this flag */
  174. /* isn't correctly set for some weird composite glyphs currently). */
  175. /* */
  176. /* We do this by computing bounding box points, and computing their */
  177. /* curvature. */
  178. /* */
  179. /* The function returns either 1 or -1. */
  180. /* */
  181. static FT_Int
  182. ah_get_orientation( FT_Outline* outline )
  183. {
  184. FT_BBox box;
  185. FT_Int indices_xMin, indices_yMin, indices_xMax, indices_yMax;
  186. FT_Int n, last;
  187. indices_xMin = -1;
  188. indices_yMin = -1;
  189. indices_xMax = -1;
  190. indices_yMax = -1;
  191. box.xMin = box.yMin = 32767L;
  192. box.xMax = box.yMax = -32768L;
  193. /* is it empty? */
  194. if ( outline->n_contours < 1 )
  195. return 1;
  196. last = outline->contours[outline->n_contours - 1];
  197. for ( n = 0; n <= last; n++ )
  198. {
  199. FT_Pos x, y;
  200. x = outline->points[n].x;
  201. if ( x < box.xMin )
  202. {
  203. box.xMin = x;
  204. indices_xMin = n;
  205. }
  206. if ( x > box.xMax )
  207. {
  208. box.xMax = x;
  209. indices_xMax = n;
  210. }
  211. y = outline->points[n].y;
  212. if ( y < box.yMin )
  213. {
  214. box.yMin = y;
  215. indices_yMin = n;
  216. }
  217. if ( y > box.yMax )
  218. {
  219. box.yMax = y;
  220. indices_yMax = n;
  221. }
  222. }
  223. /* test orientation of the xmin */
  224. n = ah_test_extrema( outline, indices_xMin );
  225. if ( n )
  226. goto Exit;
  227. n = ah_test_extrema( outline, indices_yMin );
  228. if ( n )
  229. goto Exit;
  230. n = ah_test_extrema( outline, indices_xMax );
  231. if ( n )
  232. goto Exit;
  233. n = ah_test_extrema( outline, indices_yMax );
  234. if ( !n )
  235. n = 1;
  236. Exit:
  237. return n;
  238. }
  239. /*************************************************************************/
  240. /* */
  241. /* <Function> */
  242. /* ah_outline_new */
  243. /* */
  244. /* <Description> */
  245. /* Creates a new and empty AH_OutlineRec object. */
  246. /* */
  247. FT_LOCAL_DEF( FT_Error )
  248. ah_outline_new( FT_Memory memory,
  249. AH_Outline* aoutline )
  250. {
  251. FT_Error error;
  252. AH_Outline outline;
  253. if ( !FT_NEW( outline ) )
  254. {
  255. outline->memory = memory;
  256. *aoutline = outline;
  257. }
  258. return error;
  259. }
  260. /*************************************************************************/
  261. /* */
  262. /* <Function> */
  263. /* ah_outline_done */
  264. /* */
  265. /* <Description> */
  266. /* Destroys a given AH_OutlineRec object. */
  267. /* */
  268. FT_LOCAL_DEF( void )
  269. ah_outline_done( AH_Outline outline )
  270. {
  271. FT_Memory memory = outline->memory;
  272. FT_FREE( outline->horz_edges );
  273. FT_FREE( outline->horz_segments );
  274. FT_FREE( outline->contours );
  275. FT_FREE( outline->points );
  276. FT_FREE( outline );
  277. }
  278. /*************************************************************************/
  279. /* */
  280. /* <Function> */
  281. /* ah_outline_save */
  282. /* */
  283. /* <Description> */
  284. /* Saves the contents of a given AH_OutlineRec object into a face's */
  285. /* glyph slot. */
  286. /* */
  287. FT_LOCAL_DEF( void )
  288. ah_outline_save( AH_Outline outline,
  289. AH_Loader gloader )
  290. {
  291. AH_Point point = outline->points;
  292. AH_Point point_limit = point + outline->num_points;
  293. FT_Vector* vec = gloader->current.outline.points;
  294. char* tag = gloader->current.outline.tags;
  295. /* we assume that the glyph loader has already been checked for storage */
  296. for ( ; point < point_limit; point++, vec++, tag++ )
  297. {
  298. vec->x = point->x;
  299. vec->y = point->y;
  300. if ( point->flags & AH_FLAG_CONIC )
  301. tag[0] = FT_CURVE_TAG_CONIC;
  302. else if ( point->flags & AH_FLAG_CUBIC )
  303. tag[0] = FT_CURVE_TAG_CUBIC;
  304. else
  305. tag[0] = FT_CURVE_TAG_ON;
  306. }
  307. }
  308. /*************************************************************************/
  309. /* */
  310. /* <Function> */
  311. /* ah_outline_load */
  312. /* */
  313. /* <Description> */
  314. /* Loads an unscaled outline from a glyph slot into an AH_OutlineRec */
  315. /* object. */
  316. /* */
  317. FT_LOCAL_DEF( FT_Error )
  318. ah_outline_load( AH_Outline outline,
  319. FT_Face face )
  320. {
  321. FT_Memory memory = outline->memory;
  322. FT_Error error = AH_Err_Ok;
  323. FT_Outline* source = &face->glyph->outline;
  324. FT_Int num_points = source->n_points;
  325. FT_Int num_contours = source->n_contours;
  326. AH_Point points;
  327. /* check arguments */
  328. if ( !face ||
  329. !face->size ||
  330. face->glyph->format != FT_GLYPH_FORMAT_OUTLINE )
  331. return AH_Err_Invalid_Argument;
  332. /* first of all, reallocate the contours array if necessary */
  333. if ( num_contours > outline->max_contours )
  334. {
  335. FT_Int new_contours = ( num_contours + 3 ) & -4;
  336. if ( FT_RENEW_ARRAY( outline->contours,
  337. outline->max_contours,
  338. new_contours ) )
  339. goto Exit;
  340. outline->max_contours = new_contours;
  341. }
  342. /* then, reallocate the points, segments & edges arrays if needed -- */
  343. /* note that we reserved two additional point positions, used to */
  344. /* hint metrics appropriately */
  345. /* */
  346. if ( num_points + 2 > outline->max_points )
  347. {
  348. FT_Int news = ( num_points + 2 + 7 ) & -8;
  349. FT_Int max = outline->max_points;
  350. if ( FT_RENEW_ARRAY( outline->points, max, news ) ||
  351. FT_RENEW_ARRAY( outline->horz_edges, max * 2, news * 2 ) ||
  352. FT_RENEW_ARRAY( outline->horz_segments, max * 2, news * 2 ) )
  353. goto Exit;
  354. /* readjust some pointers */
  355. outline->vert_edges = outline->horz_edges + news;
  356. outline->vert_segments = outline->horz_segments + news;
  357. outline->max_points = news;
  358. }
  359. outline->num_points = num_points;
  360. outline->num_contours = num_contours;
  361. outline->num_hedges = 0;
  362. outline->num_vedges = 0;
  363. outline->num_hsegments = 0;
  364. outline->num_vsegments = 0;
  365. /* We can't rely on the value of `FT_Outline.flags' to know the fill */
  366. /* direction used for a glyph, given that some fonts are broken (e.g. */
  367. /* the Arphic ones). We thus recompute it each time we need to. */
  368. /* */
  369. outline->vert_major_dir = AH_DIR_UP;
  370. outline->horz_major_dir = AH_DIR_LEFT;
  371. if ( ah_get_orientation( source ) > 1 )
  372. {
  373. outline->vert_major_dir = AH_DIR_DOWN;
  374. outline->horz_major_dir = AH_DIR_RIGHT;
  375. }
  376. outline->x_scale = face->size->metrics.x_scale;
  377. outline->y_scale = face->size->metrics.y_scale;
  378. points = outline->points;
  379. if ( outline->num_points == 0 )
  380. goto Exit;
  381. {
  382. /* do one thing at a time -- it is easier to understand, and */
  383. /* the code is clearer */
  384. AH_Point point;
  385. AH_Point point_limit = points + outline->num_points;
  386. /* compute coordinates */
  387. {
  388. FT_Vector* vec = source->points;
  389. FT_Fixed x_scale = outline->x_scale;
  390. FT_Fixed y_scale = outline->y_scale;
  391. for ( point = points; point < point_limit; vec++, point++ )
  392. {
  393. point->fx = vec->x;
  394. point->fy = vec->y;
  395. point->ox = point->x = FT_MulFix( vec->x, x_scale );
  396. point->oy = point->y = FT_MulFix( vec->y, y_scale );
  397. point->flags = 0;
  398. }
  399. }
  400. /* compute Bezier flags */
  401. {
  402. char* tag = source->tags;
  403. for ( point = points; point < point_limit; point++, tag++ )
  404. {
  405. switch ( FT_CURVE_TAG( *tag ) )
  406. {
  407. case FT_CURVE_TAG_CONIC:
  408. point->flags = AH_FLAG_CONIC; break;
  409. case FT_CURVE_TAG_CUBIC:
  410. point->flags = AH_FLAG_CUBIC; break;
  411. default:
  412. ;
  413. }
  414. }
  415. }
  416. /* compute `next' and `prev' */
  417. {
  418. FT_Int contour_index;
  419. AH_Point prev;
  420. AH_Point first;
  421. AH_Point end;
  422. contour_index = 0;
  423. first = points;
  424. end = points + source->contours[0];
  425. prev = end;
  426. for ( point = points; point < point_limit; point++ )
  427. {
  428. point->prev = prev;
  429. if ( point < end )
  430. {
  431. point->next = point + 1;
  432. prev = point;
  433. }
  434. else
  435. {
  436. point->next = first;
  437. contour_index++;
  438. if ( point + 1 < point_limit )
  439. {
  440. end = points + source->contours[contour_index];
  441. first = point + 1;
  442. prev = end;
  443. }
  444. }
  445. }
  446. }
  447. /* set-up the contours array */
  448. {
  449. AH_Point* contour = outline->contours;
  450. AH_Point* contour_limit = contour + outline->num_contours;
  451. short* end = source->contours;
  452. short idx = 0;
  453. for ( ; contour < contour_limit; contour++, end++ )
  454. {
  455. contour[0] = points + idx;
  456. idx = (short)( end[0] + 1 );
  457. }
  458. }
  459. /* compute directions of in & out vectors */
  460. {
  461. for ( point = points; point < point_limit; point++ )
  462. {
  463. AH_Point prev;
  464. AH_Point next;
  465. FT_Vector ivec, ovec;
  466. prev = point->prev;
  467. ivec.x = point->fx - prev->fx;
  468. ivec.y = point->fy - prev->fy;
  469. point->in_dir = ah_compute_direction( ivec.x, ivec.y );
  470. next = point->next;
  471. ovec.x = next->fx - point->fx;
  472. ovec.y = next->fy - point->fy;
  473. point->out_dir = ah_compute_direction( ovec.x, ovec.y );
  474. #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
  475. if ( point->flags & (AH_FLAG_CONIC | AH_FLAG_CUBIC) )
  476. {
  477. Is_Weak_Point:
  478. point->flags |= AH_FLAG_WEAK_INTERPOLATION;
  479. }
  480. else if ( point->out_dir == point->in_dir )
  481. {
  482. AH_Angle angle_in, angle_out, delta;
  483. if ( point->out_dir != AH_DIR_NONE )
  484. goto Is_Weak_Point;
  485. angle_in = ah_angle( &ivec );
  486. angle_out = ah_angle( &ovec );
  487. delta = angle_in - angle_out;
  488. if ( delta > AH_PI )
  489. delta = AH_2PI - delta;
  490. if ( delta < 0 )
  491. delta = -delta;
  492. if ( delta < 2 )
  493. goto Is_Weak_Point;
  494. }
  495. else if ( point->in_dir == -point->out_dir )
  496. goto Is_Weak_Point;
  497. #endif
  498. }
  499. }
  500. }
  501. Exit:
  502. return error;
  503. }
  504. FT_LOCAL_DEF( void )
  505. ah_setup_uv( AH_Outline outline,
  506. AH_UV source )
  507. {
  508. AH_Point point = outline->points;
  509. AH_Point point_limit = point + outline->num_points;
  510. for ( ; point < point_limit; point++ )
  511. {
  512. FT_Pos u, v;
  513. switch ( source )
  514. {
  515. case AH_UV_FXY:
  516. u = point->fx;
  517. v = point->fy;
  518. break;
  519. case AH_UV_FYX:
  520. u = point->fy;
  521. v = point->fx;
  522. break;
  523. case AH_UV_OXY:
  524. u = point->ox;
  525. v = point->oy;
  526. break;
  527. case AH_UV_OYX:
  528. u = point->oy;
  529. v = point->ox;
  530. break;
  531. case AH_UV_YX:
  532. u = point->y;
  533. v = point->x;
  534. break;
  535. case AH_UV_OX:
  536. u = point->x;
  537. v = point->ox;
  538. break;
  539. case AH_UV_OY:
  540. u = point->y;
  541. v = point->oy;
  542. break;
  543. default:
  544. u = point->x;
  545. v = point->y;
  546. break;
  547. }
  548. point->u = u;
  549. point->v = v;
  550. }
  551. }
  552. /* compute all inflex points in a given glyph */
  553. static void
  554. ah_outline_compute_inflections( AH_Outline outline )
  555. {
  556. AH_Point* contour = outline->contours;
  557. AH_Point* contour_limit = contour + outline->num_contours;
  558. /* load original coordinates in (u,v) */
  559. ah_setup_uv( outline, AH_UV_FXY );
  560. /* do each contour separately */
  561. for ( ; contour < contour_limit; contour++ )
  562. {
  563. FT_Vector vec;
  564. AH_Point point = contour[0];
  565. AH_Point first = point;
  566. AH_Point start = point;
  567. AH_Point end = point;
  568. AH_Point before;
  569. AH_Point after;
  570. AH_Angle angle_in, angle_seg, angle_out;
  571. AH_Angle diff_in, diff_out;
  572. FT_Int finished = 0;
  573. /* compute first segment in contour */
  574. first = point;
  575. start = end = first;
  576. do
  577. {
  578. end = end->next;
  579. if ( end == first )
  580. goto Skip;
  581. } while ( end->u == first->u && end->v == first->v );
  582. vec.x = end->u - start->u;
  583. vec.y = end->v - start->v;
  584. angle_seg = ah_angle( &vec );
  585. /* extend the segment start whenever possible */
  586. before = start;
  587. do
  588. {
  589. do
  590. {
  591. start = before;
  592. before = before->prev;
  593. if ( before == first )
  594. goto Skip;
  595. } while ( before->u == start->u && before->v == start->v );
  596. vec.x = start->u - before->u;
  597. vec.y = start->v - before->v;
  598. angle_in = ah_angle( &vec );
  599. } while ( angle_in == angle_seg );
  600. first = start;
  601. diff_in = ah_angle_diff( angle_in, angle_seg );
  602. /* now, process all segments in the contour */
  603. do
  604. {
  605. /* first, extend current segment's end whenever possible */
  606. after = end;
  607. do
  608. {
  609. do
  610. {
  611. end = after;
  612. after = after->next;
  613. if ( after == first )
  614. finished = 1;
  615. } while ( end->u == after->u && end->v == after->v );
  616. vec.x = after->u - end->u;
  617. vec.y = after->v - end->v;
  618. angle_out = ah_angle( &vec );
  619. } while ( angle_out == angle_seg );
  620. diff_out = ah_angle_diff( angle_seg, angle_out );
  621. if ( ( diff_in ^ diff_out ) < 0 )
  622. {
  623. /* diff_in and diff_out have different signs, we have */
  624. /* inflection points here... */
  625. do
  626. {
  627. start->flags |= AH_FLAG_INFLECTION;
  628. start = start->next;
  629. } while ( start != end );
  630. start->flags |= AH_FLAG_INFLECTION;
  631. }
  632. start = end;
  633. end = after;
  634. angle_seg = angle_out;
  635. diff_in = diff_out;
  636. } while ( !finished );
  637. Skip:
  638. ;
  639. }
  640. }
  641. FT_LOCAL_DEF( void )
  642. ah_outline_compute_segments( AH_Outline outline )
  643. {
  644. int dimension;
  645. AH_Segment segments;
  646. FT_Int* p_num_segments;
  647. AH_Direction segment_dir;
  648. AH_Direction major_dir;
  649. segments = outline->horz_segments;
  650. p_num_segments = &outline->num_hsegments;
  651. major_dir = AH_DIR_RIGHT; /* This value must be positive! */
  652. segment_dir = major_dir;
  653. /* set up (u,v) in each point */
  654. ah_setup_uv( outline, AH_UV_FYX );
  655. for ( dimension = 1; dimension >= 0; dimension-- )
  656. {
  657. AH_Point* contour = outline->contours;
  658. AH_Point* contour_limit = contour + outline->num_contours;
  659. AH_Segment segment = segments;
  660. FT_Int num_segments = 0;
  661. #ifdef AH_HINT_METRICS
  662. AH_Point min_point = 0;
  663. AH_Point max_point = 0;
  664. FT_Pos min_coord = 32000;
  665. FT_Pos max_coord = -32000;
  666. #endif
  667. /* do each contour separately */
  668. for ( ; contour < contour_limit; contour++ )
  669. {
  670. AH_Point point = contour[0];
  671. AH_Point last = point->prev;
  672. int on_edge = 0;
  673. FT_Pos min_pos = +32000; /* minimum segment pos != min_coord */
  674. FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
  675. FT_Bool passed;
  676. #ifdef AH_HINT_METRICS
  677. if ( point->u < min_coord )
  678. {
  679. min_coord = point->u;
  680. min_point = point;
  681. }
  682. if ( point->u > max_coord )
  683. {
  684. max_coord = point->u;
  685. max_point = point;
  686. }
  687. #endif
  688. if ( point == last ) /* skip singletons -- just in case? */
  689. continue;
  690. if ( ABS( last->out_dir ) == major_dir &&
  691. ABS( point->out_dir ) == major_dir )
  692. {
  693. /* we are already on an edge, try to locate its start */
  694. last = point;
  695. for (;;)
  696. {
  697. point = point->prev;
  698. if ( ABS( point->out_dir ) != major_dir )
  699. {
  700. point = point->next;
  701. break;
  702. }
  703. if ( point == last )
  704. break;
  705. }
  706. }
  707. last = point;
  708. passed = 0;
  709. for (;;)
  710. {
  711. FT_Pos u, v;
  712. if ( on_edge )
  713. {
  714. u = point->u;
  715. if ( u < min_pos )
  716. min_pos = u;
  717. if ( u > max_pos )
  718. max_pos = u;
  719. if ( point->out_dir != segment_dir || point == last )
  720. {
  721. /* we are just leaving an edge; record a new segment! */
  722. segment->last = point;
  723. segment->pos = ( min_pos + max_pos ) >> 1;
  724. /* a segment is round if either its first or last point */
  725. /* is a control point */
  726. if ( ( segment->first->flags | point->flags ) &
  727. AH_FLAG_CONTROL )
  728. segment->flags |= AH_EDGE_ROUND;
  729. /* compute segment size */
  730. min_pos = max_pos = point->v;
  731. v = segment->first->v;
  732. if ( v < min_pos )
  733. min_pos = v;
  734. if ( v > max_pos )
  735. max_pos = v;
  736. segment->min_coord = min_pos;
  737. segment->max_coord = max_pos;
  738. on_edge = 0;
  739. num_segments++;
  740. segment++;
  741. /* fallthrough */
  742. }
  743. }
  744. /* now exit if we are at the start/end point */
  745. if ( point == last )
  746. {
  747. if ( passed )
  748. break;
  749. passed = 1;
  750. }
  751. if ( !on_edge && ABS( point->out_dir ) == major_dir )
  752. {
  753. /* this is the start of a new segment! */
  754. segment_dir = point->out_dir;
  755. /* clear all segment fields */
  756. FT_ZERO( segment );
  757. segment->dir = segment_dir;
  758. segment->flags = AH_EDGE_NORMAL;
  759. min_pos = max_pos = point->u;
  760. segment->first = point;
  761. segment->last = point;
  762. segment->contour = contour;
  763. on_edge = 1;
  764. #ifdef AH_HINT_METRICS
  765. if ( point == max_point )
  766. max_point = 0;
  767. if ( point == min_point )
  768. min_point = 0;
  769. #endif
  770. }
  771. point = point->next;
  772. }
  773. } /* contours */
  774. #ifdef AH_HINT_METRICS
  775. /* we need to ensure that there are edges on the left-most and */
  776. /* right-most points of the glyph in order to hint the metrics; */
  777. /* we do this by inserting fake segments when needed */
  778. if ( dimension == 0 )
  779. {
  780. AH_Point point = outline->points;
  781. AH_Point point_limit = point + outline->num_points;
  782. FT_Pos min_pos = 32000;
  783. FT_Pos max_pos = -32000;
  784. min_point = 0;
  785. max_point = 0;
  786. /* compute minimum and maximum points */
  787. for ( ; point < point_limit; point++ )
  788. {
  789. FT_Pos x = point->fx;
  790. if ( x < min_pos )
  791. {
  792. min_pos = x;
  793. min_point = point;
  794. }
  795. if ( x > max_pos )
  796. {
  797. max_pos = x;
  798. max_point = point;
  799. }
  800. }
  801. /* insert minimum segment */
  802. if ( min_point )
  803. {
  804. /* clear all segment fields */
  805. FT_ZERO( segment );
  806. segment->dir = segment_dir;
  807. segment->flags = AH_EDGE_NORMAL;
  808. segment->first = min_point;
  809. segment->last = min_point;
  810. segment->pos = min_pos;
  811. num_segments++;
  812. segment++;
  813. }
  814. /* insert maximum segment */
  815. if ( max_point )
  816. {
  817. /* clear all segment fields */
  818. FT_ZERO( segment );
  819. segment->dir = segment_dir;
  820. segment->flags = AH_EDGE_NORMAL;
  821. segment->first = max_point;
  822. segment->last = max_point;
  823. segment->pos = max_pos;
  824. num_segments++;
  825. segment++;
  826. }
  827. }
  828. #endif /* AH_HINT_METRICS */
  829. *p_num_segments = num_segments;
  830. segments = outline->vert_segments;
  831. major_dir = AH_DIR_UP;
  832. p_num_segments = &outline->num_vsegments;
  833. ah_setup_uv( outline, AH_UV_FXY );
  834. }
  835. }
  836. FT_LOCAL_DEF( void )
  837. ah_outline_link_segments( AH_Outline outline )
  838. {
  839. AH_Segment segments;
  840. AH_Segment segment_limit;
  841. int dimension;
  842. ah_setup_uv( outline, AH_UV_FYX );
  843. segments = outline->horz_segments;
  844. segment_limit = segments + outline->num_hsegments;
  845. for ( dimension = 1; dimension >= 0; dimension-- )
  846. {
  847. AH_Segment seg1;
  848. AH_Segment seg2;
  849. /* now compare each segment to the others */
  850. for ( seg1 = segments; seg1 < segment_limit; seg1++ )
  851. {
  852. FT_Pos best_score;
  853. AH_Segment best_segment;
  854. /* the fake segments are introduced to hint the metrics -- */
  855. /* we must never link them to anything */
  856. if ( seg1->first == seg1->last )
  857. continue;
  858. best_segment = seg1->link;
  859. if ( best_segment )
  860. best_score = seg1->score;
  861. else
  862. best_score = 32000;
  863. for ( seg2 = segments; seg2 < segment_limit; seg2++ )
  864. if ( seg1 != seg2 && seg1->dir + seg2->dir == 0 )
  865. {
  866. FT_Pos pos1 = seg1->pos;
  867. FT_Pos pos2 = seg2->pos;
  868. FT_Bool is_dir;
  869. FT_Bool is_pos;
  870. /* check that the segments are correctly oriented and */
  871. /* positioned to form a black distance */
  872. is_dir = (FT_Bool)( seg1->dir == outline->horz_major_dir ||
  873. seg1->dir == outline->vert_major_dir );
  874. is_pos = (FT_Bool)( pos1 > pos2 );
  875. if ( pos1 == pos2 || !(is_dir ^ is_pos) )
  876. continue;
  877. {
  878. FT_Pos min = seg1->min_coord;
  879. FT_Pos max = seg1->max_coord;
  880. FT_Pos len, dist, score;
  881. if ( min < seg2->min_coord )
  882. min = seg2->min_coord;
  883. if ( max > seg2->max_coord )
  884. max = seg2->max_coord;
  885. len = max - min;
  886. if ( len >= 8 )
  887. {
  888. dist = seg2->pos - seg1->pos;
  889. if ( dist < 0 )
  890. dist = -dist;
  891. score = dist + 3000 / len;
  892. if ( score < best_score )
  893. {
  894. best_score = score;
  895. best_segment = seg2;
  896. }
  897. }
  898. }
  899. }
  900. if ( best_segment )
  901. {
  902. seg1->link = best_segment;
  903. seg1->score = best_score;
  904. best_segment->num_linked++;
  905. }
  906. } /* edges 1 */
  907. /* now, compute the `serif' segments */
  908. for ( seg1 = segments; seg1 < segment_limit; seg1++ )
  909. {
  910. seg2 = seg1->link;
  911. if ( seg2 && seg2->link != seg1 )
  912. {
  913. seg1->link = 0;
  914. seg1->serif = seg2->link;
  915. }
  916. }
  917. ah_setup_uv( outline, AH_UV_FXY );
  918. segments = outline->vert_segments;
  919. segment_limit = segments + outline->num_vsegments;
  920. }
  921. }
  922. static void
  923. ah_outline_compute_edges( AH_Outline outline )
  924. {
  925. AH_Edge edges;
  926. AH_Segment segments;
  927. AH_Segment segment_limit;
  928. AH_Direction up_dir;
  929. FT_Int* p_num_edges;
  930. FT_Int dimension;
  931. FT_Fixed scale;
  932. FT_Pos edge_distance_threshold;
  933. edges = outline->horz_edges;
  934. segments = outline->horz_segments;
  935. segment_limit = segments + outline->num_hsegments;
  936. p_num_edges = &outline->num_hedges;
  937. up_dir = AH_DIR_RIGHT;
  938. scale = outline->y_scale;
  939. for ( dimension = 1; dimension >= 0; dimension-- )
  940. {
  941. AH_Edge edge;
  942. AH_Edge edge_limit; /* really == edge + num_edges */
  943. AH_Segment seg;
  944. /*********************************************************************/
  945. /* */
  946. /* We will begin by generating a sorted table of edges for the */
  947. /* current direction. To do so, we simply scan each segment and try */
  948. /* to find an edge in our table that corresponds to its position. */
  949. /* */
  950. /* If no edge is found, we create and insert a new edge in the */
  951. /* sorted table. Otherwise, we simply add the segment to the edge's */
  952. /* list which will be processed in the second step to compute the */
  953. /* edge's properties. */
  954. /* */
  955. /* Note that the edges table is sorted along the segment/edge */
  956. /* position. */
  957. /* */
  958. /*********************************************************************/
  959. edge_distance_threshold = FT_MulFix( outline->edge_distance_threshold,
  960. scale );
  961. if ( edge_distance_threshold > 64 / 4 )
  962. edge_distance_threshold = 64 / 4;
  963. edge_limit = edges;
  964. for ( seg = segments; seg < segment_limit; seg++ )
  965. {
  966. AH_Edge found = 0;
  967. /* look for an edge corresponding to the segment */
  968. for ( edge = edges; edge < edge_limit; edge++ )
  969. {
  970. FT_Pos dist;
  971. dist = seg->pos - edge->fpos;
  972. if ( dist < 0 )
  973. dist = -dist;
  974. dist = FT_MulFix( dist, scale );
  975. if ( dist < edge_distance_threshold )
  976. {
  977. found = edge;
  978. break;
  979. }
  980. }
  981. if ( !found )
  982. {
  983. /* insert a new edge in the list and */
  984. /* sort according to the position */
  985. while ( edge > edges && edge[-1].fpos > seg->pos )
  986. {
  987. edge[0] = edge[-1];
  988. edge--;
  989. }
  990. edge_limit++;
  991. /* clear all edge fields */
  992. FT_MEM_ZERO( edge, sizeof ( *edge ) );
  993. /* add the segment to the new edge's list */
  994. edge->first = seg;
  995. edge->last = seg;
  996. edge->fpos = seg->pos;
  997. edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
  998. seg->edge_next = seg;
  999. }
  1000. else
  1001. {
  1002. /* if an edge was found, simply add the segment to the edge's */
  1003. /* list */
  1004. seg->edge_next = edge->first;
  1005. edge->last->edge_next = seg;
  1006. edge->last = seg;
  1007. }
  1008. }
  1009. *p_num_edges = (FT_Int)( edge_limit - edges );
  1010. /*********************************************************************/
  1011. /* */
  1012. /* Good, we will now compute each edge's properties according to */
  1013. /* segments found on its position. Basically, these are: */
  1014. /* */
  1015. /* - edge's main direction */
  1016. /* - stem edge, serif edge or both (which defaults to stem then) */
  1017. /* - rounded edge, straigth or both (which defaults to straight) */
  1018. /* - link for edge */
  1019. /* */
  1020. /*********************************************************************/
  1021. /* first of all, set the `edge' field in each segment -- this is */
  1022. /* required in order to compute edge links */
  1023. for ( edge = edges; edge < edge_limit; edge++ )
  1024. {
  1025. seg = edge->first;
  1026. if ( seg )
  1027. do
  1028. {
  1029. seg->edge = edge;
  1030. seg = seg->edge_next;
  1031. }
  1032. while ( seg != edge->first );
  1033. }
  1034. /* now, compute each edge properties */
  1035. for ( edge = edges; edge < edge_limit; edge++ )
  1036. {
  1037. FT_Int is_round = 0; /* does it contain round segments? */
  1038. FT_Int is_straight = 0; /* does it contain straight segments? */
  1039. FT_Pos ups = 0; /* number of upwards segments */
  1040. FT_Pos downs = 0; /* number of downwards segments */
  1041. seg = edge->first;
  1042. do
  1043. {
  1044. FT_Bool is_serif;
  1045. /* check for roundness of segment */
  1046. if ( seg->flags & AH_EDGE_ROUND )
  1047. is_round++;
  1048. else
  1049. is_straight++;
  1050. /* check for segment direction */
  1051. if ( seg->dir == up_dir )
  1052. ups += seg->max_coord-seg->min_coord;
  1053. else
  1054. downs += seg->max_coord-seg->min_coord;
  1055. /* check for links -- if seg->serif is set, then seg->link must */
  1056. /* be ignored */
  1057. is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
  1058. if ( seg->link || is_serif )
  1059. {
  1060. AH_Edge edge2;
  1061. AH_Segment seg2;
  1062. edge2 = edge->link;
  1063. seg2 = seg->link;
  1064. if ( is_serif )
  1065. {
  1066. seg2 = seg->serif;
  1067. edge2 = edge->serif;
  1068. }
  1069. if ( edge2 )
  1070. {
  1071. FT_Pos edge_delta;
  1072. FT_Pos seg_delta;
  1073. edge_delta = edge->fpos - edge2->fpos;
  1074. if ( edge_delta < 0 )
  1075. edge_delta = -edge_delta;
  1076. seg_delta = seg->pos - seg2->pos;
  1077. if ( seg_delta < 0 )
  1078. seg_delta = -seg_delta;
  1079. if ( seg_delta < edge_delta )
  1080. edge2 = seg2->edge;
  1081. }
  1082. else
  1083. edge2 = seg2->edge;
  1084. if ( is_serif )
  1085. edge->serif = edge2;
  1086. else
  1087. edge->link = edge2;
  1088. }
  1089. seg = seg->edge_next;
  1090. } while ( seg != edge->first );
  1091. /* set the round/straight flags */
  1092. edge->flags = AH_EDGE_NORMAL;
  1093. if ( is_round > 0 && is_round >= is_straight )
  1094. edge->flags |= AH_EDGE_ROUND;
  1095. /* set the edge's main direction */
  1096. edge->dir = AH_DIR_NONE;
  1097. if ( ups > downs )
  1098. edge->dir = up_dir;
  1099. else if ( ups < downs )
  1100. edge->dir = - up_dir;
  1101. else if ( ups == downs )
  1102. edge->dir = 0; /* both up and down !! */
  1103. /* gets rid of serifs if link is set */
  1104. /* XXX: This gets rid of many unpleasant artefacts! */
  1105. /* Example: the `c' in cour.pfa at size 13 */
  1106. if ( edge->serif && edge->link )
  1107. edge->serif = 0;
  1108. }
  1109. edges = outline->vert_edges;
  1110. segments = outline->vert_segments;
  1111. segment_limit = segments + outline->num_vsegments;
  1112. p_num_edges = &outline->num_vedges;
  1113. up_dir = AH_DIR_UP;
  1114. scale = outline->x_scale;
  1115. }
  1116. }
  1117. /*************************************************************************/
  1118. /* */
  1119. /* <Function> */
  1120. /* ah_outline_detect_features */
  1121. /* */
  1122. /* <Description> */
  1123. /* Performs feature detection on a given AH_OutlineRec object. */
  1124. /* */
  1125. FT_LOCAL_DEF( void )
  1126. ah_outline_detect_features( AH_Outline outline )
  1127. {
  1128. ah_outline_compute_segments ( outline );
  1129. ah_outline_link_segments ( outline );
  1130. ah_outline_compute_edges ( outline );
  1131. ah_outline_compute_inflections( outline );
  1132. }
  1133. /*************************************************************************/
  1134. /* */
  1135. /* <Function> */
  1136. /* ah_outline_compute_blue_edges */
  1137. /* */
  1138. /* <Description> */
  1139. /* Computes the `blue edges' in a given outline (i.e. those that must */
  1140. /* be snapped to a blue zone edge (top or bottom). */
  1141. /* */
  1142. FT_LOCAL_DEF( void )
  1143. ah_outline_compute_blue_edges( AH_Outline outline,
  1144. AH_Face_Globals face_globals )
  1145. {
  1146. AH_Edge edge = outline->horz_edges;
  1147. AH_Edge edge_limit = edge + outline->num_hedges;
  1148. AH_Globals globals = &face_globals->design;
  1149. FT_Fixed y_scale = outline->y_scale;
  1150. FT_Bool blue_active[AH_BLUE_MAX];
  1151. /* compute which blue zones are active, i.e. have their scaled */
  1152. /* size < 3/4 pixels */
  1153. {
  1154. AH_Blue blue;
  1155. FT_Bool check = 0;
  1156. for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ )
  1157. {
  1158. FT_Pos ref, shoot, dist;
  1159. ref = globals->blue_refs[blue];
  1160. shoot = globals->blue_shoots[blue];
  1161. dist = ref-shoot;
  1162. if ( dist < 0 )
  1163. dist = -dist;
  1164. blue_active[blue] = 0;
  1165. if ( FT_MulFix( dist, y_scale ) < 48 )
  1166. {
  1167. blue_active[blue] = 1;
  1168. check = 1;
  1169. }
  1170. }
  1171. /* return immediately if no blue zone is active */
  1172. if ( !check )
  1173. return;
  1174. }
  1175. /* compute for each horizontal edge, which blue zone is closer */
  1176. for ( ; edge < edge_limit; edge++ )
  1177. {
  1178. AH_Blue blue;
  1179. FT_Pos* best_blue = 0;
  1180. FT_Pos best_dist; /* initial threshold */
  1181. /* compute the initial threshold as a fraction of the EM size */
  1182. best_dist = FT_MulFix( face_globals->face->units_per_EM / 40, y_scale );
  1183. if ( best_dist > 64 / 4 )
  1184. best_dist = 64 / 4;
  1185. for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ )
  1186. {
  1187. /* if it is a top zone, check for right edges -- if it is a bottom */
  1188. /* zone, check for left edges */
  1189. /* */
  1190. /* of course, that's for TrueType XXX */
  1191. FT_Bool is_top_blue =
  1192. FT_BOOL( AH_IS_TOP_BLUE( blue ) );
  1193. FT_Bool is_major_dir =
  1194. FT_BOOL( edge->dir == outline->horz_major_dir );
  1195. if ( !blue_active[blue] )
  1196. continue;
  1197. /* if it is a top zone, the edge must be against the major */
  1198. /* direction; if it is a bottom zone, it must be in the major */
  1199. /* direction */
  1200. if ( is_top_blue ^ is_major_dir )
  1201. {
  1202. FT_Pos dist;
  1203. FT_Pos* blue_pos = globals->blue_refs + blue;
  1204. /* first of all, compare it to the reference position */
  1205. dist = edge->fpos - *blue_pos;
  1206. if ( dist < 0 )
  1207. dist = -dist;
  1208. dist = FT_MulFix( dist, y_scale );
  1209. if ( dist < best_dist )
  1210. {
  1211. best_dist = dist;
  1212. best_blue = blue_pos;
  1213. }
  1214. /* now, compare it to the overshoot position if the edge is */
  1215. /* rounded, and if the edge is over the reference position of a */
  1216. /* top zone, or under the reference position of a bottom zone */
  1217. if ( edge->flags & AH_EDGE_ROUND && dist != 0 )
  1218. {
  1219. FT_Bool is_under_ref = FT_BOOL( edge->fpos < *blue_pos );
  1220. if ( is_top_blue ^ is_under_ref )
  1221. {
  1222. blue_pos = globals->blue_shoots + blue;
  1223. dist = edge->fpos - *blue_pos;
  1224. if ( dist < 0 )
  1225. dist = -dist;
  1226. dist = FT_MulFix( dist, y_scale );
  1227. if ( dist < best_dist )
  1228. {
  1229. best_dist = dist;
  1230. best_blue = blue_pos;
  1231. }
  1232. }
  1233. }
  1234. }
  1235. }
  1236. if ( best_blue )
  1237. edge->blue_edge = best_blue;
  1238. }
  1239. }
  1240. /*************************************************************************/
  1241. /* */
  1242. /* <Function> */
  1243. /* ah_outline_scale_blue_edges */
  1244. /* */
  1245. /* <Description> */
  1246. /* This functions must be called before hinting in order to re-adjust */
  1247. /* the contents of the detected edges (basically change the `blue */
  1248. /* edge' pointer from `design units' to `scaled ones'). */
  1249. /* */
  1250. FT_LOCAL_DEF( void )
  1251. ah_outline_scale_blue_edges( AH_Outline outline,
  1252. AH_Face_Globals globals )
  1253. {
  1254. AH_Edge edge = outline->horz_edges;
  1255. AH_Edge edge_limit = edge + outline->num_hedges;
  1256. FT_Pos delta;
  1257. delta = globals->scaled.blue_refs - globals->design.blue_refs;
  1258. for ( ; edge < edge_limit; edge++ )
  1259. {
  1260. if ( edge->blue_edge )
  1261. edge->blue_edge += delta;
  1262. }
  1263. }
  1264. /* END */