|
- /*
- * libcaca Colour ASCII-Art library
- * Copyright (c) 2002-2009 Sam Hocevar <sam@hocevar.net>
- * All Rights Reserved
- *
- * $Id$
- *
- * This library is free software. It comes without any warranty, to
- * the extent permitted by applicable law. You can redistribute it
- * and/or modify it under the terms of the Do What The Fuck You Want
- * To Public License, Version 2, as published by Sam Hocevar. See
- * http://sam.zoy.org/wtfpl/COPYING for more details.
- */
-
- /*
- * This file contains horizontal and vertical flipping routines.
- */
-
- #include "config.h"
-
- #if !defined(__KERNEL__)
- # include <stdlib.h>
- #endif
-
- #include "caca.h"
- #include "caca_internals.h"
-
- static uint32_t flipchar(uint32_t ch);
- static uint32_t flopchar(uint32_t ch);
- static uint32_t rotatechar(uint32_t ch);
- static uint32_t leftchar(uint32_t ch);
- static uint32_t rightchar(uint32_t ch);
- static void leftpair(uint32_t pair[2]);
- static void rightpair(uint32_t pair[2]);
-
- /** \brief Invert a canvas' colours.
- *
- * Invert a canvas' colours (black becomes white, red becomes cyan, etc.)
- * without changing the characters in it.
- *
- * This function never fails.
- *
- * \param cv The canvas to invert.
- * \return This function always returns 0.
- */
- int caca_invert(caca_canvas_t *cv)
- {
- uint32_t *attrs = cv->attrs;
- int i;
-
- for(i = cv->height * cv->width; i--; )
- {
- *attrs = *attrs ^ 0x000f000f;
- attrs++;
- }
-
- if(!cv->dirty_disabled)
- caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
-
- return 0;
- }
-
- /** \brief Flip a canvas horizontally.
- *
- * Flip a canvas horizontally, choosing characters that look like the
- * mirrored version wherever possible. Some characters will stay
- * unchanged by the process, but the operation is guaranteed to be
- * involutive: performing it again gives back the original canvas.
- *
- * This function never fails.
- *
- * \param cv The canvas to flip.
- * \return This function always returns 0.
- */
- int caca_flip(caca_canvas_t *cv)
- {
- int y;
-
- for(y = 0; y < cv->height; y++)
- {
- uint32_t *cleft = cv->chars + y * cv->width;
- uint32_t *cright = cleft + cv->width - 1;
- uint32_t *aleft = cv->attrs + y * cv->width;
- uint32_t *aright = aleft + cv->width - 1;
-
- while(cleft < cright)
- {
- uint32_t ch;
- uint32_t attr;
-
- /* Swap attributes */
- attr = *aright;
- *aright-- = *aleft;
- *aleft++ = attr;
-
- /* Swap characters */
- ch = *cright;
- *cright-- = flipchar(*cleft);
- *cleft++ = flipchar(ch);
- }
-
- if(cleft == cright)
- *cleft = flipchar(*cleft);
-
- /* Fix fullwidth characters. Could it be done in one loop? */
- cleft = cv->chars + y * cv->width;
- cright = cleft + cv->width - 1;
- for( ; cleft < cright; cleft++)
- {
- if(cleft[0] == CACA_MAGIC_FULLWIDTH)
- {
- cleft[0] = cleft[1];
- cleft[1] = CACA_MAGIC_FULLWIDTH;
- cleft++;
- }
- }
- }
-
- if(!cv->dirty_disabled)
- caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
-
- return 0;
- }
-
- /** \brief Flip a canvas vertically.
- *
- * Flip a canvas vertically, choosing characters that look like the
- * mirrored version wherever possible. Some characters will stay
- * unchanged by the process, but the operation is guaranteed to be
- * involutive: performing it again gives back the original canvas.
- *
- * This function never fails.
- *
- * \param cv The canvas to flop.
- * \return This function always returns 0.
- */
- int caca_flop(caca_canvas_t *cv)
- {
- int x;
-
- for(x = 0; x < cv->width; x++)
- {
- uint32_t *ctop = cv->chars + x;
- uint32_t *cbottom = ctop + cv->width * (cv->height - 1);
- uint32_t *atop = cv->attrs + x;
- uint32_t *abottom = atop + cv->width * (cv->height - 1);
-
- while(ctop < cbottom)
- {
- uint32_t ch;
- uint32_t attr;
-
- /* Swap attributes */
- attr = *abottom; *abottom = *atop; *atop = attr;
-
- /* Swap characters */
- ch = *cbottom; *cbottom = flopchar(*ctop); *ctop = flopchar(ch);
-
- ctop += cv->width; cbottom -= cv->width;
- atop += cv->width; abottom -= cv->width;
- }
-
- if(ctop == cbottom)
- *ctop = flopchar(*ctop);
- }
-
- if(!cv->dirty_disabled)
- caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
-
- return 0;
- }
-
- /** \brief Rotate a canvas.
- *
- * Apply a 180-degree transformation to a canvas, choosing characters
- * that look like the upside-down version wherever possible. Some
- * characters will stay unchanged by the process, but the operation is
- * guaranteed to be involutive: performing it again gives back the
- * original canvas.
- *
- * This function never fails.
- *
- * \param cv The canvas to rotate.
- * \return This function always returns 0.
- */
- int caca_rotate_180(caca_canvas_t *cv)
- {
- uint32_t *cbegin = cv->chars;
- uint32_t *cend = cbegin + cv->width * cv->height - 1;
- uint32_t *abegin = cv->attrs;
- uint32_t *aend = abegin + cv->width * cv->height - 1;
- int y;
-
- if(!cbegin)
- return 0;
-
- while(cbegin < cend)
- {
- uint32_t ch;
- uint32_t attr;
-
- /* Swap attributes */
- attr = *aend; *aend = *abegin; *abegin = attr;
-
- /* Swap characters */
- ch = *cend; *cend = rotatechar(*cbegin); *cbegin = rotatechar(ch);
-
- cbegin++; cend--; abegin++; aend--;
- }
-
- if(cbegin == cend)
- *cbegin = rotatechar(*cbegin);
-
- /* Fix fullwidth characters. Could it be done in one loop? */
- for(y = 0; y < cv->height; y++)
- {
- cbegin = cv->chars + y * cv->width;
- cend = cbegin + cv->width - 1;
- for( ; cbegin < cend; cbegin++)
- {
- if(cbegin[0] == CACA_MAGIC_FULLWIDTH)
- {
- cbegin[0] = cbegin[1];
- cbegin[1] = CACA_MAGIC_FULLWIDTH;
- cbegin++;
- }
- }
- }
-
- if(!cv->dirty_disabled)
- caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
-
- return 0;
- }
-
- /** \brief Rotate a canvas, 90 degrees counterclockwise.
- *
- * Apply a 90-degree transformation to a canvas, choosing characters
- * that look like the rotated version wherever possible. Characters cells
- * are rotated two-by-two. Some characters will stay unchanged by the
- * process, some others will be replaced by close equivalents. Fullwidth
- * characters at odd horizontal coordinates will be lost. The operation is
- * not guaranteed to be reversible at all.
- *
- * Note that the width of the canvas is divided by two and becomes the
- * new height. Height is multiplied by two and becomes the new width. If
- * the original width is an odd number, the division is rounded up.
- *
- * If an error occurs, -1 is returned and \b errno is set accordingly:
- * - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
- * - \c ENOMEM Not enough memory to allocate the new canvas size. If this
- * happens, the previous canvas handle is still valid.
- *
- * \param cv The canvas to rotate left.
- * \return 0 in case of success, -1 if an error occurred.
- */
- int caca_rotate_left(caca_canvas_t *cv)
- {
- uint32_t *newchars, *newattrs;
- int x, y, w2, h2;
-
- if(cv->refcount)
- {
- seterrno(EBUSY);
- return -1;
- }
-
- /* Save the current frame shortcuts */
- _caca_save_frame_info(cv);
-
- w2 = (cv->width + 1) / 2;
- h2 = cv->height;
-
- newchars = malloc(w2 * h2 * 2 * sizeof(uint32_t));
- if(!newchars)
- {
- seterrno(ENOMEM);
- return -1;
- }
-
- newattrs = malloc(w2 * h2 * 2 * sizeof(uint32_t));
- if(!newattrs)
- {
- free(newchars);
- seterrno(ENOMEM);
- return -1;
- }
-
- for(y = 0; y < h2; y++)
- {
- for(x = 0; x < w2; x++)
- {
- uint32_t pair[2], attr1, attr2;
-
- pair[0] = cv->chars[cv->width * y + x * 2];
- attr1 = cv->attrs[cv->width * y + x * 2];
-
- if((cv->width & 1) && x == w2 - 1)
- {
- /* Special case: odd column */
- pair[1] = ' ';
- attr2 = attr1;
- }
- else
- {
- pair[1] = cv->chars[cv->width * y + x * 2 + 1];
- attr2 = cv->attrs[cv->width * y + x * 2 + 1];
- }
-
- /* If one of the characters is a space, we simply ignore
- * its colour attributes. Otherwise the resulting characters
- * may have totally wrong colours. */
- if(pair[0] == ' ')
- attr1 = attr2;
- else if(pair[1] == ' ')
- attr2 = attr1;
-
- leftpair(pair);
-
- newchars[(h2 * (w2 - 1 - x) + y) * 2] = pair[0];
- newattrs[(h2 * (w2 - 1 - x) + y) * 2] = attr1;
- newchars[(h2 * (w2 - 1 - x) + y) * 2 + 1] = pair[1];
- newattrs[(h2 * (w2 - 1 - x) + y) * 2 + 1] = attr2;
- }
- }
-
- free(cv->chars);
- free(cv->attrs);
-
- /* Swap X and Y information */
- x = cv->frames[cv->frame].x;
- y = cv->frames[cv->frame].y;
- cv->frames[cv->frame].x = y * 2;
- cv->frames[cv->frame].y = (cv->width - 1 - x) / 2;
-
- x = cv->frames[cv->frame].handlex;
- y = cv->frames[cv->frame].handley;
- cv->frames[cv->frame].handlex = y * 2;
- cv->frames[cv->frame].handley = (cv->width - 1 - x) / 2;
-
- cv->frames[cv->frame].width = cv->height * 2;
- cv->frames[cv->frame].height = (cv->width + 1) / 2;
-
- cv->frames[cv->frame].chars = newchars;
- cv->frames[cv->frame].attrs = newattrs;
-
- /* Reset the current frame shortcuts */
- _caca_load_frame_info(cv);
-
- if(!cv->dirty_disabled)
- caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
-
- return 0;
- }
-
- /** \brief Rotate a canvas, 90 degrees counterclockwise.
- *
- * Apply a 90-degree transformation to a canvas, choosing characters
- * that look like the rotated version wherever possible. Characters cells
- * are rotated two-by-two. Some characters will stay unchanged by the
- * process, some others will be replaced by close equivalents. Fullwidth
- * characters at odd horizontal coordinates will be lost. The operation is
- * not guaranteed to be reversible at all.
- *
- * Note that the width of the canvas is divided by two and becomes the
- * new height. Height is multiplied by two and becomes the new width. If
- * the original width is an odd number, the division is rounded up.
- *
- * If an error occurs, -1 is returned and \b errno is set accordingly:
- * - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
- * - \c ENOMEM Not enough memory to allocate the new canvas size. If this
- * happens, the previous canvas handle is still valid.
- *
- * \param cv The canvas to rotate right.
- * \return 0 in case of success, -1 if an error occurred.
- */
- int caca_rotate_right(caca_canvas_t *cv)
- {
- uint32_t *newchars, *newattrs;
- int x, y, w2, h2;
-
- if(cv->refcount)
- {
- seterrno(EBUSY);
- return -1;
- }
-
- /* Save the current frame shortcuts */
- _caca_save_frame_info(cv);
-
- w2 = (cv->width + 1) / 2;
- h2 = cv->height;
-
- newchars = malloc(w2 * h2 * 2 * sizeof(uint32_t));
- if(!newchars)
- {
- seterrno(ENOMEM);
- return -1;
- }
-
- newattrs = malloc(w2 * h2 * 2 * sizeof(uint32_t));
- if(!newattrs)
- {
- free(newchars);
- seterrno(ENOMEM);
- return -1;
- }
-
- for(y = 0; y < h2; y++)
- {
- for(x = 0; x < w2; x++)
- {
- uint32_t pair[2], attr1, attr2;
-
- pair[0] = cv->chars[cv->width * y + x * 2];
- attr1 = cv->attrs[cv->width * y + x * 2];
-
- if((cv->width & 1) && x == w2 - 1)
- {
- /* Special case: odd column */
- pair[1] = ' ';
- attr2 = attr1;
- }
- else
- {
- pair[1] = cv->chars[cv->width * y + x * 2 + 1];
- attr2 = cv->attrs[cv->width * y + x * 2 + 1];
- }
-
- /* If one of the characters is a space, we simply ignore
- * its colour attributes. Otherwise the resulting characters
- * may have totally wrong colours. */
- if(pair[0] == ' ')
- attr1 = attr2;
- else if(pair[1] == ' ')
- attr2 = attr1;
-
- rightpair(pair);
-
- newchars[(h2 * x + h2 - 1 - y) * 2] = pair[0];
- newattrs[(h2 * x + h2 - 1 - y) * 2] = attr1;
- newchars[(h2 * x + h2 - 1 - y) * 2 + 1] = pair[1];
- newattrs[(h2 * x + h2 - 1 - y) * 2 + 1] = attr2;
- }
- }
-
- free(cv->chars);
- free(cv->attrs);
-
- /* Swap X and Y information */
- x = cv->frames[cv->frame].x;
- y = cv->frames[cv->frame].y;
- cv->frames[cv->frame].x = (cv->height - 1 - y) * 2;
- cv->frames[cv->frame].y = x / 2;
-
- x = cv->frames[cv->frame].handlex;
- y = cv->frames[cv->frame].handley;
- cv->frames[cv->frame].handlex = (cv->height - 1 - y) * 2;
- cv->frames[cv->frame].handley = x / 2;
-
- cv->frames[cv->frame].width = cv->height * 2;
- cv->frames[cv->frame].height = (cv->width + 1) / 2;
-
- cv->frames[cv->frame].chars = newchars;
- cv->frames[cv->frame].attrs = newattrs;
-
- /* Reset the current frame shortcuts */
- _caca_load_frame_info(cv);
-
- if(!cv->dirty_disabled)
- caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
-
- return 0;
- }
-
- /** \brief Rotate and stretch a canvas, 90 degrees counterclockwise.
- *
- * Apply a 90-degree transformation to a canvas, choosing characters
- * that look like the rotated version wherever possible. Some characters
- * will stay unchanged by the process, some others will be replaced by
- * close equivalents. Fullwidth characters will be lost. The operation is
- * not guaranteed to be reversible at all.
- *
- * Note that the width and height of the canvas are swapped, causing its
- * aspect ratio to look stretched.
- *
- * If an error occurs, -1 is returned and \b errno is set accordingly:
- * - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
- * - \c ENOMEM Not enough memory to allocate the new canvas size. If this
- * happens, the previous canvas handle is still valid.
- *
- * \param cv The canvas to rotate left.
- * \return 0 in case of success, -1 if an error occurred.
- */
- int caca_stretch_left(caca_canvas_t *cv)
- {
- uint32_t *newchars, *newattrs;
- int x, y;
-
- if(cv->refcount)
- {
- seterrno(EBUSY);
- return -1;
- }
-
- /* Save the current frame shortcuts */
- _caca_save_frame_info(cv);
-
- newchars = malloc(cv->width * cv->height * sizeof(uint32_t));
- if(!newchars)
- {
- seterrno(ENOMEM);
- return -1;
- }
-
- newattrs = malloc(cv->width * cv->height * sizeof(uint32_t));
- if(!newattrs)
- {
- free(newchars);
- seterrno(ENOMEM);
- return -1;
- }
-
- for(y = 0; y < cv->height; y++)
- {
- for(x = 0; x < cv->width; x++)
- {
- uint32_t ch, attr;
-
- ch = cv->chars[cv->width * y + x];
- attr = cv->attrs[cv->width * y + x];
-
- /* FIXME: do something about fullwidth characters */
- ch = leftchar(ch);
-
- newchars[cv->height * (cv->width - 1 - x) + y] = ch;
- newattrs[cv->height * (cv->width - 1 - x) + y] = attr;
- }
- }
-
- free(cv->chars);
- free(cv->attrs);
-
- /* Swap X and Y information */
- x = cv->frames[cv->frame].x;
- y = cv->frames[cv->frame].y;
- cv->frames[cv->frame].x = y;
- cv->frames[cv->frame].y = cv->width - 1 - x;
-
- x = cv->frames[cv->frame].handlex;
- y = cv->frames[cv->frame].handley;
- cv->frames[cv->frame].handlex = y;
- cv->frames[cv->frame].handley = cv->width - 1 - x;
-
- cv->frames[cv->frame].width = cv->height;
- cv->frames[cv->frame].height = cv->width;
-
- cv->frames[cv->frame].chars = newchars;
- cv->frames[cv->frame].attrs = newattrs;
-
- /* Reset the current frame shortcuts */
- _caca_load_frame_info(cv);
-
- caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
-
- return 0;
- }
-
- /** \brief Rotate and stretch a canvas, 90 degrees clockwise.
- *
- * Apply a 270-degree transformation to a canvas, choosing characters
- * that look like the rotated version wherever possible. Some characters
- * will stay unchanged by the process, some others will be replaced by
- * close equivalents. Fullwidth characters will be lost. The operation is
- * not guaranteed to be reversible at all.
- *
- * Note that the width and height of the canvas are swapped, causing its
- * aspect ratio to look stretched.
- *
- * If an error occurs, -1 is returned and \b errno is set accordingly:
- * - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
- * - \c ENOMEM Not enough memory to allocate the new canvas size. If this
- * happens, the previous canvas handle is still valid.
- *
- * \param cv The canvas to rotate right.
- * \return 0 in case of success, -1 if an error occurred.
- */
- int caca_stretch_right(caca_canvas_t *cv)
- {
- uint32_t *newchars, *newattrs;
- int x, y;
-
- if(cv->refcount)
- {
- seterrno(EBUSY);
- return -1;
- }
-
- /* Save the current frame shortcuts */
- _caca_save_frame_info(cv);
-
- newchars = malloc(cv->width * cv->height * sizeof(uint32_t));
- if(!newchars)
- {
- seterrno(ENOMEM);
- return -1;
- }
-
- newattrs = malloc(cv->width * cv->height * sizeof(uint32_t));
- if(!newattrs)
- {
- free(newchars);
- seterrno(ENOMEM);
- return -1;
- }
-
- for(y = 0; y < cv->height; y++)
- {
- for(x = 0; x < cv->width; x++)
- {
- uint32_t ch, attr;
-
- ch = cv->chars[cv->width * y + x];
- attr = cv->attrs[cv->width * y + x];
-
- /* FIXME: do something about fullwidth characters */
- ch = rightchar(ch);
-
- newchars[cv->height * x + cv->height - 1 - y] = ch;
- newattrs[cv->height * x + cv->height - 1 - y] = attr;
- }
- }
-
- free(cv->chars);
- free(cv->attrs);
-
- /* Swap X and Y information */
- x = cv->frames[cv->frame].x;
- y = cv->frames[cv->frame].y;
- cv->frames[cv->frame].x = cv->height - 1 - y;
- cv->frames[cv->frame].y = x;
-
- x = cv->frames[cv->frame].handlex;
- y = cv->frames[cv->frame].handley;
- cv->frames[cv->frame].handlex = cv->height - 1 - y;
- cv->frames[cv->frame].handley = x;
-
- cv->frames[cv->frame].width = cv->height;
- cv->frames[cv->frame].height = cv->width;
-
- cv->frames[cv->frame].chars = newchars;
- cv->frames[cv->frame].attrs = newattrs;
-
- /* Reset the current frame shortcuts */
- _caca_load_frame_info(cv);
-
- caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
-
- return 0;
- }
-
- /* FIXME: as the lookup tables grow bigger, use a log(n) lookup instead
- * of linear lookup. */
- static uint32_t flipchar(uint32_t ch)
- {
- int i;
-
- static uint32_t const noflip[] =
- {
- /* ASCII */
- ' ', '"', '#', '\'', '-', '.', '*', '+', ':', '=', '0', '8',
- 'A', 'H', 'I', 'M', 'O', 'T', 'U', 'V', 'W', 'X', 'Y', '^',
- '_', 'i', 'o', 'v', 'w', 'x', '|',
- /* CP437 and box drawing */
- 0x2591, 0x2592, 0x2593, 0x2588, 0x2584, 0x2580, /* ░ ▒ ▓ █ ▄ ▀ */
- 0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */
- 0x252c, 0x2534, 0x2533, 0x253b, 0x2566, 0x2569, /* ┬ ┴ ┳ ┻ ╦ ╩ */
- 0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */
- 0x2575, 0x2577, 0x2579, 0x257b, /* ╵ ╷ ╹ ╻ */
- 0
- };
-
- static uint32_t const pairs[] =
- {
- /* ASCII */
- '(', ')',
- '/', '\\',
- '<', '>',
- '[', ']',
- 'b', 'd',
- 'p', 'q',
- '{', '}',
- /* ASCII-Unicode */
- ';', 0x204f, /* ; ⁏ */
- '`', 0x00b4, /* ` ´ */
- ',', 0x02ce, /* , ˎ */
- '1', 0x07c1, /* 1 ߁ */
- 'B', 0x10412,/* B 𐐒 */
- 'C', 0x03fd, /* C Ͻ */
- 'D', 0x15e1, /* D ᗡ */
- 'E', 0x018e, /* E Ǝ */
- 'J', 0x1490, /* J ᒐ */
- 'L', 0x2143, /* L ⅃ */
- 'N', 0x0418, /* N И */
- 'P', 0x1040b,/* P 𐐋 */
- 'R', 0x042f, /* R Я */
- 'S', 0x01a7, /* S Ƨ */
- 'c', 0x0254, /* c ɔ */
- 'e', 0x0258, /* e ɘ */
- /* CP437 */
- 0x258c, 0x2590, /* ▌ ▐ */
- 0x2596, 0x2597, /* ▖ ▗ */
- 0x2598, 0x259d, /* ▘ ▝ */
- 0x2599, 0x259f, /* ▙ ▟ */
- 0x259a, 0x259e, /* ▚ ▞ */
- 0x259b, 0x259c, /* ▛ ▜ */
- 0x25ba, 0x25c4, /* ► ◄ */
- 0x2192, 0x2190, /* → ← */
- 0x2310, 0xac, /* ⌐ ¬ */
- /* Box drawing */
- 0x250c, 0x2510, /* ┌ ┐ */
- 0x2514, 0x2518, /* └ ┘ */
- 0x251c, 0x2524, /* ├ ┤ */
- 0x250f, 0x2513, /* ┏ ┓ */
- 0x2517, 0x251b, /* ┗ ┛ */
- 0x2523, 0x252b, /* ┣ ┫ */
- 0x2552, 0x2555, /* ╒ ╕ */
- 0x2558, 0x255b, /* ╘ ╛ */
- 0x2553, 0x2556, /* ╓ ╖ */
- 0x2559, 0x255c, /* ╙ ╜ */
- 0x2554, 0x2557, /* ╔ ╗ */
- 0x255a, 0x255d, /* ╚ ╝ */
- 0x255e, 0x2561, /* ╞ ╡ */
- 0x255f, 0x2562, /* ╟ ╢ */
- 0x2560, 0x2563, /* ╠ ╣ */
- 0x2574, 0x2576, /* ╴ ╶ */
- 0x2578, 0x257a, /* ╸ ╺ */
- 0
- };
-
- for(i = 0; noflip[i]; i++)
- if(ch == noflip[i])
- return ch;
-
- for(i = 0; pairs[i]; i++)
- if(ch == pairs[i])
- return pairs[i ^ 1];
-
- return ch;
- }
-
- static uint32_t flopchar(uint32_t ch)
- {
- int i;
-
- static uint32_t const noflop[] =
- {
- /* ASCII */
- ' ', '(', ')', '*', '+', '-', '0', '3', '8', ':', '<', '=',
- '>', 'B', 'C', 'D', 'E', 'H', 'I', 'K', 'O', 'X', '[', ']',
- 'c', 'o', '{', '|', '}',
- /* CP437 and box drawing */
- 0x2591, 0x2592, 0x2593, 0x2588, 0x258c, 0x2590, /* ░ ▒ ▓ █ ▌ ▐ */
- 0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */
- 0x251c, 0x2524, 0x2523, 0x252b, 0x2560, 0x2563, /* ├ ┤ ┣ ┫ ╠ ╣ */
- 0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */
- 0x2574, 0x2576, 0x2578, 0x257a, /* ╴ ╶ ╸ ╺ */
- 0
- };
-
- static uint32_t const pairs[] =
- {
- /* ASCII */
- '/', '\\',
- 'M', 'W',
- ',', '`',
- 'b', 'p',
- 'd', 'q',
- 'p', 'q',
- 'f', 't',
- '.', '\'',
- /* ASCII-Unicode */
- '_', 0x203e, /* _ ‾ */
- '!', 0x00a1, /* ! ¡ */
- 'A', 0x2200, /* A ∀ */
- 'J', 0x1489, /* J ᒉ */
- 'L', 0x0413, /* L Г */
- 'N', 0x0418, /* N И */
- 'P', 0x042c, /* P Ь */
- 'R', 0x0281, /* R ʁ */
- 'S', 0x01a7, /* S Ƨ */
- 'U', 0x0548, /* U Ո */
- 'V', 0x039b, /* V Λ */
- 'Y', 0x2144, /* Y ⅄ */
- 'h', 0x03bc, /* h μ */
- 'i', 0x1d09, /* i ᴉ */
- 'j', 0x1e37, /* j ḷ */
- 'l', 0x0237, /* l ȷ */
- 'v', 0x028c, /* v ʌ */
- 'w', 0x028d, /* w ʍ */
- 'y', 0x03bb, /* y λ */
- /* Not perfect, but better than nothing */
- '"', 0x201e, /* " „ */
- 'm', 0x026f, /* m ɯ */
- 'n', 'u',
- /* CP437 */
- 0x2584, 0x2580, /* ▄ ▀ */
- 0x2596, 0x2598, /* ▖ ▘ */
- 0x2597, 0x259d, /* ▗ ▝ */
- 0x2599, 0x259b, /* ▙ ▛ */
- 0x259f, 0x259c, /* ▟ ▜ */
- 0x259a, 0x259e, /* ▚ ▞ */
- /* Box drawing */
- 0x250c, 0x2514, /* ┌ └ */
- 0x2510, 0x2518, /* ┐ ┘ */
- 0x252c, 0x2534, /* ┬ ┴ */
- 0x250f, 0x2517, /* ┏ ┗ */
- 0x2513, 0x251b, /* ┓ ┛ */
- 0x2533, 0x253b, /* ┳ ┻ */
- 0x2554, 0x255a, /* ╔ ╚ */
- 0x2557, 0x255d, /* ╗ ╝ */
- 0x2566, 0x2569, /* ╦ ╩ */
- 0x2552, 0x2558, /* ╒ ╘ */
- 0x2555, 0x255b, /* ╕ ╛ */
- 0x2564, 0x2567, /* ╤ ╧ */
- 0x2553, 0x2559, /* ╓ ╙ */
- 0x2556, 0x255c, /* ╖ ╜ */
- 0x2565, 0x2568, /* ╥ ╨ */
- 0x2575, 0x2577, /* ╵ ╷ */
- 0x2579, 0x257b, /* ╹ ╻ */
- 0
- };
-
- for(i = 0; noflop[i]; i++)
- if(ch == noflop[i])
- return ch;
-
- for(i = 0; pairs[i]; i++)
- if(ch == pairs[i])
- return pairs[i ^ 1];
-
- return ch;
- }
-
- static uint32_t rotatechar(uint32_t ch)
- {
- int i;
-
- static uint32_t const norotate[] =
- {
- /* ASCII */
- ' ', '*', '+', '-', '/', '0', '8', ':', '=', 'H', 'I', 'N',
- 'O', 'S', 'X', 'Z', '\\', 'o', 's', 'x', 'z', '|',
- /* Unicode */
- 0x2591, 0x2592, 0x2593, 0x2588, 0x259a, 0x259e, /* ░ ▒ ▓ █ ▚ ▞ */
- 0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */
- 0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */
- 0
- };
-
- static uint32_t const pairs[] =
- {
- /* ASCII */
- '(', ')',
- '<', '>',
- '[', ']',
- '{', '}',
- '.', '\'',
- '6', '9',
- 'M', 'W',
- 'b', 'q',
- 'd', 'p',
- 'n', 'u',
- /* ASCII-Unicode */
- '_', 0x203e, /* _ ‾ */
- ',', 0x00b4, /* , ´ */
- '`', 0x02ce, /* ` ˎ */
- '&', 0x214b, /* & ⅋ */
- '!', 0x00a1, /* ! ¡ */
- '?', 0x00bf, /* ? ¿ */
- 'A', 0x2200, /* A ∀ */
- 'B', 0x10412,/* B 𐐒 */
- 'C', 0x03fd, /* C Ͻ */
- 'D', 0x15e1, /* D ᗡ */
- 'E', 0x018e, /* E Ǝ */
- 'F', 0x2132, /* F Ⅎ -- 0x07c3 looks better, but is RTL */
- 'G', 0x2141, /* G ⅁ */
- 'J', 0x148b, /* J ᒋ */
- 'L', 0x2142, /* L ⅂ */
- 'U', 0x0548, /* U Ո */
- 'V', 0x039b, /* V Λ */
- 'Y', 0x2144, /* Y ⅄ */
- 'a', 0x0250, /* a ɐ */
- 'c', 0x0254, /* c ɔ */
- 'e', 0x01dd, /* e ǝ */
- 'f', 0x025f, /* f ɟ */
- 'g', 0x1d77, /* g ᵷ */
- 'h', 0x0265, /* h ɥ */
- 'i', 0x1d09, /* i ᴉ */
- 'j', 0x1e37, /* j ḷ */
- 'k', 0x029e, /* k ʞ */
- 'l', 0x0237, /* l ȷ */
- 'm', 0x026f, /* m ɯ */
- 'r', 0x0279, /* r ɹ */
- 't', 0x0287, /* t ʇ */
- 'v', 0x028c, /* v ʌ */
- 'w', 0x028d, /* w ʍ */
- 'y', 0x028e, /* y ʎ */
- /* Unicode-ASCII to match third-party software */
- 0x0183, 'g', /* ƃ g */
- 0x0259, 'e', /* ə e */
- 0x027e, 'j', /* ɾ j */
- 0x02d9, '.', /* ˙ . */
- 0x05df, 'l', /* ן l */
- /* Not perfect, but better than nothing */
- '"', 0x201e, /* " „ */
- /* Misc Unicode */
- 0x00e6, 0x1d02, /* æ ᴂ */
- 0x0153, 0x1d14, /* œ ᴔ */
- 0x03b5, 0x025c, /* ε ɜ */
- 0x025b, 0x025c, /* ɛ ɜ */
- /* CP437 */
- 0x258c, 0x2590, /* ▌ ▐ */
- 0x2584, 0x2580, /* ▄ ▀ */
- 0x2596, 0x259d, /* ▖ ▝ */
- 0x2597, 0x2598, /* ▗ ▘ */
- 0x2599, 0x259c, /* ▙ ▜ */
- 0x259f, 0x259b, /* ▟ ▛ */
- /* Box drawing */
- 0x250c, 0x2518, /* ┌ ┘ */
- 0x2510, 0x2514, /* ┐ └ */
- 0x251c, 0x2524, /* ├ ┤ */
- 0x252c, 0x2534, /* ┬ ┴ */
- 0x250f, 0x251b, /* ┏ ┛ */
- 0x2513, 0x2517, /* ┓ ┗ */
- 0x2523, 0x252b, /* ┣ ┫ */
- 0x2533, 0x253b, /* ┳ ┻ */
- 0x2554, 0x255d, /* ╔ ╝ */
- 0x2557, 0x255a, /* ╗ ╚ */
- 0x2560, 0x2563, /* ╠ ╣ */
- 0x2566, 0x2569, /* ╦ ╩ */
- 0x2552, 0x255b, /* ╒ ╛ */
- 0x2555, 0x2558, /* ╕ ╘ */
- 0x255e, 0x2561, /* ╞ ╡ */
- 0x2564, 0x2567, /* ╤ ╧ */
- 0x2553, 0x255c, /* ╓ ╜ */
- 0x2556, 0x2559, /* ╖ ╙ */
- 0x255f, 0x2562, /* ╟ ╢ */
- 0x2565, 0x2568, /* ╥ ╨ */
- 0x2574, 0x2576, /* ╴ ╶ */
- 0x2575, 0x2577, /* ╵ ╷ */
- 0x2578, 0x257a, /* ╸ ╺ */
- 0x2579, 0x257b, /* ╹ ╻ */
- 0
- };
-
- for(i = 0; norotate[i]; i++)
- if(ch == norotate[i])
- return ch;
-
- for(i = 0; pairs[i]; i++)
- if(ch == pairs[i])
- return pairs[i ^ 1];
-
- return ch;
- }
-
- static uint32_t const leftright2[] =
- {
- /* ASCII */
- '/', '\\',
- '|', '-',
- '|', '_', /* This is all right because there was already a '|' before */
- /* ASCII-Unicode */
- '|', 0x203e, /* | ‾ */
- /* Misc Unicode */
- 0x2571, 0x2572, /* ╱ ╲ */
- /* Box drawing */
- 0x2500, 0x2502, /* ─ │ */
- 0x2501, 0x2503, /* ━ ┃ */
- 0x2550, 0x2551, /* ═ ║ */
- 0, 0
- };
-
- static uint32_t const leftright4[] =
- {
- /* ASCII */
- '<', 'v', '>', '^',
- ',', '.', '\'', '`',
- /* ASCII / Unicode */
- '(', 0x203f, ')', 0x2040, /* ( ‿ ) ⁀ */
- /* Misc Unicode */
- 0x256d, 0x2570, 0x256f, 0x256e, /* ╭ ╰ ╯ ╮ */
- /* CP437 */
- 0x258c, 0x2584, 0x2590, 0x2580, /* ▌ ▄ ▐ ▀ */
- 0x2596, 0x2597, 0x259d, 0x2598, /* ▖ ▗ ▝ ▘ */
- 0x2599, 0x259f, 0x259c, 0x259b, /* ▙ ▟ ▜ ▛ */
- /* Box drawing */
- 0x250c, 0x2514, 0x2518, 0x2510, /* ┌ └ ┘ ┐ */
- 0x250f, 0x2517, 0x251b, 0x2513, /* ┏ ┗ ┛ ┓ */
- 0x251c, 0x2534, 0x2524, 0x252c, /* ├ ┴ ┤ ┬ */
- 0x2523, 0x253b, 0x252b, 0x2533, /* ┣ ┻ ┫ ┳ */
- 0x2552, 0x2559, 0x255b, 0x2556, /* ╒ ╙ ╛ ╖ */
- 0x2553, 0x2558, 0x255c, 0x2555, /* ╓ ╘ ╜ ╕ */
- 0x2554, 0x255a, 0x255d, 0x2557, /* ╔ ╚ ╝ ╗ */
- 0x255e, 0x2568, 0x2561, 0x2565, /* ╞ ╨ ╡ ╥ */
- 0x255f, 0x2567, 0x2562, 0x2564, /* ╟ ╧ ╢ ╤ */
- 0x2560, 0x2569, 0x2563, 0x2566, /* ╠ ╩ ╣ ╦ */
- 0x2574, 0x2577, 0x2576, 0x2575, /* ╴ ╷ ╶ ╵ */
- 0x2578, 0x257b, 0x257a, 0x2579, /* ╸ ╻ ╺ ╹ */
- 0, 0, 0, 0
- };
-
- static uint32_t leftchar(uint32_t ch)
- {
- int i;
-
- for(i = 0; leftright2[i]; i++)
- if(ch == leftright2[i])
- return leftright2[(i & ~1) | ((i + 1) & 1)];
-
- for(i = 0; leftright4[i]; i++)
- if(ch == leftright4[i])
- return leftright4[(i & ~3) | ((i + 1) & 3)];
-
- return ch;
- }
-
- static uint32_t rightchar(uint32_t ch)
- {
- int i;
-
- for(i = 0; leftright2[i]; i++)
- if(ch == leftright2[i])
- return leftright2[(i & ~1) | ((i - 1) & 1)];
-
- for(i = 0; leftright4[i]; i++)
- if(ch == leftright4[i])
- return leftright4[(i & ~3) | ((i - 1) & 3)];
-
- return ch;
- }
-
- static uint32_t const leftright2x2[] =
- {
- /* ASCII / Unicode */
- '-', '-', 0x4e28, CACA_MAGIC_FULLWIDTH, /* -- 丨 */
- '|', '|', 0x2f06, CACA_MAGIC_FULLWIDTH, /* || ⼆ */
- /* Unicode */
- 0x2584, 0x2580, 0x2580, 0x2584, /* ▄▀ ▀▄ */
- 0, 0, 0, 0
- };
-
- static uint32_t const leftright2x4[] =
- {
- /* ASCII */
- ':', ' ', '.', '.', ' ', ':', '\'', '\'',
- /* ASCII / Unicode */
- ' ', '`', 0x00b4, ' ', 0x02ce, ' ', ' ', ',', /* ` ´ ˎ , */
- ' ', '`', '\'', ' ', '.', ' ', ' ', ',', /* fallback ASCII */
- '`', ' ', ',', ' ', ' ', 0x00b4, ' ', 0x02ce, /* ` , ˎ ´ */
- '`', ' ', ',', ' ', ' ', '.', ' ', '\'', /* fallback ASCII */
- '/', ' ', '-', 0x02ce, ' ', '/', '`', '-', /* / -ˎ / `- */
- '/', ' ', '-', '.', ' ', '/', '\'', '-', /* fallback ASCII */
- '\\', ' ', ',', '-', ' ', '\\', '-', 0x00b4, /* \ ,- \ -´ */
- '\\', ' ', '.', '-', ' ', '\\', '-', '\'', /* fallback ASCII */
- '\\', ' ', '_', ',', ' ', '\\', 0x00b4, 0x203e, /* \ _, \ ´‾ */
- '\\', '_', '_', '/', 0x203e, '\\', '/', 0x203e, /* \_ _/ ‾\ /‾ */
- '_', '\\', 0x203e, '/', '\\', 0x203e, '/', '_', /* _\ ‾/ \‾ /_ */
- '|', ' ', '_', '_', ' ', '|', 0x203e, 0x203e, /* | __ | ‾‾ */
- '_', '|', 0x203e, '|', '|', 0x203e, '|', '_', /* _| ‾| |‾ |_ */
- '|', '_', '_', '|', 0x203e, '|', '|', 0x203e, /* |_ _| ‾| |‾ */
- '_', ' ', ' ', 0x2577, ' ', 0x203e, 0x2575, ' ', /* _ ╷ ‾ ╵ */
- ' ', '_', ' ', 0x2575, 0x203e, ' ', 0x2577, ' ', /* _ ╵ ‾ ╷ */
- '.', '_', '.', 0x2575, 0x203e, '\'', 0x2577, '\'', /* ._ .╵ ‾' ╷' */
- '(', '_', 0x203f, '|', 0x203e, ')', '|', 0x2040, /* (_ ‿| ‾) |⁀ */
- '(', 0x203e, '|', 0x203f, '_', ')', 0x2040, '|', /* (‾ |‿ _) ⁀| */
- '\\', '/', 0xff1e, CACA_MAGIC_FULLWIDTH,
- '/', '\\', 0xff1c, CACA_MAGIC_FULLWIDTH, /* \/ > /\ < */
- ')', ' ', 0xfe35, CACA_MAGIC_FULLWIDTH,
- ' ', '(', 0xfe36, CACA_MAGIC_FULLWIDTH, /* ) ︵ ( ︶ */
- '}', ' ', 0xfe37, CACA_MAGIC_FULLWIDTH,
- ' ', '{', 0xfe38, CACA_MAGIC_FULLWIDTH, /* } ︷ { ︸ */
- /* Not perfect, but better than nothing */
- '(', ' ', 0x02ce, ',', ' ', ')', 0x00b4, '`', /* ( ˎ, ) ´` */
- ' ', 'v', '>', ' ', 0x028c, ' ', ' ', '<', /* v > ʌ < */
- ' ', 'V', '>', ' ', 0x039b, ' ', ' ', '<', /* V > Λ < */
- 'v', ' ', '>', ' ', ' ', 0x028c, ' ', '<', /* v > ʌ < */
- 'V', ' ', '>', ' ', ' ', 0x039b, ' ', '<', /* V > Λ < */
- '\\', '|', 0xff1e, CACA_MAGIC_FULLWIDTH,
- '|', '\\', 0xff1c, CACA_MAGIC_FULLWIDTH, /* \| > |\ < */
- '|', '/', 0xff1e, CACA_MAGIC_FULLWIDTH,
- '/', '|', 0xff1c, CACA_MAGIC_FULLWIDTH, /* |/ > /| < */
- /* Unicode */
- 0x2584, ' ', ' ', 0x2584, ' ', 0x2580, 0x2580, ' ', /* ▄ ▄ ▀ ▀ */
- 0x2588, ' ', 0x2584, 0x2584, ' ', 0x2588, 0x2580, 0x2580, /* █ ▄▄ █ ▀▀ */
- 0x2588, 0x2584, 0x2584, 0x2588,
- 0x2580, 0x2588, 0x2588, 0x2580, /* █▄ ▄█ ▀█ █▀ */
- /* TODO: Braille */
- /* Not perfect, but better than nothing */
- 0x2591, ' ', 0x28e4, 0x28e4, ' ', 0x2591, 0x281b, 0x281b, /* ░ ⣤⣤ ░ ⠛⠛ */
- 0x2592, ' ', 0x28f6, 0x28f6, ' ', 0x2592, 0x283f, 0x283f, /* ▒ ⣶⣶ ▒ ⠿⠿ */
- 0, 0, 0, 0, 0, 0, 0, 0
- };
-
- static void leftpair(uint32_t pair[2])
- {
- int i;
-
- for(i = 0; leftright2x2[i]; i += 2)
- if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1])
- {
- pair[0] = leftright2x2[(i & ~3) | ((i + 2) & 3)];
- pair[1] = leftright2x2[((i & ~3) | ((i + 2) & 3)) + 1];
- return;
- }
-
- for(i = 0; leftright2x4[i]; i += 2)
- if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1])
- {
- pair[0] = leftright2x4[(i & ~7) | ((i + 2) & 7)];
- pair[1] = leftright2x4[((i & ~7) | ((i + 2) & 7)) + 1];
- return;
- }
- }
-
- static void rightpair(uint32_t pair[2])
- {
- int i;
-
- for(i = 0; leftright2x2[i]; i += 2)
- if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1])
- {
- pair[0] = leftright2x2[(i & ~3) | ((i - 2) & 3)];
- pair[1] = leftright2x2[((i & ~3) | ((i - 2) & 3)) + 1];
- return;
- }
-
- for(i = 0; leftright2x4[i]; i += 2)
- if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1])
- {
- pair[0] = leftright2x4[(i & ~7) | ((i - 2) & 7)];
- pair[1] = leftright2x4[((i & ~7) | ((i - 2) & 7)) + 1];
- return;
- }
- }
-
- /*
- * XXX: The following functions are aliases.
- */
-
- int cucul_invert(cucul_canvas_t *) CACA_ALIAS(caca_invert);
- int cucul_flip(cucul_canvas_t *) CACA_ALIAS(caca_flip);
- int cucul_flop(cucul_canvas_t *) CACA_ALIAS(caca_flop);
- int cucul_rotate_180(cucul_canvas_t *) CACA_ALIAS(caca_rotate_180);
- int cucul_rotate_left(cucul_canvas_t *) CACA_ALIAS(caca_rotate_left);
- int cucul_rotate_right(cucul_canvas_t *) CACA_ALIAS(caca_rotate_right);
- int cucul_stretch_left(cucul_canvas_t *) CACA_ALIAS(caca_stretch_left);
- int cucul_stretch_right(cucul_canvas_t *) CACA_ALIAS(caca_stretch_right);
|