Now Reading
Recreating ANSI Artwork from a screenshot

Recreating ANSI Artwork from a screenshot

2023-02-27 14:20:34

Within the early to mid-90s, I ran a BBS out of my bed room. It wasn’t highly regarded, however I did have numerous time on my arms and I spent numerous that point modding it. Initially, I wrote mods for myself, however ultimately I began releasing them to different folks. As a youngster, this was the primary software program that I wrote that different folks used. This was my contribution to “the scene” and now my solely declare to fame from that point is that I’m a part of the ultimate member listing of ACiD Productions below BBS Modifications.

Throughout this period, I requested certainly one of my customers to create some ANSI artwork for me:

me asking someone to create ansi art

And so they agreed!

user agreeing to create ansi art

11 days later, they uploaded the ANSI artwork to my BBS.

delivering the ansi art

Chances are you’ll be questioning what the cope with these Home windows XP-era screenshots is. Effectively, I used to backup my BBS with a Colorado Tape Backup drive. Right here’s an image of 1 I discovered on the Web:

colorado tape backup

In 2001, I discovered a 250 MB tape that was the final backup of my previous BBS and restored it to my pc. Feeling nostalgic, I logged in to it regionally and took just a few screenshots. Once I first logged-in to it, it displayed the ANSI artwork that my person had made for me:

screenshot of ansi art

It scrolled by fairly quick, so I took a collection of screenshots of it after which used Photoshop to mix them. I made just a little webpage for it so I might have a look at it from time to time and went on with my life.

Shortly after this, my laborious drive died. I feel it was an IBM Deathstar. I do not know what occurred to the 250 MB tape, however I feel it’s secure to say that even when I do discover it, it most likely isn’t going to work.

Fortunately, because of the ability of the online, I nonetheless have these screenshots. Unluckily, I by no means truly uploaded the unique ANSI file, so I don’t have that. From time to time I’ll seek for it on the Web, however I’ve concluded that it by no means made it into an artpack and thus I had the one copy of it, till I didn’t.

It’s not essentially the most stunning ANSI artwork, however it’s one thing that somebody made for me and I’ve all the time been just a little bummed that I can’t have a look at it in one of many many ANSI viewers (or DOS emulators) that exist right now.

Let’s repair that!

The very first thing to grasp about ANSI artwork is that it combines two issues: characters from the IBM Code page 437 and ANSI escape sequences that do issues like change colours and transfer the cursor round.

I discovered this extraordinarily handy page that exhibits all of the totally different characters in Codepage 437.

Codepage 437

Characters #0 – #31 are management characters, and #127 is DEL, so we are able to ignore these. The remainder of them are utilized in ANSI artwork, though the shade blocks and half blocks are predominately used.

The best way that you’d sort certainly one of these bizarre characters on an IBM PC is that you might maintain down ALT and sort the ASCII code in your numpad. Once you launched ALT, the character would present up. However most artists would use a program like TheDraw or ACiDDraw to design their artwork.

Talking of TheDraw, let’s check out the colour choice display screen from it:

TheDraw color selection screen

There are sixteen foreground colours and eight background colours. Ignore 16-31, I captured this screenshot mid-blink.

Altering the foreground and background colours and writing the characters from Codepage 437 produces the ANSI artwork that we all know right now:

LU-Holiday.ans by Luciano Ayres from Blocktronics Blockfury
LU-Vacation.ans by Luciano Ayres from Blocktronics Blockfury

To precisely show ANSI artwork, it’s necessary to make use of an acceptable IBM PC font, like this one. It will be certain that your artwork seems the way in which that it was meant by the artist.

The technique for conversion that I got here up with was: cut up the screenshot into particular person characters. For every character, generate each doable permutation of background colour, foreground colour, and character from Codepage 437 and evaluate it to the character within the screenshot, then choose the one that’s the most related.

There’s most likely numerous alternative ways to do that, however I figured the simplest can be to make a webpage and use the Canvas API.

As a check case, I used a program referred to as ansilove to take an present .ANS file and generate a PNG of it. It even got here with an instance ANS file:

example input

The picture is 640×464. Assuming that we now have a 8×16 font, because of this it comprises 80×29 characters.

We create a canvas and cargo the picture into it:

const canvas = doc.getElementById("canvas");
const ctx = canvas.getContext("2nd", { willReadFrequently: true });

const img = new Picture();
img.addEventListener("load", (e) => {
    ctx.drawImage(img, 0, 0);
});

