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  }