tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/examples/ili9341/pyportal_boing/main.go (about) 1 // Port of Adafruit's "pyportal_boing" demo found here: 2 // https://github.com/adafruit/Adafruit_ILI9341/blob/master/examples/pyportal_boing 3 package main 4 5 import ( 6 "time" 7 8 "tinygo.org/x/drivers/examples/ili9341/initdisplay" 9 "tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics" 10 "tinygo.org/x/drivers/ili9341" 11 "tinygo.org/x/drivers/pixel" 12 ) 13 14 const ( 15 BGCOLOR = pixel.RGB565BE(0x75AD) 16 GRIDCOLOR = pixel.RGB565BE(0x15A8) 17 BGSHADOW = pixel.RGB565BE(0x8552) 18 GRIDSHADOW = pixel.RGB565BE(0x0C60) 19 RED = pixel.RGB565BE(0x00F8) 20 WHITE = pixel.RGB565BE(0xFFFF) 21 22 YBOTTOM = 123 // Ball Y coord at bottom 23 YBOUNCE = -3.5 // Upward velocity on ball bounce 24 25 _debug = false 26 ) 27 28 var ( 29 frameBuffer = pixel.NewImage[pixel.RGB565BE](graphics.BALLWIDTH+8, graphics.BALLHEIGHT+8) 30 31 startTime int64 32 frame int64 33 34 // Ball coordinates are stored floating-point because screen refresh 35 // is so quick, whole-pixel movements are just too fast! 36 ballx float32 37 bally float32 38 ballvx float32 39 ballvy float32 40 ballframe float32 41 balloldx float32 42 balloldy float32 43 44 // Color table for ball rotation effect 45 palette [16]pixel.RGB565BE 46 ) 47 48 var ( 49 display *ili9341.Device 50 ) 51 52 func main() { 53 display = initdisplay.InitDisplay() 54 55 print("width, height == ") 56 width, height := display.Size() 57 println(width, height) 58 59 DrawBackground() 60 61 startTime = time.Now().UnixNano() 62 frame = 0 63 64 ballx = 20.0 65 bally = YBOTTOM // Current ball position 66 ballvx = 0.8 67 ballvy = YBOUNCE // Ball velocity 68 ballframe = 3 // Ball animation frame # 69 balloldx = ballx 70 balloldy = bally // Prior ball position 71 72 for { 73 74 balloldx = ballx // Save prior position 75 balloldy = bally 76 ballx += ballvx // Update position 77 bally += ballvy 78 ballvy += 0.06 // Update Y velocity 79 if (ballx <= 15) || (ballx >= graphics.SCREENWIDTH-graphics.BALLWIDTH) { 80 ballvx *= -1 // Left/right bounce 81 } 82 if bally >= YBOTTOM { // Hit ground? 83 bally = YBOTTOM // Clip and 84 ballvy = YBOUNCE // bounce up 85 } 86 87 // Determine screen area to update. This is the bounds of the ball's 88 // prior and current positions, so the old ball is fully erased and new 89 // ball is fully drawn. 90 var minx, miny, maxx, maxy, width, height int16 91 92 // Determine bounds of prior and new positions 93 minx = int16(ballx) 94 if int16(balloldx) < minx { 95 minx = int16(balloldx) 96 } 97 miny = int16(bally) 98 if int16(balloldy) < miny { 99 miny = int16(balloldy) 100 } 101 maxx = int16(ballx + graphics.BALLWIDTH - 1) 102 if int16(balloldx+graphics.BALLWIDTH-1) > maxx { 103 maxx = int16(balloldx + graphics.BALLWIDTH - 1) 104 } 105 maxy = int16(bally + graphics.BALLHEIGHT - 1) 106 if int16(balloldy+graphics.BALLHEIGHT-1) > maxy { 107 maxy = int16(balloldy + graphics.BALLHEIGHT - 1) 108 } 109 110 width = maxx - minx + 1 111 height = maxy - miny + 1 112 buffer := frameBuffer.Rescale(int(width), int(height)) 113 114 // Ball animation frame # is incremented opposite the ball's X velocity 115 ballframe -= ballvx * 0.5 116 if ballframe < 0 { 117 ballframe += 14 // Constrain from 0 to 13 118 } else if ballframe >= 14 { 119 ballframe -= 14 120 } 121 122 // Set 7 palette entries to white, 7 to red, based on frame number. 123 // This makes the ball spin 124 for i := 0; i < 14; i++ { 125 if (int(ballframe)+i)%14 < 7 { 126 palette[i+2] = WHITE 127 } else { 128 palette[i+2] = RED 129 } // Palette entries 0 and 1 aren't used (clear and shadow, respectively) 130 } 131 132 // Only the changed rectangle is drawn into the 'renderbuf' array... 133 var c pixel.RGB565BE //, *destPtr; 134 bx := minx - int16(ballx) // X relative to ball bitmap (can be negative) 135 by := miny - int16(bally) // Y relative to ball bitmap (can be negative) 136 bgx := minx // X relative to background bitmap (>= 0) 137 bgy := miny // Y relative to background bitmap (>= 0) 138 var bx1, bgx1 int16 // Loop counters and working vars 139 var p uint8 // 'packed' value of 2 ball pixels 140 var bufIdx int8 = 0 141 142 //tft.setAddrWindow(minx, miny, width, height) 143 144 for y := 0; y < int(height); y++ { // For each row... 145 //destPtr = &renderbuf[bufIdx][0]; 146 bx1 = bx // Need to keep the original bx and bgx values, 147 bgx1 = bgx // so copies of them are made here (and changed in loop below) 148 for x := 0; x < int(width); x++ { 149 var bgidx = int(bgy)*(graphics.SCREENWIDTH/8) + int(bgx1/8) 150 if (bx1 >= 0) && (bx1 < graphics.BALLWIDTH) && // Is current pixel row/column 151 (by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area? 152 // Yes, do ball compositing math... 153 p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels) 154 var nibble uint8 155 if (bx1 & 1) != 0 { 156 nibble = p & 0xF 157 } else { 158 nibble = p >> 4 159 } // Unpack high or low nybble 160 if nibble == 0 { // Outside ball - just draw grid 161 if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 { 162 c = GRIDCOLOR 163 } else { 164 c = BGCOLOR 165 } 166 } else if nibble > 1 { // In ball area... 167 c = palette[nibble] 168 } else { // In shadow area... 169 if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 { 170 c = GRIDSHADOW 171 } else { 172 c = BGSHADOW 173 } 174 } 175 } else { // Outside ball bitmap, just draw background bitmap... 176 if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 { 177 c = GRIDCOLOR 178 } else { 179 c = BGCOLOR 180 } 181 } 182 buffer.Set(x, y, c) 183 bx1++ // Increment bitmap position counters (X axis) 184 bgx1++ 185 } 186 //tft.dmaWait(); // Wait for prior line to complete 187 //tft.writePixels(&renderbuf[bufIdx][0], width, false); // Non-blocking write 188 bufIdx = 1 - bufIdx 189 by++ // Increment bitmap position counters (Y axis) 190 bgy++ 191 } 192 193 display.DrawBitmap(minx, miny, buffer) 194 195 // Show approximate frame rate 196 frame++ 197 if frame&255 == 0 { // Every 256 frames... 198 elapsed := (time.Now().UnixNano() - startTime) / int64(time.Second) 199 if elapsed > 0 { 200 println(frame/elapsed, " fps") 201 } 202 } 203 } 204 } 205 206 func DrawBackground() { 207 w, h := display.Size() 208 byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte 209 var b uint8 210 buffer := frameBuffer.Rescale(int(w), 1) 211 for j := int16(0); j < h; j++ { 212 for k := int16(0); k < w; k++ { 213 if k&7 > 0 { 214 b <<= 1 215 } else { 216 b = graphics.Background[j*byteWidth+k/8] 217 } 218 if b&0x80 == 0 { 219 buffer.Set(int(k), 0, BGCOLOR) 220 } else { 221 buffer.Set(int(k), 0, GRIDCOLOR) 222 } 223 } 224 display.DrawBitmap(0, j, buffer) 225 } 226 }