/src/utils/path2d.c

https://github.com/svettom/gpac · C · 1359 lines · 1111 code · 152 blank · 96 comment · 303 complexity · e54eed4f332ee3af2b109cf4ef8beb8c MD5 · raw file

  1. /*
  2. * GPAC - Multimedia Framework C SDK
  3. *
  4. * Authors: Jean Le Feuvre
  5. * Copyright (c) Telecom ParisTech 2000-2012
  6. * All rights reserved
  7. *
  8. * This file is part of GPAC / common tools sub-project
  9. *
  10. * GPAC is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU Lesser General Public License as published by
  12. * the Free Software Foundation; either version 2, or (at your option)
  13. * any later version.
  14. *
  15. * GPAC is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; see the file COPYING. If not, write to
  22. * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  23. *
  24. */
  25. #include "../../include/gpac/path2d.h"
  26. GF_EXPORT
  27. GF_Path *gf_path_new()
  28. {
  29. GF_Path *gp;
  30. GF_SAFEALLOC(gp, GF_Path);
  31. gp->fineness = FIX_ONE;
  32. return gp;
  33. }
  34. GF_EXPORT
  35. void gf_path_reset(GF_Path *gp)
  36. {
  37. Fixed fineness;
  38. u32 flags;
  39. if (!gp) return;
  40. if (gp->contours) gf_free(gp->contours);
  41. if (gp->tags) gf_free(gp->tags);
  42. if (gp->points) gf_free(gp->points);
  43. fineness = gp->fineness ? gp->fineness : FIX_ONE;
  44. flags = gp->flags;
  45. memset(gp, 0, sizeof(GF_Path));
  46. gp->flags = flags | GF_PATH_FLATTENED | GF_PATH_BBOX_DIRTY;
  47. gp->fineness = fineness;
  48. }
  49. GF_EXPORT
  50. GF_Path *gf_path_clone(GF_Path *gp)
  51. {
  52. GF_Path *dst;
  53. GF_SAFEALLOC(dst, GF_Path);
  54. if (!dst) return NULL;
  55. dst->contours = (u32 *)gf_malloc(sizeof(u32)*gp->n_contours);
  56. if (!dst->contours) { gf_free(dst); return NULL; }
  57. dst->points = (GF_Point2D *) gf_malloc(sizeof(GF_Point2D)*gp->n_points);
  58. if (!dst->points) { gf_free(dst->contours); gf_free(dst); return NULL; }
  59. dst->tags = (u8 *) gf_malloc(sizeof(u8)*gp->n_points);
  60. if (!dst->tags) { gf_free(dst->points); gf_free(dst->contours); gf_free(dst); return NULL; }
  61. memcpy(dst->contours, gp->contours, sizeof(u32)*gp->n_contours);
  62. dst->n_contours = gp->n_contours;
  63. memcpy(dst->points, gp->points, sizeof(GF_Point2D)*gp->n_points);
  64. memcpy(dst->tags, gp->tags, sizeof(u8)*gp->n_points);
  65. dst->n_alloc_points = dst->n_points = gp->n_points;
  66. dst->flags = gp->flags;
  67. dst->bbox = gp->bbox;
  68. dst->fineness = gp->fineness;
  69. return dst;
  70. }
  71. GF_EXPORT
  72. void gf_path_del(GF_Path *gp)
  73. {
  74. if (!gp) return;
  75. if (gp->contours) gf_free(gp->contours);
  76. if (gp->tags) gf_free(gp->tags);
  77. if (gp->points) gf_free(gp->points);
  78. gf_free(gp);
  79. }
  80. #define GF_2D_REALLOC(_gp) \
  81. if (_gp->n_alloc_points < _gp->n_points+3) { \
  82. _gp->n_alloc_points = (_gp->n_alloc_points<5) ? 10 : (_gp->n_alloc_points*3/2); \
  83. _gp->points = (GF_Point2D *)gf_realloc(_gp->points, sizeof(GF_Point2D)*(_gp->n_alloc_points)); \
  84. _gp->tags = (u8 *) gf_realloc(_gp->tags, sizeof(u8)*(_gp->n_alloc_points)); \
  85. } \
  86. GF_EXPORT
  87. GF_Err gf_path_add_move_to(GF_Path *gp, Fixed x, Fixed y)
  88. {
  89. if (!gp) return GF_BAD_PARAM;
  90. #if 0
  91. /*skip empty paths*/
  92. if ((gp->n_contours>=2) && (gp->contours[gp->n_contours-2]+1==gp->contours[gp->n_contours-1])) {
  93. gp->points[gp->n_points].x = x;
  94. gp->points[gp->n_points].y = y;
  95. return GF_OK;
  96. }
  97. #endif
  98. gp->contours = (u32 *) gf_realloc(gp->contours, sizeof(u32)*(gp->n_contours+1));
  99. GF_2D_REALLOC(gp)
  100. gp->points[gp->n_points].x = x;
  101. gp->points[gp->n_points].y = y;
  102. gp->tags[gp->n_points] = 1;
  103. /*set end point*/
  104. gp->contours[gp->n_contours] = gp->n_points;
  105. /*new contour*/
  106. gp->n_contours++;
  107. gp->n_points++;
  108. gp->flags |= GF_PATH_BBOX_DIRTY;
  109. return GF_OK;
  110. }
  111. GF_EXPORT
  112. GF_Err gf_path_add_move_to_vec(GF_Path *gp, GF_Point2D *pt) { return gf_path_add_move_to(gp, pt->x, pt->y); }
  113. GF_EXPORT
  114. GF_Err gf_path_add_line_to(GF_Path *gp, Fixed x, Fixed y)
  115. {
  116. if (!gp || !gp->n_contours) return GF_BAD_PARAM;
  117. /*we allow line to same point as move (seen in SVG sequences) - striking will make a point*/
  118. GF_2D_REALLOC(gp)
  119. gp->points[gp->n_points].x = x;
  120. gp->points[gp->n_points].y = y;
  121. gp->tags[gp->n_points] = 1;
  122. /*set end point*/
  123. gp->contours[gp->n_contours-1] = gp->n_points;
  124. gp->n_points++;
  125. gp->flags |= GF_PATH_BBOX_DIRTY;
  126. return GF_OK;
  127. }
  128. GF_EXPORT
  129. GF_Err gf_path_add_line_to_vec(GF_Path *gp, GF_Point2D *pt) { return gf_path_add_line_to(gp, pt->x, pt->y); }
  130. GF_EXPORT
  131. GF_Err gf_path_close(GF_Path *gp)
  132. {
  133. Fixed diff;
  134. GF_Point2D start, end;
  135. if (!gp || !gp->n_contours) return GF_BAD_PARAM;
  136. if (gp->n_contours<=1) start = gp->points[0];
  137. else start = gp->points[gp->contours[gp->n_contours-2] + 1];
  138. end = gp->points[gp->n_points-1];
  139. end.x -= start.x;
  140. end.y -= start.y;
  141. diff = gf_mulfix(end.x, end.x) + gf_mulfix(end.y, end.y);
  142. if (ABS(diff) > FIX_ONE/1000) {
  143. GF_Err e = gf_path_add_line_to(gp, start.x, start.y);
  144. if (e) return e;
  145. }
  146. gp->tags[gp->n_points-1] = GF_PATH_CLOSE;
  147. return GF_OK;
  148. }
  149. GF_EXPORT
  150. GF_Err gf_path_add_cubic_to(GF_Path *gp, Fixed c1_x, Fixed c1_y, Fixed c2_x, Fixed c2_y, Fixed x, Fixed y)
  151. {
  152. if (!gp || !gp->n_contours) return GF_BAD_PARAM;
  153. GF_2D_REALLOC(gp)
  154. gp->points[gp->n_points].x = c1_x;
  155. gp->points[gp->n_points].y = c1_y;
  156. gp->tags[gp->n_points] = GF_PATH_CURVE_CUBIC;
  157. gp->n_points++;
  158. gp->points[gp->n_points].x = c2_x;
  159. gp->points[gp->n_points].y = c2_y;
  160. gp->tags[gp->n_points] = GF_PATH_CURVE_CUBIC;
  161. gp->n_points++;
  162. gp->points[gp->n_points].x = x;
  163. gp->points[gp->n_points].y = y;
  164. gp->tags[gp->n_points] = GF_PATH_CURVE_ON;
  165. /*set end point*/
  166. gp->contours[gp->n_contours-1] = gp->n_points;
  167. gp->n_points++;
  168. gp->flags |= GF_PATH_BBOX_DIRTY;
  169. gp->flags &= ~GF_PATH_FLATTENED;
  170. return GF_OK;
  171. }
  172. GF_EXPORT
  173. GF_Err gf_path_add_cubic_to_vec(GF_Path *gp, GF_Point2D *c1, GF_Point2D *c2, GF_Point2D *pt)
  174. {
  175. return gf_path_add_cubic_to(gp, c1->x, c1->y, c2->x, c2->y, pt->x, pt->y);
  176. }
  177. GF_EXPORT
  178. GF_Err gf_path_add_quadratic_to(GF_Path *gp, Fixed c_x, Fixed c_y, Fixed x, Fixed y)
  179. {
  180. if (!gp || !gp->n_contours) return GF_BAD_PARAM;
  181. GF_2D_REALLOC(gp)
  182. gp->points[gp->n_points].x = c_x;
  183. gp->points[gp->n_points].y = c_y;
  184. gp->tags[gp->n_points] = GF_PATH_CURVE_CONIC;
  185. gp->n_points++;
  186. gp->points[gp->n_points].x = x;
  187. gp->points[gp->n_points].y = y;
  188. gp->tags[gp->n_points] = GF_PATH_CURVE_ON;
  189. /*set end point*/
  190. gp->contours[gp->n_contours-1] = gp->n_points;
  191. gp->n_points++;
  192. gp->flags |= GF_PATH_BBOX_DIRTY;
  193. gp->flags &= ~GF_PATH_FLATTENED;
  194. return GF_OK;
  195. }
  196. GF_EXPORT
  197. GF_Err gf_path_add_quadratic_to_vec(GF_Path *gp, GF_Point2D *c, GF_Point2D *pt)
  198. {
  199. return gf_path_add_quadratic_to(gp, c->x, c->y, pt->x, pt->y);
  200. }
  201. /*adds rectangle centered on cx, cy*/
  202. GF_EXPORT
  203. GF_Err gf_path_add_rect_center(GF_Path *gp, Fixed cx, Fixed cy, Fixed w, Fixed h)
  204. {
  205. GF_Err e = gf_path_add_move_to(gp, cx - w/2, cy - h/2);
  206. if (e) return e;
  207. e = gf_path_add_line_to(gp, cx + w/2, cy - h/2);
  208. if (e) return e;
  209. e = gf_path_add_line_to(gp, cx + w/2, cy + h/2);
  210. if (e) return e;
  211. e = gf_path_add_line_to(gp, cx - w/2, cy + h/2);
  212. if (e) return e;
  213. return gf_path_close(gp);
  214. }
  215. GF_EXPORT
  216. GF_Err gf_path_add_rect(GF_Path *gp, Fixed ox, Fixed oy, Fixed w, Fixed h)
  217. {
  218. GF_Err e = gf_path_add_move_to(gp, ox, oy);
  219. if (e) return e;
  220. e = gf_path_add_line_to(gp, ox + w, oy);
  221. if (e) return e;
  222. e = gf_path_add_line_to(gp, ox+w, oy-h);
  223. if (e) return e;
  224. e = gf_path_add_line_to(gp, ox, oy-h);
  225. if (e) return e;
  226. return gf_path_close(gp);
  227. }
  228. #define GF_2D_DEFAULT_RES 64
  229. GF_EXPORT
  230. GF_Err gf_path_add_ellipse(GF_Path *gp, Fixed cx, Fixed cy, Fixed a_axis, Fixed b_axis)
  231. {
  232. GF_Err e;
  233. Fixed _vx, _vy, cur;
  234. u32 i;
  235. a_axis /= 2;
  236. b_axis /= 2;
  237. e = gf_path_add_move_to(gp, cx+a_axis, cy);
  238. if (e) return e;
  239. for (i=1; i<GF_2D_DEFAULT_RES; i++) {
  240. cur = GF_2PI*i/GF_2D_DEFAULT_RES;
  241. _vx = gf_mulfix(a_axis, gf_cos(cur) );
  242. _vy = gf_mulfix(b_axis, gf_sin(cur) );
  243. e = gf_path_add_line_to(gp, _vx + cx, _vy + cy);
  244. if (e) return e;
  245. }
  246. return gf_path_close(gp);
  247. }
  248. GF_Err gf_path_add_subpath(GF_Path *gp, GF_Path *src, GF_Matrix2D *mx)
  249. {
  250. u32 i;
  251. if (!src) return GF_OK;
  252. gp->contours = gf_realloc(gp->contours, sizeof(u32) * (gp->n_contours + src->n_contours));
  253. if (!gp->contours) return GF_OUT_OF_MEM;
  254. for (i=0; i<src->n_contours; i++) {
  255. gp->contours[i+gp->n_contours] = src->contours[i] + gp->n_points;
  256. }
  257. gp->n_contours += src->n_contours;
  258. gp->n_alloc_points += src->n_alloc_points;
  259. gp->points = gf_realloc(gp->points, sizeof(GF_Point2D)*gp->n_alloc_points);
  260. if (!gp->points) return GF_OUT_OF_MEM;
  261. gp->tags = gf_realloc(gp->tags, sizeof(u8)*gp->n_alloc_points);
  262. if (!gp->tags) return GF_OUT_OF_MEM;
  263. memcpy(gp->points + gp->n_points, src->points, sizeof(GF_Point2D)*src->n_points);
  264. if (mx) {
  265. for (i=0;i<src->n_points; i++) {
  266. gf_mx2d_apply_coords(mx, &gp->points[i+gp->n_points].x, &gp->points[i+gp->n_points].y);
  267. }
  268. }
  269. memcpy(gp->tags + gp->n_points, src->tags, sizeof(u8)*src->n_points);
  270. gp->n_points += src->n_points;
  271. gf_rect_union(&gp->bbox, &src->bbox);
  272. if (!(src->flags & GF_PATH_FLATTENED)) gp->flags &= ~GF_PATH_FLATTENED;
  273. if (src->flags & GF_PATH_BBOX_DIRTY) gp->flags |= GF_PATH_BBOX_DIRTY;
  274. return GF_OK;
  275. }
  276. /*generic N-bezier*/
  277. static void NBezier(GF_Point2D *pts, s32 n, Double mu, GF_Point2D *pt_out)
  278. {
  279. s32 k,kn,nn,nkn;
  280. Double blend, muk, munk;
  281. pt_out->x = pt_out->y = 0;
  282. muk = 1;
  283. munk = pow(1-mu,(Double)n);
  284. for (k=0;k<=n;k++) {
  285. nn = n;
  286. kn = k;
  287. nkn = n - k;
  288. blend = muk * munk;
  289. muk *= mu;
  290. munk /= (1-mu);
  291. while (nn >= 1) {
  292. blend *= nn;
  293. nn--;
  294. if (kn > 1) {
  295. blend /= (double)kn;
  296. kn--;
  297. }
  298. if (nkn > 1) {
  299. blend /= (double)nkn;
  300. nkn--;
  301. }
  302. }
  303. pt_out->x += gf_mulfix(pts[k].x, FLT2FIX(blend));
  304. pt_out->y += gf_mulfix(pts[k].y, FLT2FIX(blend));
  305. }
  306. }
  307. static void gf_add_n_bezier(GF_Path *gp, GF_Point2D *newpts, u32 nbPoints)
  308. {
  309. Double mu;
  310. u32 numPoints, i;
  311. GF_Point2D end;
  312. numPoints = (u32) FIX2INT(GF_2D_DEFAULT_RES * gp->fineness);
  313. mu = 0.0;
  314. if (numPoints) mu = 1/(Double)numPoints;
  315. for (i=1; i<numPoints; i++) {
  316. NBezier(newpts, nbPoints - 1, i*mu, &end);
  317. gf_path_add_line_to(gp, end.x, end.y);
  318. }
  319. gf_path_add_line_to(gp, newpts[nbPoints-1].x, newpts[nbPoints-1].y);
  320. }
  321. GF_EXPORT
  322. GF_Err gf_path_add_bezier(GF_Path *gp, GF_Point2D *pts, u32 nbPoints)
  323. {
  324. GF_Point2D *newpts;
  325. if (!gp->n_points) return GF_BAD_PARAM;
  326. newpts = (GF_Point2D *) gf_malloc(sizeof(GF_Point2D) * (nbPoints+1));
  327. newpts[0] = gp->points[gp->n_points-1];
  328. memcpy(&newpts[1], pts, sizeof(GF_Point2D) * nbPoints);
  329. gf_add_n_bezier(gp, newpts, nbPoints + 1);
  330. gf_free(newpts);
  331. return GF_OK;
  332. }
  333. GF_EXPORT
  334. GF_Err gf_path_add_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed fa_x, Fixed fa_y, Fixed fb_x, Fixed fb_y, Bool cw)
  335. {
  336. GF_Matrix2D mat, inv;
  337. Fixed angle, start_angle, end_angle, sweep, axis_w, axis_h, tmp, cx, cy, _vx, _vy, start_x, start_y;
  338. s32 i, num_steps;
  339. if (!gp->n_points) return GF_BAD_PARAM;
  340. start_x = gp->points[gp->n_points-1].x;
  341. start_y = gp->points[gp->n_points-1].y;
  342. cx = (fb_x + fa_x)/2;
  343. cy = (fb_y + fa_y)/2;
  344. angle = gf_atan2(fb_y-fa_y, fb_x-fa_x);
  345. gf_mx2d_init(mat);
  346. gf_mx2d_add_rotation(&mat, 0, 0, angle);
  347. gf_mx2d_add_translation(&mat, cx, cy);
  348. gf_mx2d_copy(inv, mat);
  349. gf_mx2d_inverse(&inv);
  350. gf_mx2d_apply_coords(&inv, &start_x, &start_y);
  351. gf_mx2d_apply_coords(&inv, &end_x, &end_y);
  352. gf_mx2d_apply_coords(&inv, &fa_x, &fa_y);
  353. gf_mx2d_apply_coords(&inv, &fb_x, &fb_y);
  354. //start angle and end angle
  355. start_angle = gf_atan2(start_y, start_x);
  356. end_angle = gf_atan2(end_y, end_x);
  357. tmp = gf_mulfix((start_x - fa_x), (start_x - fa_x)) + gf_mulfix((start_y - fa_y), (start_y - fa_y));
  358. axis_w = gf_sqrt(tmp);
  359. tmp = gf_mulfix((start_x - fb_x) , (start_x - fb_x)) + gf_mulfix((start_y - fb_y), (start_y - fb_y));
  360. axis_w += gf_sqrt(tmp);
  361. axis_w /= 2;
  362. axis_h = gf_sqrt(gf_mulfix(axis_w, axis_w) - gf_mulfix(fa_x,fa_x));
  363. sweep = end_angle - start_angle;
  364. if (cw) {
  365. if (sweep>0) sweep -= 2*GF_PI;
  366. } else {
  367. if (sweep<0) sweep += 2*GF_PI;
  368. }
  369. num_steps = GF_2D_DEFAULT_RES/2;
  370. for (i=1; i<=num_steps; i++) {
  371. angle = start_angle + sweep*i/num_steps;
  372. _vx = gf_mulfix(axis_w, gf_cos(angle));
  373. _vy = gf_mulfix(axis_h, gf_sin(angle));
  374. /*re-invert*/
  375. gf_mx2d_apply_coords(&mat, &_vx, &_vy);
  376. gf_path_add_line_to(gp, _vx, _vy);
  377. }
  378. return GF_OK;
  379. }
  380. GF_EXPORT
  381. GF_Err gf_path_add_svg_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed r_x, Fixed r_y, Fixed x_axis_rotation, Bool large_arc_flag, Bool sweep_flag)
  382. {
  383. Fixed start_x, start_y;
  384. Fixed xmid,ymid;
  385. Fixed xmidp,ymidp;
  386. Fixed xmidpsq,ymidpsq;
  387. Fixed phi, cos_phi, sin_phi;
  388. Fixed c_x, c_y;
  389. Fixed cxp, cyp;
  390. Fixed scale;
  391. Fixed rxsq, rysq;
  392. Fixed start_angle, sweep_angle;
  393. Fixed radius_scale;
  394. Fixed vx, vy, normv;
  395. Fixed ux, uy, normu;
  396. Fixed sign;
  397. u32 i, num_steps;
  398. if (!gp->n_points) return GF_BAD_PARAM;
  399. if (!r_x || !r_y) {
  400. gf_path_add_line_to(gp, end_x, end_y);
  401. return GF_OK;
  402. }
  403. if (r_x < 0) r_x = -r_x;
  404. if (r_y < 0) r_y = -r_y;
  405. start_x = gp->points[gp->n_points-1].x;
  406. start_y = gp->points[gp->n_points-1].y;
  407. phi = gf_mulfix(gf_divfix(x_axis_rotation, 180), GF_PI);
  408. cos_phi = gf_cos(phi);
  409. sin_phi = gf_sin(phi);
  410. xmid = (start_x - end_x)/2;
  411. ymid = (start_y - end_y)/2;
  412. if (!xmid && !ymid) {
  413. gf_path_add_line_to(gp, end_x, end_y);
  414. return GF_OK;
  415. }
  416. xmidp = gf_mulfix(cos_phi, xmid) + gf_mulfix(sin_phi, ymid);
  417. ymidp = gf_mulfix(-sin_phi, xmid) + gf_mulfix(cos_phi, ymid);
  418. xmidpsq = gf_mulfix(xmidp, xmidp);
  419. ymidpsq = gf_mulfix(ymidp, ymidp);
  420. rxsq = gf_mulfix(r_x, r_x);
  421. rysq = gf_mulfix(r_y, r_y);
  422. assert(rxsq && rxsq);
  423. radius_scale = gf_divfix(xmidpsq, rxsq) + gf_divfix(ymidpsq, rysq);
  424. if (radius_scale > FIX_ONE) {
  425. r_x = gf_mulfix(gf_sqrt(radius_scale), r_x);
  426. r_y = gf_mulfix(gf_sqrt(radius_scale), r_y);
  427. rxsq = gf_mulfix(r_x, r_x);
  428. rysq = gf_mulfix(r_y, r_y);
  429. }
  430. #if 0
  431. /* Old code with overflow problems in fixed point,
  432. sign was sometimes negative (cf tango SVG icons appointment-new.svg)*/
  433. sign = gf_mulfix(rxsq,ymidpsq) + gf_mulfix(rysq, xmidpsq);
  434. scale = FIX_ONE;
  435. /*FIXME - what if scale is 0 ??*/
  436. if (sign) scale = gf_divfix(
  437. (gf_mulfix(rxsq,rysq) - gf_mulfix(rxsq, ymidpsq) - gf_mulfix(rysq,xmidpsq)),
  438. sign
  439. );
  440. #else
  441. /* New code: the sign variable computation is split into simpler cases and
  442. the expression is divided by rxsq to reduce the range */
  443. if ((rxsq == 0 || ymidpsq ==0) && (rysq == 0 || xmidpsq == 0)) {
  444. scale = FIX_ONE;
  445. } else if (rxsq == 0 || ymidpsq ==0) {
  446. scale = gf_divfix(rxsq,xmidpsq) - FIX_ONE;
  447. } else if (rysq == 0 || xmidpsq == 0) {
  448. scale = gf_divfix(rysq,ymidpsq) - FIX_ONE;
  449. } else {
  450. Fixed tmp;
  451. tmp = gf_mulfix(gf_divfix(rysq, rxsq), xmidpsq);
  452. sign = ymidpsq + tmp;
  453. scale = gf_divfix((rysq - ymidpsq - tmp),sign);
  454. }
  455. #endif
  456. /* precision problem may lead to negative value around zero, we need to take care of it before sqrt */
  457. scale = gf_sqrt(ABS(scale));
  458. cxp = gf_mulfix(scale, gf_divfix(gf_mulfix(r_x, ymidp),r_y));
  459. cyp = gf_mulfix(scale, -gf_divfix(gf_mulfix(r_y, xmidp),r_x));
  460. cxp = (large_arc_flag == sweep_flag ? - cxp : cxp);
  461. cyp = (large_arc_flag == sweep_flag ? - cyp : cyp);
  462. c_x = gf_mulfix(cos_phi, cxp) - gf_mulfix(sin_phi, cyp) + (start_x + end_x)/2;
  463. c_y = gf_mulfix(sin_phi, cxp) + gf_mulfix(cos_phi, cyp) + (start_y + end_y)/2;
  464. ux = FIX_ONE;
  465. uy = 0;
  466. normu = FIX_ONE;
  467. vx = gf_divfix(xmidp-cxp,r_x);
  468. vy = gf_divfix(ymidp-cyp,r_y);
  469. normv = gf_sqrt(gf_mulfix(vx, vx) + gf_mulfix(vy,vy));
  470. sign = vy;
  471. start_angle = gf_acos(gf_divfix(vx,normv));
  472. start_angle = (sign > 0 ? start_angle: -start_angle);
  473. ux = vx;
  474. uy = vy;
  475. normu = normv;
  476. vx = gf_divfix(-xmidp-cxp,r_x);
  477. vy = gf_divfix(-ymidp-cyp,r_y);
  478. normu = gf_sqrt(gf_mulfix(ux, ux) + gf_mulfix(uy,uy));
  479. sign = gf_mulfix(ux, vy) - gf_mulfix(uy, vx);
  480. sweep_angle = gf_divfix( gf_mulfix(ux,vx) + gf_mulfix(uy, vy), gf_mulfix(normu, normv) );
  481. /*numerical stability safety*/
  482. sweep_angle = MAX(-FIX_ONE, MIN(sweep_angle, FIX_ONE));
  483. sweep_angle = gf_acos(sweep_angle);
  484. sweep_angle = (sign > 0 ? sweep_angle: -sweep_angle);
  485. if (sweep_flag == 0) {
  486. if (sweep_angle > 0) sweep_angle -= GF_2PI;
  487. } else {
  488. if (sweep_angle < 0) sweep_angle += GF_2PI;
  489. }
  490. num_steps = GF_2D_DEFAULT_RES/2;
  491. for (i=1; i<=num_steps; i++) {
  492. Fixed _vx, _vy;
  493. Fixed _vxp, _vyp;
  494. Fixed angle = start_angle + sweep_angle*i/num_steps;
  495. _vx = gf_mulfix(r_x, gf_cos(angle));
  496. _vy = gf_mulfix(r_y, gf_sin(angle));
  497. _vxp = gf_mulfix(cos_phi, _vx) - gf_mulfix(sin_phi, _vy) + c_x;
  498. _vyp = gf_mulfix(sin_phi, _vx) + gf_mulfix(cos_phi, _vy) + c_y;
  499. gf_path_add_line_to(gp, _vxp, _vyp);
  500. }
  501. return GF_OK;
  502. }
  503. GF_EXPORT
  504. GF_Err gf_path_add_arc(GF_Path *gp, Fixed radius, Fixed start_angle, Fixed end_angle, u32 close_type)
  505. {
  506. GF_Err e;
  507. Fixed _vx, _vy, step, cur;
  508. s32 i, do_run;
  509. step = (end_angle - start_angle) / (GF_2D_DEFAULT_RES);
  510. radius *= 2;
  511. /*pie*/
  512. i=0;
  513. if (close_type==2) {
  514. gf_path_add_move_to(gp, 0, 0);
  515. i=1;
  516. }
  517. do_run = 1;
  518. cur=start_angle;
  519. while (do_run) {
  520. if (cur>=end_angle) {
  521. do_run = 0;
  522. cur = end_angle;
  523. }
  524. _vx = gf_mulfix(radius, gf_cos(cur));
  525. _vy = gf_mulfix(radius, gf_sin(cur));
  526. if (!i) {
  527. e = gf_path_add_move_to(gp, _vx, _vy);
  528. i = 1;
  529. } else {
  530. e = gf_path_add_line_to(gp, _vx, _vy);
  531. }
  532. if (e) return e;
  533. cur+=step;
  534. }
  535. if (close_type) e = gf_path_close(gp);
  536. return e;
  537. }
  538. GF_EXPORT
  539. GF_Err gf_path_get_control_bounds(GF_Path *gp, GF_Rect *rc)
  540. {
  541. GF_Point2D *pt, *end;
  542. Fixed xMin, xMax, yMin, yMax;
  543. if (!gp || !rc) return GF_BAD_PARAM;
  544. if (!gp->n_points) {
  545. rc->x = rc->y = rc->width = rc->height = 0;
  546. return GF_OK;
  547. }
  548. pt = gp->points;
  549. end = pt + gp->n_points;
  550. xMin = xMax = pt->x;
  551. yMin = yMax = pt->y;
  552. pt++;
  553. for ( ; pt < end; pt++ ) {
  554. Fixed v;
  555. v = pt->x;
  556. if (v < xMin) xMin = v;
  557. if (v > xMax) xMax = v;
  558. v = pt->y;
  559. if (v < yMin) yMin = v;
  560. if (v > yMax) yMax = v;
  561. }
  562. rc->x = xMin;
  563. rc->y = yMax;
  564. rc->width = xMax - xMin;
  565. rc->height = yMax - yMin;
  566. #if 0
  567. /*take care of straight line path by adding a default width if height and vice-versa*/
  568. if (rc->height && !rc->width) {
  569. rc->width = 2*FIX_ONE;
  570. rc->x -= FIX_ONE;
  571. }
  572. else if (!rc->height && rc->width) {
  573. rc->height = 2*FIX_ONE;
  574. rc->y += FIX_ONE;
  575. }
  576. #endif
  577. return GF_OK;
  578. }
  579. /*
  580. * conic bbox computing taken from freetype
  581. * Copyright 1996-2001, 2002, 2004 by
  582. * David Turner, Robert Wilhelm, and Werner Lemberg.
  583. * License: FTL or GPL
  584. */
  585. static void gf_conic_check(Fixed y1, Fixed y2, Fixed y3, Fixed *min, Fixed *max)
  586. {
  587. /* flat arc */
  588. if ((y1 <= y3) && (y2 == y1)) goto Suite;
  589. if ( y1 < y3 ) {
  590. /* ascending arc */
  591. if ((y2 >= y1) && (y2 <= y3)) goto Suite;
  592. } else {
  593. /* descending arc */
  594. if ((y2 >= y3) && (y2 <= y1)) {
  595. y2 = y1;
  596. y1 = y3;
  597. y3 = y2;
  598. goto Suite;
  599. }
  600. }
  601. y1 = y3 = y1 - gf_muldiv(y2 - y1, y2 - y1, y1 - 2*y2 + y3);
  602. Suite:
  603. if ( y1 < *min ) *min = y1;
  604. if ( y3 > *max ) *max = y3;
  605. }
  606. /*
  607. * cubic bbox computing taken from freetype
  608. * Copyright 1996-2001, 2002, 2004 by
  609. * David Turner, Robert Wilhelm, and Werner Lemberg.
  610. * License: FTL or GPL
  611. * Note: we're using the decomposition method, not the equation one which is not usable with Floats (only 16.16 fixed)
  612. */
  613. static void gf_cubic_check(Fixed p1, Fixed p2, Fixed p3, Fixed p4, Fixed *min, Fixed *max)
  614. {
  615. Fixed stack[32*3 + 1], *arc;
  616. arc = stack;
  617. arc[0] = p1;
  618. arc[1] = p2;
  619. arc[2] = p3;
  620. arc[3] = p4;
  621. do {
  622. Fixed y1 = arc[0];
  623. Fixed y2 = arc[1];
  624. Fixed y3 = arc[2];
  625. Fixed y4 = arc[3];
  626. if (ABS(y1)<FIX_EPSILON) arc[0] = y1 = 0;
  627. if (ABS(y2)<FIX_EPSILON) arc[1] = y2 = 0;
  628. if (ABS(y3)<FIX_EPSILON) arc[2] = y3 = 0;
  629. if (ABS(y4)<FIX_EPSILON) arc[3] = y4 = 0;
  630. if ( y1 == y4 ) {
  631. /* flat */
  632. if ((y1 == y2) && (y1 == y3)) goto Test;
  633. }
  634. else if ( y1 < y4 ) {
  635. /* ascending */
  636. if ((y2 >= y1) && (y2 <= y4) && (y3 >= y1) && (y3 <= y4)) goto Test;
  637. } else {
  638. /* descending */
  639. if ((y2 >= y4) && (y2 <= y1) && (y3 >= y4) && (y3 <= y1)) {
  640. y2 = y1;
  641. y1 = y4;
  642. y4 = y2;
  643. goto Test;
  644. }
  645. }
  646. /* unknown direction -- split the arc in two */
  647. arc[6] = y4;
  648. arc[1] = y1 = ( y1 + y2 ) / 2;
  649. arc[5] = y4 = ( y4 + y3 ) / 2;
  650. y2 = ( y2 + y3 ) / 2;
  651. arc[2] = y1 = ( y1 + y2 ) / 2;
  652. arc[4] = y4 = ( y4 + y2 ) / 2;
  653. arc[3] = ( y1 + y4 ) / 2;
  654. arc += 3;
  655. goto Suite;
  656. Test:
  657. if ( y1 < *min ) *min = y1;
  658. if ( y4 > *max ) *max = y4;
  659. arc -= 3;
  660. Suite:
  661. ;
  662. }
  663. while ( arc >= stack );
  664. }
  665. GF_EXPORT
  666. GF_Err gf_path_get_bounds(GF_Path *gp, GF_Rect *rc)
  667. {
  668. u32 i;
  669. GF_Point2D *pt, *end, *ctrl1, *ctrl2;
  670. Fixed xMin, xMax, yMin, yMax, cxMin, cxMax, cyMin, cyMax;
  671. if (!gp || !rc) return GF_BAD_PARAM;
  672. if (!(gp->flags & GF_PATH_BBOX_DIRTY)) {
  673. *rc = gp->bbox;
  674. return GF_OK;
  675. }
  676. /*no curves in path*/
  677. if (gp->flags & GF_PATH_FLATTENED) {
  678. GF_Err e;
  679. gp->flags &= ~GF_PATH_BBOX_DIRTY;
  680. e = gf_path_get_control_bounds(gp, &gp->bbox);
  681. *rc = gp->bbox;
  682. return e;
  683. }
  684. gp->flags &= ~GF_PATH_BBOX_DIRTY;
  685. if (!gp->n_points) {
  686. gp->bbox.x = gp->bbox.y = gp->bbox.width = gp->bbox.height = 0;
  687. *rc = gp->bbox;
  688. return GF_OK;
  689. }
  690. pt = gp->points;
  691. end = pt + gp->n_points;
  692. xMin = xMax = cxMin = cxMax = pt->x;
  693. yMin = yMax = cyMin = cyMax = pt->y;
  694. pt++;
  695. for (i=1 ; i < gp->n_points; i++ ) {
  696. Fixed x, y;
  697. x = pt->x;
  698. y = pt->y;
  699. if (x < cxMin) cxMin = x;
  700. if (x > cxMax) cxMax = x;
  701. if (y < cyMin) cyMin = y;
  702. if (y > cyMax) cyMax = y;
  703. /*point on curve, update*/
  704. if (gp->tags[i] & GF_PATH_CURVE_ON) {
  705. if (x < xMin) xMin = x;
  706. if (x > xMax) xMax = x;
  707. if (y < yMin) yMin = y;
  708. if (y > yMax) yMax = y;
  709. }
  710. pt++;
  711. }
  712. /*control box is bigger than box , decompose curves*/
  713. if ((cxMin < xMin) || (cxMax > xMax) || (cyMin < yMin) || (cyMax > yMax)) {
  714. /*decompose all control points*/
  715. pt = gp->points;
  716. for (i=1 ; i < gp->n_points; ) {
  717. switch (gp->tags[i]) {
  718. case GF_PATH_CURVE_ON:
  719. case GF_PATH_CLOSE:
  720. pt = &gp->points[i];
  721. i++;
  722. break;
  723. case GF_PATH_CURVE_CONIC:
  724. /*decompose*/
  725. ctrl1 = &gp->points[i];
  726. end = &gp->points[i+1];
  727. if ((ctrl1->x < xMin) || (ctrl1->x > xMax))
  728. gf_conic_check(pt->x, ctrl1->x, end->x, &xMin, &xMax);
  729. if ((ctrl1->y < yMin) || (ctrl1->y > yMax))
  730. gf_conic_check(pt->y, ctrl1->y, end->y, &yMin, &yMax);
  731. /*and move*/
  732. pt = end;
  733. i+=2;
  734. break;
  735. case GF_PATH_CURVE_CUBIC:
  736. /*decompose*/
  737. ctrl1 = &gp->points[i];
  738. ctrl2 = &gp->points[i+1];
  739. end = &gp->points[i+2];
  740. if ((ctrl1->x < xMin) || (ctrl1->x > xMax) || (ctrl2->x < xMin) || (ctrl2->x > xMax))
  741. gf_cubic_check(pt->x, ctrl1->x, ctrl2->x, end->x, &xMin, &xMax);
  742. if ((ctrl1->y < yMin) || (ctrl1->y > yMax) || (ctrl2->y < yMin) || (ctrl2->y > yMax))
  743. gf_cubic_check(pt->y, ctrl1->y, ctrl2->y, end->y, &yMin, &yMax);
  744. /*and move*/
  745. pt = end;
  746. i+=3;
  747. break;
  748. }
  749. }
  750. }
  751. gp->bbox.x = xMin;
  752. gp->bbox.y = yMax;
  753. gp->bbox.width = xMax - xMin;
  754. gp->bbox.height = yMax - yMin;
  755. *rc = gp->bbox;
  756. return GF_OK;
  757. }
  758. /*flattening algo taken from libart but passed to sqrt tests for line distance to avoid 16.16 fixed overflow*/
  759. static GF_Err gf_subdivide_cubic(GF_Path *gp, Fixed x0, Fixed y0, Fixed x1, Fixed y1, Fixed x2, Fixed y2, Fixed x3, Fixed y3, Fixed fineness)
  760. {
  761. GF_Point2D pt;
  762. Fixed x3_0, y3_0, z3_0, z1_0, z1_dot, z2_dot, z1_perp, z2_perp;
  763. Fixed max_perp;
  764. Fixed x_m, y_m, xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2;
  765. GF_Err e;
  766. pt.x = x3_0 = x3 - x0;
  767. pt.y = y3_0 = y3 - y0;
  768. /*z3_0 is dist z0-z3*/
  769. z3_0 = gf_v2d_len(&pt);
  770. pt.x = x1 - x0;
  771. pt.y = y1 - y0;
  772. z1_0 = gf_v2d_len(&pt);
  773. if ((z3_0*100 < FIX_ONE) && (z1_0*100 < FIX_ONE))
  774. goto nosubdivide;
  775. /* perp is distance from line, multiplied by dist z0-z3 */
  776. max_perp = gf_mulfix(fineness, z3_0);
  777. z1_perp = gf_mulfix((y1 - y0), x3_0) - gf_mulfix((x1 - x0), y3_0);
  778. if (ABS(z1_perp) > max_perp)
  779. goto subdivide;
  780. z2_perp = gf_mulfix((y3 - y2), x3_0) - gf_mulfix((x3 - x2), y3_0);
  781. if (ABS(z2_perp) > max_perp)
  782. goto subdivide;
  783. z1_dot = gf_mulfix((x1 - x0), x3_0) + gf_mulfix((y1 - y0), y3_0);
  784. if ((z1_dot < 0) && (ABS(z1_dot) > max_perp))
  785. goto subdivide;
  786. z2_dot = gf_mulfix((x3 - x2), x3_0) + gf_mulfix((y3 - y2), y3_0);
  787. if ((z2_dot < 0) && (ABS(z2_dot) > max_perp))
  788. goto subdivide;
  789. if (gf_divfix(z1_dot + z1_dot, z3_0) > z3_0)
  790. goto subdivide;
  791. if (gf_divfix(z2_dot + z2_dot, z3_0) > z3_0)
  792. goto subdivide;
  793. nosubdivide:
  794. /* don't subdivide */
  795. return gf_path_add_line_to(gp, x3, y3);
  796. subdivide:
  797. xa1 = (x0 + x1) / 2;
  798. ya1 = (y0 + y1) / 2;
  799. xa2 = (x0 + 2 * x1 + x2) / 4;
  800. ya2 = (y0 + 2 * y1 + y2) / 4;
  801. xb1 = (x1 + 2 * x2 + x3) / 4;
  802. yb1 = (y1 + 2 * y2 + y3) / 4;
  803. xb2 = (x2 + x3) / 2;
  804. yb2 = (y2 + y3) / 2;
  805. x_m = (xa2 + xb1) / 2;
  806. y_m = (ya2 + yb1) / 2;
  807. /*safeguard for numerical stability*/
  808. if ( (ABS(x_m-x0) < FIX_EPSILON) && (ABS(y_m-y0) < FIX_EPSILON))
  809. return gf_path_add_line_to(gp, x3, y3);
  810. if ( (ABS(x3-x_m) < FIX_EPSILON) && (ABS(y3-y_m) < FIX_EPSILON))
  811. return gf_path_add_line_to(gp, x3, y3);
  812. e = gf_subdivide_cubic(gp, x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, fineness);
  813. if (e) return e;
  814. return gf_subdivide_cubic(gp, x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, fineness);
  815. }
  816. GF_EXPORT
  817. GF_Path *gf_path_get_flatten(GF_Path *gp)
  818. {
  819. GF_Path *ngp;
  820. Fixed fineness;
  821. u32 i, *countour;
  822. GF_Point2D *pt;
  823. if (!gp || !gp->n_points) return NULL;
  824. if (gp->flags & GF_PATH_FLATTENED) return gf_path_clone(gp);
  825. /*avoid too high precision */
  826. fineness = MAX(FIX_ONE - gp->fineness, FIX_ONE / 100);
  827. ngp = gf_path_new();
  828. pt = &gp->points[0];
  829. gf_path_add_move_to_vec(ngp, pt);
  830. countour = gp->contours;
  831. for (i=1; i<gp->n_points; ) {
  832. switch (gp->tags[i]) {
  833. case GF_PATH_CURVE_ON:
  834. case GF_PATH_CLOSE:
  835. pt = &gp->points[i];
  836. if (*countour == i-1) {
  837. gf_path_add_move_to_vec(ngp, pt);
  838. countour++;
  839. } else {
  840. gf_path_add_line_to_vec(ngp, pt);
  841. }
  842. if (gp->tags[i]==GF_PATH_CLOSE) gf_path_close(ngp);
  843. i++;
  844. break;
  845. case GF_PATH_CURVE_CONIC:
  846. {
  847. GF_Point2D *ctl, *end, c1, c2;
  848. ctl = &gp->points[i];
  849. end = &gp->points[i+1];
  850. c1.x = pt->x + 2*(ctl->x - pt->x)/3;
  851. c1.y = pt->y + 2*(ctl->y - pt->y)/3;
  852. c2.x = c1.x + (end->x - pt->x) / 3;
  853. c2.y = c1.y + (end->y - pt->y) / 3;
  854. gf_subdivide_cubic(ngp, pt->x, pt->y, c1.x, c1.y, c2.x, c2.y, end->x, end->y, fineness);
  855. pt = end;
  856. if (gp->tags[i+1]==GF_PATH_CLOSE) gf_path_close(ngp);
  857. i+=2;
  858. }
  859. break;
  860. case GF_PATH_CURVE_CUBIC:
  861. gf_subdivide_cubic(ngp, pt->x, pt->y, gp->points[i].x, gp->points[i].y, gp->points[i+1].x, gp->points[i+1].y, gp->points[i+2].x, gp->points[i+2].y, fineness);
  862. pt = &gp->points[i+2];
  863. if (gp->tags[i+2]==GF_PATH_CLOSE) gf_path_close(ngp);
  864. i+=3;
  865. break;
  866. }
  867. }
  868. if (gp->flags & GF_PATH_FILL_ZERO_NONZERO) ngp->flags |= GF_PATH_FILL_ZERO_NONZERO;
  869. ngp->flags |= (GF_PATH_BBOX_DIRTY | GF_PATH_FLATTENED);
  870. return ngp;
  871. }
  872. GF_EXPORT
  873. void gf_path_flatten(GF_Path *gp)
  874. {
  875. GF_Path *res;
  876. if (gp->flags & GF_PATH_FLATTENED) return;
  877. if (!gp->n_points) return;
  878. res = gf_path_get_flatten(gp);
  879. if (gp->contours) gf_free(gp->contours);
  880. if (gp->points) gf_free(gp->points);
  881. if (gp->tags) gf_free(gp->tags);
  882. memcpy(gp, res, sizeof(GF_Path));
  883. gf_free(res);
  884. }
  885. #define isLeft(P0, P1, P2) \
  886. ( gf_mulfix((P1.x - P0.x), (P2.y - P0.y)) - gf_mulfix((P2.x - P0.x), (P1.y - P0.y)) )
  887. static void gf_subdivide_cubic_hit_test(Fixed h_x, Fixed h_y, Fixed x0, Fixed y0, Fixed x1, Fixed y1, Fixed x2, Fixed y2, Fixed x3, Fixed y3, s32 *wn)
  888. {
  889. GF_Point2D s, e, pt;
  890. Fixed x_m, y_m, xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2, y_min, y_max;
  891. /*if hit line doesn't intersects control box abort*/
  892. y_min = MIN(y0, MIN(y1, MIN(y2, y3)));
  893. y_max = MAX(y0, MAX(y1, MAX(y2, y3)));
  894. if ((h_y<y_min) || (h_y>y_max) ) return;
  895. /*if vert diff between end points larger than 1 pixels, subdivide (we need pixel accuracy for is_over)*/
  896. if (y_max - y_min > FIX_ONE) {
  897. xa1 = (x0 + x1) / 2;
  898. ya1 = (y0 + y1) / 2;
  899. xa2 = (x0 + 2 * x1 + x2) / 4;
  900. ya2 = (y0 + 2 * y1 + y2) / 4;
  901. xb1 = (x1 + 2 * x2 + x3) / 4;
  902. yb1 = (y1 + 2 * y2 + y3) / 4;
  903. xb2 = (x2 + x3) / 2;
  904. yb2 = (y2 + y3) / 2;
  905. x_m = (xa2 + xb1) / 2;
  906. y_m = (ya2 + yb1) / 2;
  907. gf_subdivide_cubic_hit_test(h_x, h_y, x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, wn);
  908. gf_subdivide_cubic_hit_test(h_x, h_y, x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, wn);
  909. return;
  910. }
  911. s.x = x0;
  912. s.y = y0;
  913. e.x = x3;
  914. e.y = y3;
  915. pt.x = h_x;
  916. pt.y= h_y;
  917. if (s.y<=pt.y) {
  918. if (e.y>pt.y) {
  919. if (isLeft(s, e, pt) > 0)
  920. (*wn)++;
  921. }
  922. }
  923. else if (e.y<=pt.y) {
  924. if (isLeft(s, e, pt) < 0)
  925. (*wn)--;
  926. }
  927. }
  928. GF_EXPORT
  929. Bool gf_path_point_over(GF_Path *gp, Fixed x, Fixed y)
  930. {
  931. u32 i, *contour, start_idx;
  932. s32 wn;
  933. GF_Point2D start, s, e, pt;
  934. GF_Rect rc;
  935. /*check if not in bounds*/
  936. gf_path_get_bounds(gp, &rc);
  937. if ((x<rc.x) || (y>rc.y) || (x>rc.x+rc.width) || (y<rc.y-rc.height)) return 0;
  938. if (!gp || (gp->n_points<2)) return 0;
  939. pt.x = x;
  940. pt.y = y;
  941. wn = 0;
  942. s = start = gp->points[0];
  943. start_idx = 0;
  944. contour = gp->contours;
  945. for (i=1; i<gp->n_points; ) {
  946. switch (gp->tags[i]) {
  947. case GF_PATH_CURVE_ON:
  948. case GF_PATH_CLOSE:
  949. e = gp->points[i];
  950. if (s.y<=pt.y) {
  951. if (e.y>pt.y) {
  952. if (isLeft(s, e, pt) > 0) wn++;
  953. }
  954. }
  955. else if (e.y<=pt.y) {
  956. if (isLeft(s, e, pt) < 0) wn--;
  957. }
  958. s = e;
  959. i++;
  960. break;
  961. case GF_PATH_CURVE_CONIC:
  962. {
  963. GF_Point2D *ctl, *end, c1, c2;
  964. ctl = &gp->points[i];
  965. end = &gp->points[i+1];
  966. c1.x = s.x + 2*(ctl->x - s.x) / 3;
  967. c1.y = s.y + 2*(ctl->y - s.y) / 3;
  968. c2.x = c1.x + (end->x - s.x) / 3;
  969. c2.y = c1.y + (end->y - s.y) / 3;
  970. gf_subdivide_cubic_hit_test(x, y, s.x, s.y, c1.x, c1.y, c2.x, c2.y, end->x, end->y, &wn);
  971. s = *end;
  972. }
  973. i+=2;
  974. break;
  975. case GF_PATH_CURVE_CUBIC:
  976. gf_subdivide_cubic_hit_test(x, y, s.x, s.y, gp->points[i].x, gp->points[i].y, gp->points[i+1].x, gp->points[i+1].y, gp->points[i+2].x, gp->points[i+2].y, &wn);
  977. s = gp->points[i+2];
  978. i+=3;
  979. break;
  980. }
  981. /*end of subpath*/
  982. if (*contour==i-1) {
  983. /*close path if needed, but don't test for lines...*/
  984. if ((i-start_idx > 2) && (pt.y<s.y)) {
  985. if ((start.x != s.x) || (start.y != s.y)) {
  986. e = start;
  987. if (s.x<=pt.x) {
  988. if (e.y>pt.y) {
  989. if (isLeft(s, e, pt) > 0) wn++;
  990. }
  991. }
  992. else if (e.y<=pt.y) {
  993. if (isLeft(s, e, pt) < 0) wn--;
  994. }
  995. }
  996. }
  997. if ( i < gp->n_points )
  998. s = start = gp->points[i];
  999. i++;
  1000. }
  1001. }
  1002. if (gp->flags & GF_PATH_FILL_ZERO_NONZERO) return wn ? 1 : 0;
  1003. return wn%2 ? 1 : 0;
  1004. }
  1005. GF_EXPORT
  1006. Bool gf_path_is_empty(GF_Path *gp)
  1007. {
  1008. if (gp && gp->contours) return 0;
  1009. return 1;
  1010. }
  1011. /*iteration info*/
  1012. typedef struct
  1013. {
  1014. Fixed len;
  1015. Fixed dx, dy;
  1016. Fixed start_x, start_y;
  1017. } IterInfo;
  1018. struct _path_iterator
  1019. {
  1020. u32 num_seg;
  1021. IterInfo *seg;
  1022. Fixed length;
  1023. };
  1024. GF_EXPORT
  1025. GF_PathIterator *gf_path_iterator_new(GF_Path *gp)
  1026. {
  1027. GF_Path *flat;
  1028. GF_PathIterator *it;
  1029. u32 i, j, cur;
  1030. GF_Point2D start, end;
  1031. GF_SAFEALLOC(it, GF_PathIterator);
  1032. if (!it) return NULL;
  1033. flat = gf_path_get_flatten(gp);
  1034. if (!flat) {
  1035. gf_free(it);
  1036. return NULL;
  1037. }
  1038. it->seg = (IterInfo *) gf_malloc(sizeof(IterInfo) * flat->n_points);
  1039. it->num_seg = 0;
  1040. it->length = 0;
  1041. cur = 0;
  1042. for (i=0; i<flat->n_contours; i++) {
  1043. Fixed dx, dy;
  1044. u32 nb_pts = 1+flat->contours[i]-cur;
  1045. start = flat->points[cur];
  1046. for (j=1; j<nb_pts; j++) {
  1047. end = flat->points[cur+j];
  1048. it->seg[it->num_seg].start_x = start.x;
  1049. it->seg[it->num_seg].start_y = start.y;
  1050. dx = it->seg[it->num_seg].dx = end.x - start.x;
  1051. dy = it->seg[it->num_seg].dy = end.y - start.y;
  1052. it->seg[it->num_seg].len = gf_sqrt(gf_mulfix(dx, dx) + gf_mulfix(dy, dy));
  1053. it->length += it->seg[it->num_seg].len;
  1054. start = end;
  1055. it->num_seg++;
  1056. }
  1057. cur += nb_pts;
  1058. }
  1059. gf_path_del(flat);
  1060. return it;
  1061. }
  1062. GF_EXPORT
  1063. Fixed gf_path_iterator_get_length(GF_PathIterator *it)
  1064. {
  1065. return it ? it->length : 0;
  1066. }
  1067. GF_EXPORT
  1068. Bool gf_path_iterator_get_transform(GF_PathIterator *path, Fixed offset, Bool follow_tangent, GF_Matrix2D *mat, Bool smooth_edges, Fixed length_after_point)
  1069. {
  1070. GF_Matrix2D final, rot;
  1071. Bool tang = 0;
  1072. Fixed res, angle, angleNext;
  1073. u32 i;
  1074. Fixed curLen = 0;
  1075. if (!path) return 0;
  1076. for (i=0; i<path->num_seg; i++) {
  1077. if (curLen + path->seg[i].len >= offset) goto found;
  1078. curLen += path->seg[i].len;
  1079. }
  1080. if (!follow_tangent) return 0;
  1081. tang = 1;
  1082. i--;
  1083. found:
  1084. gf_mx2d_init(final);
  1085. res = gf_divfix(offset - curLen, path->seg[i].len);
  1086. if (tang) res += 1;
  1087. /*move to current point*/
  1088. gf_mx2d_add_translation(&final, path->seg[i].start_x + gf_mulfix(path->seg[i].dx, res), path->seg[i].start_y + gf_mulfix(path->seg[i].dy, res));
  1089. if (!path->seg[i].dx) {
  1090. angle = GF_PI2;
  1091. } else {
  1092. angle = gf_acos( gf_divfix(path->seg[i].dx , path->seg[i].len) );
  1093. }
  1094. if (path->seg[i].dy<0) angle *= -1;
  1095. if (smooth_edges) {
  1096. if (offset + length_after_point > curLen + path->seg[i].len) {
  1097. Fixed ratio = gf_divfix(curLen + path->seg[i].len-offset, length_after_point);
  1098. if (i < path->num_seg - 1) {
  1099. if (!path->seg[i+1].dx) {
  1100. angleNext = GF_PI2;
  1101. } else {
  1102. angleNext = gf_acos( gf_divfix(path->seg[i+1].dx, path->seg[i+1].len) );
  1103. }
  1104. if (path->seg[i+1].dy<0) angleNext *= -1;
  1105. if (angle<0 && angleNext>0) {
  1106. angle = gf_mulfix(FIX_ONE-ratio, angleNext) - gf_mulfix(ratio, angle);
  1107. } else {
  1108. angle = gf_mulfix(ratio, angle) + gf_mulfix(FIX_ONE-ratio, angleNext);
  1109. }
  1110. }
  1111. }
  1112. }
  1113. /*handle res=0 case for rotation (point on line join)*/
  1114. else if (res==1) {
  1115. if (i < path->num_seg - 1) {
  1116. if (!path->seg[i+1].dx) {
  1117. angleNext = GF_PI2;
  1118. } else {
  1119. angleNext = gf_acos( gf_divfix(path->seg[i+1].dx, path->seg[i+1].len) );
  1120. }
  1121. if (path->seg[i+1].dy<0) angleNext *= -1;
  1122. angle = ( angle + angleNext) / 2;
  1123. }
  1124. }
  1125. gf_mx2d_init(rot);
  1126. gf_mx2d_add_rotation(&rot, 0, 0, angle);
  1127. gf_mx2d_add_matrix(mat, &rot);
  1128. gf_mx2d_add_matrix(mat, &final);
  1129. return 1;
  1130. }
  1131. GF_EXPORT
  1132. void gf_path_iterator_del(GF_PathIterator *it)
  1133. {
  1134. if (it->seg) gf_free(it->seg);
  1135. gf_free(it);
  1136. }
  1137. #define ConvexCompare(delta) \
  1138. ( (delta.x > 0) ? -1 : \
  1139. (delta.x < 0) ? 1 : \
  1140. (delta.y > 0) ? -1 : \
  1141. (delta.y < 0) ? 1 : \
  1142. 0 )
  1143. #define ConvexGetPointDelta(delta, pprev, pcur ) \
  1144. /* Given a previous point 'pprev', read a new point into 'pcur' */ \
  1145. /* and return delta in 'delta'. */ \
  1146. pcur = pts[iread++]; \
  1147. delta.x = pcur.x - pprev.x; \
  1148. delta.y = pcur.y - pprev.y; \
  1149. #define ConvexCross(p, q) gf_mulfix(p.x,q.y) - gf_mulfix(p.y,q.x);
  1150. #define ConvexCheckTriple \
  1151. if ( (thisDir = ConvexCompare(dcur)) == -curDir ) { \
  1152. ++dirChanges; \
  1153. /* if ( dirChanges > 2 ) return NotConvex; */ \
  1154. } \
  1155. curDir = thisDir; \
  1156. cross = ConvexCross(dprev, dcur); \
  1157. if ( cross > 0 ) { \
  1158. if ( angleSign == -1 ) return GF_POLYGON_COMPLEX_CW; \
  1159. angleSign = 1; \
  1160. } \
  1161. else if (cross < 0) { \
  1162. if (angleSign == 1) return GF_POLYGON_COMPLEX_CCW; \
  1163. angleSign = -1; \
  1164. } \
  1165. pSecond = pThird; \
  1166. dprev.x = dcur.x; \
  1167. dprev.y = dcur.y; \
  1168. GF_EXPORT
  1169. u32 gf_polygone2d_get_convexity(GF_Point2D *pts, u32 len)
  1170. {
  1171. s32 curDir, thisDir = 0, dirChanges = 0, angleSign = 0;
  1172. u32 iread;
  1173. Fixed cross;
  1174. GF_Point2D pSecond, pThird, pSaveSecond;
  1175. GF_Point2D dprev, dcur;
  1176. /* Get different point, return if less than 3 diff points. */
  1177. if (len < 3 ) return GF_POLYGON_CONVEX_LINE;
  1178. iread = 1;
  1179. ConvexGetPointDelta(dprev, (pts[0]), pSecond);
  1180. pSaveSecond = pSecond;
  1181. /*initial direction */
  1182. curDir = ConvexCompare(dprev);
  1183. while ( iread < len) {
  1184. /* Get different point, break if no more points */
  1185. ConvexGetPointDelta(dcur, pSecond, pThird );
  1186. if ( (dcur.x == 0) && (dcur.y == 0) ) continue;
  1187. /* Check current three points */
  1188. ConvexCheckTriple;
  1189. }
  1190. /* Must check for direction changes from last vertex back to first */
  1191. /* Prepare for 'ConvexCheckTriple' */
  1192. pThird = pts[0];
  1193. dcur.x = pThird.x - pSecond.x;
  1194. dcur.y = pThird.y - pSecond.y;
  1195. if ( ConvexCompare(dcur) ) ConvexCheckTriple;
  1196. /* and check for direction changes back to second vertex */
  1197. dcur.x = pSaveSecond.x - pSecond.x;
  1198. dcur.y = pSaveSecond.y - pSecond.y;
  1199. /* Don't care about 'pThird' now */
  1200. ConvexCheckTriple;
  1201. /* Decide on polygon type given accumulated status */
  1202. if ( dirChanges > 2 ) return GF_POLYGON_COMPLEX;
  1203. if ( angleSign > 0 ) return GF_POLYGON_CONVEX_CCW;
  1204. if ( angleSign < 0 ) return GF_POLYGON_CONVEX_CW;
  1205. return GF_POLYGON_CONVEX_LINE;
  1206. }