/* * EffecTV - Realtime Digital Video Effector * Copyright (C) 2001-2003 FUKUCHI Kentaro * * matrixTV - A Matrix Like effect. * This plugin for EffectTV is under GNU General Public License * See the "COPYING" that should be shiped with this source code * Copyright (C) 2001-2003 Monniez Christophe * d-fence@swing.be * * 2003/12/24 Kentaro Fukuchi * - Completely rewrote but based on Monniez's idea. * - Uses edge detection, not only G value of each pixel. * - Added 4x4 font includes number, alphabet and Japanese Katakana characters. */ #include #include #include #include "EffecTV.h" #include "utils.h" #include "matrixFont.xpm" #define CHARNUM 80 #define FONT_W 4 #define FONT_H 4 #define FONT_DEPTH 4 static int start(void); static int stop(void); static int draw(RGB32 *src, RGB32 *dest); static int event(SDL_Event *); static char *effectname = "MatrixTV"; static int stat; static int mode = 0; static unsigned char font[CHARNUM * FONT_W * FONT_H]; static unsigned char *cmap; static unsigned char *vmap; static unsigned char *img; static int mapW, mapH; static RGB32 palette[256 * FONT_DEPTH]; typedef struct { int mode; int y; int timer; int speed; } Blip; #define MODE_NONE 0 #define MODE_FALL 1 #define MODE_STOP 2 #define MODE_SLID 3 static Blip *blips; static RGB32 green(unsigned int v); static void setPalette(void); static void setPattern(void); static void drawChar(RGB32 *dest, unsigned char c, unsigned char v); static void createImg(RGB32 *src); static void updateCharMap(void); effect *matrixRegister() { effect *entry; mapW = video_width / FONT_W; mapH = video_height / FONT_H; cmap = (unsigned char *)sharedbuffer_alloc(mapW * mapH); vmap = (unsigned char *)sharedbuffer_alloc(mapW * mapH); img = (unsigned char *)sharedbuffer_alloc(mapW * mapH); if(cmap == NULL || vmap == NULL || img == NULL) { return NULL; } blips = (Blip *)sharedbuffer_alloc(mapW * sizeof(Blip)); if(blips == NULL) { return NULL; } setPattern(); setPalette(); entry = (effect *)malloc(sizeof(effect)); if(entry == NULL) { return NULL; } entry->name = effectname; entry->start = start; entry->stop = stop; entry->draw = draw; entry->event = 0; return entry; } static int start() { memset(cmap, CHARNUM - 1, mapW * mapH * sizeof(unsigned char)); memset(vmap, 0, mapW * mapH * sizeof(unsigned char)); memset(blips, 0, mapW * sizeof(Blip)); stat = 1; return 0; } static int stop() { stat = 0; return 0; } static int draw(RGB32 *src, RGB32 *dest) { int x, y; RGB32 *p, *q; unsigned char *c, *v, *i; unsigned int val; RGB32 a, b; updateCharMap(); createImg(src); c = cmap; v = vmap; i = img; p = dest; for(y=0; y 255) val = 255; drawChar(q, *c, val); i++; v++; c++; q += FONT_W; } p += video_width * FONT_H; } if(mode == 1) { for(x=0; x> 1; *dest++ = a | b; } } return 0; } /* Create edge-enhanced image data from the input */ static void createImg(RGB32 *src) { int x, y; RGB32 *p; unsigned char *q; unsigned int val; RGB32 pc, pr, pb; //center, right, below int r, g, b; q = img; for(y=0; y> 15; g = (int)(pc & 0x00ff00) >> 7; b = (int)(pc & 0x0000ff) * 2; val = (r + 2*g + b) >> 5; // val < 64 r -= (int)(pr & 0xff0000)>>16; g -= (int)(pr & 0x00ff00)>>8; b -= (int)(pr & 0x0000ff); r -= (int)(pb & 0xff0000)>>16; g -= (int)(pb & 0x00ff00)>>8; b -= (int)(pb & 0x0000ff); val += (r * r + g * g + b * b)>>5; if(val > 160) val = 160; // want not to make blip from the edge. *q = (unsigned char)val; p += FONT_W; q++; } src += video_width * FONT_H; } } #define WHITE 0.45 static RGB32 green(unsigned int v) { unsigned int w; if(v < 256) { return ((int)(v*WHITE)<<16)|(v<<8)|(int)(v*WHITE); } w = v - (int)(256*WHITE); if(w > 255) w = 255; return (w << 16) + 0xff00 + w; } static void setPalette(void) { int i, c; for(i=0; i<256; i++) { c = i; palette[i*FONT_DEPTH ] = 0; palette[i*FONT_DEPTH+1] = green(0x44 * c / 170); palette[i*FONT_DEPTH+2] = green(0x99 * c / 170); palette[i*FONT_DEPTH+3] = green(0xff * c / 170); } } static void setPattern(void) { int c, l, x, y, cx, cy; char *p; unsigned char v; /* FIXME: This code is highly depends on the structure of bundled */ /* matrixFont.xpm. */ for(l = 0; l < 32; l++) { p = matrixFont[5 + l]; cy = l /4; y = l % 4; for(c = 0; c < 40; c++) { cx = c / 4; x = c % 4; switch(*p) { case ' ': v = 0; break; case '.': v = 1; break; case 'o': v = 2; break; case 'O': default: v = 3; break; } font[(cy * 10 + cx) * FONT_W * FONT_H + y * FONT_W + x] = v; p++; } } } static void drawChar(RGB32 *dest, unsigned char c, unsigned char v) { int x, y, i; int *p; unsigned char *f; i = 0; if(v == 255) { // sticky characters v = 160; } p = &palette[(int)v * FONT_DEPTH]; f = &font[(int)c * FONT_W * FONT_H]; for(y=0; y> 30) + 1; blips[x].timer = 0; } else if((r & 0x0f000) == 0x0f000) { blips[x].mode = MODE_SLID; blips[x].timer = (r >> 28) + 15; blips[x].speed = ((r >> 24) & 3) + 2; } } static void blipFall(int x) { int i, y; unsigned char *p, *c; unsigned int r; y = blips[x].y; p = vmap + x + y * mapW; c = cmap + x + y * mapW; for(i=blips[x].speed; i>0; i--) { if(blips[x].timer > 0) { *p = 255; } else { *p = 254 - i * 10; } *c = inline_fastrand() % CHARNUM; p += mapW; c += mapW; y++; if(y >= mapH) break; } if(blips[x].timer > 0) { blips[x].timer--; } if(y > mapH) { blips[x].mode = MODE_NONE; } if(y < mapH) blips[x].y = y; else blips[x].y = 0; if(blips[x].timer == 0) { r = inline_fastrand(); if((r & 0x3f00) == 0x3f00) { blips[x].timer = (r >> 28) + 8; } else if(blips[x].speed > 1 && (r & 0x7f) == 0x7f) { blips[x].mode = MODE_STOP; blips[x].timer = (r >> 26) + 30; } } } static void blipStop(int x) { int y; y = blips[x].y; vmap[x + y * mapW] = 254; cmap[x + y * mapW] = inline_fastrand() % CHARNUM; blips[x].timer--; if(blips[x].timer < 0) { blips[x].mode = MODE_FALL; } } static void blipSlide(int x) { int y, dy; unsigned char *p; blips[x].timer--; if(blips[x].timer < 0) { blips[x].mode = MODE_NONE; } p = cmap + x + mapW * (mapH - 1); dy = mapW * blips[x].speed; for(y=mapH - blips[x].speed; y>0; y--) { *p = *(p - dy); p -= mapW; } for(y=blips[x].speed; y>0; y--) { *p = inline_fastrand() % CHARNUM; p -= mapW; } } static int event(SDL_Event *event) { if(event->type == SDL_KEYDOWN) { switch(event->key.keysym.sym) { case SDLK_SPACE: memset(cmap, CHARNUM - 1, mapW * mapH * sizeof(unsigned char)); memset(vmap, 0, mapW * mapH * sizeof(unsigned char)); memset(blips, 0, mapW * sizeof(Blip)); break; case SDLK_1: case SDLK_KP1: mode = 0; break; case SDLK_2: case SDLK_KP2: mode = 1; break; default: break; } } return 0; }