micro_gui/graphics/
mod.rs

1//! Graphics module provides underlying rendering functions over a generic buffer
2//! This is used by components or widgets for rendering purposes.
3//!
4//! Copyright 2017 Ryan Kurte
5
6
7use std::marker::PhantomData;
8
9
10use crate::core::buffer::Buff;
11use crate::types::rect::Rect;
12use crate::types::point::Point;
13
14/// Renderable trait implemented by types that can render themselves
15/// For example, widgets should implement the renderable trait to be bound into layers
16pub trait Renderable<Pixel> {
17    fn render(&mut self, graphics: &mut Graphics<Pixel>, buffer: &mut Buff<Pixel>);
18}
19
20/// Sprite trait implemented by types that can be rendered from a buffer
21pub trait Sprite<Pixel> {
22    fn get(&mut self, x: usize, y: usize) -> &Pixel;
23    fn size(&self) -> (usize, usize);
24}
25
26
27/// Graphics context used for rendering components
28/// This includes offsets and dimensions to shift rendering scopes
29pub struct Graphics<Pixel> {
30    x: usize,
31    y: usize,
32    w: usize,
33    h: usize,
34    _pixel: PhantomData<Pixel>,
35}
36
37impl <Pixel>Graphics<Pixel> {
38    /// New creates a new graphcs context with the provided offsets and limits
39    pub fn new(x: usize, y: usize, w: usize, h: usize) -> Self {
40        return Self{x, y, w, h, _pixel: PhantomData};
41    }
42
43    /// Set wraps a buffer in a graphics context to shift rendering functions
44    pub fn set(&self, b: &mut Buff<Pixel>, x: usize, y: usize, p: &Pixel) {
45        let new_x = self.x + x;
46        let new_y = self.y + y;
47
48        if x < self.w && y < self.h {
49            b.set(new_x, new_y, p);
50        }
51    }
52
53    pub fn get_bounds(&mut self) -> Rect {
54        return Rect::new(self.x, self.y, self.w, self.h);
55    }
56
57    /// Set new bounds for rendering
58    /// This will offset and limit rendering by the provided values.
59    pub fn set_bounds(&mut self, bounds: &Rect) {
60        self.x = bounds.x;
61        self.y = bounds.y;
62        self.w = bounds.w;
63        self.h = bounds.h;
64    }
65
66    fn limit(min: usize, max: usize, actual: usize) -> usize {
67        if actual > max { return max }
68        if actual < min { return min }
69        return actual
70    }
71
72    /// Draws a line between two points with the provided pixel style
73    pub fn draw_line(&self, buf: &mut Buff<Pixel>, p1: Point, p2: Point, p: &Pixel) {
74        // Bresenham's line algorithm (https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm), implementation from:
75	    // https://www.opengl.org/discussion_boards/showthread.php/168761-Drawing-Line-Bresenhem-midpoint-algorithm
76
77        // Create local mutable copies of the required points
78        let mut a = p1;
79        let mut b = p2;
80        
81        // Limit points to drawable space
82        //a.x = self::limit(0, self.w, a.x);
83        //a.y = self::limit(0, self.h, a.y);
84        //b.x = self::limit(0, self.w, b.x);
85        //b.y = self::limit(0, self.h, b.y);
86
87        let mut delta_x = b.x as isize - a.x as isize;
88        let mut delta_y = b.y as isize - a.y as isize;
89
90        let sign_x = delta_x.signum();
91        let sign_y = delta_y.signum();
92
93        delta_x = delta_x.abs();
94        delta_y = delta_y.abs();
95
96        b.x = (b.x as isize + sign_x) as usize;
97        b.y = (b.y as isize + sign_y) as usize;
98
99        self.set(buf, a.x, a.y, p);
100
101        if delta_x > delta_y {
102            let mut accum = delta_x / 2;
103            loop {
104                self.set(buf, a.x, a.y, p);
105                accum -= delta_y;
106                if accum < 0 {
107                    accum += delta_x;
108                    a.y = (a.y as isize + sign_y) as usize;
109                }
110                a.x = (a.x as isize + sign_x) as usize;
111
112                if a.x == b.x { break; }
113            }
114            
115        } else {
116            let mut accum = delta_y / 2;
117            loop {
118                self.set(buf, a.x, a.y, p);
119
120                accum -= delta_x;
121                if accum < 0 {
122                    accum += delta_y;
123                     a.x = (a.x as isize + sign_x) as usize;
124                }
125                a.y = (a.y as isize + sign_y) as usize;
126
127                if a.y == b.y { break; }
128            }
129        }
130    }
131
132    /// Draws a rectange with the provided pixel style
133    pub fn draw_rect(&self, b: &mut Buff<Pixel>, r: Rect, p: &Pixel) {
134        for x in 0..r.w {
135            self.set(b, r.x + x, r.y, p);
136            self.set(b, r.x + x, r.y + r.h, p);
137        }
138        for y in 0..r.h {
139            self.set(b, r.x, r.y + y, p);
140            self.set(b, r.x + r.w, r.y + y, p);
141        }
142    }
143
144    /// Draws a rectangle with the provided pixel style
145    pub fn fill_rect(&self, b: &mut Buff<Pixel>, r: Rect, p: &Pixel) {
146        for y in 0..r.h {
147            for x in 0..r.w {
148                self.set(b, r.x + x, r.y + y, p);
149            }
150        }
151    }
152
153    /// Draws a polyline connecting a list of points
154    pub fn draw_polyline(&self, b: &mut Buff<Pixel>, points: &[Point], p: &Pixel) {
155        let len = points.len();
156        for i in 0..len-1 {
157            let p1 = points[i];
158            let p2 = points[i+1];
159            self.draw_line(b, p1, p2, p);
160        }
161    }
162
163    /// Draws an ellipse to fill the provided rectangle
164    pub fn draw_ellipse(&self, buf: &mut Buff<Pixel>, r: Rect, p: &Pixel) {
165        // Implementation also from:
166	    // https://www.opengl.org/discussion_boards/showthread.php/168761-Drawing-Line-Bresenhem-midpoint-algorithm
167
168        let left = (r.x) as isize;
169	    let right = (r.x + r.w) as isize;
170	    let top = (r.y) as isize;
171	    let bottom = (r.y + r.h) as isize;
172
173        let a = ((right - left + 1) / 2) as isize;
174	    let b = ((bottom - top + 1) / 2) as isize;
175
176        if a != 0 && b != 0 {
177            let a2 = (a * a) as isize;
178            let b2 = (b * b) as isize;
179            let two_a2 = (a2 * 2) as isize;
180            let two_b2 = (b2 * 2) as isize;
181            let four_a2 = (a2 * 4) as isize;
182            let four_b2 = (b2 * 4) as isize;
183            let mut x = 0 as isize;
184            let mut y = b as isize;
185            let mut s = a2 * (1 - (b * 2)) + two_b2;
186            let mut t = b2 - two_a2 * ((b * 2) - 1);
187
188            self.set(buf, (right + x - a) as usize, (bottom + y - b) as usize, p);
189	        self.set(buf, (left - x + a) as usize, (bottom + y - b) as usize, p);
190	        self.set(buf, (left - x + a) as usize, (top - y + b) as usize, p);
191	        self.set(buf, (right + x - a) as usize, (top - y + b) as usize, p);
192
193            loop {
194                if s < 0 {
195                    s += two_b2 * ((x << 1) + 3);
196                    t += four_b2 * (x + 1);
197                    x += 1;
198                } else if t < 0 {
199                    s += two_b2 * ((x << 1) + 3) - four_a2 * (y - 1);
200                    t += four_b2 * (x + 1) - two_a2 * ((y << 1) - 3);
201                    x += 1;
202                    y -= 1;
203                } else {
204                    s -= four_a2 * (y - 1);
205                    t -= two_a2 * ((y << 1) - 3);
206                    y -= 1;
207                }
208
209                self.set(buf, (right + x - a) as usize, (bottom + y - b) as usize, p);
210	            self.set(buf, (left - x + a) as usize, (bottom + y - b) as usize, p);
211	            self.set(buf, (left - x + a) as usize, (top - y + b) as usize, p);
212	            self.set(buf, (right + x - a) as usize, (top - y + b) as usize, p);
213
214                if y <= 0 { break; }
215            }
216
217        }
218    }
219
220    //pub fn draw_sprite(&self, b: &mut Buff<Pixel>, p: &point::Point, s: &sprite::Sprite) { }
221    //pub fn draw_text(&self, b: &mut Buff<Pixel>, ) { }
222}