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

Last change on this file since 2305 was 2305, checked in by Sam Hocevar, 12 years ago
  • Remove all unsigned ints from exported functions. Signed arithmetic is far better for error checking.
  • Property svn:keywords set to Id
File size: 33.3 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 2305 2008-04-19 19:25:52Z 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 "cucul.h"
26#include "cucul_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 cucul_invert(cucul_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 cucul_flip(cucul_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] == CUCUL_MAGIC_FULLWIDTH)
108            {
109                cleft[0] = cleft[1];
110                cleft[1] = CUCUL_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 cucul_flop(cucul_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 cucul_rotate_180(cucul_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] == CUCUL_MAGIC_FULLWIDTH)
210            {
211                cbegin[0] = cbegin[1];
212                cbegin[1] = CUCUL_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 cucul_rotate_left(cucul_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    _cucul_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    _cucul_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 cucul_rotate_right(cucul_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    _cucul_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    _cucul_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 cucul_stretch_left(cucul_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    _cucul_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    _cucul_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 cucul_stretch_right(cucul_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    _cucul_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    _cucul_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, CUCUL_MAGIC_FULLWIDTH, /* -- 丨 */
1003    '|', '|', 0x2f06, CUCUL_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, CUCUL_MAGIC_FULLWIDTH,
1034            '/', '\\', 0xff1c, CUCUL_MAGIC_FULLWIDTH,  /* \/ > /\ < */
1035    ')', ' ', 0xfe35, CUCUL_MAGIC_FULLWIDTH,
1036            ' ', '(', 0xfe36, CUCUL_MAGIC_FULLWIDTH,   /* )  ︵  ( ︶ */
1037    '}', ' ', 0xfe37, CUCUL_MAGIC_FULLWIDTH,
1038            ' ', '{', 0xfe38, CUCUL_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, CUCUL_MAGIC_FULLWIDTH,
1046            '|', '\\', 0xff1c, CUCUL_MAGIC_FULLWIDTH,  /* \| > |\ < */
1047    '|', '/', 0xff1e, CUCUL_MAGIC_FULLWIDTH,
1048            '/', '|', 0xff1c, CUCUL_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.