C# How to: Image ASCII Art

Article Purpose

This article explores the concept of rendering from source . Beyond exploring concepts this article also provides a practical implementation of all the steps required in creating an ASCII Filter.

Sir Tim Berners-Lee: 2 Pixels Per Character, 12 Characters,  Font Size 4, Zoom 100

Sir Tim Berners-Lee 2 Pixels Per Character, 12 Characters,  Font Size 4, Zoom 100

Sample Source Code

This article is accompanied by a sample source code Visual Studio project which is available for download .

Using the Sample Application

The sample source code that accompanies this article includes a sample application. The concepts illustrated in this article can tested and replicated using the sample application.

The sample application user interface implements a variety of functionality which can be described as follows:

  • Loading/Saving Images – Users are able to load source/input from the local system through clicking the Load Image button. Rendered can be saved as an file when clicking the Save Image button.
  • Pixels Per Character – This configuration option determines the number of pixels represented by a single character. Lower values result in better detail/definition and a larger proportional output. Higher values result in less detail/definition and a smaller proportional output.
  • Character Count – The number of unique characters to be rendered can be adjusted through updating this value. This value can be considered similar to number of shades of gray in a  .
  • Font Size – This option determines the Font Size related to the rendered text.
  • Zoom Level – Configure this value in order to apply a scaling level when rendering text.
  • Copy to Clipboard – Copy the current to the Windows Clipboard, in Rich Text Format.

The following image is screenshot of the Image ASCII Art sample application is action:

Image ASCII Art Sample Application

Image ASCII Art Sample Application

Anders Hejlsberg: 1 Pixel Per Character, 24 Characters, Font Size 6, Zoom 20

Anders Hejlsberg: 1 Pixel Per Character, 24 Characters, Font Size 6, Zoom 20

Converting Pixels to Characters

in various forms have been part of computer culture since the pioneering days of computing. From we gain the following:

ASCII art is a graphic design technique that uses computers for presentation and consists of pictures pieced together from the 95 printable (from a total of 128) characters defined by the ASCII Standard from 1963 and ASCII compliant character sets with proprietary extended characters (beyond the 128 characters of standard 7-bit ASCII). The term is also loosely used to refer to text based visual art in general. ASCII art can be created with any text editor, and is often used with free-form languages. Most examples of ASCII art require a fixed-width font (non-proportional fonts, as on a traditional typewriter) such as Courier for presentation.

Among the oldest known examples of ASCII art are the creations by computer-art pioneer Kenneth Knowlton from around 1966, who was working for Bell Labs at the time.[1] "Studies in Perception I" by Ken Knowlton and Leon Harmon from 1966 shows some examples of their early ASCII art.[2]

One of the main reasons ASCII art was born was because early printers often lacked graphics ability and thus characters were used in place of graphic marks. Also, to mark divisions between different print jobs from different users, bulk printers often used ASCII art to print large banners, making the division easier to spot so that the results could be more easily separated by a computer operator or clerk. ASCII art was also used in early e-mail when images could not be embedded.

Bjarne Stroustrup: 1 Pixel Per Character, 12 Characters, Font Size 6, Zoom 60

Bjarne Stroustrup: 1 Pixel Per Character, 12 Characters, Font Size 6, Zoom 60

This article explores the steps involved in rendering text representing , implementing source/input in rendering text representations. The following sections details the steps required to render text from source/input :

  1. Generate Random Characters – Generate a consisting of random characters. The number of characters will be determined through user input relating to the Character Count option. When generating the random ensure that all characters added to the are unique. In addition avoid adding control characters or punctuation characters. Control characters are non-visible characters such as Start of Text, Beep, New Line or Carriage Return. Most punctuation characters occupy a lot less screen space compared to regular alphabet characters.
  2. Determine Row and Column Count – Rows and Columns in terms of the Character Count option indicate the ratio between pixels and characters. The number of rows equate to the height in pixels divided by the Character Count. The number of columns equate to the width in pixels divided by the Character Count.
  3. Iterate Rows/Columns and Determine Colour Averages – Iterate pixels in terms of a rows and columns grid strategy. Calculate the sum total of each grid region’s colour values. Calculate the average/mean colour value through dividing the colour sum total by the Character Count squared.
  4. Map Average Colour Intensity to a Character – Using the average colour values calculate in the previous step, calculate a colour intensity value ranging between 0 and the number of randomly generate characters. The intensity value should be implemented as an array index in accessing the of random characters. All of the pixels included in calculating an average value should be represented by the random character located at the index equating to the colour average intensity value.

