tiffwrite/
lib.rs

1#[cfg(feature = "python")]
2mod py;
3
4use anyhow::Result;
5use chrono::Utc;
6use flate2::write::ZlibEncoder;
7use ndarray::{s, ArcArray2, AsArray, Ix2};
8use num::{traits::ToBytes, Complex, FromPrimitive, Rational32};
9use rayon::prelude::*;
10use std::collections::HashSet;
11use std::fs::{File, OpenOptions};
12use std::hash::{DefaultHasher, Hash, Hasher};
13use std::io::{Read, Seek, SeekFrom, Write};
14use std::path::Path;
15use std::time::Duration;
16use std::{cmp::Ordering, collections::HashMap};
17use std::{
18    thread,
19    thread::{available_parallelism, sleep, JoinHandle},
20};
21use zstd::zstd_safe::CompressionLevel;
22use zstd::{stream::Encoder, DEFAULT_COMPRESSION_LEVEL};
23
24const TAG_SIZE: usize = 20;
25const OFFSET_SIZE: usize = 8;
26const OFFSET: u64 = 16;
27
28/// Compression: deflate or zstd
29#[derive(Clone, Debug)]
30pub enum Compression {
31    Deflate,
32    Zstd(CompressionLevel),
33}
34
35impl Compression {
36    fn index(&self) -> u16 {
37        match self {
38            Compression::Deflate => 8,
39            Compression::Zstd(_) => 50000,
40        }
41    }
42}
43
44/// Image File Directory
45#[allow(clippy::upper_case_acronyms)]
46#[derive(Clone, Debug)]
47struct IFD {
48    tags: HashSet<Tag>,
49}
50
51impl IFD {
52    /// new IFD with empty set of tags
53    pub fn new() -> Self {
54        IFD {
55            tags: HashSet::new(),
56        }
57    }
58
59    fn write(&mut self, ijtifffile: &mut IJTiffFile, where_to_write_offset: u64) -> Result<u64> {
60        let mut tags = self.tags.drain().collect::<Vec<_>>();
61        tags.sort();
62        ijtifffile.file.seek(SeekFrom::End(0))?;
63        if ijtifffile.file.stream_position()? % 2 == 1 {
64            ijtifffile.file.write_all(&[0])?;
65        }
66        let offset = ijtifffile.file.stream_position()?;
67        ijtifffile
68            .file
69            .write_all(&(tags.len() as u64).to_le_bytes())?;
70
71        for tag in tags.iter_mut() {
72            tag.write_tag(ijtifffile)?;
73        }
74        let where_to_write_next_ifd_offset = ijtifffile.file.stream_position()?;
75        ijtifffile.file.write_all(&[0; OFFSET_SIZE])?;
76        for tag in tags.iter() {
77            tag.write_data(ijtifffile)?;
78        }
79        ijtifffile
80            .file
81            .seek(SeekFrom::Start(where_to_write_offset))?;
82        ijtifffile.file.write_all(&offset.to_le_bytes())?;
83        Ok(where_to_write_next_ifd_offset)
84    }
85}
86
87/// Tiff tag, use one of the constructors to get a tag of a specific type
88#[derive(Clone, Debug, Eq)]
89pub struct Tag {
90    code: u16,
91    bytes: Vec<u8>,
92    ttype: u16,
93    offset: u64,
94}
95
96impl PartialOrd<Self> for Tag {
97    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
98        Some(self.cmp(other))
99    }
100}
101
102impl Ord for Tag {
103    fn cmp(&self, other: &Self) -> Ordering {
104        self.code.cmp(&other.code)
105    }
106}
107
108impl PartialEq for Tag {
109    fn eq(&self, other: &Self) -> bool {
110        self.code == other.code
111    }
112}
113
114impl Hash for Tag {
115    fn hash<H: Hasher>(&self, state: &mut H) {
116        self.code.hash(state);
117    }
118}
119
120impl Tag {
121    pub fn new(code: u16, bytes: Vec<u8>, ttype: u16) -> Self {
122        Tag {
123            code,
124            bytes,
125            ttype,
126            offset: 0,
127        }
128    }
129
130    pub fn byte(code: u16, value: &Vec<u8>) -> Self {
131        Tag::new(code, value.to_owned(), 1)
132    }
133
134    pub fn ascii(code: u16, value: &str) -> Self {
135        let mut bytes = value.as_bytes().to_vec();
136        bytes.push(0);
137        Tag::new(code, bytes, 2)
138    }
139
140    pub fn short(code: u16, value: &[u16]) -> Self {
141        Tag::new(
142            code,
143            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
144            3,
145        )
146    }
147
148    pub fn long(code: u16, value: &[u32]) -> Self {
149        Tag::new(
150            code,
151            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
152            4,
153        )
154    }
155
156    pub fn rational(code: u16, value: &[Rational32]) -> Self {
157        Tag::new(
158            code,
159            value
160                .iter()
161                .flat_map(|x| {
162                    u32::try_from(*x.denom())
163                        .unwrap()
164                        .to_le_bytes()
165                        .into_iter()
166                        .chain(u32::try_from(*x.numer()).unwrap().to_le_bytes())
167                        .collect::<Vec<_>>()
168                })
169                .collect(),
170            5,
171        )
172    }
173
174    pub fn sbyte(code: u16, value: &[i8]) -> Self {
175        Tag::new(
176            code,
177            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
178            6,
179        )
180    }
181
182    pub fn sshort(code: u16, value: &[i16]) -> Self {
183        Tag::new(
184            code,
185            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
186            8,
187        )
188    }
189
190    pub fn slong(code: u16, value: &[i32]) -> Self {
191        Tag::new(
192            code,
193            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
194            9,
195        )
196    }
197
198    pub fn srational(code: u16, value: &[Rational32]) -> Self {
199        Tag::new(
200            code,
201            value
202                .iter()
203                .flat_map(|x| {
204                    x.denom()
205                        .to_le_bytes()
206                        .into_iter()
207                        .chain(x.numer().to_le_bytes())
208                        .collect::<Vec<_>>()
209                })
210                .collect(),
211            10,
212        )
213    }
214
215    pub fn float(code: u16, value: &[f32]) -> Self {
216        Tag::new(
217            code,
218            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
219            11,
220        )
221    }
222
223    pub fn double(code: u16, value: &[f64]) -> Self {
224        Tag::new(
225            code,
226            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
227            12,
228        )
229    }
230
231    pub fn ifd(code: u16, value: &[u32]) -> Self {
232        Tag::new(
233            code,
234            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
235            13,
236        )
237    }
238
239    pub fn unicode(code: u16, value: &str) -> Self {
240        let mut bytes: Vec<u8> = value.encode_utf16().flat_map(|x| x.to_le_bytes()).collect();
241        bytes.push(0);
242        Tag::new(code, bytes, 14)
243    }
244
245    pub fn complex(code: u16, value: &[Complex<f32>]) -> Self {
246        Tag::new(
247            code,
248            value
249                .iter()
250                .flat_map(|x| {
251                    x.re.to_le_bytes()
252                        .into_iter()
253                        .chain(x.im.to_le_bytes())
254                        .collect::<Vec<_>>()
255                })
256                .collect(),
257            15,
258        )
259    }
260
261    pub fn long8(code: u16, value: &[u64]) -> Self {
262        Tag::new(
263            code,
264            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
265            16,
266        )
267    }
268
269    pub fn slong8(code: u16, value: &[i64]) -> Self {
270        Tag::new(
271            code,
272            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
273            17,
274        )
275    }
276
277    pub fn ifd8(code: u16, value: &[u64]) -> Self {
278        Tag::new(
279            code,
280            value.iter().flat_map(|x| x.to_le_bytes()).collect(),
281            18,
282        )
283    }
284
285    pub fn short_long_or_long8(code: u16, value: &[u64]) -> Self {
286        let m = *value.iter().max().unwrap();
287        if m < 65536 {
288            Tag::short(code, &value.iter().map(|x| *x as u16).collect::<Vec<_>>())
289        } else if m < 4294967296 {
290            Tag::long(code, &value.iter().map(|x| *x as u32).collect::<Vec<_>>())
291        } else {
292            Tag::long8(code, value)
293        }
294    }
295
296    /// get the number of values in the tag
297    pub fn count(&self) -> u64 {
298        let c = match self.ttype {
299            1 => self.bytes.len(),      // BYTE
300            2 => self.bytes.len(),      // ASCII
301            3 => self.bytes.len() / 2,  // SHORT
302            4 => self.bytes.len() / 4,  // LONG
303            5 => self.bytes.len() / 8,  // RATIONAL
304            6 => self.bytes.len(),      // SBYTE
305            7 => self.bytes.len(),      // UNDEFINED
306            8 => self.bytes.len() / 2,  // SSHORT
307            9 => self.bytes.len() / 4,  // SLONG
308            10 => self.bytes.len() / 8, // SRATIONAL
309            11 => self.bytes.len() / 4, // FLOAT
310            12 => self.bytes.len() / 8, // DOUBLE
311            13 => self.bytes.len() / 4, // IFD
312            14 => self.bytes.len() / 2, // UNICODE
313            15 => self.bytes.len() / 8, // COMPLEX
314            16 => self.bytes.len() / 8, // LONG8
315            17 => self.bytes.len() / 8, // SLONG8
316            18 => self.bytes.len() / 8, // IFD8
317            _ => self.bytes.len(),
318        };
319        c as u64
320    }
321
322    fn write_tag(&mut self, ijtifffile: &mut IJTiffFile) -> Result<()> {
323        self.offset = ijtifffile.file.stream_position()?;
324        ijtifffile.file.write_all(&self.code.to_le_bytes())?;
325        ijtifffile.file.write_all(&self.ttype.to_le_bytes())?;
326        ijtifffile.file.write_all(&self.count().to_le_bytes())?;
327        if self.bytes.len() <= OFFSET_SIZE {
328            ijtifffile.file.write_all(&self.bytes)?;
329            ijtifffile
330                .file
331                .write_all(&vec![0; OFFSET_SIZE - self.bytes.len()])?;
332        } else {
333            ijtifffile.file.write_all(&[0; OFFSET_SIZE])?;
334        }
335        Ok(())
336    }
337
338    fn write_data(&self, ijtifffile: &mut IJTiffFile) -> Result<()> {
339        if self.bytes.len() > OFFSET_SIZE {
340            ijtifffile.file.seek(SeekFrom::End(0))?;
341            let offset = ijtifffile.write(&self.bytes)?;
342            ijtifffile.file.seek(SeekFrom::Start(
343                self.offset + (TAG_SIZE - OFFSET_SIZE) as u64,
344            ))?;
345            ijtifffile.file.write_all(&offset.to_le_bytes())?;
346            if ijtifffile.file.stream_position()? % 2 == 1 {
347                ijtifffile.file.write_all(&[0])?;
348            }
349        }
350        Ok(())
351    }
352}
353
354#[derive(Debug)]
355struct CompressedFrame {
356    bytes: Vec<Vec<u8>>,
357    image_width: u32,
358    image_length: u32,
359    tile_width: usize,
360    tile_length: usize,
361    bits_per_sample: u16,
362    sample_format: u16,
363}
364
365impl CompressedFrame {
366    fn new<T>(frame: ArcArray2<T>, compression: Compression) -> CompressedFrame
367    where
368        T: Bytes + Send + Sync,
369    {
370        let shape = frame.shape();
371        let tile_size = 2usize
372            .pow(((shape[0] as f64 * shape[1] as f64 / 2f64).log2() / 2f64).round() as u32)
373            .clamp(16, 1024);
374
375        let tile_width = tile_size;
376        let tile_length = tile_size;
377        let n = shape[0] / tile_width;
378        let m = shape[1] / tile_length;
379        let mut slices = Vec::new();
380        for i in 0..n {
381            for j in 0..m {
382                slices.push((
383                    i * tile_width,
384                    (i + 1) * tile_width,
385                    j * tile_length,
386                    (j + 1) * tile_length,
387                ));
388            }
389            if shape[1] % tile_length != 0 {
390                slices.push((
391                    i * tile_width,
392                    (i + 1) * tile_width,
393                    m * tile_length,
394                    shape[1],
395                ));
396            }
397        }
398        if shape[0] % tile_width != 0 {
399            for j in 0..m {
400                slices.push((
401                    n * tile_width,
402                    shape[0],
403                    j * tile_length,
404                    (j + 1) * tile_length,
405                ));
406            }
407            if shape[1] % tile_length != 0 {
408                slices.push((n * tile_width, shape[0], m * tile_length, shape[1]));
409            }
410        }
411
412        let bytes: Vec<_> = match compression {
413            Compression::Deflate => {
414                if slices.len() > 4 {
415                    slices
416                        .into_par_iter()
417                        .map(|slice| {
418                            CompressedFrame::compress_tile_deflate(
419                                frame.clone(),
420                                slice,
421                                tile_size,
422                                tile_size,
423                            )
424                            .unwrap()
425                        })
426                        .collect()
427                } else {
428                    slices
429                        .into_iter()
430                        .map(|slice| {
431                            CompressedFrame::compress_tile_deflate(
432                                frame.clone(),
433                                slice,
434                                tile_size,
435                                tile_size,
436                            )
437                            .unwrap()
438                        })
439                        .collect()
440                }
441            }
442
443            Compression::Zstd(level) => {
444                if slices.len() > 4 {
445                    slices
446                        .into_par_iter()
447                        .map(|slice| {
448                            CompressedFrame::compress_tile_zstd(
449                                frame.clone(),
450                                slice,
451                                tile_size,
452                                tile_size,
453                                level,
454                            )
455                            .unwrap()
456                        })
457                        .collect()
458                } else {
459                    slices
460                        .into_iter()
461                        .map(|slice| {
462                            CompressedFrame::compress_tile_zstd(
463                                frame.clone(),
464                                slice,
465                                tile_size,
466                                tile_size,
467                                level,
468                            )
469                            .unwrap()
470                        })
471                        .collect()
472                }
473            }
474        };
475
476        CompressedFrame {
477            bytes,
478            image_width: shape[1] as u32,
479            image_length: shape[0] as u32,
480            tile_width,
481            tile_length,
482            bits_per_sample: T::BITS_PER_SAMPLE,
483            sample_format: T::SAMPLE_FORMAT,
484        }
485    }
486
487    fn encode<W, T>(
488        mut encoder: W,
489        frame: ArcArray2<T>,
490        slice: (usize, usize, usize, usize),
491        tile_width: usize,
492        tile_length: usize,
493    ) -> Result<W>
494    where
495        W: Write,
496        T: Bytes,
497    {
498        let bytes_per_sample = (T::BITS_PER_SAMPLE / 8) as usize;
499        let shape = (slice.1 - slice.0, slice.3 - slice.2);
500        for i in 0..shape.0 {
501            encoder.write_all(
502                &frame
503                    .slice(s![slice.0..slice.1, slice.2..slice.3])
504                    .slice(s![i, ..])
505                    .map(|x| x.bytes())
506                    .into_iter()
507                    .flatten()
508                    .collect::<Vec<_>>(),
509            )?;
510            encoder.write_all(&vec![0; bytes_per_sample * (tile_width - shape.1)])?;
511        }
512        encoder.write_all(&vec![
513            0;
514            bytes_per_sample * tile_width * (tile_length - shape.0)
515        ])?;
516        Ok(encoder)
517    }
518
519    fn compress_tile_deflate<T>(
520        frame: ArcArray2<T>,
521        slice: (usize, usize, usize, usize),
522        tile_width: usize,
523        tile_length: usize,
524    ) -> Result<Vec<u8>>
525    where
526        T: Bytes,
527    {
528        let mut encoder = ZlibEncoder::new(Vec::new(), flate2::Compression::default());
529        encoder = CompressedFrame::encode(encoder, frame, slice, tile_width, tile_length)?;
530        Ok(encoder.finish()?)
531    }
532
533    fn compress_tile_zstd<T>(
534        frame: ArcArray2<T>,
535        slice: (usize, usize, usize, usize),
536        tile_width: usize,
537        tile_length: usize,
538        compression_level: i32,
539    ) -> Result<Vec<u8>>
540    where
541        T: Bytes,
542    {
543        let mut dest = Vec::new();
544        let mut encoder = Encoder::new(&mut dest, compression_level)?;
545        let bytes_per_sample = (T::BITS_PER_SAMPLE / 8) as usize;
546        encoder.include_contentsize(true)?;
547        encoder.set_pledged_src_size(Some((bytes_per_sample * tile_width * tile_length) as u64))?;
548        encoder.include_checksum(true)?;
549        encoder = CompressedFrame::encode(encoder, frame, slice, tile_width, tile_length)?;
550        encoder.finish()?;
551        Ok(dest)
552    }
553}
554
555#[derive(Clone, Debug)]
556struct Frame {
557    offsets: Vec<u64>,
558    bytecounts: Vec<u64>,
559    image_width: u32,
560    image_length: u32,
561    bits_per_sample: u16,
562    sample_format: u16,
563    tile_width: u16,
564    tile_length: u16,
565}
566
567impl Frame {
568    #[allow(clippy::too_many_arguments)]
569    fn new(
570        offsets: Vec<u64>,
571        bytecounts: Vec<u64>,
572        image_width: u32,
573        image_length: u32,
574        bits_per_sample: u16,
575        sample_format: u16,
576        tile_width: u16,
577        tile_length: u16,
578    ) -> Self {
579        Frame {
580            offsets,
581            bytecounts,
582            image_width,
583            image_length,
584            bits_per_sample,
585            sample_format,
586            tile_width,
587            tile_length,
588        }
589    }
590}
591
592/// trait to convert numbers to bytes
593pub trait Bytes {
594    const BITS_PER_SAMPLE: u16;
595    /// 1: unsigned int, 2: signed int, 3: float
596    const SAMPLE_FORMAT: u16;
597
598    fn bytes(&self) -> Vec<u8>;
599}
600
601macro_rules! bytes_impl {
602    ($T:ty, $bits_per_sample:expr, $sample_format:expr) => {
603        impl Bytes for $T {
604            const BITS_PER_SAMPLE: u16 = $bits_per_sample;
605            const SAMPLE_FORMAT: u16 = $sample_format;
606
607            #[inline]
608            fn bytes(&self) -> Vec<u8> {
609                self.to_le_bytes().to_vec()
610            }
611        }
612    };
613}
614
615bytes_impl!(u8, 8, 1);
616bytes_impl!(u16, 16, 1);
617bytes_impl!(u32, 32, 1);
618bytes_impl!(u64, 64, 1);
619bytes_impl!(u128, 128, 1);
620#[cfg(target_pointer_width = "64")]
621bytes_impl!(usize, 64, 1);
622#[cfg(target_pointer_width = "32")]
623bytes_impl!(usize, 32, 1);
624bytes_impl!(i8, 8, 2);
625bytes_impl!(i16, 16, 2);
626bytes_impl!(i32, 32, 2);
627bytes_impl!(i64, 64, 2);
628bytes_impl!(i128, 128, 2);
629#[cfg(target_pointer_width = "64")]
630bytes_impl!(isize, 64, 2);
631#[cfg(target_pointer_width = "32")]
632bytes_impl!(isize, 32, 2);
633bytes_impl!(f32, 32, 3);
634bytes_impl!(f64, 64, 3);
635
636/// what colormap to save in the tiff;
637#[derive(Clone, Debug)]
638pub enum Colors {
639    None,
640    /// gradient from black to rgb color, 1 vec per channel
641    Colors(Vec<Vec<u8>>),
642    /// vec of rgb colors
643    Colormap(Vec<Vec<u8>>),
644}
645
646/// save 2d arrays in a tif file compatible with Fiji/ImageJ
647#[derive(Debug)]
648pub struct IJTiffFile {
649    file: File,
650    frames: HashMap<(usize, usize, usize), Frame>,
651    hashes: HashMap<u64, u64>,
652    threads: HashMap<(usize, usize, usize), JoinHandle<CompressedFrame>>,
653    /// zstd: -7 ..= 22
654    pub compression: Compression,
655    pub colors: Colors,
656    pub comment: Option<String>,
657    /// um per pixel
658    pub px_size: Option<f64>,
659    /// um per slice
660    pub delta_z: Option<f64>,
661    /// s per frame
662    pub time_interval: Option<f64>,
663    /// extra tags; per frame: key = Some((c, z, t)), global: key = None
664    pub extra_tags: HashMap<Option<(usize, usize, usize)>, Vec<Tag>>,
665}
666
667impl Drop for IJTiffFile {
668    fn drop(&mut self) {
669        if let Err(e) = self.close() {
670            println!("Error closing IJTiffFile: {:?}", e);
671        }
672    }
673}
674
675impl IJTiffFile {
676    /// create new tifffile from path, use it's save() method to save frames
677    /// the file is finalized when it goes out of scope
678    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
679        let mut file = OpenOptions::new()
680            .create(true)
681            .truncate(true)
682            .write(true)
683            .read(true)
684            .open(path)?;
685        file.write_all(b"II")?;
686        file.write_all(&43u16.to_le_bytes())?;
687        file.write_all(&8u16.to_le_bytes())?;
688        file.write_all(&0u16.to_le_bytes())?;
689        file.write_all(&OFFSET.to_le_bytes())?;
690        Ok(IJTiffFile {
691            file,
692            frames: HashMap::new(),
693            hashes: HashMap::new(),
694            threads: HashMap::new(),
695            compression: Compression::Zstd(DEFAULT_COMPRESSION_LEVEL),
696            colors: Colors::None,
697            comment: None,
698            px_size: None,
699            delta_z: None,
700            time_interval: None,
701            extra_tags: HashMap::new(),
702        })
703    }
704
705    /// set compression: zstd(level) or deflate
706    pub fn set_compression(&mut self, compression: Compression) {
707        self.compression = compression;
708    }
709
710    /// to be saved in description tag (270)
711    pub fn description(&self, c_size: usize, z_size: usize, t_size: usize) -> String {
712        let mut desc: String = String::from("ImageJ=1.11a");
713        if let Colors::None = self.colors {
714            desc += &format!("\nimages={}", c_size);
715            desc += &format!("\nslices={}", z_size);
716            desc += &format!("\nframes={}", t_size);
717        } else {
718            desc += &format!("\nimages={}", c_size * z_size * t_size);
719            desc += &format!("\nchannels={}", c_size);
720            desc += &format!("\nslices={}", z_size);
721            desc += &format!("\nframes={}", t_size);
722        };
723        if c_size == 1 {
724            desc += "\nmode=grayscale";
725        } else {
726            desc += "\nmode=composite";
727        }
728        desc += "\nhyperstack=true\nloop=false\nunit=micron";
729        if let Some(delta_z) = self.delta_z {
730            desc += &format!("\nspacing={}", delta_z);
731        }
732        if let Some(timeinterval) = self.time_interval {
733            desc += &format!("\ninterval={}", timeinterval);
734        }
735        if let Some(comment) = &self.comment {
736            desc += &format!("\ncomment={}", comment);
737        }
738        desc
739    }
740
741    fn get_czt(
742        &self,
743        frame_number: usize,
744        channel: u8,
745        c_size: usize,
746        z_size: usize,
747    ) -> (usize, usize, usize) {
748        if let Colors::None = self.colors {
749            (
750                channel as usize,
751                frame_number % z_size,
752                frame_number / z_size,
753            )
754        } else {
755            (
756                frame_number % c_size,
757                frame_number / c_size % z_size,
758                frame_number / c_size / z_size,
759            )
760        }
761    }
762
763    fn spp_and_n_frames(&self, c_size: usize, z_size: usize, t_size: usize) -> (u8, usize) {
764        if let Colors::None = &self.colors {
765            (c_size as u8, z_size * t_size)
766        } else {
767            (1, c_size * z_size * t_size)
768        }
769    }
770
771    fn hash<T: Hash>(value: &T) -> u64 {
772        let mut hasher = DefaultHasher::new();
773        value.hash(&mut hasher);
774        hasher.finish()
775    }
776
777    fn hash_check(&mut self, bytes: &Vec<u8>, offset: u64) -> Result<bool> {
778        let current_offset = self.file.stream_position()?;
779        self.file.seek(SeekFrom::Start(offset))?;
780        let mut buffer = vec![0; bytes.len()];
781        self.file.read_exact(&mut buffer)?;
782        let same = bytes == &buffer;
783        self.file.seek(SeekFrom::Start(current_offset))?;
784        Ok(same)
785    }
786
787    fn write(&mut self, bytes: &Vec<u8>) -> Result<u64> {
788        let hash = IJTiffFile::hash(&bytes);
789        if self.hashes.contains_key(&hash)
790            && self.hash_check(bytes, *self.hashes.get(&hash).unwrap())?
791        {
792            Ok(*self.hashes.get(&hash).unwrap())
793        } else {
794            if self.file.stream_position()? % 2 == 1 {
795                self.file.write_all(&[0])?;
796            }
797            let offset = self.file.stream_position()?;
798            self.hashes.insert(hash, offset);
799            self.file.write_all(bytes)?;
800            Ok(offset)
801        }
802    }
803
804    /// save a 2d array to the tiff file at channel c, slice z, and time t
805    pub fn save<'a, A, T>(&mut self, frame: A, c: usize, z: usize, t: usize) -> Result<()>
806    where
807        A: AsArray<'a, T, Ix2>,
808        T: Bytes + Clone + Send + Sync + 'static,
809    {
810        let n_threads = usize::from(available_parallelism()?);
811        loop {
812            self.collect_threads(false)?;
813            if self.threads.len() < n_threads {
814                break;
815            }
816            sleep(Duration::from_millis(100));
817        }
818        let compression = self.compression.clone();
819        let frame = frame.into().to_shared();
820        self.threads.insert(
821            (c, z, t),
822            thread::spawn(move || CompressedFrame::new(frame, compression)),
823        );
824        Ok(())
825    }
826
827    fn collect_threads(&mut self, block: bool) -> Result<()> {
828        for (c, z, t) in self.threads.keys().cloned().collect::<Vec<_>>() {
829            if block || self.threads[&(c, z, t)].is_finished() {
830                if let Some(thread) = self.threads.remove(&(c, z, t)) {
831                    self.write_frame(thread.join().unwrap(), c, z, t)?;
832                }
833            }
834        }
835        Ok(())
836    }
837
838    fn write_frame(&mut self, frame: CompressedFrame, c: usize, z: usize, t: usize) -> Result<()> {
839        let mut offsets = Vec::new();
840        let mut bytecounts = Vec::new();
841        for tile in frame.bytes {
842            bytecounts.push(tile.len() as u64);
843            offsets.push(self.write(&tile)?);
844        }
845        let frame = Frame::new(
846            offsets,
847            bytecounts,
848            frame.image_width,
849            frame.image_length,
850            frame.bits_per_sample,
851            frame.sample_format,
852            frame.tile_width as u16,
853            frame.tile_length as u16,
854        );
855        self.frames.insert((c, z, t), frame);
856        Ok(())
857    }
858
859    fn get_colormap(&self, colormap: &Vec<Vec<u8>>, bits_per_sample: u16) -> Vec<u16> {
860        let mut r = Vec::new();
861        let mut g = Vec::new();
862        let mut b = Vec::new();
863        let n = 2usize.pow(bits_per_sample as u32 - 8);
864        for color in colormap {
865            r.extend(vec![(color[0] as u16) * 257; n]);
866            g.extend(vec![(color[1] as u16) * 257; n]);
867            b.extend(vec![(color[2] as u16) * 257; n]);
868        }
869        r.extend(g);
870        r.extend(b);
871        r
872    }
873
874    fn get_color(&self, colors: &Vec<u8>, bits_per_sample: u16) -> Vec<u16> {
875        let mut c = Vec::new();
876        let n = 2usize.pow(bits_per_sample as u32 - 8);
877        for color in colors {
878            for i in 0..256 {
879                c.extend(vec![i * (*color as u16) / 255 * 257; n])
880            }
881        }
882        c
883    }
884
885    fn close(&mut self) -> Result<()> {
886        self.collect_threads(true)?;
887        let mut c_size = 1;
888        let mut z_size = 1;
889        let mut t_size = 1;
890        for (c, z, t) in self.frames.keys() {
891            c_size = c_size.max(c + 1);
892            z_size = z_size.max(z + 1);
893            t_size = t_size.max(t + 1);
894        }
895
896        let mut where_to_write_next_ifd_offset = OFFSET - OFFSET_SIZE as u64;
897        let mut warn = Vec::new();
898        let (samples_per_pixel, n_frames) = self.spp_and_n_frames(c_size, t_size, z_size);
899        for frame_number in 0..n_frames {
900            if let Some(frame) = self
901                .frames
902                .get(&self.get_czt(frame_number, 0, c_size, z_size))
903            {
904                let mut offsets = Vec::new();
905                let mut bytecounts = Vec::new();
906                let mut frame_count = 0;
907                for channel in 0..samples_per_pixel {
908                    if let Some(frame_n) =
909                        self.frames
910                            .get(&self.get_czt(frame_number, channel, c_size, z_size))
911                    {
912                        offsets.extend(frame_n.offsets.iter());
913                        bytecounts.extend(frame_n.bytecounts.iter());
914                        frame_count += 1;
915                    } else {
916                        warn.push((frame_number, channel));
917                    }
918                }
919                let mut ifd = IFD::new();
920                ifd.tags.insert(Tag::long(256, &[frame.image_width]));
921                ifd.tags.insert(Tag::long(257, &[frame.image_length]));
922                ifd.tags
923                    .insert(Tag::short(258, &vec![frame.bits_per_sample; frame_count]));
924                ifd.tags
925                    .insert(Tag::short(259, &[self.compression.index()]));
926                ifd.tags
927                    .insert(Tag::ascii(270, &self.description(c_size, z_size, t_size)));
928                ifd.tags.insert(Tag::short(277, &[frame_count as u16]));
929                ifd.tags.insert(Tag::ascii(305, "tiffwrite_rs"));
930                ifd.tags.insert(Tag::short(322, &[frame.tile_width]));
931                ifd.tags.insert(Tag::short(323, &[frame.tile_length]));
932                ifd.tags.insert(Tag::short_long_or_long8(324, &offsets));
933                ifd.tags.insert(Tag::short_long_or_long8(325, &bytecounts));
934                if frame.sample_format > 1 {
935                    ifd.tags.insert(Tag::short(339, &[frame.sample_format]));
936                }
937                if let Some(px_size) = self.px_size {
938                    let r = [Rational32::from_f64(px_size).unwrap()];
939                    ifd.tags.insert(Tag::rational(282, &r));
940                    ifd.tags.insert(Tag::rational(283, &r));
941                    ifd.tags.insert(Tag::short(296, &[1]));
942                }
943                if let Colors::Colormap(_) = &self.colors {
944                    ifd.tags.insert(Tag::short(262, &[3]));
945                } else if let Colors::None = self.colors {
946                    ifd.tags.insert(Tag::short(262, &[1]));
947                }
948                if frame_number == 0 {
949                    if let Colors::Colormap(colormap) = &self.colors {
950                        ifd.tags.insert(Tag::short(
951                            320,
952                            &self.get_colormap(colormap, frame.bits_per_sample),
953                        ));
954                    }
955                }
956                if frame_number < c_size {
957                    if let Colors::Colors(colors) = &self.colors {
958                        ifd.tags.insert(Tag::short(
959                            320,
960                            &self.get_color(&colors[frame_number], frame.bits_per_sample),
961                        ));
962                        ifd.tags.insert(Tag::short(262, &[3]));
963                    }
964                }
965                if let Colors::None = &self.colors {
966                    if c_size > 1 {
967                        ifd.tags.insert(Tag::short(284, &[2]));
968                    }
969                }
970                for channel in 0..samples_per_pixel {
971                    let czt = self.get_czt(frame_number, channel, c_size, z_size);
972                    if let Some(extra_tags) = self.extra_tags.get(&Some(czt)) {
973                        for tag in extra_tags {
974                            ifd.tags.insert(tag.to_owned());
975                        }
976                    }
977                }
978                if let Some(extra_tags) = self.extra_tags.get(&None) {
979                    for tag in extra_tags {
980                        ifd.tags.insert(tag.to_owned());
981                    }
982                }
983                if frame_number == 0 {
984                    ifd.tags.insert(Tag::ascii(
985                        306,
986                        &format!("{}", Utc::now().format("%Y:%m:%d %H:%M:%S")),
987                    ));
988                }
989                where_to_write_next_ifd_offset = ifd.write(self, where_to_write_next_ifd_offset)?;
990            } else {
991                warn.push((frame_number, 0));
992            }
993            if !warn.is_empty() {
994                println!("The following frames were not added to the tif file:");
995                for (frame_number, channel) in &warn {
996                    let (c, z, t) = self.get_czt(*frame_number, *channel, c_size, z_size);
997                    println!("c: {c}, z: {z}, t: {t}")
998                }
999                println!(
1000                    "Either you forgot them, \
1001                         or an error occurred and the tif file was closed prematurely."
1002                )
1003            }
1004        }
1005        self.file
1006            .seek(SeekFrom::Start(where_to_write_next_ifd_offset))?;
1007        self.file.write_all(&0u64.to_le_bytes())?;
1008        Ok(())
1009    }
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014    use super::*;
1015    use ndarray::Array2;
1016
1017    #[test]
1018    /// An example of generating julia fractals.
1019    fn julia_test() -> Result<()> {
1020        let imgx = 800;
1021        let imgy = 600;
1022
1023        let scalex = 3.0 / imgx as f32;
1024        let scaley = 3.0 / imgy as f32;
1025
1026        let mut im_r = Array2::<u8>::zeros((imgy, imgx));
1027        let mut im_g = Array2::<u8>::zeros((imgy, imgx));
1028        let mut im_b = Array2::<u8>::zeros((imgy, imgx));
1029        for x in 0..imgx {
1030            for y in 0..imgy {
1031                im_r[[y, x]] = (0.3 * x as f32) as u8;
1032                im_b[[y, x]] = (0.3 * y as f32) as u8;
1033
1034                let cx = y as f32 * scalex - 1.5;
1035                let cy = x as f32 * scaley - 1.5;
1036
1037                let c = Complex::new(-0.4, 0.6);
1038                let mut z = Complex::new(cx, cy);
1039
1040                let mut i = 0;
1041                while i < 255 && z.norm() <= 2.0 {
1042                    z = z * z + c;
1043                    i += 1;
1044                }
1045
1046                im_g[[y, x]] = i as u8;
1047            }
1048        }
1049
1050        let mut f = IJTiffFile::new("julia.tif")?;
1051        f.save(&im_r, 0, 0, 0)?;
1052        f.save(&im_g, 1, 0, 0)?;
1053        f.save(&im_b, 2, 0, 0)?;
1054
1055        Ok(())
1056    }
1057}