PageRenderTime 117ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 1ms

/ExternalLibs/freetype2/src/base/ftstroke.c

https://bitbucket.org/King_DuckZ/aquaria
C | 2010 lines | 1380 code | 485 blank | 145 comment | 189 complexity | d15fe173f167d7c84a632d5961ee71cd MD5 | raw file
Possible License(s): BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /***************************************************************************/
  2. /* */
  3. /* ftstroke.c */
  4. /* */
  5. /* FreeType path stroker (body). */
  6. /* */
  7. /* Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009 by */
  8. /* David Turner, Robert Wilhelm, and Werner Lemberg. */
  9. /* */
  10. /* This file is part of the FreeType project, and may only be used, */
  11. /* modified, and distributed under the terms of the FreeType project */
  12. /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
  13. /* this file you indicate that you have read the license and */
  14. /* understand and accept it fully. */
  15. /* */
  16. /***************************************************************************/
  17. #include <ft2build.h>
  18. #include FT_STROKER_H
  19. #include FT_TRIGONOMETRY_H
  20. #include FT_OUTLINE_H
  21. #include FT_INTERNAL_MEMORY_H
  22. #include FT_INTERNAL_DEBUG_H
  23. #include FT_INTERNAL_OBJECTS_H
  24. /* documentation is in ftstroke.h */
  25. FT_EXPORT_DEF( FT_StrokerBorder )
  26. FT_Outline_GetInsideBorder( FT_Outline* outline )
  27. {
  28. FT_Orientation o = FT_Outline_Get_Orientation( outline );
  29. return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
  30. : FT_STROKER_BORDER_LEFT ;
  31. }
  32. /* documentation is in ftstroke.h */
  33. FT_EXPORT_DEF( FT_StrokerBorder )
  34. FT_Outline_GetOutsideBorder( FT_Outline* outline )
  35. {
  36. FT_Orientation o = FT_Outline_Get_Orientation( outline );
  37. return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
  38. : FT_STROKER_BORDER_RIGHT ;
  39. }
  40. /***************************************************************************/
  41. /***************************************************************************/
  42. /***** *****/
  43. /***** BEZIER COMPUTATIONS *****/
  44. /***** *****/
  45. /***************************************************************************/
  46. /***************************************************************************/
  47. #define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 )
  48. #define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 6 )
  49. #define FT_EPSILON 2
  50. #define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
  51. static FT_Pos
  52. ft_pos_abs( FT_Pos x )
  53. {
  54. return x >= 0 ? x : -x ;
  55. }
  56. static void
  57. ft_conic_split( FT_Vector* base )
  58. {
  59. FT_Pos a, b;
  60. base[4].x = base[2].x;
  61. b = base[1].x;
  62. a = base[3].x = ( base[2].x + b ) / 2;
  63. b = base[1].x = ( base[0].x + b ) / 2;
  64. base[2].x = ( a + b ) / 2;
  65. base[4].y = base[2].y;
  66. b = base[1].y;
  67. a = base[3].y = ( base[2].y + b ) / 2;
  68. b = base[1].y = ( base[0].y + b ) / 2;
  69. base[2].y = ( a + b ) / 2;
  70. }
  71. static FT_Bool
  72. ft_conic_is_small_enough( FT_Vector* base,
  73. FT_Angle *angle_in,
  74. FT_Angle *angle_out )
  75. {
  76. FT_Vector d1, d2;
  77. FT_Angle theta;
  78. FT_Int close1, close2;
  79. d1.x = base[1].x - base[2].x;
  80. d1.y = base[1].y - base[2].y;
  81. d2.x = base[0].x - base[1].x;
  82. d2.y = base[0].y - base[1].y;
  83. close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
  84. close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
  85. if ( close1 )
  86. {
  87. if ( close2 )
  88. *angle_in = *angle_out = 0;
  89. else
  90. *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
  91. }
  92. else if ( close2 )
  93. {
  94. *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
  95. }
  96. else
  97. {
  98. *angle_in = FT_Atan2( d1.x, d1.y );
  99. *angle_out = FT_Atan2( d2.x, d2.y );
  100. }
  101. theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
  102. return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
  103. }
  104. static void
  105. ft_cubic_split( FT_Vector* base )
  106. {
  107. FT_Pos a, b, c, d;
  108. base[6].x = base[3].x;
  109. c = base[1].x;
  110. d = base[2].x;
  111. base[1].x = a = ( base[0].x + c ) / 2;
  112. base[5].x = b = ( base[3].x + d ) / 2;
  113. c = ( c + d ) / 2;
  114. base[2].x = a = ( a + c ) / 2;
  115. base[4].x = b = ( b + c ) / 2;
  116. base[3].x = ( a + b ) / 2;
  117. base[6].y = base[3].y;
  118. c = base[1].y;
  119. d = base[2].y;
  120. base[1].y = a = ( base[0].y + c ) / 2;
  121. base[5].y = b = ( base[3].y + d ) / 2;
  122. c = ( c + d ) / 2;
  123. base[2].y = a = ( a + c ) / 2;
  124. base[4].y = b = ( b + c ) / 2;
  125. base[3].y = ( a + b ) / 2;
  126. }
  127. static FT_Bool
  128. ft_cubic_is_small_enough( FT_Vector* base,
  129. FT_Angle *angle_in,
  130. FT_Angle *angle_mid,
  131. FT_Angle *angle_out )
  132. {
  133. FT_Vector d1, d2, d3;
  134. FT_Angle theta1, theta2;
  135. FT_Int close1, close2, close3;
  136. d1.x = base[2].x - base[3].x;
  137. d1.y = base[2].y - base[3].y;
  138. d2.x = base[1].x - base[2].x;
  139. d2.y = base[1].y - base[2].y;
  140. d3.x = base[0].x - base[1].x;
  141. d3.y = base[0].y - base[1].y;
  142. close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
  143. close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
  144. close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
  145. if ( close1 || close3 )
  146. {
  147. if ( close2 )
  148. {
  149. /* basically a point */
  150. *angle_in = *angle_out = *angle_mid = 0;
  151. }
  152. else if ( close1 )
  153. {
  154. *angle_in = *angle_mid = FT_Atan2( d2.x, d2.y );
  155. *angle_out = FT_Atan2( d3.x, d3.y );
  156. }
  157. else /* close2 */
  158. {
  159. *angle_in = FT_Atan2( d1.x, d1.y );
  160. *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
  161. }
  162. }
  163. else if ( close2 )
  164. {
  165. *angle_in = *angle_mid = FT_Atan2( d1.x, d1.y );
  166. *angle_out = FT_Atan2( d3.x, d3.y );
  167. }
  168. else
  169. {
  170. *angle_in = FT_Atan2( d1.x, d1.y );
  171. *angle_mid = FT_Atan2( d2.x, d2.y );
  172. *angle_out = FT_Atan2( d3.x, d3.y );
  173. }
  174. theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) );
  175. theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
  176. return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
  177. theta2 < FT_SMALL_CUBIC_THRESHOLD );
  178. }
  179. /***************************************************************************/
  180. /***************************************************************************/
  181. /***** *****/
  182. /***** STROKE BORDERS *****/
  183. /***** *****/
  184. /***************************************************************************/
  185. /***************************************************************************/
  186. typedef enum FT_StrokeTags_
  187. {
  188. FT_STROKE_TAG_ON = 1, /* on-curve point */
  189. FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */
  190. FT_STROKE_TAG_BEGIN = 4, /* sub-path start */
  191. FT_STROKE_TAG_END = 8 /* sub-path end */
  192. } FT_StrokeTags;
  193. #define FT_STROKE_TAG_BEGIN_END (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END)
  194. typedef struct FT_StrokeBorderRec_
  195. {
  196. FT_UInt num_points;
  197. FT_UInt max_points;
  198. FT_Vector* points;
  199. FT_Byte* tags;
  200. FT_Bool movable;
  201. FT_Int start; /* index of current sub-path start point */
  202. FT_Memory memory;
  203. FT_Bool valid;
  204. } FT_StrokeBorderRec, *FT_StrokeBorder;
  205. static FT_Error
  206. ft_stroke_border_grow( FT_StrokeBorder border,
  207. FT_UInt new_points )
  208. {
  209. FT_UInt old_max = border->max_points;
  210. FT_UInt new_max = border->num_points + new_points;
  211. FT_Error error = FT_Err_Ok;
  212. if ( new_max > old_max )
  213. {
  214. FT_UInt cur_max = old_max;
  215. FT_Memory memory = border->memory;
  216. while ( cur_max < new_max )
  217. cur_max += ( cur_max >> 1 ) + 16;
  218. if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
  219. FT_RENEW_ARRAY( border->tags, old_max, cur_max ) )
  220. goto Exit;
  221. border->max_points = cur_max;
  222. }
  223. Exit:
  224. return error;
  225. }
  226. static void
  227. ft_stroke_border_close( FT_StrokeBorder border,
  228. FT_Bool reverse )
  229. {
  230. FT_UInt start = border->start;
  231. FT_UInt count = border->num_points;
  232. FT_ASSERT( border->start >= 0 );
  233. /* don't record empty paths! */
  234. if ( count <= start + 1U )
  235. border->num_points = start;
  236. else
  237. {
  238. /* copy the last point to the start of this sub-path, since */
  239. /* it contains the `adjusted' starting coordinates */
  240. border->num_points = --count;
  241. border->points[start] = border->points[count];
  242. if ( reverse )
  243. {
  244. /* reverse the points */
  245. {
  246. FT_Vector* vec1 = border->points + start + 1;
  247. FT_Vector* vec2 = border->points + count - 1;
  248. for ( ; vec1 < vec2; vec1++, vec2-- )
  249. {
  250. FT_Vector tmp;
  251. tmp = *vec1;
  252. *vec1 = *vec2;
  253. *vec2 = tmp;
  254. }
  255. }
  256. /* then the tags */
  257. {
  258. FT_Byte* tag1 = border->tags + start + 1;
  259. FT_Byte* tag2 = border->tags + count - 1;
  260. for ( ; tag1 < tag2; tag1++, tag2-- )
  261. {
  262. FT_Byte tmp;
  263. tmp = *tag1;
  264. *tag1 = *tag2;
  265. *tag2 = tmp;
  266. }
  267. }
  268. }
  269. border->tags[start ] |= FT_STROKE_TAG_BEGIN;
  270. border->tags[count - 1] |= FT_STROKE_TAG_END;
  271. }
  272. border->start = -1;
  273. border->movable = FALSE;
  274. }
  275. static FT_Error
  276. ft_stroke_border_lineto( FT_StrokeBorder border,
  277. FT_Vector* to,
  278. FT_Bool movable )
  279. {
  280. FT_Error error = FT_Err_Ok;
  281. FT_ASSERT( border->start >= 0 );
  282. if ( border->movable )
  283. {
  284. /* move last point */
  285. border->points[border->num_points - 1] = *to;
  286. }
  287. else
  288. {
  289. /* add one point */
  290. error = ft_stroke_border_grow( border, 1 );
  291. if ( !error )
  292. {
  293. FT_Vector* vec = border->points + border->num_points;
  294. FT_Byte* tag = border->tags + border->num_points;
  295. vec[0] = *to;
  296. tag[0] = FT_STROKE_TAG_ON;
  297. border->num_points += 1;
  298. }
  299. }
  300. border->movable = movable;
  301. return error;
  302. }
  303. static FT_Error
  304. ft_stroke_border_conicto( FT_StrokeBorder border,
  305. FT_Vector* control,
  306. FT_Vector* to )
  307. {
  308. FT_Error error;
  309. FT_ASSERT( border->start >= 0 );
  310. error = ft_stroke_border_grow( border, 2 );
  311. if ( !error )
  312. {
  313. FT_Vector* vec = border->points + border->num_points;
  314. FT_Byte* tag = border->tags + border->num_points;
  315. vec[0] = *control;
  316. vec[1] = *to;
  317. tag[0] = 0;
  318. tag[1] = FT_STROKE_TAG_ON;
  319. border->num_points += 2;
  320. }
  321. border->movable = FALSE;
  322. return error;
  323. }
  324. static FT_Error
  325. ft_stroke_border_cubicto( FT_StrokeBorder border,
  326. FT_Vector* control1,
  327. FT_Vector* control2,
  328. FT_Vector* to )
  329. {
  330. FT_Error error;
  331. FT_ASSERT( border->start >= 0 );
  332. error = ft_stroke_border_grow( border, 3 );
  333. if ( !error )
  334. {
  335. FT_Vector* vec = border->points + border->num_points;
  336. FT_Byte* tag = border->tags + border->num_points;
  337. vec[0] = *control1;
  338. vec[1] = *control2;
  339. vec[2] = *to;
  340. tag[0] = FT_STROKE_TAG_CUBIC;
  341. tag[1] = FT_STROKE_TAG_CUBIC;
  342. tag[2] = FT_STROKE_TAG_ON;
  343. border->num_points += 3;
  344. }
  345. border->movable = FALSE;
  346. return error;
  347. }
  348. #define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 )
  349. static FT_Error
  350. ft_stroke_border_arcto( FT_StrokeBorder border,
  351. FT_Vector* center,
  352. FT_Fixed radius,
  353. FT_Angle angle_start,
  354. FT_Angle angle_diff )
  355. {
  356. FT_Angle total, angle, step, rotate, next, theta;
  357. FT_Vector a, b, a2, b2;
  358. FT_Fixed length;
  359. FT_Error error = FT_Err_Ok;
  360. /* compute start point */
  361. FT_Vector_From_Polar( &a, radius, angle_start );
  362. a.x += center->x;
  363. a.y += center->y;
  364. total = angle_diff;
  365. angle = angle_start;
  366. rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
  367. while ( total != 0 )
  368. {
  369. step = total;
  370. if ( step > FT_ARC_CUBIC_ANGLE )
  371. step = FT_ARC_CUBIC_ANGLE;
  372. else if ( step < -FT_ARC_CUBIC_ANGLE )
  373. step = -FT_ARC_CUBIC_ANGLE;
  374. next = angle + step;
  375. theta = step;
  376. if ( theta < 0 )
  377. theta = -theta;
  378. theta >>= 1;
  379. /* compute end point */
  380. FT_Vector_From_Polar( &b, radius, next );
  381. b.x += center->x;
  382. b.y += center->y;
  383. /* compute first and second control points */
  384. length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
  385. ( 0x10000L + FT_Cos( theta ) ) * 3 );
  386. FT_Vector_From_Polar( &a2, length, angle + rotate );
  387. a2.x += a.x;
  388. a2.y += a.y;
  389. FT_Vector_From_Polar( &b2, length, next - rotate );
  390. b2.x += b.x;
  391. b2.y += b.y;
  392. /* add cubic arc */
  393. error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
  394. if ( error )
  395. break;
  396. /* process the rest of the arc ?? */
  397. a = b;
  398. total -= step;
  399. angle = next;
  400. }
  401. return error;
  402. }
  403. static FT_Error
  404. ft_stroke_border_moveto( FT_StrokeBorder border,
  405. FT_Vector* to )
  406. {
  407. /* close current open path if any ? */
  408. if ( border->start >= 0 )
  409. ft_stroke_border_close( border, FALSE );
  410. border->start = border->num_points;
  411. border->movable = FALSE;
  412. return ft_stroke_border_lineto( border, to, FALSE );
  413. }
  414. static void
  415. ft_stroke_border_init( FT_StrokeBorder border,
  416. FT_Memory memory )
  417. {
  418. border->memory = memory;
  419. border->points = NULL;
  420. border->tags = NULL;
  421. border->num_points = 0;
  422. border->max_points = 0;
  423. border->start = -1;
  424. border->valid = FALSE;
  425. }
  426. static void
  427. ft_stroke_border_reset( FT_StrokeBorder border )
  428. {
  429. border->num_points = 0;
  430. border->start = -1;
  431. border->valid = FALSE;
  432. }
  433. static void
  434. ft_stroke_border_done( FT_StrokeBorder border )
  435. {
  436. FT_Memory memory = border->memory;
  437. FT_FREE( border->points );
  438. FT_FREE( border->tags );
  439. border->num_points = 0;
  440. border->max_points = 0;
  441. border->start = -1;
  442. border->valid = FALSE;
  443. }
  444. static FT_Error
  445. ft_stroke_border_get_counts( FT_StrokeBorder border,
  446. FT_UInt *anum_points,
  447. FT_UInt *anum_contours )
  448. {
  449. FT_Error error = FT_Err_Ok;
  450. FT_UInt num_points = 0;
  451. FT_UInt num_contours = 0;
  452. FT_UInt count = border->num_points;
  453. FT_Vector* point = border->points;
  454. FT_Byte* tags = border->tags;
  455. FT_Int in_contour = 0;
  456. for ( ; count > 0; count--, num_points++, point++, tags++ )
  457. {
  458. if ( tags[0] & FT_STROKE_TAG_BEGIN )
  459. {
  460. if ( in_contour != 0 )
  461. goto Fail;
  462. in_contour = 1;
  463. }
  464. else if ( in_contour == 0 )
  465. goto Fail;
  466. if ( tags[0] & FT_STROKE_TAG_END )
  467. {
  468. in_contour = 0;
  469. num_contours++;
  470. }
  471. }
  472. if ( in_contour != 0 )
  473. goto Fail;
  474. border->valid = TRUE;
  475. Exit:
  476. *anum_points = num_points;
  477. *anum_contours = num_contours;
  478. return error;
  479. Fail:
  480. num_points = 0;
  481. num_contours = 0;
  482. goto Exit;
  483. }
  484. static void
  485. ft_stroke_border_export( FT_StrokeBorder border,
  486. FT_Outline* outline )
  487. {
  488. /* copy point locations */
  489. FT_ARRAY_COPY( outline->points + outline->n_points,
  490. border->points,
  491. border->num_points );
  492. /* copy tags */
  493. {
  494. FT_UInt count = border->num_points;
  495. FT_Byte* read = border->tags;
  496. FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points;
  497. for ( ; count > 0; count--, read++, write++ )
  498. {
  499. if ( *read & FT_STROKE_TAG_ON )
  500. *write = FT_CURVE_TAG_ON;
  501. else if ( *read & FT_STROKE_TAG_CUBIC )
  502. *write = FT_CURVE_TAG_CUBIC;
  503. else
  504. *write = FT_CURVE_TAG_CONIC;
  505. }
  506. }
  507. /* copy contours */
  508. {
  509. FT_UInt count = border->num_points;
  510. FT_Byte* tags = border->tags;
  511. FT_Short* write = outline->contours + outline->n_contours;
  512. FT_Short idx = (FT_Short)outline->n_points;
  513. for ( ; count > 0; count--, tags++, idx++ )
  514. {
  515. if ( *tags & FT_STROKE_TAG_END )
  516. {
  517. *write++ = idx;
  518. outline->n_contours++;
  519. }
  520. }
  521. }
  522. outline->n_points = (short)( outline->n_points + border->num_points );
  523. FT_ASSERT( FT_Outline_Check( outline ) == 0 );
  524. }
  525. /***************************************************************************/
  526. /***************************************************************************/
  527. /***** *****/
  528. /***** STROKER *****/
  529. /***** *****/
  530. /***************************************************************************/
  531. /***************************************************************************/
  532. #define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
  533. typedef struct FT_StrokerRec_
  534. {
  535. FT_Angle angle_in;
  536. FT_Angle angle_out;
  537. FT_Vector center;
  538. FT_Bool first_point;
  539. FT_Bool subpath_open;
  540. FT_Angle subpath_angle;
  541. FT_Vector subpath_start;
  542. FT_Stroker_LineCap line_cap;
  543. FT_Stroker_LineJoin line_join;
  544. FT_Fixed miter_limit;
  545. FT_Fixed radius;
  546. FT_Bool valid;
  547. FT_StrokeBorderRec borders[2];
  548. FT_Memory memory;
  549. } FT_StrokerRec;
  550. /* documentation is in ftstroke.h */
  551. FT_EXPORT_DEF( FT_Error )
  552. FT_Stroker_New( FT_Library library,
  553. FT_Stroker *astroker )
  554. {
  555. FT_Error error;
  556. FT_Memory memory;
  557. FT_Stroker stroker;
  558. if ( !library )
  559. return FT_Err_Invalid_Argument;
  560. memory = library->memory;
  561. if ( !FT_NEW( stroker ) )
  562. {
  563. stroker->memory = memory;
  564. ft_stroke_border_init( &stroker->borders[0], memory );
  565. ft_stroke_border_init( &stroker->borders[1], memory );
  566. }
  567. *astroker = stroker;
  568. return error;
  569. }
  570. /* documentation is in ftstroke.h */
  571. FT_EXPORT_DEF( void )
  572. FT_Stroker_Set( FT_Stroker stroker,
  573. FT_Fixed radius,
  574. FT_Stroker_LineCap line_cap,
  575. FT_Stroker_LineJoin line_join,
  576. FT_Fixed miter_limit )
  577. {
  578. stroker->radius = radius;
  579. stroker->line_cap = line_cap;
  580. stroker->line_join = line_join;
  581. stroker->miter_limit = miter_limit;
  582. FT_Stroker_Rewind( stroker );
  583. }
  584. /* documentation is in ftstroke.h */
  585. FT_EXPORT_DEF( void )
  586. FT_Stroker_Rewind( FT_Stroker stroker )
  587. {
  588. if ( stroker )
  589. {
  590. ft_stroke_border_reset( &stroker->borders[0] );
  591. ft_stroke_border_reset( &stroker->borders[1] );
  592. }
  593. }
  594. /* documentation is in ftstroke.h */
  595. FT_EXPORT_DEF( void )
  596. FT_Stroker_Done( FT_Stroker stroker )
  597. {
  598. if ( stroker )
  599. {
  600. FT_Memory memory = stroker->memory;
  601. ft_stroke_border_done( &stroker->borders[0] );
  602. ft_stroke_border_done( &stroker->borders[1] );
  603. stroker->memory = NULL;
  604. FT_FREE( stroker );
  605. }
  606. }
  607. /* creates a circular arc at a corner or cap */
  608. static FT_Error
  609. ft_stroker_arcto( FT_Stroker stroker,
  610. FT_Int side )
  611. {
  612. FT_Angle total, rotate;
  613. FT_Fixed radius = stroker->radius;
  614. FT_Error error = FT_Err_Ok;
  615. FT_StrokeBorder border = stroker->borders + side;
  616. rotate = FT_SIDE_TO_ROTATE( side );
  617. total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
  618. if ( total == FT_ANGLE_PI )
  619. total = -rotate * 2;
  620. error = ft_stroke_border_arcto( border,
  621. &stroker->center,
  622. radius,
  623. stroker->angle_in + rotate,
  624. total );
  625. border->movable = FALSE;
  626. return error;
  627. }
  628. /* adds a cap at the end of an opened path */
  629. static FT_Error
  630. ft_stroker_cap( FT_Stroker stroker,
  631. FT_Angle angle,
  632. FT_Int side )
  633. {
  634. FT_Error error = FT_Err_Ok;
  635. if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
  636. {
  637. /* add a round cap */
  638. stroker->angle_in = angle;
  639. stroker->angle_out = angle + FT_ANGLE_PI;
  640. error = ft_stroker_arcto( stroker, side );
  641. }
  642. else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
  643. {
  644. /* add a square cap */
  645. FT_Vector delta, delta2;
  646. FT_Angle rotate = FT_SIDE_TO_ROTATE( side );
  647. FT_Fixed radius = stroker->radius;
  648. FT_StrokeBorder border = stroker->borders + side;
  649. FT_Vector_From_Polar( &delta2, radius, angle + rotate );
  650. FT_Vector_From_Polar( &delta, radius, angle );
  651. delta.x += stroker->center.x + delta2.x;
  652. delta.y += stroker->center.y + delta2.y;
  653. error = ft_stroke_border_lineto( border, &delta, FALSE );
  654. if ( error )
  655. goto Exit;
  656. FT_Vector_From_Polar( &delta2, radius, angle - rotate );
  657. FT_Vector_From_Polar( &delta, radius, angle );
  658. delta.x += delta2.x + stroker->center.x;
  659. delta.y += delta2.y + stroker->center.y;
  660. error = ft_stroke_border_lineto( border, &delta, FALSE );
  661. }
  662. Exit:
  663. return error;
  664. }
  665. /* process an inside corner, i.e. compute intersection */
  666. static FT_Error
  667. ft_stroker_inside( FT_Stroker stroker,
  668. FT_Int side)
  669. {
  670. FT_StrokeBorder border = stroker->borders + side;
  671. FT_Angle phi, theta, rotate;
  672. FT_Fixed length, thcos, sigma;
  673. FT_Vector delta;
  674. FT_Error error = FT_Err_Ok;
  675. rotate = FT_SIDE_TO_ROTATE( side );
  676. /* compute median angle */
  677. theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
  678. if ( theta == FT_ANGLE_PI )
  679. theta = rotate;
  680. else
  681. theta = theta / 2;
  682. phi = stroker->angle_in + theta;
  683. thcos = FT_Cos( theta );
  684. sigma = FT_MulFix( stroker->miter_limit, thcos );
  685. /* TODO: find better criterion to switch off the optimization */
  686. if ( sigma < 0x10000L )
  687. {
  688. FT_Vector_From_Polar( &delta, stroker->radius,
  689. stroker->angle_out + rotate );
  690. delta.x += stroker->center.x;
  691. delta.y += stroker->center.y;
  692. border->movable = FALSE;
  693. }
  694. else
  695. {
  696. length = FT_DivFix( stroker->radius, thcos );
  697. FT_Vector_From_Polar( &delta, length, phi + rotate );
  698. delta.x += stroker->center.x;
  699. delta.y += stroker->center.y;
  700. }
  701. error = ft_stroke_border_lineto( border, &delta, FALSE );
  702. return error;
  703. }
  704. /* process an outside corner, i.e. compute bevel/miter/round */
  705. static FT_Error
  706. ft_stroker_outside( FT_Stroker stroker,
  707. FT_Int side )
  708. {
  709. FT_StrokeBorder border = stroker->borders + side;
  710. FT_Error error;
  711. FT_Angle rotate;
  712. if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
  713. error = ft_stroker_arcto( stroker, side );
  714. else
  715. {
  716. /* this is a mitered or beveled corner */
  717. FT_Fixed sigma, radius = stroker->radius;
  718. FT_Angle theta, phi;
  719. FT_Fixed thcos;
  720. FT_Bool miter;
  721. rotate = FT_SIDE_TO_ROTATE( side );
  722. miter = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
  723. theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
  724. if ( theta == FT_ANGLE_PI )
  725. {
  726. theta = rotate;
  727. phi = stroker->angle_in;
  728. }
  729. else
  730. {
  731. theta = theta / 2;
  732. phi = stroker->angle_in + theta + rotate;
  733. }
  734. thcos = FT_Cos( theta );
  735. sigma = FT_MulFix( stroker->miter_limit, thcos );
  736. if ( sigma >= 0x10000L )
  737. miter = FALSE;
  738. if ( miter ) /* this is a miter (broken angle) */
  739. {
  740. FT_Vector middle, delta;
  741. FT_Fixed length;
  742. /* compute middle point */
  743. FT_Vector_From_Polar( &middle,
  744. FT_MulFix( radius, stroker->miter_limit ),
  745. phi );
  746. middle.x += stroker->center.x;
  747. middle.y += stroker->center.y;
  748. /* compute first angle point */
  749. length = FT_MulFix( radius,
  750. FT_DivFix( 0x10000L - sigma,
  751. ft_pos_abs( FT_Sin( theta ) ) ) );
  752. FT_Vector_From_Polar( &delta, length, phi + rotate );
  753. delta.x += middle.x;
  754. delta.y += middle.y;
  755. error = ft_stroke_border_lineto( border, &delta, FALSE );
  756. if ( error )
  757. goto Exit;
  758. /* compute second angle point */
  759. FT_Vector_From_Polar( &delta, length, phi - rotate );
  760. delta.x += middle.x;
  761. delta.y += middle.y;
  762. error = ft_stroke_border_lineto( border, &delta, FALSE );
  763. if ( error )
  764. goto Exit;
  765. /* finally, add a movable end point */
  766. FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
  767. delta.x += stroker->center.x;
  768. delta.y += stroker->center.y;
  769. error = ft_stroke_border_lineto( border, &delta, TRUE );
  770. }
  771. else /* this is a bevel (intersection) */
  772. {
  773. FT_Fixed length;
  774. FT_Vector delta;
  775. length = FT_DivFix( stroker->radius, thcos );
  776. FT_Vector_From_Polar( &delta, length, phi );
  777. delta.x += stroker->center.x;
  778. delta.y += stroker->center.y;
  779. error = ft_stroke_border_lineto( border, &delta, FALSE );
  780. if ( error )
  781. goto Exit;
  782. /* now add end point */
  783. FT_Vector_From_Polar( &delta, stroker->radius,
  784. stroker->angle_out + rotate );
  785. delta.x += stroker->center.x;
  786. delta.y += stroker->center.y;
  787. error = ft_stroke_border_lineto( border, &delta, TRUE );
  788. }
  789. }
  790. Exit:
  791. return error;
  792. }
  793. static FT_Error
  794. ft_stroker_process_corner( FT_Stroker stroker )
  795. {
  796. FT_Error error = FT_Err_Ok;
  797. FT_Angle turn;
  798. FT_Int inside_side;
  799. turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
  800. /* no specific corner processing is required if the turn is 0 */
  801. if ( turn == 0 )
  802. goto Exit;
  803. /* when we turn to the right, the inside side is 0 */
  804. inside_side = 0;
  805. /* otherwise, the inside side is 1 */
  806. if ( turn < 0 )
  807. inside_side = 1;
  808. /* process the inside side */
  809. error = ft_stroker_inside( stroker, inside_side );
  810. if ( error )
  811. goto Exit;
  812. /* process the outside side */
  813. error = ft_stroker_outside( stroker, 1 - inside_side );
  814. Exit:
  815. return error;
  816. }
  817. /* add two points to the left and right borders corresponding to the */
  818. /* start of the subpath */
  819. static FT_Error
  820. ft_stroker_subpath_start( FT_Stroker stroker,
  821. FT_Angle start_angle )
  822. {
  823. FT_Vector delta;
  824. FT_Vector point;
  825. FT_Error error;
  826. FT_StrokeBorder border;
  827. FT_Vector_From_Polar( &delta, stroker->radius,
  828. start_angle + FT_ANGLE_PI2 );
  829. point.x = stroker->center.x + delta.x;
  830. point.y = stroker->center.y + delta.y;
  831. border = stroker->borders;
  832. error = ft_stroke_border_moveto( border, &point );
  833. if ( error )
  834. goto Exit;
  835. point.x = stroker->center.x - delta.x;
  836. point.y = stroker->center.y - delta.y;
  837. border++;
  838. error = ft_stroke_border_moveto( border, &point );
  839. /* save angle for last cap */
  840. stroker->subpath_angle = start_angle;
  841. stroker->first_point = FALSE;
  842. Exit:
  843. return error;
  844. }
  845. /* documentation is in ftstroke.h */
  846. FT_EXPORT_DEF( FT_Error )
  847. FT_Stroker_LineTo( FT_Stroker stroker,
  848. FT_Vector* to )
  849. {
  850. FT_Error error = FT_Err_Ok;
  851. FT_StrokeBorder border;
  852. FT_Vector delta;
  853. FT_Angle angle;
  854. FT_Int side;
  855. delta.x = to->x - stroker->center.x;
  856. delta.y = to->y - stroker->center.y;
  857. angle = FT_Atan2( delta.x, delta.y );
  858. FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
  859. /* process corner if necessary */
  860. if ( stroker->first_point )
  861. {
  862. /* This is the first segment of a subpath. We need to */
  863. /* add a point to each border at their respective starting */
  864. /* point locations. */
  865. error = ft_stroker_subpath_start( stroker, angle );
  866. if ( error )
  867. goto Exit;
  868. }
  869. else
  870. {
  871. /* process the current corner */
  872. stroker->angle_out = angle;
  873. error = ft_stroker_process_corner( stroker );
  874. if ( error )
  875. goto Exit;
  876. }
  877. /* now add a line segment to both the `inside' and `outside' paths */
  878. for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
  879. {
  880. FT_Vector point;
  881. point.x = to->x + delta.x;
  882. point.y = to->y + delta.y;
  883. error = ft_stroke_border_lineto( border, &point, TRUE );
  884. if ( error )
  885. goto Exit;
  886. delta.x = -delta.x;
  887. delta.y = -delta.y;
  888. }
  889. stroker->angle_in = angle;
  890. stroker->center = *to;
  891. Exit:
  892. return error;
  893. }
  894. /* documentation is in ftstroke.h */
  895. FT_EXPORT_DEF( FT_Error )
  896. FT_Stroker_ConicTo( FT_Stroker stroker,
  897. FT_Vector* control,
  898. FT_Vector* to )
  899. {
  900. FT_Error error = FT_Err_Ok;
  901. FT_Vector bez_stack[34];
  902. FT_Vector* arc;
  903. FT_Vector* limit = bez_stack + 30;
  904. FT_Angle start_angle;
  905. FT_Bool first_arc = TRUE;
  906. arc = bez_stack;
  907. arc[0] = *to;
  908. arc[1] = *control;
  909. arc[2] = stroker->center;
  910. while ( arc >= bez_stack )
  911. {
  912. FT_Angle angle_in, angle_out;
  913. angle_in = angle_out = 0; /* remove compiler warnings */
  914. if ( arc < limit &&
  915. !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
  916. {
  917. ft_conic_split( arc );
  918. arc += 2;
  919. continue;
  920. }
  921. if ( first_arc )
  922. {
  923. first_arc = FALSE;
  924. start_angle = angle_in;
  925. /* process corner if necessary */
  926. if ( stroker->first_point )
  927. error = ft_stroker_subpath_start( stroker, start_angle );
  928. else
  929. {
  930. stroker->angle_out = start_angle;
  931. error = ft_stroker_process_corner( stroker );
  932. }
  933. }
  934. /* the arc's angle is small enough; we can add it directly to each */
  935. /* border */
  936. {
  937. FT_Vector ctrl, end;
  938. FT_Angle theta, phi, rotate;
  939. FT_Fixed length;
  940. FT_Int side;
  941. theta = FT_Angle_Diff( angle_in, angle_out ) / 2;
  942. phi = angle_in + theta;
  943. length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
  944. for ( side = 0; side <= 1; side++ )
  945. {
  946. rotate = FT_SIDE_TO_ROTATE( side );
  947. /* compute control point */
  948. FT_Vector_From_Polar( &ctrl, length, phi + rotate );
  949. ctrl.x += arc[1].x;
  950. ctrl.y += arc[1].y;
  951. /* compute end point */
  952. FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
  953. end.x += arc[0].x;
  954. end.y += arc[0].y;
  955. error = ft_stroke_border_conicto( stroker->borders + side,
  956. &ctrl, &end );
  957. if ( error )
  958. goto Exit;
  959. }
  960. }
  961. arc -= 2;
  962. if ( arc < bez_stack )
  963. stroker->angle_in = angle_out;
  964. }
  965. stroker->center = *to;
  966. Exit:
  967. return error;
  968. }
  969. /* documentation is in ftstroke.h */
  970. FT_EXPORT_DEF( FT_Error )
  971. FT_Stroker_CubicTo( FT_Stroker stroker,
  972. FT_Vector* control1,
  973. FT_Vector* control2,
  974. FT_Vector* to )
  975. {
  976. FT_Error error = FT_Err_Ok;
  977. FT_Vector bez_stack[37];
  978. FT_Vector* arc;
  979. FT_Vector* limit = bez_stack + 32;
  980. FT_Angle start_angle;
  981. FT_Bool first_arc = TRUE;
  982. arc = bez_stack;
  983. arc[0] = *to;
  984. arc[1] = *control2;
  985. arc[2] = *control1;
  986. arc[3] = stroker->center;
  987. while ( arc >= bez_stack )
  988. {
  989. FT_Angle angle_in, angle_mid, angle_out;
  990. /* remove compiler warnings */
  991. angle_in = angle_out = angle_mid = 0;
  992. if ( arc < limit &&
  993. !ft_cubic_is_small_enough( arc, &angle_in,
  994. &angle_mid, &angle_out ) )
  995. {
  996. ft_cubic_split( arc );
  997. arc += 3;
  998. continue;
  999. }
  1000. if ( first_arc )
  1001. {
  1002. first_arc = FALSE;
  1003. /* process corner if necessary */
  1004. start_angle = angle_in;
  1005. if ( stroker->first_point )
  1006. error = ft_stroker_subpath_start( stroker, start_angle );
  1007. else
  1008. {
  1009. stroker->angle_out = start_angle;
  1010. error = ft_stroker_process_corner( stroker );
  1011. }
  1012. if ( error )
  1013. goto Exit;
  1014. }
  1015. /* the arc's angle is small enough; we can add it directly to each */
  1016. /* border */
  1017. {
  1018. FT_Vector ctrl1, ctrl2, end;
  1019. FT_Angle theta1, phi1, theta2, phi2, rotate;
  1020. FT_Fixed length1, length2;
  1021. FT_Int side;
  1022. theta1 = ft_pos_abs( angle_mid - angle_in ) / 2;
  1023. theta2 = ft_pos_abs( angle_out - angle_mid ) / 2;
  1024. phi1 = (angle_mid + angle_in ) / 2;
  1025. phi2 = (angle_mid + angle_out ) / 2;
  1026. length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
  1027. length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) );
  1028. for ( side = 0; side <= 1; side++ )
  1029. {
  1030. rotate = FT_SIDE_TO_ROTATE( side );
  1031. /* compute control points */
  1032. FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
  1033. ctrl1.x += arc[2].x;
  1034. ctrl1.y += arc[2].y;
  1035. FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
  1036. ctrl2.x += arc[1].x;
  1037. ctrl2.y += arc[1].y;
  1038. /* compute end point */
  1039. FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
  1040. end.x += arc[0].x;
  1041. end.y += arc[0].y;
  1042. error = ft_stroke_border_cubicto( stroker->borders + side,
  1043. &ctrl1, &ctrl2, &end );
  1044. if ( error )
  1045. goto Exit;
  1046. }
  1047. }
  1048. arc -= 3;
  1049. if ( arc < bez_stack )
  1050. stroker->angle_in = angle_out;
  1051. }
  1052. stroker->center = *to;
  1053. Exit:
  1054. return error;
  1055. }
  1056. /* documentation is in ftstroke.h */
  1057. FT_EXPORT_DEF( FT_Error )
  1058. FT_Stroker_BeginSubPath( FT_Stroker stroker,
  1059. FT_Vector* to,
  1060. FT_Bool open )
  1061. {
  1062. /* We cannot process the first point, because there is not enough */
  1063. /* information regarding its corner/cap. The latter will be processed */
  1064. /* in the `FT_Stroker_EndSubPath' routine. */
  1065. /* */
  1066. stroker->first_point = TRUE;
  1067. stroker->center = *to;
  1068. stroker->subpath_open = open;
  1069. /* record the subpath start point for each border */
  1070. stroker->subpath_start = *to;
  1071. return FT_Err_Ok;
  1072. }
  1073. static FT_Error
  1074. ft_stroker_add_reverse_left( FT_Stroker stroker,
  1075. FT_Bool open )
  1076. {
  1077. FT_StrokeBorder right = stroker->borders + 0;
  1078. FT_StrokeBorder left = stroker->borders + 1;
  1079. FT_Int new_points;
  1080. FT_Error error = FT_Err_Ok;
  1081. FT_ASSERT( left->start >= 0 );
  1082. new_points = left->num_points - left->start;
  1083. if ( new_points > 0 )
  1084. {
  1085. error = ft_stroke_border_grow( right, (FT_UInt)new_points );
  1086. if ( error )
  1087. goto Exit;
  1088. {
  1089. FT_Vector* dst_point = right->points + right->num_points;
  1090. FT_Byte* dst_tag = right->tags + right->num_points;
  1091. FT_Vector* src_point = left->points + left->num_points - 1;
  1092. FT_Byte* src_tag = left->tags + left->num_points - 1;
  1093. while ( src_point >= left->points + left->start )
  1094. {
  1095. *dst_point = *src_point;
  1096. *dst_tag = *src_tag;
  1097. if ( open )
  1098. dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
  1099. else
  1100. {
  1101. FT_Byte ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
  1102. /* switch begin/end tags if necessary */
  1103. if ( ttag == FT_STROKE_TAG_BEGIN ||
  1104. ttag == FT_STROKE_TAG_END )
  1105. dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
  1106. }
  1107. src_point--;
  1108. src_tag--;
  1109. dst_point++;
  1110. dst_tag++;
  1111. }
  1112. }
  1113. left->num_points = left->start;
  1114. right->num_points += new_points;
  1115. right->movable = FALSE;
  1116. left->movable = FALSE;
  1117. }
  1118. Exit:
  1119. return error;
  1120. }
  1121. /* documentation is in ftstroke.h */
  1122. /* there's a lot of magic in this function! */
  1123. FT_EXPORT_DEF( FT_Error )
  1124. FT_Stroker_EndSubPath( FT_Stroker stroker )
  1125. {
  1126. FT_Error error = FT_Err_Ok;
  1127. if ( stroker->subpath_open )
  1128. {
  1129. FT_StrokeBorder right = stroker->borders;
  1130. /* All right, this is an opened path, we need to add a cap between */
  1131. /* right & left, add the reverse of left, then add a final cap */
  1132. /* between left & right. */
  1133. error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
  1134. if ( error )
  1135. goto Exit;
  1136. /* add reversed points from `left' to `right' */
  1137. error = ft_stroker_add_reverse_left( stroker, TRUE );
  1138. if ( error )
  1139. goto Exit;
  1140. /* now add the final cap */
  1141. stroker->center = stroker->subpath_start;
  1142. error = ft_stroker_cap( stroker,
  1143. stroker->subpath_angle + FT_ANGLE_PI, 0 );
  1144. if ( error )
  1145. goto Exit;
  1146. /* Now end the right subpath accordingly. The left one is */
  1147. /* rewind and doesn't need further processing. */
  1148. ft_stroke_border_close( right, FALSE );
  1149. }
  1150. else
  1151. {
  1152. FT_Angle turn;
  1153. FT_Int inside_side;
  1154. /* close the path if needed */
  1155. if ( stroker->center.x != stroker->subpath_start.x ||
  1156. stroker->center.y != stroker->subpath_start.y )
  1157. {
  1158. error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
  1159. if ( error )
  1160. goto Exit;
  1161. }
  1162. /* process the corner */
  1163. stroker->angle_out = stroker->subpath_angle;
  1164. turn = FT_Angle_Diff( stroker->angle_in,
  1165. stroker->angle_out );
  1166. /* no specific corner processing is required if the turn is 0 */
  1167. if ( turn != 0 )
  1168. {
  1169. /* when we turn to the right, the inside side is 0 */
  1170. inside_side = 0;
  1171. /* otherwise, the inside side is 1 */
  1172. if ( turn < 0 )
  1173. inside_side = 1;
  1174. error = ft_stroker_inside( stroker, inside_side );
  1175. if ( error )
  1176. goto Exit;
  1177. /* process the outside side */
  1178. error = ft_stroker_outside( stroker, 1 - inside_side );
  1179. if ( error )
  1180. goto Exit;
  1181. }
  1182. /* then end our two subpaths */
  1183. ft_stroke_border_close( stroker->borders + 0, TRUE );
  1184. ft_stroke_border_close( stroker->borders + 1, FALSE );
  1185. }
  1186. Exit:
  1187. return error;
  1188. }
  1189. /* documentation is in ftstroke.h */
  1190. FT_EXPORT_DEF( FT_Error )
  1191. FT_Stroker_GetBorderCounts( FT_Stroker stroker,
  1192. FT_StrokerBorder border,
  1193. FT_UInt *anum_points,
  1194. FT_UInt *anum_contours )
  1195. {
  1196. FT_UInt num_points = 0, num_contours = 0;
  1197. FT_Error error;
  1198. if ( !stroker || border > 1 )
  1199. {
  1200. error = FT_Err_Invalid_Argument;
  1201. goto Exit;
  1202. }
  1203. error = ft_stroke_border_get_counts( stroker->borders + border,
  1204. &num_points, &num_contours );
  1205. Exit:
  1206. if ( anum_points )
  1207. *anum_points = num_points;
  1208. if ( anum_contours )
  1209. *anum_contours = num_contours;
  1210. return error;
  1211. }
  1212. /* documentation is in ftstroke.h */
  1213. FT_EXPORT_DEF( FT_Error )
  1214. FT_Stroker_GetCounts( FT_Stroker stroker,
  1215. FT_UInt *anum_points,
  1216. FT_UInt *anum_contours )
  1217. {
  1218. FT_UInt count1, count2, num_points = 0;
  1219. FT_UInt count3, count4, num_contours = 0;
  1220. FT_Error error;
  1221. error = ft_stroke_border_get_counts( stroker->borders + 0,
  1222. &count1, &count2 );
  1223. if ( error )
  1224. goto Exit;
  1225. error = ft_stroke_border_get_counts( stroker->borders + 1,
  1226. &count3, &count4 );
  1227. if ( error )
  1228. goto Exit;
  1229. num_points = count1 + count3;
  1230. num_contours = count2 + count4;
  1231. Exit:
  1232. *anum_points = num_points;
  1233. *anum_contours = num_contours;
  1234. return error;
  1235. }
  1236. /* documentation is in ftstroke.h */
  1237. FT_EXPORT_DEF( void )
  1238. FT_Stroker_ExportBorder( FT_Stroker stroker,
  1239. FT_StrokerBorder border,
  1240. FT_Outline* outline )
  1241. {
  1242. if ( border == FT_STROKER_BORDER_LEFT ||
  1243. border == FT_STROKER_BORDER_RIGHT )
  1244. {
  1245. FT_StrokeBorder sborder = & stroker->borders[border];
  1246. if ( sborder->valid )
  1247. ft_stroke_border_export( sborder, outline );
  1248. }
  1249. }
  1250. /* documentation is in ftstroke.h */
  1251. FT_EXPORT_DEF( void )
  1252. FT_Stroker_Export( FT_Stroker stroker,
  1253. FT_Outline* outline )
  1254. {
  1255. FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
  1256. FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
  1257. }
  1258. /* documentation is in ftstroke.h */
  1259. /*
  1260. * The following is very similar to FT_Outline_Decompose, except
  1261. * that we do support opened paths, and do not scale the outline.
  1262. */
  1263. FT_EXPORT_DEF( FT_Error )
  1264. FT_Stroker_ParseOutline( FT_Stroker stroker,
  1265. FT_Outline* outline,
  1266. FT_Bool opened )
  1267. {
  1268. FT_Vector v_last;
  1269. FT_Vector v_control;
  1270. FT_Vector v_start;
  1271. FT_Vector* point;
  1272. FT_Vector* limit;
  1273. char* tags;
  1274. FT_Error error;
  1275. FT_Int n; /* index of contour in outline */
  1276. FT_UInt first; /* index of first point in contour */
  1277. FT_Int tag; /* current point's state */
  1278. if ( !outline || !stroker )
  1279. return FT_Err_Invalid_Argument;
  1280. FT_Stroker_Rewind( stroker );
  1281. first = 0;
  1282. for ( n = 0; n < outline->n_contours; n++ )
  1283. {
  1284. FT_UInt last; /* index of last point in contour */
  1285. last = outline->contours[n];
  1286. limit = outline->points + last;
  1287. /* skip empty points; we don't stroke these */
  1288. if ( last <= first )
  1289. {
  1290. first = last + 1;
  1291. continue;
  1292. }
  1293. v_start = outline->points[first];
  1294. v_last = outline->points[last];
  1295. v_control = v_start;
  1296. point = outline->points + first;
  1297. tags = outline->tags + first;
  1298. tag = FT_CURVE_TAG( tags[0] );
  1299. /* A contour cannot start with a cubic control point! */
  1300. if ( tag == FT_CURVE_TAG_CUBIC )
  1301. goto Invalid_Outline;
  1302. /* check first point to determine origin */
  1303. if ( tag == FT_CURVE_TAG_CONIC )
  1304. {
  1305. /* First point is conic control. Yes, this happens. */
  1306. if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
  1307. {
  1308. /* start at last point if it is on the curve */
  1309. v_start = v_last;
  1310. limit--;
  1311. }
  1312. else
  1313. {
  1314. /* if both first and last points are conic, */
  1315. /* start at their middle and record its position */
  1316. /* for closure */
  1317. v_start.x = ( v_start.x + v_last.x ) / 2;
  1318. v_start.y = ( v_start.y + v_last.y ) / 2;
  1319. v_last = v_start;
  1320. }
  1321. point--;
  1322. tags--;
  1323. }
  1324. error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
  1325. if ( error )
  1326. goto Exit;
  1327. while ( point < limit )
  1328. {
  1329. point++;
  1330. tags++;
  1331. tag = FT_CURVE_TAG( tags[0] );
  1332. switch ( tag )
  1333. {
  1334. case FT_CURVE_TAG_ON: /* emit a single line_to */
  1335. {
  1336. FT_Vector vec;
  1337. vec.x = point->x;
  1338. vec.y = point->y;
  1339. error = FT_Stroker_LineTo( stroker, &vec );
  1340. if ( error )
  1341. goto Exit;
  1342. continue;
  1343. }
  1344. case FT_CURVE_TAG_CONIC: /* consume conic arcs */
  1345. v_control.x = point->x;
  1346. v_control.y = point->y;
  1347. Do_Conic:
  1348. if ( point < limit )
  1349. {
  1350. FT_Vector vec;
  1351. FT_Vector v_middle;
  1352. point++;
  1353. tags++;
  1354. tag = FT_CURVE_TAG( tags[0] );
  1355. vec = point[0];
  1356. if ( tag == FT_CURVE_TAG_ON )
  1357. {
  1358. error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
  1359. if ( error )
  1360. goto Exit;
  1361. continue;
  1362. }
  1363. if ( tag != FT_CURVE_TAG_CONIC )
  1364. goto Invalid_Outline;
  1365. v_middle.x = ( v_control.x + vec.x ) / 2;
  1366. v_middle.y = ( v_control.y + vec.y ) / 2;
  1367. error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
  1368. if ( error )
  1369. goto Exit;
  1370. v_control = vec;
  1371. goto Do_Conic;
  1372. }
  1373. error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
  1374. goto Close;
  1375. default: /* FT_CURVE_TAG_CUBIC */
  1376. {
  1377. FT_Vector vec1, vec2;
  1378. if ( point + 1 > limit ||
  1379. FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
  1380. goto Invalid_Outline;
  1381. point += 2;
  1382. tags += 2;
  1383. vec1 = point[-2];
  1384. vec2 = point[-1];
  1385. if ( point <= limit )
  1386. {
  1387. FT_Vector vec;
  1388. vec = point[0];
  1389. error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
  1390. if ( error )
  1391. goto Exit;
  1392. continue;
  1393. }
  1394. error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
  1395. goto Close;
  1396. }
  1397. }
  1398. }
  1399. Close:
  1400. if ( error )
  1401. goto Exit;
  1402. error = FT_Stroker_EndSubPath( stroker );
  1403. if ( error )
  1404. goto Exit;
  1405. first = last + 1;
  1406. }
  1407. return FT_Err_Ok;
  1408. Exit:
  1409. return error;
  1410. Invalid_Outline:
  1411. return FT_Err_Invalid_Outline;
  1412. }
  1413. extern const FT_Glyph_Class ft_outline_glyph_class;
  1414. /* documentation is in ftstroke.h */
  1415. FT_EXPORT_DEF( FT_Error )
  1416. FT_Glyph_Stroke( FT_Glyph *pglyph,
  1417. FT_Stroker stroker,
  1418. FT_Bool destroy )
  1419. {
  1420. FT_Error error = FT_Err_Invalid_Argument;
  1421. FT_Glyph glyph = NULL;
  1422. if ( pglyph == NULL )
  1423. goto Exit;
  1424. glyph = *pglyph;
  1425. if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
  1426. goto Exit;
  1427. {
  1428. FT_Glyph copy;
  1429. error = FT_Glyph_Copy( glyph, &copy );
  1430. if ( error )
  1431. goto Exit;
  1432. glyph = copy;
  1433. }
  1434. {
  1435. FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph;
  1436. FT_Outline* outline = &oglyph->outline;
  1437. FT_UInt num_points, num_contours;
  1438. error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
  1439. if ( error )
  1440. goto Fail;
  1441. (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
  1442. FT_Outline_Done( glyph->library, outline );
  1443. error = FT_Outline_New( glyph->library,
  1444. num_points, num_contours, outline );
  1445. if ( error )
  1446. goto Fail;
  1447. outline->n_points = 0;
  1448. outline->n_contours = 0;
  1449. FT_Stroker_Export( stroker, outline );
  1450. }
  1451. if ( destroy )
  1452. FT_Done_Glyph( *pglyph );
  1453. *pglyph = glyph;
  1454. goto Exit;
  1455. Fail:
  1456. FT_Done_Glyph( glyph );
  1457. glyph = NULL;
  1458. if ( !destroy )
  1459. *pglyph = NULL;
  1460. Exit:
  1461. return error;
  1462. }
  1463. /* documentation is in ftstroke.h */
  1464. FT_EXPORT_DEF( FT_Error )
  1465. FT_Glyph_StrokeBorder( FT_Glyph *pglyph,
  1466. FT_Stroker stroker,
  1467. FT_Bool inside,
  1468. FT_Bool destroy

Large files files are truncated, but you can click here to view the full file