img.src="https://bert.org/2023/02/27/recreating-ansi-art-from-a-screenshot/enter.png";

Subsequent, we now have to have a listing of the foreground and background colours. I discovered that iTerm2 has a color scheme for CGA that appears correct, so I loaded it into iTerm after which extracted the hex codes from it, double-checking towards the TheDraw colour picker. This gave me two lists:

var fgColors = [
    "000000",
    "aa0000",
    "00aa00",
    "aa5500",
    "0000aa",
    "aa00aa",
    "00aaaa",
    "aaaaaa",
    "555555",
    "ff5555",
    "55ff55",
    "ffff55",
    "5555ff",
    "ff55ff",
    "55ffff",
    "feffff"
];

var bgColors = [
    "000000",
    "aa0000",
    "00aa00",
    "aa5500",
    "0000aa",
    "aa00aa",
    "00aaaa",
    "aaaaaa",
];

Now we’d like all of the characters within the Codepage 437 to loop over. Fortunately, Unicode supplies a text file that translates all CP437 codes to their UTF-8 equivalents.

I took this textual content file, eliminated the management characters and DEL, and created an array of their UTF-8 counterparts.

Then I created one other canvas, looped over every background colour, foreground colour, and character, and wrote them to the canvas utilizing the IBM PC font.

const char1canvas = doc.getElementById("char1");
const char1ctx = char1canvas.getContext("2nd", { willReadFrequently: true });

char1ctx.fillStyle = "#000000";
char1ctx.fillRect(0,0,8,16);

var imgData = [];

for (var i = 0; i < bgColors.size; i++) {
    for (var j = 0; j < fgColors.size; j++) {

        if (bgColors[i] == fgColors[j]) {
            proceed;
        }

        for (var okay = 0; okay < chars.size; okay++) {

            char2ctx.fillStyle = "#" + bgColors[i];
            char2ctx.fillRect(0,0,8,16);

            char2ctx.fillStyle = "#" + fgColors[j];

            char2ctx.font = "16px xx437";
            char2ctx.fillText(chars[k], 0, 12);

            if (!imgData[i]) {
                imgData[i] = [];
            }

            if (!imgData[i][j]) {
                imgData[i][j] = [];
            }

            imgData[i][j][k] = char2ctx.getImageData(0,0,8,16);

        }
    }
}

For some purpose I needed to offset the fillText by 12 pixels to get it to accurately write it to the canvas as I might anticipate. I do not know why, however CSS has by no means been a power of mine. After every time that we write the character, we retailer the picture information of the end in a lookup desk, by background colour, foreground colour, and character.

Subsequent, we loop over every character part of the unique picture’s canvas and extract the picture information of this part:

for (var y = 0; y <= 24; y++) {
    for (var x = 0; x <= 79; x++) {
        char1ctx.drawImage(canvas, x*8, y*16, 8, 16, 0, 0, 8, 16);
        var imgChar1 = char1ctx.getImageData(0,0,8,16);
    }
}

I discovered a library referred to as pixelmatch that permits you to evaluate two units of imagedata. It returns the variety of mismatched pixels and if you would like it, a diff of the 2.

var outcome = pixelmatch(imgChar1.information, imgChar2.information, null, 8, 16, {
    threshold: 0.1,
});

So then we are able to loop over each permutation of background colour, foreground colour, and character and evaluate it to the picture’s character and choose the one which has the bottom variety of mismatched pixels – ideally 0.

Subsequent we create an output canvas and utilizing the recognized combos for every character, write again the identical ANSI artwork to that canvas:

example comparison

The picture on the left is the enter picture and the picture on the suitable is the generated ANSI artwork. It seems fairly good! The guts in the course of ANSI and LOVE has been transformed to an oblong bullet – presumably as a result of I excluded the management characters and the guts occurred to be in them.

However that is solely helpful if we are able to generate the ANSI artwork file. Let’s return to the ANSI escape codes:

