Skip to main content

rawlib/
raw_processor.rs

1//! 高级安全的 LibRaw 包装器
2//!
3//! 这个模块提供了一个类型安全的 Rust API 来操作 LibRaw 库。
4//! 它封装了底层的 C FFI 调用,提供了 Rust 风格的错误处理和内存管理。
5//!
6//! 主要功能:
7//! - 安全的 LibRaw 实例管理(RAII 模式)
8//! - 缩略图提取和处理
9//! - 错误转换和处理
10//! - 多平台文件名支持
11
12use crate::ffi;
13use std::ffi::CStr;
14#[cfg(not(windows))]
15use std::ffi::CString;
16use std::fmt;
17use std::path::Path;
18
19/// LibRaw 操作的错误类型
20///
21/// 封装了 LibRaw C 库的错误代码,并提供用户友好的错误信息
22#[derive(Debug, Clone)]
23pub struct RawError {
24    /// LibRaw 错误代码
25    code: i32,
26    /// 错误描述信息
27    message: String,
28}
29
30impl fmt::Display for RawError {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        write!(f, "LibRaw error {}: {}", self.code, self.message)
33    }
34}
35
36impl std::error::Error for RawError {}
37
38/// 模块级别的结果类型别名
39pub type Result<T> = std::result::Result<T, RawError>;
40
41/// 包含格式信息的缩略图数据结构
42///
43/// 这个结构体包含了从 RAW 文件中提取的缩略图的完整信息,
44/// 包括图像格式、尺寸和原始数据。
45#[derive(Debug, Clone)]
46pub struct ThumbnailData {
47    /// 图像格式(JPEG 或位图)
48    pub format: ImageFormat,
49    /// 缩略图宽度(像素)
50    pub width: u16,
51    /// 缩略图高度(像素)
52    pub height: u16,
53    /// 颜色通道数
54    pub colors: u16,
55    /// 每样本位数
56    pub bits: u16,
57    /// 原始图像数据字节数组
58    pub data: Vec<u8>,
59}
60
61/// 图像格式枚举
62///
63/// 定义了 LibRaw 支持的输出图像格式
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum ImageFormat {
66    /// JPEG 压缩图像
67    Jpeg,
68    /// 未压缩的位图(RGB 数据)
69    Bitmap,
70    /// Unknown format
71    Unknown(i32),
72}
73
74impl ImageFormat {
75    fn from_code(code: i32) -> Self {
76        match code {
77            ffi::LIBRAW_IMAGE_JPEG => ImageFormat::Jpeg,
78            ffi::LIBRAW_IMAGE_BITMAP => ImageFormat::Bitmap,
79            _ => ImageFormat::Unknown(code),
80        }
81    }
82    
83    /// Get MIME type for the format
84    pub fn mime_type(&self) -> &'static str {
85        match self {
86            ImageFormat::Jpeg => "image/jpeg",
87            ImageFormat::Bitmap => "image/bmp",
88            ImageFormat::Unknown(_) => "application/octet-stream",
89        }
90    }
91}
92
93/// RAW 图像文件的主要处理器
94///
95/// 这是 RawLib 库的核心结构体,提供了与 LibRaw 库交互的高级接口。
96/// 它使用 RAII 模式管理 LibRaw 实例的生命周期,确保资源被正确释放。
97///
98/// # 安全性
99/// - 使用 RAII 模式自动管理 LibRaw 实例
100/// - 实现了 Send trait,支持跨线程使用
101/// - 所有 FFI 调用都被包装在安全的接口中
102pub struct RawProcessor {
103    /// 指向 LibRaw 数据结构的不透明指针
104    /// 注意:这个指针由 LibRaw 管理,不应该被直接操作
105    data: *mut ffi::libraw_data_t,
106}
107
108impl RawProcessor {
109    /// 创建新的 RawProcessor 实例
110    ///
111    /// 初始化一个 LibRaw 实例并准备处理 RAW 文件。
112    /// 如果初始化失败,将返回错误。
113    ///
114    /// # 返回值
115    /// `Ok(RawProcessor)` - 成功创建的处理器实例
116    /// `Err(RawError)` - 初始化失败时的错误信息
117    ///
118    /// # 示例
119    /// ```no_run
120    /// use rawlib::RawProcessor;
121    /// 
122    /// let processor = RawProcessor::new()?;
123    /// # Ok::<(), rawlib::RawError>(())
124    /// ```
125    pub fn new() -> Result<Self> {
126        // 调用 LibRaw C API 初始化实例
127        let data = unsafe { ffi::libraw_init(ffi::LIBRAW_OPTIONS_NONE) };
128
129        if data.is_null() {
130            return Err(RawError {
131                code: -1,
132                message: "Failed to initialize LibRaw".to_string(),
133            });
134        }
135
136        Ok(RawProcessor { data })
137    }
138    
139    /// 从文件系统打开 RAW 文件
140    ///
141    /// 打开指定的 RAW 文件并读取其基本信息。这个方法会:
142    /// 1. 检查文件是否存在
143    /// 2. 根据平台选择合适的 API(Windows 使用宽字符,Unix 使用 UTF-8)
144    /// 3. 调用 LibRaw 打开文件
145    ///
146    /// # 参数
147    /// * `path` - RAW 文件的路径
148    ///
149    /// # 返回值
150    /// `Ok(())` - 文件成功打开
151    /// `Err(RawError)` - 打开失败时的错误信息
152    ///
153    /// # 平台支持
154    /// - Windows: 使用宽字符 API 支持完整的 Unicode 文件名
155    /// - Unix/Linux/macOS: 使用 UTF-8 字符串
156    pub fn open_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
157        let path_ref = path.as_ref();
158
159        // 验证文件是否存在
160        if !path_ref.exists() {
161            return Err(RawError {
162                code: -1,
163                message: format!("File does not exist: {}", path_ref.display()),
164            });
165        }
166
167        // Windows 平台:使用宽字符 API 以获得更好的 Unicode 支持
168        #[cfg(windows)]
169        {
170            use std::os::windows::ffi::OsStrExt;
171            // 将路径转换为 UTF-16 宽字符字符串,并以 null 结尾
172            let wide: Vec<u16> = path_ref
173                .as_os_str()
174                .encode_wide()
175                .chain(std::iter::once(0))
176                .collect();
177
178            // 调用 LibRaw 的宽字符 API
179            let ret = unsafe { ffi::libraw_open_wfile(self.data, wide.as_ptr()) };
180
181            if ret != ffi::LIBRAW_SUCCESS {
182                let mut err = self.make_error(ret);
183                err.message = format!("{} (file: {})", err.message, path_ref.display());
184                return Err(err);
185            }
186        }
187
188        // Unix 平台:使用常规的 UTF-8 路径
189        #[cfg(not(windows))]
190        {
191            // 确保 Path 可以转换为有效的 UTF-8 字符串
192            let path_str = path_ref.to_str().ok_or_else(|| RawError {
193                code: -1,
194                message: format!("Invalid path encoding: {}", path_ref.display()),
195            })?;
196            
197            let c_path = CString::new(path_str).map_err(|e| RawError {
198                code: -1,
199                message: format!("Path contains null byte: {} (error: {})", path_str, e),
200            })?;
201            
202            let ret = unsafe { ffi::libraw_open_file(self.data, c_path.as_ptr()) };
203            
204            if ret != ffi::LIBRAW_SUCCESS {
205                let mut err = self.make_error(ret);
206                err.message = format!("{} (file: {})", err.message, path_str);
207                return Err(err);
208            }
209        }
210        
211        Ok(())
212    }
213    
214    /// Unpack the RAW data
215    pub fn unpack(&mut self) -> Result<()> {
216        let ret = unsafe { ffi::libraw_unpack(self.data) };
217        
218        if ret != ffi::LIBRAW_SUCCESS {
219            return Err(self.make_error(ret));
220        }
221        
222        Ok(())
223    }
224    
225    /// Process the RAW data (demosaic, white balance, etc.)
226    pub fn dcraw_process(&mut self) -> Result<()> {
227        let ret = unsafe { ffi::libraw_dcraw_process(self.data) };
228        
229        if ret != ffi::LIBRAW_SUCCESS {
230            return Err(self.make_error(ret));
231        }
232        
233        Ok(())
234    }
235    
236    /// Recycle internal buffers
237    pub fn recycle(&mut self) {
238        unsafe { ffi::libraw_recycle(self.data) };
239    }
240    
241    /// Unpack thumbnail data
242    pub fn unpack_thumb(&mut self) -> Result<()> {
243        let ret = unsafe { ffi::libraw_unpack_thumb(self.data) };
244        
245        if ret != ffi::LIBRAW_SUCCESS {
246            return Err(self.make_error(ret));
247        }
248        
249        Ok(())
250    }
251    
252    /// Extract thumbnail as raw bytes
253    /// 
254    /// This method opens the RAW file, extracts the embedded thumbnail,
255    /// and returns it as a byte vector. The thumbnail is typically in JPEG format.
256    /// 
257    /// # Arguments
258    /// 
259    /// * `path` - Path to the RAW file
260    /// 
261    /// # Returns
262    /// 
263    /// Returns `ThumbnailData` containing the thumbnail image data and metadata
264    /// 
265    /// # Example
266    /// 
267    /// ```no_run
268    /// use rawlib::RawProcessor;
269    /// 
270    /// let thumb_data = RawProcessor::extract_thumbnail("image.cr2").unwrap();
271    /// std::fs::write("thumb.jpg", &thumb_data.data).unwrap();
272    /// ```
273    ///
274    /// 这是提取缩略图的最简单方法,它处理了整个流程:
275    /// 1. 创建新的处理器实例
276    /// 2. 打开 RAW 文件
277    /// 3. 解包缩略图数据
278    /// 4. 获取处理后的缩略图数据
279    pub fn extract_thumbnail<P: AsRef<Path>>(path: P) -> Result<ThumbnailData> {
280        let mut processor = RawProcessor::new()?;
281        processor.open_file(path)?;
282        processor.unpack_thumb()?;
283        processor.get_thumbnail()
284    }
285
286    /// 从已打开和解包的文件中获取缩略图数据
287    ///
288    /// 这个方法假设:
289    /// - 文件已经被 `open_file()` 打开
290    /// - 缩略图数据已经被 `unpack_thumb()` 解包
291    ///
292    /// # 返回值
293    /// `Ok(ThumbnailData)` - 包含格式、尺寸和原始数据的缩略图信息
294    /// `Err(RawError)` - 获取缩略图失败时的错误信息
295    pub fn get_thumbnail(&self) -> Result<ThumbnailData> {
296        let mut errc: i32 = 0;
297
298        // 调用 LibRaw 创建内存中的缩略图图像
299        let img_ptr = unsafe {
300            ffi::libraw_dcraw_make_mem_thumb(self.data, &mut errc as *mut i32)
301        };
302        
303        if img_ptr.is_null() {
304            return Err(self.make_error(errc));
305        }
306        
307        // SAFETY: img_ptr is valid and we'll copy the data before freeing
308        let thumbnail = unsafe {
309            let img = &*img_ptr;
310            let format = ImageFormat::from_code(img.image_type);
311            
312            // Calculate the actual data size
313            let data_size = img.data_size as usize;
314            
315            // Copy the image data
316            // SAFETY: data field is a flexible array member, 
317            // actual size is data_size bytes
318            let data_ptr = img.data.as_ptr();
319            let mut data = vec![0u8; data_size];
320            std::ptr::copy_nonoverlapping(data_ptr, data.as_mut_ptr(), data_size);
321            
322            ThumbnailData {
323                format,
324                width: img.width,
325                height: img.height,
326                colors: img.colors,
327                bits: img.bits,
328                data,
329            }
330        };
331        
332        // Free the LibRaw allocated memory
333        unsafe {
334            ffi::libraw_dcraw_clear_mem(img_ptr);
335        }
336        
337        Ok(thumbnail)
338    }
339    
340    /// 获取 LibRaw 版本字符串
341    ///
342    /// 返回当前链接的 LibRaw 库的版本信息字符串,例如 "0.21.4-Release"。
343    /// 这个方法对于调试和兼容性检查很有用。
344    ///
345    /// # 返回值
346    /// 包含版本信息的字符串
347    pub fn version() -> String {
348        unsafe {
349            // 调用 LibRaw 获取版本字符串
350            let ver = ffi::libraw_version();
351            CStr::from_ptr(ver)
352                .to_string_lossy()
353                .into_owned()
354        }
355    }
356
357    /// 获取 LibRaw 版本号(整数格式)
358    ///
359    /// 返回 LibRaw 库的数值版本号,例如 5380(对应 0.21.4)。
360    /// 版本号编码格式为:major * 10000 + minor * 100 + patch
361    ///
362    /// # 返回值
363    /// 整数格式的版本号
364    pub fn version_number() -> i32 {
365        unsafe { ffi::libraw_versionNumber() }
366    }
367    
368    /// 根据 LibRaw 错误代码创建错误信息
369    ///
370    /// 调用 LibRaw 的错误字符串函数获取用户友好的错误描述
371    fn make_error(&self, code: i32) -> RawError {
372        let msg = unsafe {
373            // 获取 LibRaw 提供的错误描述字符串
374            let err_str = ffi::libraw_strerror(code);
375            CStr::from_ptr(err_str)
376                .to_string_lossy()
377                .into_owned()
378        };
379
380        RawError {
381            code,
382            message: msg,
383        }
384    }
385}
386
387/// RAII 资源清理实现
388///
389/// 当 RawProcessor 实例离开作用域时,自动释放 LibRaw 分配的资源。
390/// 这确保了即使在发生错误时也不会出现内存泄漏。
391impl Drop for RawProcessor {
392    fn drop(&mut self) {
393        if !self.data.is_null() {
394            // 调用 LibRaw 的清理函数释放所有资源
395            unsafe { ffi::libraw_close(self.data) };
396        }
397    }
398}
399
400/// 线程安全实现
401///
402/// LibRaw 本身不是线程安全的,但是 RawProcessor 实例可以安全地
403/// 在不同线程之间传递(只要不在多个线程中同时使用同一个实例)。
404/// Send trait 允许我们将 RawProcessor 实例发送到其他线程。
405unsafe impl Send for RawProcessor {}
406
407/// 默认构造函数实现
408///
409/// 提供了默认构造函数,使 RawProcessor 可以更容易地在需要默认值的
410/// 场景中使用。如果创建失败,会 panic,因为这是默认实现的一部分。
411impl Default for RawProcessor {
412    fn default() -> Self {
413        Self::new().expect("Failed to create RawProcessor")
414    }
415}