Browse Source

More CAM changes and WIP trimming

pull/137/head
Andrea Ciliberti 1 year ago
parent
commit
b17bed0aeb
  1. 22
      ctru-rs/examples/camera-image.rs
  2. 403
      ctru-rs/src/services/cam.rs

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

@ -11,8 +11,8 @@ use ctru::services::gspgpu::FramebufferFormat;
use std::time::Duration; use std::time::Duration;
const WIDTH: usize = 200; const WIDTH: i16 = 400;
const HEIGHT: usize = 100; const HEIGHT: i16 = 240;
const WAIT_TIMEOUT: Duration = Duration::from_millis(300); const WAIT_TIMEOUT: Duration = Duration::from_millis(300);
@ -51,17 +51,13 @@ fn main() {
camera camera
.set_white_balance(WhiteBalance::Auto) .set_white_balance(WhiteBalance::Auto)
.expect("Failed to enable auto white balance"); .expect("Failed to enable auto white balance");
camera camera.set_trimming(Trimming::Centered {
.set_trimming(Trimming::Centered { width: WIDTH,
width: WIDTH as i16, height: HEIGHT,
height: HEIGHT as i16, });
})
.expect("Failed to disable trimming");
// We don't intend on making any other modifications to the camera, so this size should be enough. // We don't intend on making any other modifications to the camera, so this size should be enough.
let len = camera let len = camera.max_byte_count();
.max_byte_count()
.expect("could not retrieve max image buffer size");
let mut buf = vec![0u8; len]; let mut buf = vec![0u8; len];
println!("\nPress R to take a new picture"); println!("\nPress R to take a new picture");
@ -86,9 +82,7 @@ fn main() {
.take_picture(&mut buf, WAIT_TIMEOUT) .take_picture(&mut buf, WAIT_TIMEOUT)
.expect("Failed to take picture"); .expect("Failed to take picture");
let image_size = camera let image_size = camera.final_image_size();
.final_image_size()
.expect("could not retrieve final image size");
// Play the normal shutter sound. // Play the normal shutter sound.
cam.play_shutter_sound(ShutterSound::Normal) cam.play_shutter_sound(ShutterSound::Normal)

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

@ -261,27 +261,40 @@ pub struct ImageQualityCalibrationData(pub ctru_sys::CAMU_ImageQualityCalibratio
#[derive(Default, Clone, Copy, Debug)] #[derive(Default, Clone, Copy, Debug)]
pub struct StereoCameraCalibrationData(pub ctru_sys::CAMU_StereoCameraCalibrationData); 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). /// Inward camera representation (facing the user of the 3DS).
/// ///
/// Usually used for selfies. /// Usually used for selfies.
#[non_exhaustive] #[non_exhaustive]
pub struct InwardCam { pub struct InwardCam {
view_size: ViewSize, configuration: Configuration,
trimming: Trimming,
} }
/// Right-side outward camera representation. /// Right-side outward camera representation.
#[non_exhaustive] #[non_exhaustive]
pub struct OutwardRightCam { pub struct OutwardRightCam {
view_size: ViewSize, configuration: Configuration,
trimming: Trimming,
} }
/// Left-side outward camera representation. /// Left-side outward camera representation.
#[non_exhaustive] #[non_exhaustive]
pub struct OutwardLeftCam { pub struct OutwardLeftCam {
view_size: ViewSize, configuration: Configuration,
trimming: Trimming,
} }
/// Both outer cameras combined. /// Both outer cameras combined.
@ -289,8 +302,7 @@ pub struct OutwardLeftCam {
/// Usually used for 3D photos. /// Usually used for 3D photos.
#[non_exhaustive] #[non_exhaustive]
pub struct BothOutwardCam { pub struct BothOutwardCam {
view_size: ViewSize, configuration: Configuration,
trimming: Trimming,
} }
impl BothOutwardCam { impl BothOutwardCam {
@ -309,13 +321,52 @@ impl BothOutwardCam {
} }
} }
macro_rules! trimming_checks {
($trimming:ident, $view_size:expr) => {
match $trimming {
Trimming::Absolute {
top_left,
bottom_right,
} => {
let view_size: (i16, i16) = $view_size;
// Top left corner is "before" bottom right corner.
assert!(top_left.0 <= bottom_right.0 && top_left.1 <= bottom_right.1);
// All coordinates are positive.
assert!(
top_left.0 >= 0
&& top_left.1 >= 0
&& bottom_right.0 >= 0
&& bottom_right.1 >= 0
);
// All coordinates are within the view.
assert!(
top_left.0 < view_size.0
&& bottom_right.0 < view_size.0
&& top_left.1 < view_size.1
&& bottom_right.1 < view_size.1
);
}
Trimming::Centered { width, height } => {
let view_size: (i16, i16) = $view_size;
// Trim sizes are positive.
assert!(width >= 0 && height >= 0);
// Trim sizes are within the view.
assert!(width <= view_size.0 && height <= view_size.1);
}
Trimming::Off => (),
}
};
}
impl Camera for InwardCam { impl Camera for InwardCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ { fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_IN1 ctru_sys::SELECT_IN1
} }
fn view_size(&self) -> ViewSize { fn view_size(&self) -> ViewSize {
self.view_size self.configuration.view_size
} }
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> { fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
@ -327,7 +378,7 @@ impl Camera for InwardCam {
))?; ))?;
} }
self.view_size = size; self.configuration.view_size = size;
self.set_trimming(Trimming::Off); self.set_trimming(Trimming::Off);
@ -335,67 +386,14 @@ impl Camera for InwardCam {
} }
fn trimming(&self) -> Trimming { fn trimming(&self) -> Trimming {
self.trimming self.configuration.trimming
} }
fn set_trimming(&mut self, trimming: Trimming) { fn set_trimming(&mut self, trimming: Trimming) {
match trimming { // Run checks for all trimming possibilities.
Trimming::Absolute { trimming_checks!(trimming, self.view_size().into());
top_left,
bottom_right,
} => unsafe {
// Top left corner is "before" bottom right corner.
assert!(top_left.0 <= bottom_right.0 && top_left.1 <= bottom_right.1);
// All coordinates are positive.
assert!(
top_left.0 >= 0
&& top_left.1 >= 0
&& bottom_right.0 >= 0
&& bottom_right.1 >= 0
);
// All coordinates are within the view.
assert!(
top_left.0 < view_size.0
&& bottom_right.0 < view_size.0
&& top_left.1 < view_size.1
&& bottom_right.1 < view_size.1
);
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
ResultCode(ctru_sys::CAMU_SetTrimmingParams(
self.port_as_raw(),
top_left.0,
top_left.1,
bottom_right.0,
bottom_right.1,
))?;
Ok(())
},
Trimming::Centered { width, height } => unsafe {
let view_size: (i16, i16) = self.view_size().into();
// Trim sizes are positive.
assert!(width >= 0 && height >= 0);
// Trim sizes are within the view.
assert!(width <= view_size.0 && height <= view_size.1);
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
ResultCode(ctru_sys::CAMU_SetTrimmingParamsCenter( self.configuration.trimming = trimming;
self.port_as_raw(),
width,
height,
view_size.0,
view_size.1,
))?;
Ok(())
},
Trimming::Off => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), false))?;
Ok(())
},
}
} }
} }
@ -405,7 +403,7 @@ impl Camera for OutwardRightCam {
} }
fn view_size(&self) -> ViewSize { fn view_size(&self) -> ViewSize {
self.view_size self.configuration.view_size
} }
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> { fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
@ -417,12 +415,23 @@ impl Camera for OutwardRightCam {
))?; ))?;
} }
self.view_size = size; self.configuration.view_size = size;
self.set_trimming(Trimming::Off)?; self.set_trimming(Trimming::Off);
Ok(()) 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;
}
} }
impl Camera for OutwardLeftCam { impl Camera for OutwardLeftCam {
@ -431,7 +440,7 @@ impl Camera for OutwardLeftCam {
} }
fn view_size(&self) -> ViewSize { fn view_size(&self) -> ViewSize {
self.view_size self.configuration.view_size
} }
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> { fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
@ -443,12 +452,23 @@ impl Camera for OutwardLeftCam {
))?; ))?;
} }
self.view_size = size; self.configuration.view_size = size;
self.set_trimming(Trimming::Off)?; self.set_trimming(Trimming::Off);
Ok(()) 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;
}
} }
impl Camera for BothOutwardCam { impl Camera for BothOutwardCam {
@ -461,7 +481,7 @@ impl Camera for BothOutwardCam {
} }
fn view_size(&self) -> ViewSize { fn view_size(&self) -> ViewSize {
self.view_size self.configuration.view_size
} }
fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> { fn set_view_size(&mut self, size: ViewSize) -> crate::Result<()> {
@ -473,12 +493,23 @@ impl Camera for BothOutwardCam {
))?; ))?;
} }
self.view_size = size; self.configuration.view_size = size;
self.set_trimming(Trimming::Off)?; self.set_trimming(Trimming::Off);
Ok(()) 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. /// Generic functionality common to all cameras.
@ -547,8 +578,8 @@ pub trait Camera {
/// # } /// # }
/// ``` /// ```
#[doc(alias = "CAMU_GetTransferBytes")] #[doc(alias = "CAMU_GetTransferBytes")]
fn max_byte_count(&self) -> crate::Result<usize> { fn max_byte_count(&self) -> usize {
let size = self.final_image_size()?; let size = self.final_image_size();
let mut res: usize = (size.0 as usize * size.1 as usize) * std::mem::size_of::<i16>(); let mut res: usize = (size.0 as usize * size.1 as usize) * std::mem::size_of::<i16>();
@ -557,7 +588,7 @@ pub trait Camera {
res = res * 2; res = res * 2;
} }
Ok(res) res
} }
/// Returns the dimensions of the final image based on the view size, trimming and other /// Returns the dimensions of the final image based on the view size, trimming and other
@ -585,31 +616,18 @@ pub trait Camera {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
fn final_image_size(&self) -> crate::Result<(i16, i16)> { fn final_image_size(&self) -> (i16, i16) {
match self.is_trimming_enabled()? { match self.trimming() {
// Take trimming into account Trimming::Absolute {
true => { top_left,
// This request should never fail, at least not under safe `ctru-rs` usage. bottom_right,
let trimming = self.trimming_configuration()?; } => (bottom_right.0 - top_left.0, bottom_right.1 - top_left.1),
Trimming::Centered { width, height } => (width, height),
let Trimming::Absolute { Trimming::Off => self.view_size().into(),
top_left,
bottom_right,
} = trimming
else {
unreachable!()
};
let width = bottom_right.0 - top_left.0;
let height = bottom_right.1 - top_left.1;
Ok((width, height))
}
// Simply use the full view size.
false => Ok(self.view_size().into()),
} }
} }
/// Returns the [`Trimming`] configuration currently set.
fn trimming(&self) -> Trimming; fn trimming(&self) -> Trimming;
/// Set trimming bounds to trim the camera photo. /// Set trimming bounds to trim the camera photo.
@ -618,37 +636,8 @@ pub trait Camera {
/// Returns whether or not trimming is currently enabled for the camera. /// Returns whether or not trimming is currently enabled for the camera.
#[doc(alias = "CAMU_IsTrimming")] #[doc(alias = "CAMU_IsTrimming")]
fn is_trimming_enabled(&self) -> crate::Result<bool> { fn is_trimming(&self) -> bool {
unsafe { matches!(self.trimming(), Trimming::Off)
let mut trimming = false;
ResultCode(ctru_sys::CAMU_IsTrimming(&mut trimming, self.port_as_raw()))?;
Ok(trimming)
}
}
/// Returns the [`Trimming`] configuration currently set.
///
/// # Notes
///
/// If successful, this function will always return a [`Trimming::Absolute`].
#[doc(alias = "CAMU_GetTrimmingParams")]
fn trimming_configuration(&self) -> crate::Result<Trimming> {
unsafe {
let mut top_left = (0, 0);
let mut bottom_right = (0, 0);
ResultCode(ctru_sys::CAMU_GetTrimmingParams(
&mut top_left.0,
&mut top_left.1,
&mut bottom_right.0,
&mut bottom_right.1,
self.port_as_raw(),
))?;
Ok(Trimming::Absolute {
top_left,
bottom_right,
})
}
} }
/// Set the exposure level of the camera. /// Set the exposure level of the camera.
@ -672,22 +661,6 @@ pub trait Camera {
} }
} }
/// Set the white balance of the camera.
// TODO: Explain what "without base up" means.
#[doc(alias = "CAMU_SetWhiteBalanceWithoutBaseUp")]
fn set_white_balance_without_base_up(
&mut self,
white_balance: WhiteBalance,
) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetWhiteBalanceWithoutBaseUp(
self.camera_as_raw(),
white_balance.into(),
))?;
Ok(())
}
}
/// Set the sharpness of the camera. /// Set the sharpness of the camera.
#[doc(alias = "CAMU_SetSharpness")] #[doc(alias = "CAMU_SetSharpness")]
fn set_sharpness(&mut self, sharpness: i8) -> crate::Result<()> { fn set_sharpness(&mut self, sharpness: i8) -> crate::Result<()> {
@ -913,16 +886,6 @@ pub trait Camera {
} }
} }
/// Set the camera as the current sleep camera.
// TODO: Explain sleep camera
#[doc(alias = "CAMU_SetSleepCamera")]
fn set_sleep_camera(&mut self) -> crate::Result<()> {
unsafe {
ResultCode(ctru_sys::CAMU_SetSleepCamera(self.camera_as_raw()))?;
Ok(())
}
}
/// Request the camera to take a picture and write it in a buffer. /// Request the camera to take a picture and write it in a buffer.
/// ///
/// # Errors /// # Errors
@ -966,21 +929,13 @@ pub trait Camera {
fn take_picture(&mut self, buffer: &mut [u8], timeout: Duration) -> crate::Result<()> { fn take_picture(&mut self, buffer: &mut [u8], timeout: Duration) -> crate::Result<()> {
let full_view: (i16, i16) = self.view_size().into(); let full_view: (i16, i16) = self.view_size().into();
// Check whether the input buffer is big enough for the image. // It seems like doing this as the first step gives the option to use trimming and get correct readings for the transfer bytes.
let max_size =
(final_view.0 as usize * final_view.1 as usize) * std::mem::size_of::<i16>();
if buffer.len() < max_size {
return Err(Error::BufferTooShort {
provided: buffer.len(),
wanted: max_size,
});
}
unsafe { unsafe {
ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?; ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?;
}; };
let transfer_unit = match self.trimming() { // Obtain the final view size and make the needed modifications to the camera.
let final_view: (i16, i16) = match self.trimming() {
Trimming::Absolute { Trimming::Absolute {
top_left, top_left,
bottom_right, bottom_right,
@ -995,29 +950,7 @@ pub trait Camera {
bottom_right.1, bottom_right.1,
))?; ))?;
// The transfer unit is NOT the "max number of bytes" or whatever the docs make you think it is... (bottom_right.0 - top_left.0, bottom_right.1 - top_left.1)
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,
))?;
};
transfer_unit
}, },
Trimming::Centered { width, height } => unsafe { Trimming::Centered { width, height } => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?; ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?;
@ -1030,38 +963,57 @@ pub trait Camera {
full_view.1, full_view.1,
))?; ))?;
// The transfer unit is NOT the "max number of bytes" or whatever the docs make you think it is... (width, height)
let transfer_unit = unsafe {
let mut transfer_unit = 0;
ResultCode(ctru_sys::CAMU_GetMaxBytes(
&mut transfer_unit,
width,
height,
))?;
transfer_unit
};
unsafe {
ResultCode(ctru_sys::CAMU_SetTransferBytes(
self.port_as_raw(),
transfer_unit,
width,
height,
))?;
};
transfer_unit
}, },
Trimming::Off => unsafe { Trimming::Off => unsafe {
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), false))?; ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), false))?;
full_view
}, },
}; };
println!("CAMU_GetMaxBytes{:?}", final_view);
// 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,
))?;
};
// Check whether the input buffer is big enough for the image.
let max_size = (final_view.0 as usize * final_view.1 as usize) * std::mem::size_of::<i16>();
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,
});
}
let receive_event = unsafe { let receive_event = unsafe {
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,
buffer.as_mut_ptr().cast(), buffer.as_mut_ptr().cast(),
@ -1073,6 +1025,7 @@ pub trait Camera {
completion_handle completion_handle
}; };
// Start capturing with the camera.
unsafe { unsafe {
ResultCode(ctru_sys::CAMU_StartCapture(self.port_as_raw()))?; ResultCode(ctru_sys::CAMU_StartCapture(self.port_as_raw()))?;
}; };
@ -1140,18 +1093,12 @@ impl Cam {
}, },
)?; )?;
let mut inner_cam = InwardCam { let configuration = Configuration::new();
view_size: ViewSize::TopLCD,
}; let mut inner_cam = InwardCam { configuration };
let mut outer_right_cam = OutwardRightCam { let mut outer_right_cam = OutwardRightCam { configuration };
view_size: ViewSize::TopLCD, let mut outer_left_cam = OutwardLeftCam { configuration };
}; let mut both_outer_cams = BothOutwardCam { configuration };
let mut outer_left_cam = OutwardLeftCam {
view_size: ViewSize::TopLCD,
};
let mut both_outer_cams = BothOutwardCam {
view_size: ViewSize::TopLCD,
};
inner_cam.set_view_size(ViewSize::TopLCD)?; inner_cam.set_view_size(ViewSize::TopLCD)?;
outer_right_cam.set_view_size(ViewSize::TopLCD)?; outer_right_cam.set_view_size(ViewSize::TopLCD)?;

Loading…
Cancel
Save