Skip to content

Commit 42ec970

Browse files
peterhinchdpgeorge
authored andcommittedAug 19, 2022
extmod/modframebuf: Add ellipse drawing method.
1 parent 127b340 commit 42ec970

File tree

5 files changed

+885
-6
lines changed

5 files changed

+885
-6
lines changed
 

‎docs/library/framebuf.rst

+16-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ class FrameBuffer
1111
-----------------
1212

1313
The FrameBuffer class provides a pixel buffer which can be drawn upon with
14-
pixels, lines, rectangles, text and even other FrameBuffer's. It is useful
15-
when generating output for displays.
14+
pixels, lines, rectangles, ellipses, text and even other FrameBuffers. It is
15+
useful when generating output for displays.
1616

1717
For example::
1818

@@ -84,6 +84,20 @@ The following methods draw shapes onto the FrameBuffer.
8484
The optional *f* parameter can be set to ``True`` to fill the rectangle.
8585
Otherwise just a one pixel outline is drawn.
8686

87+
.. method:: FrameBuffer.ellipse(x, y, xr, yr, c[, f, m])
88+
89+
Draw an ellipse at the given location. Radii *xr* and *yr* define the
90+
geometry; equal values cause a circle to be drawn. The *c* parameter
91+
defines the color.
92+
93+
The optional *f* parameter can be set to ``True`` to fill the ellipse.
94+
Otherwise just a one pixel outline is drawn.
95+
96+
The optional *m* parameter enables drawing to be restricted to certain
97+
quadrants of the ellipse. The LS four bits determine which quadrants are
98+
to be drawn, with bit 0 specifying Q1, b1 Q2, b2 Q3 and b3 Q4. Quadrants
99+
are numbered counterclockwise with Q1 being top right.
100+
87101
Drawing text
88102
------------
89103

‎examples/natmod/framebuf/framebuf.c

+5-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ mp_obj_type_t mp_type_framebuf;
1212

1313
#include "extmod/modframebuf.c"
1414

15-
mp_map_elem_t framebuf_locals_dict_table[10];
15+
mp_map_elem_t framebuf_locals_dict_table[11];
1616
STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table);
1717

1818
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
@@ -29,9 +29,10 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a
2929
framebuf_locals_dict_table[4] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_vline), MP_OBJ_FROM_PTR(&framebuf_vline_obj) };
3030
framebuf_locals_dict_table[5] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_rect), MP_OBJ_FROM_PTR(&framebuf_rect_obj) };
3131
framebuf_locals_dict_table[6] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_line), MP_OBJ_FROM_PTR(&framebuf_line_obj) };
32-
framebuf_locals_dict_table[7] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_blit), MP_OBJ_FROM_PTR(&framebuf_blit_obj) };
33-
framebuf_locals_dict_table[8] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_scroll), MP_OBJ_FROM_PTR(&framebuf_scroll_obj) };
34-
framebuf_locals_dict_table[9] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_text), MP_OBJ_FROM_PTR(&framebuf_text_obj) };
32+
framebuf_locals_dict_table[7] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_ellipse), MP_OBJ_FROM_PTR(&framebuf_ellipse_obj) };
33+
framebuf_locals_dict_table[8] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_blit), MP_OBJ_FROM_PTR(&framebuf_blit_obj) };
34+
framebuf_locals_dict_table[9] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_scroll), MP_OBJ_FROM_PTR(&framebuf_scroll_obj) };
35+
framebuf_locals_dict_table[10] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_text), MP_OBJ_FROM_PTR(&framebuf_text_obj) };
3536
mp_type_framebuf.locals_dict = (void*)&framebuf_locals_dict;
3637

3738
mp_store_global(MP_QSTR_FrameBuffer, MP_OBJ_FROM_PTR(&mp_type_framebuf));

‎extmod/modframebuf.c

+95
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,100 @@ STATIC mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) {
469469
}
470470
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line);
471471

