Browse Source

3D images without stereo calibration

pull/137/head
Andrea Ciliberti 1 year ago
parent
commit
0ec4c92fbc
  1. 45
      ctru-rs/examples/camera-image.rs
  2. 174
      ctru-rs/src/services/cam.rs

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

@ -6,7 +6,7 @@ use ctru::prelude::*;
use ctru::services::cam::{ use ctru::services::cam::{
Cam, Camera, OutputFormat, ShutterSound, Trimming, ViewSize, WhiteBalance, 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 ctru::services::gspgpu::FramebufferFormat;
use std::time::Duration; use std::time::Duration;
@ -20,9 +20,12 @@ fn main() {
let mut hid = Hid::new().expect("Failed to initialize Hid service."); let mut hid = Hid::new().expect("Failed to initialize Hid service.");
let gfx = Gfx::new().expect("Failed to initialize GFX service."); let gfx = Gfx::new().expect("Failed to initialize GFX service.");
let mut top_screen = gfx.top_screen.borrow_mut(); gfx.top_screen.borrow_mut().set_double_buffering(true);
top_screen.set_double_buffering(true); gfx.top_screen
top_screen.set_framebuffer_format(FramebufferFormat::Rgb565); .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()); 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."); let mut cam = Cam::new().expect("Failed to initialize CAM service.");
// Camera setup. // Camera setup.
let camera = &mut cam.outer_right_cam; let camera = &mut cam.both_outer_cams;
{ {
camera camera
.set_view_size(ViewSize::TopLCD) .set_view_size(ViewSize::TopLCD)
@ -73,7 +76,7 @@ fn main() {
if keys_down.contains(KeyPad::R) { if keys_down.contains(KeyPad::R) {
println!("Capturing new image"); 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. // Take a picture and write it to the buffer.
camera camera
@ -86,17 +89,29 @@ fn main() {
cam.play_shutter_sound(ShutterSound::Normal) cam.play_shutter_sound(ShutterSound::Normal)
.expect("Failed to play shutter sound"); .expect("Failed to play shutter sound");
// Rotate the image and correctly display it on the screen. {
rotate_image_to_screen( let (mut left_side, mut right_side) = top_screen_3d.split_mut();
&buf,
top_screen.raw_framebuffer().ptr, // Rotate the left image and correctly display it on the screen.
image_size.0 as usize, rotate_image_to_screen(
image_size.1 as usize, &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`. // We will only flush and swap the "camera" screen, since the other screen is handled by the `Console`.
top_screen.flush_buffers(); top_screen_3d.flush_buffers();
top_screen.swap_buffers(); top_screen_3d.swap_buffers();
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }

174
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. /// Data used by the camera to calibrate image quality for a single camera.
// TODO: Implement Image quality calibration.
#[doc(alias = "CAMU_ImageQualityCalibrationData")] #[doc(alias = "CAMU_ImageQualityCalibrationData")]
#[derive(Default, Clone, Copy, Debug)] #[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. /// Data used by the camera to calibrate image quality when using both outward cameras.
// TODO: Implement Stereo camera calibration. // TODO: Implement Stereo camera calibration.
#[doc(alias = "CAMU_StereoCameraCalibrationData")] #[doc(alias = "CAMU_StereoCameraCalibrationData")]
#[derive(Default, Clone, Copy, Debug)] #[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). /// Inward camera representation (facing the user of the 3DS).
/// ///
@ -358,8 +359,44 @@ impl BothOutwardCam {
ResultCode(ctru_sys::CAMU_SetBrightnessSynchronization( ResultCode(ctru_sys::CAMU_SetBrightnessSynchronization(
brightness_synchronization, brightness_synchronization,
))?; ))?;
Ok(())
} }
Ok(())
}
#[doc(alias = "CAMU_GetStereoCameraCalibrationData")]
/// Returns the currently set [`StereoCameraCalibration`].
pub fn stereo_calibration(&self) -> crate::Result<StereoCameraCalibration> {
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_ { fn port_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::PORT_BOTH 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. /// Generic functionality common to all cameras.
@ -445,6 +590,7 @@ pub trait Camera: private::ConfigurableCamera {
/// ///
/// # Notes /// # 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! /// Remember to query this information again if *any* changes are applied to the [`Camera`] configuration!
/// ///
/// # Example /// # 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")] #[doc(alias = "CAMU_SetImageQualityCalibrationData")]
fn set_image_quality_calibration_data( fn set_image_quality_calibration(
&mut self, &mut self,
data: ImageQualityCalibrationData, data: ImageQualityCalibration,
) -> crate::Result<()> { ) -> crate::Result<()> {
unsafe { unsafe {
ResultCode(ctru_sys::CAMU_SetImageQualityCalibrationData(data.0))?; 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")] #[doc(alias = "CAMU_GetImageQualityCalibrationData")]
fn image_quality_calibration_data(&self) -> crate::Result<ImageQualityCalibrationData> { fn image_quality_calibration(&self) -> crate::Result<ImageQualityCalibration> {
unsafe { unsafe {
let mut data = ImageQualityCalibrationData::default(); let mut data = ImageQualityCalibration::default();
ResultCode(ctru_sys::CAMU_GetImageQualityCalibrationData(&mut data.0))?; ResultCode(ctru_sys::CAMU_GetImageQualityCalibrationData(&mut data.0))?;
Ok(data) Ok(data)
} }
@ -826,6 +972,13 @@ pub trait Camera: private::ConfigurableCamera {
/// # Errors /// # Errors
/// ///
/// This function will return an error if the camera is already busy or if the timeout duration is reached. /// 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 /// # Example
/// ///
/// ```no_run /// ```no_run
@ -858,13 +1011,12 @@ pub trait Camera: private::ConfigurableCamera {
// Check whether the provided buffer is big enough to store the image. // Check whether the provided buffer is big enough to store the image.
let max_size = self.final_byte_length(); let max_size = self.final_byte_length();
if buffer.len() < max_size { if buffer.len() < max_size {
return Err(Error::BufferTooShort { return Err(Error::BufferTooShort {
provided: buffer.len(), provided: buffer.len(),
wanted: max_size, wanted: max_size,
}); });
} }
let final_view = self.final_view_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... // The transfer unit is NOT the "max number of bytes" or whatever the docs make you think it is...

Loading…
Cancel
Save