Fix keyword argument decoding issue on MicroPython and finalize raw image endpoint and clock sync changes
This commit is contained in:
+179
@@ -2,6 +2,7 @@ import time
|
||||
from machine import Pin, SPI
|
||||
import framebuf
|
||||
import micropython
|
||||
import struct
|
||||
|
||||
class ST7796:
|
||||
def __init__(self, spi, cs, dc, rst, bl=None, width=480, height=320, invert_color=True):
|
||||
@@ -89,6 +90,184 @@ class ST7796:
|
||||
except OSError:
|
||||
print(f"Error: Could not open {filename}")
|
||||
|
||||
@micropython.native
|
||||
def _convert_bgr24_to_rgb565(self, bgr_buf, rgb565_buf, width, src_offset, num_pixels):
|
||||
idx = 0
|
||||
for i in range(src_offset, src_offset + num_pixels):
|
||||
b = bgr_buf[i * 3]
|
||||
g = bgr_buf[i * 3 + 1]
|
||||
r = bgr_buf[i * 3 + 2]
|
||||
r_5 = r >> 3
|
||||
g_6 = g >> 2
|
||||
b_5 = b >> 3
|
||||
rgb565_buf[idx] = (r_5 << 3) | (g_6 >> 3)
|
||||
rgb565_buf[idx + 1] = ((g_6 & 0x07) << 5) | b_5
|
||||
idx += 2
|
||||
|
||||
@micropython.native
|
||||
def _convert_bgra32_to_rgb565(self, bgra_buf, rgb565_buf, width, src_offset, num_pixels):
|
||||
idx = 0
|
||||
for i in range(src_offset, src_offset + num_pixels):
|
||||
b = bgra_buf[i * 4]
|
||||
g = bgra_buf[i * 4 + 1]
|
||||
r = bgra_buf[i * 4 + 2]
|
||||
r_5 = r >> 3
|
||||
g_6 = g >> 2
|
||||
b_5 = b >> 3
|
||||
rgb565_buf[idx] = (r_5 << 3) | (g_6 >> 3)
|
||||
rgb565_buf[idx + 1] = ((g_6 & 0x07) << 5) | b_5
|
||||
idx += 2
|
||||
|
||||
def draw_bmp(self, filename, x=0, y=0):
|
||||
"""Draw a 24-bit or 32-bit uncompressed color BMP image at (x, y) coordinates."""
|
||||
try:
|
||||
with open(filename, 'rb') as f:
|
||||
header = f.read(54)
|
||||
if len(header) < 54 or header[0:2] != b'BM':
|
||||
print("Err: Not a valid BMP file")
|
||||
return False
|
||||
|
||||
pixel_offset = struct.unpack('<I', header[10:14])[0]
|
||||
width, height = struct.unpack('<ii', header[18:26])
|
||||
planes, bpp = struct.unpack('<HH', header[26:30])
|
||||
compression = struct.unpack('<I', header[30:34])[0]
|
||||
|
||||
if bpp not in (24, 32):
|
||||
print("Err: Only 24-bit and 32-bit BMP formats supported")
|
||||
return False
|
||||
|
||||
if compression != 0:
|
||||
print("Err: Only uncompressed BMP supported")
|
||||
return False
|
||||
|
||||
f.seek(pixel_offset)
|
||||
bottom_up = True
|
||||
if height < 0:
|
||||
height = -height
|
||||
bottom_up = False
|
||||
|
||||
row_bytes = (width * bpp) // 8
|
||||
row_padded = ((width * bpp + 31) // 32) * 4
|
||||
|
||||
read_buf = bytearray(row_padded)
|
||||
rgb565_buf = bytearray(width * 2)
|
||||
|
||||
for row_idx in range(height):
|
||||
n = f.readinto(read_buf)
|
||||
if n < row_padded:
|
||||
break
|
||||
|
||||
screen_y = y + (height - 1 - row_idx) if bottom_up else y + row_idx
|
||||
if screen_y < 0 or screen_y >= self.height:
|
||||
continue
|
||||
|
||||
x_start = x
|
||||
x_end = x + width - 1
|
||||
|
||||
if x_start >= self.width or x_end < 0:
|
||||
continue
|
||||
|
||||
win_x0 = max(0, x_start)
|
||||
win_x1 = min(self.width - 1, x_end)
|
||||
|
||||
if win_x1 < win_x0:
|
||||
continue
|
||||
|
||||
src_offset_pixels = win_x0 - x_start
|
||||
win_w = win_x1 - win_x0 + 1
|
||||
|
||||
# Convert pixel data to RGB565 row buffer
|
||||
if bpp == 24:
|
||||
self._convert_bgr24_to_rgb565(read_buf, rgb565_buf, width, src_offset_pixels, win_w)
|
||||
elif bpp == 32:
|
||||
self._convert_bgra32_to_rgb565(read_buf, rgb565_buf, width, src_offset_pixels, win_w)
|
||||
|
||||
# Draw directly to the screen via SPI window
|
||||
self.set_window(win_x0, screen_y, win_x1, screen_y)
|
||||
self.dc(1)
|
||||
self.cs(0)
|
||||
self.spi.write(memoryview(rgb565_buf)[:win_w * 2])
|
||||
self.cs(1)
|
||||
|
||||
# Also update internal 1-bit canvas buffer for screenshots/refresh consistency
|
||||
for px in range(win_w):
|
||||
screen_x = win_x0 + px
|
||||
src_px = src_offset_pixels + px
|
||||
if bpp == 24:
|
||||
b = read_buf[src_px * 3]
|
||||
g = read_buf[src_px * 3 + 1]
|
||||
r = read_buf[src_px * 3 + 2]
|
||||
else:
|
||||
b = read_buf[src_px * 4]
|
||||
g = read_buf[src_px * 4 + 1]
|
||||
r = read_buf[src_px * 4 + 2]
|
||||
# 0 = Black, 1 = White in conversion for MONO_HLSB canvas
|
||||
lum = (r * 299 + g * 587 + b * 114) // 1000
|
||||
mono_c = 1 if lum >= 128 else 0
|
||||
self.canvas.pixel(screen_x, screen_y, mono_c)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print("Error drawing BMP:", e)
|
||||
return False
|
||||
|
||||
@micropython.native
|
||||
def _update_mono_canvas_rgb565(self, x, y, w, h, data):
|
||||
for cy in range(h):
|
||||
screen_y = y + cy
|
||||
if screen_y < 0 or screen_y >= self.height:
|
||||
continue
|
||||
for cx in range(w):
|
||||
screen_x = x + cx
|
||||
if screen_x < 0 or screen_x >= self.width:
|
||||
continue
|
||||
idx = (cy * w + cx) * 2
|
||||
h_byte = data[idx]
|
||||
l_byte = data[idx + 1]
|
||||
# Extract RGB from RGB565
|
||||
r = (h_byte & 0xF8)
|
||||
g = ((h_byte & 0x07) << 5) | ((l_byte & 0xE0) >> 3)
|
||||
b = (l_byte & 0x1F) << 3
|
||||
# Convert to luminance
|
||||
lum = (r * 299 + g * 587 + b * 114) // 1000
|
||||
mono = 1 if lum >= 128 else 0
|
||||
self.canvas.pixel(screen_x, screen_y, mono)
|
||||
|
||||
def draw_rgb565(self, x, y, w, h, data):
|
||||
"""Draw raw RGB565 pixel data on the screen at specified (x,y) with width and height."""
|
||||
# Clip coordinates
|
||||
x_start = max(0, x)
|
||||
x_end = min(self.width - 1, x + w - 1)
|
||||
y_start = max(0, y)
|
||||
y_end = min(self.height - 1, y + h - 1)
|
||||
|
||||
if x_start > x_end or y_start > y_end:
|
||||
return True
|
||||
|
||||
# Fast path: if completely visible on screen, draw in one go
|
||||
if x_start == x and x_end == x + w - 1 and y_start == y and y_end == y + h - 1:
|
||||
self.set_window(x_start, y_start, x_end, y_end)
|
||||
self.dc(1)
|
||||
self.cs(0)
|
||||
self.spi.write(data)
|
||||
self.cs(1)
|
||||
else:
|
||||
# Slow path: row-by-row clipping
|
||||
for cy in range(y_start, y_end + 1):
|
||||
src_y = cy - y
|
||||
src_row_offset = (src_y * w + (x_start - x)) * 2
|
||||
row_len_bytes = (x_end - x_start + 1) * 2
|
||||
|
||||
self.set_window(x_start, cy, x_end, cy)
|
||||
self.dc(1)
|
||||
self.cs(0)
|
||||
self.spi.write(memoryview(data)[src_row_offset : src_row_offset + row_len_bytes])
|
||||
self.cs(1)
|
||||
|
||||
# Sync the internal 1-bit canvas buffer
|
||||
self._update_mono_canvas_rgb565(x, y, w, h, data)
|
||||
return True
|
||||
|
||||
# --- SCREENSHOT ---
|
||||
def save_screenshot(self, filename):
|
||||
print(f"Saving screenshot to {filename}...")
|
||||
|
||||
Reference in New Issue
Block a user