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

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