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

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