Dark Bit Factory & Gravity

GENERAL => Challenges & Competitions => Topic started by: Jim on July 11, 2006

Title: ASCII AVI
Post by: Jim on July 11, 2006
http://members.iinet.net.au/~jimshaw/ascii.zip
Run ascii.exe and point it at an avi file (some xvids don't work, sorry).
Might take a few seconds (a minute?) to pre-compute some stuff after you've picked the file.
Have fun!
Jim

Here's the source (C++).  If you try to use it with devc++ you need to link with winmm.lib and vfw32.lib.
Code: [Select]
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <mmsystem.h>
#include <vfw.h>
#include <wincon.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <assert.h>

#define SCREEN_WIDTH 80
#define SCREEN_HEIGHT 25

#define FIRST_CHAR 32
#define LAST_CHAR 255
//#define FIRST_CHAR '0'
//#define LAST_CHAR '9'
#define NUM_CHARS (LAST_CHAR-FIRST_CHAR)

CHAR_INFO screen[SCREEN_WIDTH*SCREEN_HEIGHT];

void Sprite(int xx, int yy, unsigned int *spr, unsigned int w, unsigned int h);

struct
{
unsigned int rgb;
unsigned int h,s,v;
} colourtable[NUM_CHARS][256];

struct
{
unsigned int fg;
unsigned int bg;
unsigned int rf,gf,bf;
unsigned int rb,gb,bb;
} colours[256];

struct
{
unsigned char ch;
unsigned char col;
} lookup[32768];

unsigned int myabs(int x)
{
return x>=0?x:-x;
}

void loadimage(const char *fname, unsigned int **buffer)
{
struct
{
BITMAPINFOHEADER bmih;
RGBQUAD bmic[4];
} bmp={0};
HBITMAP hbm;
HDC hdc;
hbm = (HBITMAP)LoadImage(NULL, fname, IMAGE_BITMAP, 0,0, LR_LOADFROMFILE);
hdc = GetDC(NULL);
bmp.bmih.biSize = sizeof(BITMAPINFOHEADER);
GetDIBits(hdc, hbm, 0,0, NULL, (BITMAPINFO *)&bmp, 0);
bmp.bmih.biBitCount = 32;
*buffer = new unsigned int [myabs(bmp.bmih.biHeight)*bmp.bmih.biWidth];
GetDIBits(hdc, hbm, 0, bmp.bmih.biHeight, *buffer, (BITMAPINFO *)&bmp, DIB_RGB_COLORS);
ReleaseDC(NULL, hdc);
DeleteObject((HGDIOBJ)hbm);

int w = bmp.bmih.biWidth;
int h = myabs(bmp.bmih.biHeight);
unsigned int tmp[2048];
unsigned int *src = *buffer;
unsigned int *dst = *buffer+(h-1)*w;
int y;
for (y = 0; y < h/2; y++)
{
memcpy(tmp, src, w*sizeof *src);
memcpy(src, dst, w*sizeof *src);
memcpy(dst, tmp, w*sizeof *src);
src += w;
dst -= w;
}

}

void freeimage(unsigned int *buffer)
{
delete [] buffer;
}

PAVISTREAM avistream;
HDC avihdc;
HBITMAP avibmp;
unsigned int currframe;
PGETFRAME frame;
unsigned int fps;

bool openavi(const char *fname)
{
AVISTREAMINFO info;

if (AVIStreamOpenFromFile(&avistream, fname, streamtypeVIDEO, 0, OF_READ, NULL))
return false;
if ((frame = AVIStreamGetFrameOpen(avistream, NULL))==NULL)
return false;
AVIStreamLength(avistream);
AVIStreamInfo(avistream, &info, sizeof info);
fps = info.dwRate/info.dwScale;

currframe = 0;

HDC hdc = GetDC(NULL);
avihdc = CreateCompatibleDC(hdc);
avibmp = CreateCompatibleBitmap(hdc, SCREEN_WIDTH, SCREEN_HEIGHT);
SetStretchBltMode(avihdc, HALFTONE);
SetBrushOrgEx(avihdc, 0,0, NULL);
SelectObject(avihdc, avibmp);

ReleaseDC(NULL, hdc);
return true;
}

bool avigetframe(void)
{
BITMAPINFOHEADER *bmi;
unsigned int avispr[SCREEN_WIDTH*SCREEN_HEIGHT];
struct
{
BITMAPINFOHEADER bmih;
RGBQUAD bmic[4];
} bmp={0};

bmp.bmih.biSize = sizeof(BITMAPINFOHEADER);

bmi = (BITMAPINFOHEADER *)AVIStreamGetFrame(frame, currframe++);
if (bmi == NULL)
return false;

StretchDIBits(avihdc, 0,0, SCREEN_WIDTH,SCREEN_HEIGHT, 0,0, bmi->biWidth,bmi->biHeight, (void *)(bmi+1), (BITMAPINFO *)bmi, DIB_RGB_COLORS, SRCCOPY);
GetDIBits(avihdc, avibmp, 0,0, NULL, (BITMAPINFO *)&bmp, 0);
GetDIBits(avihdc, avibmp, 0,SCREEN_HEIGHT, avispr, (BITMAPINFO *)&bmp, DIB_RGB_COLORS);

int w = bmp.bmih.biWidth;
int h = myabs(bmp.bmih.biHeight);
unsigned int tmp[2048];
unsigned int *src = avispr;
unsigned int *dst = avispr+(h-1)*w;
int y;
for (y = 0; y < h/2; y++)
{
memcpy(tmp, src, w*sizeof *src);
memcpy(src, dst, w*sizeof *src);
memcpy(dst, tmp, w*sizeof *src);
src += w;
dst -= w;
}

Sprite(0,0,avispr,SCREEN_WIDTH, SCREEN_HEIGHT);
return true;
}

void avirestart(void)
{
currframe = 0;
}

void closeavi(void)
{
DeleteObject(avibmp);
DeleteDC(avihdc);

AVIStreamGetFrameClose(frame);
AVIStreamRelease(avistream);
}

void rgb2hsv(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v)
{
unsigned int mn, mx;
int d;
int ht;

if (r <= g && r <= b)
mn = r;
else if (g <= r && g <= b)
mn = g;
else
mn = b;

if (r >= g && r >= b)
mx = r;
else if (g >= r && g >= b)
mx = g;
else
mx = b;

d = mx - mn;
*v = mx;

if (mx == 0 || (mx-mn) == 0)
{
*s = 0;
*h = 0;
}
else
{
*s = (d<<8)/mx;
if (mx == r)
ht = (int)((g-b)<<8)/d;
else if (mx == g)
ht = 512+(int)((b-r)<<8)/d;
else
ht = 1024+(int)((r-g)<<8)/d;
ht = (ht*60)>>8;
if (ht < 0)
ht += 360;
*h = ht;
}
}

void buildcolourtable(void)
{
unsigned int rf,gf,bf;
unsigned int rb,gb,bb;
unsigned int x;

for (x = 0; x < 256; x++)
{
if (x&FOREGROUND_BLUE)
bf = 127;
else
bf = 0;
if (x&FOREGROUND_GREEN)
gf = 127;
else
gf = 0;
if (x&FOREGROUND_RED)
rf = 127;
else
rf = 0;
if (x&FOREGROUND_INTENSITY)
{
bf<<=1;
gf<<=1;
rf<<=1;
}
if (x&BACKGROUND_BLUE)
bb = 127;
else
bb = 0;
if (x&BACKGROUND_GREEN)
gb = 127;
else
gb = 0;
if (x&BACKGROUND_RED)
rb = 127;
else
rb = 0;
if (x&BACKGROUND_INTENSITY)
{
bb<<=1;
gb<<=1;
rb<<=1;
}
colours[x].fg = (rf<<16)|(gf<<8)|bf;
colours[x].rf = rf;
colours[x].gf = gf;
colours[x].bf = bf;
colours[x].bg = (rb<<16)|(gb<<8)|bb;
colours[x].rb = rb;
colours[x].gb = gb;
colours[x].bb = bb;
}
}

unsigned char colourmatch(unsigned int rgb, unsigned char *ch)
{
unsigned int r,g,b;
unsigned int h,s,v;
int dh,ds,dv;
unsigned int x;
unsigned int dist, d;
unsigned int fx,fz;
r = rgb&0xff0000;
g = rgb&0x00ff00;
b = rgb&0x0000ff;

rgb2hsv(r>>16,g>>8,b,&h,&s,&v);

dist = UINT_MAX;
fx=fz=0;
for (x = 0; x < NUM_CHARS; x++)
{
for (int z = 0; z < 256; z++)
{
//dh=ds=dv=0;
dh = h - colourtable[x][z].h;
ds = s - colourtable[x][z].s;
dv = v - colourtable[x][z].v;
d = dh*dh + ds*ds + dv*dv;
if (d < dist)
{
dist = d;
fz = z;
fx = x;
}
}
}
*ch = (unsigned char)(FIRST_CHAR+fx);
return (unsigned char)fz;
}

unsigned char lookupcolourmatch(unsigned int rgb, unsigned char *ch)
{
rgb = ((rgb>>3)&0x001f)|((rgb>>6)&0x3e0)|((rgb>>9)&0x7c00);
*ch = lookup[rgb].ch;
return lookup[rgb].col;
}


void createlookup(void)
{
unsigned int r,g,b,rgb;
unsigned int x;

for (x = 0; x < 32768; x++)
{
r = x&0x7C00;
g = x&0x3E0;
b = x&0x1F;

rgb = (r<<9)|(g<<6)|(b<<3);
rgb |= (rgb>>5)&0x070707;

lookup[x].col = colourmatch(rgb, &lookup[x].ch);
}
}

void create_colours(unsigned int c, unsigned int *bmp, unsigned int w, unsigned int h, unsigned int bg, unsigned fg)
{
unsigned int scale = w*h;
unsigned int r,g,b;
int x;
bmp;
for (x = 0; x < 256; x++)
{
r = (bg*colours[x].rb + fg*colours[x].rf)/scale;
g = (bg*colours[x].gb + fg*colours[x].gf)/scale;
b = (bg*colours[x].bb + fg*colours[x].bf)/scale;

if (r>255) r = 255;
if (g>255) g = 255;
if (b>255) b = 255;

colourtable[c-FIRST_CHAR][x].rgb = (r<<16)|(g<<8)|b;
rgb2hsv(r,g,b,&colourtable[c-FIRST_CHAR][x].h, &colourtable[c-FIRST_CHAR][x].s, &colourtable[c-FIRST_CHAR][x].v);
}
}

void compute_colours(HFONT font, unsigned int w, unsigned int h)
{
HDC hdc,hdc2;
HBITMAP memBM;

hdc = GetDC(NULL);
hdc2 = CreateCompatibleDC(hdc);
memBM = CreateCompatibleBitmap(hdc,w,h);
ReleaseDC(NULL, hdc);

  Â  SelectObject(hdc2, memBM);
SelectObject(hdc2, font);

unsigned int *letter = new unsigned int[w*h];

buildcolourtable();

unsigned int x,d,a;
for (x = FIRST_CHAR; x <= LAST_CHAR; x++)
{
char xstr[1];
xstr[0]=(char)x;
TextOut(hdc2, 0,0, xstr, 1);
unsigned int bg,fg;
bg=fg=0;
for (d = 0; d < h; d++)
{
for (a = 0; a < w; a++)
{
letter[a+d*w] = (unsigned int)GetPixel(hdc2, a,d);
if (letter[a+d*w]==0xffffff)
bg++;
else
fg++;
}
}
create_colours(x,letter,w,h,bg,fg);
}
delete [] letter;
DeleteObject(memBM);
DeleteDC(hdc2);

createlookup();
}

void Print(int xx, int yy, const char *string, unsigned char colour)
{
size_t len;
len = strlen(string);
CHAR_INFO *dst;

dst = &screen[xx+yy*SCREEN_WIDTH];
while (len--)
{
dst->Char.AsciiChar = *string++;
dst->Attributes = colour;
dst++;
}
}

void Plot(int x, int y, unsigned int colour)
{
CHAR_INFO *dst;

dst = &screen[x+y*SCREEN_WIDTH];
dst->Attributes = lookupcolourmatch(colour, (unsigned char *)&dst->Char.AsciiChar);
}

void Sprite(int xx, int yy, unsigned int *spr, unsigned int w, unsigned int h)
{
int x, y;
CHAR_INFO *dst;

dst = &screen[xx+yy*SCREEN_WIDTH];

for (y = yy; y < (int)(yy+h); y++)
{
for (x = xx; x < (int)(xx+w); x++)
{
dst->Attributes = lookupcolourmatch(*spr++, (unsigned char *)&dst->Char.AsciiChar);
dst++;
}
dst += SCREEN_WIDTH-w;
}
}

int main(void)
{
HANDLE output;
COORD size = {SCREEN_WIDTH,SCREEN_HEIGHT};
COORD origin = {0,0};
SMALL_RECT dim = {0,0,SCREEN_WIDTH-1,SCREEN_HEIGHT-1};
CONSOLE_FONT_INFO fonti;
COORD fontsize;
HFONT font;

timeBeginPeriod(1);
AVIFileInit();

//create the console
output = CreateConsoleScreenBuffer(GENERIC_WRITE|GENERIC_READ , 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
SetConsoleActiveScreenBuffer(output);
SetConsoleScreenBufferSize(output, size);
GetCurrentConsoleFont(output, TRUE, &fonti);
fontsize = GetConsoleFontSize(output, fonti.nFont);

//unsigned int *bee;
//loadimage(NULL, &bee);
OPENFILENAME op;
char filename[_MAX_PATH];
char *load_ext=".avi";
BOOL res;

filename[0] = '\0';

op.lStructSize = sizeof(OPENFILENAME);
op.hwndOwner = NULL;
op.hInstance = 0;
op.lpstrFilter = "AVI Files\0*.avi\0All Files\0*\0";
op.lpstrCustomFilter = NULL;
op.nMaxCustFilter = 0;
op.nFilterIndex = 0;//curr_file_ext;
op.lpstrFile = filename;// <- default file name
op.nMaxFile = _MAX_PATH;
op.lpstrFileTitle = NULL;
op.nMaxFileTitle = 0;
op.lpstrInitialDir = NULL;// <- default dir
op.lpstrTitle = "Select File to Load";
op.Flags = OFN_ENABLESIZING|OFN_FILEMUSTEXIST|OFN_EXPLORER|OFN_LONGNAMES|OFN_NOREADONLYRETURN|OFN_PATHMUSTEXIST;
op.nFileOffset = 0;
op.nFileExtension = 0;
op.lpstrDefExt = load_ext;
op.lCustData = 0;
op.lpfnHook = NULL;
op.lpTemplateName = NULL;

res = GetOpenFileName(&op);

if (!res)
goto novid;

if (!openavi(filename))
{
MessageBox(NULL, "Sorry, that AVI file can't be played.", "Video Error", MB_ICONERROR|MB_OK);
goto novid;
}

//graphics version of console font
font = CreateFont(
-fontsize.Y,
fontsize.X,
0,0,
FW_DONTCARE,
0,0,0,
OEM_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
NONANTIALIASED_QUALITY,
FIXED_PITCH|FF_DONTCARE,
"Terminal"
);
compute_colours(font, fontsize.X, fontsize.Y);
DeleteObject(font);

LARGE_INTEGER pc,fq,ti;
QueryPerformanceFrequency(&fq);
QueryPerformanceCounter(&pc);

for (;;)
{
if (!avigetframe())
{
avirestart();
avigetframe();
}

//Sprite(0,0,bee,80,25);
QueryPerformanceCounter(&ti);
char tmp[256];
sprintf(tmp, "%3.2f", (double)fq.QuadPart/(ti.QuadPart-pc.QuadPart));
Print(0,0,tmp,FOREGROUND_RED|BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY|FOREGROUND_INTENSITY);
pc = ti;

WriteConsoleOutput(output, screen, size, origin, &dim);

if (GetAsyncKeyState(VK_ESCAPE))
break;
Sleep(1000/fps);
}
closeavi();

novid:
CloseHandle(output);
AVIFileExit();
timeEndPeriod(1);

return 0;
}
Title: Re: ASCII AVI
Post by: Tetra on July 11, 2006
Thats pretty cool, I didnt really know what to expect and was fairly supprised. Nice one Jim  8)
Title: Re: ASCII AVI
Post by: Shockwave on July 11, 2006
Had lots of fun lololm8! :)

