Dark Bit Factory &  Gravity
		GENERAL => Challenges & Competitions => Topic started 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.
#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;
}
			 
			
			- 
				Thats pretty cool, I didnt really know what to expect and was fairly supprised. Nice one Jim  8)
			
 
			
			- 
				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.
			 
			
			- 
				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.)
			
 
			
			- 
				The more the merrier dude! Or perhaps you could adapt your program to do something else?
			
 
			
			- 
				@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.
			 
			
			- 
				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
			 
			
			- 
				Cool program :) well done :cheers:
			
 
			
			- 
				Thats pretty cool Jim :)