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

Last change on this file since 1808 was 1808, checked in by Sam Hocevar, 15 years ago
  • More characters for the wide rotations.
  • Updated test/text to show what the wide rotations now do.
  • Property svn:keywords set to Id
File size: 30.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 1808 2007-07-17 17:20:11Z 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. Some characters
226 *  will stay unchanged by the process, some others will be replaced by
227 *  close equivalents. Fullwidth characters will be lost. The operation is
228 *  not guaranteed to be reversible at all.
229 *
230 *  Note that the width and height of the canvas are swapped.
231 *
232 *  If an error occurs, -1 is returned and \b errno is set accordingly:
233 *  - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
234 *  - \c ENOMEM Not enough memory to allocate the new canvas size. If this
235 *    happens, the previous canvas handle is still valid.
236 *
237 *  \param cv The canvas to rotate left.
238 *  \return 0 in case of success, -1 if an error occurred.
239 */
240int cucul_rotate_left(cucul_canvas_t *cv)
241{
242    uint32_t *newchars, *newattrs;
243    unsigned int x, y;
244
245    if(cv->refcount)
246    {
247        seterrno(EBUSY);
248        return -1;
249    }
250
251    /* Save the current frame shortcuts */
252    _cucul_save_frame_info(cv);
253
254    newchars = malloc(cv->width * cv->height * sizeof(uint32_t));
255    if(!newchars)
256        return -1;
257
258    newattrs = malloc(cv->width * cv->height * sizeof(uint32_t));
259    if(!newattrs)
260    {
261        free(newchars);
262        return -1;
263    }
264
265    for(y = 0; y < cv->height; y++)
266    {
267        for(x = 0; x < cv->width; x++)
268        {
269            uint32_t ch, attr;
270
271            ch = cv->chars[cv->width * y + x];
272            attr = cv->attrs[cv->width * y + x];
273
274            /* FIXME: do something about fullwidth characters */
275            ch = leftchar(ch);
276
277            newchars[cv->height * (cv->width - 1 - x) + y] = ch;
278            newattrs[cv->height * (cv->width - 1 - x) + y] = attr;
279        }
280    }
281
282    free(cv->chars);
283    free(cv->attrs);
284
285    /* Swap X and Y information */
286    x = cv->frames[cv->frame].x;
287    y = cv->frames[cv->frame].y;
288    cv->frames[cv->frame].x = y;
289    cv->frames[cv->frame].y = cv->width - 1 - x;
290
291    x = cv->frames[cv->frame].handlex;
292    y = cv->frames[cv->frame].handley;
293    cv->frames[cv->frame].handlex = y;
294    cv->frames[cv->frame].handley = cv->width - 1 - x;
295
296    cv->frames[cv->frame].width = cv->height;
297    cv->frames[cv->frame].height = cv->width;
298
299    cv->frames[cv->frame].chars = newchars;
300    cv->frames[cv->frame].attrs = newattrs;
301
302    /* Reset the current frame shortcuts */
303    _cucul_load_frame_info(cv);
304
305    return 0;
306}
307
308/** \brief Rotate a canvas, 90 degrees counterclockwise (widechar version).
309 *
310 *  Apply a 90-degree transformation to a canvas, choosing characters
311 *  that look like the rotated version wherever possible. Characters cells
312 *  are rotated two-by-two. Some characters will stay unchanged by the
313 *  process, some others will be replaced by close equivalents. Fullwidth
314 *  characters at odd horizontal coordinates will be lost. The operation is
315 *  not guaranteed to be reversible at all.
316 *
317 *  Note that the width of the canvas is divided by two and becomes the
318 *  new height. Height is multiplied by two and becomes the new width. It
319 *  is illegal to pass a canvas with an odd width to this function.
320 *
321 *  If an error occurs, -1 is returned and \b errno is set accordingly:
322 *  - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
323 *  - \c EINVAL The canvas' width is odd.
324 *  - \c ENOMEM Not enough memory to allocate the new canvas size. If this
325 *    happens, the previous canvas handle is still valid.
326 *
327 *  \param cv The canvas to rotate left.
328 *  \return 0 in case of success, -1 if an error occurred.
329 */
330int cucul_rotate_left_wide(cucul_canvas_t *cv)
331{
332    uint32_t *newchars, *newattrs;
333    unsigned int x, y, subwidth, subheight;
334
335    if(cv->refcount)
336    {
337        seterrno(EBUSY);
338        return -1;
339    }
340
341    if(cv->width & 1)
342    {
343        seterrno(EINVAL);
344        return -1;
345    }
346
347    /* Save the current frame shortcuts */
348    _cucul_save_frame_info(cv);
349
350    newchars = malloc(cv->width * cv->height * sizeof(uint32_t));
351    if(!newchars)
352        return -1;
353
354    newattrs = malloc(cv->width * cv->height * sizeof(uint32_t));
355    if(!newattrs)
356    {
357        free(newchars);
358        return -1;
359    }
360
361    subwidth = cv->width / 2;
362    subheight = cv->height;
363
364    for(y = 0; y < subheight; y++)
365    {
366        for(x = 0; x < subwidth; x++)
367        {
368            uint32_t pair[2], attr1, attr2;
369
370            pair[0] = cv->chars[(subwidth * y + x) * 2];
371            attr1 = cv->attrs[(subwidth * y + x) * 2];
372            pair[1] = cv->chars[(subwidth * y + x) * 2 + 1];
373            attr2 = cv->attrs[(subwidth * y + x) * 2 + 1];
374
375            leftpair(pair);
376
377            newchars[(subheight * (subwidth - 1 - x) + y) * 2] = pair[0];
378            newattrs[(subheight * (subwidth - 1 - x) + y) * 2] = attr1;
379            newchars[(subheight * (subwidth - 1 - x) + y) * 2 + 1] = pair[1];
380            newattrs[(subheight * (subwidth - 1 - x) + y) * 2 + 1] = attr2;
381        }
382    }
383
384    free(cv->chars);
385    free(cv->attrs);
386
387    /* Swap X and Y information */
388    x = cv->frames[cv->frame].x;
389    y = cv->frames[cv->frame].y;
390    cv->frames[cv->frame].x = y * 2;
391    cv->frames[cv->frame].y = (cv->width - 1 - x) / 2;
392
393    x = cv->frames[cv->frame].handlex;
394    y = cv->frames[cv->frame].handley;
395    cv->frames[cv->frame].handlex = y * 2;
396    cv->frames[cv->frame].handley = (cv->width - 1 - x) / 2;
397
398    cv->frames[cv->frame].width = cv->height * 2;
399    cv->frames[cv->frame].height = cv->width / 2;
400
401    cv->frames[cv->frame].chars = newchars;
402    cv->frames[cv->frame].attrs = newattrs;
403
404    /* Reset the current frame shortcuts */
405    _cucul_load_frame_info(cv);
406
407    return 0;
408}
409
410/** \brief Rotate a canvas, 90 degrees clockwise.
411 *
412 *  Apply a 270-degree transformation to a canvas, choosing characters
413 *  that look like the rotated version wherever possible. Some characters
414 *  will stay unchanged by the process, some others will be replaced by
415 *  close equivalents. Fullwidth characters will be lost. The operation is
416 *  not guaranteed to be reversible at all.
417 *
418 *  Note that the width and height of the canvas are swapped.
419 *
420 *  If an error occurs, -1 is returned and \b errno is set accordingly:
421 *  - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
422 *  - \c ENOMEM Not enough memory to allocate the new canvas size. If this
423 *    happens, the previous canvas handle is still valid.
424 *
425 *  \param cv The canvas to rotate right.
426 *  \return 0 in case of success, -1 if an error occurred.
427 */
428int cucul_rotate_right(cucul_canvas_t *cv)
429{
430    uint32_t *newchars, *newattrs;
431    unsigned int x, y;
432
433    if(cv->refcount)
434    {
435        seterrno(EBUSY);
436        return -1;
437    }
438
439    /* Save the current frame shortcuts */
440    _cucul_save_frame_info(cv);
441
442    newchars = malloc(cv->width * cv->height * sizeof(uint32_t));
443    if(!newchars)
444    {
445        seterrno(ENOMEM);
446        return -1;
447    }
448
449    newattrs = malloc(cv->width * cv->height * sizeof(uint32_t));
450    if(!newattrs)
451    {
452        free(newchars);
453        seterrno(ENOMEM);
454        return -1;
455    }
456
457    for(y = 0; y < cv->height; y++)
458    {
459        for(x = 0; x < cv->width; x++)
460        {
461            uint32_t ch, attr;
462
463            ch = cv->chars[cv->width * y + x];
464            attr = cv->attrs[cv->width * y + x];
465
466            /* FIXME: do something about fullwidth characters */
467            ch = rightchar(ch);
468
469            newchars[cv->height * x + cv->height - 1 - y] = ch;
470            newattrs[cv->height * x + cv->height - 1 - y] = attr;
471        }
472    }
473
474    free(cv->chars);
475    free(cv->attrs);
476
477    /* Swap X and Y information */
478    x = cv->frames[cv->frame].x;
479    y = cv->frames[cv->frame].y;
480    cv->frames[cv->frame].x = cv->height - 1 - y;
481    cv->frames[cv->frame].y = x;
482
483    x = cv->frames[cv->frame].handlex;
484    y = cv->frames[cv->frame].handley;
485    cv->frames[cv->frame].handlex = cv->height - 1 - y;
486    cv->frames[cv->frame].handley = x;
487
488    cv->frames[cv->frame].width = cv->height;
489    cv->frames[cv->frame].height = cv->width;
490
491    cv->frames[cv->frame].chars = newchars;
492    cv->frames[cv->frame].attrs = newattrs;
493
494    /* Reset the current frame shortcuts */
495    _cucul_load_frame_info(cv);
496
497    return 0;
498}
499
500/** \brief Rotate a canvas, 90 degrees counterclockwise (widechar version).
501 *
502 *  Apply a 90-degree transformation to a canvas, choosing characters
503 *  that look like the rotated version wherever possible. Characters cells
504 *  are rotated two-by-two. Some characters will stay unchanged by the
505 *  process, some others will be replaced by close equivalents. Fullwidth
506 *  characters at odd horizontal coordinates will be lost. The operation is
507 *  not guaranteed to be reversible at all.
508 *
509 *  Note that the width of the canvas is divided by two and becomes the
510 *  new height. Height is multiplied by two and becomes the new width. It
511 *  is illegal to pass a canvas with an odd width to this function.
512 *
513 *  If an error occurs, -1 is returned and \b errno is set accordingly:
514 *  - \c EBUSY The canvas is in use by a display driver and cannot be rotated.
515 *  - \c EINVAL The canvas' width is odd.
516 *  - \c ENOMEM Not enough memory to allocate the new canvas size. If this
517 *    happens, the previous canvas handle is still valid.
518 *
519 *  \param cv The canvas to rotate right.
520 *  \return 0 in case of success, -1 if an error occurred.
521 */
522int cucul_rotate_right_wide(cucul_canvas_t *cv)
523{
524    uint32_t *newchars, *newattrs;
525    unsigned int x, y, subwidth, subheight;
526
527    if(cv->refcount)
528    {
529        seterrno(EBUSY);
530        return -1;
531    }
532
533    if(cv->width & 1)
534    {
535        seterrno(EINVAL);
536        return -1;
537    }
538
539    /* Save the current frame shortcuts */
540    _cucul_save_frame_info(cv);
541
542    newchars = malloc(cv->width * cv->height * sizeof(uint32_t));
543    if(!newchars)
544        return -1;
545
546    newattrs = malloc(cv->width * cv->height * sizeof(uint32_t));
547    if(!newattrs)
548    {
549        free(newchars);
550        return -1;
551    }
552
553    subwidth = cv->width / 2;
554    subheight = cv->height;
555
556    for(y = 0; y < subheight; y++)
557    {
558        for(x = 0; x < subwidth; x++)
559        {
560            uint32_t pair[2], attr1, attr2;
561
562            pair[0] = cv->chars[(subwidth * y + x) * 2];
563            attr1 = cv->attrs[(subwidth * y + x) * 2];
564            pair[1] = cv->chars[(subwidth * y + x) * 2 + 1];
565            attr2 = cv->attrs[(subwidth * y + x) * 2 + 1];
566
567            rightpair(pair);
568
569            newchars[(subheight * x + subheight - 1 - y) * 2] = pair[0];
570            newattrs[(subheight * x + subheight - 1 - y) * 2] = attr1;
571            newchars[(subheight * x + subheight - 1 - y) * 2 + 1] = pair[1];
572            newattrs[(subheight * x + subheight - 1 - y) * 2 + 1] = attr2;
573        }
574    }
575
576    free(cv->chars);
577    free(cv->attrs);
578
579    /* Swap X and Y information */
580    x = cv->frames[cv->frame].x;
581    y = cv->frames[cv->frame].y;
582    cv->frames[cv->frame].x = (cv->height - 1 - y) * 2;
583    cv->frames[cv->frame].y = x / 2;
584
585    x = cv->frames[cv->frame].handlex;
586    y = cv->frames[cv->frame].handley;
587    cv->frames[cv->frame].handlex = (cv->height - 1 - y) * 2;
588    cv->frames[cv->frame].handley = x / 2;
589
590    cv->frames[cv->frame].width = cv->height * 2;
591    cv->frames[cv->frame].height = cv->width / 2;
592
593    cv->frames[cv->frame].chars = newchars;
594    cv->frames[cv->frame].attrs = newattrs;
595
596    /* Reset the current frame shortcuts */
597    _cucul_load_frame_info(cv);
598
599    return 0;
600}
601
602/* FIXME: as the lookup tables grow bigger, use a log(n) lookup instead
603 * of linear lookup. */
604static uint32_t flipchar(uint32_t ch)
605{
606    int i;
607
608    static uint32_t const noflip[] =
609    {
610         /* ASCII */
611         ' ', '"', '#', '\'', '-', '.', '*', '+', ':', '=', '0', '8',
612         'A', 'H', 'I', 'M', 'O', 'T', 'U', 'V', 'W', 'X', 'Y', '^',
613         '_', 'i', 'o', 'v', 'w', 'x', '|',
614         /* CP437 and box drawing */
615         0x2591, 0x2592, 0x2593, 0x2588, 0x2584, 0x2580, /* ░ ▒ ▓ █ ▄ ▀ */
616         0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */
617         0x252c, 0x2534, 0x2533, 0x253b, 0x2566, 0x2569, /* ┬ ┴ ┳ ┻ ╦ ╩ */
618         0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */
619         0x2575, 0x2577, 0x2579, 0x257b, /* ╵ ╷ ╹ ╻ */
620         0
621    };
622
623    static uint32_t const pairs[] =
624    {
625         /* ASCII */
626         '(', ')',
627         '/', '\\',
628         '<', '>',
629         '[', ']',
630         'b', 'd',
631         'p', 'q',
632         '{', '}',
633         /* ASCII-Unicode */
634         ';', 0x204f, /* ; ⁏ */
635         '`', 0x00b4, /* ` ´ */
636         ',', 0x02ce, /* , ˎ */
637         'C', 0x03fd, /* C Ͻ */
638         'E', 0x018e, /* E Ǝ */
639         'L', 0x2143, /* L ⅃ */
640         'N', 0x0418, /* N И */
641         'R', 0x042f, /* R Я */
642         'S', 0x01a7, /* S Ƨ */
643         'c', 0x0254, /* c ɔ */
644         'e', 0x0258, /* e ɘ */
645         /* CP437 */
646         0x258c, 0x2590, /* ▌ ▐ */
647         0x2596, 0x2597, /* ▖ ▗ */
648         0x2598, 0x259d, /* ▘ ▝ */
649         0x2599, 0x259f, /* ▙ ▟ */
650         0x259a, 0x259e, /* ▚ ▞ */
651         0x259b, 0x259c, /* ▛ ▜ */
652         0x25ba, 0x25c4, /* ► ◄ */
653         0x2192, 0x2190, /* → ← */
654         0x2310, 0xac,   /* ⌐ ¬ */
655         /* Box drawing */
656         0x250c, 0x2510, /* ┌ ┐ */
657         0x2514, 0x2518, /* └ ┘ */
658         0x251c, 0x2524, /* ├ ┤ */
659         0x250f, 0x2513, /* ┏ ┓ */
660         0x2517, 0x251b, /* ┗ ┛ */
661         0x2523, 0x252b, /* ┣ ┫ */
662         0x2552, 0x2555, /* ╒ ╕ */
663         0x2558, 0x255b, /* ╘ ╛ */
664         0x2553, 0x2556, /* ╓ ╖ */
665         0x2559, 0x255c, /* ╙ ╜ */
666         0x2554, 0x2557, /* ╔ ╗ */
667         0x255a, 0x255d, /* ╚ ╝ */
668         0x255e, 0x2561, /* ╞ ╡ */
669         0x255f, 0x2562, /* ╟ ╢ */
670         0x2560, 0x2563, /* ╠ ╣ */
671         0x2574, 0x2576, /* ╴ ╶ */
672         0x2578, 0x257a, /* ╸ ╺ */
673         0
674    };
675
676    for(i = 0; noflip[i]; i++)
677        if(ch == noflip[i])
678            return ch;
679
680    for(i = 0; pairs[i]; i++)
681        if(ch == pairs[i])
682            return pairs[i ^ 1];
683
684    return ch;
685}
686
687static uint32_t flopchar(uint32_t ch)
688{
689    int i;
690
691    static uint32_t const noflop[] =
692    {
693         /* ASCII */
694         ' ', '(', ')', '*', '+', '-', '0', '3', '8', ':', '<', '=',
695         '>', 'B', 'C', 'D', 'E', 'H', 'I', 'K', 'O', 'X', '[', ']',
696         'c', 'o', '{', '|', '}',
697         /* CP437 and box drawing */
698         0x2591, 0x2592, 0x2593, 0x2588, 0x258c, 0x2590, /* ░ ▒ ▓ █ ▌ ▐ */
699         0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */
700         0x251c, 0x2524, 0x2523, 0x252b, 0x2560, 0x2563, /* ├ ┤ ┣ ┫ ╠ ╣ */
701         0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */
702         0x2574, 0x2576, 0x2578, 0x257a, /* ╴ ╶ ╸ ╺ */
703         0
704    };
705
706    static uint32_t const pairs[] =
707    {
708         /* ASCII */
709         '/', '\\',
710         'M', 'W',
711         ',', '`',
712         'b', 'p',
713         'd', 'q',
714         'p', 'q',
715         'f', 't',
716         '.', '\'',
717         /* ASCII-Unicode */
718         '_', 0x203e, /* _ ‾ */
719         '!', 0x00a1, /* ! ¡ */
720         'L', 0x0413, /* L Г */
721         'N', 0x0418, /* N И */
722         'P', 0x042c, /* P Ь */
723         'R', 0x0281, /* R ʁ */
724         'S', 0x01a7, /* S Ƨ */
725         'U', 0x0548, /* U Ո */
726         'V', 0x039b, /* V Λ */
727         'Y', 0x2144, /* Y ⅄ */
728         'h', 0x03bc, /* h μ */
729         'i', 0x1d09, /* i ᴉ */
730         'j', 0x1e37, /* j ḷ */
731         'l', 0x0237, /* l ȷ */
732         'v', 0x028c, /* v ʌ */
733         'w', 0x028d, /* w ʍ */
734         'y', 0x03bb, /* y λ */
735         /* Not perfect, but better than nothing */
736         '"', 0x201e, /* " „ */
737         'm', 0x026f, /* m ɯ */
738         'n', 'u',
739         /* CP437 */
740         0x2584, 0x2580, /* ▄ ▀ */
741         0x2596, 0x2598, /* ▖ ▘ */
742         0x2597, 0x259d, /* ▗ ▝ */
743         0x2599, 0x259b, /* ▙ ▛ */
744         0x259f, 0x259c, /* ▟ ▜ */
745         0x259a, 0x259e, /* ▚ ▞ */
746         /* Box drawing */
747         0x250c, 0x2514, /* ┌ └ */
748         0x2510, 0x2518, /* ┐ ┘ */
749         0x252c, 0x2534, /* ┬ ┴ */
750         0x250f, 0x2517, /* ┏ ┗ */
751         0x2513, 0x251b, /* ┓ ┛ */
752         0x2533, 0x253b, /* ┳ ┻ */
753         0x2554, 0x255a, /* ╔ ╚ */
754         0x2557, 0x255d, /* ╗ ╝ */
755         0x2566, 0x2569, /* ╦ ╩ */
756         0x2552, 0x2558, /* ╒ ╘ */
757         0x2555, 0x255b, /* ╕ ╛ */
758         0x2564, 0x2567, /* ╤ ╧ */
759         0x2553, 0x2559, /* ╓ ╙ */
760         0x2556, 0x255c, /* ╖ ╜ */
761         0x2565, 0x2568, /* ╥ ╨ */
762         0x2575, 0x2577, /* ╵ ╷ */
763         0x2579, 0x257b, /* ╹ ╻ */
764         0
765    };
766
767    for(i = 0; noflop[i]; i++)
768        if(ch == noflop[i])
769            return ch;
770
771    for(i = 0; pairs[i]; i++)
772        if(ch == pairs[i])
773            return pairs[i ^ 1];
774
775    return ch;
776}
777
778static uint32_t rotatechar(uint32_t ch)
779{
780    int i;
781
782    static uint32_t const norotate[] =
783    {
784         /* ASCII */
785         ' ', '*', '+', '-', '/', '0', '8', ':', '=', 'H', 'I', 'N',
786         'O', 'S', 'X', 'Z', '\\', 'o', 's', 'x', 'z', '|',
787         /* Unicode */
788         0x2591, 0x2592, 0x2593, 0x2588, 0x259a, 0x259e, /* ░ ▒ ▓ █ ▚ ▞ */
789         0x2500, 0x2501, 0x2503, 0x2503, 0x253c, 0x254b, /* ─ ━ │ ┃ ┼ ╋ */
790         0x2550, 0x2551, 0x256c, /* ═ ║ ╬ */
791         0
792    };
793
794    static uint32_t const pairs[] =
795    {
796         /* ASCII */
797         '(', ')',
798         '<', '>',
799         '[', ']',
800         '{', '}',
801         '.', '\'',
802         '6', '9',
803         'M', 'W',
804         'b', 'q',
805         'd', 'p',
806         'n', 'u',
807         /* ASCII-Unicode */
808         '_', 0x203e, /* _ ‾ */
809         ',', 0x00b4, /* , ´ */
810         '`', 0x02ce, /* ` ˎ */
811         '&', 0x214b, /* & ⅋ */
812         '!', 0x00a1, /* ! ¡ */
813         '?', 0x00bf, /* ? ¿ */
814         'C', 0x03fd, /* C Ͻ */
815         'E', 0x018e, /* E Ǝ */
816         'F', 0x2132, /* F Ⅎ */
817         'L', 0x2142, /* L ⅂ */
818         'U', 0x0548, /* U Ո */
819         'V', 0x039b, /* V Λ */
820         'Y', 0x2144, /* Y ⅄ */
821         'a', 0x0250, /* a ɐ */
822         'c', 0x0254, /* c ɔ */
823         'e', 0x0259, /* e ə */
824         'f', 0x025f, /* f ɟ */
825         'g', 0x1d77, /* g ᵷ */
826         'h', 0x0265, /* h ɥ */
827         'i', 0x1d09, /* i ᴉ */
828         'j', 0x1e37, /* j ḷ */
829         'k', 0x029e, /* k ʞ */
830         'l', 0x0237, /* l ȷ */
831         'm', 0x026f, /* m ɯ */
832         'r', 0x0279, /* r ɹ */
833         't', 0x0287, /* t ʇ */
834         'v', 0x028c, /* v ʌ */
835         'w', 0x028d, /* w ʍ */
836         'y', 0x028e, /* y ʎ */
837         /* Not perfect, but better than nothing */
838         '"', 0x201e, /* " „ */
839         /* Misc Unicode */
840         0x00e6, 0x1d02, /* æ ᴂ */
841         0x0153, 0x1d14, /* œ ᴔ */
842         /* CP437 */
843         0x258c, 0x2590, /* ▌ ▐ */
844         0x2584, 0x2580, /* ▄ ▀ */
845         0x2596, 0x259d, /* ▖ ▝ */
846         0x2597, 0x2598, /* ▗ ▘ */
847         0x2599, 0x259c, /* ▙ ▜ */
848         0x259f, 0x259b, /* ▟ ▛ */
849         /* Box drawing */
850         0x250c, 0x2518, /* ┌ ┘ */
851         0x2510, 0x2514, /* ┐ └ */
852         0x251c, 0x2524, /* ├ ┤ */
853         0x252c, 0x2534, /* ┬ ┴ */
854         0x250f, 0x251b, /* ┏ ┛ */
855         0x2513, 0x2517, /* ┓ ┗ */
856         0x2523, 0x252b, /* ┣ ┫ */
857         0x2533, 0x253b, /* ┳ ┻ */
858         0x2554, 0x255d, /* ╔ ╝ */
859         0x2557, 0x255a, /* ╗ ╚ */
860         0x2560, 0x2563, /* ╠ ╣ */
861         0x2566, 0x2569, /* ╦ ╩ */
862         0x2552, 0x255b, /* ╒ ╛ */
863         0x2555, 0x2558, /* ╕ ╘ */
864         0x255e, 0x2561, /* ╞ ╡ */
865         0x2564, 0x2567, /* ╤ ╧ */
866         0x2553, 0x255c, /* ╓ ╜ */
867         0x2556, 0x2559, /* ╖ ╙ */
868         0x255f, 0x2562, /* ╟ ╢ */
869         0x2565, 0x2568, /* ╥ ╨ */
870         0x2574, 0x2576, /* ╴ ╶ */
871         0x2575, 0x2577, /* ╵ ╷ */
872         0x2578, 0x257a, /* ╸ ╺ */
873         0x2579, 0x257b, /* ╹ ╻ */
874         0
875    };
876
877    for(i = 0; norotate[i]; i++)
878        if(ch == norotate[i])
879            return ch;
880
881    for(i = 0; pairs[i]; i++)
882        if(ch == pairs[i])
883            return pairs[i ^ 1];
884
885    return ch;
886}
887
888static uint32_t const leftright2[] =
889{
890    /* ASCII */
891    '/', '\\',
892    '|', '-',
893    '|', '_', /* This is all right because there was already a '|' before */
894    /* ASCII-Unicode */
895    '|', 0x203e, /* | ‾ */
896    /* Misc Unicode */
897    0x2571, 0x2572, /* ╱ ╲ */
898    /* Box drawing */
899    0x2500, 0x2502, /* ─ │ */
900    0x2501, 0x2503, /* ━ ┃ */
901    0x2550, 0x2551, /* ═ ║ */
902    0, 0
903};
904
905static uint32_t const leftright4[] =
906{
907    /* ASCII */
908    '<', 'v', '>', '^',
909    ',', '.', '\'', '`',
910    /* ASCII / Unicode */
911    '(', 0x203f, ')', 0x2040,       /* ( ‿ ) ⁀ */
912    /* Misc Unicode */
913    0x256d, 0x2570, 0x256f, 0x256e, /* ╭ ╰ ╯ ╮ */
914    /* CP437 */
915    0x258c, 0x2584, 0x2590, 0x2580, /* ▌ ▄ ▐ ▀ */
916    0x2596, 0x2597, 0x259d, 0x2598, /* ▖ ▗ ▝ ▘ */
917    0x2599, 0x259f, 0x259c, 0x259b, /* ▙ ▟ ▜ ▛ */
918    /* Box drawing */
919    0x250c, 0x2514, 0x2518, 0x2510, /* ┌ └ ┘ ┐ */
920    0x250f, 0x2517, 0x251b, 0x2513, /* ┏ ┗ ┛ ┓ */
921    0x251c, 0x2534, 0x2524, 0x252c, /* ├ ┴ ┤ ┬ */
922    0x2523, 0x253b, 0x252b, 0x2533, /* ┣ ┻ ┫ ┳ */
923    0x2552, 0x2559, 0x255b, 0x2556, /* ╒ ╙ ╛ ╖ */
924    0x2553, 0x2558, 0x255c, 0x2555, /* ╓ ╘ ╜ ╕ */
925    0x2554, 0x255a, 0x255d, 0x2557, /* ╔ ╚ ╝ ╗ */
926    0x255e, 0x2568, 0x2561, 0x2565, /* ╞ ╨ ╡ ╥ */
927    0x255f, 0x2567, 0x2562, 0x2564, /* ╟ ╧ ╢ ╤ */
928    0x2560, 0x2569, 0x2563, 0x2566, /* ╠ ╩ ╣ ╦ */
929    0x2574, 0x2577, 0x2576, 0x2575, /* ╴ ╷ ╶ ╵ */
930    0x2578, 0x257b, 0x257a, 0x2579, /* ╸ ╻ ╺ ╹ */
931    0, 0, 0, 0
932};
933
934static uint32_t leftchar(uint32_t ch)
935{
936    int i;
937
938    for(i = 0; leftright2[i]; i++)
939        if(ch == leftright2[i])
940            return leftright2[(i & ~1) | ((i + 1) & 1)];
941
942    for(i = 0; leftright4[i]; i++)
943        if(ch == leftright4[i])
944            return leftright4[(i & ~3) | ((i + 1) & 3)];
945
946    return ch;
947}
948
949static uint32_t rightchar(uint32_t ch)
950{
951    int i;
952
953    for(i = 0; leftright2[i]; i++)
954        if(ch == leftright2[i])
955            return leftright2[(i & ~1) | ((i - 1) & 1)];
956
957    for(i = 0; leftright4[i]; i++)
958        if(ch == leftright4[i])
959            return leftright4[(i & ~3) | ((i - 1) & 3)];
960
961    return ch;
962}
963
964static uint32_t const leftright2x2[] =
965{
966    /* ASCII / Unicode */
967    '-', '-', 0x4e28, CUCUL_MAGIC_FULLWIDTH, /* -- 丨 */
968    0, 0, 0, 0
969};
970
971static uint32_t const leftright2x4[] =
972{
973    /* ASCII */
974    ':', ' ', '.', '.', ' ', ':', '\'', '\'',
975    /* ASCII / Unicode */
976    ' ', '`', 0x00b4, ' ', 0x02ce, ' ', ' ', ',',    /*  ` ´  ˎ   , */
977    ' ', '`', '\'',   ' ', '.',    ' ', ' ', ',',
978    '`', ' ', ',', ' ', ' ', 0x00b4, ' ', 0x02ce,    /*  ` ,   ˎ  ´ */
979    '`', ' ', ',', ' ', ' ', '.',    ' ', '\'',
980    '/', ' ', '-', 0x02ce, ' ', '/', '`', '-',       /* /  -ˎ  / `- */
981    '/', ' ', '-', '.',    ' ', '/', '\'', '-',   
982    '\\', ' ', ',', '-', ' ', '\\', '-', 0x00b4,     /* \  ,-  \ -´ */
983    '\\', ' ', '.', '-', ' ', '\\', '-', '\'',
984    '|', ' ', '_', '_', ' ', '|', 0x203e, 0x203e,    /* |  __  | ‾‾ */
985    '_', '|', 0x203e, '|', '|', 0x203e, '|', '_',    /* _| ‾| |‾ |_ */
986    '|', '_', '_', '|', 0x203e, '|', '|', 0x203e,    /* |_ _| ‾| |‾ */
987    '_', ' ', ' ', 0x2577, ' ', 0x203e, 0x2575, ' ', /* _   ╷  ‾ ╵  */
988    ' ', '_', ' ', 0x2575, 0x203e, ' ', 0x2577, ' ', /*  _  ╵ ‾  ╷  */
989    /* Not perfect, but better than nothing */
990    ' ', '`', '\'', ' ', '.', ' ', ' ', ',',
991    '`', ' ', ',', ' ', ' ', '.', ' ', '\'',
992    0, 0, 0, 0, 0, 0, 0, 0
993};
994
995static void leftpair(uint32_t pair[2])
996{
997    int i;
998
999    for(i = 0; leftright2x2[i]; i += 2)
1000        if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1])
1001        {
1002            pair[0] = leftright2x2[(i & ~3) | ((i + 2) & 3)];
1003            pair[1] = leftright2x2[((i & ~3) | ((i + 2) & 3)) + 1];
1004            return;
1005        }
1006
1007    for(i = 0; leftright2x4[i]; i += 2)
1008        if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1])
1009        {
1010            pair[0] = leftright2x4[(i & ~7) | ((i + 2) & 7)];
1011            pair[1] = leftright2x4[((i & ~7) | ((i + 2) & 7)) + 1];
1012            return;
1013        }
1014}
1015
1016static void rightpair(uint32_t pair[2])
1017{
1018    int i;
1019
1020    for(i = 0; leftright2x2[i]; i += 2)
1021        if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1])
1022        {
1023            pair[0] = leftright2x2[(i & ~3) | ((i - 2) & 3)];
1024            pair[1] = leftright2x2[((i & ~3) | ((i - 2) & 3)) + 1];
1025            return;
1026        }
1027
1028    for(i = 0; leftright2x4[i]; i += 2)
1029        if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1])
1030        {
1031            pair[0] = leftright2x4[(i & ~7) | ((i - 2) & 7)];
1032            pair[1] = leftright2x4[((i & ~7) | ((i - 2) & 7)) + 1];
1033            return;
1034        }
1035}
1036
Note: See TracBrowser for help on using the repository browser.