Linus Torvalds: 1 Pixel Per Character, 16 Characters, Font Size 5, Zoom 60

Linus Torvalds: 1 Pixel Per Character, 16 Characters, Font Size 5, Zoom 60

Converting Text to an Image

When rendering high definition the resulting text can easily consist of several thousand characters. Attempting to display such a vast number of text in a traditional text editor in most scenarios would be futile. An alternative method of retaining a high definition whilst still being viewable can be achieved through creating an from the rendered text and then reducing the dimensions.

The sample code employs the following steps when converting rendered text to an :

  1. Determine Required Image Dimensions – Determine the dimensions required to fit the rendered text.
  2. Create a new Image and set the background colour – After having determined the required dimensions create a new consisting of those dimensions. Set every pixel in the new to Black.
  3. Draw Rendered Text – The rendered text should be drawn on the new in plain White.
  4. Resize Image – In order to ensure more manageable dimensions resize the with a specified factor.

Alan Turing: 1 Pixel Per Character, 16 Characters, Font Size 4, Zoom 100

Alan Turing: 1 Pixel Per Character, 16 Characters, Font Size 4, Zoom 100

Implementing an Image ASCII Filter

The sample source code implements four methods when implementing an ASCII Filter, the methods are:

  • ASCIIFilter
  • GenerateRandomString
  • RandomStringSort
  • GetColorCharacter

The GenerateRandomString  method, as the name implies, generates a consisting of randomly selected characters. The number of characters contained in the will be determined by the parameter value passed to this method. The following code snippet provides the implementation of the GenerateRandomString method:

private static string GenerateRandomString(int maxSize) 
{
    StringBuilder stringBuilder = new StringBuilder(maxSize); 
    Random randomChar = new Random(); 

char charValue;
for (int k = 0; k < maxSize; k++) { charValue = (char)(Math.Floor(255 * randomChar.NextDouble() * 4));
if (stringBuilder.ToString().IndexOf(charValue) != -1) { charValue = (char)(Math.Floor((byte)charValue * randomChar.NextDouble())); }
if (Char.IsControl(charValue) == false && Char.IsPunctuation(charValue) == false && stringBuilder.ToString().IndexOf(charValue) == -1) { stringBuilder.Append(charValue); randomChar = new Random((int)((byte)charValue * (k + 1) + DateTime.Now.Ticks)); } else { randomChar = new Random((int)((byte)charValue * (k + 1) + DateTime.UtcNow.Ticks)); k -= 1; } }
return stringBuilder.ToString().RandomStringSort(); }

Sir Tim Berners-Lee: 4 Pixels Per Character, 16 Characters, Font Size 6, Zoom 100

Sir Tim Berners-Lee, 4 Pixels Per Character, 16 Characters, Font Size 6, Zoom 100

The RandomStringSort method has been defined as an targeting the . This method provides a means of sorting a in a random manner, in essence shuffling a ’s characters. The definition as follows:

public static string RandomStringSort(this string stringValue) 
{
    char[] charArray = stringValue.ToCharArray(); 

Random randomIndex = new Random((byte)charArray[0]); int iterator = charArray.Length;
while(iterator > 1) { iterator -= 1;
int nextIndex = randomIndex.Next(iterator + 1);
char nextValue = charArray[nextIndex]; charArray[nextIndex] = charArray[iterator]; charArray[iterator] = nextValue; }
return new string(charArray); }

Anders Hejlsberg: 3 Pixels Per Character, 12 Characters, Font Size 5, Zoom 50

Anders Hejlsberg: 3 Pixels Per Character, 12 Characters, Font Size 5, Zoom 50

The sample source code defines the GetColorCharacter method, intended to map pixels to character values. This method has been defined as an targeting the . The definition as follows:

private static string colorCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 

private static string GetColorCharacter(int blue, int green, int red) { string colorChar = String.Empty; int intensity = (blue + green + red) / 3 * (colorCharacters.Length - 1) / 255;
colorChar = colorCharacters.Substring(intensity, 1).ToUpper(); colorChar += colorChar.ToLower();
return colorChar; }

Bjarne Stroustrup: 1 Pixel Per Character, 12 Characters, Font Size 4, Zoom 100

