frambuf initalizes with ValueError
Port, board and/or hardware
all variants that use the framebuf module
MicroPython version
MicroPython v1.24.0-preview.149.g6007f3e20 on 2024-07-26; Raspberry Pi Pico W with RP2040
Reproduction
I'm using a simple frambuffer with one bit per pixel. This code segment used to work on earlier micropython versions but fails now:
import framebuf
w=21; h=8
data = bytearray(w*h // 8)
fb = framebuf.FrameBuffer(data, w, h, framebuf.MONO_HMSB)
Expected behaviour
Expected to accept the input buffer without a ValueError
Observed behaviour
this causes a
File "<stdin>", line 1, in <module>
ValueError:
ValueError is raised with no further details
Additional Information
I tracked this due to this input validation here:
https://github.com/micropython/micropython/blob/master/extmod/modframebuf.c#L318
and I think that's due to
https://github.com/micropython/micropython/blob/master/extmod/modframebuf.c#L294
which modifies the stride value to be 24 ((21 + 7) & ~7 == 24) and therefore the provided input buffer is too small. Modifying the example above with a input buffer of 24 then works as expected.
I'm not sure why this check was introduced. Maybe due to other functionalities in the framebuffer. But in any case it's now difficult to provide a matching input buffer size without the knowledge of what is checked inside the module.
This could be addressed via a doc update. Or raise a ValueError that indicates what went wrong (e.g. "expected buffer length of xxx, got yyy")
Code of Conduct
Yes, I agree
FrameBuffer Initialisation fails with ValueError on `memoryview`
Port, board and/or hardware
RP2; Unix (but I believe this affects any board with framebuffer support)
MicroPython version
MicroPython v1.23.0 on 2024-06-02; Raspberry Pi Pico with RP2040
Reproduction
Run the code below in the REPL, or run a script with the same code in it:
import array
import framebuf
buf = array.array('H', bytearray(2*8*8))
sub_buf = memoryview(buf)[4:]
fbuf = framebuf.FrameBuffer(sub_buf, 4, 8, framebuf.RGB565, 8)
Expected behaviour
I expected the code to work without error.
Observed behaviour
I get an unexpected ValueError with no explanation:
>>> import array
>>> import framebuf
>>> buf = array.array('H', bytearray(2*8*8))
>>> sub_buf = memoryview(buf)[4:]
>>> fbuf = framebuf.FrameBuffer(sub_buf, 4, 8, framebuf.RGB565, 8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError:
Additional Information
The "what am I actually trying to do" here is to create a frame buffer on a subsection of another buffer using a memory view to avoid copying data, either:
- to clip out a sprite from a larger sprite-sheet buffer to display using
FrameBuffer.blit - to clip drawing to a sub-region of a larger image (for this you have to have a memory view into the same underlying buffer)
This is likely related to, but different from #15571, as that is an issue with bit-alignment. As with that issue, I think that the lines at fault are here: https://github.com/micropython/micropython/blob/17d82344581ad3a76033fae54c5d3304e17f185f/extmod/modframebuf.c#L317-L319
If you look at the underlying buffers, you have something like the following:
....xxxx
ssssxxxx
ssssxxxx
ssssxxxx
ssssxxxx
ssssxxxx
ssssxxxx
ssssxxxx
where . are values in the underlying buffer that are skipped by the memoryview, x are values that should be visible in the framebuffer, and s are values which are skipped by the stride of 8.
It appears that what the FrameBuffer code is expecting is the following:
....xxxx
ssssxxxx
ssssxxxx
ssssxxxx
ssssxxxx
ssssxxxx
ssssxxxx
ssssxxxx
ssss
even though the last 4 values are never written to.
Work-around
This can be worked around by allocating extra memory to the underlying buffer, although that's fiddly for the first use-case if the data is loaded from an existing sprite sheet buffer, eg. loaded from a file.
Code of Conduct
Yes, I agree