Drawing PPM images on the Tildagon in MicroPython


The Tildagon has 2MB of RAM. That's not enough to do... well, most things you'd want to do with a computer! There's not much processing power, so running complex image decoding algorithms might be a bit beyond it.

Is there a simple image format which can be parsed and displayed? Yes! The ancient Portable PixMap (PPM) format.

Various circuit boards showing images.

The standard is beautiful in its simplicity. Here's the header:

 PPMP6
# Created by GIMP version 2.10.38 PNM plug-in
120 120
255
���t�{...

The P6 identifies it as a PPM file. The # is a comment. 120 120 says that the image's dimensions are 120 pixels horizontal, and 120 vertical. 255 is the maximum value for each colour.

Then comes a big blob of binary data. Each byte is a value from 0 to 255.

To find the Red, Green, and Blue values of the first pixel, read the first 3 bytes. The next 3 bytes are the RGB of the next pixel. And so on.

There's no compression. Just pure pixel values.

Because of the low memory limits of the Tildagon, I found it impossible to load the entire file into memory and then paint it on the screen. Instead, I read it in chunks.

First, load the file as a read-only binary. Then skip the header and get straight to the pixel data.

Python 3 Python 3#   Open a 120px x 120px Raw / Binary PPM file
with open('/apps/ppm/chrome120.ppm', 'rb') as ppm_file:
    print("Skipping Header")
    # Skip the header
    header = b''
    while True:
        line = ppm_file.readline()
        header += line
        if header.endswith(b'\n255\n'):
            break

Images on the Tildagon are drawn from the top left, which has co-ordinates -120,-120

Python 3 Python 3    #   Start at the top left
    x, y = -120, -120

Next, read in a line of pixels. The image is 120px wide, each pixel has 3 values, so that's 360 bytes. Grab the pixel values and draw them to screen:

Python 3 Python 3    while True:
        #   Read in 1 line at a time (3 bytes * 120px)
        chunk = ppm_file.read(360)
        if not chunk:
            break  # End of file
        #   Read the RGB, convert to float
        for i in range(0, len(chunk), 3):
            r = chunk[i]    /255
            g = chunk[i + 1]/255
            b = chunk[i + 2]/255
            #   Draw the pixel in a 2x2 square
            ctx.rgb(r, g, b).rectangle(x, y, 2, 2).fill()

The screen's resolution is 240x240, so each pixel from the 120x120 image needs to be drawn as 2x2 rectangle.

Once that's done, move to the next square to be drawn. Once a full line has been drawn, move down to drawing the next line.

Python 3 Python 3            #   Move the the next square
            x += 2
            #   If a complete line has been drawn
            #   Move down a line (2px) and reset the x coordinate
            if x >= 120:
                x = -120
                y += 2
        #   Clear the chunk from memory
        del chunk
        #   Perform garbage collection
        gc.collect()

A bit of manual garbage collection doesn't hurt! And then a bit more for good luck!

Python 3 Python 3del(ppm_file)
#   Final collection
print("Collecting")
gc.collect()

Share this post on…

  • Mastodon
  • Facebook
  • LinkedIn
  • BlueSky
  • Threads
  • Reddit
  • HackerNews
  • Lobsters
  • WhatsApp
  • Telegram

2 thoughts on “Drawing PPM images on the Tildagon in MicroPython”

What are your reckons?

All comments are moderated and may not be published immediately. Your email address will not be published.

Allowed HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <p> <pre> <br> <img src="" alt="" title="" srcset="">