You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1150 lines
35 KiB

  1. /*
  2. * libcaca Colour ASCII-Art library
  3. * Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * $Id$
  7. *
  8. * This library is free software. It comes without any warranty, to
  9. * the extent permitted by applicable law. You can redistribute it
  10. * and/or modify it under the terms of the Do What The Fuck You Want
  11. * To Public License, Version 2, as published by Sam Hocevar. See
  12. * http://sam.zoy.org/wtfpl/COPYING for more details.
  13. */
  14. /*
  15. * This file contains horizontal and vertical flipping routines.
  16. */
  17. #include "config.h"
  18. #if !defined(__KERNEL__)
  19. # include <stdlib.h>
  20. #endif
  21. #include "caca.h"
  22. #include "caca_internals.h"
  23. static uint32_t flipchar(uint32_t ch);
  24. static uint32_t flopchar(uint32_t ch);
  25. static uint32_t rotatechar(uint32_t ch);
  26. static uint32_t leftchar(uint32_t ch);
  27. static uint32_t rightchar(uint32_t ch);
  28. static void leftpair(uint32_t pair[2]);
  29. static void rightpair(uint32_t pair[2]);
  30. /** \brief Invert a canvas' colours.
  31. *
  32. * Invert a canvas' colours (black becomes white, red becomes cyan, etc.)
  33. * without changing the characters in it.
  34. *
  35. * This function never fails.
  36. *
  37. * \param cv The canvas to invert.
  38. * \return This function always returns 0.
  39. */
  40. int caca_invert(caca_canvas_t *cv)
  41. {
  42. uint32_t *attrs = cv->attrs;
  43. int i;
  44. for(i = cv->height * cv->width; i--; )
  45. {
  46. *attrs = *attrs ^ 0x000f000f;
  47. attrs++;
  48. }
  49. caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
  50. return 0;
  51. }
  52. /** \brief Flip a canvas horizontally.
  53. *
  54. * Flip a canvas horizontally, choosing characters that look like the
  55. * mirrored version wherever possible. Some characters will stay
  56. * unchanged by the process, but the operation is guaranteed to be
  57. * involutive: performing it again gives back the original canvas.
  58. *
  59. * This function never fails.
  60. *
  61. * \param cv The canvas to flip.
  62. * \return This function always returns 0.
  63. */
  64. int caca_flip(caca_canvas_t *cv)
  65. {
  66. int y;
  67. for(y = 0; y < cv->height; y++)
  68. {
  69. uint32_t *cleft = cv->chars + y * cv->width;
  70. uint32_t *cright = cleft + cv->width - 1;
  71. uint32_t *aleft = cv->attrs + y * cv->width;
  72. uint32_t *aright = aleft + cv->width - 1;
  73. while(cleft < cright)
  74. {
  75. uint32_t ch;
  76. uint32_t attr;
  77. /* Swap attributes */
  78. attr = *aright;
  79. *aright-- = *aleft;
  80. *aleft++ = attr;
  81. /* Swap characters */
  82. ch = *cright;
  83. *cright-- = flipchar(*cleft);
  84. *cleft++ = flipchar(ch);
  85. }
  86. if(cleft == cright)
  87. *cleft = flipchar(*cleft);
  88. /* Fix fullwidth characters. Could it be done in one loop? */
  89. cleft = cv->chars + y * cv->width;
  90. cright = cleft + cv->width - 1;
  91. for( ; cleft < cright; cleft++)
  92. {
  93. if(cleft[0] == CACA_MAGIC_FULLWIDTH)
  94. {
  95. cleft[0] = cleft[1];
  96. cleft[1] = CACA_MAGIC_FULLWIDTH;
  97. cleft++;
  98. }
  99. }
  100. }
  101. caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
  102. return 0;
  103. }
  104. /** \brief Flip a canvas vertically.
  105. *
  106. * Flip a canvas vertically, choosing characters that look like the
  107. * mirrored version wherever possible. Some characters will stay
  108. * unchanged by the process, but the operation is guaranteed to be
  109. * involutive: performing it again gives back the original canvas.
  110. *
  111. * This function never fails.
  112. *
  113. * \param cv The canvas to flop.
  114. * \return This function always returns 0.
  115. */
  116. int caca_flop(caca_canvas_t *cv)
  117. {
  118. int x;
  119. for(x = 0; x < cv->width; x++)
  120. {
  121. uint32_t *ctop = cv->chars + x;
  122. uint32_t *cbottom = ctop + cv->width * (cv->height - 1);
  123. uint32_t *atop = cv->attrs + x;
  124. uint32_t *abottom = atop + cv->width * (cv->height - 1);
  125. while(ctop < cbottom)
  126. {
  127. uint32_t ch;
  128. uint32_t attr;
  129. /* Swap attributes */
  130. attr = *abottom; *abottom = *atop; *atop = attr;
  131. /* Swap characters */
  132. ch = *cbottom; *cbottom = flopchar(*ctop); *ctop = flopchar(ch);
  133. ctop += cv->width; cbottom -= cv->width;
  134. atop += cv->width; abottom -= cv->width;
  135. }
  136. if(ctop == cbottom)
  137. *ctop = flopchar(*ctop);
  138. }
  139. caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
  140. return 0;
  141. }
  142. /** \brief Rotate a canvas.
  143. *
  144. * Apply a 180-degree transformation to a canvas, choosing characters
  145. * that look like the upside-down version wherever possible. Some
  146. * characters will stay unchanged by the process, but the operation is
  147. * guaranteed to be involutive: performing it again gives back the
  148. * original canvas.
  149. *
  150. * This function never fails.
  151. *
  152. * \param cv The canvas to rotate.
  153. * \return This function always returns 0.
  154. */
  155. int caca_rotate_180(caca_canvas_t *cv)
  156. {
  157. uint32_t *cbegin = cv->chars;
  158. uint32_t *cend = cbegin + cv->width * cv->height - 1;
  159. uint32_t *abegin = cv->attrs;
  160. uint32_t *aend = abegin + cv->width * cv->height - 1;
  161. int y;
  162. while(cbegin < cend)
  163. {
  164. uint32_t ch;
  165. uint32_t attr;
  166. /* Swap attributes */
  167. attr = *aend; *aend = *abegin; *abegin = attr;
  168. /* Swap characters */
  169. ch = *cend; *cend = rotatechar(*cbegin); *cbegin = rotatechar(ch);
  170. cbegin++; cend--; abegin++; aend--;
  171. }
  172. if(cbegin == cend)
  173. *cbegin = rotatechar(*cbegin);
  174. /* Fix fullwidth characters. Could it be done in one loop? */
  175. for(y = 0; y < cv->height; y++)
  176. {
  177. cbegin = cv->chars + y * cv->width;
  178. cend = cbegin + cv->width - 1;
  179. for( ; cbegin < cend; cbegin++)
  180. {
  181. if(cbegin[0] == CACA_MAGIC_FULLWIDTH)
  182. {
  183. cbegin[0] = cbegin[1];
  184. cbegin[1] = CACA_MAGIC_FULLWIDTH;
  185. cbegin++;
  186. }
  187. }
  188. }
  189. caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
  190. return 0;
  191. }
  192. /** \brief Rotate a canvas, 90 degrees counterclockwise.
  193. *
  194. * Apply a 90-degree transformation to a canvas, choosing characters
  195. * that look like the rotated version wherever possible. Characters cells
  196. * are rotated two-by-two. Some characters will stay unchanged by the
  197. * process, some others will be replaced by close equivalents. Fullwidth
  198. * characters at odd horizontal coordinates will be lost. The operation is
  199. * not guaranteed to be reversible at all.
  200. *
  201. * Note that the width of the canvas is divided by two and becomes the
  202. * new height. Height is multiplied by two and becomes the new width. If
  203. * the original width is an odd number, the division is rounded up.
  204. *
  205. * If an error occurs, -1 is returned and \b errno is set accordingly:
  206. * - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
  207. * - \c ENOMEM Not enough memory to allocate the new canvas size. If this
  208. * happens, the previous canvas handle is still valid.
  209. *
  210. * \param cv The canvas to rotate left.
  211. * \return 0 in case of success, -1 if an error occurred.
  212. */
  213. int caca_rotate_left(caca_canvas_t *cv)
  214. {
  215. uint32_t *newchars, *newattrs;
  216. int x, y, w2, h2;
  217. if(cv->refcount)
  218. {
  219. seterrno(EBUSY);
  220. return -1;
  221. }
  222. /* Save the current frame shortcuts */
  223. _caca_save_frame_info(cv);
  224. w2 = (cv->width + 1) / 2;
  225. h2 = cv->height;
  226. newchars = malloc(w2 * h2 * 2 * sizeof(uint32_t));
  227. if(!newchars)
  228. {
  229. seterrno(ENOMEM);
  230. return -1;
  231. }
  232. newattrs = malloc(w2 * h2 * 2 * sizeof(uint32_t));
  233. if(!newattrs)
  234. {
  235. free(newchars);
  236. seterrno(ENOMEM);
  237. return -1;
  238. }
  239. for(y = 0; y < h2; y++)
  240. {
  241. for(x = 0; x < w2; x++)
  242. {
  243. uint32_t pair[2], attr1, attr2;
  244. pair[0] = cv->chars[cv->width * y + x * 2];
  245. attr1 = cv->attrs[cv->width * y + x * 2];
  246. if((cv->width & 1) && x == w2 - 1)
  247. {
  248. /* Special case: odd column */
  249. pair[1] = ' ';
  250. attr2 = attr1;
  251. }
  252. else
  253. {
  254. pair[1] = cv->chars[cv->width * y + x * 2 + 1];
  255. attr2 = cv->attrs[cv->width * y + x * 2 + 1];
  256. }
  257. /* If one of the characters is a space, we simply ignore
  258. * its colour attributes. Otherwise the resulting characters
  259. * may have totally wrong colours. */
  260. if(pair[0] == ' ')
  261. attr1 = attr2;
  262. else if(pair[1] == ' ')
  263. attr2 = attr1;
  264. leftpair(pair);
  265. newchars[(h2 * (w2 - 1 - x) + y) * 2] = pair[0];
  266. newattrs[(h2 * (w2 - 1 - x) + y) * 2] = attr1;
  267. newchars[(h2 * (w2 - 1 - x) + y) * 2 + 1] = pair[1];
  268. newattrs[(h2 * (w2 - 1 - x) + y) * 2 + 1] = attr2;
  269. }
  270. }
  271. free(cv->chars);
  272. free(cv->attrs);
  273. /* Swap X and Y information */
  274. x = cv->frames[cv->frame].x;
  275. y = cv->frames[cv->frame].y;
  276. cv->frames[cv->frame].x = y * 2;
  277. cv->frames[cv->frame].y = (cv->width - 1 - x) / 2;
  278. x = cv->frames[cv->frame].handlex;
  279. y = cv->frames[cv->frame].handley;
  280. cv->frames[cv->frame].handlex = y * 2;
  281. cv->frames[cv->frame].handley = (cv->width - 1 - x) / 2;
  282. cv->frames[cv->frame].width = cv->height * 2;
  283. cv->frames[cv->frame].height = (cv->width + 1) / 2;
  284. cv->frames[cv->frame].chars = newchars;
  285. cv->frames[cv->frame].attrs = newattrs;
  286. /* Reset the current frame shortcuts */
  287. _caca_load_frame_info(cv);
  288. caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
  289. return 0;
  290. }
  291. /** \brief Rotate a canvas, 90 degrees counterclockwise.
  292. *
  293. * Apply a 90-degree transformation to a canvas, choosing characters
  294. * that look like the rotated version wherever possible. Characters cells
  295. * are rotated two-by-two. Some characters will stay unchanged by the
  296. * process, some others will be replaced by close equivalents. Fullwidth
  297. * characters at odd horizontal coordinates will be lost. The operation is
  298. * not guaranteed to be reversible at all.
  299. *
  300. * Note that the width of the canvas is divided by two and becomes the
  301. * new height. Height is multiplied by two and becomes the new width. If
  302. * the original width is an odd number, the division is rounded up.
  303. *
  304. * If an error occurs, -1 is returned and \b errno is set accordingly:
  305. * - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
  306. * - \c ENOMEM Not enough memory to allocate the new canvas size. If this
  307. * happens, the previous canvas handle is still valid.
  308. *
  309. * \param cv The canvas to rotate right.
  310. * \return 0 in case of success, -1 if an error occurred.
  311. */
  312. int caca_rotate_right(caca_canvas_t *cv)
  313. {
  314. uint32_t *newchars, *newattrs;
  315. int x, y, w2, h2;
  316. if(cv->refcount)
  317. {
  318. seterrno(EBUSY);
  319. return -1;
  320. }
  321. /* Save the current frame shortcuts */
  322. _caca_save_frame_info(cv);
  323. w2 = (cv->width + 1) / 2;
  324. h2 = cv->height;
  325. newchars = malloc(w2 * h2 * 2 * sizeof(uint32_t));
  326. if(!newchars)
  327. {
  328. seterrno(ENOMEM);
  329. return -1;
  330. }
  331. newattrs = malloc(w2 * h2 * 2 * sizeof(uint32_t));
  332. if(!newattrs)
  333. {
  334. free(newchars);
  335. seterrno(ENOMEM);
  336. return -1;
  337. }
  338. for(y = 0; y < h2; y++)
  339. {
  340. for(x = 0; x < w2; x++)
  341. {
  342. uint32_t pair[2], attr1, attr2;
  343. pair[0] = cv->chars[cv->width * y + x * 2];
  344. attr1 = cv->attrs[cv->width * y + x * 2];
  345. if((cv->width & 1) && x == w2 - 1)
  346. {
  347. /* Special case: odd column */
  348. pair[1] = ' ';
  349. attr2 = attr1;
  350. }
  351. else
  352. {
  353. pair[1] = cv->chars[cv->width * y + x * 2 + 1];
  354. attr2 = cv->attrs[cv->width * y + x * 2 + 1];
  355. }
  356. /* If one of the characters is a space, we simply ignore
  357. * its colour attributes. Otherwise the resulting characters
  358. * may have totally wrong colours. */
  359. if(pair[0] == ' ')
  360. attr1 = attr2;
  361. else if(pair[1] == ' ')
  362. attr2 = attr1;
  363. rightpair(pair);
  364. newchars[(h2 * x + h2 - 1 - y) * 2] = pair[0];
  365. newattrs[(h2 * x + h2 - 1 - y) * 2] = attr1;
  366. newchars[(h2 * x + h2 - 1 - y) * 2 + 1] = pair[1];
  367. newattrs[(h2 * x + h2 - 1 - y) * 2 + 1] = attr2;
  368. }
  369. }
  370. free(cv->chars);
  371. free(cv->attrs);
  372. /* Swap X and Y information */
  373. x = cv->frames[cv->frame].x;
  374. y = cv->frames[cv->frame].y;
  375. cv->frames[cv->frame].x = (cv->height - 1 - y) * 2;
  376. cv->frames[cv->frame].y = x / 2;
  377. x = cv->frames[cv->frame].handlex;
  378. y = cv->frames[cv->frame].handley;
  379. cv->frames[cv->frame].handlex = (cv->height - 1 - y) * 2;
  380. cv->frames[cv->frame].handley = x / 2;
  381. cv->frames[cv->frame].width = cv->height * 2;
  382. cv->frames[cv->frame].height = (cv->width + 1) / 2;
  383. cv->frames[cv->frame].chars = newchars;
  384. cv->frames[cv->frame].attrs = newattrs;
  385. /* Reset the current frame shortcuts */
  386. _caca_load_frame_info(cv);
  387. caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
  388. return 0;
  389. }
  390. /** \brief Rotate and stretch a canvas, 90 degrees counterclockwise.
  391. *
  392. * Apply a 90-degree transformation to a canvas, choosing characters
  393. * that look like the rotated version wherever possible. Some characters
  394. * will stay unchanged by the process, some others will be replaced by
  395. * close equivalents. Fullwidth characters will be lost. The operation is
  396. * not guaranteed to be reversible at all.
  397. *
  398. * Note that the width and height of the canvas are swapped, causing its
  399. * aspect ratio to look stretched.
  400. *
  401. * If an error occurs, -1 is returned and \b errno is set accordingly:
  402. * - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
  403. * - \c ENOMEM Not enough memory to allocate the new canvas size. If this
  404. * happens, the previous canvas handle is still valid.
  405. *
  406. * \param cv The canvas to rotate left.
  407. * \return 0 in case of success, -1 if an error occurred.
  408. */
  409. int caca_stretch_left(caca_canvas_t *cv)
  410. {
  411. uint32_t *newchars, *newattrs;
  412. int x, y;
  413. if(cv->refcount)
  414. {
  415. seterrno(EBUSY);
  416. return -1;
  417. }
  418. /* Save the current frame shortcuts */
  419. _caca_save_frame_info(cv);
  420. newchars = malloc(cv->width * cv->height * sizeof(uint32_t));
  421. if(!newchars)
  422. {
  423. seterrno(ENOMEM);
  424. return -1;
  425. }
  426. newattrs = malloc(cv->width * cv->height * sizeof(uint32_t));
  427. if(!newattrs)
  428. {
  429. free(newchars);
  430. seterrno(ENOMEM);
  431. return -1;
  432. }
  433. for(y = 0; y < cv->height; y++)
  434. {
  435. for(x = 0; x < cv->width; x++)
  436. {
  437. uint32_t ch, attr;
  438. ch = cv->chars[cv->width * y + x];
  439. attr = cv->attrs[cv->width * y + x];
  440. /* FIXME: do something about fullwidth characters */
  441. ch = leftchar(ch);
  442. newchars[cv->height * (cv->width - 1 - x) + y] = ch;
  443. newattrs[cv->height * (cv->width - 1 - x) + y] = attr;
  444. }
  445. }
  446. free(cv->chars);
  447. free(cv->attrs);
  448. /* Swap X and Y information */
  449. x = cv->frames[cv->frame].x;
  450. y = cv->frames[cv->frame].y;
  451. cv->frames[cv->frame].x = y;
  452. cv->frames[cv->frame].y = cv->width - 1 - x;
  453. x = cv->frames[cv->frame].handlex;
  454. y = cv->frames[cv->frame].handley;
  455. cv->frames[cv->frame].handlex = y;
  456. cv->frames[cv->frame].handley = cv->width - 1 - x;
  457. cv->frames[cv->frame].width = cv->height;
  458. cv->frames[cv->frame].height = cv->width;
  459. cv->frames[cv->frame].chars = newchars;
  460. cv->frames[cv->frame].attrs = newattrs;
  461. /* Reset the current frame shortcuts */
  462. _caca_load_frame_info(cv);
  463. caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
  464. return 0;
  465. }
  466. /** \brief Rotate and stretch a canvas, 90 degrees clockwise.
  467. *
  468. * Apply a 270-degree transformation to a canvas, choosing characters
  469. * that look like the rotated version wherever possible. Some characters
  470. * will stay unchanged by the process, some others will be replaced by
  471. * close equivalents. Fullwidth characters will be lost. The operation is
  472. * not guaranteed to be reversible at all.
  473. *
  474. * Note that the width and height of the canvas are swapped, causing its
  475. * aspect ratio to look stretched.
  476. *
  477. * If an error occurs, -1 is returned and \b errno is set accordingly:
  478. * - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
  479. * - \c ENOMEM Not enough memory to allocate the new canvas size. If this
  480. * happens, the previous canvas handle is still valid.
  481. *
  482. * \param cv The canvas to rotate right.
  483. * \return 0 in case of success, -1 if an error occurred.
  484. */
  485. int caca_stretch_right(caca_canvas_t *cv)
  486. {
  487. uint32_t *newchars, *newattrs;
  488. int x, y;
  489. if(cv->refcount)
  490. {
  491. seterrno(EBUSY);
  492. return -1;
  493. }
  494. /* Save the current frame shortcuts */
  495. _caca_save_frame_info(cv);
  496. newchars = malloc(cv->width * cv->height * sizeof(uint32_t));
  497. if(!newchars)
  498. {
  499. seterrno(ENOMEM);
  500. return -1;
  501. }
  502. newattrs = malloc(cv->width * cv->height * sizeof(uint32_t));
  503. if(!newattrs)
  504. {
  505. free(newchars);
  506. seterrno(ENOMEM);
  507. return -1;
  508. }
  509. for(y = 0; y < cv->height; y++)
  510. {
  511. for(x = 0; x < cv->width; x++)
  512. {
  513. uint32_t ch, attr;
  514. ch = cv->chars[cv->width * y + x];
  515. attr = cv->attrs[cv->width * y + x];
  516. /* FIXME: do something about fullwidth characters */
  517. ch = rightchar(ch);
  518. newchars[cv->height * x + cv->height - 1 - y] = ch;
  519. newattrs[cv->height * x + cv->height - 1 - y] = attr;
  520. }
  521. }
  522. free(cv->chars);
  523. free(cv->attrs);
  524. /* Swap X and Y information */
  525. x = cv->frames[cv->frame].x;
  526. y = cv->frames[cv->frame].y;
  527. cv->frames[cv->frame].x = cv->height - 1 - y;
  528. cv->frames[cv->frame].y = x;
  529. x = cv->frames[cv->frame].handlex;
  530. y = cv->frames[cv->frame].handley;
  531. cv->frames[cv->frame].handlex = cv->height - 1 - y;
  532. cv->frames[cv->frame].handley = x;
  533. cv->frames[cv->frame].width = cv->height;
  534. cv->frames[cv->frame].height = cv->width;
  535. cv->frames[cv->frame].chars = newchars;
  536. cv->frames[cv->frame].attrs = newattrs;
  537. /* Reset the current frame shortcuts */
  538. _caca_load_frame_info(cv);
  539. caca_add_dirty_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
  540. return 0;
  541. }
  542. /* FIXME: as the lookup tables grow bigger, use a log(n) lookup instead
  543. * of linear lookup. */
  544. static uint32_t flipchar(uint32_t ch)
  545. {
  546. int i;
  547. static uint32_t const noflip[] =
  548. {
  549. /* ASCII */
  550. ' ', '"', '#', '\'', '-', '.', '*', '+', ':', '=', '0', '8',
  551. 'A', 'H', 'I', 'M', 'O', 'T', 'U', 'V', 'W', 'X', 'Y', '^',
  552. '_', 'i', 'o', 'v', 'w', 'x', '|',
  553. /* CP437 and box drawing */
  554. 0x2591, 0x2592, 0x2593, 0x2588, 0x2584, 0x2580, /* ░ ▒ ▓ █ ▄ ▀ */
  555. 0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */
  556. 0x252c, 0x2534, 0x2533, 0x253b, 0x2566, 0x2569, /* ┬ ┴ ┳ ┻ ╦ ╩ */
  557. 0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */
  558. 0x2575, 0x2577, 0x2579, 0x257b, /* ╵ ╷ ╹ ╻ */
  559. 0
  560. };
  561. static uint32_t const pairs[] =
  562. {
  563. /* ASCII */
  564. '(', ')',
  565. '/', '\\',
  566. '<', '>',
  567. '[', ']',
  568. 'b', 'd',
  569. 'p', 'q',
  570. '{', '}',
  571. /* ASCII-Unicode */
  572. ';', 0x204f, /* ; ⁏ */
  573. '`', 0x00b4, /* ` ´ */
  574. ',', 0x02ce, /* , ˎ */
  575. '1', 0x07c1, /* 1 ߁ */
  576. 'B', 0x10412,/* B 𐐒 */
  577. 'C', 0x03fd, /* C Ͻ */
  578. 'D', 0x15e1, /* D ᗡ */
  579. 'E', 0x018e, /* E Ǝ */
  580. 'J', 0x1490, /* J ᒐ */
  581. 'L', 0x2143, /* L ⅃ */
  582. 'N', 0x0418, /* N И */
  583. 'P', 0x1040b,/* P 𐐋 */
  584. 'R', 0x042f, /* R Я */
  585. 'S', 0x01a7, /* S Ƨ */
  586. 'c', 0x0254, /* c ɔ */
  587. 'e', 0x0258, /* e ɘ */
  588. /* CP437 */
  589. 0x258c, 0x2590, /* ▌ ▐ */
  590. 0x2596, 0x2597, /* ▖ ▗ */
  591. 0x2598, 0x259d, /* ▘ ▝ */
  592. 0x2599, 0x259f, /* ▙ ▟ */
  593. 0x259a, 0x259e, /* ▚ ▞ */
  594. 0x259b, 0x259c, /* ▛ ▜ */
  595. 0x25ba, 0x25c4, /* ► ◄ */
  596. 0x2192, 0x2190, /* → ← */
  597. 0x2310, 0xac, /* ⌐ ¬ */
  598. /* Box drawing */
  599. 0x250c, 0x2510, /* ┌ ┐ */
  600. 0x2514, 0x2518, /* └ ┘ */
  601. 0x251c, 0x2524, /* ├ ┤ */
  602. 0x250f, 0x2513, /* ┏ ┓ */
  603. 0x2517, 0x251b, /* ┗ ┛ */
  604. 0x2523, 0x252b, /* ┣ ┫ */
  605. 0x2552, 0x2555, /* ╒ ╕ */
  606. 0x2558, 0x255b, /* ╘ ╛ */
  607. 0x2553, 0x2556, /* ╓ ╖ */
  608. 0x2559, 0x255c, /* ╙ ╜ */
  609. 0x2554, 0x2557, /* ╔ ╗ */
  610. 0x255a, 0x255d, /* ╚ ╝ */
  611. 0x255e, 0x2561, /* ╞ ╡ */
  612. 0x255f, 0x2562, /* ╟ ╢ */
  613. 0x2560, 0x2563, /* ╠ ╣ */
  614. 0x2574, 0x2576, /* ╴ ╶ */
  615. 0x2578, 0x257a, /* ╸ ╺ */
  616. 0
  617. };
  618. for(i = 0; noflip[i]; i++)
  619. if(ch == noflip[i])
  620. return ch;
  621. for(i = 0; pairs[i]; i++)
  622. if(ch == pairs[i])
  623. return pairs[i ^ 1];
  624. return ch;
  625. }
  626. static uint32_t flopchar(uint32_t ch)
  627. {
  628. int i;
  629. static uint32_t const noflop[] =
  630. {
  631. /* ASCII */
  632. ' ', '(', ')', '*', '+', '-', '0', '3', '8', ':', '<', '=',
  633. '>', 'B', 'C', 'D', 'E', 'H', 'I', 'K', 'O', 'X', '[', ']',
  634. 'c', 'o', '{', '|', '}',
  635. /* CP437 and box drawing */
  636. 0x2591, 0x2592, 0x2593, 0x2588, 0x258c, 0x2590, /* ░ ▒ ▓ █ ▌ ▐ */
  637. 0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */
  638. 0x251c, 0x2524, 0x2523, 0x252b, 0x2560, 0x2563, /* ├ ┤ ┣ ┫ ╠ ╣ */
  639. 0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */
  640. 0x2574, 0x2576, 0x2578, 0x257a, /* ╴ ╶ ╸ ╺ */
  641. 0
  642. };
  643. static uint32_t const pairs[] =
  644. {
  645. /* ASCII */
  646. '/', '\\',
  647. 'M', 'W',
  648. ',', '`',
  649. 'b', 'p',
  650. 'd', 'q',
  651. 'p', 'q',
  652. 'f', 't',
  653. '.', '\'',
  654. /* ASCII-Unicode */
  655. '_', 0x203e, /* _ ‾ */
  656. '!', 0x00a1, /* ! ¡ */
  657. 'A', 0x2200, /* A ∀ */
  658. 'J', 0x1489, /* J ᒉ */
  659. 'L', 0x0413, /* L Г */
  660. 'N', 0x0418, /* N И */
  661. 'P', 0x042c, /* P Ь */
  662. 'R', 0x0281, /* R ʁ */
  663. 'S', 0x01a7, /* S Ƨ */
  664. 'U', 0x0548, /* U Ո */
  665. 'V', 0x039b, /* V Λ */
  666. 'Y', 0x2144, /* Y ⅄ */
  667. 'h', 0x03bc, /* h μ */
  668. 'i', 0x1d09, /* i ᴉ */
  669. 'j', 0x1e37, /* j ḷ */
  670. 'l', 0x0237, /* l ȷ */
  671. 'v', 0x028c, /* v ʌ */
  672. 'w', 0x028d, /* w ʍ */
  673. 'y', 0x03bb, /* y λ */
  674. /* Not perfect, but better than nothing */
  675. '"', 0x201e, /* " „ */
  676. 'm', 0x026f, /* m ɯ */
  677. 'n', 'u',
  678. /* CP437 */
  679. 0x2584, 0x2580, /* ▄ ▀ */
  680. 0x2596, 0x2598, /* ▖ ▘ */
  681. 0x2597, 0x259d, /* ▗ ▝ */
  682. 0x2599, 0x259b, /* ▙ ▛ */
  683. 0x259f, 0x259c, /* ▟ ▜ */
  684. 0x259a, 0x259e, /* ▚ ▞ */
  685. /* Box drawing */
  686. 0x250c, 0x2514, /* ┌ └ */
  687. 0x2510, 0x2518, /* ┐ ┘ */
  688. 0x252c, 0x2534, /* ┬ ┴ */
  689. 0x250f, 0x2517, /* ┏ ┗ */
  690. 0x2513, 0x251b, /* ┓ ┛ */
  691. 0x2533, 0x253b, /* ┳ ┻ */
  692. 0x2554, 0x255a, /* ╔ ╚ */
  693. 0x2557, 0x255d, /* ╗ ╝ */
  694. 0x2566, 0x2569, /* ╦ ╩ */
  695. 0x2552, 0x2558, /* ╒ ╘ */
  696. 0x2555, 0x255b, /* ╕ ╛ */
  697. 0x2564, 0x2567, /* ╤ ╧ */
  698. 0x2553, 0x2559, /* ╓ ╙ */
  699. 0x2556, 0x255c, /* ╖ ╜ */
  700. 0x2565, 0x2568, /* ╥ ╨ */
  701. 0x2575, 0x2577, /* ╵ ╷ */
  702. 0x2579, 0x257b, /* ╹ ╻ */
  703. 0
  704. };
  705. for(i = 0; noflop[i]; i++)
  706. if(ch == noflop[i])
  707. return ch;
  708. for(i = 0; pairs[i]; i++)
  709. if(ch == pairs[i])
  710. return pairs[i ^ 1];
  711. return ch;
  712. }
  713. static uint32_t rotatechar(uint32_t ch)
  714. {
  715. int i;
  716. static uint32_t const norotate[] =
  717. {
  718. /* ASCII */
  719. ' ', '*', '+', '-', '/', '0', '8', ':', '=', 'H', 'I', 'N',
  720. 'O', 'S', 'X', 'Z', '\\', 'o', 's', 'x', 'z', '|',
  721. /* Unicode */
  722. 0x2591, 0x2592, 0x2593, 0x2588, 0x259a, 0x259e, /* ░ ▒ ▓ █ ▚ ▞ */
  723. 0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */
  724. 0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */
  725. 0
  726. };
  727. static uint32_t const pairs[] =
  728. {
  729. /* ASCII */
  730. '(', ')',
  731. '<', '>',
  732. '[', ']',
  733. '{', '}',
  734. '.', '\'',
  735. '6', '9',
  736. 'M', 'W',
  737. 'b', 'q',
  738. 'd', 'p',
  739. 'n', 'u',
  740. /* ASCII-Unicode */
  741. '_', 0x203e, /* _ ‾ */
  742. ',', 0x00b4, /* , ´ */
  743. '`', 0x02ce, /* ` ˎ */
  744. '&', 0x214b, /* & ⅋ */
  745. '!', 0x00a1, /* ! ¡ */
  746. '?', 0x00bf, /* ? ¿ */
  747. 'A', 0x2200, /* A ∀ */
  748. 'B', 0x10412,/* B 𐐒 */
  749. 'C', 0x03fd, /* C Ͻ */
  750. 'D', 0x15e1, /* D ᗡ */
  751. 'E', 0x018e, /* E Ǝ */
  752. 'F', 0x2132, /* F Ⅎ -- 0x07c3 looks better, but is RTL */
  753. 'G', 0x2141, /* G ⅁ */
  754. 'J', 0x148b, /* J ᒋ */
  755. 'L', 0x2142, /* L ⅂ */
  756. 'U', 0x0548, /* U Ո */
  757. 'V', 0x039b, /* V Λ */
  758. 'Y', 0x2144, /* Y ⅄ */
  759. 'a', 0x0250, /* a ɐ */
  760. 'c', 0x0254, /* c ɔ */
  761. 'e', 0x01dd, /* e ǝ */
  762. 'f', 0x025f, /* f ɟ */
  763. 'g', 0x1d77, /* g ᵷ */
  764. 'h', 0x0265, /* h ɥ */
  765. 'i', 0x1d09, /* i ᴉ */
  766. 'j', 0x1e37, /* j ḷ */
  767. 'k', 0x029e, /* k ʞ */
  768. 'l', 0x0237, /* l ȷ */
  769. 'm', 0x026f, /* m ɯ */
  770. 'r', 0x0279, /* r ɹ */
  771. 't', 0x0287, /* t ʇ */
  772. 'v', 0x028c, /* v ʌ */
  773. 'w', 0x028d, /* w ʍ */
  774. 'y', 0x028e, /* y ʎ */
  775. /* Unicode-ASCII to match third-party software */
  776. 0x0183, 'g', /* ƃ g */
  777. 0x0259, 'e', /* ə e */
  778. 0x027e, 'j', /* ɾ j */
  779. 0x02d9, '.', /* ˙ . */
  780. 0x05df, 'l', /* ן l */
  781. /* Not perfect, but better than nothing */
  782. '"', 0x201e, /* " „ */
  783. /* Misc Unicode */
  784. 0x00e6, 0x1d02, /* æ ᴂ */
  785. 0x0153, 0x1d14, /* œ ᴔ */
  786. /* CP437 */
  787. 0x258c, 0x2590, /* ▌ ▐ */
  788. 0x2584, 0x2580, /* ▄ ▀ */
  789. 0x2596, 0x259d, /* ▖ ▝ */
  790. 0x2597, 0x2598, /* ▗ ▘ */
  791. 0x2599, 0x259c, /* ▙ ▜ */
  792. 0x259f, 0x259b, /* ▟ ▛ */
  793. /* Box drawing */
  794. 0x250c, 0x2518, /* ┌ ┘ */
  795. 0x2510, 0x2514, /* ┐ └ */
  796. 0x251c, 0x2524, /* ├ ┤ */
  797. 0x252c, 0x2534, /* ┬ ┴ */
  798. 0x250f, 0x251b, /* ┏ ┛ */
  799. 0x2513, 0x2517, /* ┓ ┗ */
  800. 0x2523, 0x252b, /* ┣ ┫ */
  801. 0x2533, 0x253b, /* ┳ ┻ */
  802. 0x2554, 0x255d, /* ╔ ╝ */
  803. 0x2557, 0x255a, /* ╗ ╚ */
  804. 0x2560, 0x2563, /* ╠ ╣ */
  805. 0x2566, 0x2569, /* ╦ ╩ */
  806. 0x2552, 0x255b, /* ╒ ╛ */
  807. 0x2555, 0x2558, /* ╕ ╘ */
  808. 0x255e, 0x2561, /* ╞ ╡ */
  809. 0x2564, 0x2567, /* ╤ ╧ */
  810. 0x2553, 0x255c, /* ╓ ╜ */
  811. 0x2556, 0x2559, /* ╖ ╙ */
  812. 0x255f, 0x2562, /* ╟ ╢ */
  813. 0x2565, 0x2568, /* ╥ ╨ */
  814. 0x2574, 0x2576, /* ╴ ╶ */
  815. 0x2575, 0x2577, /* ╵ ╷ */
  816. 0x2578, 0x257a, /* ╸ ╺ */
  817. 0x2579, 0x257b, /* ╹ ╻ */
  818. 0
  819. };
  820. for(i = 0; norotate[i]; i++)
  821. if(ch == norotate[i])
  822. return ch;
  823. for(i = 0; pairs[i]; i++)
  824. if(ch == pairs[i])
  825. return pairs[i ^ 1];
  826. return ch;
  827. }
  828. static uint32_t const leftright2[] =
  829. {
  830. /* ASCII */
  831. '/', '\\',
  832. '|', '-',
  833. '|', '_', /* This is all right because there was already a '|' before */
  834. /* ASCII-Unicode */
  835. '|', 0x203e, /* | ‾ */
  836. /* Misc Unicode */
  837. 0x2571, 0x2572, /* ╱ ╲ */
  838. /* Box drawing */
  839. 0x2500, 0x2502, /* ─ │ */
  840. 0x2501, 0x2503, /* ━ ┃ */
  841. 0x2550, 0x2551, /* ═ ║ */
  842. 0, 0
  843. };
  844. static uint32_t const leftright4[] =
  845. {
  846. /* ASCII */
  847. '<', 'v', '>', '^',
  848. ',', '.', '\'', '`',
  849. /* ASCII / Unicode */
  850. '(', 0x203f, ')', 0x2040, /* ( ‿ ) ⁀ */
  851. /* Misc Unicode */
  852. 0x256d, 0x2570, 0x256f, 0x256e, /* ╭ ╰ ╯ ╮ */
  853. /* CP437 */
  854. 0x258c, 0x2584, 0x2590, 0x2580, /* ▌ ▄ ▐ ▀ */
  855. 0x2596, 0x2597, 0x259d, 0x2598, /* ▖ ▗ ▝ ▘ */
  856. 0x2599, 0x259f, 0x259c, 0x259b, /* ▙ ▟ ▜ ▛ */
  857. /* Box drawing */
  858. 0x250c, 0x2514, 0x2518, 0x2510, /* ┌ └ ┘ ┐ */
  859. 0x250f, 0x2517, 0x251b, 0x2513, /* ┏ ┗ ┛ ┓ */
  860. 0x251c, 0x2534, 0x2524, 0x252c, /* ├ ┴ ┤ ┬ */
  861. 0x2523, 0x253b, 0x252b, 0x2533, /* ┣ ┻ ┫ ┳ */
  862. 0x2552, 0x2559, 0x255b, 0x2556, /* ╒ ╙ ╛ ╖ */
  863. 0x2553, 0x2558, 0x255c, 0x2555, /* ╓ ╘ ╜ ╕ */
  864. 0x2554, 0x255a, 0x255d, 0x2557, /* ╔ ╚ ╝ ╗ */
  865. 0x255e, 0x2568, 0x2561, 0x2565, /* ╞ ╨ ╡ ╥ */
  866. 0x255f, 0x2567, 0x2562, 0x2564, /* ╟ ╧ ╢ ╤ */
  867. 0x2560, 0x2569, 0x2563, 0x2566, /* ╠ ╩ ╣ ╦ */
  868. 0x2574, 0x2577, 0x2576, 0x2575, /* ╴ ╷ ╶ ╵ */
  869. 0x2578, 0x257b, 0x257a, 0x2579, /* ╸ ╻ ╺ ╹ */
  870. 0, 0, 0, 0
  871. };
  872. static uint32_t leftchar(uint32_t ch)
  873. {
  874. int i;
  875. for(i = 0; leftright2[i]; i++)
  876. if(ch == leftright2[i])
  877. return leftright2[(i & ~1) | ((i + 1) & 1)];
  878. for(i = 0; leftright4[i]; i++)
  879. if(ch == leftright4[i])
  880. return leftright4[(i & ~3) | ((i + 1) & 3)];
  881. return ch;
  882. }
  883. static uint32_t rightchar(uint32_t ch)
  884. {
  885. int i;
  886. for(i = 0; leftright2[i]; i++)
  887. if(ch == leftright2[i])
  888. return leftright2[(i & ~1) | ((i - 1) & 1)];
  889. for(i = 0; leftright4[i]; i++)
  890. if(ch == leftright4[i])
  891. return leftright4[(i & ~3) | ((i - 1) & 3)];
  892. return ch;
  893. }
  894. static uint32_t const leftright2x2[] =
  895. {
  896. /* ASCII / Unicode */
  897. '-', '-', 0x4e28, CACA_MAGIC_FULLWIDTH, /* -- 丨 */
  898. '|', '|', 0x2f06, CACA_MAGIC_FULLWIDTH, /* || ⼆ */
  899. /* Unicode */
  900. 0x2584, 0x2580, 0x2580, 0x2584, /* ▄▀ ▀▄ */
  901. 0, 0, 0, 0
  902. };
  903. static uint32_t const leftright2x4[] =
  904. {
  905. /* ASCII */
  906. ':', ' ', '.', '.', ' ', ':', '\'', '\'',
  907. /* ASCII / Unicode */
  908. ' ', '`', 0x00b4, ' ', 0x02ce, ' ', ' ', ',', /* ` ´ ˎ , */
  909. ' ', '`', '\'', ' ', '.', ' ', ' ', ',', /* fallback ASCII */
  910. '`', ' ', ',', ' ', ' ', 0x00b4, ' ', 0x02ce, /* ` , ˎ ´ */
  911. '`', ' ', ',', ' ', ' ', '.', ' ', '\'', /* fallback ASCII */
  912. '/', ' ', '-', 0x02ce, ' ', '/', '`', '-', /* / -ˎ / `- */
  913. '/', ' ', '-', '.', ' ', '/', '\'', '-', /* fallback ASCII */
  914. '\\', ' ', ',', '-', ' ', '\\', '-', 0x00b4, /* \ ,- \ -´ */
  915. '\\', ' ', '.', '-', ' ', '\\', '-', '\'', /* fallback ASCII */
  916. '\\', ' ', '_', ',', ' ', '\\', 0x00b4, 0x203e, /* \ _, \ ´‾ */
  917. '\\', '_', '_', '/', 0x203e, '\\', '/', 0x203e, /* \_ _/ ‾\ /‾ */
  918. '_', '\\', 0x203e, '/', '\\', 0x203e, '/', '_', /* _\ ‾/ \‾ /_ */
  919. '|', ' ', '_', '_', ' ', '|', 0x203e, 0x203e, /* | __ | ‾‾ */
  920. '_', '|', 0x203e, '|', '|', 0x203e, '|', '_', /* _| ‾| |‾ |_ */
  921. '|', '_', '_', '|', 0x203e, '|', '|', 0x203e, /* |_ _| ‾| |‾ */
  922. '_', ' ', ' ', 0x2577, ' ', 0x203e, 0x2575, ' ', /* _ ╷ ‾ ╵ */
  923. ' ', '_', ' ', 0x2575, 0x203e, ' ', 0x2577, ' ', /* _ ╵ ‾ ╷ */
  924. '.', '_', '.', 0x2575, 0x203e, '\'', 0x2577, '\'', /* ._ .╵ ‾' ╷' */
  925. '(', '_', 0x203f, '|', 0x203e, ')', '|', 0x2040, /* (_ ‿| ‾) |⁀ */
  926. '(', 0x203e, '|', 0x203f, '_', ')', 0x2040, '|', /* (‾ |‿ _) ⁀| */
  927. '\\', '/', 0xff1e, CACA_MAGIC_FULLWIDTH,
  928. '/', '\\', 0xff1c, CACA_MAGIC_FULLWIDTH, /* \/ > /\ < */
  929. ')', ' ', 0xfe35, CACA_MAGIC_FULLWIDTH,
  930. ' ', '(', 0xfe36, CACA_MAGIC_FULLWIDTH, /* ) ︵ ( ︶ */
  931. '}', ' ', 0xfe37, CACA_MAGIC_FULLWIDTH,
  932. ' ', '{', 0xfe38, CACA_MAGIC_FULLWIDTH, /* } ︷ { ︸ */
  933. /* Not perfect, but better than nothing */
  934. '(', ' ', 0x02ce, ',', ' ', ')', 0x00b4, '`', /* ( ˎ, ) ´` */
  935. ' ', 'v', '>', ' ', 0x028c, ' ', ' ', '<', /* v > ʌ < */
  936. ' ', 'V', '>', ' ', 0x039b, ' ', ' ', '<', /* V > Λ < */
  937. 'v', ' ', '>', ' ', ' ', 0x028c, ' ', '<', /* v > ʌ < */
  938. 'V', ' ', '>', ' ', ' ', 0x039b, ' ', '<', /* V > Λ < */
  939. '\\', '|', 0xff1e, CACA_MAGIC_FULLWIDTH,
  940. '|', '\\', 0xff1c, CACA_MAGIC_FULLWIDTH, /* \| > |\ < */
  941. '|', '/', 0xff1e, CACA_MAGIC_FULLWIDTH,
  942. '/', '|', 0xff1c, CACA_MAGIC_FULLWIDTH, /* |/ > /| < */
  943. /* Unicode */
  944. 0x2584, ' ', ' ', 0x2584, ' ', 0x2580, 0x2580, ' ', /* ▄ ▄ ▀ ▀ */
  945. 0x2588, ' ', 0x2584, 0x2584, ' ', 0x2588, 0x2580, 0x2580, /* █ ▄▄ █ ▀▀ */
  946. 0x2588, 0x2584, 0x2584, 0x2588,
  947. 0x2580, 0x2588, 0x2588, 0x2580, /* █▄ ▄█ ▀█ █▀ */
  948. /* TODO: Braille */
  949. /* Not perfect, but better than nothing */
  950. 0x2591, ' ', 0x28e4, 0x28e4, ' ', 0x2591, 0x281b, 0x281b, /* ░ ⣤⣤ ░ ⠛⠛ */
  951. 0x2592, ' ', 0x28f6, 0x28f6, ' ', 0x2592, 0x283f, 0x283f, /* ▒ ⣶⣶ ▒ ⠿⠿ */
  952. 0, 0, 0, 0, 0, 0, 0, 0
  953. };
  954. static void leftpair(uint32_t pair[2])
  955. {
  956. int i;
  957. for(i = 0; leftright2x2[i]; i += 2)
  958. if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1])
  959. {
  960. pair[0] = leftright2x2[(i & ~3) | ((i + 2) & 3)];
  961. pair[1] = leftright2x2[((i & ~3) | ((i + 2) & 3)) + 1];
  962. return;
  963. }
  964. for(i = 0; leftright2x4[i]; i += 2)
  965. if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1])
  966. {
  967. pair[0] = leftright2x4[(i & ~7) | ((i + 2) & 7)];
  968. pair[1] = leftright2x4[((i & ~7) | ((i + 2) & 7)) + 1];
  969. return;
  970. }
  971. }
  972. static void rightpair(uint32_t pair[2])
  973. {
  974. int i;
  975. for(i = 0; leftright2x2[i]; i += 2)
  976. if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1])
  977. {
  978. pair[0] = leftright2x2[(i & ~3) | ((i - 2) & 3)];
  979. pair[1] = leftright2x2[((i & ~3) | ((i - 2) & 3)) + 1];
  980. return;
  981. }
  982. for(i = 0; leftright2x4[i]; i += 2)
  983. if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1])
  984. {
  985. pair[0] = leftright2x4[(i & ~7) | ((i - 2) & 7)];
  986. pair[1] = leftright2x4[((i & ~7) | ((i - 2) & 7)) + 1];
  987. return;
  988. }
  989. }
  990. /*
  991. * XXX: The following functions are aliases.
  992. */
  993. int cucul_invert(cucul_canvas_t *) CACA_ALIAS(caca_invert);
  994. int cucul_flip(cucul_canvas_t *) CACA_ALIAS(caca_flip);
  995. int cucul_flop(cucul_canvas_t *) CACA_ALIAS(caca_flop);
  996. int cucul_rotate_180(cucul_canvas_t *) CACA_ALIAS(caca_rotate_180);
  997. int cucul_rotate_left(cucul_canvas_t *) CACA_ALIAS(caca_rotate_left);
  998. int cucul_rotate_right(cucul_canvas_t *) CACA_ALIAS(caca_rotate_right);
  999. int cucul_stretch_left(cucul_canvas_t *) CACA_ALIAS(caca_stretch_left);
  1000. int cucul_stretch_right(cucul_canvas_t *) CACA_ALIAS(caca_stretch_right);