Skip to content

Commit c91bdbb

Browse files
committed
feat: add blit, lines
1 parent 5c3929b commit c91bdbb

7 files changed

Lines changed: 213 additions & 44 deletions

File tree

Cargo.lock

Lines changed: 61 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ softbuffer = "0.4.8"
1414
tracing = "0.1.44"
1515
vex-sdk = "0.28.0"
1616
winit = "0.30.12"
17+
embedded-graphics = { version = "0.8.2", default-features = false }
1718

1819
[target.'cfg(target_os = "macos")'.dependencies]
1920
dispatch2 = "0.3.1"
@@ -23,5 +24,6 @@ objc2-app-kit = { version = "0.3.2", features = ["objc2-core-graphics", "NSImage
2324
objc2-core-graphics = { version = "0.3.2", features = ["CGDataProvider", "CGColorSpace"] }
2425

2526
[dev-dependencies]
27+
tinybmp = "0.7.0"
2628
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
2729
vexide = { version = "0.8.0", features = ["full"] }

examples/airplane.bmp

768 KB
Binary file not shown.

examples/display.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
use std::{thread, time::Duration};
1+
use std::{
2+
f64::consts::{PI, TAU},
3+
thread,
4+
time::Duration,
5+
};
26

7+
use embedded_graphics::{
8+
pixelcolor::{Rgb888, raw::RawU24},
9+
prelude::RawData,
10+
};
11+
use tinybmp::Bmp;
312
use tracing_subscriber::EnvFilter;
413
use vex_sdk_desktop::sdk::*;
514

615
fn main() {
7-
// e.g. RUST_LOG=debug,vex_sdk_sim=trace
16+
// e.g. RUST_LOG=debug,vex_sdk_desktop=trace
817
tracing_subscriber::fmt()
918
.with_env_filter(EnvFilter::from_default_env())
1019
.init();
@@ -31,6 +40,27 @@ fn main() {
3140
vexDisplayCircleDraw(150, 100, 10);
3241
vexDisplayCircleFill(180, 100, 10);
3342

43+
const AIRPLANE: &[u8] = include_bytes!("airplane.bmp");
44+
let bmp = Bmp::<Rgb888>::from_slice(AIRPLANE).unwrap();
45+
let img_data = bmp
46+
.pixels()
47+
.map(|p| RawU24::from(p.1).into_inner())
48+
.collect::<Vec<u32>>();
49+
let width = bmp.as_raw().header().image_size.width;
50+
51+
let offset = 200 * width + 325;
52+
53+
unsafe {
54+
vexDisplayCopyRect(
55+
20,
56+
150,
57+
100,
58+
220,
59+
img_data.as_ptr().cast_mut().wrapping_add(offset as usize),
60+
width as i32,
61+
);
62+
}
63+
3464
let mut velocity = 0.0;
3565
let mut position = -50.0;
3666
loop {
@@ -44,6 +74,22 @@ fn main() {
4474
vexDisplayForegroundColor(0x00_FF_00);
4575
vexDisplayCircleFill(210, y, 10);
4676

77+
let angle = (position / 50.0 + 1.0) * PI;
78+
let (y, x) = angle.sin_cos();
79+
println!("({x}, {y})");
80+
81+
let x1 = 250 + (x * 20.0) as i32;
82+
let x2 = 250 - (x * 20.0) as i32;
83+
84+
let y1 = 100 + (y * 20.0) as i32;
85+
let y2 = 100 - (y * 20.0) as i32;
86+
87+
vexDisplayForegroundColor(0xFF_FF_FF);
88+
vexDisplayCircleFill(250, 100, 20);
89+
vexDisplayForegroundColor(0x00_00_00);
90+
println!("{:?}", (x1, y1, x2, y2));
91+
vexDisplayLineDraw(x1, y1, x2, y2);
92+
4793
vexDisplayRender(true, false);
4894
}
4995
})

src/canvas.rs

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,11 @@ use std::{
22
fmt::{self, Formatter},
33
mem,
44
ops::RangeInclusive,
5-
sync::atomic::AtomicBool,
65
};
76

87
use line_drawing::{Bresenham, BresenhamCircle};
98
use parking_lot::Mutex;
10-
use tracing::{debug, info, trace};
11-
12-
use crate::{
13-
SIM_APP, SimEvent,
14-
display::{SimDisplay, SimDisplayWindow},
15-
};
9+
use tracing::trace;
1610

1711
pub const WIDTH: u32 = 480;
1812
pub const HEIGHT: u32 = 272;
@@ -26,13 +20,13 @@ pub const HEADER_COLOR: u32 = 0x00_99_CC;
2620
/// The canvas instance used by user code.
2721
pub static CANVAS: Mutex<Canvas> = Mutex::new(Canvas::new());
2822

29-
/// Indicates whether the render thread should automatically render the [user canvas](CANVAS).
30-
pub static AUTORENDER: AtomicBool = AtomicBool::new(true);
31-
3223
#[derive(Debug, Clone, Copy)]
3324
pub struct CanvasState {
3425
pub fg_color: u32,
3526
pub bg_color: u32,
27+
// This doesn't seem to affect any operations, but we store it so we can return it from
28+
// `vexDisplayPenSizeGet`.
29+
pub pen_size: u32,
3630
clip_region: Rect,
3731
}
3832

@@ -45,6 +39,10 @@ impl CanvasState {
4539
region.clip_to(&Rect::FULL_CLIP);
4640
self.clip_region = region;
4741
}
42+
43+
pub fn clip_region(&self) -> Rect {
44+
self.clip_region
45+
}
4846
}
4947

5048
pub struct Canvas {
@@ -59,6 +57,7 @@ impl Canvas {
5957
fg_color: DEFAULT_FG_COLOR,
6058
bg_color: DEFAULT_BG_COLOR,
6159
clip_region: Rect::FULL_CLIP,
60+
pen_size: 1,
6261
};
6362

6463
Self {
@@ -96,7 +95,7 @@ impl Canvas {
9695
let clip = self.state.clip_region;
9796

9897
// Is the line above or below the clip region?
99-
if !(clip.0.y..=clip.1.y).contains(&y) {
98+
if !(clip.0.y..clip.1.y).contains(&y) {
10099
return;
101100
}
102101

@@ -117,7 +116,7 @@ impl Canvas {
117116
let clip = self.state.clip_region;
118117

119118
// Is the line left or right of the clip region?
120-
if !(clip.0.x..=clip.1.x).contains(&x) {
119+
if !(clip.0.x..clip.1.x).contains(&x) {
121120
return;
122121
}
123122

@@ -132,6 +131,14 @@ impl Canvas {
132131
}
133132
}
134133

134+
pub fn draw_line(&mut self, start: Point, end: Point) {
135+
trace!(?start, ?end, "line");
136+
137+
for (x, y) in Bresenham::new((start.x, start.y), (end.x, end.y)) {
138+
self.set_pixel(Point { x, y });
139+
}
140+
}
141+
135142
pub fn fill_rect(&mut self, mut bounds: Rect) {
136143
trace!(color = %Hex(self.state.fg_color), ?bounds, "fill rect");
137144

@@ -174,7 +181,7 @@ impl Canvas {
174181
let mut lines = vec![(center.x, center.x); num_lines as usize];
175182

176183
for (dx, i) in BresenhamCircle::new(0, radius as i32, radius as i32) {
177-
let x = center.x as i32 + dx;
184+
let x = center.x + dx;
178185

179186
// The tops and bottoms of circles have several points on the same line, so only record
180187
// the leftmost or rightmost point for our horizontal line.
@@ -205,13 +212,27 @@ impl Canvas {
205212

206213
let clip = self.state.clip_region;
207214

208-
for (x, y) in BresenhamCircle::new(center.x as i32, center.y as i32, radius as i32) {
215+
for (x, y) in BresenhamCircle::new(center.x, center.y, radius as i32) {
209216
if (Point { x, y }).is_inside(&clip) {
210217
self.write_pixel(Point { x, y }, self.state.fg_color);
211218
}
212219
}
213220
}
214221

222+
pub unsafe fn copy_rect(&mut self, mut bounds: Rect, source: *const u32, stride: usize) {
223+
trace!(?bounds, ?source, ?stride, "copy rect");
224+
bounds.clip_to(&self.state.clip_region);
225+
226+
for (row_idx, row) in (bounds.0.y..bounds.1.y).enumerate() {
227+
for (col_idx, col) in (bounds.0.x..bounds.1.x).enumerate() {
228+
let dest_idx = row * WIDTH as i32 + col;
229+
let source_idx = row_idx * stride + col_idx;
230+
let pixel = unsafe { source.add(source_idx).read() };
231+
self.buffer[dest_idx as usize] = pixel;
232+
}
233+
}
234+
}
235+
215236
pub fn draw_header(&mut self) {
216237
self.state.fg_color = HEADER_COLOR;
217238
self.fill_rect(Rect::HEADER_CLIP);
@@ -247,8 +268,8 @@ pub struct Rect(pub Point, pub Point);
247268

248269
impl Rect {
249270
pub const FULL_CLIP: Self = Rect::new(0, 0, WIDTH as i32, HEIGHT as i32);
250-
pub const USER_CLIP: Self = Rect::new(0, HEADER_HEIGHT as i32, WIDTH as i32, HEIGHT as i32);
251-
pub const HEADER_CLIP: Self = Rect::new(0, 0, WIDTH as i32, HEADER_HEIGHT as i32);
271+
pub const USER_CLIP: Self = Rect::new(0, HEADER_HEIGHT, WIDTH as i32, HEIGHT as i32);
272+
pub const HEADER_CLIP: Self = Rect::new(0, 0, WIDTH as i32, HEADER_HEIGHT);
252273

253274
pub const fn new(mut x0: i32, mut y0: i32, mut x1: i32, mut y1: i32) -> Self {
254275
if x0 > x1 {

0 commit comments

Comments
 (0)