kreida/core/
buffer.rs

1use std::slice;
2use std::ptr;
3
4use super::Color;
5use super::space::*;
6
7pub struct Buffer<'a> {
8    buf: &'a mut [Color],
9    width: usize,
10    height: usize,
11}
12
13impl<'a> Buffer<'a> {
14    pub fn from_row(ptr: *mut Color, len: usize, width: usize) -> Self {
15        if len % width != 0 {
16            unreachable!()
17        }
18        Buffer {
19            buf: unsafe { slice::from_raw_parts_mut(ptr, len) },
20            width,
21            height: len / width,
22        }
23    }
24
25    pub fn height(&self) -> usize {
26        self.height
27    }
28
29    pub fn width(&self) -> usize {
30        self.width
31    }
32
33    pub fn len(&self) -> usize {
34        self.buf.len()
35    }
36
37    pub fn clear(&mut self, color: Color) {
38        unsafe {
39            ptr::copy(&color, self.buf.as_mut_ptr(), self.buf.len());
40        }
41    }
42
43    pub fn fade(&mut self, k: u8) {
44        for pixel in self.buf.iter_mut() {
45            pixel.a = (pixel.a as u16 * k as u16 / 256) as u8;
46        }
47    }
48
49    pub fn point(&mut self, x: isize, y: isize, color: Color) {
50        if clip_value(x, 0, self.width as isize).is_fixed() ||
51            clip_value(y, 0, self.height as isize).is_fixed()
52        {
53            return;
54        }
55        self.unchecked_point(x as usize, y as usize, color);
56    }
57
58    #[inline]
59    pub fn unchecked_point(&mut self, x: usize, y: usize, color: Color) {
60        self.buf[y * self.width + x] = color;
61    }
62
63    pub fn bar<R>(&mut self, rect: R, color: Color)
64        where R: Into<Rect<isize>>
65    {
66        let (rect, _is_clipped) = rect.into()
67            .clip(((0, 0), (self.width as isize, self.height as isize)))
68            .into_inner_checked();
69
70        if rect.is_collapsed() {
71            return;
72        }
73
74        let (left, top) = (rect.a.x as usize, rect.a.y as usize);
75        let (right, bottom) = (rect.b.x as usize, rect.b.y as usize);
76        let width = right - left;
77
78        let mut start = top * self.width + left;
79        for _ in top .. bottom {
80            for v in &mut self.buf[start .. start + width] {
81                *v = color;
82            }
83            start += self.width;
84        }
85    }
86
87    /// Bresenham's line
88    pub fn line<PA, PB>(&mut self, a: PA, b: PB, color: Color)
89        where
90            PA: Into<Point<isize>>,
91            PB: Into<Point<isize>>,
92    {
93        let (a, b) = (a.into(), b.into());
94
95        if a.x == b.x || a.y == b.y {
96            self.bar((a, b), color);
97        }
98
99        let (a, b) =
100            if a.x > b.x { (b, a) } else { (a, b) };
101
102        let (transpose, a, b) =
103            if (b.y - a.y).abs() > (b.x - a.x).abs() {
104                (true, a.transpose(), b.transpose())
105            } else {
106                (false, a, b)
107            };
108
109        let (a, b) =
110            if a.x > b.x { (b, a) } else { (a, b) };
111
112        debug_assert!(b.x >= a.x);
113
114        let dx = b.x - a.x;
115        let dy = b.y - a.y;
116
117        //debug_assert!(dy.abs() < dx.abs());
118
119        let (sy, sd) = if dy > 0 {
120            (1, 2 * dy)
121        } else {
122            (-1, - 2 * dy)
123        };
124
125        let mut d: isize = sd - dx;
126        let mut y: isize = a.y;
127
128        for x in a.x .. b.x {
129            let (tx, ty) = if transpose { (y, x) } else { (x, y) };
130            self.point(tx, ty, color);
131            if d > 0 {
132                y += sy;
133                d -= 2 * dx;
134            }
135            d += sd;
136        }
137    }
138
139    pub fn rect(&mut self, x1: isize, y1: isize, x2: isize, y2: isize, color: Color) {
140        let x1 = clip_value(x1, 0, self.width as isize).into_inner();
141        let x2 = clip_value(x2, 0, self.width as isize).into_inner();
142        if x1 == x2 {
143            return;
144        }
145        let y1 = clip_value(y1, 0, self.height as isize).into_inner();
146        let y2 = clip_value(y2, 0, self.height as isize).into_inner();
147        if y1 == y2 {
148            return;
149        }
150
151        let (left, right) = sort(x1 as usize, x2 as usize).into_inner();
152        let (top, bottom) = sort(y1 as usize, y2 as usize).into_inner();
153        let (right_1, bottom_1) = (right - 1, bottom - 1);
154
155        for x in left .. right {
156            self.unchecked_point(x, top, color);
157        }
158        if bottom_1 != top {
159            for x in left .. right {
160                self.unchecked_point(x, bottom_1, color);
161            }
162        }
163        for y in top .. bottom {
164            self.unchecked_point(left, y, color);
165        }
166        if right_1 != left {
167            for y in top .. bottom {
168                self.unchecked_point(right_1, y, color);
169            }
170        }
171    }
172}