use std::os::raw::{c_char, c_uint, c_void, c_longlong, c_long}; use std::ffi::CString; pub struct StandardOutput { pub buffer: Vec, pub width: usize, pub height: usize, } pub struct CompressedOutput { pub buffer: Vec, pub width: usize, pub height: usize, } impl std::fmt::Debug for StandardOutput { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("StandardOutput") .field("width", &self.width) .field("height", &self.height) .finish() } } /// # TIFF Internal Structure /// An opaque type #[repr(C)] pub struct TIFF { _unused: [u8; 0], } /// # TIFF Warning Handler Definition type TIFFWarningHandler = Option; /// # TIFF Ignore Warnings Handle unsafe extern "C" fn tiff_ignore_warning_handle(_module: *const c_char, _fmt: *const c_char, _ap: *mut std::ffi::c_void) { // Do nothing } #[link(name = "tiff")] unsafe extern "C" { fn TIFFSetWarningHandler(handler: TIFFWarningHandler) -> TIFFWarningHandler; fn TIFFOpen(filename: *const c_char, mode: *const c_char) -> *mut TIFF; fn TIFFClose(tiff_ptr: *mut TIFF); fn TIFFGetField(tiff_ptr: *mut TIFF, tag: c_uint, ...) -> c_long; fn TIFFStripSize(tiff_ptr: *mut TIFF) -> c_longlong; fn TIFFNumberOfStrips(tiff_ptr: *mut TIFF) -> c_longlong; fn TIFFReadRawStrip(tiff_ptr: *mut TIFF, strip: c_uint, buf: *mut c_void, size: c_longlong) -> c_longlong; } /// From tiff.h const TIFFTAG_IMAGEWIDTH: u32 = 256; /// From tiff.h const TIFFTAG_IMAGEHEIGHT: u32 = 257; /// # Ignore TIFF Warnings pub fn ignore_warnings() { unsafe { let _ = TIFFSetWarningHandler(Some(tiff_ignore_warning_handle)); } } /// # Open TIFF File pub fn open(filename: &str) -> Option<*mut TIFF> { let c_filename = CString::new(filename).expect("Cast error"); let c_mode = CString::new("r").expect("Cast error"); unsafe { let result = TIFFOpen(c_filename.as_ptr(), c_mode.as_ptr()); if result.is_null() { return None; } else { return Some(result); } } } /// # Close TIFF File pub fn close(tiff_ptr: *mut TIFF) { unsafe { TIFFClose(tiff_ptr); } } pub fn get_image_width(tiff_ptr: *mut TIFF) -> Option { let mut width: u32 = 0; unsafe { let status = TIFFGetField(tiff_ptr, TIFFTAG_IMAGEWIDTH, &mut width as *mut u32); if status < 0 { return None; } else { return Some(width as usize); } } } pub fn get_image_height(tiff_ptr: *mut TIFF) -> Option { let mut height: u32 = 0; unsafe { let status = TIFFGetField(tiff_ptr, TIFFTAG_IMAGEHEIGHT, &mut height as *mut u32); if status < 0 { return None; } else { return Some(height as usize); } } } pub fn get_image_channels(tiff_ptr: *mut TIFF) -> usize { let strip_size = get_strip_size(tiff_ptr); let strip_count = get_strip_count(tiff_ptr); let width = get_image_width(tiff_ptr) .expect("Image should have width!"); let height = get_image_height(tiff_ptr) .expect("Image should have height!"); return (strip_size * strip_count) / (width * height); } fn get_strip_size(tiff_ptr: *mut TIFF) -> usize { unsafe { TIFFStripSize(tiff_ptr) as usize } } fn get_strip_count(tiff_ptr: *mut TIFF) -> usize { unsafe { TIFFNumberOfStrips(tiff_ptr) as usize } } fn read_strip(tiff_ptr: *mut TIFF, strip: u32) -> Vec { let strip_size = get_strip_size(tiff_ptr); assert!(strip_size>0); let mut buf: Vec = vec![0u8; strip_size as usize]; unsafe { TIFFReadRawStrip(tiff_ptr, strip, buf.as_mut_ptr() as *mut c_void, strip_size as c_longlong); } buf } pub fn read(tiff_ptr: *mut TIFF) -> Vec { let strip_size = get_strip_size(tiff_ptr); let strip_count = get_strip_count(tiff_ptr); let mut total_buf: Vec = vec![0u8; (strip_size * strip_count) as usize]; for strip_index in 0..strip_count { let sub_buffer: Vec = read_strip(tiff_ptr, strip_index as u32); let base_index = strip_index * strip_size; for data_index in 0..strip_size { let total_index = base_index + data_index; let data = sub_buffer[data_index as usize]; total_buf[total_index as usize] = data; } } total_buf } pub fn as_standard_format(tiff_file_name: &str) -> Option { if let Some(tiff) = open(tiff_file_name) { let width_opt = get_image_width(tiff); let height_opt = get_image_height(tiff); let channels = get_image_channels(tiff); if (width_opt == None) || (height_opt == None) { return None; } if (channels == 0) || (channels > 4) { return None } let width = width_opt.expect("Width Prechecked"); let height = height_opt.expect("Height Prechecked"); let mut standard_buffer = vec![0u32; (width*height) as usize]; let tiff_data = read(tiff); for x in 0..width { for y in 0..height { for c in 0..channels { let data: u8 = tiff_data[c + channels * (x + width * y)]; let data: u32 = data as u32; let mut data = data << (8 * c); if (channels == 4) && (c == 3) { data = 0; } standard_buffer[x + width*y] += data; } } } close(tiff); return Some(StandardOutput { buffer: standard_buffer, width, height, }); } // Otherwise return None; } fn flood(source: &StandardOutput, destination: &mut Vec, x: usize, y: usize, from_color: u32, to_color: u16) { let width = source.width; let destination_color = destination[x + y * width]; if destination_color != 0 { return; } let source_color = source.buffer[x + y * width]; if source_color != from_color { return; } destination[x + y * width] = to_color; if x > 0 { flood(source, destination, x-1, y, from_color, to_color); } if (x+1) < width { flood(source, destination, x+1, y, from_color, to_color); } if y > 0 { flood(source, destination, x, y-1, from_color, to_color); } if (y+1) < source.height { flood(source, destination, x, y+1, from_color, to_color); } } pub fn standard_to_labels(buffer: StandardOutput) -> CompressedOutput { let mut label: u16 = 1; let mut output_buffer: Vec = vec![0u16; buffer.buffer.len()]; for y in 0..buffer.height { for x in 0..buffer.width { let index = x + y*buffer.width; if buffer.buffer[index] == 0 { continue; } if output_buffer[index] == 0 { let color = buffer.buffer[index]; flood(&buffer, &mut output_buffer, x, y, color, label); label += 1; } } } return CompressedOutput { buffer: output_buffer, width: buffer.width, height: buffer.height, }; } #[cfg(test)] mod tests { use super::*; #[test] fn tiff_strip_size() { ignore_warnings(); if let Some(t_handle) = open(crate::TEST_IMAGE_PATH) { let strip_size = get_strip_size(t_handle); assert!(strip_size > 0); close(t_handle); } else { assert!(false); } } #[test] fn tiff_strip_count() { ignore_warnings(); if let Some(t_handle) = open(crate::TEST_IMAGE_PATH) { let strip_count = get_strip_count(t_handle); assert!(strip_count > 0); close(t_handle); } else { assert!(false); } } #[test] fn tiff_strip_read() { ignore_warnings(); if let Some(t_handle) = open(crate::TEST_IMAGE_PATH) { let _strip = read_strip(t_handle, 0); close(t_handle); } else { assert!(false); } } }