Browse Source

Fix example and speedup performance

pull/94/head
Andrea Ciliberti 2 years ago
parent
commit
326cfde36f
  1. 37
      ctru-rs/examples/camera-image.rs
  2. 6
      ctru-rs/src/error.rs
  3. 33
      ctru-rs/src/services/cam.rs

37
ctru-rs/examples/camera-image.rs

@ -9,9 +9,9 @@ const WIDTH: usize = 400;
const HEIGHT: usize = 240; const HEIGHT: usize = 240;
// The screen size is the width and height multiplied by 2 (RGB565 store pixels in 2 bytes) // 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() { fn main() {
ctru::use_panic_handler(); ctru::use_panic_handler();
@ -56,7 +56,7 @@ fn main() {
.expect("Failed to disable trimming"); .expect("Failed to disable trimming");
} }
let mut buf; let mut buf = vec![0; BUF_SIZE];
println!("\nPress R to take a new picture"); println!("\nPress R to take a new picture");
println!("Press Start to exit to Homebrew Launcher"); println!("Press Start to exit to Homebrew Launcher");
@ -74,8 +74,9 @@ fn main() {
let camera = &mut cam.outer_right_cam; let camera = &mut cam.outer_right_cam;
buf = camera camera
.take_picture( .take_picture(
&mut buf,
WIDTH.try_into().unwrap(), WIDTH.try_into().unwrap(),
HEIGHT.try_into().unwrap(), HEIGHT.try_into().unwrap(),
WAIT_TIMEOUT, WAIT_TIMEOUT,
@ -85,15 +86,12 @@ fn main() {
cam.play_shutter_sound(CamShutterSoundType::NORMAL) cam.play_shutter_sound(CamShutterSoundType::NORMAL)
.expect("Failed to play shutter sound"); .expect("Failed to play shutter sound");
let img = rotate_image(&buf, WIDTH, HEIGHT); rotate_image_to_screen(
&buf,
unsafe { gfx.top_screen.borrow_mut().get_raw_framebuffer().ptr,
gfx.top_screen WIDTH,
.borrow_mut() HEIGHT,
.get_raw_framebuffer() );
.ptr
.copy_from(img.as_ptr(), img.len());
}
gfx.flush_buffers(); gfx.flush_buffers();
gfx.swap_buffers(); gfx.swap_buffers();
@ -104,9 +102,8 @@ fn main() {
// The 3DS' screens are 2 vertical LCD panels rotated by 90 degrees. // 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. // 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. // This functions rotates an horizontal image by 90 degrees to the right.
fn rotate_image(img: &[u8], width: usize, height: usize) -> Vec<u8> { fn rotate_image_to_screen(src: &[u8], framebuf: *mut u8, width: usize, height: usize) {
let mut res = vec![0u8; img.len()];
for j in 0..height { for j in 0..height {
for i in 0..width { for i in 0..width {
// Y-coordinate of where to draw in the frame buffer // Y-coordinate of where to draw in the frame buffer
@ -121,9 +118,11 @@ fn rotate_image(img: &[u8], width: usize, height: usize) -> Vec<u8> {
// Initial index of where to draw in the frame buffer based on y and x coordinates // 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) 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]; unsafe {
res[draw_index + 1] = img[read_index + 1]; let pixel_pointer = framebuf.offset(draw_index as isize);
*pixel_pointer = src[read_index];
*pixel_pointer.offset(1) = src[read_index + 1];
}
} }
} }
res
} }

6
ctru-rs/src/error.rs

@ -21,7 +21,11 @@ impl Try for ResultCode {
} }
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> { fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
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()) ControlFlow::Break(self.into())
} else { } else {
ControlFlow::Continue(()) ControlFlow::Continue(())

33
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 /// # Errors
/// ///
/// This will error if the camera is busy or if the timeout duration is reached. /// 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 /// # Arguments
/// ///
/// * `width` - Width of the desired image /// * `width` - Width of the desired image
@ -717,10 +721,11 @@ pub trait Camera {
/// * `timeout` - Duration to wait for the image /// * `timeout` - Duration to wait for the image
fn take_picture( fn take_picture(
&mut self, &mut self,
buffer: &mut [u8],
width: u16, width: u16,
height: u16, height: u16,
timeout: Duration, timeout: Duration,
) -> crate::Result<Vec<u8>> { ) -> crate::Result<()> {
let transfer_unit = unsafe { let transfer_unit = unsafe {
let mut buf_size = 0; let mut buf_size = 0;
ResultCode(ctru_sys::CAMU_GetMaxBytes( ResultCode(ctru_sys::CAMU_GetMaxBytes(
@ -731,10 +736,6 @@ pub trait Camera {
Ok::<u32, i32>(buf_size) Ok::<u32, i32>(buf_size)
}?; }?;
let screen_size = u32::from(width) * u32::from(height) * 2;
let mut buf = vec![0u8; usize::try_from(screen_size).unwrap()];
unsafe { unsafe {
ResultCode(ctru_sys::CAMU_SetTransferBytes( ResultCode(ctru_sys::CAMU_SetTransferBytes(
self.port_as_raw(), 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 { unsafe {
ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?; ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?;
ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?; ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?;
@ -754,7 +760,7 @@ pub trait Camera {
let mut completion_handle: Handle = 0; let mut completion_handle: Handle = 0;
ResultCode(ctru_sys::CAMU_SetReceiving( ResultCode(ctru_sys::CAMU_SetReceiving(
&mut completion_handle, &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(), self.port_as_raw(),
screen_size, screen_size,
transfer_unit.try_into().unwrap(), transfer_unit.try_into().unwrap(),
@ -763,16 +769,21 @@ pub trait Camera {
}?; }?;
unsafe { 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, receive_event,
timeout.as_nanos().try_into().unwrap(), 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::CAMU_StopCapture(self.port_as_raw()))?;
ResultCode(ctru_sys::svcCloseHandle(receive_event))?;
ResultCode(ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE))?; ResultCode(ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE))?;
wait_result?;
}; };
Ok(buf) Ok(())
} }
} }

Loading…
Cancel
Save