summaryrefslogtreecommitdiff
path: root/vendor/image/src/codecs/hdr/encoder.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/image/src/codecs/hdr/encoder.rs
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz
fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/image/src/codecs/hdr/encoder.rs')
-rw-r--r--vendor/image/src/codecs/hdr/encoder.rs433
1 files changed, 433 insertions, 0 deletions
diff --git a/vendor/image/src/codecs/hdr/encoder.rs b/vendor/image/src/codecs/hdr/encoder.rs
new file mode 100644
index 0000000..c3a176d
--- /dev/null
+++ b/vendor/image/src/codecs/hdr/encoder.rs
@@ -0,0 +1,433 @@
+use crate::codecs::hdr::{rgbe8, Rgbe8Pixel, SIGNATURE};
+use crate::color::Rgb;
+use crate::error::ImageResult;
+use std::cmp::Ordering;
+use std::io::{Result, Write};
+
+/// Radiance HDR encoder
+pub struct HdrEncoder<W: Write> {
+ w: W,
+}
+
+impl<W: Write> HdrEncoder<W> {
+ /// Creates encoder
+ pub fn new(w: W) -> HdrEncoder<W> {
+ HdrEncoder { w }
+ }
+
+ /// Encodes the image ```data```
+ /// that has dimensions ```width``` and ```height```
+ pub fn encode(mut self, data: &[Rgb<f32>], width: usize, height: usize) -> ImageResult<()> {
+ assert!(data.len() >= width * height);
+ let w = &mut self.w;
+ w.write_all(SIGNATURE)?;
+ w.write_all(b"\n")?;
+ w.write_all(b"# Rust HDR encoder\n")?;
+ w.write_all(b"FORMAT=32-bit_rle_rgbe\n\n")?;
+ w.write_all(format!("-Y {} +X {}\n", height, width).as_bytes())?;
+
+ if !(8..=32_768).contains(&width) {
+ for &pix in data {
+ write_rgbe8(w, to_rgbe8(pix))?;
+ }
+ } else {
+ // new RLE marker contains scanline width
+ let marker = rgbe8(2, 2, (width / 256) as u8, (width % 256) as u8);
+ // buffers for encoded pixels
+ let mut bufr = vec![0; width];
+ let mut bufg = vec![0; width];
+ let mut bufb = vec![0; width];
+ let mut bufe = vec![0; width];
+ let mut rle_buf = vec![0; width];
+ for scanline in data.chunks(width) {
+ for ((((r, g), b), e), &pix) in bufr
+ .iter_mut()
+ .zip(bufg.iter_mut())
+ .zip(bufb.iter_mut())
+ .zip(bufe.iter_mut())
+ .zip(scanline.iter())
+ {
+ let cp = to_rgbe8(pix);
+ *r = cp.c[0];
+ *g = cp.c[1];
+ *b = cp.c[2];
+ *e = cp.e;
+ }
+ write_rgbe8(w, marker)?; // New RLE encoding marker
+ rle_buf.clear();
+ rle_compress(&bufr[..], &mut rle_buf);
+ w.write_all(&rle_buf[..])?;
+ rle_buf.clear();
+ rle_compress(&bufg[..], &mut rle_buf);
+ w.write_all(&rle_buf[..])?;
+ rle_buf.clear();
+ rle_compress(&bufb[..], &mut rle_buf);
+ w.write_all(&rle_buf[..])?;
+ rle_buf.clear();
+ rle_compress(&bufe[..], &mut rle_buf);
+ w.write_all(&rle_buf[..])?;
+ }
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+enum RunOrNot {
+ Run(u8, usize),
+ Norun(usize, usize),
+}
+use self::RunOrNot::{Norun, Run};
+
+const RUN_MAX_LEN: usize = 127;
+const NORUN_MAX_LEN: usize = 128;
+
+struct RunIterator<'a> {
+ data: &'a [u8],
+ curidx: usize,
+}
+
+impl<'a> RunIterator<'a> {
+ fn new(data: &'a [u8]) -> RunIterator<'a> {
+ RunIterator { data, curidx: 0 }
+ }
+}
+
+impl<'a> Iterator for RunIterator<'a> {
+ type Item = RunOrNot;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.curidx == self.data.len() {
+ None
+ } else {
+ let cv = self.data[self.curidx];
+ let crun = self.data[self.curidx..]
+ .iter()
+ .take_while(|&&v| v == cv)
+ .take(RUN_MAX_LEN)
+ .count();
+ let ret = if crun > 2 {
+ Run(cv, crun)
+ } else {
+ Norun(self.curidx, crun)
+ };
+ self.curidx += crun;
+ Some(ret)
+ }
+ }
+}
+
+struct NorunCombineIterator<'a> {
+ runiter: RunIterator<'a>,
+ prev: Option<RunOrNot>,
+}
+
+impl<'a> NorunCombineIterator<'a> {
+ fn new(data: &'a [u8]) -> NorunCombineIterator<'a> {
+ NorunCombineIterator {
+ runiter: RunIterator::new(data),
+ prev: None,
+ }
+ }
+}
+
+// Combines sequential noruns produced by RunIterator
+impl<'a> Iterator for NorunCombineIterator<'a> {
+ type Item = RunOrNot;
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ match self.prev.take() {
+ Some(Run(c, len)) => {
+ // Just return stored run
+ return Some(Run(c, len));
+ }
+ Some(Norun(idx, len)) => {
+ // Let's see if we need to continue norun
+ match self.runiter.next() {
+ Some(Norun(_, len1)) => {
+ // norun continues
+ let clen = len + len1; // combined length
+ match clen.cmp(&NORUN_MAX_LEN) {
+ Ordering::Equal => return Some(Norun(idx, clen)),
+ Ordering::Greater => {
+ // combined norun exceeds maximum length. store extra part of norun
+ self.prev =
+ Some(Norun(idx + NORUN_MAX_LEN, clen - NORUN_MAX_LEN));
+ // then return maximal norun
+ return Some(Norun(idx, NORUN_MAX_LEN));
+ }
+ Ordering::Less => {
+ // len + len1 < NORUN_MAX_LEN
+ self.prev = Some(Norun(idx, len + len1));
+ // combine and continue loop
+ }
+ }
+ }
+ Some(Run(c, len1)) => {
+ // Run encountered. Store it
+ self.prev = Some(Run(c, len1));
+ return Some(Norun(idx, len)); // and return combined norun
+ }
+ None => {
+ // End of sequence
+ return Some(Norun(idx, len)); // return combined norun
+ }
+ }
+ } // End match self.prev.take() == Some(NoRun())
+ None => {
+ // No norun to combine
+ match self.runiter.next() {
+ Some(Norun(idx, len)) => {
+ self.prev = Some(Norun(idx, len));
+ // store for combine and continue the loop
+ }
+ Some(Run(c, len)) => {
+ // Some run. Just return it
+ return Some(Run(c, len));
+ }
+ None => {
+ // That's all, folks
+ return None;
+ }
+ }
+ } // End match self.prev.take() == None
+ } // End match
+ } // End loop
+ }
+}
+
+// Appends RLE compressed ```data``` to ```rle```
+fn rle_compress(data: &[u8], rle: &mut Vec<u8>) {
+ rle.clear();
+ if data.is_empty() {
+ rle.push(0); // Technically correct. It means read next 0 bytes.
+ return;
+ }
+ // Task: split data into chunks of repeating (max 127) and non-repeating bytes (max 128)
+ // Prepend non-repeating chunk with its length
+ // Replace repeating byte with (run length + 128) and the byte
+ for rnr in NorunCombineIterator::new(data) {
+ match rnr {
+ Run(c, len) => {
+ assert!(len <= 127);
+ rle.push(128u8 + len as u8);
+ rle.push(c);
+ }
+ Norun(idx, len) => {
+ assert!(len <= 128);
+ rle.push(len as u8);
+ rle.extend_from_slice(&data[idx..idx + len]);
+ }
+ }
+ }
+}
+
+fn write_rgbe8<W: Write>(w: &mut W, v: Rgbe8Pixel) -> Result<()> {
+ w.write_all(&[v.c[0], v.c[1], v.c[2], v.e])
+}
+
+/// Converts ```Rgb<f32>``` into ```Rgbe8Pixel```
+pub fn to_rgbe8(pix: Rgb<f32>) -> Rgbe8Pixel {
+ let pix = pix.0;
+ let mx = f32::max(pix[0], f32::max(pix[1], pix[2]));
+ if mx <= 0.0 {
+ Rgbe8Pixel { c: [0, 0, 0], e: 0 }
+ } else {
+ // let (frac, exp) = mx.frexp(); // unstable yet
+ let exp = mx.log2().floor() as i32 + 1;
+ let mul = f32::powi(2.0, exp);
+ let mut conv = [0u8; 3];
+ for (cv, &sv) in conv.iter_mut().zip(pix.iter()) {
+ *cv = f32::trunc(sv / mul * 256.0) as u8;
+ }
+ Rgbe8Pixel {
+ c: conv,
+ e: (exp + 128) as u8,
+ }
+ }
+}
+
+#[test]
+fn to_rgbe8_test() {
+ use crate::codecs::hdr::rgbe8;
+ let test_cases = vec![rgbe8(0, 0, 0, 0), rgbe8(1, 1, 128, 128)];
+ for &pix in &test_cases {
+ assert_eq!(pix, to_rgbe8(pix.to_hdr()));
+ }
+ for mc in 128..255 {
+ // TODO: use inclusive range when stable
+ let pix = rgbe8(mc, mc, mc, 100);
+ assert_eq!(pix, to_rgbe8(pix.to_hdr()));
+ let pix = rgbe8(mc, 0, mc, 130);
+ assert_eq!(pix, to_rgbe8(pix.to_hdr()));
+ let pix = rgbe8(0, 0, mc, 140);
+ assert_eq!(pix, to_rgbe8(pix.to_hdr()));
+ let pix = rgbe8(1, 0, mc, 150);
+ assert_eq!(pix, to_rgbe8(pix.to_hdr()));
+ let pix = rgbe8(1, mc, 10, 128);
+ assert_eq!(pix, to_rgbe8(pix.to_hdr()));
+ for c in 0..255 {
+ // Radiance HDR seems to be pre IEEE 754.
+ // exponent can be -128 (represented as 0u8), so some colors cannot be represented in normalized f32
+ // Let's exclude exponent value of -128 (0u8) from testing
+ let pix = rgbe8(1, mc, c, if c == 0 { 1 } else { c });
+ assert_eq!(pix, to_rgbe8(pix.to_hdr()));
+ }
+ }
+ fn relative_dist(a: Rgb<f32>, b: Rgb<f32>) -> f32 {
+ // maximal difference divided by maximal value
+ let max_diff =
+ a.0.iter()
+ .zip(b.0.iter())
+ .fold(0.0, |diff, (&a, &b)| f32::max(diff, (a - b).abs()));
+ let max_val =
+ a.0.iter()
+ .chain(b.0.iter())
+ .fold(0.0, |maxv, &a| f32::max(maxv, a));
+ if max_val == 0.0 {
+ 0.0
+ } else {
+ max_diff / max_val
+ }
+ }
+ let test_values = vec![
+ 0.000_001, 0.000_02, 0.000_3, 0.004, 0.05, 0.6, 7.0, 80.0, 900.0, 1_000.0, 20_000.0,
+ 300_000.0,
+ ];
+ for &r in &test_values {
+ for &g in &test_values {
+ for &b in &test_values {
+ let c1 = Rgb([r, g, b]);
+ let c2 = to_rgbe8(c1).to_hdr();
+ let rel_dist = relative_dist(c1, c2);
+ // Maximal value is normalized to the range 128..256, thus we have 1/128 precision
+ assert!(
+ rel_dist <= 1.0 / 128.0,
+ "Relative distance ({}) exceeds 1/128 for {:?} and {:?}",
+ rel_dist,
+ c1,
+ c2
+ );
+ }
+ }
+ }
+}
+
+#[test]
+fn runiterator_test() {
+ let data = [];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), None);
+ let data = [5];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Norun(0, 1)));
+ assert_eq!(run_iter.next(), None);
+ let data = [1, 1];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Norun(0, 2)));
+ assert_eq!(run_iter.next(), None);
+ let data = [0, 0, 0];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Run(0u8, 3)));
+ assert_eq!(run_iter.next(), None);
+ let data = [0, 0, 1, 1];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Norun(0, 2)));
+ assert_eq!(run_iter.next(), Some(Norun(2, 2)));
+ assert_eq!(run_iter.next(), None);
+ let data = [0, 0, 0, 1, 1];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Run(0u8, 3)));
+ assert_eq!(run_iter.next(), Some(Norun(3, 2)));
+ assert_eq!(run_iter.next(), None);
+ let data = [1, 2, 2, 2];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Norun(0, 1)));
+ assert_eq!(run_iter.next(), Some(Run(2u8, 3)));
+ assert_eq!(run_iter.next(), None);
+ let data = [1, 1, 2, 2, 2];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Norun(0, 2)));
+ assert_eq!(run_iter.next(), Some(Run(2u8, 3)));
+ assert_eq!(run_iter.next(), None);
+ let data = [2; 128];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Run(2u8, 127)));
+ assert_eq!(run_iter.next(), Some(Norun(127, 1)));
+ assert_eq!(run_iter.next(), None);
+ let data = [2; 129];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Run(2u8, 127)));
+ assert_eq!(run_iter.next(), Some(Norun(127, 2)));
+ assert_eq!(run_iter.next(), None);
+ let data = [2; 130];
+ let mut run_iter = RunIterator::new(&data[..]);
+ assert_eq!(run_iter.next(), Some(Run(2u8, 127)));
+ assert_eq!(run_iter.next(), Some(Run(2u8, 3)));
+ assert_eq!(run_iter.next(), None);
+}
+
+#[test]
+fn noruncombine_test() {
+ fn a<T>(mut v: Vec<T>, mut other: Vec<T>) -> Vec<T> {
+ v.append(&mut other);
+ v
+ }
+
+ let v = vec![];
+ let mut rsi = NorunCombineIterator::new(&v[..]);
+ assert_eq!(rsi.next(), None);
+
+ let v = vec![1];
+ let mut rsi = NorunCombineIterator::new(&v[..]);
+ assert_eq!(rsi.next(), Some(Norun(0, 1)));
+ assert_eq!(rsi.next(), None);
+
+ let v = vec![2, 2];
+ let mut rsi = NorunCombineIterator::new(&v[..]);
+ assert_eq!(rsi.next(), Some(Norun(0, 2)));
+ assert_eq!(rsi.next(), None);
+
+ let v = vec![3, 3, 3];
+ let mut rsi = NorunCombineIterator::new(&v[..]);
+ assert_eq!(rsi.next(), Some(Run(3, 3)));
+ assert_eq!(rsi.next(), None);
+
+ let v = vec![4, 4, 3, 3, 3];
+ let mut rsi = NorunCombineIterator::new(&v[..]);
+ assert_eq!(rsi.next(), Some(Norun(0, 2)));
+ assert_eq!(rsi.next(), Some(Run(3, 3)));
+ assert_eq!(rsi.next(), None);
+
+ let v = vec![40; 400];
+ let mut rsi = NorunCombineIterator::new(&v[..]);
+ assert_eq!(rsi.next(), Some(Run(40, 127)));
+ assert_eq!(rsi.next(), Some(Run(40, 127)));
+ assert_eq!(rsi.next(), Some(Run(40, 127)));
+ assert_eq!(rsi.next(), Some(Run(40, 19)));
+ assert_eq!(rsi.next(), None);
+
+ let v = a(a(vec![5; 3], vec![6; 129]), vec![7, 3, 7, 10, 255]);
+ let mut rsi = NorunCombineIterator::new(&v[..]);
+ assert_eq!(rsi.next(), Some(Run(5, 3)));
+ assert_eq!(rsi.next(), Some(Run(6, 127)));
+ assert_eq!(rsi.next(), Some(Norun(130, 7)));
+ assert_eq!(rsi.next(), None);
+
+ let v = a(a(vec![5; 2], vec![6; 129]), vec![7, 3, 7, 7, 255]);
+ let mut rsi = NorunCombineIterator::new(&v[..]);
+ assert_eq!(rsi.next(), Some(Norun(0, 2)));
+ assert_eq!(rsi.next(), Some(Run(6, 127)));
+ assert_eq!(rsi.next(), Some(Norun(129, 7)));
+ assert_eq!(rsi.next(), None);
+
+ let v: Vec<_> = ::std::iter::repeat(())
+ .flat_map(|_| (0..2))
+ .take(257)
+ .collect();
+ let mut rsi = NorunCombineIterator::new(&v[..]);
+ assert_eq!(rsi.next(), Some(Norun(0, 128)));
+ assert_eq!(rsi.next(), Some(Norun(128, 128)));
+ assert_eq!(rsi.next(), Some(Norun(256, 1)));
+ assert_eq!(rsi.next(), None);
+}