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#[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#[allow(clippy::upper_case_acronyms)]
47#[derive(Clone, Debug)]
48struct IFD {
49 tags: HashSet<Tag>,
50}
51
52impl IFD {
53 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#[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 pub fn count(&self) -> u64 {
299 let c = match self.ttype {
300 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(),
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
593pub trait Bytes {
595 const BITS_PER_SAMPLE: u16;
596 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#[derive(Clone, Debug)]
639pub enum Colors {
640 None,
641 Colors(Vec<Vec<u8>>),
643 Colormap(Vec<Vec<u8>>),
645}
646
647#[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 pub compression: Compression,
656 pub colors: Colors,
657 pub comment: Option<String>,
658 pub px_size: Option<f64>,
660 pub delta_z: Option<f64>,
662 pub time_interval: Option<f64>,
664 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 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 pub fn set_compression(&mut self, compression: Compression) {
708 self.compression = compression;
709 }
710
711 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 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 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 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}