33[0m resets every part again to regular.

33[31m will set the foreground to crimson. The doable colours are 30-37.

33[44m will set the background to blue. The doable colours are 40-47.

To be able to get the intense foreground colours, we merely should set the daring attribute, which we are able to use 33[1m to set. We simply have to recollect to make use of 33[0m to reset it after we’re performed.

So for every character, we simply all the time reset it, set the background colour, set the foreground colour, and optionally set daring. Then we write the character.

But when we simply write this to a string, it will give us a file that comprises ANSI escape codes however UTF-8 characters – we have to convert it to a DOS format.

There’s a helpful bundle referred to as iconv-lite that may encode totally different character encodings, so we are able to do one thing like:

iconv.encode(ansiTxt, 'cp437');

I reverse engineered how this CP437 converter works to output the file and I believed the way in which that it downloaded it was fairly intelligent, so I integrated it as effectively:

var c = doc.createElement("a");
c.href = "https://bert.org/2023/02/27/recreating-ansi-art-from-a-screenshot/information:textual content/plain;base64," + iconv.encode(ansiTxt, 'cp437').toString('base64');
c.obtain = 'output.ans';
doc.physique.appendChild(c);
c.click on();
doc.physique.removeChild(c);

This robotically began downloading the ANS file with the proper encodings.

Lastly, it was time to ship my screenshot via it!

The screenshots that I had taken in 2001 had been one display screen at a time, or 80×25 characters. This was the primary one:

See Also

first screenshot

The scale had been 560×300. However that signifies that every character was 7×12 as a substitute of 8×16. And that is after I remembered that after I took the screenshots, I had taken them in a DOS window in Home windows, which used no matter font that Home windows used for DOS prompts. What if we simply… resized the picture so that every character was 8×16? I resized the picture to 640×400, ensuring to make use of “Nearest Neighbor” to attempt to maintain the pixels right, and it sort-of labored:

first comparison

The textual content detection is laughably dangerous nevertheless it appeared to get the stable and half blocks – it was actually largely battling the shade blocks. There are solely 3 shade blocks, 4 in the event you rely the utterly stable one.

I wrote an ANS file to output simply three totally different shade blocks and exported a picture of it utilizing ansilove and it was clear – no matter font Home windows was utilizing for the DOS immediate had a very totally different concept of what a shade block regarded like than what the IBM PC font did.

Then I had an concept – what if I simply taught my program what the bizarre Home windows shade blocks regarded like?

I zoomed in on the shade blocks within the screenshot and extracted certainly one of every of the different sorts. From left to proper, these signify gentle, medium, and heavy:

shade blocks

I loaded every one right into a canvas and looped over every pixel. Once I encountered crimson, I saved this as a 1 in a multi-dimensional array, and after I didn’t discover crimson, I saved this as 0. Utilizing this mapping, I once more generated each permutation of background colour, foreground colour, and now these new shade blocks and saved their picture information in a lookup desk. When the picture character matched it, I chosen the suitable shade block character.

And that labored! Right here you possibly can see the distinction between the screenshot shade blocks and the precise shade blocks is kind of stark:

second comparison

Apparently I hadn’t seen what this ANSI artwork accurately regarded like because the 90s. The textual content was nonetheless unsuitable, however I figured I might simply recreate it from the screenshots. So I began processing the opposite screenshots, which was working nice till I obtained to this one:

third comparison

The shading on the flames was utterly gone, which meant that it extra carefully recognized the character with the stable block than the shade block. That’s bizarre. I zoomed in nearer to the block on the unique screenshot. Right here it’s within the center subsequent to 2 of the unique shade blocks that I mapped.

new shade block

It appeared to be similar to the one of many left, however flipped. I’m unsure the way it obtained flipped, perhaps one thing to do with the display screen seize or resizing course of, however I used the identical process to map it, storing a 1 for every crimson pixel and producing all of the permutations, lastly storing it within the lookup desk, and I ran it once more. And.. it obtained the identical outcome.

I used to be beginning to really feel like I used to be going loopy after I remembered the TheDraw colour choice display screen:

TheDraw color selection screen

There are two foreground colours, brown and yellow. Yellow is the daring model of brown. However there is just one background colour, which occurs to be brown. It dawned on me that what I used to be taking a look at was not a crimson shade block on yellow, however a yellow shade block on crimson. It actually makes you respect the constraints of the 90s ANSI artist. After retraining it to search for yellow pixels, it accurately recognized the block.

The textual content was nonetheless a rubbish fireplace, however after I mixed the ANSI recordsdata in Pablodraw, I simply retyped it.

Right here’s what it regarded like after I cleaned it up:

final output

And right here is the ANSI file: mp-h&c.ans.

You’ll be able to view in one thing like nfov or Pablodraw, and even iconv -f 437 mp-h&c.ans in the event you’ve obtained the proper colors and font arrange in your terminal.

I don’t intend on dropping it once more.

Source Link

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top