source: libcaca/trunk/cucul/transform.c @ 1820

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