badger2040 icon indicating copy to clipboard operation
badger2040 copied to clipboard

Broken Badger2040.image() method

Open niutech opened this issue 2 years ago • 4 comments

In version 0.0.2, the Badger2040.image() method looked like this:

for(auto y = 0; y < dh; y++) {
  for(auto x = 0; x < dw; x++) {
    // work out byte offset in source data
    uint32_t o = ((y + sy) * (stride >> 3)) + ((x + sx) >> 3);
    // extract bitmask for this pixel
    uint32_t bm = 0b10000000 >> ((x + sx) & 0b111);
    // draw the pixel
    uc8151_legacy.pixel(dx + x, dy + y, data[o] & bm);
  }
}

However, in version 0.0.3 it looks like this:

for oy in range(h):
    row = data[oy]
    for ox in range(w):
        if row & 0b1 == 0:
            self.display.pixel(x + ox, y + oy)
        row >>= 1

So putting the same image data results in garbled pixels after upgrade to 0.0.3. Please make it compatible, like this:

for oy in range(h):
    for ox in range(w):
        o = oy * (w >> 3) + (ox >> 3)
        bm = 0b10000000 >> (ox & 0b111)
        if data[o] & bm:
            self.display.pixel(x + ox, y + oy)

niutech avatar Aug 07 '23 14:08 niutech

I'm not sure what happened here, but this is a slow and awful kludge that's likely to be completely deprecated in favour of PNG support in the next release.

Gadgetoid avatar Aug 08 '23 10:08 Gadgetoid

PNG is a nice addition, but isn't PNG decoding overcomplicated when displaying many small icons on the Badger2040 screen? Drawing a bytearray should be much faster.

Message ID: @.***>

niutech avatar Aug 08 '23 12:08 niutech

Faster, maybe, but accessible to the vast majority of users, not especially.

The implementation currently in Badger 2040 is probably slower than PNG, it's really about the worst way to do it.

There might be a case to be made for supporting this in PicoGraphics, but there's no guarantee the bytes in the drawing surface will be in any particular order, endianess, type or size.

Anyway the best way to do this in the interim is to implement the function in your own code, and use memoryview(display.display) to access the raw buffer as quickly as possible, eg:

import io
from badger2040 import Badger2040

display = Badger2040()

display.set_pen(15)
display.clear()


def display_bitmap(o_x, o_y, width, height, data):
    WIDTH, HEIGHT = display.display.get_bounds()

    y_bytes = int(height // 8)

    if len(data) < width * height / 8:
        raise ValueError("Data undersized")

    for x in range(width):
        src = x * y_bytes
        dst = (x + o_x) * int(HEIGHT // 8) + int(o_y // 8)
        b = bytes(data[src:src + y_bytes])
        memoryview(display.display)[dst:dst + y_bytes] = b


display_bitmap(10, 10, 8, 16, [
    0b01111000, 0b11111110,
    0b10111110, 0b11111110,
    0b11011110, 0b11111110,
    0b11101111, 0b11111110,
    0b11110111, 0b11111110,
    0b11111011, 0b11111110,
    0b00111101, 0b11111110,
    0b00111110, 0b11111110,
])

display.update()

Noting that this limits you to heights and Y positions in multiples of 8 pixels, since that's how the internal data is packed, but if you want fast that's about as fast as you can get without dropping into C.

Gadgetoid avatar Aug 08 '23 13:08 Gadgetoid

Thank you!

niutech avatar Aug 11 '23 11:08 niutech