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.

transform.c 33 KiB

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