Context
In my spare time, I've been working on a simple text-based game for my 4 year old to play. He found my 10 year old Linux laptop and has been having a blast with the terminal.
When I started writing the game, I picked GoLang as the language and researched a variety of TUI (text user interface) libraries. After some quick prototypes, I settled with tview
for the solid primitives and ease of keyboard interaction.
Objective
During development, I realized it could use more feedback when getting an answer right or wrong. The way I wanted to solve that was by adding simple animations (e.g. happy vs sad cat). Nothing in tview
(or the other libraries) had anything readily available.
The following is the process I went through to build a new tview
object that supported displaying animated gifs:
- Parse the image frames and timing information
- Display the frames on the screens
- Scale for multiple animations
Step 1. Parsing Gifs
GoLang has a simple built in Gif library image/gif
. After decoding the file, you are left with:
- Set of frames as
image.Paletted
- Set of delays between frames as
int
Step 2. Image to Text
I stumbled across pixelview
which converts images into formatted text for tview
by using colored half-block unicode characters (â–€
).
It also accepts the image.Image
interface (which is what image.Paletted
above uses).
Now I can display each of those Gif frames into a tview
box.
Step 3. Optimizing
The above prototype wouldn't really work. Besides the single-threaded iteratation through a single gif (which prevents us from adding multiple gifs), there are performance issues to take into account. Depending on the speed and the number of frames, you would see CPU load during quick rendering and constant conversion of image objects. Additionally, the use of TextView
and the added features (scrolling and highlighting) slows down rendering.
To reduce that impact and support multiple images, I switched to my own Box
class and made some important changes.
First, I switched the animation to be on-demand. That way, whenever the view needed to be re-drawn it would calculate the current frame it should be on and render that.
I did that by recording the start time of the view and iterating on each frame delay until we knew where we were.
Second, I removed our usage of TextView
and made my own stripped down version of the TextView
draw function.
Finally, I triggered a global re-draw on a periodic basis. That way all Gifs would be animated consistently (although not as smooth).
Final Product
In the end, I created a library that allowed me to add basic animated gifs into my son's game using a single interface.
Here is the library as well as the documentation.
And this is a simple example (dancing banana not included):
Top comments (0)