diff --git a/flif-cli/src/main.rs b/flif-cli/src/main.rs index 6fccdcb..f5017b8 100644 --- a/flif-cli/src/main.rs +++ b/flif-cli/src/main.rs @@ -91,11 +91,11 @@ fn decode(identify: bool, input: &str, output: Option) -> Result<()> { let mut writer = encoder.write_header().unwrap(); // Get the raw pixel array of the FLIF image - let data = image.get_raw_pixels(); + let data = image.raw(); // Save as PNG writer.write_image_data(&data).unwrap(); } else { - std::io::stdout().write_all(&image.get_raw_pixels())?; + std::io::stdout().write_all(&image.raw())?; } } Ok(()) @@ -111,14 +111,17 @@ fn id_file(info: &FlifInfo) { println!("channels: {:?}", info.header.channels); println!("dimensions: {} x {}", info.header.width, info.header.height); - let len = info.second_header.transformations.len(); + let len = info.second_header.transformations.set.len(); if len != 0 { println!("transformations:"); - for transformation in info.second_header.transformations[..len - 1].iter() { + for transformation in info.second_header.transformations.set[..len - 1].iter() { println!("├── {}", transformation); } - println!("└── {}", info.second_header.transformations[len - 1]); + println!( + "└── {}", + info.second_header.transformations.set[len - 1] + ); } } diff --git a/flif/examples/decode.rs b/flif/examples/decode.rs index d75da2a..9438570 100644 --- a/flif/examples/decode.rs +++ b/flif/examples/decode.rs @@ -9,8 +9,14 @@ use flif::Flif; use png::HasParameters; fn main() { - decode_and_write("resources/flif_logo.flif", "examples/flif_logo_out.png"); - decode_and_write("resources/sea_snail.flif", "examples/sea_snail_out.png"); + // decode_and_write( + // "resources/flif_logo.flif", + // "flif/examples/flif_logo_out.png", + // ); + decode_and_write( + "resources/sea_snail_cutout.flif", + "flif/examples/sea_snail.png", + ); } fn decode_and_write(input: &str, output: &str) { diff --git a/flif/examples/sea_snail_out.png b/flif/examples/sea_snail_out.png index 6e9d080..10fa9e3 100644 Binary files a/flif/examples/sea_snail_out.png and b/flif/examples/sea_snail_out.png differ diff --git a/flif/src/components/header.rs b/flif/src/components/header.rs index 60fcba7..c427197 100644 --- a/flif/src/components/header.rs +++ b/flif/src/components/header.rs @@ -1,13 +1,15 @@ +use components::transformations::TransformationSet; +use pixels::Greyscale; +use pixels::Rgb; +use pixels::Rgba; use std::io::Read; -use super::transformations; -use super::transformations::{Transform, Transformation}; -use pixels::ColorSpace; use error::*; use numbers::chances::UpdateTable; use numbers::rac::RacRead; use numbers::symbol::UniformSymbolCoder; use numbers::FlifReadExt; +use pixels::ColorSpace; use Limits; #[derive(Eq, PartialEq, Debug, Clone, Copy)] @@ -116,7 +118,7 @@ impl Header { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug)] pub struct SecondHeader { pub bits_per_pixel: Vec, pub alpha_zero: bool, @@ -126,22 +128,18 @@ pub struct SecondHeader { pub cutoff: u8, pub alpha_divisor: u8, pub custom_bitchance: bool, - pub transformations: Vec, // Placeholder until transformations are implemented + pub transformations: TransformationSet, // Placeholder until transformations are implemented pub invis_pixel_predictor: Option, } impl SecondHeader { - pub(crate) fn from_rac( - main_header: &Header, - rac: &mut R, - ) -> Result<(Self, Box)> { + pub(crate) fn from_rac(main_header: &Header, rac: &mut R) -> Result { let bits_per_pixel = (0..main_header.channels as u8) .map(|_| match main_header.bytes_per_channel { BytesPerChannel::One => Ok(8), BytesPerChannel::Two => Ok(16), BytesPerChannel::Custom => rac.read_val(1, 16), - }) - .collect::>>()?; + }).collect::>>()?; let alpha_zero = if main_header.channels == ColorSpace::RGBA { rac.read_bool()? @@ -179,30 +177,35 @@ impl SecondHeader { }; let update_table = UpdateTable::new(alpha_divisor, cutoff); - let (transformations, transform) = - transformations::load_transformations(rac, main_header.channels, &update_table)?; - + let transformations = match main_header.channels { + ColorSpace::Monochrome => ::components::transformations::load_transformations::< + _, + Greyscale, + >(rac, &update_table)?, + ColorSpace::RGB => { + ::components::transformations::load_transformations::<_, Rgb>(rac, &update_table)? + } + ColorSpace::RGBA => { + ::components::transformations::load_transformations::<_, Rgba>(rac, &update_table)? + } + }; let invis_pixel_predictor = if alpha_zero && main_header.interlaced { Some(rac.read_val(0, 2)?) } else { - // Garbage value None }; - Ok(( - SecondHeader { - bits_per_pixel, - alpha_zero, - loops, - frame_delay, - custom_cutoff, - cutoff, - alpha_divisor, - custom_bitchance, - transformations, - invis_pixel_predictor, - }, - transform, - )) + Ok(SecondHeader { + bits_per_pixel, + alpha_zero, + loops, + frame_delay, + custom_cutoff, + cutoff, + alpha_divisor, + custom_bitchance, + transformations, + invis_pixel_predictor, + }) } } diff --git a/flif/src/components/transformations/bounds.rs b/flif/src/components/transformations/bounds.rs index 17803a2..855eecf 100644 --- a/flif/src/components/transformations/bounds.rs +++ b/flif/src/components/transformations/bounds.rs @@ -1,29 +1,28 @@ use super::Transform; -use pixels::{Rgba, RgbaChannels, ColorSpace}; use components::transformations::ColorRange; use error::*; use numbers::chances::{ChanceTable, UpdateTable}; use numbers::near_zero::NearZeroCoder; use numbers::rac::RacRead; +use pixels::Pixel; +use pixels::{ChannelsTrait, RgbaChannels}; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Bounds { ranges: [ColorRange; 4], - previous_transformation: Box, } impl Bounds { - pub fn new( + pub fn new( rac: &mut R, - trans: Box, - channels: ColorSpace, + trans: &T, update_table: &UpdateTable, ) -> Result { let mut context = ChanceTable::new(update_table); let mut ranges = [ColorRange::default(); 4]; - for &c in &RgbaChannels::ORDER[..channels as usize] { - let t_range = trans.range(c); - let c = c as usize; + for c in P::get_channels().as_ref() { + let t_range = trans.range::

(*c); + let c = c.as_channel() as usize; ranges[c].min = rac.read_near_zero(t_range.min, t_range.max, &mut context)?; ranges[c].max = rac.read_near_zero(ranges[c].min, t_range.max, &mut context)?; @@ -32,29 +31,33 @@ impl Bounds { ranges[c].max = ::std::cmp::min(ranges[c].max, t_range.max); } - Ok(Bounds { - ranges, - previous_transformation: trans, - }) + Ok(Bounds { ranges }) } } impl Transform for Bounds { - fn undo(&self, pixel: Rgba) -> Rgba { - self.previous_transformation.undo(pixel) + fn undo(&self, pixel: P) -> P { + pixel } - fn range(&self, channel: RgbaChannels) -> ColorRange { - self.ranges[channel as usize] + fn range(&self, channel: P::Channels) -> ColorRange { + self.ranges[channel.as_channel() as usize] } - fn crange(&self, channel: RgbaChannels, values: Rgba) -> ColorRange { - if channel == RgbaChannels::Red || channel == RgbaChannels::Alpha { - return self.ranges[channel as usize]; + fn crange( + &self, + channel: P::Channels, + values: P, + previous: &[T], + ) -> ColorRange { + let rgba_channel = channel.as_channel(); + if rgba_channel == RgbaChannels::Red || rgba_channel == RgbaChannels::Alpha { + return self.ranges[rgba_channel as usize]; } - let mut range = self.previous_transformation.crange(channel, values); - let channel = channel as usize; + let (last, rest) = previous.split_last().unwrap(); + let mut range = last.crange(channel, values, rest); + let channel = rgba_channel as usize; range.min = range.min.max(self.ranges[channel].min); range.max = range.max.min(self.ranges[channel].max); diff --git a/flif/src/components/transformations/channel_compact.rs b/flif/src/components/transformations/channel_compact.rs index e4b446c..da45eab 100644 --- a/flif/src/components/transformations/channel_compact.rs +++ b/flif/src/components/transformations/channel_compact.rs @@ -4,31 +4,29 @@ use error::*; use numbers::chances::{ChanceTable, UpdateTable}; use numbers::near_zero::NearZeroCoder; use numbers::rac::RacRead; -use pixels::{ColorSpace, Rgba, RgbaChannels}; +use pixels::ChannelsTrait; +use pixels::Pixel; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ChannelCompact { ranges: [ColorRange; 4], decompacted: [Vec; 4], - channels: ColorSpace, } impl ChannelCompact { - pub fn new( + pub fn new( rac: &mut R, - transformation: T, - channels: ColorSpace, + transformation: &T, update_table: &UpdateTable, ) -> Result { let mut context = ChanceTable::new(update_table); let mut t = ChannelCompact { ranges: Default::default(), decompacted: Default::default(), - channels, }; - for &c in &RgbaChannels::ORDER[..channels as usize] { - let t_range = transformation.range(c); - let c = c as usize; + for c in P::get_channels().as_ref() { + let t_range = transformation.range::

(*c); + let c = c.as_channel() as usize; t.ranges[c].max = rac.read_near_zero(0, t_range.max - t_range.min, &mut context)?; let mut min = t_range.min; for i in 0..t.ranges[c].max + 1 { @@ -48,20 +46,28 @@ impl ChannelCompact { } impl Transform for ChannelCompact { - fn undo(&self, mut pixel: Rgba) -> Rgba { - for c in self.channels { - let c = c as usize; - pixel.0[c] = self.decompacted[c][pixel.0[c] as usize]; + fn undo(&self, mut pixel: P) -> P { + for c in P::get_channels().as_ref() { + let previous = pixel.get_value(*c); + pixel.set_value( + self.decompacted[c.as_channel() as usize][previous as usize], + *c, + ); } pixel } - fn range(&self, channel: RgbaChannels) -> ColorRange { - self.ranges[channel as usize] + fn range(&self, channel: P::Channels) -> ColorRange { + self.ranges[channel.as_channel() as usize] } - fn crange(&self, channel: RgbaChannels, _values: Rgba) -> ColorRange { - self.ranges[channel as usize] + fn crange( + &self, + channel: P::Channels, + _values: P, + _previous: &[T], + ) -> ColorRange { + self.ranges[channel.as_channel() as usize] } } diff --git a/flif/src/components/transformations/mod.rs b/flif/src/components/transformations/mod.rs index 7270bee..fa5a7c6 100644 --- a/flif/src/components/transformations/mod.rs +++ b/flif/src/components/transformations/mod.rs @@ -2,23 +2,24 @@ use self::bounds::Bounds; use self::channel_compact::ChannelCompact; use self::permute_planes::PermutePlanes; use self::ycocg::YCoGg; -use pixels::{Rgba, RgbaChannels, ColorSpace, ColorValue}; use error::*; use numbers::chances::UpdateTable; use numbers::rac::RacRead; use numbers::symbol::UniformSymbolCoder; +use pixels::{ColorValue, Pixel}; mod bounds; mod channel_compact; mod permute_planes; mod ycocg; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum Transformation { - ChannelCompact, - YCoGg, - PermutePlanes, - Bounds, + Orig, + ChannelCompact(ChannelCompact), + YCoGg(YCoGg), + PermutePlanes(PermutePlanes), + Bounds(Bounds), PaletteAlpha, Palette, ColorBuckets, @@ -28,46 +29,149 @@ pub enum Transformation { } impl Transformation { - pub(crate) fn from_id(id: u8) -> Option { - use self::Transformation::*; - match id { - 0 => Some(ChannelCompact), - 1 => Some(YCoGg), - 3 => Some(PermutePlanes), - 4 => Some(Bounds), - 5 => Some(PaletteAlpha), - 6 => Some(Palette), - 7 => Some(ColorBuckets), - 10 => Some(DuplicateFrame), - 11 => Some(FrameShape), - 12 => Some(FrameLookback), - _ => None, + pub(crate) fn name(&self) -> &'static str { + match self { + Transformation::Orig => "Original (Pseudo Transformation)", + Transformation::ChannelCompact(_) => "Channel Compact", + Transformation::YCoGg(_) => "YCoCg", + Transformation::PermutePlanes(_) => "Permute Planes", + Transformation::Bounds(_) => "Bounds", + Transformation::PaletteAlpha => "Palette Alpha", + Transformation::Palette => "Palette", + Transformation::ColorBuckets => "Color Buckets", + Transformation::DuplicateFrame => "Duplicate Frame", + Transformation::FrameShape => "Frame Shape", + Transformation::FrameLookback => "Frame Lookback", + } + } + + pub(crate) fn from_rac( + rac: &mut R, + previous: &T, + update_table: &UpdateTable, + ) -> Result { + let id = rac.read_val(0, 13)?; + let t = match id { + 0 => Transformation::ChannelCompact(ChannelCompact::new::( + rac, + previous, + update_table, + )?), + 1 => Transformation::YCoGg(YCoGg::new::(previous)), + 3 => Transformation::PermutePlanes(PermutePlanes::new::(previous)), + 4 => Transformation::Bounds(Bounds::new::(rac, previous, update_table)?), + 5 => Transformation::PaletteAlpha, + 6 => Transformation::Palette, + 7 => Transformation::ColorBuckets, + 10 => Transformation::DuplicateFrame, + 11 => Transformation::FrameShape, + 12 => Transformation::FrameLookback, + _ => { + return Err(Error::InvalidOperation( + "Invalid transformation identifier read, possibly corrupt file".into(), + )); + } + }; + + Ok(t) + } +} + +impl Transform for Transformation { + #[inline(always)] + fn snap( + &self, + channel: P::Channels, + pixel: P, + value: ColorValue, + previous: ColorRange, + ) -> ColorValue { + match self { + Transformation::Orig => Orig.snap(channel, pixel, value, previous), + Transformation::ChannelCompact(t) => t.snap(channel, pixel, value, previous), + Transformation::YCoGg(t) => t.snap(channel, pixel, value, previous), + Transformation::PermutePlanes(t) => t.snap(channel, pixel, value, previous), + Transformation::Bounds(t) => t.snap(channel, pixel, value, previous), + Transformation::PaletteAlpha => unimplemented!(), + Transformation::Palette => unimplemented!(), + Transformation::ColorBuckets => unimplemented!(), + Transformation::DuplicateFrame => unimplemented!(), + Transformation::FrameShape => unimplemented!(), + Transformation::FrameLookback => unimplemented!(), + } + } + + #[inline(always)] + fn undo(&self, pixel: P) -> P { + match self { + Transformation::Orig => Orig.undo(pixel), + Transformation::ChannelCompact(t) => t.undo(pixel), + Transformation::YCoGg(t) => t.undo(pixel), + Transformation::PermutePlanes(t) => t.undo(pixel), + Transformation::Bounds(t) => t.undo(pixel), + Transformation::PaletteAlpha => unimplemented!(), + Transformation::Palette => unimplemented!(), + Transformation::ColorBuckets => unimplemented!(), + Transformation::DuplicateFrame => unimplemented!(), + Transformation::FrameShape => unimplemented!(), + Transformation::FrameLookback => unimplemented!(), + } + } + + #[inline(always)] + fn range(&self, channel: P::Channels) -> ColorRange { + match self { + Transformation::Orig => Orig.range::

(channel), + Transformation::ChannelCompact(t) => t.range::

(channel), + Transformation::YCoGg(t) => t.range::

(channel), + Transformation::PermutePlanes(t) => t.range::

(channel), + Transformation::Bounds(t) => t.range::

(channel), + Transformation::PaletteAlpha => unimplemented!(), + Transformation::Palette => unimplemented!(), + Transformation::ColorBuckets => unimplemented!(), + Transformation::DuplicateFrame => unimplemented!(), + Transformation::FrameShape => unimplemented!(), + Transformation::FrameLookback => unimplemented!(), + } + } + + #[inline(always)] + fn crange( + &self, + channel: P::Channels, + values: P, + previous: &[T], + ) -> ColorRange { + match self { + Transformation::Orig => Orig.crange(channel, values, previous), + Transformation::ChannelCompact(t) => t.crange(channel, values, previous), + Transformation::YCoGg(t) => t.crange(channel, values, previous), + Transformation::PermutePlanes(t) => t.crange(channel, values, previous), + Transformation::Bounds(t) => t.crange(channel, values, previous), + Transformation::PaletteAlpha => unimplemented!(), + Transformation::Palette => unimplemented!(), + Transformation::ColorBuckets => unimplemented!(), + Transformation::DuplicateFrame => unimplemented!(), + Transformation::FrameShape => unimplemented!(), + Transformation::FrameLookback => unimplemented!(), } } } impl ::std::fmt::Display for Transformation { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - use self::Transformation::*; - match *self { - ChannelCompact => write!(f, "Channel Compact"), - YCoGg => write!(f, "YCoCg"), - PermutePlanes => write!(f, "Permute Planes"), - Bounds => write!(f, "Bounds"), - PaletteAlpha => write!(f, "Palette Alpha"), - Palette => write!(f, "Palette"), - ColorBuckets => write!(f, "Color Buckets"), - DuplicateFrame => write!(f, "Duplicate Frame"), - FrameShape => write!(f, "Frame Shape"), - FrameLookback => write!(f, "Frame Lookback"), - } + write!(f, "{}", self.name()) } } pub trait Transform: ::std::fmt::Debug + Send + Sync { - fn snap(&self, channel: RgbaChannels, pixel: Rgba, value: ColorValue) -> ColorValue { - let range = self.crange(channel, pixel); - + fn snap( + &self, + _channel: P::Channels, + _pixel: P, + value: ColorValue, + range: ColorRange, + ) -> ColorValue { if value > range.max { range.max } else if value < range.min { @@ -77,73 +181,88 @@ pub trait Transform: ::std::fmt::Debug + Send + Sync { } } - fn undo(&self, pixel: Rgba) -> Rgba; + fn undo(&self, pixel: P) -> P; - fn range(&self, channel: RgbaChannels) -> ColorRange; + fn range(&self, channel: P::Channels) -> ColorRange; - fn crange(&self, channel: RgbaChannels, values: Rgba) -> ColorRange; + fn crange( + &self, + channel: P::Channels, + values: P, + previous: &[T], + ) -> ColorRange; } -impl Transform for Box { - fn undo(&self, pixel: Rgba) -> Rgba { - (**self).undo(pixel) +#[derive(Debug)] +struct Orig; + +impl Transform for Orig { + fn undo(&self, pixel: P) -> P { + pixel } - fn range(&self, channel: RgbaChannels) -> ColorRange { - (**self).range(channel) + fn range(&self, _channel: P::Channels) -> ColorRange { + ColorRange { min: 0, max: 255 } } - fn crange(&self, channel: RgbaChannels, values: Rgba) -> ColorRange { - (**self).crange(channel, values) + fn crange( + &self, + _channel: P::Channels, + _values: P, + _previous: &[T], + ) -> ColorRange { + ColorRange { min: 0, max: 255 } } } -#[derive(Debug)] -struct Orig; +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TransformationSet { + pub set: Vec, + pub last: Transformation, +} -impl Transform for Orig { - fn undo(&self, pixel: Rgba) -> Rgba { pixel } +impl TransformationSet { + pub fn snap(&self, channel: P::Channels, pixel: P, value: ColorValue) -> ColorValue { + let range = self.last.crange(channel, pixel, &self.set); + self.last.snap(channel, pixel, value, range) + } - fn range(&self, _channel: RgbaChannels) -> ColorRange { - ColorRange { min: 0, max: 255 } + pub fn undo(&self, mut pixel: P) -> P { + for t in self.set.iter().rev() { + pixel = t.undo(pixel); + } + + self.last.undo(pixel) } - fn crange(&self, _channel: RgbaChannels, _values: Rgba) -> ColorRange { - ColorRange { min: 0, max: 255 } + pub fn range(&self, channel: P::Channels) -> ColorRange { + self.last.range::

(channel) + } + + pub fn crange(&self, channel: P::Channels, values: P) -> ColorRange { + self.last.crange(channel, values, &self.set) } } -pub fn load_transformations( +pub fn load_transformations( rac: &mut R, - channels: ColorSpace, update_table: &UpdateTable, -) -> Result<(Vec, Box)> { - let mut transform: Box = Box::new(Orig); - let mut transformations = Vec::new(); +) -> Result { + let mut transformations = vec![Transformation::Orig]; while rac.read_bit()? { - let id = Transformation::from_id(rac.read_val(0, 13)?).ok_or(Error::InvalidOperation( - "Invalid transformation identifier read, possibly corrupt file".into(), - ))?; - transform = match id { - Transformation::ChannelCompact => { - Box::new(ChannelCompact::new(rac, transform, channels, update_table)?) - } - Transformation::YCoGg => Box::new(YCoGg::new(transform)) as Box, - Transformation::PermutePlanes => { - Box::new(PermutePlanes::new(transform)) as Box - } - Transformation::Bounds => { - Box::new(Bounds::new(rac, transform, channels, update_table)?) - } - _ => { - return Err(Error::UnimplementedTransformation(id.to_string())); - } - }; - - transformations.push(id); + let transformation = Transformation::from_rac::<_, R, P>( + rac, + transformations.last().unwrap_or(&Transformation::Orig), + update_table, + )?; + transformations.push(transformation); } - Ok((transformations, transform)) + let last = transformations.pop().unwrap(); + Ok(TransformationSet { + set: transformations, + last: last, + }) } #[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] diff --git a/flif/src/components/transformations/permute_planes.rs b/flif/src/components/transformations/permute_planes.rs index 6f6f848..13164f7 100644 --- a/flif/src/components/transformations/permute_planes.rs +++ b/flif/src/components/transformations/permute_planes.rs @@ -1,31 +1,34 @@ use super::Transform; use components::transformations::ColorRange; -use pixels::{Rgba, RgbaChannels}; +use pixels::Pixel; +use pixels::{ChannelsTrait, RgbaChannels}; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct PermutePlanes { max: i16, } impl PermutePlanes { - pub fn new(transformation: T) -> PermutePlanes { - let max_iter = [ - transformation.range(RgbaChannels::Red).max, - transformation.range(RgbaChannels::Green).max, - transformation.range(RgbaChannels::Blue).max, - ]; + pub fn new(transformation: &T) -> PermutePlanes { + let old_max = P::get_channels() + .as_ref() + .iter() + .map(|c| transformation.range::

(*c).max) + .max() + .unwrap(); - let old_max = max_iter.iter().max().unwrap(); let new_max = (((old_max / 4) + 1) * 4) - 1; PermutePlanes { max: new_max } } } impl Transform for PermutePlanes { - fn undo(&self, pixel: Rgba) -> Rgba { pixel } + fn undo(&self, pixel: P) -> P { + pixel + } - fn range(&self, channel: RgbaChannels) -> ColorRange { - let min = match channel { + fn range(&self, channel: P::Channels) -> ColorRange { + let min = match channel.as_channel() { RgbaChannels::Red => 0, _ => -self.max, }; @@ -33,7 +36,12 @@ impl Transform for PermutePlanes { ColorRange { min, max: self.max } } - fn crange(&self, _channel: RgbaChannels, _values: Rgba) -> ColorRange { + fn crange( + &self, + _channel: P::Channels, + _values: P, + _previous: &[T], + ) -> ColorRange { unimplemented!() } } diff --git a/flif/src/components/transformations/ycocg.rs b/flif/src/components/transformations/ycocg.rs index 9ce1758..eec91bb 100644 --- a/flif/src/components/transformations/ycocg.rs +++ b/flif/src/components/transformations/ycocg.rs @@ -1,74 +1,89 @@ use super::Transform; -use pixels::{Rgba, RgbaChannels}; use components::transformations::ColorRange; +use pixels::Pixel; +use pixels::{ChannelsTrait, RgbaChannels}; -const R: usize = 0; -const G: usize = 1; -const B: usize = 2; - -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct YCoGg { max: i16, - alpha_range: ColorRange, + alpha_range: Option, } impl YCoGg { - pub fn new(transformation: T) -> YCoGg { - let max_iter = [ - transformation.range(RgbaChannels::Red).max, - transformation.range(RgbaChannels::Blue).max, - transformation.range(RgbaChannels::Green).max, - ]; - - let old_max = max_iter.iter().max().unwrap(); + pub fn new(transformation: &T) -> YCoGg { + let old_max = P::get_channels() + .as_ref() + .iter() + .map(|c| transformation.range::

(*c).max) + .max() + .unwrap(); + let new_max = (((old_max / 4) + 1) * 4) - 1; + let alpha_range = if let Some(alpha) = P::Channels::alpha() { + Some(transformation.range::

(alpha)) + } else { + None + }; + YCoGg { max: new_max, - alpha_range: transformation.range(RgbaChannels::Alpha), + alpha_range, } } } impl Transform for YCoGg { - fn undo(&self, pixel: Rgba) -> Rgba { - let pixel = pixel.0; - let red = pixel[G] + pixel[R] + ((1 - pixel[B]) >> 1) - (pixel[G] >> 1); - let green = pixel[R] - ((-pixel[B]) >> 1); - let blue = pixel[R] + ((1 - pixel[B]) >> 1) - (pixel[G] >> 1); - let alpha = pixel[3]; - - Rgba([red, green, blue, alpha]) + fn undo(&self, mut pixel: P) -> P { + let r = P::Channels::red().unwrap(); + let g = P::Channels::green().unwrap(); + let b = P::Channels::blue().unwrap(); + let red = pixel.get_value(g) + pixel.get_value(r) + ((1 - pixel.get_value(b)) >> 1) + - (pixel.get_value(g) >> 1); + + let green = pixel.get_value(r) - ((-pixel.get_value(b)) >> 1); + let blue = pixel.get_value(r) + ((1 - pixel.get_value(b)) >> 1) - (pixel.get_value(g) >> 1); + + pixel.set_value(red, r); + pixel.set_value(green, g); + pixel.set_value(blue, b); + + pixel } - fn range(&self, channel: RgbaChannels) -> ColorRange { - let (min, max) = match channel { + fn range(&self, channel: P::Channels) -> ColorRange { + let (min, max) = match channel.as_channel() { RgbaChannels::Red => (0, self.max), RgbaChannels::Green | RgbaChannels::Blue => (-self.max, self.max), - _ => (self.alpha_range.min, self.alpha_range.max), + _ => (self.alpha_range.unwrap().min, self.alpha_range.unwrap().max), }; ColorRange { min, max } } - fn crange(&self, channel: RgbaChannels, values: Rgba) -> ColorRange { - let values = values.0; + fn crange( + &self, + channel: P::Channels, + values: P, + _previous: &[T], + ) -> ColorRange { let origmax4 = (self.max + 1) / 4; - match channel { - channel @ RgbaChannels::Red => self.range(channel), + match channel.as_channel() { + RgbaChannels::Red => self.range::

(channel), RgbaChannels::Green => { - let min = if values[R] < origmax4 - 1 { - -3 - (4 * values[R]) - } else if values[R] > (3 * origmax4) - 1 { - 4 * (values[R] - self.max) + let r = values.get_value(P::Channels::red().unwrap()); + let min = if r < origmax4 - 1 { + -3 - (4 * r) + } else if r > (3 * origmax4) - 1 { + 4 * (r - self.max) } else { -self.max }; - let max = if values[R] < origmax4 - 1 { - 3 + (4 * values[R]) - } else if values[R] > (3 * origmax4) - 1 { - 4 * origmax4 - 4 * (1 + values[R] - 3 * origmax4) + let max = if r < origmax4 - 1 { + 3 + (4 * r) + } else if r > (3 * origmax4) - 1 { + 4 * origmax4 - 4 * (1 + r - 3 * origmax4) } else { self.max }; @@ -76,11 +91,13 @@ impl Transform for YCoGg { ColorRange { min, max } } RgbaChannels::Blue => { - let co = values[G]; - let y = values[R]; - let min = if values[R] < origmax4 - 1 { + let r = values.get_value(P::Channels::red().unwrap()); + let g = values.get_value(P::Channels::green().unwrap()); + let co = g; + let y = r; + let min = if r < origmax4 - 1 { -(2 * y + 1) - } else if values[R] > (3 * origmax4) - 1 { + } else if r > (3 * origmax4) - 1 { -(2 * (4 * origmax4 - 1 - y) - ((1 + co.abs()) / 2) * 2) } else { -::std::cmp::min( @@ -89,9 +106,9 @@ impl Transform for YCoGg { ) }; - let max = if values[R] < origmax4 - 1 { + let max = if r < origmax4 - 1 { 1 + 2 * y - (co.abs() / 2) * 2 - } else if values[R] > (3 * origmax4) - 1 { + } else if r > (3 * origmax4) - 1 { 2 * (4 * origmax4 - 1 - y) } else { -::std::cmp::max( @@ -102,7 +119,7 @@ impl Transform for YCoGg { ColorRange { min, max } } - RgbaChannels::Alpha => self.alpha_range, + RgbaChannels::Alpha => self.alpha_range.unwrap(), } } } diff --git a/flif/src/decoder.rs b/flif/src/decoder.rs index 4c68fc0..82ff6ba 100644 --- a/flif/src/decoder.rs +++ b/flif/src/decoder.rs @@ -3,11 +3,11 @@ use std::io::Read; use super::{Flif, FlifInfo, Metadata}; use components::header::{BytesPerChannel, Header, SecondHeader}; use decoding_image::DecodingImage; -use pixels::{Greyscale, Rgb, Rgba}; use error::*; use numbers::chances::UpdateTable; use numbers::rac::Rac; use pixels::ColorSpace; +use pixels::{Greyscale, Rgb, Rgba}; use Limits; pub struct Decoder { @@ -63,21 +63,24 @@ impl Decoder { ); let raw = match self.info.header.channels { - ColorSpace::Monochrome => { - DecodingImage::::new( - &self.info, &mut self.rac, &self.limits, &update_table - )?.process()? - }, - ColorSpace::RGB => { - DecodingImage::::new( - &self.info, &mut self.rac, &self.limits, &update_table - )?.process()? - }, - ColorSpace::RGBA => { - DecodingImage::::new( - &self.info, &mut self.rac, &self.limits, &update_table - )?.process()? - }, + ColorSpace::Monochrome => DecodingImage::::new( + &self.info, + &mut self.rac, + &self.limits, + &update_table, + )?.process()?, + ColorSpace::RGB => DecodingImage::::new( + &self.info, + &mut self.rac, + &self.limits, + &update_table, + )?.process()?, + ColorSpace::RGBA => DecodingImage::::new( + &self.info, + &mut self.rac, + &self.limits, + &update_table, + )?.process()?, }; Ok(Flif { @@ -102,14 +105,13 @@ fn identify_internal(mut reader: R, limits: Limits) -> Result<(FlifInfo // the Read object directly. let mut rac: Rac<_> = Rac::from_reader(reader)?; - let (second_header, transform) = SecondHeader::from_rac(&main_header, &mut rac)?; + let second_header = SecondHeader::from_rac(&main_header, &mut rac)?; Ok(( FlifInfo { header: main_header, metadata, second_header, - transform, }, rac, )) diff --git a/flif/src/decoding_image.rs b/flif/src/decoding_image.rs index 5f328e5..7572a56 100644 --- a/flif/src/decoding_image.rs +++ b/flif/src/decoding_image.rs @@ -1,16 +1,15 @@ use std::io::Read; -use pixels::ColorValue; -use components::transformations::Transform; pub use error::{Error, Result}; use maniac::{core_pvec, edge_pvec, ManiacTree}; -use numbers::rac::Rac; -use numbers::median3; use numbers::chances::UpdateTable; +use numbers::median3; +use numbers::rac::Rac; +use pixels::ColorValue; use {FlifInfo, Limits}; -use pixels::{Pixel, ChannelsTrait}; pub use decoder::Decoder; +use pixels::{ChannelsTrait, Pixel}; pub(crate) struct DecodingImage<'a, P: Pixel, R: Read + 'a> { height: u32, @@ -49,7 +48,9 @@ pub(crate) struct CorePixelVicinity { // safety criterias defined by `debug_assert`s impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { pub fn new( - info: &'a FlifInfo, rac: &'a mut Rac, limits: &'a Limits, + info: &'a FlifInfo, + rac: &'a mut Rac, + limits: &'a Limits, update_table: &'a UpdateTable, ) -> Result> { let pixels = (info.header.height * info.header.width) as usize; @@ -78,9 +79,7 @@ impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { self.data.get_unchecked(self.get_idx(x, y)).get_value(chan) } - unsafe fn get_edge_vicinity(&self, x: u32, y: u32, chan: P::Channels) - -> EdgePixelVicinity

- { + unsafe fn get_edge_vicinity(&self, x: u32, y: u32, chan: P::Channels) -> EdgePixelVicinity

{ debug_assert!(x < self.width && y < self.height && self.check_data()); EdgePixelVicinity { pixel: *self.data.get_unchecked(self.get_idx(x, y)), @@ -118,9 +117,7 @@ impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { } } - unsafe fn get_core_vicinity(&self, x: u32, y: u32, chan: P::Channels) - -> CorePixelVicinity

- { + unsafe fn get_core_vicinity(&self, x: u32, y: u32, chan: P::Channels) -> CorePixelVicinity

{ debug_assert!(x < self.width - 1 && y < self.height && x > 1 && y > 1 && self.check_data()); CorePixelVicinity { pixel: *self.data.get_unchecked(self.get_idx(x, y)), @@ -139,15 +136,20 @@ impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { vic: EdgePixelVicinity

, chan: P::Channels, maniac: &mut Option>, - ) -> Result - { - let c = chan.as_channel(); - let pix = vic.pixel.to_rgba(); - let range = self.info.transform.crange(c, pix); + ) -> Result { + let range = self + .info + .second_header + .transformations + .crange(chan, vic.pixel); Ok(if let Some(ref mut maniac) = maniac { let guess = make_edge_guess(self.info, &vic); - let snap = self.info.transform.snap(c, pix, guess); + let snap = self + .info + .second_header + .transformations + .snap(chan, vic.pixel, guess); let pvec = edge_pvec(snap, &vic); maniac.process(self.rac, &pvec, snap, range.min, range.max)? } else { @@ -160,15 +162,20 @@ impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { vic: CorePixelVicinity

, chan: P::Channels, maniac: &mut Option>, - ) -> Result - { - let c = chan.as_channel(); - let pix = vic.pixel.to_rgba(); - let range = self.info.transform.crange(c, pix); + ) -> Result { + let range = self + .info + .second_header + .transformations + .crange(chan, vic.pixel); Ok(if let Some(ref mut maniac) = maniac { let guess = make_core_guess(&vic); - let snap = self.info.transform.snap(c, pix, guess); + let snap = self + .info + .second_header + .transformations + .snap(chan, vic.pixel, guess); let pvec = core_pvec(snap, &vic); maniac.process(self.rac, &pvec, snap, range.min, range.max)? } else { @@ -182,8 +189,7 @@ impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { y: u32, chan: P::Channels, maniac: &mut Option>, - ) -> Result<()> - { + ) -> Result<()> { let vic = self.get_edge_vicinity(x, y, chan); let val = self.process_edge_pixel_safe(vic, chan, maniac)?; let idx = self.get_idx(x, y); @@ -197,8 +203,7 @@ impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { y: u32, chan: P::Channels, maniac: &mut Option>, - ) -> Result<()> - { + ) -> Result<()> { let vic = self.get_core_vicinity(x, y, chan); let val = self.process_core_pixel_safe(vic, chan, maniac)?; let idx = self.get_idx(x, y); @@ -210,14 +215,13 @@ impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { let channels = P::get_chan_order(); let mut maniac: [Option; 4] = Default::default(); for (i, chan) in channels.as_ref().iter().enumerate() { - let channel = chan.as_channel(); - let range = self.info.transform.range(channel); + let range = self.info.second_header.transformations.range::

(*chan); if range.min == range.max { maniac[i] = None; } else { - let tree = ManiacTree::new( + let tree = ManiacTree::new::<_, P>( self.rac, - channel, + *chan, self.info, self.update_table, self.limits, @@ -232,17 +236,24 @@ impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { // undo transofrms and copy raw data let n = P::size(); - let mut raw = Vec::with_capacity(n*self.data.len()); - for pixel in self.data.iter_mut() { - let rgba = self.info.transform.undo(pixel.to_rgba()); - raw.extend(rgba.0[..n].iter().map(|v| *v as u8 )); + let mut raw = Vec::with_capacity(n * self.data.len()); + for pixel in self.data.iter() { + let rgba = self + .info + .second_header + .transformations + .undo(*pixel) + .to_rgba(); + raw.extend(rgba.0[..n].iter().map(|v| *v as u8)); } Ok(raw.into_boxed_slice()) } fn channel_pass( - &mut self, chan: P::Channels, maniac: &mut Option> + &mut self, + chan: P::Channels, + maniac: &mut Option>, ) -> Result<()> { let width = self.width; let height = self.height; @@ -281,7 +292,6 @@ impl<'a, P: Pixel, R: Read> DecodingImage<'a, P, R> { } } - fn make_core_guess(pix_vic: &CorePixelVicinity

) -> i16 { let left = pix_vic.left; let top = pix_vic.top; @@ -291,28 +301,24 @@ fn make_core_guess(pix_vic: &CorePixelVicinity

) -> i16 { } fn make_edge_guess

(info: &FlifInfo, vic: &EdgePixelVicinity

) -> i16 - where P: Pixel, P::Channels: ChannelsTrait +where + P: Pixel, + P::Channels: ChannelsTrait, { - let transformation = &info.transform; + let transformation = &info.second_header.transformations; let left = if let Some(val) = vic.left { val } else if let Some(val) = vic.top { val - } else if info.second_header.alpha_zero && - !vic.chan.is_alpha() && vic.pixel.is_alpha_zero() - { - let chan = vic.chan.as_channel(); - (transformation.range(chan).min + transformation.range(chan).max) / 2 + } else if info.second_header.alpha_zero && !vic.chan.is_alpha() && vic.pixel.is_alpha_zero() { + let chan = vic.chan; + (transformation.range::

(chan).min + transformation.range::

(chan).max) / 2 } else { - transformation.range(vic.chan.as_channel()).min + transformation.range::

(vic.chan).min }; - let top = if let Some(val) = vic.top { - val - } else { - left - }; + let top = if let Some(val) = vic.top { val } else { left }; let top_left = if let Some(val) = vic.top_left { val diff --git a/flif/src/lib.rs b/flif/src/lib.rs index 9ae785d..b23b926 100644 --- a/flif/src/lib.rs +++ b/flif/src/lib.rs @@ -22,7 +22,6 @@ use std::io::Read; use components::header::{Header, SecondHeader}; use components::metadata::Metadata; -use components::transformations::Transform; use decoding_image::DecodingImage; pub use decoder::Decoder; @@ -55,7 +54,10 @@ impl Flif { &self.info } - #[deprecated(since="0.3.1", note="please use `raw` and `into_raw` instead")] + #[deprecated( + since = "0.3.1", + note = "please use `raw` and `into_raw` instead" + )] pub fn get_raw_pixels(&self) -> Vec { self.raw.to_vec() } @@ -98,5 +100,4 @@ pub struct FlifInfo { pub header: Header, pub metadata: Vec, pub second_header: SecondHeader, - transform: Box, } diff --git a/flif/src/maniac/mod.rs b/flif/src/maniac/mod.rs index 8223f95..1802962 100644 --- a/flif/src/maniac/mod.rs +++ b/flif/src/maniac/mod.rs @@ -1,14 +1,15 @@ #![allow(unused)] +use pixels::Pixel; use std::io::Read; use components::transformations::ColorRange; use components::transformations::Transform; use error::*; -use pixels::{RgbaChannels, ColorSpace, ColorValue}; use numbers::chances::{ChanceTable, UpdateTable}; use numbers::near_zero::NearZeroCoder; use numbers::rac::{Rac, RacRead}; +use pixels::{ChannelsTrait, ColorSpace, ColorValue, RgbaChannels}; use DecodingImage; use FlifInfo; use Limits; @@ -21,9 +22,9 @@ pub struct ManiacTree<'a> { } impl<'a> ManiacTree<'a> { - pub fn new( + pub fn new( rac: &mut Rac, - channel: RgbaChannels, + channel: P::Channels, info: &FlifInfo, update_table: &'a UpdateTable, limits: &Limits, @@ -32,7 +33,7 @@ impl<'a> ManiacTree<'a> { let context_b = ChanceTable::new(update_table); let context_c = ChanceTable::new(update_table); - let prange = Self::build_prange_vec(channel, info); + let prange = Self::build_prange_vec::

(channel, info); let nodes = Self::create_nodes( rac, &mut [context_a, context_b, context_c], @@ -295,29 +296,29 @@ impl<'a> ManiacTree<'a> { } } - fn build_prange_vec(channel: RgbaChannels, info: &FlifInfo) -> Vec { + fn build_prange_vec(chan: P::Channels, info: &FlifInfo) -> Vec { let mut prange = Vec::new(); + let transform = &info.second_header.transformations; - let transform = &info.transform; - + let channel = chan.as_channel(); if channel == RgbaChannels::Green || channel == RgbaChannels::Blue { - prange.push(transform.range(RgbaChannels::Red)); + prange.push(transform.range::

(P::Channels::red().unwrap())); } if channel == RgbaChannels::Blue { - prange.push(transform.range(RgbaChannels::Green)); + prange.push(transform.range::

(P::Channels::green().unwrap())); } if channel != RgbaChannels::Alpha && info.header.channels == ColorSpace::RGBA { - prange.push(transform.range(RgbaChannels::Alpha)); + prange.push(transform.range::

(P::Channels::alpha().unwrap())); } - prange.push(transform.range(channel)); + prange.push(transform.range::

(chan)); prange.push(ColorRange { min: 0, max: 2 }); let maxdiff = ColorRange { - min: transform.range(channel).min - transform.range(channel).max, - max: transform.range(channel).max - transform.range(channel).min, + min: transform.range::

(chan).min - transform.range::

(chan).max, + max: transform.range::

(chan).max - transform.range::

(chan).min, }; prange.push(maxdiff); prange.push(maxdiff); diff --git a/flif/src/pixels.rs b/flif/src/pixels.rs index 7ad5c99..d4c7f28 100644 --- a/flif/src/pixels.rs +++ b/flif/src/pixels.rs @@ -1,3 +1,5 @@ +use std::fmt::Debug; + pub type ColorValue = i16; #[derive(PartialEq, Eq, Debug, Clone, Copy)] @@ -7,13 +9,17 @@ pub enum ColorSpace { RGBA = 4, } -pub trait ChannelsTrait { +pub trait ChannelsTrait: Sized + Debug + Copy { + fn red() -> Option; + fn green() -> Option; + fn blue() -> Option; + fn alpha() -> Option; fn as_channel(&self) -> RgbaChannels; fn is_alpha(&self) -> bool; } -pub trait Pixel: Default + Copy { - type Channels: ChannelsTrait + Copy; +pub trait Pixel: Default + Copy + Debug { + type Channels: ChannelsTrait + Copy + Send + Sync; type ChanOrder: AsRef<[Self::Channels]>; fn is_rgba() -> bool; @@ -32,6 +38,9 @@ pub trait Pixel: Default + Copy { fn to_rgba(&self) -> Rgba; fn get_chan_order() -> Self::ChanOrder; + + fn get_channels() -> Self::ChanOrder; + fn size() -> usize; } @@ -45,9 +54,29 @@ pub enum GreyChannels { impl ChannelsTrait for GreyChannels { #[inline(always)] - fn as_channel(&self) -> RgbaChannels { RgbaChannels::Red } + fn red() -> Option { + Some(GreyChannels::Grey) + } + #[inline(always)] + fn green() -> Option { + None + } + #[inline(always)] + fn blue() -> Option { + None + } + #[inline(always)] + fn alpha() -> Option { + None + } + #[inline(always)] + fn as_channel(&self) -> RgbaChannels { + RgbaChannels::Red + } #[inline(always)] - fn is_alpha(&self) -> bool { false } + fn is_alpha(&self) -> bool { + false + } } impl Pixel for Greyscale { @@ -55,17 +84,25 @@ impl Pixel for Greyscale { type ChanOrder = [GreyChannels; 1]; #[inline(always)] - fn is_rgba() -> bool { false } + fn is_rgba() -> bool { + false + } #[inline(always)] - fn get_value(&self, _chan: Self::Channels) -> ColorValue { self.0 } + fn get_value(&self, _chan: Self::Channels) -> ColorValue { + self.0 + } #[inline(always)] fn set_value(&mut self, val: ColorValue, _chan: Self::Channels) { self.0 = val; } #[inline(always)] - fn is_alpha_zero(&self) -> bool { false } + fn is_alpha_zero(&self) -> bool { + false + } #[inline(always)] - fn get_red_pvec(&self, _chan: Self::Channels) -> Option { None } + fn get_red_pvec(&self, _chan: Self::Channels) -> Option { + None + } #[inline(always)] fn get_green_pvec(&self, _chan: Self::Channels) -> Option { None @@ -75,14 +112,23 @@ impl Pixel for Greyscale { None } #[inline(always)] - fn to_rgba(&self) -> Rgba { Rgba([self.0, 0, 0, 0]) } + fn to_rgba(&self) -> Rgba { + Rgba([self.0, 0, 0, 0]) + } + #[inline(always)] + fn get_chan_order() -> Self::ChanOrder { + [GreyChannels::Grey] + } #[inline(always)] - fn get_chan_order() -> Self::ChanOrder { [GreyChannels::Grey] } + fn get_channels() -> Self::ChanOrder { + [GreyChannels::Grey] + } #[inline(always)] - fn size() -> usize { 1 } + fn size() -> usize { + 1 + } } - #[derive(Debug, Default, Copy, Clone)] pub struct Rgb([ColorValue; 3]); @@ -94,6 +140,22 @@ pub enum RgbChannels { } impl ChannelsTrait for RgbChannels { + #[inline(always)] + fn red() -> Option { + Some(RgbChannels::Red) + } + #[inline(always)] + fn green() -> Option { + Some(RgbChannels::Green) + } + #[inline(always)] + fn blue() -> Option { + Some(RgbChannels::Blue) + } + #[inline(always)] + fn alpha() -> Option { + None + } #[inline(always)] fn as_channel(&self) -> RgbaChannels { match self { @@ -103,7 +165,9 @@ impl ChannelsTrait for RgbChannels { } } #[inline(always)] - fn is_alpha(&self) -> bool { false } + fn is_alpha(&self) -> bool { + false + } } impl Pixel for Rgb { @@ -111,7 +175,9 @@ impl Pixel for Rgb { type ChanOrder = [RgbChannels; 3]; #[inline(always)] - fn is_rgba() -> bool { false } + fn is_rgba() -> bool { + false + } #[inline(always)] fn get_value(&self, chan: Self::Channels) -> ColorValue { self.0[chan as usize] @@ -121,7 +187,9 @@ impl Pixel for Rgb { self.0[chan as usize] = val; } #[inline(always)] - fn is_alpha_zero(&self) -> bool { false } + fn is_alpha_zero(&self) -> bool { + false + } #[inline(always)] fn get_red_pvec(&self, chan: Self::Channels) -> Option { if chan == RgbChannels::Green || chan == RgbChannels::Blue { @@ -132,23 +200,34 @@ impl Pixel for Rgb { } #[inline(always)] fn get_green_pvec(&self, chan: Self::Channels) -> Option { - if chan == RgbChannels::Blue { Some(self.0[1]) } else { None } + if chan == RgbChannels::Blue { + Some(self.0[1]) + } else { + None + } } #[inline(always)] fn get_alpha_pvec(&self, _chan: Self::Channels) -> Option { None } #[inline(always)] - fn to_rgba(&self) -> Rgba { Rgba([self.0[0], self.0[1], self.0[2], 0]) } + fn to_rgba(&self) -> Rgba { + Rgba([self.0[0], self.0[1], self.0[2], 0]) + } #[inline(always)] fn get_chan_order() -> Self::ChanOrder { [RgbChannels::Red, RgbChannels::Green, RgbChannels::Blue] } #[inline(always)] - fn size() -> usize { 3 } + fn get_channels() -> Self::ChanOrder { + [RgbChannels::Red, RgbChannels::Green, RgbChannels::Blue] + } + #[inline(always)] + fn size() -> usize { + 3 + } } - #[derive(Debug, Default, Copy, Clone)] pub struct Rgba(pub [ColorValue; 4]); @@ -163,16 +242,38 @@ pub enum RgbaChannels { impl RgbaChannels { /// this order is different from `get_chan_order` pub const ORDER: [Self; 4] = [ - RgbaChannels::Red, RgbaChannels::Green, - RgbaChannels::Blue, RgbaChannels::Alpha + RgbaChannels::Red, + RgbaChannels::Green, + RgbaChannels::Blue, + RgbaChannels::Alpha, ]; } impl ChannelsTrait for RgbaChannels { #[inline(always)] - fn as_channel(&self) -> RgbaChannels { *self } + fn red() -> Option { + Some(RgbaChannels::Red) + } + #[inline(always)] + fn green() -> Option { + Some(RgbaChannels::Green) + } + #[inline(always)] + fn blue() -> Option { + Some(RgbaChannels::Blue) + } + #[inline(always)] + fn alpha() -> Option { + Some(RgbaChannels::Alpha) + } + #[inline(always)] + fn as_channel(&self) -> RgbaChannels { + *self + } #[inline(always)] - fn is_alpha(&self) -> bool { *self == RgbaChannels::Alpha } + fn is_alpha(&self) -> bool { + *self == RgbaChannels::Alpha + } } impl Pixel for Rgba { @@ -180,7 +281,9 @@ impl Pixel for Rgba { type ChanOrder = [RgbaChannels; 4]; #[inline(always)] - fn is_rgba() -> bool { true } + fn is_rgba() -> bool { + true + } #[inline(always)] fn get_value(&self, chan: Self::Channels) -> ColorValue { self.0[chan as usize] @@ -190,7 +293,9 @@ impl Pixel for Rgba { self.0[chan as usize] = val; } #[inline(always)] - fn is_alpha_zero(&self) -> bool { self.0[3] == 0 } + fn is_alpha_zero(&self) -> bool { + self.0[3] == 0 + } #[inline(always)] fn get_red_pvec(&self, chan: Self::Channels) -> Option { if chan == RgbaChannels::Green || chan == RgbaChannels::Blue { @@ -201,14 +306,24 @@ impl Pixel for Rgba { } #[inline(always)] fn get_green_pvec(&self, chan: Self::Channels) -> Option { - if chan == RgbaChannels::Blue { Some(self.0[1]) } else { None } + if chan == RgbaChannels::Blue { + Some(self.0[1]) + } else { + None + } } #[inline(always)] fn get_alpha_pvec(&self, chan: Self::Channels) -> Option { - if chan != RgbaChannels::Alpha { Some(self.0[3]) } else { None } + if chan != RgbaChannels::Alpha { + Some(self.0[3]) + } else { + None + } } #[inline(always)] - fn to_rgba(&self) -> Rgba { *self } + fn to_rgba(&self) -> Rgba { + *self + } #[inline(always)] fn get_chan_order() -> Self::ChanOrder { [ @@ -219,6 +334,16 @@ impl Pixel for Rgba { ] } #[inline(always)] - fn size() -> usize { 4 } + fn get_channels() -> Self::ChanOrder { + [ + RgbaChannels::Red, + RgbaChannels::Green, + RgbaChannels::Blue, + RgbaChannels::Alpha, + ] + } + #[inline(always)] + fn size() -> usize { + 4 + } } - diff --git a/flif/tests/channel_compact.rs b/flif/tests/channel_compact.rs index 90535f0..9c1993c 100644 --- a/flif/tests/channel_compact.rs +++ b/flif/tests/channel_compact.rs @@ -3,7 +3,6 @@ extern crate flif; use std::fs::File; use std::io::BufReader; -use flif::components::Transformation; use flif::Decoder; #[test] @@ -12,8 +11,11 @@ fn invalid_tree() { let image = Decoder::new(file).unwrap(); let info = image.info(); - let expected = vec![Transformation::ChannelCompact]; - assert_eq!(expected, info.second_header.transformations); + let expected = "Channel Compact"; + assert_eq!( + expected, + format!("{}", info.second_header.transformations.last) + ); } #[test] @@ -22,6 +24,9 @@ fn invalid_transform() { let image = Decoder::new(file).unwrap(); let info = image.info(); - let expected = vec![Transformation::ChannelCompact]; - assert_eq!(expected, info.second_header.transformations); + let expected = "Channel Compact"; + assert_eq!( + expected, + format!("{}", info.second_header.transformations.last) + ); } diff --git a/flif/tests/png_equality.rs b/flif/tests/png_equality.rs index 427c9dc..1ca7992 100644 --- a/flif/tests/png_equality.rs +++ b/flif/tests/png_equality.rs @@ -82,7 +82,7 @@ fn road2() { let file = BufReader::new(File::open("../resources/road2.flif").unwrap()); let image = Flif::decode(file).unwrap(); - let data = image.get_raw_pixels(); + let data = image.raw(); assert_eq!(buf[..4], data[..4]); }