Browse Source

Merge pull request #65 from SteveCookTU/camera

Initial CAM implementation
pull/77/head
Mark Drobnak 2 years ago committed by GitHub
parent
commit
399d1b2ea2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 138
      ctru-rs/examples/camera-image.rs
  2. 944
      ctru-rs/src/services/cam.rs
  3. 1
      ctru-rs/src/services/mod.rs

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

@ -0,0 +1,138 @@
use ctru::console::Console;
use ctru::gfx::{Screen, Side};
use ctru::services::cam::{Cam, CamOutputFormat, CamShutterSoundType, CamSize, Camera};
use ctru::services::hid::KeyPad;
use ctru::services::{Apt, Hid};
use ctru::Gfx;
use std::time::Duration;
const WIDTH: usize = 400;
const HEIGHT: usize = 240;
// The screen size is the width and height multiplied by 2 and
// then multiplied by 2 again for 3D images
const BUF_SIZE: usize = WIDTH * HEIGHT * 2 * 2;
const WAIT_TIMEOUT: Duration = Duration::from_micros(300);
fn main() {
ctru::init();
let apt = Apt::init().expect("Failed to initialize Apt service.");
let hid = Hid::init().expect("Failed to initialize Hid service.");
let gfx = Gfx::init().expect("Failed to initialize GFX service.");
gfx.top_screen.borrow_mut().set_double_buffering(true);
gfx.bottom_screen.borrow_mut().set_double_buffering(false);
let _console = Console::init(gfx.bottom_screen.borrow_mut());
let mut keys_down;
println!("Initializing camera");
let mut cam = Cam::init().expect("Failed to initialize CAM service.");
{
let camera = &mut cam.outer_right_cam;
camera
.set_view_size(CamSize::CTR_TOP_LCD)
.expect("Failed to set camera size");
camera
.set_output_format(CamOutputFormat::RGB_565)
.expect("Failed to set camera output format");
camera
.set_noise_filter(true)
.expect("Failed to enable noise filter");
camera
.set_auto_exposure(true)
.expect("Failed to enable auto exposure");
camera
.set_auto_white_balance(true)
.expect("Failed to enable auto white balance");
camera
.set_trimming(false)
.expect("Failed to disable trimming");
}
let mut buf = vec![0u8; BUF_SIZE];
println!("\nPress R to take a new picture");
println!("Press Start to exit to Homebrew Launcher");
while apt.main_loop() {
hid.scan_input();
keys_down = hid.keys_down();
if keys_down.contains(KeyPad::KEY_START) {
break;
}
if keys_down.contains(KeyPad::KEY_R) {
println!("Capturing new image");
cam.play_shutter_sound(CamShutterSoundType::NORMAL)
.expect("Failed to play shutter sound");
let camera = &mut cam.outer_right_cam;
buf = camera
.take_picture(
WIDTH.try_into().unwrap(),
HEIGHT.try_into().unwrap(),
WAIT_TIMEOUT,
)
.expect("Failed to take picture");
}
let img = convert_image_to_rgb8(&buf, 0, 0, WIDTH as usize, HEIGHT as usize);
unsafe {
gfx.top_screen
.borrow_mut()
.get_raw_framebuffer(Side::Left)
.ptr
.copy_from(img.as_ptr(), img.len());
}
gfx.flush_buffers();
gfx.swap_buffers();
gfx.wait_for_vblank();
}
}
// The available camera output formats are both using u16 values.
// To write to the frame buffer with the default RGB8 format,
// the values must be converted.
//
// Alternatively, the frame buffer format could be set to RGB565 as well
// but the image would need to be rotated 90 degrees.
fn convert_image_to_rgb8(img: &[u8], x: usize, y: usize, width: usize, height: usize) -> Vec<u8> {
let mut rgb8 = vec![0u8; img.len()];
for j in 0..height {
for i in 0..width {
// Y-coordinate of where to draw in the frame buffer
let draw_y = y + height - j;
// X-coordinate of where to draw in the frame buffer
let draw_x = x + i;
// Initial index of where to draw in the frame buffer based on y and x coordinates
let draw_index = (draw_y + draw_x * height) * 3;
// Index of the pixel to draw within the image buffer
let index = (j * width + i) * 2;
// Pixels in the image are 2 bytes because of the RGB565 format.
let pixel = u16::from_ne_bytes(img[index..index + 2].try_into().unwrap());
// b value from the pixel
let b = (((pixel >> 11) & 0x1F) << 3) as u8;
// g value from the pixel
let g = (((pixel >> 5) & 0x3F) << 2) as u8;
// r value from the pixel
let r = ((pixel & 0x1F) << 3) as u8;
// set the r, g, and b values to the calculated index within the frame buffer
rgb8[draw_index] = r;
rgb8[draw_index + 1] = g;
rgb8[draw_index + 2] = b;
}
}
rgb8
}

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

