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

Last change on this file since 3470 was 3470, checked in by Sam Hocevar, 11 years ago

Change the dirty rectangle API so that it can handle several rectangles. The
inner implementation still only handles one dirty rectangle, but this way
we can prepare supporting applictions for the future.

  • Property svn:keywords set to Id
File size: 35.1 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: transform.c 3470 2009-05-19 00:51:47Z sam $
7 *
8 *  This library is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  This file contains horizontal and vertical flipping routines.
17 */
18
19#include "config.h"
20
21#if !defined(__KERNEL__)
22#   include <stdlib.h>
23#endif
24
25#include "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_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
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_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
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_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
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_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
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_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
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_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
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_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
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_rectangle(cv, 0, 0, cv->width - 1, cv->height - 1);
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         /* CP437 */
912         0x258c, 0x2590, /* ▌ ▐ */
913         0x2584, 0x2580, /* ▄ ▀ */
914         0x2596, 0x259d, /* ▖ ▝ */
915         0x2597, 0x2598, /* ▗ ▘ */
916         0x2599, 0x259c, /* ▙ ▜ */
917         0x259f, 0x259b, /* ▟ ▛ */
918         /* Box drawing */
919         0x250c, 0x2518, /* ┌ ┘ */
920         0x2510, 0x2514, /* ┐ └ */
921         0x251c, 0x2524, /* ├ ┤ */
922         0x252c, 0x2534, /* ┬ ┴ */
923         0x250f, 0x251b, /* ┏ ┛ */
924         0x2513, 0x2517, /* ┓ ┗ */
925         0x2523, 0x252b, /* ┣ ┫ */
926         0x2533, 0x253b, /* ┳ ┻ */
927         0x2554, 0x255d, /* ╔ ╝ */
928         0x2557, 0x255a, /* ╗ ╚ */
929         0x2560, 0x2563, /* ╠ ╣ */
930         0x2566, 0x2569, /* ╦ ╩ */
931         0x2552, 0x255b, /* ╒ ╛ */
932         0x2555, 0x2558, /* ╕ ╘ */
933         0x255e, 0x2561, /* ╞ ╡ */
934         0x2564, 0x2567, /* ╤ ╧ */
935         0x2553, 0x255c, /* ╓ ╜ */
936         0x2556, 0x2559, /* ╖ ╙ */
937         0x255f, 0x2562, /* ╟ ╢ */
938         0x2565, 0x2568, /* ╥ ╨ */
939         0x2574, 0x2576, /* ╴ ╶ */
940         0x2575, 0x2577, /* ╵ ╷ */
941         0x2578, 0x257a, /* ╸ ╺ */
942         0x2579, 0x257b, /* ╹ ╻ */
943         0
944    };
945
946    for(i = 0; norotate[i]; i++)
947        if(ch == norotate[i])
948            return ch;
949
950    for(i = 0; pairs[i]; i++)
951        if(ch == pairs[i])
952            return pairs[i ^ 1];
953
954    return ch;
955}
956
957static uint32_t const leftright2[] =
958{
959    /* ASCII */
960    '/', '\\',
961    '|', '-',
962    '|', '_', /* This is all right because there was already a '|' before */
963    /* ASCII-Unicode */
964    '|', 0x203e, /* | ‾ */
965    /* Misc Unicode */
966    0x2571, 0x2572, /* ╱ ╲ */
967    /* Box drawing */
968    0x2500, 0x2502, /* ─ │ */
969    0x2501, 0x2503, /* ━ ┃ */
970    0x2550, 0x2551, /* ═ ║ */
971    0, 0
972};
973
974static uint32_t const leftright4[] =
975{
976    /* ASCII */
977    '<', 'v', '>', '^',
978    ',', '.', '\'', '`',
979    /* ASCII / Unicode */
980    '(', 0x203f, ')', 0x2040,       /* ( ‿ ) ⁀ */
981    /* Misc Unicode */
982    0x256d, 0x2570, 0x256f, 0x256e, /* ╭ ╰ ╯ ╮ */
983    /* CP437 */
984    0x258c, 0x2584, 0x2590, 0x2580, /* ▌ ▄ ▐ ▀ */
985    0x2596, 0x2597, 0x259d, 0x2598, /* ▖ ▗ ▝ ▘ */
986    0x2599, 0x259f, 0x259c, 0x259b, /* ▙ ▟ ▜ ▛ */
987    /* Box drawing */
988    0x250c, 0x2514, 0x2518, 0x2510, /* ┌ └ ┘ ┐ */
989    0x250f, 0x2517, 0x251b, 0x2513, /* ┏ ┗ ┛ ┓ */
990    0x251c, 0x2534, 0x2524, 0x252c, /* ├ ┴ ┤ ┬ */
991    0x2523, 0x253b, 0x252b, 0x2533, /* ┣ ┻ ┫ ┳ */
992    0x2552, 0x2559, 0x255b, 0x2556, /* ╒ ╙ ╛ ╖ */
993    0x2553, 0x2558, 0x255c, 0x2555, /* ╓ ╘ ╜ ╕ */
994    0x2554, 0x255a, 0x255d, 0x2557, /* ╔ ╚ ╝ ╗ */
995    0x255e, 0x2568, 0x2561, 0x2565, /* ╞ ╨ ╡ ╥ */
996    0x255f, 0x2567, 0x2562, 0x2564, /* ╟ ╧ ╢ ╤ */
997    0x2560, 0x2569, 0x2563, 0x2566, /* ╠ ╩ ╣ ╦ */
998    0x2574, 0x2577, 0x2576, 0x2575, /* ╴ ╷ ╶ ╵ */
999    0x2578, 0x257b, 0x257a, 0x2579, /* ╸ ╻ ╺ ╹ */
1000    0, 0, 0, 0
1001};
1002
1003static uint32_t leftchar(uint32_t ch)
1004{
1005    int i;
1006
1007    for(i = 0; leftright2[i]; i++)
1008        if(ch == leftright2[i])
1009            return leftright2[(i & ~1) | ((i + 1) & 1)];
1010
1011    for(i = 0; leftright4[i]; i++)
1012        if(ch == leftright4[i])
1013            return leftright4[(i & ~3) | ((i + 1) & 3)];
1014
1015    return ch;
1016}
1017
1018static uint32_t rightchar(uint32_t ch)
1019{
1020    int i;
1021
1022    for(i = 0; leftright2[i]; i++)
1023        if(ch == leftright2[i])
1024            return leftright2[(i & ~1) | ((i - 1) & 1)];
1025
1026    for(i = 0; leftright4[i]; i++)
1027        if(ch == leftright4[i])
1028            return leftright4[(i & ~3) | ((i - 1) & 3)];
1029
1030    return ch;
1031}
1032
1033static uint32_t const leftright2x2[] =
1034{
1035    /* ASCII / Unicode */
1036    '-', '-', 0x4e28, CACA_MAGIC_FULLWIDTH, /* -- 丨 */
1037    '|', '|', 0x2f06, CACA_MAGIC_FULLWIDTH, /* || ⼆ */
1038    /* Unicode */
1039    0x2584, 0x2580, 0x2580, 0x2584, /* ▄▀ ▀▄ */
1040    0, 0, 0, 0
1041};
1042
1043static uint32_t const leftright2x4[] =
1044{
1045    /* ASCII */
1046    ':', ' ', '.', '.', ' ', ':', '\'', '\'',
1047    /* ASCII / Unicode */
1048    ' ', '`', 0x00b4, ' ', 0x02ce, ' ', ' ', ',',      /*  ` ´  ˎ   , */
1049    ' ', '`', '\'',   ' ', '.',    ' ', ' ', ',',      /* fallback ASCII */
1050    '`', ' ', ',', ' ', ' ', 0x00b4, ' ', 0x02ce,      /*  ` ,   ˎ  ´ */
1051    '`', ' ', ',', ' ', ' ', '.',    ' ', '\'',        /* fallback ASCII */
1052    '/', ' ', '-', 0x02ce, ' ', '/', '`', '-',         /* /  -ˎ  / `- */
1053    '/', ' ', '-', '.',    ' ', '/', '\'', '-',        /* fallback ASCII */
1054    '\\', ' ', ',', '-', ' ', '\\', '-', 0x00b4,       /* \  ,-  \ -´ */
1055    '\\', ' ', '.', '-', ' ', '\\', '-', '\'',         /* fallback ASCII */
1056    '\\', ' ', '_', ',', ' ', '\\', 0x00b4, 0x203e,    /* \  _,  \ ´‾ */
1057    '\\', '_', '_', '/', 0x203e, '\\', '/', 0x203e,    /* \_ _/ ‾\ /‾ */
1058    '_', '\\', 0x203e, '/', '\\', 0x203e, '/', '_',    /* _\ ‾/ \‾ /_ */
1059    '|', ' ', '_', '_', ' ', '|', 0x203e, 0x203e,      /* |  __  | ‾‾ */
1060    '_', '|', 0x203e, '|', '|', 0x203e, '|', '_',      /* _| ‾| |‾ |_ */
1061    '|', '_', '_', '|', 0x203e, '|', '|', 0x203e,      /* |_ _| ‾| |‾ */
1062    '_', ' ', ' ', 0x2577, ' ', 0x203e, 0x2575, ' ',   /* _   ╷  ‾ ╵  */
1063    ' ', '_', ' ', 0x2575, 0x203e, ' ', 0x2577, ' ',   /*  _  ╵ ‾  ╷  */
1064    '.', '_', '.', 0x2575, 0x203e, '\'', 0x2577, '\'', /* ._ .╵ ‾' ╷' */
1065    '(', '_', 0x203f, '|', 0x203e, ')', '|', 0x2040,   /* (_ ‿| ‾) |⁀ */
1066    '(', 0x203e, '|', 0x203f, '_', ')', 0x2040, '|',   /* (‾ |‿ _) ⁀| */
1067    '\\', '/', 0xff1e, CACA_MAGIC_FULLWIDTH,
1068            '/', '\\', 0xff1c, CACA_MAGIC_FULLWIDTH,  /* \/ > /\ < */
1069    ')', ' ', 0xfe35, CACA_MAGIC_FULLWIDTH,
1070            ' ', '(', 0xfe36, CACA_MAGIC_FULLWIDTH,   /* )  ︵  ( ︶ */
1071    '}', ' ', 0xfe37, CACA_MAGIC_FULLWIDTH,
1072            ' ', '{', 0xfe38, CACA_MAGIC_FULLWIDTH,   /* }  ︷  { ︸ */
1073    /* Not perfect, but better than nothing */
1074    '(', ' ', 0x02ce, ',', ' ', ')', 0x00b4, '`',      /* (  ˎ,  ) ´` */
1075    ' ', 'v', '>', ' ', 0x028c, ' ', ' ', '<',         /*  v >  ʌ   < */
1076    ' ', 'V', '>', ' ', 0x039b, ' ', ' ', '<',         /*  V >  Λ   < */
1077    'v', ' ', '>', ' ', ' ', 0x028c, ' ', '<',         /* v  >   ʌ  < */
1078    'V', ' ', '>', ' ', ' ', 0x039b, ' ', '<',         /* V  >   Λ  < */
1079    '\\', '|', 0xff1e, CACA_MAGIC_FULLWIDTH,
1080            '|', '\\', 0xff1c, CACA_MAGIC_FULLWIDTH,  /* \| > |\ < */
1081    '|', '/', 0xff1e, CACA_MAGIC_FULLWIDTH,
1082            '/', '|', 0xff1c, CACA_MAGIC_FULLWIDTH,   /* |/ > /| < */
1083    /* Unicode */
1084    0x2584, ' ', ' ', 0x2584, ' ', 0x2580, 0x2580, ' ',       /* ▄   ▄  ▀ ▀  */
1085    0x2588, ' ', 0x2584, 0x2584, ' ', 0x2588, 0x2580, 0x2580, /* █  ▄▄  █ ▀▀ */
1086    0x2588, 0x2584, 0x2584, 0x2588,
1087            0x2580, 0x2588, 0x2588, 0x2580,                   /* █▄ ▄█ ▀█ █▀ */
1088    /* TODO: Braille */
1089    /* Not perfect, but better than nothing */
1090    0x2591, ' ', 0x28e4, 0x28e4, ' ', 0x2591, 0x281b, 0x281b, /* ░  ⣤⣤  ░ ⠛⠛ */
1091    0x2592, ' ', 0x28f6, 0x28f6, ' ', 0x2592, 0x283f, 0x283f, /* ▒  ⣶⣶  ▒ ⠿⠿ */
1092    0, 0, 0, 0, 0, 0, 0, 0
1093};
1094
1095static void leftpair(uint32_t pair[2])
1096{
1097    int i;
1098
1099    for(i = 0; leftright2x2[i]; i += 2)
1100        if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1])
1101        {
1102            pair[0] = leftright2x2[(i & ~3) | ((i + 2) & 3)];
1103            pair[1] = leftright2x2[((i & ~3) | ((i + 2) & 3)) + 1];
1104            return;
1105        }
1106
1107    for(i = 0; leftright2x4[i]; i += 2)
1108        if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1])
1109        {
1110            pair[0] = leftright2x4[(i & ~7) | ((i + 2) & 7)];
1111            pair[1] = leftright2x4[((i & ~7) | ((i + 2) & 7)) + 1];
1112            return;
1113        }
1114}
1115
1116static void rightpair(uint32_t pair[2])
1117{
1118    int i;
1119
1120    for(i = 0; leftright2x2[i]; i += 2)
1121        if(pair[0] == leftright2x2[i] && pair[1] == leftright2x2[i + 1])
1122        {
1123            pair[0] = leftright2x2[(i & ~3) | ((i - 2) & 3)];
1124            pair[1] = leftright2x2[((i & ~3) | ((i - 2) & 3)) + 1];
1125            return;
1126        }
1127
1128    for(i = 0; leftright2x4[i]; i += 2)
1129        if(pair[0] == leftright2x4[i] && pair[1] == leftright2x4[i + 1])
1130        {
1131            pair[0] = leftright2x4[(i & ~7) | ((i - 2) & 7)];
1132            pair[1] = leftright2x4[((i & ~7) | ((i - 2) & 7)) + 1];
1133            return;
1134        }
1135}
1136
1137/*
1138 * XXX: The following functions are aliases.
1139 */
1140
1141int cucul_invert(cucul_canvas_t *) CACA_ALIAS(caca_invert);
1142int cucul_flip(cucul_canvas_t *) CACA_ALIAS(caca_flip);
1143int cucul_flop(cucul_canvas_t *) CACA_ALIAS(caca_flop);
1144int cucul_rotate_180(cucul_canvas_t *) CACA_ALIAS(caca_rotate_180);
1145int cucul_rotate_left(cucul_canvas_t *) CACA_ALIAS(caca_rotate_left);
1146int cucul_rotate_right(cucul_canvas_t *) CACA_ALIAS(caca_rotate_right);
1147int cucul_stretch_left(cucul_canvas_t *) CACA_ALIAS(caca_stretch_left);
1148int cucul_stretch_right(cucul_canvas_t *) CACA_ALIAS(caca_stretch_right);
1149
Note: See TracBrowser for help on using the repository browser.