Tuesday, January 30, 2018

Displaying 256 colour images and 16 colour sprites

The material in this post is from Daniel, who has been working hard on tools for preparing and displaying 256-colour images on the MEGA65, using the full-colour text mode, where each pixel is represented by a byte, and where characters can be re-used to save space when drawing a low-entropy image. But over to Daniel...

As you all might know, I've been working on a few demos getting a few things tested and working...  At first, I needed a nice sprite pointer and there were some problems I couldn't solve so I handed the tests over to Paul and he sent me back an awesome demo.  After implementing my pointer, I was quite inspired and wanted to try to improve on what he had started.  So I upped-the-ante and released a three sprite animation with a few more frames.  I had to generate all of the data myself, including extracting each frame of the animation.  I wrote a GUI tool to handle the data conversion because I couldn't find Paul's for looking.  I had tried to do a five sprite version but I couldn't figure out the memory utilisation at the time but I'll will be revisiting it in the future.

Next, I wanted to get some 320x200x256 images on the screen.  Quite some time ago (gosh, is it two years already?) I built a tool to convert images into a format that I could use with the 16bit tile feature on the Mega65.  I dragged it out and tried to get it working.  I found out that it was well out-of-date and didn't work.  In fact, when I built the tool, the feature was still in the design stage and it was never actually used to produce an image on the M65 (at the time the C64GS).  

After some consultation with Paul I started to rework the tool.  I also had to build a loader program for the M65 side.  After quite a bit of trial-and-error and annoying Paul with some silly questions, I finally got an image on the screen.  I didn't actually release that demo because it was too simple and seemed to have a few problems.  And...  The image was a very basic outline of something that I might want to reuse later...

I needed to test the colour use so I made the very pretty spectrum example.  I just used a gradient brush in GIMP to paint out the gradient and converted it to 256 colours using dithering which I don't normally do because I thought it might be nice for this example.  However, when I tried to convert it, my tool came back with some 800 tiles.  That was impossible for my loader to handle at the time because it required that the tiles be stored in a contiguous block.  So, I reworked the image, copying sections such that it is actually comprised of only two unique sections (one of them repeated three times).  I was worried it would look terrible but I think its okay.

[Ed: each 8x8 tile in this mode requires 64 bytes, one byte for each pixel, so 800 tiles requires 51,200 bytes, so some would need to go under IO or under ROMs to fit in the C64 memory map].

So the general principle was now sound but I needed to be sure that the tile reuse by foreground colour replacement was actually working.  I came up with the idea of making a little Andy Warhol tribute using the breadbox image which I had made some time ago.  For something that seems so simple, I had to struggle with GIMP to get it to do what I wanted.  I had to munge the images a few times before I got what I was looking for.  In the end, I think its rather pretty and the colour substitution was working.  I did have a problem with the flip bits initially but I checked some information Paul gave me and corrected the problem in my tool.

Now onto the big stuff...  In staring the process I had wanted to put a 1000 tile image on the screen.  That's a unique tile for every location, a real 320x200x256 image and I wanted to have sprites, too.  I had already made the image I wanted to use (a very long process in Photoshop, let me tell you!  I recoloured the whole image - every single detail and that was just the start!).  It was actually for a little animation demo I did a long time ago...

I wanted to replicate some of that demo but my tool and loader were not up to the task in any shape or form.  Firstly, I was having to modify my loader for each individual image and secondly, my tool could only output to a contiguous block of RAM.  If you look at the memory map for the M65, you'll see that there just isn't a contiguous 64kB block of RAM that you can use when you need screen RAM and so on, too.

When I was asking Paul about it he quite casually said, "just don't make it contiguous and use whatever memory you can."  Hmm...  I don't know if he knew the complexity of what he was suggesting but I knew what I had to do.  I had to rework my tool to and image format to allow for segment based mapping.  I also wanted it to be able to be processed by a generic loader because I was really over all of the complicated calculations I had to do to get the data loading loops to work and it just wasn't something that could be sold as a solution.

