1use super::internal::UnmanagedBlob;
2use super::{AlphaFormat, ILockedFramebuffer, PixelFormat};
3use crate::{PixelSize, Vector};
4use std::cell::RefCell;
5use std::rc::Rc;
6
7pub struct RetainedFramebuffer {
13 size: PixelSize,
14 row_bytes: i32,
15 format: PixelFormat,
16 alpha_format: AlphaFormat,
17 blob: RefCell<Option<Rc<UnmanagedBlob>>>,
18}
19
20fn validate_known_format(format: PixelFormat) -> PixelFormat {
21 if format.bits_per_pixel() % 8 == 0 {
22 format
23 } else {
24 panic!("Specified argument was out of the range of valid values. (Parameter 'format')");
25 }
26}
27
28impl RetainedFramebuffer {
29 pub fn new(size: PixelSize, format: PixelFormat, alpha_format: AlphaFormat) -> Rc<Self> {
35 let format = validate_known_format(format);
36 Self::with_row_bytes(size, format, alpha_format, format.bits_per_pixel() as i32 / 8 * size.width)
37 }
38
39 pub fn with_row_bytes(size: PixelSize, format: PixelFormat, alpha_format: AlphaFormat, row_bytes: i32) -> Rc<Self> {
45 if size.width <= 0 || size.height <= 0 {
46 panic!("Specified argument was out of the range of valid values. (Parameter 'size')");
47 }
48 if size.width * (format.bits_per_pixel() as i32 / 8) > row_bytes {
49 panic!("Specified argument was out of the range of valid values. (Parameter 'rowBytes')");
50 }
51 Rc::new(Self {
52 size,
53 row_bytes,
54 format,
55 alpha_format,
56 blob: RefCell::new(Some(Rc::new(UnmanagedBlob::new(row_bytes * size.height)))),
57 })
58 }
59
60 pub fn size(&self) -> PixelSize {
62 self.size
63 }
64
65 pub fn row_bytes(&self) -> i32 {
67 self.row_bytes
68 }
69
70 pub fn format(&self) -> PixelFormat {
72 self.format
73 }
74
75 pub fn alpha_format(&self) -> AlphaFormat {
77 self.alpha_format
78 }
79
80 pub fn address(&self) -> *mut u8 {
85 self.blob().address()
86 }
87
88 fn blob(&self) -> Rc<UnmanagedBlob> {
89 match self.blob.borrow().as_ref() {
90 Some(blob) => blob.clone(),
91 None => panic!("Cannot access a disposed object: RetainedFramebuffer"),
92 }
93 }
94
95 pub fn lock(self: &Rc<Self>, dpi: Vector, blit: impl Fn(&Rc<RetainedFramebuffer>) + 'static) -> Rc<dyn ILockedFramebuffer> {
101 let blob = self.blob();
102 let this = self.clone();
103 Rc::new(RetainedLockedFramebuffer {
104 address: blob.address(),
105 blob,
106 size: self.size,
107 row_bytes: self.row_bytes,
108 dpi,
109 format: self.format,
110 alpha_format: self.alpha_format,
111 on_dispose: Box::new(move || blit(&this)),
112 })
113 }
114
115 pub fn dispose(&self) {
117 if let Some(blob) = self.blob.borrow_mut().take() {
118 blob.dispose();
119 }
120 }
121}
122
123struct RetainedLockedFramebuffer {
125 address: *mut u8,
126 blob: Rc<UnmanagedBlob>,
127 size: PixelSize,
128 row_bytes: i32,
129 dpi: Vector,
130 format: PixelFormat,
131 alpha_format: AlphaFormat,
132 on_dispose: Box<dyn Fn()>,
133}
134
135impl ILockedFramebuffer for RetainedLockedFramebuffer {
136 fn address(&self) -> *mut u8 {
137 self.address
138 }
139
140 fn with_data(&self, access: &mut dyn FnMut(&mut [u8])) {
141 if !self.blob.with_data(access) {
142 access(&mut []);
143 }
144 }
145
146 fn size(&self) -> PixelSize {
147 self.size
148 }
149
150 fn row_bytes(&self) -> i32 {
151 self.row_bytes
152 }
153
154 fn dpi(&self) -> Vector {
155 self.dpi
156 }
157
158 fn format(&self) -> PixelFormat {
159 self.format
160 }
161
162 fn alpha_format(&self) -> AlphaFormat {
163 self.alpha_format
164 }
165
166 fn dispose(&self) {
167 (self.on_dispose)();
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use crate::platform::PixelFormats;
175 use std::cell::Cell;
176
177 #[test]
178 fn row_bytes_default_to_the_width_in_bytes() {
179 let framebuffer = RetainedFramebuffer::new(PixelSize::new(10, 4), PixelFormats::RGBA8888, AlphaFormat::Premul);
180
181 assert_eq!(PixelSize::new(10, 4), framebuffer.size());
182 assert_eq!(40, framebuffer.row_bytes());
183 assert_eq!(PixelFormats::RGBA8888, framebuffer.format());
184 assert_eq!(AlphaFormat::Premul, framebuffer.alpha_format());
185 assert!(!framebuffer.address().is_null());
186 }
187
188 #[test]
189 fn a_sixteen_bit_format_takes_two_bytes_per_pixel() {
190 let framebuffer = RetainedFramebuffer::new(PixelSize::new(3, 2), PixelFormats::RGB565, AlphaFormat::Opaque);
191
192 assert_eq!(6, framebuffer.row_bytes());
193 }
194
195 #[test]
196 fn lock_describes_the_retained_memory_and_blits_on_every_dispose() {
197 let framebuffer = RetainedFramebuffer::with_row_bytes(
198 PixelSize::new(2, 2),
199 PixelFormats::BGRA8888,
200 AlphaFormat::Premul,
201 12,
202 );
203 let blitted = Rc::new(Cell::new(0));
204 let seen = blitted.clone();
205 let expected = framebuffer.address();
206
207 let locked = framebuffer.lock(Vector::new(96.0, 192.0), move |fb| {
208 assert_eq!(12, fb.row_bytes());
209 seen.set(seen.get() + 1);
210 });
211
212 assert_eq!(expected, locked.address());
213 assert_eq!(PixelSize::new(2, 2), locked.size());
214 assert_eq!(12, locked.row_bytes());
215 assert_eq!(Vector::new(96.0, 192.0), locked.dpi());
216 assert_eq!(PixelFormats::BGRA8888, locked.format());
217 assert_eq!(AlphaFormat::Premul, locked.alpha_format());
218 let mut length = 0;
219 locked.with_data(&mut |data| {
220 length = data.len();
221 data[0] = 7;
222 });
223 assert_eq!(24, length);
224 assert_eq!(0, blitted.get());
225
226 locked.dispose();
227 assert_eq!(1, blitted.get());
228
229 locked.dispose();
231 assert_eq!(2, blitted.get());
232 }
233
234 #[test]
235 fn pixels_survive_between_locks() {
236 let framebuffer = RetainedFramebuffer::new(PixelSize::new(1, 1), PixelFormats::RGBA8888, AlphaFormat::Premul);
237
238 let first = framebuffer.lock(Vector::new(96.0, 96.0), |_| {});
239 first.with_data(&mut |data| data.copy_from_slice(&[1, 2, 3, 4]));
240 first.dispose();
241
242 let second = framebuffer.lock(Vector::new(96.0, 96.0), |_| {});
243 let mut read = Vec::new();
244 second.with_data(&mut |data| read = data.to_vec());
245 assert_eq!(vec![1, 2, 3, 4], read);
246 }
247
248 #[test]
249 #[should_panic(expected = "Parameter 'format'")]
250 fn a_format_with_partial_bytes_is_rejected() {
251 RetainedFramebuffer::new(PixelSize::new(8, 8), PixelFormats::BLACK_WHITE, AlphaFormat::Opaque);
252 }
253
254 #[test]
255 #[should_panic(expected = "Parameter 'size'")]
256 fn an_empty_size_is_rejected() {
257 RetainedFramebuffer::new(PixelSize::new(0, 8), PixelFormats::RGBA8888, AlphaFormat::Premul);
258 }
259
260 #[test]
261 #[should_panic(expected = "Parameter 'rowBytes'")]
262 fn rows_shorter_than_their_pixels_are_rejected() {
263 RetainedFramebuffer::with_row_bytes(PixelSize::new(4, 4), PixelFormats::RGBA8888, AlphaFormat::Premul, 15);
264 }
265
266 #[test]
267 #[should_panic(expected = "Cannot access a disposed object: RetainedFramebuffer")]
268 fn a_disposed_framebuffer_cannot_be_locked() {
269 let framebuffer = RetainedFramebuffer::new(PixelSize::new(1, 1), PixelFormats::RGBA8888, AlphaFormat::Premul);
270 framebuffer.dispose();
271 framebuffer.dispose();
272 framebuffer.lock(Vector::new(96.0, 96.0), |_| {});
273 }
274}