diff --git a/ctru-rs/examples/camera-image.rs b/ctru-rs/examples/camera-image.rs index 7ee9b56..ff2339f 100644 --- a/ctru-rs/examples/camera-image.rs +++ b/ctru-rs/examples/camera-image.rs @@ -6,7 +6,7 @@ use ctru::prelude::*; use ctru::services::cam::{ Cam, Camera, OutputFormat, ShutterSound, Trimming, ViewSize, WhiteBalance, }; -use ctru::services::gfx::{Flush, Screen, Swap}; +use ctru::services::gfx::{Flush, Screen, Swap, TopScreen3D}; use ctru::services::gspgpu::FramebufferFormat; use std::time::Duration; @@ -20,9 +20,12 @@ fn main() { let mut hid = Hid::new().expect("Failed to initialize Hid service."); let gfx = Gfx::new().expect("Failed to initialize GFX service."); - let mut top_screen = gfx.top_screen.borrow_mut(); - top_screen.set_double_buffering(true); - top_screen.set_framebuffer_format(FramebufferFormat::Rgb565); + gfx.top_screen.borrow_mut().set_double_buffering(true); + gfx.top_screen + .borrow_mut() + .set_framebuffer_format(FramebufferFormat::Rgb565); + + let mut top_screen_3d = TopScreen3D::from(&gfx.top_screen); let _console = Console::new(gfx.bottom_screen.borrow_mut()); @@ -31,7 +34,7 @@ fn main() { let mut cam = Cam::new().expect("Failed to initialize CAM service."); // Camera setup. - let camera = &mut cam.outer_right_cam; + let camera = &mut cam.both_outer_cams; { camera .set_view_size(ViewSize::TopLCD) @@ -73,7 +76,7 @@ fn main() { if keys_down.contains(KeyPad::R) { println!("Capturing new image"); - let camera = &mut cam.outer_right_cam; + let mut camera = &mut cam.both_outer_cams; // Take a picture and write it to the buffer. camera @@ -86,17 +89,29 @@ fn main() { cam.play_shutter_sound(ShutterSound::Normal) .expect("Failed to play shutter sound"); - // Rotate the image and correctly display it on the screen. - rotate_image_to_screen( - &buf, - top_screen.raw_framebuffer().ptr, - image_size.0 as usize, - image_size.1 as usize, - ); + { + let (mut left_side, mut right_side) = top_screen_3d.split_mut(); + + // Rotate the left image and correctly display it on the screen. + rotate_image_to_screen( + &buf, + left_side.raw_framebuffer().ptr, + image_size.0 as usize, + image_size.1 as usize, + ); + + // Rotate the right image and correctly display it on the screen. + rotate_image_to_screen( + &buf[len / 2..], + right_side.raw_framebuffer().ptr, + image_size.0 as usize, + image_size.1 as usize, + ); + } // We will only flush and swap the "camera" screen, since the other screen is handled by the `Console`. - top_screen.flush_buffers(); - top_screen.swap_buffers(); + top_screen_3d.flush_buffers(); + top_screen_3d.swap_buffers(); gfx.wait_for_vblank(); } diff --git a/ctru-rs/src/services/cam.rs b/ctru-rs/src/services/cam.rs index 614de33..991db74 100644 --- a/ctru-rs/src/services/cam.rs +++ b/ctru-rs/src/services/cam.rs @@ -241,15 +241,16 @@ pub enum Trimming { } /// Data used by the camera to calibrate image quality for a single camera. +// TODO: Implement Image quality calibration. #[doc(alias = "CAMU_ImageQualityCalibrationData")] #[derive(Default, Clone, Copy, Debug)] -pub struct ImageQualityCalibrationData(pub ctru_sys::CAMU_ImageQualityCalibrationData); +pub struct ImageQualityCalibration(pub ctru_sys::CAMU_ImageQualityCalibrationData); /// Data used by the camera to calibrate image quality when using both outward cameras. // TODO: Implement Stereo camera calibration. #[doc(alias = "CAMU_StereoCameraCalibrationData")] #[derive(Default, Clone, Copy, Debug)] -pub struct StereoCameraCalibrationData(pub ctru_sys::CAMU_StereoCameraCalibrationData); +pub struct StereoCameraCalibration(ctru_sys::CAMU_StereoCameraCalibrationData); /// Inward camera representation (facing the user of the 3DS). /// @@ -358,8 +359,44 @@ impl BothOutwardCam { ResultCode(ctru_sys::CAMU_SetBrightnessSynchronization( brightness_synchronization, ))?; - Ok(()) } + + Ok(()) + } + + #[doc(alias = "CAMU_GetStereoCameraCalibrationData")] + /// Returns the currently set [`StereoCameraCalibration`]. + pub fn stereo_calibration(&self) -> crate::Result { + let mut calibration = StereoCameraCalibration::default(); + + unsafe { + ResultCode(ctru_sys::CAMU_GetStereoCameraCalibrationData( + &mut calibration.0, + ))?; + } + + Ok(calibration) + } + + #[doc(alias = "CAMU_SetStereoCameraCalibrationData")] + /// Set the [`StereoCameraCalibration`]. + // TODO: This seems to have no effect. + pub fn set_stereo_calibration( + &mut self, + mut stereo_calibration: StereoCameraCalibration, + ) -> crate::Result<()> { + let view_size = self.final_view_size(); + + stereo_calibration.0.imageWidth = view_size.0; + stereo_calibration.0.imageHeight = view_size.1; + + unsafe { + ResultCode(ctru_sys::CAMU_SetStereoCameraCalibrationData( + stereo_calibration.0, + ))?; + } + + Ok(()) } } @@ -389,6 +426,114 @@ impl Camera for BothOutwardCam { fn port_as_raw(&self) -> ctru_sys::u32_ { ctru_sys::PORT_BOTH } + + fn take_picture(&mut self, buffer: &mut [u8], timeout: Duration) -> crate::Result<()> { + // Check whether the provided buffer is big enough to store the image. + let max_size = self.final_byte_length(); + if buffer.len() < max_size { + return Err(Error::BufferTooShort { + provided: buffer.len(), + wanted: max_size, + }); + } + + let final_view = self.final_view_size(); + + // The transfer unit is NOT the "max number of bytes" or whatever the docs make you think it is... + let transfer_unit = unsafe { + let mut transfer_unit = 0; + + ResultCode(ctru_sys::CAMU_GetMaxBytes( + &mut transfer_unit, + final_view.0, + final_view.1, + ))?; + + transfer_unit + }; + + unsafe { + ResultCode(ctru_sys::CAMU_SetTransferBytes( + self.port_as_raw(), + transfer_unit, + final_view.0, + final_view.1, + ))?; + }; + + unsafe { + ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?; + ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?; + }; + + // Synchronize the two cameras. + unsafe { + ResultCode(ctru_sys::CAMU_SynchronizeVsyncTiming( + ctru_sys::SELECT_OUT1, + ctru_sys::SELECT_OUT2, + ))?; + } + + // Start capturing with the camera. + unsafe { + ResultCode(ctru_sys::CAMU_StartCapture(self.port_as_raw()))?; + }; + + let receive_event_1 = unsafe { + let mut completion_handle: Handle = 0; + + ResultCode(ctru_sys::CAMU_SetReceiving( + &mut completion_handle, + buffer.as_mut_ptr().cast(), + ctru_sys::PORT_CAM1, + (max_size / 2) as u32, + transfer_unit.try_into().unwrap(), + ))?; + + completion_handle + }; + + let receive_event_2 = unsafe { + let mut completion_handle: Handle = 0; + + ResultCode(ctru_sys::CAMU_SetReceiving( + &mut completion_handle, + buffer[max_size / 2..].as_mut_ptr().cast(), + ctru_sys::PORT_CAM2, + (max_size / 2) as u32, + transfer_unit.try_into().unwrap(), + ))?; + + completion_handle + }; + + unsafe { + // Panicking without closing an SVC handle causes an ARM exception, we have to handle it carefully. + let wait_result_1 = ResultCode(ctru_sys::svcWaitSynchronization( + receive_event_1, + timeout.as_nanos().try_into().unwrap(), + )); + + let wait_result_2 = ResultCode(ctru_sys::svcWaitSynchronization( + receive_event_2, + timeout.as_nanos().try_into().unwrap(), + )); + + // We close everything first, then we check for possible errors + let _ = ctru_sys::svcCloseHandle(receive_event_1); // We wouldn't return the error even if there was one, so no use of ResultCode is needed. + let _ = ctru_sys::svcCloseHandle(receive_event_2); + + // Camera state cleanup + ResultCode(ctru_sys::CAMU_StopCapture(self.port_as_raw()))?; + ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?; + ResultCode(ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE))?; + + wait_result_1?; + wait_result_2?; + }; + + Ok(()) + } } /// Generic functionality common to all cameras. @@ -445,6 +590,7 @@ pub trait Camera: private::ConfigurableCamera { /// /// # Notes /// + /// The value returned will be double the image size if requested by [`BothOutwardCam`]. /// Remember to query this information again if *any* changes are applied to the [`Camera`] configuration! /// /// # Example @@ -799,11 +945,11 @@ pub trait Camera: private::ConfigurableCamera { } } - /// Set the [`ImageQualityCalibrationData`] for the camera. + /// Set the [`ImageQualityCalibration`] for the camera. #[doc(alias = "CAMU_SetImageQualityCalibrationData")] - fn set_image_quality_calibration_data( + fn set_image_quality_calibration( &mut self, - data: ImageQualityCalibrationData, + data: ImageQualityCalibration, ) -> crate::Result<()> { unsafe { ResultCode(ctru_sys::CAMU_SetImageQualityCalibrationData(data.0))?; @@ -811,11 +957,11 @@ pub trait Camera: private::ConfigurableCamera { } } - /// Returns the current [`ImageQualityCalibrationData`] for the camera. + /// Returns the current [`ImageQualityCalibration`] for the camera. #[doc(alias = "CAMU_GetImageQualityCalibrationData")] - fn image_quality_calibration_data(&self) -> crate::Result { + fn image_quality_calibration(&self) -> crate::Result { unsafe { - let mut data = ImageQualityCalibrationData::default(); + let mut data = ImageQualityCalibration::default(); ResultCode(ctru_sys::CAMU_GetImageQualityCalibrationData(&mut data.0))?; Ok(data) } @@ -826,6 +972,13 @@ pub trait Camera: private::ConfigurableCamera { /// # Errors /// /// This function will return an error if the camera is already busy or if the timeout duration is reached. + /// + /// # Notes + /// + /// If the picture is taken using [`BothOutwardCam`], the buffer will have to be able to hold both images + /// (from each camera), which will be written into it sequentially. + /// Use [`Camera::final_byte_length()`] to know how big the buffer needs to be to hold your next image. + /// /// # Example /// /// ```no_run @@ -858,13 +1011,12 @@ pub trait Camera: private::ConfigurableCamera { // Check whether the provided buffer is big enough to store the image. let max_size = self.final_byte_length(); if buffer.len() < max_size { - return Err(Error::BufferTooShort { provided: buffer.len(), wanted: max_size, }); } - + let final_view = self.final_view_size(); // The transfer unit is NOT the "max number of bytes" or whatever the docs make you think it is...