-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
extmod/framebuf: Add ellipse drawing method. #9038
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Thanks @peterhinch! It's +444 bytes on PYBV11. There's an issue when fill=0 and you specify coordinates such that it ends up drawing out of bounds because setpixel() doesn't have any checks. When fill=1, line() handles this. Interestingly if you move the check into setpixel() and then remove the check from line() and blit() then the size diff goes to +372. (perhaps this is preventing inlining of setpixel) Out of curiosity, do you think there's a need to be able to draw ellipse arcs? I'm thinking maybe for something like a rounded rect, to draw the corners. This can be done in a later PR of course, unless it might mean we need to change the signature of the ellipse method. @rkompass |
Thanks for the review. I think the check should be done in I have considered drawing arcs. The Incidentally the code format fail is puzzling me. Is there a tool I can run locally on the C code? I guess I should run black on the Python test script. |
The diff showing the code formatting problems is printed in the build output: https://github.com/micropython/micropython/runs/7768326191?check_suite_focus=true It's mostly missing whitespace e.g.: |
I have prototyped arc drawing in Python and it's straightforward. This makes As an indication of the added code here is the changed function: def draw_points(ssd, cx, cy, x, y, fill, color):
if fill & 1:
if fill & 0x02:
ssd.line(cx, cy - y, cx + x, cy - y, color) # q0
if fill & 0x04:
ssd.line(cx - x, cy - y, cx, cy - y, color) # q1
if fill & 0x08:
ssd.line(cx - x, cy + y, cx, cy + y, color) # q2
if fill & 0x10:
ssd.line(cx, cy + y, cx + x, cy + y, color) # q3
else:
if fill & 0x02:
ssd.pixel(cx + x, cy - y, color) # q0
if fill & 0x04:
ssd.pixel(cx - x, cy - y, color) # q1
if fill & 0x08:
ssd.pixel(cx - x, cy + y, color) # q2
if fill & 0x10:
ssd.pixel(cx + x, cy + y, color) # q3 My view is that this is worth doing. Feedback appreciated! |
I have pushed an update that enables quadrant selection and adds a pixel bounds check. The doc is updated but the tests need to be enhanced for the quadrant feature. |
Now has updated test and is ready for review. |
Thanks @peterhinch -- the quadrant solution is very neat and a great compromise on functionality vs size. I will review in more detail with @mbooth101 's polygon PR too now. |
Some comments on performance. I tested on an RP2 clocked at 250MHz with an ili9341 240x320x16bit display. A large display for a RAM based framebuffer. The theoretical time to refresh at 30MHz SPI is 40ms (in practice my driver takes longer as it translates 4 to 16 bit color on the fly). But the 40ms figure supplies context for rendering times. The test drew a circle of diameter 200 on the display - something approaching a worst case size. I ran with filled and outline versions and timed the t = ticks_us()
ssd.ellipse(110, 110, 100, 100, False, GREEN) # ssd subclassed from framebuf
dt = ticks_diff(ticks_us(), t) Times were:
Observations: outline drawing is fast despite the pixel bounds check. Filled seemed slow, despite its use of the line draw code - but there are a lot of pixels to draw. @jimmo I'll look at a rounded rect with a view to submitting a PR once this has gone through. I use rounded rects and circles in my GUI's so having these primitives in C will save time and bytes. |
Wow, that was fast.
|
I have working rounded rectangle code. The algorithm is trivial if you leverage existing code. For a filled rrect you need three filled rectangles and four filled circle quadrants. These can be rendered fast with existing code. For a line drawn rrect it's four lines and four circle segments. Re drawing rings, all framebuf line drawing primitives draw single pixel lines. For most purposes a ring can be drawn by overlaying one circle with a smaller one. This isn't elegant but with our ellipse code even a large filled circle can be rendered in ~600μs. So I won't be pushing for a specific ring primitive. That said, I'm not a maintainer. A PR for a ring drawing primitive might well find favour and I won't argue against it. |
Now superseded by #9045. |
…n-main Translations update from Hosted Weblate
Integer algorithm requires minimal extra code for filled ellipse.
Algorithm from "A Fast Bresenham Type Algorithm For Drawing Ellipses, John Kennedy Mathematics Department Santa Monica College".