Browse Source

Initial CAM service and example

pull/65/head
Steve Cook 2 years ago
parent
commit
72c9401a5f
  1. 204
      ctru-rs/examples/camera-image.rs
  2. 284
      ctru-rs/src/services/cam.rs
  3. 1
      ctru-rs/src/services/mod.rs
  4. 9
      ctru-sys/src/bin/docstring-to-rustdoc.rs

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

@ -0,0 +1,204 @@
use ctru::console::Console;
use ctru::gfx::{RawFrameBuffer, Screen, Side};
use ctru::services::cam::{
Cam, CamContext, CamOutputFormat, CamPort, CamSelect, CamShutterSoundType, CamSize,
};
use ctru::services::hid::KeyPad;
use ctru::services::{Apt, Hid};
use ctru::Gfx;
use ctru_sys::Handle;
const WIDTH: usize = 400;
const HEIGHT: usize = 240;
const SCREEN_SIZE: usize = WIDTH * HEIGHT * 2;
const BUF_SIZE: usize = SCREEN_SIZE * 2;
const WAIT_TIMEOUT: i64 = 300000000;
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 key_down;
let mut key_held;
println!("Initializing camera");
let mut cam = Cam::init().expect("Failed to initialize CAM service.");
cam.set_size(
CamSelect::SELECT_OUT1_OUT2,
CamSize::SIZE_CTR_TOP_LCD,
CamContext::CONTEXT_A,
)
.expect("Failed to set camera size");
cam.set_output_format(
CamSelect::SELECT_OUT1_OUT2,
CamOutputFormat::OUTPUT_RGB_565,
CamContext::CONTEXT_A,
)
.expect("Failed to set camera output format");
cam.set_noise_filter(CamSelect::SELECT_OUT1_OUT2, true)
.expect("Failed to enable noise filter");
cam.set_auto_exposure(CamSelect::SELECT_OUT1_OUT2, true)
.expect("Failed to enable auto exposure");
cam.set_auto_white_balance(CamSelect::SELECT_OUT1_OUT2, true)
.expect("Failed to enable auto white balance");
cam.set_trimming(CamPort::PORT_CAM1, false)
.expect("Failed to disable trimming for Cam Port 1");
cam.set_trimming(CamPort::PORT_CAM2, false)
.expect("Failed to disable trimming for Cam Port 2");
let mut buf = vec![0u8; BUF_SIZE];
gfx.flush_buffers();
gfx.swap_buffers();
gfx.wait_for_vblank();
let mut held_r = false;
println!("\nPress R to take a new picture");
println!("Press Start to exit to Homebrew Launcher");
gfx.top_screen.borrow_mut().set_3d_enabled(false);
while apt.main_loop() {
hid.scan_input();
key_down = hid.keys_down();
key_held = hid.keys_held();
if key_down.contains(KeyPad::KEY_START) {
break;
}
if key_held.contains(KeyPad::KEY_R) && !held_r {
println!("Capturing new image");
gfx.flush_buffers();
gfx.swap_buffers();
gfx.wait_for_vblank();
held_r = true;
take_picture(&mut cam, buf.as_mut_ptr());
} else if !key_held.contains(KeyPad::KEY_R) {
held_r = false;
}
write_picture_to_frame_buffer_rgb_565(
gfx.top_screen.borrow_mut().get_raw_framebuffer(Side::Left),
buf.as_mut_ptr(),
0,
0,
WIDTH as u16,
HEIGHT as u16,
);
gfx.flush_buffers();
gfx.swap_buffers();
gfx.wait_for_vblank();
}
}
fn take_picture(cam: &mut Cam, buf: *mut u8) {
let mut buf_size = 0;
cam.get_max_bytes(&mut buf_size, WIDTH as i16, HEIGHT as i16)
.expect("Failed to get max bytes");
cam.set_transfer_bytes(CamPort::PORT_BOTH, buf_size, WIDTH as i16, HEIGHT as i16)
.expect("Failed to set transfer bytes");
cam.activate(CamSelect::SELECT_OUT1_OUT2)
.expect("Failed to activate camera");
let mut receive_event: Handle = 0;
let mut receive_event2: Handle = 0;
cam.clear_buffer(CamPort::PORT_BOTH)
.expect("Failed to clear buffer");
cam.synchronize_vsync_timing(CamSelect::SELECT_OUT1, CamSelect::SELECT_OUT2)
.expect("Failed to sync vsync timings");
cam.start_capture(CamPort::PORT_BOTH)
.expect("Failed to start capture");
cam.set_receiving(
&mut receive_event,
buf,
CamPort::PORT_CAM1,
SCREEN_SIZE as u32,
buf_size as i16,
)
.expect("Failed to set receiving");
cam.set_receiving(
&mut receive_event2,
unsafe { buf.add(SCREEN_SIZE) },
CamPort::PORT_CAM2,
SCREEN_SIZE as u32,
buf_size as i16,
)
.expect("Failed to set receiving");
unsafe {
let mut r = ctru_sys::svcWaitSynchronization(receive_event, WAIT_TIMEOUT);
if r < 0 {
panic!("Failed to wait for handle synchronization");
}
r = ctru_sys::svcWaitSynchronization(receive_event2, WAIT_TIMEOUT);
if r < 0 {
panic!("Failed to wait for handle 2 synchronization");
}
};
cam.play_shutter_sound(CamShutterSoundType::SHUTTER_SOUND_TYPE_NORMAL)
.expect("Failed to play shutter sound");
unsafe {
let mut r = ctru_sys::svcCloseHandle(receive_event);
if r < 0 {
panic!("Failed to close handle");
}
r = ctru_sys::svcCloseHandle(receive_event2);
if r < 0 {
panic!("Failed to close handle 2");
}
};
cam.activate(CamSelect::SELECT_NONE)
.expect("Failed to deactivate camera");
}
fn write_picture_to_frame_buffer_rgb_565(
fb: RawFrameBuffer,
img: *mut u8,
x: u16,
y: u16,
width: u16,
height: u16,
) {
let fb_8 = fb.ptr;
let img_16 = img as *mut u16;
let mut draw_x;
let mut draw_y;
for j in 0..height {
for i in 0..width {
draw_y = y + height - j;
draw_x = x + i;
let v = (draw_y as usize + draw_x as usize * height as usize) * 3;
let data = unsafe { *img_16.add(j as usize * width as usize + i as usize) };
let b = (((data >> 11) & 0x1F) << 3) as u8;
let g = (((data >> 5) & 0x3F) << 2) as u8;
let r = ((data & 0x1F) << 3) as u8;
unsafe {
*fb_8.add(v) = r;
*fb_8.add(v + 1) = g;
*fb_8.add(v + 2) = b;
};
}
}
}

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

