add primitive draw call on GFX

This commit is contained in:
Nilton Constantino 2026-01-20 07:14:32 +00:00
parent d8660eac85
commit 76f02584d0
No known key found for this signature in database
3 changed files with 232 additions and 3 deletions

View File

@ -158,6 +158,136 @@ impl Gfx {
self.fill_rect_blend(x, y, w, h, color, BlendMode::None);
}
/// Draws a single pixel.
pub fn draw_pixel(&mut self, x: i32, y: i32, color: Color) {
if x >= 0 && x < self.w as i32 && y >= 0 && y < self.h as i32 {
self.back[y as usize * self.w + x as usize] = color.0;
}
}
/// Draws a line between two points using Bresenham's algorithm.
pub fn draw_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: Color) {
let dx = (x1 - x0).abs();
let sx = if x0 < x1 { 1 } else { -1 };
let dy = -(y1 - y0).abs();
let sy = if y0 < y1 { 1 } else { -1 };
let mut err = dx + dy;
let mut x = x0;
let mut y = y0;
loop {
self.draw_pixel(x, y, color);
if x == x1 && y == y1 { break; }
let e2 = 2 * err;
if e2 >= dy {
err += dy;
x += sx;
}
if e2 <= dx {
err += dx;
y += sy;
}
}
}
/// Draws a circle outline using Midpoint Circle Algorithm.
pub fn draw_circle(&mut self, xc: i32, yc: i32, r: i32, color: Color) {
if r < 0 { return; }
let mut x = 0;
let mut y = r;
let mut d = 3 - 2 * r;
self.draw_circle_points(xc, yc, x, y, color);
while y >= x {
x += 1;
if d > 0 {
y -= 1;
d = d + 4 * (x - y) + 10;
} else {
d = d + 4 * x + 6;
}
self.draw_circle_points(xc, yc, x, y, color);
}
}
fn draw_circle_points(&mut self, xc: i32, yc: i32, x: i32, y: i32, color: Color) {
self.draw_pixel(xc + x, yc + y, color);
self.draw_pixel(xc - x, yc + y, color);
self.draw_pixel(xc + x, yc - y, color);
self.draw_pixel(xc - x, yc - y, color);
self.draw_pixel(xc + y, yc + x, color);
self.draw_pixel(xc - y, yc + x, color);
self.draw_pixel(xc + y, yc - x, color);
self.draw_pixel(xc - y, yc - x, color);
}
/// Draws a filled circle.
pub fn fill_circle(&mut self, xc: i32, yc: i32, r: i32, color: Color) {
if r < 0 { return; }
let mut x = 0;
let mut y = r;
let mut d = 3 - 2 * r;
self.draw_circle_lines(xc, yc, x, y, color);
while y >= x {
x += 1;
if d > 0 {
y -= 1;
d = d + 4 * (x - y) + 10;
} else {
d = d + 4 * x + 6;
}
self.draw_circle_lines(xc, yc, x, y, color);
}
}
fn draw_circle_lines(&mut self, xc: i32, yc: i32, x: i32, y: i32, color: Color) {
self.draw_horizontal_line(xc - x, xc + x, yc + y, color);
self.draw_horizontal_line(xc - x, xc + x, yc - y, color);
self.draw_horizontal_line(xc - y, xc + y, yc + x, color);
self.draw_horizontal_line(xc - y, xc + y, yc - x, color);
}
/// Draws a disc (filled circle with border).
pub fn draw_disc(&mut self, x: i32, y: i32, r: i32, border_color: Color, fill_color: Color) {
self.fill_circle(x, y, r, fill_color);
self.draw_circle(x, y, r, border_color);
}
/// Draws a rectangle outline.
pub fn draw_rect(&mut self, x: i32, y: i32, w: i32, h: i32, color: Color) {
if w <= 0 || h <= 0 { return; }
self.draw_horizontal_line(x, x + w - 1, y, color);
self.draw_horizontal_line(x, x + w - 1, y + h - 1, color);
self.draw_vertical_line(x, y, y + h - 1, color);
self.draw_vertical_line(x + w - 1, y, y + h - 1, color);
}
/// Draws a square (filled rectangle with border).
pub fn draw_square(&mut self, x: i32, y: i32, w: i32, h: i32, border_color: Color, fill_color: Color) {
self.fill_rect(x, y, w, h, fill_color);
self.draw_rect(x, y, w, h, border_color);
}
fn draw_horizontal_line(&mut self, x0: i32, x1: i32, y: i32, color: Color) {
if y < 0 || y >= self.h as i32 { return; }
let start = x0.max(0);
let end = x1.min(self.w as i32 - 1);
if start > end { return; }
for x in start..=end {
self.back[y as usize * self.w + x as usize] = color.0;
}
}
fn draw_vertical_line(&mut self, x: i32, y0: i32, y1: i32, color: Color) {
if x < 0 || x >= self.w as i32 { return; }
let start = y0.max(0);
let end = y1.min(self.h as i32 - 1);
if start > end { return; }
for y in start..=end {
self.back[y as usize * self.w + x as usize] = color.0;
}
}
/// Double buffer swap (O(1), no pixel copying).
/// Typically called by the Host when it's time to display the finished frame.
pub fn present(&mut self) {
@ -492,15 +622,64 @@ impl Gfx {
if (row >> (2 - col_idx)) & 1 == 1 {
let px = x + col_idx as i32;
let py = y + row_idx as i32;
if px >= 0 && px < self.w as i32 && py >= 0 && py < self.h as i32 {
self.back[py as usize * self.w + px as usize] = color.0;
}
self.draw_pixel(px, py, color);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_draw_pixel() {
let mut gfx = Gfx::new(10, 10);
gfx.draw_pixel(5, 5, Color::WHITE);
assert_eq!(gfx.back[5 * 10 + 5], Color::WHITE.0);
// Out of bounds should not panic
gfx.draw_pixel(-1, -1, Color::WHITE);
gfx.draw_pixel(10, 10, Color::WHITE);
}
#[test]
fn test_draw_line() {
let mut gfx = Gfx::new(10, 10);
gfx.draw_line(0, 0, 9, 9, Color::WHITE);
assert_eq!(gfx.back[0], Color::WHITE.0);
assert_eq!(gfx.back[9 * 10 + 9], Color::WHITE.0);
}
#[test]
fn test_draw_rect() {
let mut gfx = Gfx::new(10, 10);
gfx.draw_rect(0, 0, 10, 10, Color::WHITE);
assert_eq!(gfx.back[0], Color::WHITE.0);
assert_eq!(gfx.back[9], Color::WHITE.0);
assert_eq!(gfx.back[90], Color::WHITE.0);
assert_eq!(gfx.back[99], Color::WHITE.0);
}
#[test]
fn test_fill_circle() {
let mut gfx = Gfx::new(10, 10);
gfx.fill_circle(5, 5, 2, Color::WHITE);
assert_eq!(gfx.back[5 * 10 + 5], Color::WHITE.0);
}
#[test]
fn test_draw_square() {
let mut gfx = Gfx::new(10, 10);
gfx.draw_square(2, 2, 6, 6, Color::WHITE, Color::BLACK);
// Border
assert_eq!(gfx.back[2 * 10 + 2], Color::WHITE.0);
// Fill
assert_eq!(gfx.back[3 * 10 + 3], Color::BLACK.0);
}
}
/// Blends in RGB565 per channel with saturation.
/// `dst` and `src` are RGB565 pixels (u16).
fn blend_rgb565(dst: u16, src: u16, mode: BlendMode) -> u16 {

View File

@ -580,6 +580,52 @@ impl NativeInterface for PrometeuOS {
hw.gfx_mut().fill_rect(x, y, w, h, color);
Ok(200)
}
// gfx.draw_line(x1, y1, x2, y2, color_index)
0x1003 => {
let color_idx = vm.pop_integer()? as usize;
let y2 = vm.pop_integer()? as i32;
let x2 = vm.pop_integer()? as i32;
let y1 = vm.pop_integer()? as i32;
let x1 = vm.pop_integer()? as i32;
let color = self.get_color(color_idx, hw);
hw.gfx_mut().draw_line(x1, y1, x2, y2, color);
Ok(200)
}
// gfx.draw_circle(x, y, r, color_index)
0x1004 => {
let color_idx = vm.pop_integer()? as usize;
let r = vm.pop_integer()? as i32;
let y = vm.pop_integer()? as i32;
let x = vm.pop_integer()? as i32;
let color = self.get_color(color_idx, hw);
hw.gfx_mut().draw_circle(x, y, r, color);
Ok(200)
}
// gfx.draw_disc(x, y, r, border_color_idx, fill_color_idx)
0x1005 => {
let fill_color_idx = vm.pop_integer()? as usize;
let border_color_idx = vm.pop_integer()? as usize;
let r = vm.pop_integer()? as i32;
let y = vm.pop_integer()? as i32;
let x = vm.pop_integer()? as i32;
let fill_color = self.get_color(fill_color_idx, hw);
let border_color = self.get_color(border_color_idx, hw);
hw.gfx_mut().draw_disc(x, y, r, border_color, fill_color);
Ok(300)
}
// gfx.draw_square(x, y, w, h, border_color_idx, fill_color_idx)
0x1006 => {
let fill_color_idx = vm.pop_integer()? as usize;
let border_color_idx = vm.pop_integer()? as usize;
let h = vm.pop_integer()? as i32;
let w = vm.pop_integer()? as i32;
let y = vm.pop_integer()? as i32;
let x = vm.pop_integer()? as i32;
let fill_color = self.get_color(fill_color_idx, hw);
let border_color = self.get_color(border_color_idx, hw);
hw.gfx_mut().draw_square(x, y, w, h, border_color, fill_color);
Ok(200)
}
// --- Input Syscalls ---

View File

@ -213,6 +213,10 @@ Heap is:
| `0x0002` | `system.run_cart` | - | - |
| `0x1001` | `gfx.clear` | `color_idx` | - |
| `0x1002` | `gfx.draw_rect` | `x, y, w, h, color_idx` | - |
| `0x1003` | `gfx.draw_line` | `x1, y1, x2, y2, color_idx` | - |
| `0x1004` | `gfx.draw_circle` | `xc, yc, r, color_idx` | - |
| `0x1005` | `gfx.draw_disc` | `xc, yc, r, b_col, f_col` | - |
| `0x1006` | `gfx.draw_square` | `x, y, w, h, b_col, f_col` | - |
| `0x2001` | `input.get_pad` | `button_id` | `bool` |
| `0x3001` | `audio.play` | `s_id, v_id, vol, pan, pitch`| - |