Most of the stuff I pointed it at didn't work, but then found a file called clock.avi and it worked really well! Nice one Jim.
Title: Re: ASCII AVI
Post by: Deleter on July 11, 2006
bah, i guess I wont submit my much less functional version of pretty much the same thing...(you had to extract the frames and then use a program to convert them to 'asciivid' format.)
Title: Re: ASCII AVI
Post by: Shockwave on July 11, 2006
The more the merrier dude! Or perhaps you could adapt your program to do something else?
Title: Re: ASCII AVI
Post by: Clyde on July 11, 2006
@Jim, Im heading to find some avi's and try this out. Cool idea.
@Deleter please post it dude.

Cheers and all the best,
Clyde.
Title: Re: ASCII AVI
Post by: Jim on July 11, 2006
Use virtualdubmod to convert almost anything to AVI format (pick Indeo or Intel or uncompressed formats using the 'Change' button on the Save As menu) http://virtualdubmod.sourceforge.net/.

Also, Alt-Enter goes fullscreen.  You need to squint through your eyelashes to see the effect best.

Thanks for the comments. :)

@Deleter - please post!  I'm really interested to see how you do the conversion to ascii, I don't think my way is particularly good.

Jim
Title: Re: ASCII AVI
Post by: Ghost^BHT on July 11, 2006
Cool program :) well done :cheers:
Title: Re: ASCII AVI
Post by: Clyde on July 11, 2006
Thats pretty cool Jim :)