diff --git a/src/camera/mod.rs b/src/camera/mod.rs index b5c87e7..12978e6 100644 --- a/src/camera/mod.rs +++ b/src/camera/mod.rs @@ -14,7 +14,8 @@ use ptp::{ FujiColorChromeFXBlue, FujiCustomSetting, FujiCustomSettingName, FujiDynamicRange, FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiShadowTone, FujiSharpness, - FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, UsbMode, + FujiSmoothSkinEffect, FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, + UsbMode, }, structs::DeviceInfo, }; @@ -257,6 +258,14 @@ impl Camera { pub fn set_color_chrome_fx_blue(&mut self, value: FujiColorChromeFXBlue) -> anyhow::Result<()> { self.r#impl.set_color_chrome_fx_blue(&mut self.ptp, value) } + + pub fn get_smooth_skin_effect(&mut self) -> anyhow::Result { + self.r#impl.get_smooth_skin_effect(&mut self.ptp) + } + + pub fn set_smooth_skin_effect(&mut self, value: FujiSmoothSkinEffect) -> anyhow::Result<()> { + self.r#impl.set_smooth_skin_effect(&mut self.ptp, value) + } } impl Drop for Camera { @@ -789,4 +798,25 @@ pub trait CameraImpl { )?; Ok(()) } + + fn get_smooth_skin_effect(&self, ptp: &mut Ptp) -> anyhow::Result { + let bytes = + self.get_prop_value(ptp, DevicePropCode::FujiStillCustomSettingSmoothSkinEffect)?; + let result = FujiSmoothSkinEffect::try_from_ptp(&bytes)?; + Ok(result) + } + + fn set_smooth_skin_effect( + &self, + ptp: &mut Ptp, + value: FujiSmoothSkinEffect, + ) -> anyhow::Result<()> { + let bytes = value.try_into_ptp()?; + self.set_prop_value( + ptp, + DevicePropCode::FujiStillCustomSettingSmoothSkinEffect, + &bytes, + )?; + Ok(()) + } } diff --git a/src/camera/ptp/hex.rs b/src/camera/ptp/hex.rs index f4bec34..e2bd1e3 100644 --- a/src/camera/ptp/hex.rs +++ b/src/camera/ptp/hex.rs @@ -145,7 +145,7 @@ pub enum DevicePropCode { FujiStillCustomSettingGrainEffect = 0xD195, FujiStillCustomSettingColorChromeEffect = 0xD196, FujiStillCustomSettingColorChromeFXBlue = 0xD197, - // TODO: 0xD198 All 1s + FujiStillCustomSettingSmoothSkinEffect = 0xD198, FujiStillCustomSettingWhiteBalance = 0xD199, FujiStillCustomSettingWhiteBalanceShiftRed = 0xD19A, FujiStillCustomSettingWhiteBalanceShiftBlue = 0xD19B, @@ -875,6 +875,59 @@ impl FromStr for FujiColorChromeFXBlue { } } +#[repr(u16)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Serialize, + IntoPrimitive, + TryFromPrimitive, + PtpSerialize, + PtpDeserialize, + EnumIter, +)] +pub enum FujiSmoothSkinEffect { + Strong = 0x3, + Weak = 0x2, + Off = 0x1, +} + +impl fmt::Display for FujiSmoothSkinEffect { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Strong => write!(f, "Strong"), + Self::Weak => write!(f, "Weak"), + Self::Off => write!(f, "Off"), + } + } +} + +impl FromStr for FujiSmoothSkinEffect { + type Err = anyhow::Error; + + fn from_str(s: &str) -> anyhow::Result { + let input = s.trim().to_lowercase(); + + match input.as_str() { + "strong" => return Ok(Self::Strong), + "weak" => return Ok(Self::Weak), + "off" => return Ok(Self::Off), + _ => {} + } + + let choices: Vec = Self::iter().map(|v| v.to_string()).collect(); + if let Some(best) = suggest_closest(s, &choices) { + bail!("Unknown smooth skin effect '{s}'. Did you mean '{best}'?"); + } + + bail!("Unknown smooth skin effect '{s}'"); + } +} + + #[repr(u16)] #[derive( Debug, diff --git a/src/cli/common/film.rs b/src/cli/common/film.rs index 0c9c312..031df1f 100644 --- a/src/cli/common/film.rs +++ b/src/cli/common/film.rs @@ -1,18 +1,14 @@ use clap::Args; use crate::camera::ptp::hex::{ - FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiCustomSettingName, - FujiDynamicRange, FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, + FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiDynamicRange, + FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiShadowTone, FujiSharpness, - FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, + FujiSmoothSkinEffect, FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, }; #[derive(Args, Debug)] pub struct FilmSimulationOptions { - /// The name of the slot - #[clap(long)] - pub name: Option, - /// The Fujifilm film simulation to use #[clap(long)] pub simulation: Option, @@ -84,4 +80,8 @@ pub struct FilmSimulationOptions { /// Color Chrome FX Blue #[clap(long)] pub color_chrome_fx_blue: Option, + + /// Smooth Skin Effect + #[clap(long)] + pub smooth_skin_effect: Option, } diff --git a/src/cli/simulation/mod.rs b/src/cli/simulation/mod.rs index 089ad80..32a337b 100644 --- a/src/cli/simulation/mod.rs +++ b/src/cli/simulation/mod.rs @@ -5,8 +5,8 @@ use crate::{ FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiCustomSetting, FujiCustomSettingName, FujiDynamicRange, FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, FujiHighlightTone, FujiImageQuality, FujiImageSize, - FujiShadowTone, FujiSharpness, FujiWhiteBalance, FujiWhiteBalanceShift, - FujiWhiteBalanceTemperature, + FujiShadowTone, FujiSharpness, FujiSmoothSkinEffect, FujiWhiteBalance, + FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, }, usb, }; @@ -39,6 +39,10 @@ pub enum SimulationCmd { /// Simulation slot number slot: FujiCustomSetting, + /// The name of the slot + #[clap(long)] + name: Option, + #[command(flatten)] film_simulation_options: FilmSimulationOptions, }, @@ -116,6 +120,7 @@ pub struct FilmSimulationRepr { pub grain: FujiGrainEffect, pub color_chrome_effect: FujiColorChromeEffect, pub color_chrome_fx_blue: FujiColorChromeFXBlue, + pub smooth_skin_effect: FujiSmoothSkinEffect, } impl fmt::Display for FilmSimulationRepr { @@ -145,7 +150,8 @@ impl fmt::Display for FilmSimulationRepr { writeln!(f, "Noise Reduction: {}", self.noise_reduction)?; writeln!(f, "Grain: {}", self.grain)?; writeln!(f, "Color Chrome Effect: {}", self.color_chrome_effect)?; - writeln!(f, "Color Chrome FX Blue: {}", self.color_chrome_fx_blue) + writeln!(f, "Color Chrome FX Blue: {}", self.color_chrome_fx_blue)?; + writeln!(f, "Smooth Skin Effect: {}", self.smooth_skin_effect) } } @@ -173,6 +179,7 @@ fn handle_get(json: bool, device_id: Option<&str>, slot: FujiCustomSetting) -> a grain: camera.get_grain_effect()?, color_chrome_effect: camera.get_color_chrome_effect()?, color_chrome_fx_blue: camera.get_color_chrome_fx_blue()?, + smooth_skin_effect: camera.get_smooth_skin_effect()?, }; if json { @@ -187,13 +194,14 @@ fn handle_get(json: bool, device_id: Option<&str>, slot: FujiCustomSetting) -> a fn handle_set( device_id: Option<&str>, slot: FujiCustomSetting, - options: &FilmSimulationOptions, + name: Option, + options: FilmSimulationOptions, ) -> anyhow::Result<()> { let mut camera = usb::get_camera(device_id)?; camera.set_active_custom_setting(slot)?; // General - if let Some(name) = &options.name { + if let Some(name) = &name { camera.set_custom_setting_name(name)?; } @@ -238,6 +246,10 @@ fn handle_set( camera.set_color_chrome_fx_blue(*color_chrome_fx_blue)?; } + if let Some(smooth_skin_effect) = &options.smooth_skin_effect { + camera.set_smooth_skin_effect(*smooth_skin_effect)?; + } + // White Balance if let Some(white_balance) = &options.white_balance { camera.set_white_balance(*white_balance)?; @@ -328,8 +340,9 @@ pub fn handle(cmd: SimulationCmd, json: bool, device_id: Option<&str>) -> anyhow SimulationCmd::Get { slot } => handle_get(json, device_id, slot), SimulationCmd::Set { slot, + name, film_simulation_options, - } => handle_set(device_id, slot, &film_simulation_options), + } => handle_set(device_id, slot, name, film_simulation_options), SimulationCmd::Export { slot, output_file } => handle_export(device_id, slot, &output_file), SimulationCmd::Import { slot, input_file } => handle_import(device_id, slot, &input_file), }