NEWS, EDITORIALS, REFERENCE
PETSCII Art Renderer
I'm back from March Break, and I got a lot of great coding done during the week. So I'm a bit behind in writing about my progress here. I've got a few good things coming up to talk about.
Back in October 2016 I wrote an article about Why PETSCII Anyway? In that article I talked about some of the great artwork that can be created by PETSCII text. But ever since I've been wondering to myself if I might be able to find a Graphics-to-PETSCII-art renderer that would produce a PETSCII text file from a graphic.
I found a few projects1, the most detailed and interesting is Playscii, by JP LeBreton.2 He has really put a lot of work into the project and I encourage you to check it out. It is a full drawing program with support for multiple loadable character sets from a variety of classic computers. It happens to support the ability to load in an image, which it will then render to the closest proximity with the currently loaded character set. It's really cool.
However, there are a few problems that make the project not entirely suitable for what I'm looking for. The first problem is that it isn't really geared specifically for the C64. For example, it can easily produce images that do not match the 40x25 character dimensions of the C64. And by default it doesn't, thus giving a mistaken impression of what might be possible on a C64. Its C64 character set is composed of C64 characters, but it includes characters that come from both the lower/upper set and the upper/graphics set. Yes, those are indeed all C64 characters, but on a real C64 they cannot all be displayed at the same time.
A similar issue arose with colors. Playscii allows you to load in a custom palette, and includes the C64's palette. However, when it renders, it freely chooses a foreground and background color for each character cell. This, unfortunately, is also not possible on a C64. In the standard text mode, with access to one of the complete 256 character sets (128 characters plus their reverse), only a single background color can be specified that all cells must share. Then, each cell may choose one color of 16 as the foreground. In other words, a C64 is much more limited than what Playscii allows you to render.
And a final problem is that Playscii doesn't have any support for exporting the data as a stream of PETSCII characters, or even better, ScreenCodes. Doing so would be pretty tricky, because you'd have to include all the characters in the set, and they'd have to be in the correct order, which, in the character set used by Playscii they are not.
A Zero Day Solution
I'm a web programmer by day. And I've done plenty of work with the HTML 5 Canvas tag. For one job I even had to write (or port and modify) a greyscale and dithering algorithm to the canvas tag to prepare the image data for sending to a very simple black and white printer. It got me to thinking, how hard could it be to write a converter myself? So, I started this morning and by 5 in the afternoon, not only do I have a converter, but I even wrote a short BASIC program to render the files and have tested it in VICE. The following is a description of how it works and a few samples of what it looks like.
The first thing I did was find a some images of the real C64 character sets. These are 32 characters across, and 8 rows long. The first four rows are regular characters the last four rows are reversed characters. The first set is the uppercase/lowercase, and the second is the uppercase/graphics set.
Then I started by cropping out the characters and saving them as individual images. It would be most convenient if I could do this with a macro of some kind, but I got into a rhythm and started doing them by hand. Before I got too far, I wanted to test it out so I stopped after doing just the first 32 characters of the lower/upper set.
Next, I wrote a short HTML file that displays an image, pre-sized to 320x200 on the left, and a canvas tag 320x200 to the right of the original. Next I render the image to the canvas, and then run the greyscale and dither routines on the canvas to get an image made of truly just two colors, black and white. The resultant dithered image gets rendered to the canvas and also returns an array of numbers, either 0 for black or 255 for white. The full image has 64000 pixels, so the source array has 64000 numbers, each either 0 or 255.
Then I load each of the character images into its own tiny 8x8 canvas below. I also perform the greyscale trick on each of the individual characters and capture the 8x8, or 64 byte arrays of 0 or 255 that are returned from them. Each of these 64 byte arrays is saved in a big array for the complete character set, where index 0 is equal to the C64's ScreenCode 0, and so on for as many characters as I'd cropped out.
After I got it working, I decided to crop out more characters and I got up to 192. Or 6 of the 8 rows. The screenshots presented below were rendered with only the first 192 of 256 characters taken into account. The results will be a tiny bit better when I've done all the characters, and perhaps a bit better still when I add support for the uppercase/graphics character set. Here's what it looks like after greyscale and dithering the image. (Note: The character cells below the images are in the process of rendering when I captured the screenshot, that's why you don't see all 192 of them.)
Next comes a bit of a trick. The bytes in the source buffer are not ordered the way they are ordered on a C64. The bytes are ordered linearly from left to right, all the way across the screen for 320 pixels. And then the 321st byte is in the second raster line down all the way at the left side of the screen again. Using a bit of math I extract a buffer that is 64 bytes long that covers the area of one C64 character cell. And then I pass this cell to a function called findMatch. Find match loops over all the byte arrays for each of the characters, and compares the 64 bytes of the character with the 64 bytes in the cell of the dithered image and whichever character has the highest number of matching pixels either on or off, the index of that character is returned.
Then, again using a bit of math, I render that character's little 8x8 canvas overtop of the dithered image to get a looksee at how it will appear on the C64. And then, this extract-a-cell-and-find-a-matching-character procedure is done in a loop for all 1000 character locations in the image. Each time a character is matched, I also push that character index, which is in fact a C64 ScreenCode, onto an array of screencodes.
The result of redrawing the characters overtop of the dithered image looks like this:
That's pretty damn cool! It took so little time to make that work. I'm really pleased. Next of course is using some HTML5 tricks: instead of a normal array I use Uint8Array to hold the numbers, then use a Blob and some other hacks to convert the array into a stream of binary and save it as a file. That's what the little file name and save button do.
I converted a handful of images and saved them as binary files containing exactly 1000 bytes of C64 ScreenCodes. Then I used DiskImagery64 to stick them in a .D64 disk image and mounted the image in VICE. Now, I don't normally use VICE, but hey, I was away from home and I really wanted to see what I could do with these rendered files. So I wrote a quick little BASIC program that asks for a file name, then opens the file for read. It reads one byte at a time and pokes the values into screen memory. Starting at 1024. After closing the file the final line goto's itself so that the Ready. prompt does not reappear until you press STOP. And, here's what it looks like:
Mr. Worf and a Race Car
Commodore Logo and No Apple Logo
Welcome To... World of Commodore
I'm really really pleased with the results, especially the fact that they can be loaded up and displayed on a real C64. And I'm surprised by how easy it was to put together. This is the result of just a few hours of work. Now, before you think, well they are kinda jaggy, let me point a few things out. It is really hard to draw something like a Commodore logo in PETSCII characters by hand. Trust me, I've tried. And what I discovered is that I'm either not an artist, or I have no patience for that kind of work.
On the other hand, look at the Commodore Logo above, as produced by this "renderer." It really is a good facsimile. I am especially pleased by the appearence of those captial "J"'s along the outer upper curve of the "C". Or, even more subtly the commas on the inside lower curve of the "C". Those characters find themselves there essentially as anti-aliasing. That's really cool.
There might be ways to improve the rendering by changing the way the findMatch function works. Also, it might be possible to add color support at some point. I'm also not showing in the screenshots any examples of changing the global two colors that it's currently using. I'd also be very interested to see how it renders with the upper/graphics character set.
I won't go into the details now, but I have some thoughts about how I can really use the ability to render to PETSCII art. The hint is in those last two screenshots: "Welcome to World of Commodore."
Get it on GitHub
The source code, original character set images, cropped upper/lower character set individual
images, as well as a handful of sample images I tested are available on GitHub.
You will also find in the GitHub respository a .D64 file which can be copied to a C64 or opened in VICE. It contains several pre-rendered .pet files, and the basic program petrender. The repository also has some very simple documentation on how to use the program. If you have any issues, questions or upgrade suggestions, leave a comment below.
Last Minute Update
I was so excited that I decided to finish cropping out the last 64 characters, and then I played around with a handful of logo designs and other images pulled from the web to see what I could render that would have a really nice result. Here are three that I think shine.
Another project I found was just called PETSCII.
It doesn't render images to PETSCII, but it puts an image in the background and lets you
freehand draw with PETSCII characters.
- Playscii was actually preceded by an earlier project named Edscii, also by LeBreton. It was this earlier project that I found first. When I wrote to him with some suggestions for improvement, he pointed out that he'd already completely redone the whole project as Playscii. [↩]