Skip to main content

exif_oxide/raw/
mod.rs

1//! RAW Image Format Support Module
2//!
3//! This module implements comprehensive RAW metadata extraction for all mainstream manufacturers.
4//! It provides a unified interface for processing manufacturer-specific RAW formats while
5//! following the Trust ExifTool principle exactly.
6//!
7//! ## Supported Formats (Milestone 17 Implementation)
8//!
9//! - **Kyocera** (.raw) - Milestone 17a: Simple ProcessBinaryData format (173 lines)
10//! - **Canon** (CR2, CR3) - Future: Complex TIFF-based with maker notes
11//! - **Nikon** (NEF, NRW) - Future: Integration with existing Nikon implementation
12//! - **Sony** (ARW, SR2, SRF) - Future: Advanced offset management
13//! - **Olympus** (ORF) - Future: Multiple IFD navigation
14//! - **Panasonic** (RW2) - Future: Entry-based offset handling
15//! - **Fujifilm** (RAF) - Future: Non-TIFF format
16//!
17//! ## Architecture
18//!
19//! The RAW processing system uses a hybrid approach:
20//! - **Format Detection**: FileTypeDetector identifies RAW file types
21//! - **RAW Processor**: Central dispatcher routes to manufacturer handlers
22//! - **Handler Traits**: Manufacturer-specific processing implementations
23//! - **TIFF Foundation**: Leverages existing TIFF infrastructure for TIFF-based formats
24//!
25//! ## Trust ExifTool Compliance
26//!
27//! All implementations strictly follow ExifTool's processing logic:
28//! - Exact offset calculations and data parsing
29//! - Identical tag naming and grouping
30//! - Preserved quirks and manufacturer-specific handling
31//! - No "improvements" or "optimizations" to the original logic
32
33pub mod detector;
34pub mod offset;
35pub mod processor;
36
37// Re-export main types for convenience
38pub use detector::{detect_raw_format, RawFormat};
39pub use offset::{EntryBasedOffsetProcessor, OffsetContext, SimpleOffsetProcessor};
40pub use processor::{RawFormatHandler, RawProcessor};
41
42// Import format-specific handlers (will expand as we add more formats)
43pub mod formats {
44    //! Manufacturer-specific RAW format handlers
45
46    pub mod kyocera;
47    pub mod minolta;
48    pub mod panasonic;
49
50    // Future format handlers will be added here:
51    // pub mod canon;
52    // pub mod nikon;
53    // pub mod sony;
54    // pub mod olympus;
55    // pub mod fujifilm;
56}
57
58// Re-export format handlers and utility functions
59pub use formats::kyocera::get_kyocera_tag_name;
60pub use formats::minolta::get_minolta_tag_name;
61pub use formats::panasonic::get_panasonic_tag_name;
62
63pub mod utils {
64    //! RAW processing utilities and helper functions
65
66    /// Reverse a byte string (Kyocera-specific utility)
67    /// ExifTool: KyoceraRaw.pm ReverseString function
68    /// Kyocera stores strings in byte-reversed format for unknown reasons
69    pub fn reverse_string(input: &[u8]) -> String {
70        let reversed_bytes: Vec<u8> = input.iter().copied().rev().collect();
71        String::from_utf8_lossy(&reversed_bytes)
72            .trim_start_matches('\0')
73            .trim_end_matches('\0')
74            .to_string()
75    }
76
77    /// Calculate exposure time from Kyocera-specific encoding
78    /// ExifTool: KyoceraRaw.pm ExposureTime calculation
79    /// Formula: 2^(val/8) / 16000
80    pub fn kyocera_exposure_time(val: u32) -> f64 {
81        if val == 0 {
82            return 0.0;
83        }
84        let exponent = val as f64 / 8.0;
85        2_f64.powf(exponent) / 16000.0
86    }
87
88    /// Calculate F-number from Kyocera-specific encoding  
89    /// ExifTool: KyoceraRaw.pm FNumber calculation
90    /// Formula: 2^(val/16)
91    pub fn kyocera_fnumber(val: u32) -> f64 {
92        if val == 0 {
93            return 0.0;
94        }
95        let exponent = val as f64 / 16.0;
96        2_f64.powf(exponent)
97    }
98
99    /// Convert Kyocera internal ISO values to standard ISO speeds
100    /// ExifTool: KyoceraRaw.pm %isoLookup hash
101    /// Maps internal values 7-19 to ISO speeds 25-400
102    pub fn kyocera_iso_lookup(val: u32) -> Option<u32> {
103        match val {
104            7 => Some(25),
105            8 => Some(32),
106            9 => Some(40),
107            10 => Some(50),
108            11 => Some(64),
109            12 => Some(80),
110            13 => Some(100),
111            14 => Some(125),
112            15 => Some(160),
113            16 => Some(200),
114            17 => Some(250),
115            18 => Some(320),
116            19 => Some(400),
117            _ => None,
118        }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::utils::*;
125
126    #[test]
127    fn test_reverse_string() {
128        let input = b"KYOCERA\0";
129        let result = reverse_string(input);
130        assert_eq!(result, "ARECOYK");
131
132        // Test with null termination
133        let input2 = b"TEST\0\0\0";
134        let result2 = reverse_string(input2);
135        assert_eq!(result2, "TSET");
136    }
137
138    #[test]
139    fn test_kyocera_exposure_time() {
140        // Test some known values
141        assert!((kyocera_exposure_time(0) - 0.0).abs() < f64::EPSILON);
142
143        // Test calculation: 2^(val/8) / 16000
144        let val = 64; // Should be 2^8 / 16000 = 256 / 16000 = 0.016
145        let expected = 2_f64.powf(64.0 / 8.0) / 16000.0;
146        assert!((kyocera_exposure_time(val) - expected).abs() < 0.0001);
147    }
148
149    #[test]
150    fn test_kyocera_fnumber() {
151        // Test some known values
152        assert!((kyocera_fnumber(0) - 0.0).abs() < f64::EPSILON);
153
154        // Test calculation: 2^(val/16)
155        let val = 32; // Should be 2^2 = 4.0
156        let expected = 2_f64.powf(32.0 / 16.0);
157        assert!((kyocera_fnumber(val) - expected).abs() < 0.0001);
158    }
159
160    #[test]
161    fn test_kyocera_iso_lookup() {
162        // Test known mappings
163        assert_eq!(kyocera_iso_lookup(7), Some(25));
164        assert_eq!(kyocera_iso_lookup(13), Some(100));
165        assert_eq!(kyocera_iso_lookup(19), Some(400));
166
167        // Test invalid values
168        assert_eq!(kyocera_iso_lookup(6), None);
169        assert_eq!(kyocera_iso_lookup(20), None);
170        assert_eq!(kyocera_iso_lookup(100), None);
171    }
172}