adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.09k stars 1.21k forks source link

TileGrid: Relax the tile_{width,height} restriction #2722

Open jepler opened 4 years ago

jepler commented 4 years ago

Say you want to create a memory efficient 1-value graph in a TileGrid. You need a grid of Wx1 tiles, and you need to be able to place a single "1" pixel among (H-1) "0" pixels.

Right now, to do this you need to have a HxH bitmap. In tile 0, the pixel at Y=0 is set. In tile 1, the pixel at Y=1 is set, and so on. Total bitmap usage increases according to H^2, so it can add up to a lot. To put the pixel at height Y, you just use tile #Y.

If you could specify tile_width=1, tile_height=1, then a much smaller bitmap of just (2*H-1) is needed. It has a single "1" pixel at (H-1) from the top. To put the pixel at height Y, you just use tile #(2*H-Y-1) or something like that; I may have an off-by-1 error in that calculation, but it's something like that.

(this has other uses; I actually wanted to show a rainbow pattern that could be shifted to a different height at each X coordinate, but I explained this in terms of graphs for simplicity's sake)

I think that it's possible to relax the tile_width and tile_height to be any values that aren't bigger than the underlying bitmap size. But just allowing the specification of =1 enables almost anything.

deshipu commented 4 years ago

I think that you would be much better off just having a single tile, and painting the pixels on the underlying bitmap with something like that.

kevinjwalters commented 4 years ago

I'm graphing literally at the moment using this code in https://github.com/kevinjwalters/circuitpython-examples/blob/master/clue/plotter.py - this code is still evolving a little bit.

My main issue is clearing the graph to redraw it, clearing every pixel in CircuitPython is too slow, see #2688 for potential improvements here. I've taken the "store the drawing data and undrawing each pixel/line approach" for now.

Efficient scrolling of Bitmaps would be nice too. I'm also wondering if some simple compression for the underlying storage makes sense for certain types of Bitmaps but I've not yet thought this through. For certain "plain" static Bitmaps this could be useful, e.g. draw it then freeze it which could do something like run length encode it and make it read-only.

deshipu commented 4 years ago

You don't have to clear every pixel. You can clear only the ones you drew (for example, draw the same graph, but in black), or simply make a new bitmap.

kevinjwalters commented 4 years ago

Making a replacement sounds useful but wasn't in my case for a Bitmap with 20100 bytes storage. For some reason the previous object (same size, no longer used) didn't GC to leave enough space.

deshipu commented 4 years ago

If there was no allocation between releasing the old bitmap and creating the new, there should be no fragmentation. I suppose the displayio internals may be keeping the old buffer alive until a frame finishes drawing, though.

tannewt commented 4 years ago

@jepler Why use a single TileGrid and Bitmap? It sounds like you want W 1x1 tilegrids, one for each pixel. Then you only need a 1x1 Bitmap. A scroll will generate 2W 1x1 areas to update but we could have TileGrid detect it and produce 1 2x1 area instead.