On the glasses
Drawing
Draw shapes, text and small images on the 640 × 480 display.
The canvas
- Coordinates start at the top-left corner.
xgrows to the right andygrows downward. - Every color is a gray level from
0to15.0is black, which shows nothing on the display, and15is full brightness. - Anything outside the canvas is clipped. Nothing is scaled.
The 16 levels, from 0 (off) to 15 (brightest).
Frames
Draw each frame between begin() and present(). Everything you draw after begin() replaces the previous frame when you call present(). There’s no separate clear or end call.
MIRA compares each frame with the last one and repaints only what changed, so you don’t need to redraw on every app_render call. Redraw when your state changes.
#[link(wasm_import_module = "mira")]
extern "C" {
fn begin();
fn fill_round_rect(x: i32, y: i32, w: i32, h: i32, radius: i32, gray: i32);
fn text(x: i32, y: i32, w: i32, h: i32, gray: i32, font: i32, align: i32,
line_spacing: i32, utf8: *const u8, len: i32);
fn present();
}
unsafe fn draw() {
let label = b"Hello, MIRA";
begin();
fill_round_rect(120, 176, 400, 128, 20, 3); // dim card
text(120, 220, 400, 48, 15, 2, 1, 40, label.as_ptr(), label.len() as i32);
present();
}Shapes
| Function | Draws |
|---|---|
line(x0, y0, x1, y1, width, gray) | A line, width pixels thick |
fill_rect(x, y, w, h, gray) | A filled rectangle |
stroke_rect(x, y, w, h, line, gray) | A rectangle outline, line pixels thick |
fill_round_rect(x, y, w, h, radius, gray) | A filled rounded rectangle |
stroke_round_rect(x, y, w, h, radius, line, gray) | A rounded rectangle outline |
fill_circle(cx, cy, radius, gray) | A filled circle |
stroke_circle(cx, cy, radius, line, gray) | A circle outline |
fill_triangle(x0, y0, x1, y1, x2, y2, gray) | A filled triangle |
Text
text(x, y, w, h, gray, font, align, line_spacing, utf8, len) draws UTF-8 text inside a box and clips it to that box. line_spacing is the line height in pixels. MIRA copies the text before the call returns, so you can reuse the buffer right away.
| Font | Size | Align | Meaning |
|---|---|---|---|
0 | 24 px | 0 | Left |
1 | 18 px | 1 | Center |
2 | 32 px | 2 | Right |
3 | 40 px |
MIRA doesn’t wrap long text for you. Measure and split it into lines that fit, the way the shared text helpers in the guide do.
Images
4-bit images
bitmap4(x, y, w, h, pixels, len) draws a grayscale image with 16 levels.
- Each row is
ceil(w / 2)bytes, with the left pixel in the high nibble. Pad odd widths with the low nibble. lenmust equalceil(w / 2) * h. Images can be up to 640 × 480 and are drawn at their real size.- Gray
15is transparent, so use14for the brightest visible pixel.
bitmap4_rotated(x, y, w, h, pixels, len, degrees) takes the same data and rotates the image around its center by whole degrees.
1-bit masks
bitmap1(x, y, w, h, mask, len, gray) draws a one-color mask up to 32 × 32 in the gray level you pass. Each row is ceil(w / 8) bytes, most significant bit first. Masks are a good fit for icons.
Retained frames
On text-heavy screens, draw the static part once and keep it. frame_checkpoint() saves everything drawn so far in the frame. On later frames, frame_restore() brings it back so you only draw what changed on top.
frame_restore() returns 0 when there’s nothing to restore, for example right after a switch, so redraw everything in that case. Frames that contain 4-bit images can’t be checkpointed; 1-bit masks can. Shared/retained_frame.rs wraps the pattern:
#[path = "../Shared/retained_frame.rs"] mod retained_frame;
unsafe fn draw(page_changed: bool) {
if retained_frame::start(page_changed) {
draw_page(); // only when the page changed
retained_frame::checkpoint();
}
draw_clock(); // small parts that change every frame
present();
}