source: toilet/trunk/src/filter.c @ 4068

Last change on this file since 4068 was 4068, checked in by Sam Hocevar, 13 years ago

Add a "border" filter to add a border around text.

  • Property svn:keywords set to Id
File size: 6.0 KB
Line 
1/*
2 *  TOIlet        The Other Implementation’s letters
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: filter.c 4068 2009-11-30 00:47:24Z sam $
7 *
8 *  This program 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 post-processing filter functions.
17 */
18
19#include "config.h"
20
21#if defined(HAVE_INTTYPES_H)
22#   include <inttypes.h>
23#endif
24#include <string.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <caca.h>
28
29#include "toilet.h"
30#include "filter.h"
31
32static void filter_crop(context_t *);
33static void filter_gay(context_t *);
34static void filter_metal(context_t *);
35static void filter_flip(context_t *);
36static void filter_flop(context_t *);
37static void filter_180(context_t *);
38static void filter_left(context_t *);
39static void filter_right(context_t *);
40static void filter_border(context_t *);
41
42struct
43{
44    char const *name;
45    void (*function)(context_t *);
46    char const *description;
47}
48const lookup[] =
49{
50    { "crop", filter_crop, "crop unused blanks" },
51    { "gay", filter_gay, "add a rainbow colour effect" },
52    { "metal", filter_metal, "add a metallic colour effect" },
53    { "flip", filter_flip, "flip horizontally" },
54    { "flop", filter_flop, "flip vertically" },
55    { "rotate", filter_180, NULL }, /* backwards compatibility */
56    { "180", filter_180, "rotate 180 degrees" },
57    { "left", filter_left, "rotate 90 degrees counterclockwise" },
58    { "right", filter_right, "rotate 90 degrees clockwise" },
59    { "border", filter_border, "surround text with a border" },
60};
61
62int filter_list(void)
63{
64    unsigned int i;
65
66    printf("Available filters:\n");
67    for(i = 0; i < sizeof(lookup) / sizeof(lookup[0]); i++)
68        if(lookup[i].description)
69            printf("\"%s\": %s\n", lookup[i].name, lookup[i].description);
70
71    return 0;
72}
73
74int filter_add(context_t *cx, char const *filter)
75{
76    unsigned int n;
77    int i;
78
79    for(;;)
80    {
81        while(*filter == ':')
82            filter++;
83
84        if(*filter == '\0')
85            break;
86
87        for(i = sizeof(lookup) / sizeof(lookup[0]); i--; )
88            if(!strncmp(filter, lookup[i].name, strlen(lookup[i].name)))
89            {
90                n = strlen(lookup[i].name);
91                break;
92            }
93
94        if(i == -1 || (filter[n] != ':' && filter[n] != '\0'))
95        {
96            fprintf(stderr, "unknown filter near `%s'\n", filter);
97            return -1;
98        }
99
100        if((cx->nfilters % 16) == 0)
101            cx->filters = realloc(cx->filters, (cx->nfilters + 16)
102                                                 * sizeof(lookup[0].function));
103        cx->filters[cx->nfilters] = lookup[i].function;
104        cx->nfilters++;
105
106        filter += n;
107    }
108
109    return 0;
110}
111
112int filter_do(context_t *cx)
113{
114    unsigned int i;
115
116    for(i = 0; i < cx->nfilters; i++)
117        cx->filters[i](cx);
118
119    return 0;
120}
121
122int filter_end(context_t *cx)
123{
124    free(cx->filters);
125
126    return 0;
127}
128
129static void filter_crop(context_t *cx)
130{
131    unsigned int x, y, w, h;
132    unsigned int xmin, xmax, ymin, ymax;
133
134    xmin = w = caca_get_canvas_width(cx->torender);
135    xmax = 0;
136    ymin = h = caca_get_canvas_height(cx->torender);
137    ymax = 0;
138
139    for(y = 0; y < h; y++)
140        for(x = 0; x < w; x++)
141    {
142        unsigned long int ch = caca_get_char(cx->torender, x, y);
143        if(ch != (unsigned char)' ')
144        {
145            if(x < xmin)
146                xmin = x;
147            if(x > xmax)
148                xmax = x;
149            if(y < ymin)
150                ymin = y;
151            if(y > ymax)
152                ymax = y;
153        }
154    }
155
156    if(xmax < xmin || ymax < ymin)
157        return;
158
159    caca_set_canvas_boundaries(cx->torender, xmin, ymin,
160                                xmax - xmin + 1, ymax - ymin + 1);
161}
162
163static void filter_metal(context_t *cx)
164{
165    static unsigned char const palette[] =
166    {
167        CACA_LIGHTBLUE, CACA_BLUE, CACA_LIGHTGRAY, CACA_DARKGRAY,
168    };
169
170    unsigned int x, y, w, h;
171
172    w = caca_get_canvas_width(cx->torender);
173    h = caca_get_canvas_height(cx->torender);
174
175    for(y = 0; y < h; y++)
176        for(x = 0; x < w; x++)
177    {
178        unsigned long int ch = caca_get_char(cx->torender, x, y);
179        int i;
180
181        if(ch == (unsigned char)' ')
182            continue;
183
184        i = ((cx->lines + y + x / 8) / 2) % 4;
185        caca_set_color_ansi(cx->torender, palette[i], CACA_TRANSPARENT);
186        caca_put_char(cx->torender, x, y, ch);
187    }
188}
189
190static void filter_gay(context_t *cx)
191{
192    static unsigned char const rainbow[] =
193    {
194        CACA_LIGHTMAGENTA, CACA_LIGHTRED, CACA_YELLOW,
195        CACA_LIGHTGREEN, CACA_LIGHTCYAN, CACA_LIGHTBLUE,
196    };
197    unsigned int x, y, w, h;
198
199    w = caca_get_canvas_width(cx->torender);
200    h = caca_get_canvas_height(cx->torender);
201
202    for(y = 0; y < h; y++)
203        for(x = 0; x < w; x++)
204    {
205        unsigned long int ch = caca_get_char(cx->torender, x, y);
206        if(ch != (unsigned char)' ')
207        {
208            caca_set_color_ansi(cx->torender,
209                                 rainbow[(x / 2 + y + cx->lines) % 6],
210                                 CACA_TRANSPARENT);
211            caca_put_char(cx->torender, x, y, ch);
212        }
213    }
214}
215
216static void filter_flip(context_t *cx)
217{
218    caca_flip(cx->torender);
219}
220
221static void filter_flop(context_t *cx)
222{
223    caca_flop(cx->torender);
224}
225
226static void filter_180(context_t *cx)
227{
228    caca_rotate_180(cx->torender);
229}
230
231static void filter_left(context_t *cx)
232{
233    caca_rotate_left(cx->torender);
234}
235
236static void filter_right(context_t *cx)
237{
238    caca_rotate_right(cx->torender);
239}
240
241static void filter_border(context_t *cx)
242{
243    int w, h;
244
245    w = caca_get_canvas_width(cx->torender);
246    h = caca_get_canvas_height(cx->torender);
247
248    caca_set_canvas_boundaries(cx->torender, -1, -1, w + 2, h + 2);
249
250    caca_draw_cp437_box(cx->torender, 0, 0, w + 2, h + 2);
251}
252
Note: See TracBrowser for help on using the repository browser.