adafruit / Adafruit_CircuitPython_ProgressBar

Dynamic progress bar widget for CircuitPython displays
MIT License
6 stars 9 forks source link

ProgressBar could use less memory, operate more efficiently #10

Open jepler opened 4 years ago

jepler commented 4 years ago

The ProgressBar is a 1×1 tilegrid configured to display a W×H bitmap. Instead, it could be a W×1 tile grid, displaying 1×H slices from a 3×H bitmap.

Three tall elements are needed: One which is entirely the outline color; one which is the outline color at the top and bottom and transparent/black in the middle, and one that is the outline color at the top and bottom and the bar color in the middle.

This should reduce memory consumption (from W×H to W+3×H times some constant) and speed updates, since a progress change P pixels wide would only have to update P tilegrid entries instead of P×H bitmap entries. (however, the amount of data actually sent over SPI/I2C to the display would not change)

FoamyGuy commented 4 years ago

I had started work changing the progress bar implementation over to vectorio the other day but ran into trouble.

I think with the absolute latest builds of the system the required fixes inside vectorio are in and I made a few more fixes in my working branch of the library.

I've created #11 with those changes. I did end up seeing a decent memory reduction with that approach.

What do you think about vectorio.Polygons vs. the wider tilemap with slices? I don't think I grok enough of the larger picture to know which is likely to be more efficient.

jepler commented 4 years ago

I think vectorio has a good shot at being more efficient, but if compatibility with 5.x is desired then there might have to be two sets of code...

FoamyGuy commented 4 years ago

Ah, I definitely overlooked the compatibility issue. I think it would be good to offer backward compatibility at least for a while.

makermelissa commented 4 years ago

Same with Blinka until there's a vectorio module added.

tannewt commented 4 years ago

I think a TileGrid with multiple tiles will be more efficient than a bitmap if the height of the bar is more than 8 because TileGrid allocates a byte for each tile it shows.

vectorio or displayio.Shape will be much more efficient because they just save bounds, not each pixel.

FoamyGuy commented 4 years ago

Is there any further documentation or an example of how to use displayio.Shape? From the docs here: https://circuitpython.readthedocs.io/en/latest/shared-bindings/displayio/#displayio.Shape I'm not sure how to create one and get it showing on the screen.

tannewt commented 4 years ago

I have a test somewhere but I can probably describe it.

You can use it in place of a Bitmap. Instead of storing each pixel, it stores a start and stop x value for every y. x values in the range are 1 and the rest are zero. In other words, for every row, you can store one range of "on" pixels.

This makes it easy to store a solid shape. With mirror_x and mirror_y you can define a half or quarter of the shape instead and have it mirror the pixel ranges (creating two stretches of pixels per row and therefore outlines of symmetric shapes).

set_boundary is used to set that x range for a given row. (and wow, the docs of set_boundary aren't right) I meant to add the index ability that would be ok as long as you set a pixel to 1 if it's adjacent to a 1.

A rectangle would be something like:

s = displayio.Shape(w, h, mirror_x=True, mirror_y=True)
for y in range(h // 2):
  s.set_boundary(y, 0, (w + 1) // 2)

You can then also alter the start of the first few rows to round the corners too.