mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-04 23:17:47 +00:00
6cf07aff4c
Replace DrawRGBBitmap8 with DrawBitmap, following the change in the previous commit. This improves performance from 86fps to 100fps! I didn't investigate why, but I suspect it's because it now needs only a single store instead of two to update a pixel.
227 lines
6.3 KiB
Go
227 lines
6.3 KiB
Go
// Port of Adafruit's "pyportal_boing" demo found here:
|
|
// https://github.com/adafruit/Adafruit_ILI9341/blob/master/examples/pyportal_boing
|
|
package main
|
|
|
|
import (
|
|
"time"
|
|
|
|
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
|
|
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
|
|
"tinygo.org/x/drivers/ili9341"
|
|
"tinygo.org/x/drivers/pixel"
|
|
)
|
|
|
|
const (
|
|
BGCOLOR = pixel.RGB565BE(0x75AD)
|
|
GRIDCOLOR = pixel.RGB565BE(0x15A8)
|
|
BGSHADOW = pixel.RGB565BE(0x8552)
|
|
GRIDSHADOW = pixel.RGB565BE(0x0C60)
|
|
RED = pixel.RGB565BE(0x00F8)
|
|
WHITE = pixel.RGB565BE(0xFFFF)
|
|
|
|
YBOTTOM = 123 // Ball Y coord at bottom
|
|
YBOUNCE = -3.5 // Upward velocity on ball bounce
|
|
|
|
_debug = false
|
|
)
|
|
|
|
var (
|
|
frameBuffer = pixel.NewImage[pixel.RGB565BE](graphics.BALLWIDTH+8, graphics.BALLHEIGHT+8)
|
|
|
|
startTime int64
|
|
frame int64
|
|
|
|
// Ball coordinates are stored floating-point because screen refresh
|
|
// is so quick, whole-pixel movements are just too fast!
|
|
ballx float32
|
|
bally float32
|
|
ballvx float32
|
|
ballvy float32
|
|
ballframe float32
|
|
balloldx float32
|
|
balloldy float32
|
|
|
|
// Color table for ball rotation effect
|
|
palette [16]pixel.RGB565BE
|
|
)
|
|
|
|
var (
|
|
display *ili9341.Device
|
|
)
|
|
|
|
func main() {
|
|
display = initdisplay.InitDisplay()
|
|
|
|
print("width, height == ")
|
|
width, height := display.Size()
|
|
println(width, height)
|
|
|
|
DrawBackground()
|
|
|
|
startTime = time.Now().UnixNano()
|
|
frame = 0
|
|
|
|
ballx = 20.0
|
|
bally = YBOTTOM // Current ball position
|
|
ballvx = 0.8
|
|
ballvy = YBOUNCE // Ball velocity
|
|
ballframe = 3 // Ball animation frame #
|
|
balloldx = ballx
|
|
balloldy = bally // Prior ball position
|
|
|
|
for {
|
|
|
|
balloldx = ballx // Save prior position
|
|
balloldy = bally
|
|
ballx += ballvx // Update position
|
|
bally += ballvy
|
|
ballvy += 0.06 // Update Y velocity
|
|
if (ballx <= 15) || (ballx >= graphics.SCREENWIDTH-graphics.BALLWIDTH) {
|
|
ballvx *= -1 // Left/right bounce
|
|
}
|
|
if bally >= YBOTTOM { // Hit ground?
|
|
bally = YBOTTOM // Clip and
|
|
ballvy = YBOUNCE // bounce up
|
|
}
|
|
|
|
// Determine screen area to update. This is the bounds of the ball's
|
|
// prior and current positions, so the old ball is fully erased and new
|
|
// ball is fully drawn.
|
|
var minx, miny, maxx, maxy, width, height int16
|
|
|
|
// Determine bounds of prior and new positions
|
|
minx = int16(ballx)
|
|
if int16(balloldx) < minx {
|
|
minx = int16(balloldx)
|
|
}
|
|
miny = int16(bally)
|
|
if int16(balloldy) < miny {
|
|
miny = int16(balloldy)
|
|
}
|
|
maxx = int16(ballx + graphics.BALLWIDTH - 1)
|
|
if int16(balloldx+graphics.BALLWIDTH-1) > maxx {
|
|
maxx = int16(balloldx + graphics.BALLWIDTH - 1)
|
|
}
|
|
maxy = int16(bally + graphics.BALLHEIGHT - 1)
|
|
if int16(balloldy+graphics.BALLHEIGHT-1) > maxy {
|
|
maxy = int16(balloldy + graphics.BALLHEIGHT - 1)
|
|
}
|
|
|
|
width = maxx - minx + 1
|
|
height = maxy - miny + 1
|
|
buffer := frameBuffer.Rescale(int(width), int(height))
|
|
|
|
// Ball animation frame # is incremented opposite the ball's X velocity
|
|
ballframe -= ballvx * 0.5
|
|
if ballframe < 0 {
|
|
ballframe += 14 // Constrain from 0 to 13
|
|
} else if ballframe >= 14 {
|
|
ballframe -= 14
|
|
}
|
|
|
|
// Set 7 palette entries to white, 7 to red, based on frame number.
|
|
// This makes the ball spin
|
|
for i := 0; i < 14; i++ {
|
|
if (int(ballframe)+i)%14 < 7 {
|
|
palette[i+2] = WHITE
|
|
} else {
|
|
palette[i+2] = RED
|
|
} // Palette entries 0 and 1 aren't used (clear and shadow, respectively)
|
|
}
|
|
|
|
// Only the changed rectangle is drawn into the 'renderbuf' array...
|
|
var c pixel.RGB565BE //, *destPtr;
|
|
bx := minx - int16(ballx) // X relative to ball bitmap (can be negative)
|
|
by := miny - int16(bally) // Y relative to ball bitmap (can be negative)
|
|
bgx := minx // X relative to background bitmap (>= 0)
|
|
bgy := miny // Y relative to background bitmap (>= 0)
|
|
var bx1, bgx1 int16 // Loop counters and working vars
|
|
var p uint8 // 'packed' value of 2 ball pixels
|
|
var bufIdx int8 = 0
|
|
|
|
//tft.setAddrWindow(minx, miny, width, height)
|
|
|
|
for y := 0; y < int(height); y++ { // For each row...
|
|
//destPtr = &renderbuf[bufIdx][0];
|
|
bx1 = bx // Need to keep the original bx and bgx values,
|
|
bgx1 = bgx // so copies of them are made here (and changed in loop below)
|
|
for x := 0; x < int(width); x++ {
|
|
var bgidx = int(bgy)*(graphics.SCREENWIDTH/8) + int(bgx1/8)
|
|
if (bx1 >= 0) && (bx1 < graphics.BALLWIDTH) && // Is current pixel row/column
|
|
(by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area?
|
|
// Yes, do ball compositing math...
|
|
p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels)
|
|
var nibble uint8
|
|
if (bx1 & 1) != 0 {
|
|
nibble = p & 0xF
|
|
} else {
|
|
nibble = p >> 4
|
|
} // Unpack high or low nybble
|
|
if nibble == 0 { // Outside ball - just draw grid
|
|
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
|
c = GRIDCOLOR
|
|
} else {
|
|
c = BGCOLOR
|
|
}
|
|
} else if nibble > 1 { // In ball area...
|
|
c = palette[nibble]
|
|
} else { // In shadow area...
|
|
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
|
c = GRIDSHADOW
|
|
} else {
|
|
c = BGSHADOW
|
|
}
|
|
}
|
|
} else { // Outside ball bitmap, just draw background bitmap...
|
|
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
|
c = GRIDCOLOR
|
|
} else {
|
|
c = BGCOLOR
|
|
}
|
|
}
|
|
buffer.Set(x, y, c)
|
|
bx1++ // Increment bitmap position counters (X axis)
|
|
bgx1++
|
|
}
|
|
//tft.dmaWait(); // Wait for prior line to complete
|
|
//tft.writePixels(&renderbuf[bufIdx][0], width, false); // Non-blocking write
|
|
bufIdx = 1 - bufIdx
|
|
by++ // Increment bitmap position counters (Y axis)
|
|
bgy++
|
|
}
|
|
|
|
display.DrawBitmap(minx, miny, buffer)
|
|
|
|
// Show approximate frame rate
|
|
frame++
|
|
if frame&255 == 0 { // Every 256 frames...
|
|
elapsed := (time.Now().UnixNano() - startTime) / int64(time.Second)
|
|
if elapsed > 0 {
|
|
println(frame/elapsed, " fps")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func DrawBackground() {
|
|
w, h := display.Size()
|
|
byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte
|
|
var b uint8
|
|
buffer := frameBuffer.Rescale(int(w), 1)
|
|
for j := int16(0); j < h; j++ {
|
|
for k := int16(0); k < w; k++ {
|
|
if k&7 > 0 {
|
|
b <<= 1
|
|
} else {
|
|
b = graphics.Background[j*byteWidth+k/8]
|
|
}
|
|
if b&0x80 == 0 {
|
|
buffer.Set(int(k), 0, BGCOLOR)
|
|
} else {
|
|
buffer.Set(int(k), 0, GRIDCOLOR)
|
|
}
|
|
}
|
|
display.DrawBitmap(0, j, buffer)
|
|
}
|
|
}
|