use image::RgbaImage;
#[cfg(any(target_os = "ios", target_os = "macos"))]
mod apple;
#[cfg(any(target_os = "ios", target_os = "macos"))]
mod apple_custom_image;
#[cfg(any(target_os = "ios", target_os = "macos"))]
mod apple_custom_utils;
#[cfg(target_os = "android")]
mod android;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use crate::hardware::camera::apple::AppleCamera;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use crate::hardware::camera::apple_custom_image::AppleCustomCamera;
#[cfg(target_os = "android")]
use crate::hardware::camera::android::AndroidCamera;
#[derive(Debug)]
pub enum CameraError {
AccessDenied,
WaitingForAccess,
FailedToGetFrame,
FailedToOpenCamera,
Error(String),
}
#[derive(Debug, Clone)]
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub enum AppleCameraBackend {
Standard(AppleCamera),
Custom(AppleCustomCamera),
}
#[derive(Debug, Clone)]
pub struct Camera(
#[cfg(any(target_os = "macos", target_os = "ios"))]
AppleCameraBackend,
#[cfg(target_os = "android")]
AndroidCamera,
);
impl Camera {
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn new() -> Result<Self, CameraError> {
let camera = AppleCamera::new();
camera.open_camera();
Ok(Camera(AppleCameraBackend::Standard(camera)))
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn new_unprocessed() -> Result<Self, CameraError> {
let mut camera = AppleCustomCamera::new();
camera.open_camera().map_err(|_e| {
CameraError::FailedToOpenCamera
})?;
Ok(Camera(AppleCameraBackend::Custom(camera)))
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub fn new_unprocessed() -> Result<Self, CameraError> {
Self::new()
}
#[cfg(target_os = "android")]
pub fn new() -> Result<Self, CameraError> {
let mut camera = AndroidCamera::new().map_err(|_| CameraError::AccessDenied)?;
camera.open_camera();
Ok(Camera(camera))
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn new() -> Result<Self, CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn get_frame(&mut self) -> Option<RgbaImage> {
match &mut self.0 {
AppleCameraBackend::Standard(_cam) => {
None
}
AppleCameraBackend::Custom(cam) => {
cam.get_latest_raw_frame()
}
}
}
#[cfg(target_os = "android")]
pub fn get_frame(&mut self) -> Result<(Vec<u8>, usize, usize), CameraError> {
self.0.get_latest_frame().map_err(|_| CameraError::FailedToGetFrame)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn get_frame(&mut self) -> Option<RgbaImage> {
None
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn get_latest_raw_frame(&self) -> Option<RgbaImage> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
None
}
AppleCameraBackend::Custom(cam) => {
cam.get_latest_raw_frame()
}
}
}
#[cfg(target_os = "android")]
pub fn get_latest_raw_frame(&self) -> Option<RgbaImage> {
None
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn get_latest_raw_frame(&self) -> Option<RgbaImage> {
None
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn update_settings<F>(&self, update_fn: F) -> Result<(), CameraError>
where F: FnOnce(&mut ImageSettings),
{
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame) }
AppleCameraBackend::Custom(cam) => {
cam.update_settings(update_fn);
Ok(())
}
}
}
pub fn set_exposure_and_iso(&mut self, duration: f32, iso: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.set_exposure_and_iso(duration, iso).map_err(|e| CameraError::Error(e))
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_brightness(&mut self, brightness: i16) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.brightness = brightness;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_contrast(&mut self, contrast: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.contrast = contrast;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_saturation(&mut self, saturation: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.saturation = saturation;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_gamma(&mut self, gamma: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.gamma = gamma;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_exposure(&mut self, exposure: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.exposure = exposure;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_temperature(&mut self, temperature: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.temperature = temperature;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_white_balance_r(&mut self, white_balance_r: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.white_balance_r = white_balance_r;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_white_balance_g(&mut self, white_balance_g: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.white_balance_g = white_balance_g;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_white_balance_b(&mut self, white_balance_b: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.white_balance_b = white_balance_b;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn set_white_balance_rgb(&mut self, r: f32, g: f32, b: f32) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
settings.white_balance_r = r;
settings.white_balance_g = g;
settings.white_balance_b = b;
});
Ok(())
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn reset_settings(&mut self) -> Result<(), CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
cam.update_settings(|settings| {
*settings = ImageSettings::default();
});
Ok(())
}
}
}
#[cfg(target_os = "android")]
pub fn set_brightness(&mut self, _brightness: i16) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn set_contrast(&mut self, _contrast: f32) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn set_saturation(&mut self, _saturation: f32) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn set_gamma(&mut self, _gamma: f32) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn set_exposure(&mut self, _exposure: f32) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn set_temperature(&mut self, _temperature: f32) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn set_white_balance_r(&mut self, _white_balance_r: f32) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn set_white_balance_g(&mut self, _white_balance_g: f32) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn set_white_balance_b(&mut self, _white_balance_b: f32) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn set_white_balance_rgb(&mut self, _r: f32, _g: f32, _b: f32) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn reset_settings(&mut self) -> Result<(), CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_brightness(&mut self, _brightness: i16) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_contrast(&mut self, _contrast: f32) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_saturation(&mut self, _saturation: f32) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_gamma(&mut self, _gamma: f32) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_exposure(&mut self, _exposure: f32) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_temperature(&mut self, _temperature: f32) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_white_balance_r(&mut self, _white_balance_r: f32) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_white_balance_g(&mut self, _white_balance_g: f32) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_white_balance_b(&mut self, _white_balance_b: f32) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn set_white_balance_rgb(&mut self, _r: f32, _g: f32, _b: f32) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn reset_settings(&mut self) -> Result<(), CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn get_settings(&self) -> Result<ImageSettings, CameraError> {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
Err(CameraError::FailedToGetFrame)
}
AppleCameraBackend::Custom(cam) => {
Ok(cam.get_settings())
}
}
}
#[cfg(target_os = "android")]
pub fn update_settings<F>(&self, _update_fn: F) -> Result<(), CameraError>
where F: FnOnce(&mut ImageSettings),
{
Err(CameraError::FailedToGetFrame)
}
#[cfg(target_os = "android")]
pub fn get_settings(&self) -> Result<ImageSettings, CameraError> {
Err(CameraError::FailedToGetFrame)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn update_settings<F>(&self, _update_fn: F) -> Result<(), CameraError>
where F: FnOnce(&mut ImageSettings),
{
Err(CameraError::AccessDenied)
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn get_settings(&self) -> Result<ImageSettings, CameraError> {
Err(CameraError::AccessDenied)
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn open_and_get_frame() -> Result<(Vec<u8>, usize, usize), CameraError> {
println!("Opening standard camera and getting frame (not supported)");
Err(CameraError::FailedToGetFrame)
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn open_and_get_custom_frame() -> Option<RgbaImage> {
let mut camera = AppleCustomCamera::new();
if let Err(_e) = camera.open_camera() {
return None;
}
let mut wrapper = Camera(AppleCameraBackend::Custom(camera));
for _ in 1..=10 {
std::thread::sleep(std::time::Duration::from_millis(200));
if let Some(frame) = wrapper.get_frame() {
return Some(frame);
}
}
None
}
#[cfg(target_os = "android")]
pub fn open_and_get_frame() -> Result<(Vec<u8>, usize, usize), CameraError> {
let mut camera = AndroidCamera::new().map_err(|_| CameraError::AccessDenied)?;
camera.open_camera();
let mut wrapper = Camera(camera);
wrapper.get_frame()
}
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
pub fn open_and_get_frame() -> Option<RgbaImage> {
None
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn stop_camera(&self) {
match &self.0 {
AppleCameraBackend::Standard(_cam) => {
}
AppleCameraBackend::Custom(cam) => {
cam.stop_camera();
}
}
}
}
impl Default for Camera {
fn default() -> Self {
Self::new().unwrap_or_else(|_| panic!("Failed to create default camera"))
}
}
impl Drop for Camera {
fn drop(&mut self) {
#[cfg(any(target_os = "macos", target_os = "ios"))]
self.stop_camera();
}
}
#[derive(Debug, Clone)]
pub struct ImageSettings {
pub brightness: i16,
pub contrast: f32,
pub saturation: f32,
pub gamma: f32,
pub white_balance_r: f32,
pub white_balance_g: f32,
pub white_balance_b: f32,
pub exposure: f32,
pub temperature: f32,
pub exposure_iso: f32,
pub exposure_duration: f32,
}
impl Default for ImageSettings {
fn default() -> Self {
Self {
brightness: 0,
contrast: 0.0,
saturation: 0.0,
gamma: 2.2,
white_balance_r: 1.0,
white_balance_g: 1.0,
white_balance_b: 1.0,
exposure: 0.0,
temperature: 6500.0,
exposure_iso: 0.0,
exposure_duration: 0.0
}
}
}
impl ImageSettings {
pub fn new() -> Self {
Self::default()
}
pub fn clamp_values(&mut self) {
self.brightness = self.brightness.clamp(-100, 100);
self.contrast = self.contrast.clamp(-1.0, 1.0);
self.saturation = self.saturation.clamp(-1.0, 1.0);
self.gamma = self.gamma.clamp(0.1, 3.0);
self.white_balance_r = self.white_balance_r.clamp(0.5, 2.0);
self.white_balance_g = self.white_balance_g.clamp(0.5, 2.0);
self.white_balance_b = self.white_balance_b.clamp(0.5, 2.0);
self.exposure = self.exposure.clamp(-2.0, 2.0);
self.temperature = self.temperature.clamp(2000.0, 10000.0);
}
pub fn temperature_to_rgb_multipliers(&self) -> [f32; 3] {
let temp = self.temperature;
let temp_scaled = temp / 100.0;
if temp < 6600.0 {
let r = 1.0;
let g = (0.39008157 * temp_scaled.ln() - 0.631_841_4).clamp(0.0, 1.0);
let b = if temp < 2000.0 {
0.0
} else {
(0.54320678 * (temp_scaled - 10.0).ln() - 1.196_254_1).clamp(0.0, 1.0)
};
[r, g, b]
} else {
let r = (1.292_936_2 * (temp_scaled - 60.0).powf(-0.1332047)).clamp(0.0, 1.0);
let g = (1.129_890_9 * (temp_scaled - 60.0).powf(-0.0755148)).clamp(0.0, 1.0);
let b = 1.0;
[r, g, b]
}
}
}