472+
STATIC void ellipse_pixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask) {
473+
if (mask && 0 <= x && x < fb->width && 0 <= y && y < fb->height) {
474+
setpixel(fb, x, y, col);
475+
}
476+
}
477+
478+
// Q2 Q1
479+
// Q3 Q4
480+
#define ELLIPSE_MASK_FILL (0x10)
481+
#define ELLIPSE_MASK_ALL (0x0f)
482+
#define ELLIPSE_MASK_Q1 (0x01)
483+
#define ELLIPSE_MASK_Q2 (0x02)
484+
#define ELLIPSE_MASK_Q3 (0x04)
485+
#define ELLIPSE_MASK_Q4 (0x08)
486+
487+
STATIC void draw_ellipse_points(const mp_obj_framebuf_t *fb, mp_int_t cx, mp_int_t cy, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask) {
488+
if (mask & ELLIPSE_MASK_FILL) {
489+
if (mask & ELLIPSE_MASK_Q1) {
490+
fill_rect(fb, cx, cy - y, x + 1, 1, col);
491+
}
492+
if (mask & ELLIPSE_MASK_Q2) {
493+
fill_rect(fb, cx - x, cy - y, x + 1, 1, col);
494+
}
495+
if (mask & ELLIPSE_MASK_Q3) {
496+
fill_rect(fb, cx - x, cy + y, x + 1, 1, col);
497+
}
498+
if (mask & ELLIPSE_MASK_Q4) {
499+
fill_rect(fb, cx, cy + y, x + 1, 1, col);
500+
}
501+
} else {
502+
ellipse_pixel(fb, cx + x, cy - y, col, mask & ELLIPSE_MASK_Q1);
503+
ellipse_pixel(fb, cx - x, cy - y, col, mask & ELLIPSE_MASK_Q2);
504+
ellipse_pixel(fb, cx - x, cy + y, col, mask & ELLIPSE_MASK_Q3);
505+
ellipse_pixel(fb, cx + x, cy + y, col, mask & ELLIPSE_MASK_Q4);
506+
}
507+
}
508+
509+
STATIC mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) {
510+
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
511+
mp_int_t args[5];
512+
framebuf_args(args_in, args, 5); // cx, cy, xradius, yradius, col
513+
mp_int_t mask = (n_args > 6 && mp_obj_is_true(args_in[6])) ? ELLIPSE_MASK_FILL : 0;
514+
if (n_args > 7) {
515+
mask |= mp_obj_get_int(args_in[7]) & ELLIPSE_MASK_ALL;
516+
} else {
517+
mask |= ELLIPSE_MASK_ALL;
518+
}
519+
mp_int_t two_asquare = 2 * args[2] * args[2];
520+
mp_int_t two_bsquare = 2 * args[3] * args[3];
521+
mp_int_t x = args[2];
522+
mp_int_t y = 0;
523+
mp_int_t xchange = args[3] * args[3] * (1 - 2 * args[2]);
524+
mp_int_t ychange = args[2] * args[2];
525+
mp_int_t ellipse_error = 0;
526+
mp_int_t stoppingx = two_bsquare * args[2];
527+
mp_int_t stoppingy = 0;
528+
while (stoppingx >= stoppingy) { // 1st set of points, y' > -1
529+
draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask);
530+
y += 1;
531+
stoppingy += two_asquare;
532+
ellipse_error += ychange;
533+
ychange += two_asquare;
534+
if ((2 * ellipse_error + xchange) > 0) {
535+
x -= 1;
536+
stoppingx -= two_bsquare;
537+
ellipse_error += xchange;
538+
xchange += two_bsquare;
539+
}
540+
}
541+
// 1st point set is done start the 2nd set of points
542+
x = 0;
543+
y = args[3];
544+
xchange = args[3] * args[3];
545+
ychange = args[2] * args[2] * (1 - 2 * args[3]);
546+
ellipse_error = 0;
547+
stoppingx = 0;
548+
stoppingy = two_asquare * args[3];
549+
while (stoppingx <= stoppingy) { // 2nd set of points, y' < -1
550+
draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask);
551+
x += 1;
552+
stoppingx += two_bsquare;
553+
ellipse_error += xchange;
554+
xchange += two_bsquare;
555+
if ((2 * ellipse_error + ychange) > 0) {
556+
y -= 1;
557+
stoppingy -= two_asquare;
558+
ellipse_error += ychange;
559+
ychange += two_asquare;
560+
}
561+
}
562+
return mp_const_none;
563+
}
564+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_ellipse_obj, 6, 8, framebuf_ellipse);
565+
472566
STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
473567
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
474568
mp_obj_t source_in = mp_obj_cast_to_native_base(args_in[1], MP_OBJ_FROM_PTR(&mp_type_framebuf));
@@ -603,6 +697,7 @@ STATIC const mp_rom_map_elem_t framebuf_locals_dict_table[] = {
603697
{ MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&framebuf_vline_obj) },
604698
{ MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&framebuf_rect_obj) },
605699
{ MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&framebuf_line_obj) },
700+
{ MP_ROM_QSTR(MP_QSTR_ellipse), MP_ROM_PTR(&framebuf_ellipse_obj) },
606701
{ MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) },
607702
{ MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) },
608703
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) },

‎tests/extmod/framebuf_ellipse.py

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
try:
2+
import framebuf
3+
except ImportError:
4+
print("SKIP")
5+
raise SystemExit
6+
7+
8+
def printbuf():
9+
print("--8<--")
10+
for y in range(h):
11+
for x in range(w):
12+
print("%02x" % buf[(x + y * w)], end="")
13+
print()
14+
print("-->8--")
15+
16+
17+
w = 30
18+
h = 30
19+
buf = bytearray(w * h)
20+
fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8)
21+
22+
# Outline
23+
fbuf.fill(0)
24+
fbuf.ellipse(15, 15, 12, 6, 0xFF, False)
25+
printbuf()
26+
27+
# Fill
28+
fbuf.fill(0)
29+
fbuf.ellipse(15, 15, 6, 12, 0xAA, True)
30+
printbuf()
31+
32+
# Outline and fill some different quadrant combos.
33+
for m in (0, 0b0001, 0b0010, 0b0100, 0b1000, 0b1010):
34+
fbuf.fill(0)
35+
fbuf.ellipse(15, 15, 6, 12, 0xAA, False, m)
36+
printbuf()
37+
fbuf.fill(0)
38+
fbuf.ellipse(15, 15, 6, 12, 0xAA, True, m)
39+
printbuf()
40+
41+
# Draw ellipses that will go out of bounds at each of the edges.
42+
for x, y in (
43+
(
44+
4,
45+
4,
46+
),
47+
(
48+
26,
49+
4,
50+
),
51+
(
52+
26,
53+
26,
54+
),
55+
(
56+
4,
57+
26,
58+
),
59+
):
60+
fbuf.fill(0)
61+
fbuf.ellipse(x, y, 6, 12, 0xAA, False)
62+
printbuf()
63+
fbuf.fill(0)
64+
fbuf.ellipse(x, y, 6, 12, 0xAA, True)
65+
printbuf()

‎tests/extmod/framebuf_ellipse.py.exp

+704
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.