source: libcaca/trunk/caca/transform.c @ 2821

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

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