I built a test application that would allow me to specify free, reserved and system segments in the memory range that I could use.  It actually came together rather quickly.  Next, I planned how to change my data format and make it loadable by a generic loader.  I then incorporated my changes into my conversion tool.  That wasn't quite so easy...  I found some issues with the GUI messaging that were preventing me from knowing when the user had actually done the mapping, of all things that could go wrong.  Other than a few minor bugs, the tool side was completed more quickly than I had anticipated.  

Next I had to write the loader.  Oh boy.  My loaders up to that point where horribly hacked so I pretty much had to start from scratch.  That was okay because the data format had changed quite significantly.  After a few calculation problems, complete and utter system failures (I'm sure it was my fault), copy-and-paste bugs and a weird, you have to do this, in this order thing, I got the loader working.  It took me a few more hours to do than I'd planned for though but it didn't stop me being ecstatic at my success!

While I was waiting for something, I'd written the small sprite test that I wanted to use in the final demo.  I quickly incorporated it into my image program (and bothered Paul with silly questions again which I figured out before he got back to me) and voila!  A true 320x200x256 image with 16 colour sprites!  1000 unique tiles and 256 colours!

There is still a bug though...  In the middle of the screen, offset a little to the right, a tile is incorrect.  I have no idea why.  I'll need to consult Paul.  Also, as the sprite approaches the bottom border, it gets a strange warping effect on it.  Something Paul will need to fix.

My tool allows for arbitrary sizes of images (up to 255x255 cells).  You may be able to guess what's comming next...

I'll release the updated version of my tool and loader in a day or two.  I promised someone I would be available tomorrow and have a day away from coding.

ARGH!  I was supposed to sleep but I just had to add the music I wanted, too...  Please enjoy "Beachparty" by Zyron.  It sounds a little off pitch, though.  My humble apologies!

Edit:  I got up first thing in the morning to look at why that tile was broken.  I finally figured it out and with some direction from Paul was able to fix it. [Ed: The sprite pointer list that normally lives directly after the screen memory was in the middle of one of the tiles. Fortunately, this list can be moved around easily on the MEGA65 by writing to $D06C/D/E, so that was easily solved.]

 And the following video shows all three loading and being run on the MEGA65.  Apologies for the effectively silent audio level in the video, as I don't have my decent camera here, and the microphone on my phone is half dead.


  1. The images look like they are actually quite a bit more stunning that I realise, since I only have the Nexys4DDR board running with 12bit colour. Having the full 24bit colour looks like it would be much, much better.

    1. Also try experimenting with the colour quantization algorithm, especially Neuquant. I had quite a few situations where the result in GIMP was, well, acceptable, but when switching to the command line and redoing the quantization with Neuquant there was a enormous difference.

      The algorithm in Gimp is good if you do dithering, but produces colour bands if you do gradients. I see colour bands in the shadow of the legs of the lady and in the blue ocean. Neuqant may do a better job there.

  2. Amazing ! It is absolutely amazing to see, what the MEGA65 is capable of !!!
    Thanks !!!

    1. Yes, very impressive demonstration of the graphical capabilities. Shows that going "beyond C64" is definately worthwhile, despite available high performance PCs.

  3. Reading the story a second time, I think the most important technical demonstration here is that everything is done in text mode. Therefore, this is not just a demnonstration of capapabilities to display static pictures, but also how good the graphics can look in real games, i.e. platform games.

    The ability to do 800 different tiles is really great and will have a large effect on the graphical versatility in games.

    So for the guess what is coming next: A large area scrolling graphical demo.

    1. Yes, this is exactly the point. Indeed, you can have upto 2048 tiles, if you don't use any of the RAM for anything else, but certainly several hundred to a thousand or so tiles is totally possible, and does indeed have great potential for games and the like. Also for mixed text/graphics programs, like a web browser, where the text can be reusing the character cells as normal (they can still be mono), and then the higher number character IDs point to graphics blocks, allowing mixed text + 256 colour graphics.

      As for what is coming next, even I don't know. We will have to wait and see what Daniel cooks up, but I suspect you might be on the right track.


  4. I've posted a version using the NeuQuant algorithm. It is indeed better but it has shown a problem with my tool and the limitation of the mode I'm currently using. I'll see what I can do...