Bjarne Stroustrup: 1 Pixel Per Character, 12 Characters, Font Size 4, Zoom 100

The ASCIIFilter method defined by the sample source code has the task of translating source/input into text based . This method has been defined as an targeting the class. The following code snippet provides the definition:

public static string ASCIIFilter(this Bitmap sourceBitmap, int pixelBlockSize,  
                                                           int colorCount = 0) 
{
    BitmapData sourceData = sourceBitmap.LockBits(new Rectangle (0, 0, 
                            sourceBitmap.Width, sourceBitmap.Height), 
                                              ImageLockMode.ReadOnly, 
                                        PixelFormat.Format32bppArgb); 

byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length); sourceBitmap.UnlockBits(sourceData);
StringBuilder asciiArt = new StringBuilder();
int avgBlue = 0; int avgGreen = 0; int avgRed = 0; int offset = 0;
int rows = sourceBitmap.Height / pixelBlockSize; int columns = sourceBitmap.Width / pixelBlockSize;
if (colorCount > 0) { colorCharacters = GenerateRandomString(colorCount); }
for (int y = 0; y < rows; y++) { for (int x = 0; x < columns; x++) { avgBlue = 0; avgGreen = 0; avgRed = 0;
for (int pY = 0; pY < pixelBlockSize; pY++) { for (int pX = 0; pX < pixelBlockSize; pX++) { offset = y * pixelBlockSize * sourceData.Stride + x * pixelBlockSize * 4;
offset += pY * sourceData.Stride; offset += pX * 4;
avgBlue += pixelBuffer[offset]; avgGreen += pixelBuffer[offset + 1]; avgRed += pixelBuffer[offset + 2]; } }
avgBlue = avgBlue / (pixelBlockSize * pixelBlockSize); avgGreen = avgGreen / (pixelBlockSize * pixelBlockSize); avgRed = avgRed / (pixelBlockSize * pixelBlockSize);
asciiArt.Append(GetColorCharacter(avgBlue, avgGreen, avgRed)); }
asciiArt.Append("\r\n" ); }
return asciiArt.ToString(); }

Linus Torvalds: 1 Pixel Per Character, 8 Characters, Font Size 4, Zoom 80

Linus Torvalds: 1 Pixel Per Character, 8 Characters, Font Size 4, Zoom 80

Implementing Text to Image Functionality

The sample source code implements the GDI+ class when drawing rendered text onto . The sample source code defines the TextToImage method, an extending the . The definition listed as follows:

public static Bitmap TextToImage(this string text, Font font,  
                                                float factor) 
{
    Bitmap textBitmap = new Bitmap(1, 1); 

Graphics graphics = Graphics.FromImage(textBitmap);
int width = (int)Math.Ceiling( graphics.MeasureString(text, font).Width * factor);
int height = (int)Math.Ceiling( graphics.MeasureString(text, font).Height * factor);
graphics.Dispose();
textBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
graphics = Graphics.FromImage(textBitmap); graphics.Clear(Color.Black);
graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
graphics.ScaleTransform(factor, factor); graphics.DrawString(text, font, Brushes.White, new PointF(0, 0));
graphics.Flush(); graphics.Dispose();
return textBitmap; }

Sir Tim Berners-Lee: 1 Pixel Per Character, 32 Characters, Font Size 4, Zoom 100

Sir Tim Berners-Lee, 1 Pixel Per Character, 32 Characters, Font Size 4, Zoom 100

Sample Images

This article features a number of sample images. All featured images have been licensed allowing for reproduction. The following image files feature a sample images:

The following section lists the original image files that were used as source/input images in generating the images found throughout this article.

Alan Turing

Alan Turing

Anders Hejlsberg

Anders Hejlsberg

Bjarne Stroustrup

Bjarne Stroustrup

Linus Torvalds

Linus Torvalds

Tim Berners-Lee

Tim Berners-Lee

Related Articles and Feedback

Feedback and questions are always encouraged. If you know of an alternative implementation or have ideas on a more efficient implementation please share in the comments section.

I’ve published a number of articles related to imaging and images of which you can find URL links here:

Leave a comment




Dewald Esterhuizen

Blog Stats

  • 869,810 hits

Enter your email address to follow and receive notifications of new posts by email.

Join 228 other subscribers

Archives

RSS SoftwareByDefault on MSDN

  • An error has occurred; the feed is probably down. Try again later.