Steve Cook
2 years ago
4 changed files with 493 additions and 7 deletions
@ -0,0 +1,204 @@
@@ -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 @@
@@ -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