@ -0,0 +1,944 @@
//! CAM service
//!
//! The CAM service provides access to the cameras. Cameras can return 2D images
//! in the form of byte vectors which can be used for display or other usages.
use crate::services::gspgpu::FramebufferFormat;
use bitflags::bitflags;
use ctru_sys::Handle;
use std::time::Duration;
/// A reference-counted handle to the CAM service and the usable cameras.
/// The service is closed when all instances of this struct fall out of scope.
///
/// This service requires no special permissions to use.
#[non_exhaustive]
pub struct Cam {
pub inner_cam: InwardCam,
pub outer_right_cam: OutwardRightCam,
pub outer_left_cam: OutwardLeftCam,
pub both_outer_cams: BothOutwardCam,
}
bitflags! {
/// A set of flags to be passed to [Camera::flip_image]
#[derive(Default)]
pub struct CamFlip: u32 {
const NONE = ctru_sys::FLIP_NONE;
const HORIZONTAL = ctru_sys::FLIP_HORIZONTAL;
const VERTICAL = ctru_sys::FLIP_VERTICAL;
const REVERSE = ctru_sys::FLIP_REVERSE;
}
}
bitflags! {
/// A set of flags to be passed to [Camera::set_view_size]
#[derive(Default)]
pub struct CamSize: u32 {
const VGA = ctru_sys::SIZE_VGA;
const QVGA = ctru_sys::SIZE_QVGA;
const QQVGA = ctru_sys::SIZE_QQVGA;
const CIF = ctru_sys::SIZE_CIF;
const QCIF = ctru_sys::SIZE_QCIF;
const DS_LCD = ctru_sys::SIZE_DS_LCD;
const DS_LCD_X4 = ctru_sys::SIZE_DS_LCDx4;
const CTR_TOP_LCD = ctru_sys::SIZE_CTR_TOP_LCD;
const CTR_BOTTOM_LCD = ctru_sys::SIZE_CTR_BOTTOM_LCD;
}
}
bitflags! {
/// A set of flags to be passed to [Camera::set_frame_rate]
#[derive(Default)]
pub struct CamFrameRate: u32 {
const RATE_15 = ctru_sys::FRAME_RATE_15;
const RATE_15_TO_5 = ctru_sys::FRAME_RATE_15_TO_5;
const RATE_15_TO_2 = ctru_sys::FRAME_RATE_15_TO_2;
const RATE_10 = ctru_sys::FRAME_RATE_10;
const RATE_8_5 = ctru_sys::FRAME_RATE_8_5;
const RATE_5 = ctru_sys::FRAME_RATE_5;
const RATE_20 = ctru_sys::FRAME_RATE_20;
const RATE_20_TO_5 = ctru_sys::FRAME_RATE_20_TO_5;
const RATE_30 = ctru_sys::FRAME_RATE_30;
const RATE_30_TO_5 = ctru_sys::FRAME_RATE_30_TO_5;
const RATE_15_TO_10 = ctru_sys::FRAME_RATE_15_TO_10;
const RATE_20_TO_10 = ctru_sys::FRAME_RATE_20_TO_10;
const RATE_30_TO_10 = ctru_sys::FRAME_RATE_30_TO_10;
}
}
bitflags! {
/// A set of flags to be passed to [Camera::set_white_balance] or
/// [Camera::set_white_balance_without_base_up]
#[derive(Default)]
pub struct CamWhiteBalance: u32 {
const AUTO = ctru_sys::WHITE_BALANCE_AUTO;
const BALANCE_3200K = ctru_sys::WHITE_BALANCE_3200K;
const BALANCE_4150K = ctru_sys::WHITE_BALANCE_4150K;
const BALANCE_5200K = ctru_sys::WHITE_BALANCE_5200K;
const BALANCE_6000K = ctru_sys::WHITE_BALANCE_6000K;
const BALANCE_7000K = ctru_sys::WHITE_BALANCE_7000K;
const NORMAL = ctru_sys::WHITE_BALANCE_NORMAL;
const TUNGSTEN = ctru_sys::WHITE_BALANCE_TUNGSTEN;
const WHITE_FLUORESCENT_LIGHT = ctru_sys::WHITE_BALANCE_WHITE_FLUORESCENT_LIGHT;
const DAYLIGHT = ctru_sys::WHITE_BALANCE_DAYLIGHT;
const CLOUDY = ctru_sys::WHITE_BALANCE_CLOUDY;
const HORIZON = ctru_sys::WHITE_BALANCE_HORIZON;
const SHADE = ctru_sys::WHITE_BALANCE_SHADE;
}
}
bitflags! {
/// A set of flags to be passed to [Camera::set_photo_mode]
#[derive(Default)]
pub struct CamPhotoMode: u32 {
const NORMAL = ctru_sys::PHOTO_MODE_NORMAL;
const PORTRAIT = ctru_sys::PHOTO_MODE_PORTRAIT;
const LANDSCAPE = ctru_sys::PHOTO_MODE_LANDSCAPE;
const NIGHTVIEW = ctru_sys::PHOTO_MODE_NIGHTVIEW;
const LETTER = ctru_sys::PHOTO_MODE_LETTER;
}
}
bitflags! {
/// A set of flags to be passed to [Camera::set_effect]
#[derive(Default)]
pub struct CamEffect: u32 {
const NONE = ctru_sys::EFFECT_NONE;
const MONO = ctru_sys::EFFECT_MONO;
const SEPIA = ctru_sys::EFFECT_SEPIA;
const NEGATIVE = ctru_sys::EFFECT_NEGATIVE;
const NEGAFILM = ctru_sys::EFFECT_NEGAFILM;
const SEPIA01 = ctru_sys::EFFECT_SEPIA01;
}
}
bitflags! {
/// A set of flags to be passed to [Camera::set_contrast]
#[derive(Default)]
pub struct CamContrast: u32 {
const PATTERN_01 = ctru_sys::CONTRAST_PATTERN_01;
const PATTERN_02 = ctru_sys::CONTRAST_PATTERN_02;
const PATTERN_03 = ctru_sys::CONTRAST_PATTERN_03;
const PATTERN_04 = ctru_sys::CONTRAST_PATTERN_04;
const PATTERN_05 = ctru_sys::CONTRAST_PATTERN_05;
const PATTERN_06 = ctru_sys::CONTRAST_PATTERN_06;
const PATTERN_07 = ctru_sys::CONTRAST_PATTERN_07;
const PATTERN_08 = ctru_sys::CONTRAST_PATTERN_08;
const PATTERN_09 = ctru_sys::CONTRAST_PATTERN_09;
const PATTERN_10 = ctru_sys::CONTRAST_PATTERN_10;
const PATTERN_11 = ctru_sys::CONTRAST_PATTERN_11;
const LOW = ctru_sys::CONTRAST_LOW;
const NORMAL = ctru_sys::CONTRAST_NORMAL;
const HIGH = ctru_sys::CONTRAST_HIGH;
}
}
bitflags! {
/// A set of flags to be passed to [Camera::set_lens_correction]
#[derive(Default)]
pub struct CamLensCorrection: u32 {
const OFF = ctru_sys::LENS_CORRECTION_OFF;
const ON_70 = ctru_sys::LENS_CORRECTION_ON_70;
const ON_90 = ctru_sys::LENS_CORRECTION_ON_90;
const DARK = ctru_sys::LENS_CORRECTION_DARK;
const NORMAL = ctru_sys::LENS_CORRECTION_NORMAL;
const BRIGHT = ctru_sys::LENS_CORRECTION_BRIGHT;
}
}
bitflags! {
/// A set of flags to be passed to [Camera::set_output_format]
#[derive(Default)]
pub struct CamOutputFormat: u32 {
const YUV_422 = ctru_sys::OUTPUT_YUV_422;
const RGB_565 = ctru_sys::OUTPUT_RGB_565;
}
}
impl TryFrom<FramebufferFormat> for CamOutputFormat {
type Error = ();
fn try_from(value: FramebufferFormat) -> Result<Self, Self::Error> {
match value {
FramebufferFormat::Rgb565 => Ok(CamOutputFormat::RGB_565),
_ => Err(()),
}
}
}
impl TryFrom<CamOutputFormat> for FramebufferFormat {
type Error = ();
fn try_from(value: CamOutputFormat) -> Result<Self, Self::Error> {
match value {
CamOutputFormat::RGB_565 => Ok(FramebufferFormat::Rgb565),
_ => Err(()),
}
}
}
bitflags! {
/// A set of flags to be passed to [Cam::play_shutter_sound]
#[derive(Default)]
pub struct CamShutterSoundType: u32 {
const NORMAL = ctru_sys::SHUTTER_SOUND_TYPE_NORMAL;
const MOVIE = ctru_sys::SHUTTER_SOUND_TYPE_MOVIE;
const MOVIE_END = ctru_sys::SHUTTER_SOUND_TYPE_MOVIE_END;
}
}
/// Struct containing coordinates passed to [Camera::set_trimming_params].
pub struct CamTrimmingParams {
x_start: i16,
y_start: i16,
x_end: i16,
y_end: i16,
}
impl CamTrimmingParams {
/// Creates a new [CamTrimmingParams] and guarantees the start coordinates are less than or
/// equal to the end coordinates.
///
/// `x_start <= x_end && y_start <= y_end`
pub fn new(x_start: i16, y_start: i16, x_end: i16, y_end: i16) -> CamTrimmingParams {
assert!(x_start <= x_end && y_start <= y_end);
Self {
x_start,
y_start,
x_end,
y_end,
}
}
}
/// Represents data used by the camera to calibrate image quality
#[derive(Default)]
pub struct ImageQualityCalibrationData(pub ctru_sys::CAMU_ImageQualityCalibrationData);
/// Represents data used by the camera to calibrate image quality when using both outward cameras
#[derive(Default)]
pub struct StereoCameraCalibrationData(pub ctru_sys::CAMU_StereoCameraCalibrationData);
/// Represents the camera on the inside of the 3DS
#[non_exhaustive]
pub struct InwardCam;
impl Camera for InwardCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_IN1
}
}
/// Represents the the outer right camera when the 3DS is open and the dual cameras are pointed
/// away from the user
#[non_exhaustive]
pub struct OutwardRightCam;
impl Camera for OutwardRightCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT1
}
}
/// Represents the the outer left camera when the 3DS is open and the dual cameras are pointed
/// away from the user
#[non_exhaustive]
pub struct OutwardLeftCam;
impl Camera for OutwardLeftCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT2
}
}
/// Represents the both outer cameras combined
#[non_exhaustive]
pub struct BothOutwardCam;
impl BothOutwardCam {
/// Sets whether to enable or disable synchronization
/// of brightness for both left and right cameras
pub fn set_brightness_synchronization(
&mut self,
brightness_synchronization: bool,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetBrightnessSynchronization(brightness_synchronization);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
fn synchronize_vsync_timing(&self) -> crate::Result<()> {
unsafe {
let r =
ctru_sys::CAMU_SynchronizeVsyncTiming(ctru_sys::SELECT_OUT1, ctru_sys::SELECT_OUT2);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
}
impl Camera for BothOutwardCam {
fn camera_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::SELECT_OUT1_OUT2
}
fn port_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::PORT_BOTH
}
}
/// Represents a camera and its functionality
pub trait Camera {
/// Returns the raw value of the selected camera
fn camera_as_raw(&self) -> ctru_sys::u32_;
/// Returns the raw port of the selected camera
fn port_as_raw(&self) -> ctru_sys::u32_ {
ctru_sys::PORT_CAM1
}
/// Returns true if the camera is busy (receiving data)
fn is_busy(&self) -> crate::Result<bool> {
unsafe {
let mut is_busy = false;
let r = ctru_sys::CAMU_IsBusy(&mut is_busy, self.port_as_raw());
if r < 0 {
Err(r.into())
} else {
Ok(is_busy)
}
}
}
/// Returns the maximum amount of transfer bytes based on the view size, trimming, and other
/// modifications set to the camera
fn get_transfer_bytes(&self) -> crate::Result<u32> {
unsafe {
let mut transfer_bytes = 0;
let r = ctru_sys::CAMU_GetTransferBytes(&mut transfer_bytes, self.port_as_raw());
if r < 0 {
Err(r.into())
} else {
Ok(transfer_bytes)
}
}
}
/// Sets whether or not the camera should trim the image based on parameters set by
/// [Camera::set_trimming_params]
fn set_trimming(&mut self, enabled: bool) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetTrimming(self.port_as_raw(), enabled);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Returns whether or not trimming is currently enabled for the camera
fn is_trimming_enabled(&self) -> crate::Result<bool> {
unsafe {
let mut trimming = false;
let r = ctru_sys::CAMU_IsTrimming(&mut trimming, self.port_as_raw());
if r < 0 {
Err(r.into())
} else {
Ok(trimming)
}
}
}
/// Sets trimming parameters based on coordinates specified inside a [CamTrimmingParams]
fn set_trimming_params(&mut self, params: CamTrimmingParams) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetTrimmingParams(
self.port_as_raw(),
params.x_start,
params.y_start,
params.x_end,
params.y_end,
);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Returns the set [CamTrimmingParams] from the camera
fn get_trimming_params(&self) -> crate::Result<CamTrimmingParams> {
unsafe {
let mut x_start = 0;
let mut y_start = 0;
let mut x_end = 0;
let mut y_end = 0;
let r = ctru_sys::CAMU_GetTrimmingParams(
&mut x_start,
&mut y_start,
&mut x_end,
&mut y_end,
self.port_as_raw(),
);
if r < 0 {
Err(r.into())
} else {
Ok(CamTrimmingParams {
x_start,
y_start,
x_end,
y_end,
})
}
}
}
/// Sets the trimming parameters revolving around the center of the image.
/// The new width will be `trim_width / 2` to the left and right of the center.
/// The new height will be `trim_height / 2` above and below the center.
fn set_trimming_params_center(
&self,
trim_width: i16,
trim_height: i16,
cam_width: i16,
cam_height: i16,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetTrimmingParamsCenter(
self.port_as_raw(),
trim_width,
trim_height,
cam_width,
cam_height,
);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the exposure level of the camera
fn set_exposure(&mut self, exposure: i8) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetExposure(self.camera_as_raw(), exposure);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the white balance mod of the camera based on the passed [CamWhiteBalance] argument
fn set_white_balance(&mut self, white_balance: CamWhiteBalance) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetWhiteBalance(self.camera_as_raw(), white_balance.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the white balance mode of the camera based on the passed [CamWhiteBalance] argument
// TODO: Explain base up
fn set_white_balance_without_base_up(
&mut self,
white_balance: CamWhiteBalance,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetWhiteBalanceWithoutBaseUp(
self.camera_as_raw(),
white_balance.bits(),
);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the sharpness of the camera
fn set_sharpness(&mut self, sharpness: i8) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetSharpness(self.camera_as_raw(), sharpness);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets whether auto exposure is enabled or disabled for the camera
fn set_auto_exposure(&mut self, enabled: bool) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetAutoExposure(self.camera_as_raw(), enabled);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Returns true if auto exposure is enabled for the camera
fn is_auto_exposure_enabled(&self) -> crate::Result<bool> {
unsafe {
let mut enabled = false;
let r = ctru_sys::CAMU_IsAutoExposure(&mut enabled, self.camera_as_raw());
if r < 0 {
Err(r.into())
} else {
Ok(enabled)
}
}
}
/// Sets whether auto white balance is enabled or disabled for the camera
fn set_auto_white_balance(&mut self, enabled: bool) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetAutoWhiteBalance(self.camera_as_raw(), enabled);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Returns true if auto white balance is enabled for the camera
fn is_auto_white_balance_enabled(&self) -> crate::Result<bool> {
unsafe {
let mut enabled = false;
let r = ctru_sys::CAMU_IsAutoWhiteBalance(&mut enabled, self.camera_as_raw());
if r < 0 {
Err(r.into())
} else {
Ok(enabled)
}
}
}
/// Sets the flip direction of the camera's image based on the passed [CamFlip] argument
fn flip_image(&mut self, flip: CamFlip) -> crate::Result<()> {
unsafe {
let r =
ctru_sys::CAMU_FlipImage(self.camera_as_raw(), flip.bits(), ctru_sys::CONTEXT_A);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the image resolution of the camera in detail
///
/// # Errors
///
/// This function will error if the coordinates of the first crop point are greater than the
/// coordinates of the second crop point.
///
/// # Arguments
/// * `width` - Width of the image
/// * `height` - height of the image
/// * `crop_0` - The first crop point in which the image will be trimmed
/// * `crop_0` - The second crop point in which the image will be trimmed
fn set_detail_size(
&mut self,
width: i16,
height: i16,
crop_0: (i16, i16),
crop_1: (i16, i16),
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetDetailSize(
self.camera_as_raw(),
width,
height,
crop_0.0,
crop_0.1,
crop_1.0,
crop_1.1,
ctru_sys::CONTEXT_A,
);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the view size of the camera based on the passed [CamSize] argument.
fn set_view_size(&mut self, size: CamSize) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetSize(self.camera_as_raw(), size.bits(), ctru_sys::CONTEXT_A);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the frame rate of the camera based on the passed [CamFrameRate] argument.
fn set_frame_rate(&mut self, frame_rate: CamFrameRate) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetFrameRate(self.camera_as_raw(), frame_rate.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the photo mode of the camera based on the passed [CamPhotoMode] argument.
fn set_photo_mode(&mut self, photo_mode: CamPhotoMode) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetPhotoMode(self.camera_as_raw(), photo_mode.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the effect of the camera based on the passed [CamEffect] argument.
///
/// Multiple effects can be set at once by combining the bitflags of [CamEffect]
fn set_effect(&mut self, effect: CamEffect) -> crate::Result<()> {
unsafe {
let r =
ctru_sys::CAMU_SetEffect(self.camera_as_raw(), effect.bits(), ctru_sys::CONTEXT_A);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the contrast of the camera based on the passed [CamContrast] argument.
fn set_contrast(&mut self, contrast: CamContrast) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetContrast(self.camera_as_raw(), contrast.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the lens correction of the camera based on the passed [CamLensCorrection] argument.
fn set_lens_correction(&mut self, lens_correction: CamLensCorrection) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetLensCorrection(self.camera_as_raw(), lens_correction.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the output format of the camera based on the passed [CamOutputFormat] argument.
fn set_output_format(&mut self, format: CamOutputFormat) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetOutputFormat(
self.camera_as_raw(),
format.bits(),
ctru_sys::CONTEXT_A,
);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the region in which auto exposure should be based on.
///
/// # Arguments
///
/// * `x` - Starting x coordinate of the window
/// * `y` - Starting y coordinate of the window
/// * `width` - Width of the window
/// * `height` - Height of the window
fn set_auto_exposure_window(
&mut self,
x: i16,
y: i16,
width: i16,
height: i16,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetAutoExposureWindow(self.camera_as_raw(), x, y, width, height);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the region in which auto white balance should be based on.
///
/// # Arguments
///
/// * `x` - Starting x coordinate of the window
/// * `y` - Starting y coordinate of the window
/// * `width` - Width of the window
/// * `height` - Height of the window
fn set_auto_white_balance_window(
&mut self,
x: i16,
y: i16,
width: i16,
height: i16,
) -> crate::Result<()> {
unsafe {
let r =
ctru_sys::CAMU_SetAutoWhiteBalanceWindow(self.camera_as_raw(), x, y, width, height);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets whether the noise filter should be enabled or disabled for the camera
fn set_noise_filter(&mut self, enabled: bool) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetNoiseFilter(self.camera_as_raw(), enabled);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Sets the image quality calibration data for the camera based on the passed in
/// [ImageQualityCalibrationData] argument
fn set_image_quality_calibration_data(
&mut self,
data: ImageQualityCalibrationData,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetImageQualityCalibrationData(data.0);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Returns the current [ImageQualityCalibrationData] for the camera
fn get_image_quality_calibration_data(&self) -> crate::Result<ImageQualityCalibrationData> {
unsafe {
let mut data = ImageQualityCalibrationData::default();
let r = ctru_sys::CAMU_GetImageQualityCalibrationData(&mut data.0);
if r < 0 {
Err(r.into())
} else {
Ok(data)
}
}
}
/// Sets the camera as the current sleep camera
// TODO: Explain sleep camera
fn set_sleep_camera(&mut self) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetSleepCamera(self.camera_as_raw());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
/// Requests the camera to take a picture and returns a vector containing the image bytes.
///
/// # Errors
///
/// This will error if the camera is busy or if the timeout duration is reached.
///
/// # Arguments
///
/// * `width` - Width of the desired image
/// * `height` - Height of the desired image
/// * `timeout` - Duration to wait for the image
fn take_picture(
&mut self,
width: u16,
height: u16,
timeout: Duration,
) -> crate::Result<Vec<u8>> {
let transfer_unit = unsafe {
let mut buf_size = 0;
let r = ctru_sys::CAMU_GetMaxBytes(&mut buf_size, width as i16, height as i16);
if r < 0 {
Err(crate::Error::from(r))
} else {
Ok(buf_size)
}
}?;
let screen_size = u32::from(width) * u32::from(width) * 2;
let mut buf = vec![0u8; usize::try_from(screen_size).unwrap()];
unsafe {
let r = ctru_sys::CAMU_SetTransferBytes(
self.port_as_raw(),
transfer_unit,
width as i16,
height as i16,
);
if r < 0 {
return Err(r.into());
}
};
unsafe {
let r = ctru_sys::CAMU_Activate(self.camera_as_raw());
if r < 0 {
return Err(r.into());
}
};
unsafe {
let r = ctru_sys::CAMU_ClearBuffer(self.port_as_raw());
if r < 0 {
return Err(r.into());
}
};
unsafe {
let r = ctru_sys::CAMU_StartCapture(self.port_as_raw());
if r < 0 {
return Err(r.into());
}
};
let receive_event = unsafe {
let mut completion_handle: Handle = 0;
let r = ctru_sys::CAMU_SetReceiving(
&mut completion_handle,
buf.as_mut_ptr() as *mut ::libc::c_void,
self.port_as_raw(),
screen_size,
transfer_unit.try_into().unwrap(),
);
if r < 0 {
Err(crate::Error::from(r))
} else {
Ok(completion_handle)
}
}?;
unsafe {
let r = ctru_sys::svcWaitSynchronization(
receive_event,
timeout.as_nanos().try_into().unwrap(),
);
if r < 0 {
return Err(r.into());
}
};
unsafe {
let r = ctru_sys::CAMU_StopCapture(self.port_as_raw());
if r < 0 {
return Err(r.into());
}
};
unsafe {
let r = ctru_sys::svcCloseHandle(receive_event);
if r < 0 {
return Err(r.into());
}
};
unsafe {
let r = ctru_sys::CAMU_Activate(ctru_sys::SELECT_NONE);
if r < 0 {
return Err(r.into());
}
};
Ok(buf)
}
}
impl Cam {
/// Initializes the CAM service.
///
/// # Errors
///
/// This function will return an error if the service was unable to be initialized.
/// Since this service requires no special or elevated permissions, errors are
/// rare in practice.
pub fn init() -> crate::Result<Cam> {
unsafe {
let r = ctru_sys::camInit();
if r < 0 {
Err(r.into())
} else {
Ok(Cam {
inner_cam: InwardCam,
outer_right_cam: OutwardRightCam,
outer_left_cam: OutwardLeftCam,
both_outer_cams: BothOutwardCam,
})
}
}
}
/// Plays the specified sound based on the [CamShutterSoundType] argument
pub fn play_shutter_sound(&self, sound: CamShutterSoundType) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_PlayShutterSound(sound.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
}
impl Drop for Cam {
fn drop(&mut self) {
unsafe { ctru_sys::camExit() };
}
}

1
ctru-rs/src/services/mod.rs

@ -1,4 +1,5 @@
pub mod apt; pub mod apt;
pub mod cam;
pub mod cfgu; pub mod cfgu;
pub mod fs; pub mod fs;
pub mod gspgpu; pub mod gspgpu;

Loading…
Cancel
Save