Browse Source

Squash down camera functions

pull/137/head
Andrea Ciliberti 1 year ago
parent
commit
ce8fc4ef83
  1. 14
      ctru-rs/examples/camera-image.rs
  2. 341
      ctru-rs/src/services/cam.rs

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

@ -32,7 +32,7 @@ fn main() { @@ -32,7 +32,7 @@ fn main() {
// Camera setup.
let camera = &mut cam.outer_right_cam;
{
camera
.set_view_size(ViewSize::TopLCD)
.expect("Failed to set camera size");
@ -48,12 +48,14 @@ fn main() { @@ -48,12 +48,14 @@ fn main() {
camera
.set_white_balance(WhiteBalance::Auto)
.expect("Failed to enable auto white balance");
// Un-comment this line and see how it changes!
// camera.set_trimming(Trimming::Centered(ViewSize::BottomLCD));
// This line has no effect on the camera since the photos are already shot with `TopLCD` size.
camera
.set_trimming(Trimming::Centered(ViewSize::TopLCD))
.expect("Failed to enable trimming");
}
// We don't intend on making any other modifications to the camera, so this size should be enough.
let len = camera.max_byte_count();
let len = camera.final_byte_length();
let mut buf = vec![0u8; len];
println!("\nPress R to take a new picture");
@ -78,7 +80,7 @@ fn main() { @@ -78,7 +80,7 @@ fn main() {
.take_picture(&mut buf, WAIT_TIMEOUT)
.expect("Failed to take picture");
let image_size = camera.final_image_size();
let image_size = camera.final_view_size();
// Play the normal shutter sound.
cam.play_shutter_sound(ShutterSound::Normal)

341
ctru-rs/src/services/cam.rs

@ -8,6 +8,7 @@ use crate::error::{Error, ResultCode}; @@ -8,6 +8,7 @@ use crate::error::{Error, ResultCode};
use crate::services::gspgpu::FramebufferFormat;
use crate::services::ServiceReference;
use ctru_sys::Handle;
use private::Configuration;
use std::sync::Mutex;
use std::time::Duration;
@ -250,38 +251,19 @@ pub struct ImageQualityCalibrationData(pub ctru_sys::CAMU_ImageQualityCalibratio @@ -250,38 +251,19 @@ pub struct ImageQualityCalibrationData(pub ctru_sys::CAMU_ImageQualityCalibratio
#[derive(Default, Clone, Copy, Debug)]
pub struct StereoCameraCalibrationData(pub ctru_sys::CAMU_StereoCameraCalibrationData);
/// Basic configuration needed to properly use the built-in cameras.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Configuration {
view_size: ViewSize,
trimming: Trimming,
}
impl Configuration {
fn new() -> Self {
Self {
view_size: ViewSize::TopLCD,
trimming: Trimming::Off,
}
}
}
/// Inward camera representation (facing the user of the 3DS).
///
/// Usually used for selfies.
#[non_exhaustive]
pub struct InwardCam {
configuration: Configuration,
}
/// Right-side outward camera representation.
#[non_exhaustive]
pub struct OutwardRightCam {
configuration: Configuration,
}
/// Left-side outward camera representation.
#[non_exhaustive]
pub struct OutwardLeftCam {
configuration: Configuration,
}
@ -289,150 +271,113 @@ pub struct OutwardLeftCam { @@ -289,150 +271,113 @@ pub struct OutwardLeftCam {
/// Both outer cameras combined.
///
/// Usually used for 3D photos.
#[non_exhaustive]
pub struct BothOutwardCam {
configuration: Configuration,
}
impl BothOutwardCam {
/// Set whether to enable or disable brightness synchronization between the two cameras.
#[doc(alias = "CAMU_SetBrightnessSynchronization")]
pub fn set_brightness_synchronization(
&mut self,
brightness_synchronization: bool,
) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetBrightnessSynchronization(
brightness_synchronization,
))?;
Ok(())
}
}
}
mod private {
use super::{BothOutwardCam, InwardCam, OutwardLeftCam, OutwardRightCam, Trimming, ViewSize};
macro_rules! trimming_checks {
($trimming:ident, $view_size:expr) => {
match $trimming {
Trimming::Centered(trim_size) => {
let view_size: (i16, i16) = $view_size;
let trim_size: (i16, i16) = trim_size.into();
/// Basic configuration needed to properly use the built-in cameras.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Configuration {
pub view_size: ViewSize,
pub trimming: Trimming,
}
// Trim sizes are within the view.
assert!(trim_size.0 <= view_size.0 && trim_size.1 <= view_size.1);
impl Configuration {
pub fn new() -> Self {
Self::default()
}
Trimming::Off => (),
}
};
}
impl Camera for InwardCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_IN1
impl Default for Configuration {
fn default() -> Self {
Self {
view_size: ViewSize::TopLCD,
trimming: Trimming::Off,
}
fn view_size(&self) -> ViewSize {
self.configuration.view_size
}
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
))?;
}
self.configuration.view_size = size;
self.set_trimming(Trimming::Off);
pub trait ConfigurableCamera {
fn configuration(&self) -> &Configuration;
Ok(())
fn configuration_mut(&mut self) -> &mut Configuration;
}
fn trimming(&self) -> Trimming {
self.configuration.trimming
impl ConfigurableCamera for InwardCam {
fn configuration(&self) -> &Configuration {
&self.configuration
}
fn set_trimming(&mut self, trimming: Trimming) {
// Run checks for all trimming possibilities.
trimming_checks!(trimming, self.view_size().into());
self.configuration.trimming = trimming;
fn configuration_mut(&mut self) -> &mut Configuration {
&mut self.configuration
}
}
impl Camera for OutwardRightCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT1
}
fn view_size(&self) -> ViewSize {
self.configuration.view_size
impl ConfigurableCamera for OutwardRightCam {
fn configuration(&self) -> &Configuration {
&self.configuration
}
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
))?;
fn configuration_mut(&mut self) -> &mut Configuration {
&mut self.configuration
}
self.configuration.view_size = size;
self.set_trimming(Trimming::Off);
Ok(())
}
fn trimming(&self) -> Trimming {
self.configuration.trimming
impl ConfigurableCamera for OutwardLeftCam {
fn configuration(&self) -> &Configuration {
&self.configuration
}
fn set_trimming(&mut self, trimming: Trimming) {
// Run checks for all trimming possibilities.
trimming_checks!(trimming, self.view_size().into());
self.configuration.trimming = trimming;
fn configuration_mut(&mut self) -> &mut Configuration {
&mut self.configuration
}
}
}
impl Camera for OutwardLeftCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT2
impl ConfigurableCamera for BothOutwardCam {
fn configuration(&self) -> &Configuration {
&self.configuration
}
fn view_size(&self) -> ViewSize {
self.configuration.view_size
fn configuration_mut(&mut self) -> &mut Configuration {
&mut self.configuration
}
}
}
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
impl BothOutwardCam {
/// Set whether to enable or disable brightness synchronization between the two cameras.
#[doc(alias = "CAMU_SetBrightnessSynchronization")]
pub fn set_brightness_synchronization(
&mut self,
brightness_synchronization: bool,
) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
ResultCode(ctru_sys::CAMU_SetBrightnessSynchronization(
brightness_synchronization,
))?;
}
self.configuration.view_size = size;
self.set_trimming(Trimming::Off);
Ok(())
}
}
}
fn trimming(&self) -> Trimming {
self.configuration.trimming
impl Camera for InwardCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_IN1
}
}
fn set_trimming(&mut self, trimming: Trimming) {
// Run checks for all trimming possibilities.
trimming_checks!(trimming, self.view_size().into());
impl Camera for OutwardRightCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT1
}
}
self.configuration.trimming = trimming;
impl Camera for OutwardLeftCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT2
}
}
@ -444,41 +389,10 @@ impl Camera for BothOutwardCam { @@ -444,41 +389,10 @@ impl Camera for BothOutwardCam {
fn port_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::PORT_BOTH
}
fn view_size(&self) -> ViewSize {
self.configuration.view_size
}
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
))?;
}
self.configuration.view_size = size;
self.set_trimming(Trimming::Off);
Ok(())
}
fn trimming(&self) -> Trimming {
self.configuration.trimming
}
fn set_trimming(&mut self, trimming: Trimming) {
// Run checks for all trimming possibilities.
trimming_checks!(trimming, self.view_size().into());
self.configuration.trimming = trimming;
}
}
/// Generic functionality common to all cameras.
pub trait Camera {
pub trait Camera: private::ConfigurableCamera {
/// Returns the raw value of the selected camera.
fn camera_as_raw(&self) -> ctru_sys::u32_;
@ -489,7 +403,9 @@ pub trait Camera { @@ -489,7 +403,9 @@ pub trait Camera {
/// This view is the full resolution at which the camera will take the photo.
/// If you are interested in the final image's size, calculated while taking into account all processing and modifications,
/// have a look at [`Camera::final_view_size()`].
fn view_size(&self) -> ViewSize;
fn view_size(&self) -> ViewSize {
self.configuration().view_size
}
/// Returns the raw port of the selected camera.
fn port_as_raw(&self) -> ctru_sys::u32_ {
@ -595,11 +511,50 @@ pub trait Camera { @@ -595,11 +511,50 @@ pub trait Camera {
}
/// Returns the [`Trimming`] configuration currently set.
fn trimming(&self) -> Trimming;
fn trimming(&self) -> Trimming {
self.configuration().trimming
}
/// Set trimming bounds to trim the camera photo.
///
/// # Notes
///
/// Trimming the image view directly on the cameras can only work if the final image has the size of one of the supported image formats.
/// As such, even after trimming the image (which will result in a "zoomed in" view of the original image)
/// the resulting picture must still be of [`ViewSize`] dimensions.
#[doc(alias = "CAMU_SetTrimming")]
fn set_trimming(&mut self, trimming: Trimming);
fn set_trimming(&mut self, trimming: Trimming) -> crate::Result<()> {
// Run checks for all trimming possibilities.
match trimming {
Trimming::Centered(trim_size) => unsafe {
let view_size: (i16, i16) = self.view_size().into();
let trim_size: (i16, i16) = trim_size.into();
// Trim sizes are within the view.
assert!(
trim_size.0 <= view_size.0 && trim_size.1 <= view_size.1,
"trimmed view is bigger than the camera view"
);
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
ResultCode(ctru_sys::CAMU_SetTrimmingParamsCenter(
self.port_as_raw(),
trim_size.0,
trim_size.1,
view_size.0,
view_size.1,
))?;
},
Trimming::Off => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), false))?;
},
}
self.configuration_mut().trimming = trimming;
Ok(())
}
/// Returns whether or not trimming is currently enabled for the camera.
#[doc(alias = "CAMU_IsTrimming")]
@ -681,7 +636,21 @@ pub trait Camera { @@ -681,7 +636,21 @@ pub trait Camera {
///
/// Calling this function will reset the trimming configuration.
#[doc(alias = "CAMU_SetSize")]
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()>;
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSize(
self.camera_as_raw(),
size.into(),
ctru_sys::CONTEXT_A,
))?;
}
self.configuration_mut().view_size = size;
self.set_trimming(Trimming::Off)?;
Ok(())
}
/// Set the frame rate of the camera.
#[doc(alias = "CAMU_SetFrameRate")]
@ -711,8 +680,7 @@ pub trait Camera { @@ -711,8 +680,7 @@ pub trait Camera {
///
/// # Notes
///
/// This operation will override any previously set [`Effect`]s.
/// Multiple effects can be set at once by combining the bitflags of [`Effect`].
/// This operation will override any previously set [`Effect`].
#[doc(alias = "CAMU_SetEffect")]
fn set_effect(&mut self, effect: Effect) -> crate::Result<()> {
unsafe {
@ -857,14 +825,7 @@ pub trait Camera { @@ -857,14 +825,7 @@ pub trait Camera {
///
/// # Errors
///
/// This function will return an error if the camera is busy or if the timeout duration gets reached.
///
/// # Arguments
///
/// * `width` - Width of the desired image
/// * `height` - Height of the desired image
/// * `timeout` - Duration to wait for the image
///
/// This function will return an error if the camera is already busy or if the timeout duration is reached.
/// # Example
///
/// ```no_run
@ -894,26 +855,15 @@ pub trait Camera { @@ -894,26 +855,15 @@ pub trait Camera {
/// # }
/// ```
fn take_picture(&mut self, buffer: &mut [u8], timeout: Duration) -> crate::Result<()> {
// Obtain the final view size and make the needed modifications to the camera.
match self.trimming() {
Trimming::Centered(trim_size) => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
let full_view: (i16, i16) = self.view_size().into();
let trim_size: (i16, i16) = trim_size.into();
// Check whether the provided buffer is big enough to store the image.
let max_size = self.final_byte_length();
if buffer.len() < max_size {
ResultCode(ctru_sys::CAMU_SetTrimmingParamsCenter(
self.port_as_raw(),
trim_size.0,
trim_size.1,
full_view.0,
full_view.1,
))?;
},
Trimming::Off => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), false))?;
},
};
return Err(Error::BufferTooShort {
provided: buffer.len(),
wanted: max_size,
});
}
let final_view = self.final_view_size();
@ -939,21 +889,6 @@ pub trait Camera { @@ -939,21 +889,6 @@ pub trait Camera {
))?;
};
// Check whether the input buffer is big enough for the image.
let max_size = self.final_byte_length();
if buffer.len() < max_size {
// We deactivate the camera prematurely.
//
// Note that it shouldn't be too important whether the camera closes or not here,
// since it only starts capturing later.
unsafe { ResultCode(ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE))? };
return Err(Error::BufferTooShort {
provided: buffer.len(),
wanted: max_size,
});
}
unsafe {
ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?;
ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?;
@ -979,14 +914,14 @@ pub trait Camera { @@ -979,14 +914,14 @@ pub trait Camera {
};
unsafe {
// Panicking without closing an SVC handle causes an ARM exception, we have to handle it carefully (TODO: SVC module)
// Panicking without closing an SVC handle causes an ARM exception, we have to handle it carefully.
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
let _ = ctru_sys::svcCloseHandle(receive_event); // We wouldn't return the error even if there was one, so no use of ResultCode is needed
let _ = ctru_sys::svcCloseHandle(receive_event); // We wouldn't return the error even if there was one, so no use of ResultCode is needed.
// Camera state cleanup
ResultCode(ctru_sys::CAMU_StopCapture(self.port_as_raw()))?;

Loading…
Cancel
Save