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

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

Allow to temporarily disable dirty rectangle handling. This allows for huge
speedups when the calling application knows the dirty rectangle covered by
a complex operation.

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