/* * TOIlet The Other Implementation’s letters * Copyright (c) 2006 Sam Hocevar * All Rights Reserved * * This program is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What The Fuck You Want * To Public License, Version 2, as published by Sam Hocevar. See * http://sam.zoy.org/wtfpl/COPYING for more details. */ /* * This file contains post-processing filter functions. */ #include "config.h" #if defined(HAVE_INTTYPES_H) # include #endif #include #include #include #include #include "toilet.h" #include "filter.h" static void filter_crop(context_t *); static void filter_rainbow(context_t *); static void filter_metal(context_t *); static void filter_flip(context_t *); static void filter_flop(context_t *); static void filter_180(context_t *); static void filter_left(context_t *); static void filter_right(context_t *); static void filter_border(context_t *); struct { char const *name; void (*function)(context_t *); char const *description; } const lookup[] = { { "crop", filter_crop, "crop unused blanks" }, { "rainbow", filter_rainbow, "add a rainbow colour effect" }, { "metal", filter_metal, "add a metallic colour effect" }, { "flip", filter_flip, "flip horizontally" }, { "flop", filter_flop, "flip vertically" }, { "rotate", filter_180, NULL }, /* backwards compatibility */ { "180", filter_180, "rotate 180 degrees" }, { "left", filter_left, "rotate 90 degrees counterclockwise" }, { "right", filter_right, "rotate 90 degrees clockwise" }, { "border", filter_border, "surround text with a border" }, }; int filter_list(void) { unsigned int i; printf("Available filters:\n"); for(i = 0; i < sizeof(lookup) / sizeof(lookup[0]); i++) if(lookup[i].description) printf("\"%s\": %s\n", lookup[i].name, lookup[i].description); return 0; } int filter_add(context_t *cx, char const *filter) { unsigned int n; int i; for(;;) { while(*filter == ':') filter++; if(*filter == '\0') break; for(i = sizeof(lookup) / sizeof(lookup[0]); i--; ) if(!strncmp(filter, lookup[i].name, strlen(lookup[i].name))) { n = strlen(lookup[i].name); break; } if(i == -1 || (filter[n] != ':' && filter[n] != '\0')) { fprintf(stderr, "unknown filter near `%s'\n", filter); return -1; } if((cx->nfilters % 16) == 0) cx->filters = realloc(cx->filters, (cx->nfilters + 16) * sizeof(lookup[0].function)); cx->filters[cx->nfilters] = lookup[i].function; cx->nfilters++; filter += n; } return 0; } int filter_do(context_t *cx) { unsigned int i; for(i = 0; i < cx->nfilters; i++) cx->filters[i](cx); return 0; } int filter_end(context_t *cx) { free(cx->filters); return 0; } static void filter_crop(context_t *cx) { unsigned int x, y, w, h; unsigned int xmin, xmax, ymin, ymax; xmin = w = caca_get_canvas_width(cx->torender); xmax = 0; ymin = h = caca_get_canvas_height(cx->torender); ymax = 0; for(y = 0; y < h; y++) for(x = 0; x < w; x++) { unsigned long int ch = caca_get_char(cx->torender, x, y); if(ch != (unsigned char)' ') { if(x < xmin) xmin = x; if(x > xmax) xmax = x; if(y < ymin) ymin = y; if(y > ymax) ymax = y; } } if(xmax < xmin || ymax < ymin) return; caca_set_canvas_boundaries(cx->torender, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); } static void filter_metal(context_t *cx) { static unsigned char const palette[] = { CACA_LIGHTBLUE, CACA_BLUE, CACA_LIGHTGRAY, CACA_DARKGRAY, }; unsigned int x, y, w, h; w = caca_get_canvas_width(cx->torender); h = caca_get_canvas_height(cx->torender); for(y = 0; y < h; y++) for(x = 0; x < w; x++) { unsigned long int ch = caca_get_char(cx->torender, x, y); int i; if(ch == (unsigned char)' ') continue; i = ((cx->lines + y + x / 8) / 2) % 4; caca_set_color_ansi(cx->torender, palette[i], CACA_TRANSPARENT); caca_put_char(cx->torender, x, y, ch); } } static void filter_rainbow(context_t *cx) { static unsigned char const rainbow[] = { CACA_LIGHTMAGENTA, CACA_LIGHTRED, CACA_YELLOW, CACA_LIGHTGREEN, CACA_LIGHTCYAN, CACA_LIGHTBLUE, }; unsigned int x, y, w, h; w = caca_get_canvas_width(cx->torender); h = caca_get_canvas_height(cx->torender); for(y = 0; y < h; y++) for(x = 0; x < w; x++) { unsigned long int ch = caca_get_char(cx->torender, x, y); if(ch != (unsigned char)' ') { caca_set_color_ansi(cx->torender, rainbow[(x / 2 + y + cx->lines) % 6], CACA_TRANSPARENT); caca_put_char(cx->torender, x, y, ch); } } } static void filter_flip(context_t *cx) { caca_flip(cx->torender); } static void filter_flop(context_t *cx) { caca_flop(cx->torender); } static void filter_180(context_t *cx) { caca_rotate_180(cx->torender); } static void filter_left(context_t *cx) { caca_rotate_left(cx->torender); } static void filter_right(context_t *cx) { caca_rotate_right(cx->torender); } static void filter_border(context_t *cx) { int w, h; w = caca_get_canvas_width(cx->torender); h = caca_get_canvas_height(cx->torender); caca_set_canvas_boundaries(cx->torender, -1, -1, w + 2, h + 2); caca_draw_cp437_box(cx->torender, 0, 0, w + 2, h + 2); }