|
|
@ -110,7 +110,7 @@ pub enum FrameRate { |
|
|
|
|
|
|
|
|
|
|
|
/// White balance settings.
|
|
|
|
/// White balance settings.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// See [`Camera::set_white_balance()`] and [`Camera::set_white_balance_without_base_up()`] to learn how to use this.
|
|
|
|
/// See [`Camera::set_white_balance()`] to learn how to use this.
|
|
|
|
#[doc(alias = "CAMU_WhiteBalance")] |
|
|
|
#[doc(alias = "CAMU_WhiteBalance")] |
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)] |
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)] |
|
|
|
#[repr(u32)] |
|
|
|
#[repr(u32)] |
|
|
@ -230,22 +230,11 @@ pub enum ShutterSound { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Configuration to handle image trimming.
|
|
|
|
/// Configuration to handle image trimming.
|
|
|
|
|
|
|
|
#[non_exhaustive] |
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)] |
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)] |
|
|
|
pub enum Trimming { |
|
|
|
pub enum Trimming { |
|
|
|
/// Trimming configuration based on absolute coordinates of the image.
|
|
|
|
|
|
|
|
Absolute { |
|
|
|
|
|
|
|
/// Top-left corner coordinates (x,y) of the trimmed area.
|
|
|
|
|
|
|
|
top_left: (i16, i16), |
|
|
|
|
|
|
|
/// Bottom-right corner coordinates (x,y) of the trimmed area.
|
|
|
|
|
|
|
|
bottom_right: (i16, i16), |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
/// Trimming configuration relatively to the center of the image.
|
|
|
|
/// Trimming configuration relatively to the center of the image.
|
|
|
|
Centered { |
|
|
|
Centered(ViewSize), |
|
|
|
/// Width of the trimmed area.
|
|
|
|
|
|
|
|
width: i16, |
|
|
|
|
|
|
|
/// Height of the trimmed area.
|
|
|
|
|
|
|
|
height: i16, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
/// Trimming disabled.
|
|
|
|
/// Trimming disabled.
|
|
|
|
Off, |
|
|
|
Off, |
|
|
|
} |
|
|
|
} |
|
|
@ -324,36 +313,12 @@ impl BothOutwardCam { |
|
|
|
macro_rules! trimming_checks { |
|
|
|
macro_rules! trimming_checks { |
|
|
|
($trimming:ident, $view_size:expr) => { |
|
|
|
($trimming:ident, $view_size:expr) => { |
|
|
|
match $trimming { |
|
|
|
match $trimming { |
|
|
|
Trimming::Absolute { |
|
|
|
Trimming::Centered(trim_size) => { |
|
|
|
top_left, |
|
|
|
|
|
|
|
bottom_right, |
|
|
|
|
|
|
|
} => { |
|
|
|
|
|
|
|
let view_size: (i16, i16) = $view_size; |
|
|
|
let view_size: (i16, i16) = $view_size; |
|
|
|
|
|
|
|
let trim_size: (i16, i16) = trim_size.into(); |
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
// Trim sizes are within the view.
|
|
|
|
assert!(width <= view_size.0 && height <= view_size.1); |
|
|
|
assert!(trim_size.0 <= view_size.0 && trim_size.1 <= view_size.1); |
|
|
|
} |
|
|
|
} |
|
|
|
Trimming::Off => (), |
|
|
|
Trimming::Off => (), |
|
|
|
} |
|
|
|
} |
|
|
@ -522,7 +487,8 @@ pub trait Camera { |
|
|
|
/// # Notes
|
|
|
|
/// # Notes
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// This view is the full resolution at which the camera will take the photo.
|
|
|
|
/// This view is the full resolution at which the camera will take the photo.
|
|
|
|
/// If you are interested in the final image dimension, after all processing and modifications, have a look at [`Camera::final_image_size()`].
|
|
|
|
/// 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; |
|
|
|
|
|
|
|
|
|
|
|
/// Returns the raw port of the selected camera.
|
|
|
|
/// Returns the raw port of the selected camera.
|
|
|
@ -558,9 +524,13 @@ pub trait Camera { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns the maximum amount of bytes the final image will occupy based on the view size, trimming, pixel depth and other
|
|
|
|
/// Returns the maximum amount of bytes the final image will occupy in memory based on the view size, trimming, pixel depth and other
|
|
|
|
/// modifications set to the camera.
|
|
|
|
/// modifications set to the camera.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
|
|
|
|
/// # Notes
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Remember to query this information again if *any* changes are applied to the [`Camera`] configuration!
|
|
|
|
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// ```no_run
|
|
|
@ -572,20 +542,19 @@ pub trait Camera { |
|
|
|
///
|
|
|
|
///
|
|
|
|
/// let inward = &cam.inner_cam;
|
|
|
|
/// let inward = &cam.inner_cam;
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// let transfer_count = inward.max_byte_count();
|
|
|
|
/// let transfer_count = inward.final_byte_length();
|
|
|
|
/// #
|
|
|
|
/// #
|
|
|
|
/// # Ok(())
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
#[doc(alias = "CAMU_GetTransferBytes")] |
|
|
|
fn final_byte_length(&self) -> usize { |
|
|
|
fn max_byte_count(&self) -> usize { |
|
|
|
let size = self.final_view_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>(); |
|
|
|
|
|
|
|
|
|
|
|
// If we are taking a picture using both outwards cameras, we need to expect 2 images, rather than just 1
|
|
|
|
// If we are taking a picture using both outwards cameras, we need to expect 2 images, rather than just 1
|
|
|
|
if self.port_as_raw() == ctru_sys::PORT_BOTH { |
|
|
|
if self.port_as_raw() == ctru_sys::PORT_BOTH { |
|
|
|
res = res * 2; |
|
|
|
res *= 2; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
res |
|
|
|
res |
|
|
@ -594,35 +563,33 @@ pub trait Camera { |
|
|
|
/// 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
|
|
|
|
/// modifications set to the camera.
|
|
|
|
/// modifications set to the camera.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
|
|
|
|
/// # Notes
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Remember to query this information again if *any* changes are applied to the [`Camera`] configuration!
|
|
|
|
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// ```no_run
|
|
|
|
/// # use std::error::Error;
|
|
|
|
/// # use std::error::Error;
|
|
|
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
|
|
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
|
|
|
/// #
|
|
|
|
/// #
|
|
|
|
/// use ctru::services::cam::{Cam, Camera};
|
|
|
|
/// use ctru::services::cam::{Cam, Camera, Trimming, ViewSize};
|
|
|
|
/// let cam = Cam::new()?;
|
|
|
|
/// let mut cam = Cam::new()?;
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// let mut inward = &cam.inner_cam;
|
|
|
|
/// let mut inward = &mut cam.inner_cam;
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// inward.set_trimming(Trimming::Centered {
|
|
|
|
/// // We trim the image down so that it fits on a DS screen!
|
|
|
|
/// width: 100,
|
|
|
|
/// inward.set_trimming(Trimming::Centered(ViewSize::DS));
|
|
|
|
/// height: 100,
|
|
|
|
|
|
|
|
/// });
|
|
|
|
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// // This result will take into account the trimming.
|
|
|
|
/// // This result will take into account the trimming.
|
|
|
|
/// let final_resolution = inward.final_image_size();
|
|
|
|
/// let final_resolution = inward.final_view_size();
|
|
|
|
/// #
|
|
|
|
/// #
|
|
|
|
/// # Ok(())
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
fn final_image_size(&self) -> (i16, i16) { |
|
|
|
fn final_view_size(&self) -> (i16, i16) { |
|
|
|
match self.trimming() { |
|
|
|
match self.trimming() { |
|
|
|
Trimming::Absolute { |
|
|
|
Trimming::Centered(trim_size) => trim_size.into(), |
|
|
|
top_left, |
|
|
|
|
|
|
|
bottom_right, |
|
|
|
|
|
|
|
} => (bottom_right.0 - top_left.0, bottom_right.1 - top_left.1), |
|
|
|
|
|
|
|
Trimming::Centered { width, height } => (width, height), |
|
|
|
|
|
|
|
Trimming::Off => self.view_size().into(), |
|
|
|
Trimming::Off => self.view_size().into(), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -905,74 +872,50 @@ pub trait Camera { |
|
|
|
/// # use std::time::Duration;
|
|
|
|
/// # use std::time::Duration;
|
|
|
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
|
|
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
|
|
|
/// #
|
|
|
|
/// #
|
|
|
|
/// use ctru::services::cam::{Cam, Camera, ViewSize, OutputFormat};
|
|
|
|
/// use ctru::services::cam::{Cam, Camera, ViewSize, OutputFormat, WhiteBalance};
|
|
|
|
/// let mut cam = Cam::new()?;
|
|
|
|
/// let mut cam = Cam::new()?;
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// // We borrow the inward facing `Camera`.
|
|
|
|
/// // We borrow the inward facing `Camera`.
|
|
|
|
/// let inward = &mut cam.inner_cam;
|
|
|
|
/// let camera = &mut cam.inner_cam;
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// inward.set_view_size(ViewSize::TopLCD)?;
|
|
|
|
/// camera.set_view_size(ViewSize::TopLCD)?;
|
|
|
|
/// inward.set_output_format(OutputFormat::Rgb565)?;
|
|
|
|
/// camera.set_output_format(OutputFormat::Rgb565)?;
|
|
|
|
/// inward.set_noise_filter(true)?;
|
|
|
|
/// camera.set_noise_filter(true)?;
|
|
|
|
/// inward.set_auto_exposure(true)?;
|
|
|
|
/// camera.set_auto_exposure(true)?;
|
|
|
|
/// inward.set_auto_white_balance(true)?;
|
|
|
|
/// camera.set_white_balance(WhiteBalance::Auto)?;
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// // Size of the top screen buffer at 2 bytes per pixel (RGB565).
|
|
|
|
/// // Size of the top screen buffer at 2 bytes per pixel (RGB565).
|
|
|
|
/// let mut buffer = vec![0; 400*240*2];
|
|
|
|
/// let mut buffer = vec![0; camera.final_byte_length()];
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// // Take picture with 3 seconds of timeout.
|
|
|
|
/// // Take picture with 3 seconds of timeout.
|
|
|
|
/// inward.take_picture(&mut buffer, 400, 240, Duration::from_secs(3));
|
|
|
|
/// camera.take_picture(&mut buffer, Duration::from_secs(3));
|
|
|
|
/// #
|
|
|
|
/// #
|
|
|
|
/// # Ok(())
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
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(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// It seems like doing this as the first step gives the option to use trimming and get correct readings for the transfer bytes.
|
|
|
|
|
|
|
|
unsafe { |
|
|
|
|
|
|
|
ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Obtain the final view size and make the needed modifications to the camera.
|
|
|
|
// Obtain the final view size and make the needed modifications to the camera.
|
|
|
|
let final_view: (i16, i16) = match self.trimming() { |
|
|
|
match self.trimming() { |
|
|
|
Trimming::Absolute { |
|
|
|
Trimming::Centered(trim_size) => unsafe { |
|
|
|
top_left, |
|
|
|
|
|
|
|
bottom_right, |
|
|
|
|
|
|
|
} => unsafe { |
|
|
|
|
|
|
|
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?; |
|
|
|
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?; |
|
|
|
|
|
|
|
|
|
|
|
ResultCode(ctru_sys::CAMU_SetTrimmingParams( |
|
|
|
let full_view: (i16, i16) = self.view_size().into(); |
|
|
|
self.port_as_raw(), |
|
|
|
let trim_size: (i16, i16) = trim_size.into(); |
|
|
|
top_left.0, |
|
|
|
|
|
|
|
top_left.1, |
|
|
|
|
|
|
|
bottom_right.0, |
|
|
|
|
|
|
|
bottom_right.1, |
|
|
|
|
|
|
|
))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(bottom_right.0 - top_left.0, bottom_right.1 - top_left.1) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
Trimming::Centered { width, height } => unsafe { |
|
|
|
|
|
|
|
ResultCode(ctru_sys::CAMU_SetTrimming(self.port_as_raw(), true))?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ResultCode(ctru_sys::CAMU_SetTrimmingParamsCenter( |
|
|
|
ResultCode(ctru_sys::CAMU_SetTrimmingParamsCenter( |
|
|
|
self.port_as_raw(), |
|
|
|
self.port_as_raw(), |
|
|
|
width, |
|
|
|
trim_size.0, |
|
|
|
height, |
|
|
|
trim_size.1, |
|
|
|
full_view.0, |
|
|
|
full_view.0, |
|
|
|
full_view.1, |
|
|
|
full_view.1, |
|
|
|
))?; |
|
|
|
))?; |
|
|
|
|
|
|
|
|
|
|
|
(width, height) |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
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); |
|
|
|
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...
|
|
|
|
let transfer_unit = unsafe { |
|
|
|
let transfer_unit = unsafe { |
|
|
@ -980,8 +923,8 @@ pub trait Camera { |
|
|
|
|
|
|
|
|
|
|
|
ResultCode(ctru_sys::CAMU_GetMaxBytes( |
|
|
|
ResultCode(ctru_sys::CAMU_GetMaxBytes( |
|
|
|
&mut transfer_unit, |
|
|
|
&mut transfer_unit, |
|
|
|
full_view.0, |
|
|
|
final_view.0, |
|
|
|
full_view.1, |
|
|
|
final_view.1, |
|
|
|
))?; |
|
|
|
))?; |
|
|
|
|
|
|
|
|
|
|
|
transfer_unit |
|
|
|
transfer_unit |
|
|
@ -997,7 +940,7 @@ pub trait Camera { |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Check whether the input buffer is big enough for the image.
|
|
|
|
// 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>(); |
|
|
|
let max_size = self.final_byte_length(); |
|
|
|
if buffer.len() < max_size { |
|
|
|
if buffer.len() < max_size { |
|
|
|
// We deactivate the camera prematurely.
|
|
|
|
// We deactivate the camera prematurely.
|
|
|
|
//
|
|
|
|
//
|
|
|
@ -1011,6 +954,11 @@ pub trait Camera { |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsafe { |
|
|
|
|
|
|
|
ResultCode(ctru_sys::CAMU_Activate(self.camera_as_raw()))?; |
|
|
|
|
|
|
|
ResultCode(ctru_sys::CAMU_ClearBuffer(self.port_as_raw()))?; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
let receive_event = unsafe { |
|
|
|
let receive_event = unsafe { |
|
|
|
let mut completion_handle: Handle = 0; |
|
|
|
let mut completion_handle: Handle = 0; |
|
|
|
|
|
|
|
|
|
|
|