diff --git a/src/camera/mod.rs b/src/camera/mod.rs index 2daf81d..42b60e1 100644 --- a/src/camera/mod.rs +++ b/src/camera/mod.rs @@ -11,12 +11,12 @@ use ptp::{ Ptp, hex::{ CommandCode, DevicePropCode, FujiClarity, FujiColor, FujiColorChromeEffect, - FujiColorChromeFXBlue, FujiCustomSetting, FujiCustomSettingName, FujiDynamicRange, - FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, - FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiMonochromaticColorTemperature, - FujiMonochromaticColorTint, FujiShadowTone, FujiSharpness, FujiSmoothSkinEffect, - FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, ObjectFormat, - UsbMode, + FujiColorChromeFXBlue, FujiColorSpace, FujiCustomSetting, FujiCustomSettingName, + FujiDynamicRange, FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, + FujiHighISONR, FujiHighlightTone, FujiImageQuality, FujiImageSize, + FujiLensModulationOptimizer, FujiMonochromaticColorTemperature, FujiMonochromaticColorTint, + FujiShadowTone, FujiSharpness, FujiSmoothSkinEffect, FujiWhiteBalance, + FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, ObjectFormat, UsbMode, }, structs::{DeviceInfo, ObjectInfo}, }; @@ -99,6 +99,8 @@ impl Camera { get_color_chrome_effect => FujiColorChromeEffect, get_color_chrome_fx_blue => FujiColorChromeFXBlue, get_smooth_skin_effect => FujiSmoothSkinEffect, + get_lens_modulation_optimizer => FujiLensModulationOptimizer, + get_color_space => FujiColorSpace, } camera_with_ptp! { @@ -126,6 +128,8 @@ impl Camera { set_color_chrome_effect(value: &FujiColorChromeEffect) => (), set_color_chrome_fx_blue(value: &FujiColorChromeFXBlue) => (), set_smooth_skin_effect(value: &FujiSmoothSkinEffect) => (), + set_lens_modulation_optimizer(value: &FujiLensModulationOptimizer) => (), + set_color_space(value: &FujiColorSpace) => (), } } @@ -323,28 +327,30 @@ pub trait CameraImpl { } camera_impl_custom_settings! { - active_custom_setting: FujiCustomSetting => DevicePropCode::FujiStillCustomSetting, - custom_setting_name: FujiCustomSettingName => DevicePropCode::FujiStillCustomSettingName, - image_size: FujiImageSize => DevicePropCode::FujiStillCustomSettingImageSize, - image_quality: FujiImageQuality => DevicePropCode::FujiStillCustomSettingImageQuality, - dynamic_range: FujiDynamicRange => DevicePropCode::FujiStillCustomSettingDynamicRange, - dynamic_range_priority: FujiDynamicRangePriority => DevicePropCode::FujiStillCustomSettingDynamicRangePriority, - film_simulation: FujiFilmSimulation => DevicePropCode::FujiStillCustomSettingFilmSimulation, - monochromatic_color_temperature: FujiMonochromaticColorTemperature => DevicePropCode::FujiStillCustomSettingMonochromaticColorTemperature, - monochromatic_color_tint: FujiMonochromaticColorTint => DevicePropCode::FujiStillCustomSettingMonochromaticColorTint, - grain_effect: FujiGrainEffect => DevicePropCode::FujiStillCustomSettingGrainEffect, - white_balance: FujiWhiteBalance => DevicePropCode::FujiStillCustomSettingWhiteBalance, - high_iso_nr: FujiHighISONR => DevicePropCode::FujiStillCustomSettingHighISONR, - highlight_tone: FujiHighlightTone => DevicePropCode::FujiStillCustomSettingHighlightTone, - shadow_tone: FujiShadowTone => DevicePropCode::FujiStillCustomSettingShadowTone, - color: FujiColor => DevicePropCode::FujiStillCustomSettingColor, - sharpness: FujiSharpness => DevicePropCode::FujiStillCustomSettingSharpness, - clarity: FujiClarity => DevicePropCode::FujiStillCustomSettingClarity, - white_balance_shift_red: FujiWhiteBalanceShift => DevicePropCode::FujiStillCustomSettingWhiteBalanceShiftRed, - white_balance_shift_blue: FujiWhiteBalanceShift => DevicePropCode::FujiStillCustomSettingWhiteBalanceShiftBlue, - white_balance_temperature: FujiWhiteBalanceTemperature => DevicePropCode::FujiStillCustomSettingWhiteBalanceTemperature, - color_chrome_effect: FujiColorChromeEffect => DevicePropCode::FujiStillCustomSettingColorChromeEffect, - color_chrome_fx_blue: FujiColorChromeFXBlue => DevicePropCode::FujiStillCustomSettingColorChromeFXBlue, - smooth_skin_effect: FujiSmoothSkinEffect => DevicePropCode::FujiStillCustomSettingSmoothSkinEffect, + active_custom_setting: FujiCustomSetting => DevicePropCode::FujiCustomSetting, + custom_setting_name: FujiCustomSettingName => DevicePropCode::FujiCustomSettingName, + image_size: FujiImageSize => DevicePropCode::FujiCustomSettingImageSize, + image_quality: FujiImageQuality => DevicePropCode::FujiCustomSettingImageQuality, + dynamic_range: FujiDynamicRange => DevicePropCode::FujiCustomSettingDynamicRange, + dynamic_range_priority: FujiDynamicRangePriority => DevicePropCode::FujiCustomSettingDynamicRangePriority, + film_simulation: FujiFilmSimulation => DevicePropCode::FujiCustomSettingFilmSimulation, + monochromatic_color_temperature: FujiMonochromaticColorTemperature => DevicePropCode::FujiCustomSettingMonochromaticColorTemperature, + monochromatic_color_tint: FujiMonochromaticColorTint => DevicePropCode::FujiCustomSettingMonochromaticColorTint, + grain_effect: FujiGrainEffect => DevicePropCode::FujiCustomSettingGrainEffect, + white_balance: FujiWhiteBalance => DevicePropCode::FujiCustomSettingWhiteBalance, + high_iso_nr: FujiHighISONR => DevicePropCode::FujiCustomSettingHighISONR, + highlight_tone: FujiHighlightTone => DevicePropCode::FujiCustomSettingHighlightTone, + shadow_tone: FujiShadowTone => DevicePropCode::FujiCustomSettingShadowTone, + color: FujiColor => DevicePropCode::FujiCustomSettingColor, + sharpness: FujiSharpness => DevicePropCode::FujiCustomSettingSharpness, + clarity: FujiClarity => DevicePropCode::FujiCustomSettingClarity, + white_balance_shift_red: FujiWhiteBalanceShift => DevicePropCode::FujiCustomSettingWhiteBalanceShiftRed, + white_balance_shift_blue: FujiWhiteBalanceShift => DevicePropCode::FujiCustomSettingWhiteBalanceShiftBlue, + white_balance_temperature: FujiWhiteBalanceTemperature => DevicePropCode::FujiCustomSettingWhiteBalanceTemperature, + color_chrome_effect: FujiColorChromeEffect => DevicePropCode::FujiCustomSettingColorChromeEffect, + color_chrome_fx_blue: FujiColorChromeFXBlue => DevicePropCode::FujiCustomSettingColorChromeFXBlue, + smooth_skin_effect: FujiSmoothSkinEffect => DevicePropCode::FujiCustomSettingSmoothSkinEffect, + lens_modulation_optimizer: FujiLensModulationOptimizer => DevicePropCode::FujiCustomSettingLensModulationOptimizer, + color_space: FujiColorSpace => DevicePropCode::FujiCustomSettingColorSpace, } } diff --git a/src/camera/ptp/hex.rs b/src/camera/ptp/hex.rs index df3054c..290cd45 100644 --- a/src/camera/ptp/hex.rs +++ b/src/camera/ptp/hex.rs @@ -137,31 +137,31 @@ pub enum DevicePropCode { FujiUsbMode = 0xd16e, FujiRawConversionRun = 0xD183, FujiRawConversionProfile = 0xD185, - FujiStillCustomSetting = 0xD18C, - FujiStillCustomSettingName = 0xD18D, - FujiStillCustomSettingImageSize = 0xD18E, - FujiStillCustomSettingImageQuality = 0xD18F, - FujiStillCustomSettingDynamicRange = 0xD190, - FujiStillCustomSettingDynamicRangePriority = 0xD191, - FujiStillCustomSettingFilmSimulation = 0xD192, - FujiStillCustomSettingMonochromaticColorTemperature = 0xD193, - FujiStillCustomSettingMonochromaticColorTint = 0xD194, - FujiStillCustomSettingGrainEffect = 0xD195, - FujiStillCustomSettingColorChromeEffect = 0xD196, - FujiStillCustomSettingColorChromeFXBlue = 0xD197, - FujiStillCustomSettingSmoothSkinEffect = 0xD198, - FujiStillCustomSettingWhiteBalance = 0xD199, - FujiStillCustomSettingWhiteBalanceShiftRed = 0xD19A, - FujiStillCustomSettingWhiteBalanceShiftBlue = 0xD19B, - FujiStillCustomSettingWhiteBalanceTemperature = 0xD19C, - FujiStillCustomSettingHighlightTone = 0xD19D, - FujiStillCustomSettingShadowTone = 0xD19E, - FujiStillCustomSettingColor = 0xD19F, - FujiStillCustomSettingSharpness = 0xD1A0, - FujiStillCustomSettingHighISONR = 0xD1A1, - FujiStillCustomSettingClarity = 0xD1A2, - // TODO: 0xD1A3 All 1s - // TODO: 0xD1A4 All 1s + FujiCustomSetting = 0xD18C, + FujiCustomSettingName = 0xD18D, + FujiCustomSettingImageSize = 0xD18E, + FujiCustomSettingImageQuality = 0xD18F, + FujiCustomSettingDynamicRange = 0xD190, + FujiCustomSettingDynamicRangePriority = 0xD191, + FujiCustomSettingFilmSimulation = 0xD192, + FujiCustomSettingMonochromaticColorTemperature = 0xD193, + FujiCustomSettingMonochromaticColorTint = 0xD194, + FujiCustomSettingGrainEffect = 0xD195, + FujiCustomSettingColorChromeEffect = 0xD196, + FujiCustomSettingColorChromeFXBlue = 0xD197, + FujiCustomSettingSmoothSkinEffect = 0xD198, + FujiCustomSettingWhiteBalance = 0xD199, + FujiCustomSettingWhiteBalanceShiftRed = 0xD19A, + FujiCustomSettingWhiteBalanceShiftBlue = 0xD19B, + FujiCustomSettingWhiteBalanceTemperature = 0xD19C, + FujiCustomSettingHighlightTone = 0xD19D, + FujiCustomSettingShadowTone = 0xD19E, + FujiCustomSettingColor = 0xD19F, + FujiCustomSettingSharpness = 0xD1A0, + FujiCustomSettingHighISONR = 0xD1A1, + FujiCustomSettingClarity = 0xD1A2, + FujiCustomSettingLensModulationOptimizer = 0xD1A3, + FujiCustomSettingColorSpace = 0xD1A4, // TODO: 0xD1A5 All 7s FujiBatteryInfo2 = 0xD36B, } @@ -1114,6 +1114,104 @@ impl FromStr for FujiHighISONR { } } +#[repr(u16)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Serialize, + IntoPrimitive, + TryFromPrimitive, + PtpSerialize, + PtpDeserialize, + EnumIter, +)] +pub enum FujiLensModulationOptimizer { + Off = 0x2, + On = 0x1, +} + +impl fmt::Display for FujiLensModulationOptimizer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Off => write!(f, "Off"), + Self::On => write!(f, "On"), + } + } +} + +impl FromStr for FujiLensModulationOptimizer { + type Err = anyhow::Error; + + fn from_str(s: &str) -> anyhow::Result { + let input = s.trim().to_lowercase(); + + match input.as_str() { + "off" | "false" => return Ok(Self::Off), + "on" | "true" => return Ok(Self::On), + _ => {} + } + + let choices: Vec = Self::iter().map(|v| v.to_string()).collect(); + if let Some(best) = suggest_closest(s, &choices) { + bail!("Unknown lens modulation optimizer '{s}'. Did you mean '{best}'?"); + } + + bail!("Unknown lens modulation optimizer '{s}'"); + } +} + +#[repr(u16)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Serialize, + IntoPrimitive, + TryFromPrimitive, + PtpSerialize, + PtpDeserialize, + EnumIter, +)] +pub enum FujiColorSpace { + SRGB = 0x2, + AdobeRGB = 0x1, +} + +impl fmt::Display for FujiColorSpace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::SRGB => write!(f, "sRGB"), + Self::AdobeRGB => write!(f, "Adobe RGB"), + } + } +} + +impl FromStr for FujiColorSpace { + type Err = anyhow::Error; + + fn from_str(s: &str) -> anyhow::Result { + let input = s.trim().to_lowercase(); + + match input.as_str() { + "s" | "srgb" => return Ok(Self::SRGB), + "adobe" | "adobergb" => return Ok(Self::AdobeRGB), + _ => {} + } + + let choices: Vec = Self::iter().map(|v| v.to_string()).collect(); + if let Some(best) = suggest_closest(s, &choices) { + bail!("Unknown color space '{s}'. Did you mean '{best}'?"); + } + + bail!("Unknown color space '{s}'"); + } +} + macro_rules! fuji_i16 { ($name:ident, $min:expr, $max:expr, $step:expr, $scale:literal) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, PtpSerialize, PtpDeserialize)] diff --git a/src/cli/common/film.rs b/src/cli/common/film.rs index 90d60f0..768a613 100644 --- a/src/cli/common/film.rs +++ b/src/cli/common/film.rs @@ -1,11 +1,11 @@ use clap::Args; use crate::camera::ptp::hex::{ - FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiDynamicRange, - FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, - FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiMonochromaticColorTemperature, - FujiMonochromaticColorTint, FujiShadowTone, FujiSharpness, FujiSmoothSkinEffect, - FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, + FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiColorSpace, + FujiDynamicRange, FujiDynamicRangePriority, FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, + FujiHighlightTone, FujiImageQuality, FujiImageSize, FujiLensModulationOptimizer, + FujiMonochromaticColorTemperature, FujiMonochromaticColorTint, FujiShadowTone, FujiSharpness, + FujiSmoothSkinEffect, FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, }; #[derive(Args, Debug)] @@ -93,4 +93,12 @@ pub struct FilmSimulationOptions { /// Smooth Skin Effect #[clap(long)] pub smooth_skin_effect: Option, + + /// Lens Modulation Optimizer + #[clap(long)] + pub lens_modulation_optimizer: Option, + + /// Color Space + #[clap(long)] + pub color_space: Option, } diff --git a/src/cli/simulation/mod.rs b/src/cli/simulation/mod.rs index b26bb22..003a34a 100644 --- a/src/cli/simulation/mod.rs +++ b/src/cli/simulation/mod.rs @@ -2,12 +2,12 @@ use std::fmt; use crate::{ camera::ptp::hex::{ - FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiCustomSetting, - FujiCustomSettingName, FujiDynamicRange, FujiDynamicRangePriority, FujiFilmSimulation, - FujiGrainEffect, FujiHighISONR, FujiHighlightTone, FujiImageQuality, FujiImageSize, - FujiMonochromaticColorTemperature, FujiMonochromaticColorTint, FujiShadowTone, - FujiSharpness, FujiSmoothSkinEffect, FujiWhiteBalance, FujiWhiteBalanceShift, - FujiWhiteBalanceTemperature, + FujiClarity, FujiColor, FujiColorChromeEffect, FujiColorChromeFXBlue, FujiColorSpace, + FujiCustomSetting, FujiCustomSettingName, FujiDynamicRange, FujiDynamicRangePriority, + FujiFilmSimulation, FujiGrainEffect, FujiHighISONR, FujiHighlightTone, FujiImageQuality, + FujiImageSize, FujiLensModulationOptimizer, FujiMonochromaticColorTemperature, + FujiMonochromaticColorTint, FujiShadowTone, FujiSharpness, FujiSmoothSkinEffect, + FujiWhiteBalance, FujiWhiteBalanceShift, FujiWhiteBalanceTemperature, }, usb, }; @@ -16,7 +16,7 @@ use super::common::{ file::{Input, Output}, film::FilmSimulationOptions, }; -use clap::Subcommand; +use clap::{Args, Subcommand}; use log::warn; use serde::Serialize; use strum::IntoEnumIterator; @@ -40,9 +40,8 @@ pub enum SimulationCmd { /// Simulation slot number slot: FujiCustomSetting, - /// The name of the slot - #[clap(long)] - name: Option, + #[command(flatten)] + set_film_simulation_options: SetFilmSimulationOptions, #[command(flatten)] film_simulation_options: FilmSimulationOptions, @@ -69,6 +68,13 @@ pub enum SimulationCmd { }, } +#[derive(Args, Debug)] +pub struct SetFilmSimulationOptions { + /// The name of the slot + #[clap(long)] + name: Option, +} + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CustomSettingRepr { @@ -124,6 +130,8 @@ pub struct FilmSimulationRepr { pub white_balance_temperature: FujiWhiteBalanceTemperature, pub dynamic_range: FujiDynamicRange, pub dynamic_range_priority: FujiDynamicRangePriority, + pub lens_modulation_optimizer: FujiLensModulationOptimizer, + pub color_space: FujiColorSpace, } impl fmt::Display for FilmSimulationRepr { @@ -190,7 +198,14 @@ impl fmt::Display for FilmSimulationRepr { writeln!(f, "Dynamic Range: {}", self.dynamic_range)?; } - writeln!(f, "Dynamic Range Priority: {}", self.dynamic_range_priority) + writeln!(f, "Dynamic Range Priority: {}", self.dynamic_range_priority)?; + + writeln!( + f, + "Lens Modulation Optimizer: {}", + self.lens_modulation_optimizer + )?; + writeln!(f, "Color Space: {}", self.color_space) } } @@ -221,6 +236,8 @@ fn handle_get(json: bool, device_id: Option<&str>, slot: FujiCustomSetting) -> a white_balance_temperature: camera.get_white_balance_temperature()?, dynamic_range: camera.get_dynamic_range()?, dynamic_range_priority: camera.get_dynamic_range_priority()?, + lens_modulation_optimizer: camera.get_lens_modulation_optimizer()?, + color_space: camera.get_color_space()?, }; if json { @@ -236,14 +253,14 @@ fn handle_get(json: bool, device_id: Option<&str>, slot: FujiCustomSetting) -> a fn handle_set( device_id: Option<&str>, slot: FujiCustomSetting, - name: Option<&FujiCustomSettingName>, + set_options: &SetFilmSimulationOptions, options: &FilmSimulationOptions, ) -> anyhow::Result<()> { let mut camera = usb::get_camera(device_id)?; camera.set_active_custom_setting(&slot)?; // General - if let Some(name) = &name { + if let Some(name) = &set_options.name { camera.set_custom_setting_name(name)?; } @@ -401,6 +418,15 @@ fn handle_set( } } + // Extras + if let Some(lens_modulation_optimizer) = &options.lens_modulation_optimizer { + camera.set_lens_modulation_optimizer(lens_modulation_optimizer)?; + } + + if let Some(color_space) = &options.color_space { + camera.set_color_space(color_space)?; + } + Ok(()) } @@ -426,9 +452,14 @@ 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, + set_film_simulation_options, film_simulation_options, - } => handle_set(device_id, slot, name.as_ref(), &film_simulation_options), + } => handle_set( + device_id, + slot, + &set_film_simulation_options, + &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), }