res_path.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. #include <assert.h>
  2. #include "fitz-internal.h"
  3. fz_path *
  4. fz_new_path(fz_context *ctx)
  5. {
  6. fz_path *path;
  7. path = fz_malloc_struct(ctx, fz_path);
  8. path->len = 0;
  9. path->cap = 0;
  10. path->items = NULL;
  11. path->last = -1;
  12. return path;
  13. }
  14. fz_path *
  15. fz_clone_path(fz_context *ctx, fz_path *old)
  16. {
  17. fz_path *path;
  18. assert(old);
  19. path = fz_malloc_struct(ctx, fz_path);
  20. fz_try(ctx)
  21. {
  22. path->len = old->len;
  23. path->cap = old->len;
  24. path->items = fz_malloc_array(ctx, path->cap, sizeof(fz_path_item));
  25. memcpy(path->items, old->items, sizeof(fz_path_item) * path->len);
  26. }
  27. fz_catch(ctx)
  28. {
  29. fz_free(ctx, path);
  30. fz_rethrow(ctx);
  31. }
  32. return path;
  33. }
  34. void
  35. fz_free_path(fz_context *ctx, fz_path *path)
  36. {
  37. if (path == NULL)
  38. return;
  39. fz_free(ctx, path->items);
  40. fz_free(ctx, path);
  41. }
  42. static void
  43. grow_path(fz_context *ctx, fz_path *path, int n)
  44. {
  45. int newcap = path->cap;
  46. if (path->len + n <= path->cap)
  47. {
  48. path->last = path->len;
  49. return;
  50. }
  51. while (path->len + n > newcap)
  52. newcap = newcap + 36;
  53. path->items = fz_resize_array(ctx, path->items, newcap, sizeof(fz_path_item));
  54. path->cap = newcap;
  55. path->last = path->len;
  56. }
  57. fz_point
  58. fz_currentpoint(fz_context *ctx, fz_path *path)
  59. {
  60. fz_point c, m;
  61. int i;
  62. c.x = c.y = m.x = m.y = 0;
  63. i = 0;
  64. while (i < path->len)
  65. {
  66. switch (path->items[i++].k)
  67. {
  68. case FZ_MOVETO:
  69. m.x = c.x = path->items[i++].v;
  70. m.y = c.y = path->items[i++].v;
  71. break;
  72. case FZ_LINETO:
  73. c.x = path->items[i++].v;
  74. c.y = path->items[i++].v;
  75. break;
  76. case FZ_CURVETO:
  77. i += 4;
  78. c.x = path->items[i++].v;
  79. c.y = path->items[i++].v;
  80. break;
  81. case FZ_CLOSE_PATH:
  82. c = m;
  83. }
  84. }
  85. return c;
  86. }
  87. void
  88. fz_moveto(fz_context *ctx, fz_path *path, float x, float y)
  89. {
  90. if (path->last >= 0 && path->items[path->last].k == FZ_MOVETO)
  91. {
  92. /* No point in having MOVETO then MOVETO */
  93. path->len = path->last;
  94. }
  95. grow_path(ctx, path, 3);
  96. path->items[path->len++].k = FZ_MOVETO;
  97. path->items[path->len++].v = x;
  98. path->items[path->len++].v = y;
  99. }
  100. void
  101. fz_lineto(fz_context *ctx, fz_path *path, float x, float y)
  102. {
  103. float x0, y0;
  104. if (path->last < 0)
  105. {
  106. fz_warn(ctx, "lineto with no current point");
  107. return;
  108. }
  109. if (path->items[path->last].k == FZ_CLOSE_PATH)
  110. {
  111. x0 = path->items[path->last-2].v;
  112. y0 = path->items[path->last-1].v;
  113. }
  114. else
  115. {
  116. x0 = path->items[path->len-2].v;
  117. y0 = path->items[path->len-1].v;
  118. }
  119. /* Anything other than MoveTo followed by LineTo the same place is a nop */
  120. if (path->items[path->last].k != FZ_MOVETO && x0 == x && y0 == y)
  121. return;
  122. grow_path(ctx, path, 3);
  123. path->items[path->len++].k = FZ_LINETO;
  124. path->items[path->len++].v = x;
  125. path->items[path->len++].v = y;
  126. }
  127. void
  128. fz_curveto(fz_context *ctx, fz_path *path,
  129. float x1, float y1,
  130. float x2, float y2,
  131. float x3, float y3)
  132. {
  133. float x0, y0;
  134. if (path->last < 0)
  135. {
  136. fz_warn(ctx, "curveto with no current point");
  137. return;
  138. }
  139. if (path->items[path->last].k == FZ_CLOSE_PATH)
  140. {
  141. x0 = path->items[path->last-2].v;
  142. y0 = path->items[path->last-1].v;
  143. }
  144. else
  145. {
  146. x0 = path->items[path->len-2].v;
  147. y0 = path->items[path->len-1].v;
  148. }
  149. /* Check for degenerate cases: */
  150. if (x0 == x1 && y0 == y1)
  151. {
  152. if (x2 == x3 && y2 == y3)
  153. {
  154. /* If (x1,y1)==(x2,y2) and prev wasn't a moveto, then skip */
  155. if (x1 == x2 && y1 == y2 && path->items[path->last].k != FZ_MOVETO)
  156. return;
  157. /* Otherwise a line will suffice */
  158. fz_lineto(ctx, path, x3, y3);
  159. return;
  160. }
  161. if (x1 == x2 && y1 == y2)
  162. {
  163. /* A line will suffice */
  164. fz_lineto(ctx, path, x3, y3);
  165. return;
  166. }
  167. }
  168. else if (x1 == x2 && y1 == y2 && x2 == x3 && y2 == y3)
  169. {
  170. /* A line will suffice */
  171. fz_lineto(ctx, path, x3, y3);
  172. return;
  173. }
  174. grow_path(ctx, path, 7);
  175. path->items[path->len++].k = FZ_CURVETO;
  176. path->items[path->len++].v = x1;
  177. path->items[path->len++].v = y1;
  178. path->items[path->len++].v = x2;
  179. path->items[path->len++].v = y2;
  180. path->items[path->len++].v = x3;
  181. path->items[path->len++].v = y3;
  182. }
  183. void
  184. fz_curvetov(fz_context *ctx, fz_path *path, float x2, float y2, float x3, float y3)
  185. {
  186. float x1, y1;
  187. if (path->last < 0)
  188. {
  189. fz_warn(ctx, "curvetov with no current point");
  190. return;
  191. }
  192. if (path->items[path->last].k == FZ_CLOSE_PATH)
  193. {
  194. x1 = path->items[path->last-2].v;
  195. y1 = path->items[path->last-1].v;
  196. }
  197. else
  198. {
  199. x1 = path->items[path->len-2].v;
  200. y1 = path->items[path->len-1].v;
  201. }
  202. fz_curveto(ctx, path, x1, y1, x2, y2, x3, y3);
  203. }
  204. void
  205. fz_curvetoy(fz_context *ctx, fz_path *path, float x1, float y1, float x3, float y3)
  206. {
  207. fz_curveto(ctx, path, x1, y1, x3, y3, x3, y3);
  208. }
  209. void
  210. fz_closepath(fz_context *ctx, fz_path *path)
  211. {
  212. if (path->last < 0)
  213. {
  214. fz_warn(ctx, "closepath with no current point");
  215. return;
  216. }
  217. /* CLOSE following a CLOSE is a NOP */
  218. if (path->items[path->last].k == FZ_CLOSE_PATH)
  219. return;
  220. grow_path(ctx, path, 1);
  221. path->items[path->len++].k = FZ_CLOSE_PATH;
  222. }
  223. static inline fz_rect *bound_expand(fz_rect *r, const fz_point *p)
  224. {
  225. if (p->x < r->x0) r->x0 = p->x;
  226. if (p->y < r->y0) r->y0 = p->y;
  227. if (p->x > r->x1) r->x1 = p->x;
  228. if (p->y > r->y1) r->y1 = p->y;
  229. return r;
  230. }
  231. fz_rect *
  232. fz_bound_path(fz_context *ctx, fz_path *path, fz_stroke_state *stroke, const fz_matrix *ctm, fz_rect *r)
  233. {
  234. fz_point p;
  235. int i = 0;
  236. /* If the path is empty, return the empty rectangle here - don't wait
  237. * for it to be expanded in the stroked case below. */
  238. if (path->len == 0)
  239. {
  240. *r = fz_empty_rect;
  241. return r;
  242. }
  243. /* A path must start with a moveto - and if that's all there is
  244. * then the path is empty. */
  245. if (path->len == 3)
  246. {
  247. *r = fz_empty_rect;
  248. return r;
  249. }
  250. p.x = path->items[1].v;
  251. p.y = path->items[2].v;
  252. fz_transform_point(&p, ctm);
  253. r->x0 = r->x1 = p.x;
  254. r->y0 = r->y1 = p.y;
  255. while (i < path->len)
  256. {
  257. switch (path->items[i++].k)
  258. {
  259. case FZ_CURVETO:
  260. p.x = path->items[i++].v;
  261. p.y = path->items[i++].v;
  262. bound_expand(r, fz_transform_point(&p, ctm));
  263. p.x = path->items[i++].v;
  264. p.y = path->items[i++].v;
  265. bound_expand(r, fz_transform_point(&p, ctm));
  266. p.x = path->items[i++].v;
  267. p.y = path->items[i++].v;
  268. bound_expand(r, fz_transform_point(&p, ctm));
  269. break;
  270. case FZ_MOVETO:
  271. if (i + 2 == path->len)
  272. {
  273. /* Trailing Moveto - cannot affect bbox */
  274. i += 2;
  275. break;
  276. }
  277. /* fallthrough */
  278. case FZ_LINETO:
  279. p.x = path->items[i++].v;
  280. p.y = path->items[i++].v;
  281. bound_expand(r, fz_transform_point(&p, ctm));
  282. break;
  283. case FZ_CLOSE_PATH:
  284. break;
  285. }
  286. }
  287. if (stroke)
  288. {
  289. fz_adjust_rect_for_stroke(r, stroke, ctm);
  290. }
  291. return r;
  292. }
  293. fz_rect *
  294. fz_adjust_rect_for_stroke(fz_rect *r, fz_stroke_state *stroke, const fz_matrix *ctm)
  295. {
  296. float expand;
  297. if (!stroke)
  298. return r;
  299. expand = stroke->linewidth;
  300. if (expand == 0)
  301. expand = 1.0f;
  302. expand *= fz_matrix_max_expansion(ctm);
  303. if ((stroke->linejoin == FZ_LINEJOIN_MITER || stroke->linejoin == FZ_LINEJOIN_MITER_XPS) && stroke->miterlimit > 1)
  304. expand *= stroke->miterlimit;
  305. r->x0 -= expand;
  306. r->y0 -= expand;
  307. r->x1 += expand;
  308. r->y1 += expand;
  309. return r;
  310. }
  311. void
  312. fz_transform_path(fz_context *ctx, fz_path *path, const fz_matrix *ctm)
  313. {
  314. int k, i = 0;
  315. while (i < path->len)
  316. {
  317. switch (path->items[i++].k)
  318. {
  319. case FZ_CURVETO:
  320. for (k = 0; k < 3; k++)
  321. {
  322. fz_transform_point((fz_point *)(void *)&path->items[i].v, ctm);
  323. i += 2;
  324. }
  325. break;
  326. case FZ_MOVETO:
  327. case FZ_LINETO:
  328. fz_transform_point((fz_point *)(void *)&path->items[i].v, ctm);
  329. i += 2;
  330. break;
  331. case FZ_CLOSE_PATH:
  332. break;
  333. }
  334. }
  335. }
  336. #ifndef NDEBUG
  337. void
  338. fz_print_path(fz_context *ctx, FILE *out, fz_path *path, int indent)
  339. {
  340. float x, y;
  341. int i = 0;
  342. int n;
  343. while (i < path->len)
  344. {
  345. for (n = 0; n < indent; n++)
  346. fputc(' ', out);
  347. switch (path->items[i++].k)
  348. {
  349. case FZ_MOVETO:
  350. x = path->items[i++].v;
  351. y = path->items[i++].v;
  352. fprintf(out, "%g %g m\n", x, y);
  353. break;
  354. case FZ_LINETO:
  355. x = path->items[i++].v;
  356. y = path->items[i++].v;
  357. fprintf(out, "%g %g l\n", x, y);
  358. break;
  359. case FZ_CURVETO:
  360. x = path->items[i++].v;
  361. y = path->items[i++].v;
  362. fprintf(out, "%g %g ", x, y);
  363. x = path->items[i++].v;
  364. y = path->items[i++].v;
  365. fprintf(out, "%g %g ", x, y);
  366. x = path->items[i++].v;
  367. y = path->items[i++].v;
  368. fprintf(out, "%g %g c\n", x, y);
  369. break;
  370. case FZ_CLOSE_PATH:
  371. fprintf(out, "h\n");
  372. break;
  373. }
  374. }
  375. }
  376. #endif
  377. fz_stroke_state *
  378. fz_keep_stroke_state(fz_context *ctx, fz_stroke_state *stroke)
  379. {
  380. if (!stroke)
  381. return NULL;
  382. fz_lock(ctx, FZ_LOCK_ALLOC);
  383. if (stroke->refs > 0)
  384. stroke->refs++;
  385. fz_unlock(ctx, FZ_LOCK_ALLOC);
  386. return stroke;
  387. }
  388. void
  389. fz_drop_stroke_state(fz_context *ctx, fz_stroke_state *stroke)
  390. {
  391. int drop;
  392. if (!stroke)
  393. return;
  394. fz_lock(ctx, FZ_LOCK_ALLOC);
  395. drop = (stroke->refs > 0 ? --stroke->refs == 0 : 0);
  396. fz_unlock(ctx, FZ_LOCK_ALLOC);
  397. if (drop)
  398. fz_free(ctx, stroke);
  399. }
  400. fz_stroke_state *
  401. fz_new_stroke_state_with_len(fz_context *ctx, int len)
  402. {
  403. fz_stroke_state *state;
  404. len -= nelem(state->dash_list);
  405. if (len < 0)
  406. len = 0;
  407. state = Memento_label(fz_malloc(ctx, sizeof(*state) + sizeof(state->dash_list[0]) * len), "fz_stroke_state");
  408. state->refs = 1;
  409. state->start_cap = FZ_LINECAP_BUTT;
  410. state->dash_cap = FZ_LINECAP_BUTT;
  411. state->end_cap = FZ_LINECAP_BUTT;
  412. state->linejoin = FZ_LINEJOIN_MITER;
  413. state->linewidth = 1;
  414. state->miterlimit = 10;
  415. state->dash_phase = 0;
  416. state->dash_len = 0;
  417. memset(state->dash_list, 0, sizeof(state->dash_list[0]) * (len + nelem(state->dash_list)));
  418. return state;
  419. }
  420. fz_stroke_state *
  421. fz_new_stroke_state(fz_context *ctx)
  422. {
  423. return fz_new_stroke_state_with_len(ctx, 0);
  424. }
  425. fz_stroke_state *
  426. fz_unshare_stroke_state_with_len(fz_context *ctx, fz_stroke_state *shared, int len)
  427. {
  428. int single, unsize, shsize, shlen, drop;
  429. fz_stroke_state *unshared;
  430. fz_lock(ctx, FZ_LOCK_ALLOC);
  431. single = (shared->refs == 1);
  432. fz_unlock(ctx, FZ_LOCK_ALLOC);
  433. shlen = shared->dash_len - nelem(shared->dash_list);
  434. if (shlen < 0)
  435. shlen = 0;
  436. shsize = sizeof(*shared) + sizeof(shared->dash_list[0]) * shlen;
  437. len -= nelem(shared->dash_list);
  438. if (len < 0)
  439. len = 0;
  440. if (single && shlen >= len)
  441. return shared;
  442. unsize = sizeof(*unshared) + sizeof(unshared->dash_list[0]) * len;
  443. unshared = Memento_label(fz_malloc(ctx, unsize), "fz_stroke_state");
  444. memcpy(unshared, shared, (shsize > unsize ? unsize : shsize));
  445. unshared->refs = 1;
  446. fz_lock(ctx, FZ_LOCK_ALLOC);
  447. drop = (shared->refs > 0 ? --shared->refs == 0 : 0);
  448. fz_unlock(ctx, FZ_LOCK_ALLOC);
  449. if (drop)
  450. fz_free(ctx, shared);
  451. return unshared;
  452. }
  453. fz_stroke_state *
  454. fz_unshare_stroke_state(fz_context *ctx, fz_stroke_state *shared)
  455. {
  456. return fz_unshare_stroke_state_with_len(ctx, shared, shared->dash_len);
  457. }