Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/steami_screen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ screen.text("Big", at="CENTER", scale=2)

Cardinal positions: `"N"`, `"NE"`, `"E"`, `"SE"`, `"S"`, `"SW"`, `"W"`, `"NW"`, `"CENTER"`.

Note: `scale=2` produces a bold effect (text drawn with 1px offset), not a true pixel-scale zoom. Backends can provide `draw_scaled_text()` for true scaling.
Note: `scale` > 1 produces a true pixel-scale zoom on SSD1327 displays via pixel-by-pixel framebuf rendering. Other backends can implement `draw_scaled_text()` for native scaling support. Backends without it fall back to a bold offset effect.

---

Expand Down
43 changes: 35 additions & 8 deletions lib/steami_screen/steami_screen/ssd1327.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ class SSD1327Display:

def __init__(self, raw):
self._raw = raw
self.width = getattr(raw, 'width', 128)
self.height = getattr(raw, 'height', 128)
self.width = getattr(raw, "width", 128)
self.height = getattr(raw, "height", 128)
# Resolve fill_rect dispatch once at init (avoids repeated hasattr)
if hasattr(raw, "fill_rect"):
self._fill_rect_raw = raw.fill_rect
else:
self._fill_rect_raw = raw.framebuf.fill_rect

def fill(self, color):
self._raw.fill(rgb_to_gray4(color))
Expand All @@ -34,18 +39,40 @@ def line(self, x1, y1, x2, y2, color):
self._raw.line(x1, y1, x2, y2, rgb_to_gray4(color))

def fill_rect(self, x, y, w, h, color):
gray = rgb_to_gray4(color)
if hasattr(self._raw, 'fill_rect'):
self._raw.fill_rect(x, y, w, h, gray)
else:
self._raw.framebuf.fill_rect(x, y, w, h, gray)
self._fill_rect_raw(x, y, w, h, rgb_to_gray4(color))

def rect(self, x, y, w, h, color):
gray = rgb_to_gray4(color)
if hasattr(self._raw, 'rect'):
if hasattr(self._raw, "rect"):
self._raw.rect(x, y, w, h, gray)
else:
self._raw.framebuf.rect(x, y, w, h, gray)

def show(self):
self._raw.show()

def draw_scaled_text(self, text, x, y, color, scale):
"""Draw text with true pixel-scale zoom.

Each character is rendered into a temporary 8x8 MONO_HLSB framebuf,
then each lit pixel is expanded into a scale x scale filled rectangle
on the display. The framebuf import is deferred to avoid breaking
imports in CPython environments where framebuf is not available.
"""
import framebuf

gray = rgb_to_gray4(color)
blit = self._fill_rect_raw

char_buf = bytearray(8)
fb = framebuf.FrameBuffer(char_buf, 8, 8, framebuf.MONO_HLSB)
Comment on lines +64 to +68
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

draw_scaled_text() allocates a new bytearray and FrameBuffer on every call. On MicroPython this can add GC pressure and jitter during UI updates. Consider caching the buffer/framebuf on self (lazy-init once) and reusing it across calls.

Copilot uses AI. Check for mistakes.

cx = x
for char in text:
fb.fill(0)
fb.text(char, 0, 0, 1)
for py in range(8):
for px in range(8):
if fb.pixel(px, py):
blit(cx + px * scale, y + py * scale, scale, scale, gray)
cx += 8 * scale
Loading