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

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