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#[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#[allow(clippy::upper_case_acronyms)]
46#[derive(Clone, Debug)]
47struct IFD {
48 tags: HashSet<Tag>,
49}
50
51impl IFD {
52 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#[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 pub fn count(&self) -> u64 {
298 let c = match self.ttype {
299 1 => self.bytes.len(), 2 => self.bytes.len(), 3 => self.bytes.len() / 2, 4 => self.bytes.len() / 4, 5 => self.bytes.len() / 8, 6 => self.bytes.len(), 7 => self.bytes.len(), 8 => self.bytes.len() / 2, 9 => self.bytes.len() / 4, 10 => self.bytes.len() / 8, 11 => self.bytes.len() / 4, 12 => self.bytes.len() / 8, 13 => self.bytes.len() / 4, 14 => self.bytes.len() / 2, 15 => self.bytes.len() / 8, 16 => self.bytes.len() / 8, 17 => self.bytes.len() / 8, 18 => self.bytes.len() / 8, _ => 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
592pub trait Bytes {
594 const BITS_PER_SAMPLE: u16;
595 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#[derive(Clone, Debug)]
638pub enum Colors {
639 None,
640 Colors(Vec<Vec<u8>>),
642 Colormap(Vec<Vec<u8>>),
644}
645
646#[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 pub compression: Compression,
655 pub colors: Colors,
656 pub comment: Option<String>,
657 pub px_size: Option<f64>,
659 pub delta_z: Option<f64>,
661 pub time_interval: Option<f64>,
663 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 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 pub fn set_compression(&mut self, compression: Compression) {
707 self.compression = compression;
708 }
709
710 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 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 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}