diff --git a/ctru-rs/examples/camera-image.rs b/ctru-rs/examples/camera-image.rs index 4302325..e481181 100644 --- a/ctru-rs/examples/camera-image.rs +++ b/ctru-rs/examples/camera-image.rs @@ -9,9 +9,9 @@ const WIDTH: usize = 400; const HEIGHT: usize = 240; // The screen size is the width and height multiplied by 2 (RGB565 store pixels in 2 bytes) -// const BUF_SIZE: usize = WIDTH * HEIGHT * 2; +const BUF_SIZE: usize = WIDTH * HEIGHT * 2; -const WAIT_TIMEOUT: Duration = Duration::from_micros(300); +const WAIT_TIMEOUT: Duration = Duration::from_millis(300); fn main() { ctru::use_panic_handler(); @@ -56,7 +56,7 @@ fn main() { .expect("Failed to disable trimming"); } - let mut buf; + let mut buf = vec![0; BUF_SIZE]; println!("\nPress R to take a new picture"); println!("Press Start to exit to Homebrew Launcher"); @@ -74,8 +74,9 @@ fn main() { let camera = &mut cam.outer_right_cam; - buf = camera + camera .take_picture( + &mut buf, WIDTH.try_into().unwrap(), HEIGHT.try_into().unwrap(), WAIT_TIMEOUT, @@ -85,15 +86,12 @@ fn main() { cam.play_shutter_sound(CamShutterSoundType::NORMAL) .expect("Failed to play shutter sound"); - let img = rotate_image(&buf, WIDTH, HEIGHT); - - unsafe { - gfx.top_screen - .borrow_mut() - .get_raw_framebuffer() - .ptr - .copy_from(img.as_ptr(), img.len()); - } + rotate_image_to_screen( + &buf, + gfx.top_screen.borrow_mut().get_raw_framebuffer().ptr, + WIDTH, + HEIGHT, + ); gfx.flush_buffers(); gfx.swap_buffers(); @@ -104,9 +102,8 @@ fn main() { // The 3DS' screens are 2 vertical LCD panels rotated by 90 degrees. // As such, we'll need to write a "vertical" image to the framebuffer to have it displayed properly. -// This functions handles the rotation of an horizontal image to a vertical one. -fn rotate_image(img: &[u8], width: usize, height: usize) -> Vec { - let mut res = vec![0u8; img.len()]; +// This functions rotates an horizontal image by 90 degrees to the right. +fn rotate_image_to_screen(src: &[u8], framebuf: *mut u8, width: usize, height: usize) { for j in 0..height { for i in 0..width { // Y-coordinate of where to draw in the frame buffer @@ -121,9 +118,11 @@ fn rotate_image(img: &[u8], width: usize, height: usize) -> Vec { // Initial index of where to draw in the frame buffer based on y and x coordinates let draw_index = (draw_x * height + draw_y) * 2; // This 2 stands for the number of bytes per pixel (16 bits) - res[draw_index] = img[read_index]; - res[draw_index + 1] = img[read_index + 1]; + unsafe { + let pixel_pointer = framebuf.offset(draw_index as isize); + *pixel_pointer = src[read_index]; + *pixel_pointer.offset(1) = src[read_index + 1]; + } } } - res } diff --git a/ctru-rs/src/error.rs b/ctru-rs/src/error.rs index 9aaf410..5d1d28c 100644 --- a/ctru-rs/src/error.rs +++ b/ctru-rs/src/error.rs @@ -21,7 +21,11 @@ impl Try for ResultCode { } fn branch(self) -> ControlFlow { - if self.0 < 0 { + // Wait timeouts aren't counted as "failures" in libctru, but an unfinished task means unsafety for us. + // Luckily all summary cases are for system failures (except RS_SUCCESS). + // I don't know if there are any cases in libctru where a Result holds a "failing" summary but a "success" code, so we'll just check for both. + if ctru_sys::R_FAILED(self.0) || ctru_sys::R_SUMMARY(self.0) != ctru_sys::RS_SUCCESS as i32 + { ControlFlow::Break(self.into()) } else { ControlFlow::Continue(()) diff --git a/ctru-rs/src/services/cam.rs b/ctru-rs/src/services/cam.rs index 528a27f..c4c7737 100644 --- a/ctru-rs/src/services/cam.rs +++ b/ctru-rs/src/services/cam.rs @@ -704,12 +704,16 @@ pub trait Camera { } } - /// Requests the camera to take a picture and returns a vector containing the image bytes. + /// Requests the camera to take a picture and write it in a buffer. /// /// # Errors /// /// This will error if the camera is busy or if the timeout duration is reached. /// + /// # Panics + /// + /// This function will panic if `buffer.len() < (width * height * 2)`. + /// /// # Arguments /// /// * `width` - Width of the desired image @@ -717,10 +721,11 @@ pub trait Camera { /// * `timeout` - Duration to wait for the image fn take_picture( &mut self, + buffer: &mut [u8], width: u16, height: u16, timeout: Duration, - ) -> crate::Result> { + ) -> crate::Result<()> { let transfer_unit = unsafe { let mut buf_size = 0; ResultCode(ctru_sys::CAMU_GetMaxBytes( @@ -731,10 +736,6 @@ pub trait Camera { Ok::(buf_size) }?; - let screen_size = u32::from(width) * u32::from(height) * 2; - - let mut buf = vec![0u8; usize::try_from(screen_size).unwrap()]; - unsafe { ResultCode(ctru_sys::CAMU_SetTransferBytes( self.port_as_raw(), @@ -744,6 +745,11 @@ pub trait Camera { ))?; }; + let screen_size = u32::from(width) * u32::from(height) * 2; + if buffer.len() < screen_size as usize { + panic!("Provided buffer's length is shorter than the desired width and height matrix.") + } + unsafe { ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?; ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?; @@ -754,7 +760,7 @@ pub trait Camera { let mut completion_handle: Handle = 0; ResultCode(ctru_sys::CAMU_SetReceiving( &mut completion_handle, - buf.as_mut_ptr() as *mut ::libc::c_void, + buffer.as_mut_ptr() as *mut ::libc::c_void, self.port_as_raw(), screen_size, transfer_unit.try_into().unwrap(), @@ -763,16 +769,21 @@ pub trait Camera { }?; unsafe { - ResultCode(ctru_sys::svcWaitSynchronization( + // Panicking without closing an SVC handle causes an ARM exception, we have to handle it carefully (TODO: SVC module) + let wait_result = ResultCode(ctru_sys::svcWaitSynchronization( receive_event, timeout.as_nanos().try_into().unwrap(), - ))?; + )); + + // We close everything first, then we check for possible errors + ResultCode(ctru_sys::svcCloseHandle(receive_event)); ResultCode(ctru_sys::CAMU_StopCapture(self.port_as_raw()))?; - ResultCode(ctru_sys::svcCloseHandle(receive_event))?; ResultCode(ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE))?; + + wait_result?; }; - Ok(buf) + Ok(()) } }