diff --git a/services/gam/src/bitmap.rs b/services/gam/src/bitmap.rs index 9dfb09642..c71b5104b 100644 --- a/services/gam/src/bitmap.rs +++ b/services/gam/src/bitmap.rs @@ -7,6 +7,8 @@ use std::ops::Deref; mod decode_png; pub use decode_png::*; +mod shrink; +pub use shrink::*; mod dither; pub use dither::*; @@ -536,135 +538,6 @@ pub trait GreyScaleIterator: Iterator + Sized { impl> GreyScaleIterator for I {} -// ********************************************************************** - -pub struct Shrink { - /// iterator over inbound pixels - iter: I, - /// width of the outbound image - out_width: usize, - /// the scale factor between inbound and outbound images (ie in_width/out_width) - scale: f32, - /// a pre-tabulated list of the trailing edge of each inbound strip of pixels - in_x_cap: Vec, - /// the current y coord of the inbound image - in_y: usize, - /// the current x coord of the outbound image - out_x: usize, - /// the current y coord of the outbound image - out_y: usize, - /// a buffer the width of the outbound image to stove horizontal averages - buf: Vec, - /// the width of the current stri in the inbound image - y_div: u16, - /// the x coord of the final pixel in the inbound image - out_x_last: usize, -} - -impl> Shrink { - fn new(iter: I, in_width: usize, out_width: usize) -> Shrink { - let scale = in_width as f32 / out_width as f32; - // set up a buffer to average the surrounding pixels - let buf: Vec = if scale <= 1.0 { - Vec::new() - } else { - vec![0u16; out_width] - }; - - // Pretabulate horizontal pixel positions - let mut in_x_cap: Vec = Vec::with_capacity(out_width); - let max_width: u16 = (in_width - 1).try_into().unwrap(); - for out_x in 1..=out_width { - let in_x: u16 = (scale * out_x as f32) as u16; - in_x_cap.push((in_x).min(max_width)); - } - let out_x_last = out_width; - Self { - iter, - out_width, - scale, - in_x_cap, - in_y: 0, - out_x: 0, - out_y: 1, - buf, - y_div: 0, - out_x_last, - } - } - - #[allow(dead_code)] - fn next_xy(&self) -> (usize, usize) { - (self.out_x, self.out_y) - } -} -/// Adaptor Iterator to shrink an image dimensions from in_width to out_width -impl> Iterator for Shrink { - type Item = u8; - - /// Shrinks an image from in_width to out_width - /// The algorithm divides the inbound image into vertical and horivontal strips, - /// correspoding to the columns and rows of the outbound image. Each outbound - /// pixel is the average of the pixels contained within each intersaction of - /// vertical and horizontal strips. For example, when in_width = 3 x out_width - /// each outbound pixel will be the average of 9 pixels in a 3x3 inbound block. - /// Note that with a non-integer scale the strips will be of variable width ±1. - fn next(&mut self) -> Option { - // if there is no reduction in image size then simple return image as-is - if self.scale <= 1.0 { - return match self.iter.next() { - Some(pixel) => Some(pixel), - None => None, - }; - } - // processed the last inbound pixel - if self.out_x > self.out_x_last { - return None; - } - // take the average of pixels in the horizontal, and then vertical. - let in_y_cap = (self.scale * self.out_y as f32) as usize; - while self.in_y <= in_y_cap { - let mut in_x = 0; - for (out_x, in_x_cap) in self.in_x_cap.iter().enumerate() { - let mut x_total: u16 = 0; - let mut x_div: u16 = 0; - while in_x <= *in_x_cap { - x_total += match self.iter.next() { - Some(pixel) => pixel as u16, - None => { - self.out_x_last = out_x - 1; - 0 - } - }; - in_x += 1; - x_div += 1; - } - self.buf[out_x] += x_total / x_div; - } - self.in_y += 1; - self.y_div += 1; - } - // calculate the average of the sum of pixels in the buffer, and reset buffer - let pixel: u8 = (self.buf[self.out_x] / self.y_div).try_into().unwrap(); - self.buf[self.out_x] = 0; - // prepare for the next pixel in the row or column - self.out_x += 1; - if self.out_x >= self.out_width { - self.out_x = 0; - self.out_y += 1; - self.y_div = 0; - } - Some(pixel) - } -} - -pub trait ShrinkIterator: Iterator + Sized { - fn shrink(self, in_width: usize, out_width: usize) -> Shrink { - Shrink::new(self, in_width, out_width) - } -} - -impl> ShrinkIterator for I {} // ********************************************************************** diff --git a/services/gam/src/bitmap/shrink.rs b/services/gam/src/bitmap/shrink.rs new file mode 100644 index 000000000..b9a95c4cd --- /dev/null +++ b/services/gam/src/bitmap/shrink.rs @@ -0,0 +1,135 @@ +/* + * Shrink is an adaptor Iterator to reduce the width & height of a raster image + * + * author: nworbnhoj + */ + +use std::convert::TryInto; + +pub struct Shrink { + /// iterator over inbound pixels + iter: I, + /// width of the outbound image + out_width: usize, + /// the scale factor between inbound and outbound images (ie in_width/out_width) + scale: f32, + /// a pre-tabulated list of the trailing edge of each inbound strip of pixels + in_x_cap: Vec, + /// the current y coord of the inbound image + in_y: usize, + /// the current x coord of the outbound image + out_x: usize, + /// the current y coord of the outbound image + out_y: usize, + /// a buffer the width of the outbound image to stove horizontal averages + buf: Vec, + /// the width of the current stri in the inbound image + y_div: u16, + /// the x coord of the final pixel in the inbound image + out_x_last: usize, +} + +impl> Shrink { + fn new(iter: I, in_width: usize, out_width: usize) -> Shrink { + let scale = in_width as f32 / out_width as f32; + // set up a buffer to average the surrounding pixels + let buf: Vec = if scale <= 1.0 { + Vec::new() + } else { + vec![0u16; out_width] + }; + + // Pretabulate horizontal pixel positions + let mut in_x_cap: Vec = Vec::with_capacity(out_width); + let max_width: u16 = (in_width - 1).try_into().unwrap(); + for out_x in 1..=out_width { + let in_x: u16 = (scale * out_x as f32) as u16; + in_x_cap.push((in_x).min(max_width)); + } + let out_x_last = out_width; + Self { + iter, + out_width, + scale, + in_x_cap, + in_y: 0, + out_x: 0, + out_y: 1, + buf, + y_div: 0, + out_x_last, + } + } + + #[allow(dead_code)] + fn next_xy(&self) -> (usize, usize) { + (self.out_x, self.out_y) + } +} +/// Adaptor Iterator to shrink an image dimensions from in_width to out_width +impl> Iterator for Shrink { + type Item = u8; + + /// Shrinks an image from in_width to out_width + /// The algorithm divides the inbound image into vertical and horivontal strips, + /// correspoding to the columns and rows of the outbound image. Each outbound + /// pixel is the average of the pixels contained within each intersaction of + /// vertical and horizontal strips. For example, when in_width = 3 x out_width + /// each outbound pixel will be the average of 9 pixels in a 3x3 inbound block. + /// Note that with a non-integer scale the strips will be of variable width ±1. + fn next(&mut self) -> Option { + // if there is no reduction in image size then simple return image as-is + if self.scale <= 1.0 { + return match self.iter.next() { + Some(pixel) => Some(pixel), + None => None, + }; + } + // processed the last inbound pixel + if self.out_x > self.out_x_last { + return None; + } + // take the average of pixels in the horizontal, and then vertical. + let in_y_cap = (self.scale * self.out_y as f32) as usize; + while self.in_y <= in_y_cap { + let mut in_x = 0; + for (out_x, in_x_cap) in self.in_x_cap.iter().enumerate() { + let mut x_total: u16 = 0; + let mut x_div: u16 = 0; + while in_x <= *in_x_cap { + x_total += match self.iter.next() { + Some(pixel) => pixel as u16, + None => { + self.out_x_last = out_x - 1; + 0 + } + }; + in_x += 1; + x_div += 1; + } + self.buf[out_x] += x_total / x_div; + } + self.in_y += 1; + self.y_div += 1; + } + // calculate the average of the sum of pixels in the buffer, and reset buffer + let pixel: u8 = (self.buf[self.out_x] / self.y_div).try_into().unwrap(); + self.buf[self.out_x] = 0; + // prepare for the next pixel in the row or column + self.out_x += 1; + if self.out_x >= self.out_width { + self.out_x = 0; + self.out_y += 1; + self.y_div = 0; + } + Some(pixel) + } +} + +pub trait ShrinkIterator: Iterator + Sized { + fn shrink(self, in_width: usize, out_width: usize) -> Shrink { + Shrink::new(self, in_width, out_width) + } +} + +impl> ShrinkIterator for I {}