Steve Cook
2 years ago
4 changed files with 493 additions and 7 deletions
@ -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; |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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() }; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue