C# Ascii Art Tutorial

If you got here, you probably want to know how Ascii Art works and how to use C# to transform images into text. We’ll do this by making good use of LockBits() and UnlockBits(), and also, a pointer - going unsafe !

I know those make everything more complicated, but they’re more efficient.

How does an Ascii Art generator work?

first, it opens the Image and resizes it to a custom size (about 100x100)

using 2 loops and a pointer, it gets the color of each pixel in the image (the image, stored in memory, looks like a two-dimensional array of pixels)

for each pixel, it adds a character into a text file, depending on the alpha (transparency)

Now if you got a basic idea about how this works, you can build your own program - no need to worry about the source code, you’ll find everything here, including the necessary explainations.

Start by creating a Forms Project, make sure you have checked Allow unsafe code from Project->Properties->Build.

In form1_load add the following line to load the image from the executable’s directory:

1

Imageimg=Image.FromFile("image.png");

Then, we transform this image into a Bitmap, and resize it to 100x100 pixels - don’t use HD images there, because it will take some time to check every pixel:

1
2
3

Bitmapbmp=newBitmap(img,100,100);// you can increase the Ascii Art's quality by increasing the bitmap's dimensions
//thisalsoincreasesthetimetakenbytheconversionprocess...

Now we need a StringBuilder in which we store the characters corresponding to the image’s pixels.

Update: it is more efficient to use a StringBuilder instead of a string (see here why).

1.From Pixel to Char

As I said, we’ll use those 2 functions :

LockBits() - locks the image in the system’s memory so we can directly get pixel’s attributes by using a pointerUnlockBits() - releases the memory used

As you know, an image is created by a group of pixels and each pixel takes 4 bytes of memory, that means it has 4 properties: Red, Green, Blue and Alpha/transparency. From the memory we can read each pixel’s property.

Each pixel must be transformed into a character with the same color and all the characters must be the same width and height (monospaced) so we maintain the aspect ratio.

privateunsafeStringBuilderconvert_image(Bitmapbmp){StringBuilderasciiResult=newStringBuilder();//here we store the ascii-art string
//setting the font's size & type (Courier new is monospace)
asciiResult.Append("");//storing the image's height & width
intbmpHeight=bmp.Height;intbmpWidth=bmp.Width;//here we lock the image in the memory by using LockBits
BitmapDatabmpData=bmp.LockBits(newRectangle(0,0,bmpWidth,bmpHeight),ImageLockMode.ReadOnly,bmp.PixelFormat);// bmpStride tells us how many pixels are on a line
// because images have multiple lines of pixels (like 2D arrays)
intbmpStride=bmpData.Stride;// this gets the memory address of the first pixel in the image
// currentPixel is the pointer we'll use
byte*currentPixel=(byte*)bmpData.Scan0;for(inty=0;y<bmpHeight;y++){for(intx=0;x<bmpWidth;x++){// as I said a pixel takes 4 bytes of memory so it has 4 attributes
intr=currentPixel[x*4];intg=currentPixel[x*4+1];intb=currentPixel[x*4+2];intalpha=currentPixel[x*4+3];// appending the character to the ascii-art stringbuilder
// note there's a custom function 'getAsciiChar()' - I'll explain it soon
asciiResult.Append(String.Format("<span style="color:rgb({0},{1},{2});">{3}</span>",r,g,b,getAsciiChar(alpha)));}// reached end of this line, by adding bmpStride (number of pixels on each line)
// to the memory address, it gives us the adress of the first pixel on the next line
currentPixel+=bmpStride;asciiResult.Append("
");
}asciiResult.Append("");// closing the body tag we opened at the beginning
bmp.UnlockBits(bmpData);//removing the image from the memory
returnasciiResult;// returning the ascii-art stringbuilder
}

2.Choosing the right character

There’s a function in the code above that I’ll explain here: getAsciiChar(). What it does? It returns a character depending on the transparency of the current pixel (so it looks like true ascii art).