kiss2d/
lib.rs

1#![feature(int_to_from_bytes, decl_macro, non_ascii_idents)]
2
3pub use minifb;
4pub use rusttype;
5//pub use image as ximage;
6//pub extern crate image as ximage;
7
8pub mod meter;
9pub mod wu;
10pub mod image;
11pub mod vg;
12pub mod clrs;
13pub mod geom;
14
15use minifb::{Window, MouseMode};
16use rusttype::{point, Scale};
17
18use self::image::{Rectangle, RGBA};
19
20pub use minifb::{Key, MouseButton, CursorStyle};
21pub use rusttype::Font;
22
23pub type Point = (isize, isize);
24
25pub struct Canvas {
26    buffer: Vec<u32>,
27    window: Window,
28    size: (usize, usize),
29}
30
31impl std::ops::Deref for Canvas {
32    type Target = [u32];
33    fn deref(&self) -> &[u32] { &self.buffer }
34}
35
36impl std::ops::DerefMut for Canvas {
37    fn deref_mut(&mut self) -> &mut [u32] { &mut self.buffer }
38}
39
40impl Canvas {
41    pub fn new(title: &str, width: usize, height: usize) -> minifb::Result<Self> {
42        let buffer: Vec<u32> = vec![0; width * height];
43        let window = Window::new(title, width, height, Default::default())?;
44
45        Ok(Self { buffer, window, size: (width, height) })
46    }
47
48    pub fn window(&self) -> &Window { &self.window }
49    pub fn window_mut(&mut self) -> &mut Window { &mut self.window }
50    pub fn buffer(&self) -> &[u32] { &self.buffer }
51    pub fn buffer_mut(&mut self) -> &mut [u32] { &mut self.buffer }
52
53    pub fn image_mut(&mut self) -> RGBA {
54        let (w, h) = self.size;
55        let r = Rectangle::from_size(w as isize, h as isize);
56        RGBA::from_buf32(&mut self.buffer, r)
57    }
58
59    pub fn size(&self) -> (usize, usize) { self.size }
60
61    pub fn is_open(&self) -> bool { self.window.is_open() }
62    pub fn is_keydown(&self, key: Key) -> bool { self.window.is_key_down(key) }
63
64    pub fn set_cursor_style(&mut self, cursor: CursorStyle) {
65        self.window.set_cursor_style(cursor)
66    }
67
68    pub fn keys<F: FnMut(Key)>(&self, f: F) {
69        self.window.get_keys()
70            .map(|mut keys| keys.drain(..).for_each(f));
71    }
72
73    pub fn mouse_pos(&self) -> Option<(f32, f32)> {
74        self.window.get_mouse_pos(MouseMode::Pass)
75    }
76
77    pub fn mouse_down(&self, button: MouseButton) -> bool {
78        self.window.get_mouse_down(button)
79    }
80
81    pub fn mouse_wheel(&self) -> Option<(f32, f32)> {
82        self.window.get_scroll_wheel()
83    }
84
85    pub fn udpate(&mut self) {
86        self.window.update()
87    }
88
89    pub fn redraw(&mut self) -> minifb::Result<()> {
90        self.window.update_with_buffer(&self.buffer)
91    }
92
93    pub fn clear(&mut self) {
94        self.fill(0);
95    }
96
97    pub fn fill(&mut self, color: u32) {
98        self.buffer.iter_mut().for_each(|i| *i = color);
99    }
100
101    pub fn pixel_mut(&mut self, x: usize, y: usize) -> &mut u32 {
102        let (w, h) = self.size();
103        assert!(x < w && y < h, "{}x{}", x, y);
104        let idx = x + y * w;
105        unsafe { self.buffer.get_unchecked_mut(idx) }
106    }
107
108    pub fn pixel(&mut self, x: usize, y: usize, color: u32) {
109        let (w, h) = self.size();
110        if x < w && y < h {
111            let idx = x + y * w;
112            unsafe { *self.buffer.get_unchecked_mut(idx) = color; }
113        }
114    }
115
116    pub fn line(&mut self, start: Point, end: Point, color: u32) {
117        let (w, h) = self.size();
118        let (w, h) = (w as isize, h as isize);
119        wu::clipped_aaline(start, end, (w, h), |x, y, v| {
120            if x >= 0 && x < w && y >= 0 && y < h {
121                let idx = (x + y * w) as usize;
122                unsafe { self.blend(idx, color, v as f32) }
123            }
124        })
125    }
126
127    pub fn hline(&mut self, x1: isize, x2: isize, y: isize, color: u32) {
128        let (w, h) = self.size();
129        let (w, h) = (w as isize, h as isize);
130
131        if y < 0 || y >= h { return }
132        let x1 = x1.max(0);
133        let x2 = x2.min(w);
134
135        for x in x1..x2 {
136            let idx = (x + y * w) as usize;
137            unsafe { *self.buffer.get_unchecked_mut(idx) = color; }
138        }
139    }
140
141    pub fn vline(&mut self, x: isize, y1: isize, y2: isize, color: u32) {
142        let (w, h) = self.size();
143        let (w, h) = (w as isize, h as isize);
144
145        if x < 0 || x >= w { return }
146        let y1 = y1.max(0);
147        let y2 = y2.min(h);
148
149        for y in y1..y2 {
150            let idx = (x + y * w) as usize;
151            unsafe { *self.buffer.get_unchecked_mut(idx) = color; }
152        }
153    }
154
155    pub fn text(&mut self, font: &Font, scale: f32, pos: (f32, f32), color: u32, text: &str) {
156        let scale = Scale::uniform(scale);
157        let v_metrics = font.v_metrics(scale);
158        let (w, h) = self.size();
159
160        for (line, text) in text.lines().enumerate() {
161            let point = point(pos.0, pos.1 + v_metrics.ascent * (line + 1) as f32);
162            for glyph in font.layout(text, scale, point) {
163                if let Some(bbox) = glyph.pixel_bounding_box() {
164                    glyph.draw(|x, y, v| {
165                        let x = (x + bbox.min.x as u32) as usize;
166                        let y = (y + bbox.min.y as u32) as usize;
167                        if v != 0.0 && x < w && y < h {
168                            unsafe {
169                                self.blend(x + y * w, color, v);
170                            }
171                        }
172                    });
173                }
174            }
175        }
176    }
177
178    pub fn circle(&mut self, pos: Point, radius: usize, color: u32) {
179        const PI2: f32 = std::f32::consts::PI * 2.0;
180        let nsamples = 16;
181        let fsamples = nsamples as f32;
182
183        let radius = radius as f32;
184        let pos = (pos.0 as f32, pos.1 as f32);
185
186        self.curve(color, true, (0..nsamples).map(|i| {
187            let (ax, ay) = ((i as f32) / fsamples * PI2).sin_cos();
188            let (ax, ay) = (pos.0 + ax * radius, pos.1 + ay * radius);
189            (ax as isize, ay as isize)
190        }));
191    }
192
193    pub fn curve<I: IntoIterator<Item=Point>>(&mut self, color: u32, loopped: bool, pts: I) {
194        let mut pts = pts.into_iter();
195        let first = if let Some(p) = pts.next() { p } else { return };
196
197        let mut base = first;
198        for p in pts {
199            self.line(base, p, color);
200            base = p;
201        }
202
203        if loopped {
204            self.line(base, first, color);
205        }
206    }
207
208    unsafe fn blend(&mut self, idx: usize, color: u32, alpha: f32) {
209        // http://stackoverflow.com/questions/7438263/alpha-compositing-algorithm-blend-modes#answer-11163848
210        const MAX_T: f32 = 255.0;
211
212        let pixel = self.buffer.get_unchecked_mut(idx);
213
214        let [db, dg, dr, _] = pixel.to_le_bytes();
215        let [sb, sg, sr, _] = color.to_le_bytes();
216        let (dr, dg, db) = (
217            dr as f32 / MAX_T,
218            dg as f32 / MAX_T,
219            db as f32 / MAX_T,
220        );
221        let (sr, sg, sb) = (
222            sr as f32 / MAX_T,
223            sg as f32 / MAX_T,
224            sb as f32 / MAX_T,
225        );
226
227        let inv_alpha = 1.0 - alpha;
228        let (r, g, b) = (
229            ((sr * alpha + dr * inv_alpha) * MAX_T) as u8,
230            ((sg * alpha + dg * inv_alpha) * MAX_T) as u8,
231            ((sb * alpha + db * inv_alpha) * MAX_T) as u8,
232        );
233
234        // Cast back to our initial type on return
235        *pixel = u32::from_le_bytes([b, g, r, 0xFF]);
236    }
237}