tiffwrite/
lib.rs

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