1use core::cell::RefMut;
4
5use super::{Inner, Pixels, Surface, as_bytes_mut};
6use crate::colour::Rgba;
7use crate::geometry::Rect;
8
9pub(crate) struct Canvas<'a> {
12 pub(crate) slot: &'a mut [u32],
13 pub(crate) width: u32,
14 pub(crate) height: u32,
15 pub(crate) dirty: &'a mut super::DirtyRects,
16}
17
18impl Canvas<'_> {
19 #[inline]
23 pub(crate) fn blend(&mut self, x: i64, y: i64, colour: Rgba, coverage: u8) {
24 if x < 0 || y < 0 || x >= i64::from(self.width) || y >= i64::from(self.height) {
25 return;
26 }
27 let index = y as usize * self.width as usize + x as usize;
28 let below = Rgba::from_word(self.slot[index]);
29 self.slot[index] = colour.over(below, coverage).to_word();
30 }
31
32 pub(crate) fn touch(&mut self, x0: i64, y0: i64, x1: i64, y1: i64) {
34 let clamp_x = |v: i64| v.clamp(0, i64::from(self.width)) as u32;
35 let clamp_y = |v: i64| v.clamp(0, i64::from(self.height)) as u32;
36 let (left, right) = (clamp_x(x0), clamp_x(x1));
37 let (top, bottom) = (clamp_y(y0), clamp_y(y1));
38 if left < right && top < bottom {
39 self.dirty.add(
40 Rect::new(left, top, right - left, bottom - top),
41 self.width,
42 self.height,
43 );
44 }
45 }
46
47 pub(crate) fn fill(&mut self, x0: i64, y0: i64, x1: i64, y1: i64, colour: Rgba) {
49 let left = x0.clamp(0, i64::from(self.width)) as usize;
50 let right = x1.clamp(0, i64::from(self.width)) as usize;
51 let top = y0.clamp(0, i64::from(self.height)) as usize;
52 let bottom = y1.clamp(0, i64::from(self.height)) as usize;
53 if left >= right || top >= bottom {
54 return;
55 }
56 let stride = self.width as usize;
57 for y in top..bottom {
58 let row = &mut self.slot[y * stride + left..y * stride + right];
59 if colour.a == 255 {
60 row.fill(colour.to_word());
61 } else {
62 for word in row {
63 *word = colour.over(Rgba::from_word(*word), 255).to_word();
64 }
65 }
66 }
67 self.touch(x0, y0, x1, y1);
68 }
69}
70
71impl Inner {
72 pub(crate) fn canvas(&mut self) -> Option<Canvas<'_>> {
73 let (slot, layout, dirty) = self.back()?;
74 Some(Canvas {
75 slot,
76 width: layout.geometry.px_w,
77 height: layout.geometry.px_h,
78 dirty,
79 })
80 }
81}
82
83impl Surface<Pixels> {
84 pub(crate) fn with_canvas<R>(&mut self, draw: impl FnOnce(&mut Canvas<'_>) -> R) -> Option<R> {
86 let mut inner = self.inner.borrow_mut();
87 let mut canvas = inner.canvas()?;
88 if canvas.width == 0 || canvas.height == 0 {
89 return None;
90 }
91 Some(draw(&mut canvas))
92 }
93
94 pub fn set_pixel(&mut self, x: u32, y: u32, colour: Rgba) {
96 self.with_canvas(|canvas| {
97 if x < canvas.width && y < canvas.height {
98 canvas.slot[y as usize * canvas.width as usize + x as usize] = colour.to_word();
99 canvas.touch(
100 i64::from(x),
101 i64::from(y),
102 i64::from(x) + 1,
103 i64::from(y) + 1,
104 );
105 }
106 });
107 }
108
109 pub fn blend_pixel(&mut self, x: i32, y: i32, colour: Rgba) {
111 self.with_canvas(|canvas| {
112 let (x, y) = (i64::from(x), i64::from(y));
113 canvas.blend(x, y, colour, 255);
114 canvas.touch(x, y, x + 1, y + 1);
115 });
116 }
117
118 pub fn pixel(&self, x: u32, y: u32) -> Option<Rgba> {
120 let mut inner = self.inner.borrow_mut();
121 let canvas = inner.canvas()?;
122 (x < canvas.width && y < canvas.height)
123 .then(|| Rgba::from_word(canvas.slot[y as usize * canvas.width as usize + x as usize]))
124 }
125
126 pub fn clear(&mut self, colour: Rgba) {
129 let mut inner = self.inner.borrow_mut();
130 if let Some((slot, _)) = inner.back_discard() {
131 slot.fill(colour.to_word());
132 }
133 }
134
135 pub fn blit(&mut self, x: i32, y: i32, width: u32, height: u32, rgba: &[u8]) {
140 let everything = crate::draw::Area::new(i32::MIN, i32::MIN, u32::MAX, u32::MAX);
141 self.blit_clipped(
142 x,
143 y,
144 crate::draw::Image::new(width, height, rgba),
145 everything,
146 false,
147 );
148 }
149
150 pub fn pixels_mut(&mut self) -> RefMut<'_, [u8]> {
155 let mut inner = self.inner.borrow_mut();
156 let (width, height) = inner.extent();
157 inner.mark_dirty(Rect::new(0, 0, width, height));
158 RefMut::map(inner, |inner| match inner.back() {
159 Some((slot, _, _)) => as_bytes_mut(slot),
160 None => &mut [],
161 })
162 }
163
164 pub fn pixels_untracked(&mut self) -> RefMut<'_, [u8]> {
167 RefMut::map(self.inner.borrow_mut(), |inner| match inner.back() {
168 Some((slot, _, _)) => as_bytes_mut(slot),
169 None => &mut [],
170 })
171 }
172
173 pub fn repaint_all(&mut self) -> RefMut<'_, [u8]> {
178 RefMut::map(self.inner.borrow_mut(), |inner| {
179 match inner.back_discard() {
180 Some((slot, _)) => as_bytes_mut(slot),
181 None => &mut [],
182 }
183 })
184 }
185}