@ -0,0 +1,284 @@
use bitflags::bitflags;
pub struct Cam(());
bitflags! {
#[derive(Default)]
pub struct CamPort: u32 {
const PORT_NONE = 0;
const PORT_CAM1 = 1;
const PORT_CAM2 = 2;
const PORT_BOTH = Self::PORT_CAM1.bits | Self::PORT_CAM2.bits;
}
}
bitflags! {
#[derive(Default)]
pub struct CamSelect: u32 {
const SELECT_NONE = 0;
const SELECT_OUT1 = 1;
const SELECT_IN1 = 2;
const SELECT_OUT2 = 4;
const SELECT_IN1_OUT1 = Self::SELECT_OUT1.bits | Self::SELECT_IN1.bits;
const SELECT_OUT1_OUT2 = Self::SELECT_OUT1.bits | Self::SELECT_OUT2.bits;
const SELECT_IN1_OUT2 = Self::SELECT_IN1.bits | Self::SELECT_OUT2.bits;
const SELECT_ALL = Self::SELECT_IN1.bits | Self::SELECT_OUT1.bits | Self::SELECT_OUT2.bits;
}
}
bitflags! {
#[derive(Default)]
pub struct CamSize: u32 {
const SIZE_VGA = 0;
const SIZE_QVGA = 1;
const SIZE_QQVGA = 2;
const SIZE_CIF = 3;
const SIZE_QCIF = 4;
const SIZE_DS_LCD = 5;
const SIZE_DS_LCD_X4 = 6;
const SIZE_CTR_TOP_LCD = 7;
const SIZE_CTR_BOTTOM_LCD = Self::SIZE_QVGA.bits;
}
}
bitflags! {
#[derive(Default)]
pub struct CamContext: u32 {
const CONTEXT_NONE = 0;
const CONTEXT_A = 1;
const CONTEXT_B = 2;
const CONTEXT_BOTH = Self::CONTEXT_A.bits | Self::CONTEXT_B.bits;
}
}
bitflags! {
#[derive(Default)]
pub struct CamOutputFormat: u32 {
const OUTPUT_YUV_422 = 0;
const OUTPUT_RGB_565 = 1;
}
}
bitflags! {
#[derive(Default)]
pub struct CamShutterSoundType: u32 {
const SHUTTER_SOUND_TYPE_NORMAL = 0;
const SHUTTER_SOUND_TYPE_MOVIE = 1;
const SHUTTER_SOUND_TYPE_MOVIE_END = 2;
}
}
impl Cam {
pub fn init() -> crate::Result<Cam> {
unsafe {
let r = ctru_sys::camInit();
if r < 0 {
Err(r.into())
} else {
Ok(Cam(()))
}
}
}
pub fn set_size(
&mut self,
camera: CamSelect,
size: CamSize,
context: CamContext,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetSize(camera.bits(), size.bits(), context.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn set_output_format(
&mut self,
camera: CamSelect,
format: CamOutputFormat,
context: CamContext,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetOutputFormat(camera.bits(), format.bits(), context.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn set_noise_filter(&mut self, camera: CamSelect, enabled: bool) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetNoiseFilter(camera.bits(), enabled);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn set_auto_exposure(&mut self, camera: CamSelect, enabled: bool) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetAutoExposure(camera.bits(), enabled);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn set_auto_white_balance(
&mut self,
camera: CamSelect,
enabled: bool,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetAutoWhiteBalance(camera.bits(), enabled);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn set_trimming(&mut self, port: CamPort, enabled: bool) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetTrimming(port.bits(), enabled);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn get_max_bytes(&self, buf_size: &mut u32, width: i16, height: i16) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_GetMaxBytes(buf_size, width, height);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn set_transfer_bytes(
&mut self,
port: CamPort,
buf_size: u32,
width: i16,
height: i16,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetTransferBytes(port.bits(), buf_size, width, height);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn clear_buffer(&mut self, port: CamPort) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_ClearBuffer(port.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn synchronize_vsync_timing(
&mut self,
camera1: CamSelect,
camera2: CamSelect,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SynchronizeVsyncTiming(camera1.bits(), camera2.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn set_receiving(
&mut self,
handle: &mut u32,
buf: *mut u8,
port: CamPort,
size: u32,
buf_size: i16,
) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_SetReceiving(handle, buf as *mut ::libc::c_void, port.bits(), size, buf_size);
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
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(())
}
}
}
pub fn start_capture(&self, port: CamPort) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_StartCapture(port.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn stop_capture(&self, port: CamPort) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_StopCapture(port.bits());
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
pub fn activate(&mut self, camera: CamSelect) -> crate::Result<()> {
unsafe {
let r = ctru_sys::CAMU_Activate(camera.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 fs; pub mod fs;
pub mod gspgpu; pub mod gspgpu;
pub mod hid; pub mod hid;

9
ctru-sys/src/bin/docstring-to-rustdoc.rs

@ -19,8 +19,8 @@
//! The followings are _partially_ transformed to Rustdoc format: //! The followings are _partially_ transformed to Rustdoc format:
//! * `@param` //! * `@param`
use std::{env, fs, io};
use std::path::Path; use std::path::Path;
use std::{env, fs, io};
fn main() -> io::Result<()> { fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
@ -33,8 +33,7 @@ fn main() -> io::Result<()> {
.map(|v| { .map(|v| {
// Only modify lines with the following structure: `` #[doc ... ] `` // Only modify lines with the following structure: `` #[doc ... ] ``
if v.trim_start().starts_with("#[doc") && v.trim_end().ends_with("]") { if v.trim_start().starts_with("#[doc") && v.trim_end().ends_with("]") {
v v.replace("@brief", "")
.replace("@brief", "")
// Example: ``@param offset Offset of the RomFS...`` -> ``- offset Offset of the RomFS...`` // Example: ``@param offset Offset of the RomFS...`` -> ``- offset Offset of the RomFS...``
// Will improve in the future // Will improve in the future
.replace("@param", "* ") .replace("@param", "* ")
@ -54,9 +53,7 @@ fn main() -> io::Result<()> {
String::from(v) String::from(v)
} }
}) })
.map(|v| { .map(|v| v + "\n")
v + "\n"
})
.collect::<String>(); .collect::<String>();
let old_bindings_path = bindings_path.to_str().unwrap().to_owned() + ".old"; let old_bindings_path = bindings_path.to_str().unwrap().to_owned() + ".old";

Loading…
Cancel
Save