#[cfg(any(target_os = "ios", target_os = "macos"))]
mod apple;
#[cfg(any(target_os = "ios", target_os = "macos"))]
use apple::OsCamera;
#[cfg(target_os = "android")]
mod android;
#[cfg(target_os = "android")]
use android::OsCamera;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "linux")]
use linux::OsCamera;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
use windows::OsCamera;
use image::RgbaImage;
use std::sync::{Arc, Mutex};
#[derive(Debug)]
pub enum CameraError {
FailedToGetFrame,
InitializationFailed,
DeviceNotFound,
PermissionDenied,
Unknown(String),
}
impl std::error::Error for CameraError {}
impl std::fmt::Display for CameraError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CameraError::InitializationFailed => write!(f, "Camera initialization failed"),
CameraError::DeviceNotFound => write!(f, "Camera device not found"),
CameraError::PermissionDenied => write!(f, "Camera permission denied"),
CameraError::FailedToGetFrame => write!(f, "Camera failed to get frame"),
CameraError::Unknown(msg) => write!(f, "Unknown camera error: {msg}"),
}
}
}
#[cfg(any(target_os = "ios", target_os = "macos", target_os = "android", target_os = "linux", target_os = "windows"))]
#[derive(Debug, Clone)]
pub struct Camera(OsCamera);
#[cfg(any(target_os = "ios", target_os = "macos", target_os = "android", target_os = "linux", target_os = "windows"))]
impl Camera {
pub fn new() -> Result<Self, CameraError> {
Ok(Camera(OsCamera::new_standard()?))
}
pub fn start_custom() -> Result<Self, CameraError> {
Ok(Camera(OsCamera::new_custom()?))
}
pub fn frame(&self) -> Result<RgbaImage, CameraError> {
self.0.frame()
}
pub fn settings(&mut self) -> Option<Arc<Mutex<CameraSettings>>> {
self.0.settings()
}
pub fn stop(self) {
self.0.stop()
}
}
#[derive(Debug, Clone)]
pub struct CameraSettings {
pub exposure_mode: ExposureMode,
pub custom_exposure: Option<CustomExposure>,
pub exposure_compensation: Option<f32>,
pub exposure_stacking: bool,
pub focus_mode: FocusMode,
pub focus_distance: Option<f32>,
pub focus_point_of_interest: Option<(f32, f32)>,
pub white_balance_mode: WhiteBalanceMode,
pub white_balance_gains: Option<WhiteBalanceGains>,
pub zoom_factor: Option<f32>,
pub frame_rate: Option<f32>,
pub resolution: Option<Resolution>,
pub hdr_enabled: bool,
pub stabilization_enabled: bool,
pub low_light_boost: Option<bool>,
pub scene_mode_hint: Option<SceneMode>,
pub brightness: Option<f32>,
pub contrast: Option<f32>,
pub saturation: Option<f32>,
pub sharpness: Option<f32>,
pub hue: Option<f32>,
pub noise_reduction: Option<f32>,
pub gamma: Option<f32>,
pub color_filter: Option<ColorFilter>,
pub is_updated: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct CustomExposure {
pub duration: f32,
pub iso: f32,
}
impl Default for CustomExposure {
fn default() -> Self {
Self {
iso: 0.0,
duration: 0.0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExposureMode {
Auto,
Continuous,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FocusMode {
Auto,
Continuous,
Locked,
Manual,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum WhiteBalanceMode {
Auto,
Locked,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WhiteBalanceGains {
pub red: f32,
pub green: f32,
pub blue: f32,
}
impl WhiteBalanceGains {
pub fn from(red: f32, green: f32, blue: f32) -> Self {
WhiteBalanceGains { red, green, blue }
}
}
impl Default for WhiteBalanceGains {
fn default() -> Self {
Self {
red: 0.0,
green: 0.0,
blue: 0.0,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Resolution {
pub width: u32,
pub height: u32,
}
#[derive(Debug, Clone, Copy)]
pub enum SceneMode {
Standard,
Portrait,
Night,
Action,
Backlit,
Macro,
}
#[derive(Debug, Clone, Copy)]
pub enum ColorFilter {
None,
Sepia,
Mono,
Vibrant,
Cool,
Warm,
}
impl Default for CameraSettings {
fn default() -> Self {
Self {
exposure_mode: ExposureMode::Continuous,
custom_exposure: None,
exposure_compensation: Some(0.0),
exposure_stacking: false,
focus_mode: FocusMode::Auto,
focus_distance: Some(0.5),
focus_point_of_interest: Some((0.5, 0.5)),
white_balance_mode: WhiteBalanceMode::Auto,
white_balance_gains: None,
zoom_factor: Some(1.0),
frame_rate: Some(30.0),
resolution: Some(Resolution {
width: 1920,
height: 1080,
}),
hdr_enabled: false,
stabilization_enabled: true,
low_light_boost: Some(false),
scene_mode_hint: Some(SceneMode::Standard),
brightness: Some(0.5),
contrast: Some(0.5),
saturation: Some(0.5),
sharpness: None,
hue: Some(0.5),
noise_reduction: None,
gamma: Some(0.5),
color_filter: None,
is_updated: true,
}
}
}
impl CameraSettings {
pub fn set_brightness(&mut self, value: f32) {
self.brightness = Some(value.clamp(0.0, 1.0));
self.is_updated = true;
}
pub fn set_contrast(&mut self, value: f32) {
self.contrast = Some(value.clamp(0.0, 1.0));
self.is_updated = true;
}
pub fn set_saturation(&mut self, value: f32) {
self.saturation = Some(value.clamp(0.0, 1.0));
self.is_updated = true;
}
pub fn set_sharpness(&mut self, value: f32) {
let v = value.clamp(0.0, 1.0);
self.sharpness = if v < 0.1 { None } else { Some(v) };
self.is_updated = true;
}
pub fn set_hue(&mut self, value: f32) {
self.hue = Some(value.clamp(0.0, 1.0));
self.is_updated = true;
}
pub fn set_noise_reduction(&mut self, value: f32) {
let v = value.clamp(0.0, 1.0);
self.noise_reduction = if v < 0.1 { None } else { Some(v) };
self.is_updated = true;
}
pub fn set_gamma(&mut self, value: f32) {
self.gamma = Some(value.clamp(0.0, 1.0));
self.is_updated = true;
}
pub fn set_focus_mode(&mut self, mode: FocusMode) {
if mode != FocusMode::Manual {
self.focus_distance = Some(0.5)
};
self.focus_mode = mode;
self.is_updated = true;
}
pub fn set_focus_distance(&mut self, value: f32) {
if self.focus_mode == FocusMode::Manual {
self.focus_distance = Some(value.clamp(0.0, 1.0));
self.is_updated = true;
}
}
pub fn set_exposure_compensation(&mut self, value: f32) {
self.exposure_compensation = Some((value.clamp(0.0, 1.0) * 4.0) - 2.0);
self.is_updated = true;
}
pub fn set_custom_exposure(&mut self, duration_percentage: f32, iso_percentage: f32) {
self.custom_exposure = Some(CustomExposure {
duration: duration_percentage.clamp(0.0, 1.0),
iso: iso_percentage.clamp(0.0, 1.0),
});
self.exposure_mode = ExposureMode::Custom;
self.is_updated = true;
}
pub fn set_exposure_mode(&mut self, mode: ExposureMode) {
if mode != ExposureMode::Custom {
self.custom_exposure = None
};
self.exposure_mode = mode;
self.is_updated = true;
}
pub fn set_white_balance_mode(&mut self, mode: WhiteBalanceMode) {
if mode != WhiteBalanceMode::Custom {
self.white_balance_gains = None
};
self.white_balance_mode = mode;
self.is_updated = true;
}
pub fn set_white_balance_gains_red(&mut self, red: f32) {
let g = self.white_balance_gains.unwrap_or_default();
self.white_balance_gains =
Some(WhiteBalanceGains::from(red.clamp(0.0, 1.0), g.green, g.blue));
self.white_balance_mode = WhiteBalanceMode::Custom;
self.is_updated = true;
}
pub fn set_white_balance_gains_green(&mut self, green: f32) {
let g = self.white_balance_gains.unwrap_or_default();
self.white_balance_gains = Some(WhiteBalanceGains::from(
g.red,
green.clamp(0.0, 1.0),
g.blue,
));
self.white_balance_mode = WhiteBalanceMode::Custom;
self.is_updated = true;
}
pub fn set_white_balance_gains_blue(&mut self, blue: f32) {
let g = self.white_balance_gains.unwrap_or_default();
self.white_balance_gains = Some(WhiteBalanceGains::from(
g.red,
g.green,
blue.clamp(0.0, 1.0),
));
self.white_balance_mode = WhiteBalanceMode::Custom;
self.is_updated = true;
}
pub fn set_zoom_factor(&mut self, value: f32) {
self.zoom_factor = Some(1.0 + value.clamp(0.0, 1.0) * 9.0);
self.is_updated = true;
}
pub fn set_hdr_enabled(&mut self, enabled: bool) {
self.hdr_enabled = enabled;
self.is_updated = true;
}
pub fn set_stabilization_enabled(&mut self, enabled: bool) {
self.stabilization_enabled = enabled;
self.is_updated = true;
}
pub fn set_low_light_boost(&mut self, enabled: bool) {
self.low_light_boost = Some(enabled);
self.is_updated = true;
}
pub fn set_scene_mode(&mut self, scene: SceneMode) {
self.scene_mode_hint = Some(scene);
self.is_updated = true;
}
pub fn set_focus_point_of_interest(&mut self, x: f32, y: f32) {
self.focus_point_of_interest = Some((x.clamp(0.0, 1.0), y.clamp(0.0, 1.0)));
self.is_updated = true;
}
}