Framebuffer

In the Drawing lesson, we learned how to send commands to the device to draw shapes and text on a display. While these drawing primitives work well for showing data that fits those requirements, they may not be sufficient for more complex rendering requirements. For example, you might want to draw complex shapes or images on the display that are not supported by the standard primitives. In such cases, you can take full control of the display and create any image you want on the host device. You can then send that frame to the target device to be immediately rendered. This approach works well if you want to render and animate complex 3D objects or even games.

Manual Framebuffer

We know that internal drawing functions render to internal memory and only a call to Show() will transfer that data (framebuffer) to the display. We will make our own framebuffer.

uint[] frameBuffer = new uint[128*64]; 
frameBuffer = [0] * (128*64)

This is a linear array, not a 2D array like you see on the display. Still, the size is correct, 128×64 pixels. We now need a helper function to let us set pixels in the framebuffer.

void SetPixel(uint color, int x, int y){
    if(x<0 || x>127 || y<0 || y>63) return;
    int index = y * 128 + x;
    frameBuffer[index] = color;
}
def SetPixel(color, x, y):
    if x<0 or x>127 or y<0 or y>63: 
        return
    index = y * 128 + x
    frameBuffer[index] = color

Let us also add a function to clear the screen. Note how we are making functions identical to the ones we use with the built in graphics.

void Clear(uint color){
    Array.Fill(frameBuffer, color);
}
def Clear(color):
    for i in range(len(frameBuffer)):
        frameBuffer[i]=color

The only thing left is the Show() function, which transfers the data to the display.

void Show(){    BrainPad.Display.DrawBuffer(frameBuffer, 0, frameBuffer.Length);
}
def Show():
    BrainPad.Display.DrawBuffer(frameBuffer, 0, 0)

Drawing can now be done just like we did before but now the PC is doing the drawing.

Clear(0);
SetPixel(1, 10, 10);
Show();
Clear(0)
SetPixel(1, 10, 10)
Show()

The results are very similar to using the internal graphics. However, using the PC’s processing power will be much faster. This code will draw random pixels around the screen. The PC can draw thousands/millions of pixels per second the update looks slow on the screen because it takes time to transfer the frambuffer from the PC to the BrainPad. When generating images, 3D, video, and more, you will be generating the entire frame on the PC and transfer to the BrainPad after.

var rnd = new System.Random();
uint[] frameBuffer = new uint[128*64]; 
void SetPixel(uint color, int x, int y){
    if(x<0 || x>127 || y<0 || y>63) return;
    int index = y * 128 + x;
    frameBuffer[index] = color;
}
void Clear(uint color){
    Array.Fill(frameBuffer, color);
}
void Show()
{
    BrainPad.Display.DrawBuffer(frameBuffer, 0, frameBuffer.Length);
}

Clear(0);
while(true){
    var x = rnd.Next(127);
    var y = rnd.Next(63);
    SetPixel(1, x, y);
    Show();
}
frameBuffer = [0] * (128*64) 

def SetPixel(color, x, y):
    if x<0 or x>127 or y<0 or y>63: 
        return
    index = y * 128 + x
    frameBuffer[index] = color

def Clear(color):
    for i in range(len(frameBuffer)):
        frameBuffer[i]=color

def Show():
    BrainPad.Display.DrawBuffer(frameBuffer, 0, 0)

import random
Clear(0)
while True:
    x = random.randint(0,127)
    y = random.randint(0,64)
    SetPixel(1, x, y)
    Show()

Do you know how to draw circles? Lines? Use the SetPixel() method to add them.

Using PC’s Graphics

The previous work got the bases ready for the user to implement their own drawing functions. We however can use the graphics implementation found on the system being used. This highly depends on what system and what libraries are being used. We will share an example for Python using the Python Imaging Library and and another one using .NET C#.

To use the Python Imaging Library, first import it into the Python system pip install Pillow. We now can create the needed drawing object to help with drawing shapes. This is a very powerful library with a lot of options. We are only showing very little of what it can do for demonstration purposes.

Note how for color, you can use “white” or 1 to show a white pixel and “black” or zero to clear out the pixel.

from DUELink.DUELinkController import DUELinkController
# Connect to BrainPad
availablePort = DUELinkController.GetConnectionPort()
BrainPad = DUELinkController(availablePort)
# BrainPad is ready

from PIL import Image, ImageDraw, ImageFont

framebuffer =Image.new("1", (128, 64), "black")
draw = ImageDraw.Draw(framebuffer)

# Draw something
draw.rectangle((10, 10, 20, 30), outline=1)
draw.point([0,0],fill="white")
font = ImageFont.load_default()
# Draw Some Text
draw.text(
    (30,30),
    text = "Hello Python!",
    font=font,
    fill=1,
)

# Get the pixel data as a flat array of values
pixels = list(framebuffer.getdata())
# Send to the BrainPad's display
BrainPad.Display.DrawBuffer(pixels,0,len(pixels))

.NET C# also includes powerful graphical library. IT might be easier to use since it uses the .NET libraries. Install System.Drawing.Common NuGet. An exiting use of this library is in the full font support. You can now write text on the screen in any language! If your PC can show it, then the BrainPad can as well.

using GHIElectronics.DUELink;
using System.Drawing;
using System.Drawing.Imaging;

var availablePort = DUELinkController.GetConnectionPort();
var BrainPad = new DUELinkController(availablePort);

void Render(Bitmap b)
{
    var bits = b.LockBits(new Rectangle(0, 0, 128, 64), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    var size = bits.Height * bits.Stride;
    var bytes = new byte[size];
    var uints = new uint[size / 4];
    System.Runtime.InteropServices.Marshal.Copy(bits.Scan0, bytes, 0, size);
    b.UnlockBits(bits);
   
    BrainPad.Display.DrawBufferBytes(bytes);
}

var bmp = new Bitmap(128, 64);
var dc = Graphics.FromImage(bmp);

Font fnt20 = new Font("Arial", 20, FontStyle.Regular);
Font fnt10 = new Font("Arial", 10, FontStyle.Regular);
dc.DrawString("Hello World", fnt10, Brushes.White, 0, 0);
dc.DrawString("مرحبا بالعالم", fnt20, Brushes.White, 0, 20);

Render(bmp);

What’s Next?

Do you have experience with drawing on computers? Build 3D drawing on the screen.

BrainStorm

Graphics have always been a challenge to computers’ processing power. We have gone from Pong games in the 70s to having real-life-looking graphics in a few decades. The BrainPad takes you all the way back to the 70s and brings you on a journey to see how pixels evolved into shapes, into fake-3D to real-3D and beyond!

Newsletter

Twitter Feed
Hot off the press!
March 13, 2023
November 9, 2022
September 15, 2022
March 21, 2022