In the olde days™ of CRT monitors, good computer hygiene meant installing a screensaver to prevent image burn-in. Early operating systems didn’t even provide this…such programs had to be purchased! We’ve replicated the nostalgic essence of a few popular classics…only this time, the code’s all free.
With the PicoDVI library installed as previously described, you’ll find these in Arduino’s Examples menu…
File→Examples→PicoDVI - Adafruit Fork→screensavers
If using DVI PiCowbell, use this output line in your Arduino code:
DVIGFX8 display(DVI_RES_320x240p60, true, adafruit_dvibell_cfg);
For the DVI Sock, use this:
DVIGFX8 display(DVI_RES_320x240p60, true, pico_sock_cfg);
For the 1-bit DVi Logo Bounce on the DVI PiCowbell, use this:
DVIGFX1 display(DVI_RES_640x480p60, true, adafruit_dvibell_cfg);
For the DVI Sock, use this:
DVIGFX1 display(DVI_RES_640x480p60, true, pico_sock_cfg);
If you’d prefer to just mess around with the demos, we also have pre-compiled UF2 files for the Feather RP2040 DVI boards at 400x240 resolution and for the Pico with the DVI PiCowbell at 320x240 for each example below.
The examples are all written for the Feather RP2040 DVI using 320x240 pixels, but easily adapted for other RP2040 boards, and for 400x240 pixel resolution with a little extra work. See the earlier 8bit_double_buffer example for an explanation.
We won’t be showing the code for these examples “inline” here because the embedded graphics data makes them huge…you’d be scrolling for weeks. There are comments in the code and some additional notes at the end of this page.
The classic-est of the classics! This is what made folks want to buy a screensaver.
Not emulated, just an approximation of the original.
Also not a classic PC screensaver but it had to be done.
Inspired by the trash-heap screens in Max Headroom: 20 Minutes into the Future (1985). Been noticing more working flatscreen TVs and monitors left out by the curb lately. Free pixels! What could we make of them? This was one such idea.
Here’s a version that’s just the rotating cube background:
The Amiga demo that started it all.
(This UF2 file is compiled at 320x240 for aspect ratio authenticity.)
Okay, it’s not a classic PC screensaver, but had to include this as the code’s the simplest of the lot, and also it shows crisp high-resolution (640x480 or 800x480) monochrome animation. This example is too high a resolution to run on the Pico.
Hi-res monochrome would be ideal for making retro vector-style games like Asteroids or Battlezone.
Here’s a ready-made UF2 file for Feather RP2040 DVI:
Notes
All of these examples do something similar: graphics “sprites” are embedded as tables in the code, and then bitmap-drawing functions in Adafruit GFX “blit” these to the screen. Double-buffered animation prevents flicker; everything appears buttery smooth.
The “TV Host” example is distinct in that it also lays down some vector-style graphics first, then overlays large sprites atop this. The backdrop is doing some actual 3D perspective but only in the barest sense.
None of these examples is especially “crafty” — the approach is brute-force, using large sprite tables as a tradeoff to keep the code simpler to read. The demos are nonetheless impressive for a modestly-priced microcontroller board. If slapdash code can do this, then advanced programmers will likely be creating some stunning work!
The sprite tables were generated using a mix of ImageMagick and/or Python. We don’t have a well-written universal tool for this because sometimes that’s just the nature of Python…quickly writing a sloppy one-off program for a special task.
ImageMagick was mostly used for the 1-bit sprite masks, for example:
magick mask.png -define h:format=gray -depth 1 mask.h
The output needs a little editing (see the sprite.h/sprites.h header file that’s a part of each example), but ImageMagick does most of the work.
Here’s a little Python code (not CircuitPython, but “desktop” Python) that extracts the color palette from an 8-bit PNG or GIF and converts it to the “RGB565” color format used by PicoDVI and Adafruit GFX, then follows up with a table of sprite pixel values:
from PIL import Image, ImagePalette with Image.open("dragon.png") as img: pal = img.getpalette() print("const uint16_t palette[] = {") for i in range(len(pal) // 3): r = pal[i * 3 + 0] >> 3 g = pal[i * 3 + 1] >> 2 b = pal[i * 3 + 2] >> 3 rgb = (r << 11) | (g << 5) | b; print("0x%04X, " % (rgb), end="") print("};\n") print("const uint8_t sprite[] = {") for y in range(img.size[1]): for x in range(img.size[0]): p = img.getpixel((x, y)) print("0x%02X, " % (p), end="") print("};\n")
The output of this code is then redirected to a file, such as:
python convert.py > sprite.h
That file’s contents will be a sloppy mess! To keep this simple, rather than formatting nicely in the Python code, the output is run through the clang-format tool to clean it up:
clang-format -i sprite.h
The mask data (for programs that need it) was then appended.
Sometimes ImageMagick can be used for the sprite conversion as well, but it tends to be headstrong about optimizing and reordering colors. There’s probably settings to disable those behaviors, but doing the conversion in Python was simple enough.
The boing example is a little different because the code was adapted from an earlier project for PyPortal. That one uses “packed” data, two pixels per byte.
Text editor powered by tinymce.