Skip to content

Commit

Permalink
Non-edge pixels optimization (TeXitoi#24)
Browse files Browse the repository at this point in the history
* edge/core pixels optimization

* added debug_asserts
  • Loading branch information
newpavlov authored and ZoeyR committed Jun 9, 2018
1 parent 9128c10 commit 7c9811a
Show file tree
Hide file tree
Showing 5 changed files with 325 additions and 176 deletions.
34 changes: 27 additions & 7 deletions flif/src/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use super::{Flif, FlifInfo, Metadata, DecodingImage, PixelVicinity};
use super::{Flif, FlifInfo, Metadata};
use std::io::Read;
use components::header::{BytesPerChannel, Header, SecondHeader};
use numbers::chances::UpdateTable;
use error::*;
use numbers::rac::Rac;
use numbers::median3;
use maniac::{ManiacTree, build_pvec};
use maniac::{ManiacTree, core_pvec, edge_pvec};
use colors::{Channel, ChannelSet};
use decoding_image::{DecodingImage, CorePixelVicinity, EdgePixelVicinity};
use Limits;

pub struct Decoder<R: Read> {
Expand Down Expand Up @@ -125,14 +126,25 @@ fn non_interlaced_pixels<R: Read>(
for c in CHANNEL_ORDER.iter()
.filter(|c| info.header.channels.contains_channel(**c)).cloned()
{
image.channel_pass(c, |pix_vic| {
let guess = make_guess(info, &pix_vic);
image.channel_pass(c, maniac, rac, |pix_vic, maniac, rac| {
let guess = make_edge_guess(info, &pix_vic);
let range = info.transform.crange(c, &pix_vic.pixel);
let snap = info.transform.snap(c, &pix_vic.pixel, guess);
let pvals = build_pvec(snap, &pix_vic);
let pvec = edge_pvec(snap, &pix_vic);

if let Some(ref mut maniac) = maniac[c] {
maniac.process(rac, &pvals, snap, range.min, range.max)
maniac.process(rac, &pvec, snap, range.min, range.max)
} else {
Ok(range.min)
}
}, |pix_vic, maniac, rac| {
let guess = make_core_guess(&pix_vic);
let range = info.transform.crange(c, &pix_vic.pixel);
let snap = info.transform.snap(c, &pix_vic.pixel, guess);
let pvec = core_pvec(snap, &pix_vic);

if let Some(ref mut maniac) = maniac[c] {
maniac.process(rac, &pvec, snap, range.min, range.max)
} else {
Ok(range.min)
}
Expand All @@ -144,7 +156,15 @@ fn non_interlaced_pixels<R: Read>(
Ok(image)
}

fn make_guess(info: &FlifInfo, pix_vic: &PixelVicinity) -> i16 {
fn make_core_guess(pix_vic: &CorePixelVicinity) -> i16 {
let left = pix_vic.left;
let top = pix_vic.top;
let top_left = pix_vic.top_left;

median3(left + top - top_left, left, top)
}

fn make_edge_guess(info: &FlifInfo, pix_vic: &EdgePixelVicinity) -> i16 {
let transformation = &info.transform;
let chan = pix_vic.chan;
let left = if let Some(val) = pix_vic.left {
Expand Down
189 changes: 189 additions & 0 deletions flif/src/decoding_image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
use std::io::Read;

use components::transformations::Transform;
use numbers::rac::Rac;
use colors::{Channel, ChannelSet, ColorSpace, ColorValue, Pixel};
use maniac::ManiacTree;
pub use error::{Error, Result};
use FlifInfo;

pub use decoder::Decoder;

pub(crate) struct DecodingImage {
height: usize,
width: usize,
channels: ColorSpace,
data: Vec<Pixel>,
}

#[derive(Debug)]
pub(crate) struct EdgePixelVicinity {
pub pixel: Pixel,
pub chan: Channel,
pub is_rgba: bool,

pub left: Option<ColorValue>,
pub left2: Option<ColorValue>,
pub top: Option<ColorValue>,
pub top2: Option<ColorValue>,
pub top_left: Option<ColorValue>,
pub top_right: Option<ColorValue>,
}

#[derive(Debug)]
pub(crate) struct CorePixelVicinity {
pub pixel: Pixel,
pub chan: Channel,
pub is_rgba: bool,

pub left: ColorValue,
pub left2: ColorValue,
pub top: ColorValue,
pub top2: ColorValue,
pub top_left: ColorValue,
pub top_right: ColorValue,
}

type Maniac<'a> = ChannelSet<Option<ManiacTree<'a>>>;

// safety criterias defined by `debug_assert`s
impl DecodingImage {
pub fn new(info: &FlifInfo) -> DecodingImage {
DecodingImage {
height: info.header.height,
width: info.header.width,
channels: info.header.channels,
data: vec![Pixel::default(); info.header.height * info.header.width],
}
}

fn get_idx(&self, x: usize, y: usize) -> usize {
(self.width * y) + x
}

pub fn get_data(&self) -> &[Pixel] {
&self.data
}

unsafe fn get_val(&self, x: usize, y: usize, chan: Channel) -> ColorValue {
debug_assert!(x < self.width && y < self.height &&
self.data.len() == self.width * self.height);
self.data.get_unchecked(self.get_idx(x, y))[chan]
}

unsafe fn get_edge_vicinity(&self, x: usize, y: usize, chan: Channel)
-> EdgePixelVicinity
{
debug_assert!(x < self.width && y < self.height &&
self.data.len() == self.width * self.height);
EdgePixelVicinity {
pixel: *self.data.get_unchecked((self.width * y) + x),
is_rgba: self.channels == ColorSpace::RGBA,
chan,
top: if y != 0 { Some(self.get_val(x, y - 1, chan)) } else { None },
left: if x != 0 { Some(self.get_val(x - 1, y, chan)) } else { None },
left2: if x > 1 { Some(self.get_val(x - 2, y, chan)) } else { None },
top2: if y > 1 { Some(self.get_val(x, y - 2, chan)) } else { None },
top_left: if x != 0 && y != 0 {
Some(self.get_val(x - 1, y - 1, chan))
} else {
None
},
top_right: if y != 0 && x + 1 < self.width {
Some(self.get_val(x + 1, y - 1, chan))
} else {
None
},
}
}

unsafe fn get_core_vicinity(&self, x: usize, y: usize, chan: Channel)
-> CorePixelVicinity
{
debug_assert!(x < self.width - 1 && y < self.height &&
x > 1 && y > 1 && self.data.len() == self.width * self.height);
CorePixelVicinity {
pixel: *self.data.get_unchecked((self.width * y) + x),
chan,
is_rgba: self.channels == ColorSpace::RGBA,
top: self.get_val(x, y - 1, chan),
left: self.get_val(x - 1, y, chan),
left2: self.get_val(x - 2, y, chan),
top2: self.get_val(x, y - 2, chan),
top_left: self.get_val(x - 1, y - 1, chan),
top_right: self.get_val(x + 1, y - 1, chan),
}
}

unsafe fn process_edge_pixel<E, R: Read>(
&mut self, x: usize, y: usize, chan: Channel,
maniac: &mut Maniac, rac: &mut Rac<R>,
mut edge_f: E
)-> Result<()>
where E: FnMut(EdgePixelVicinity, &mut Maniac, &mut Rac<R>) -> Result<ColorValue>
{
debug_assert!(x < self.width && y < self.height &&
self.data.len() == self.width * self.height);
let pix_vic = self.get_edge_vicinity(x, y, chan);
let val = edge_f(pix_vic, maniac, rac)?;
let idx = self.get_idx(x, y);
self.data.get_unchecked_mut(idx)[chan] = val;
Ok(())
}

// iterate over all image pixels and call closure for them without any
// bound checks
pub fn channel_pass<E, F, R: Read>(
&mut self, chan: Channel, maniac: &mut Maniac, rac: &mut Rac<R>,
mut edge_f: E, mut core_f: F,
) -> Result<()>
where E: FnMut(EdgePixelVicinity, &mut Maniac, &mut Rac<R>) -> Result<ColorValue>,
F: FnMut(CorePixelVicinity, &mut Maniac, &mut Rac<R>) -> Result<ColorValue>,
{
let width = self.width;
let height = self.height;
debug_assert!(self.data.len() == width * height);
// special case for small images
if width <= 3 || height <= 2 {
for y in 0..height {
for x in 0..width {
unsafe {
self.process_edge_pixel(x, y, chan, maniac, rac, &mut edge_f)?
}
}
}
return Ok(());
}
// process first two rows
for y in 0..2 {
for x in 0..width {
unsafe {
self.process_edge_pixel(x, y, chan, maniac, rac, &mut edge_f)?;
}
}
}
// main loop
for y in 2..height {
// safe because we are sure that x and y inside the image
unsafe {
self.process_edge_pixel(0, y, chan, maniac, rac, &mut edge_f)?;
self.process_edge_pixel(1, y, chan, maniac, rac, &mut edge_f)?;
let end = width - 1;
for x in 2..end {
let pix_vic = self.get_core_vicinity(x, y, chan);
let val = core_f(pix_vic, maniac, rac)?;
let idx = self.get_idx(x, y);
self.data.get_unchecked_mut(idx)[chan] = val;
}
self.process_edge_pixel(end, y, chan, maniac, rac, &mut edge_f)?;
}
}
Ok(())
}

pub fn undo_transform(&mut self, transform: &Transform) {
for vals in self.data.iter_mut() {
transform.undo(vals);
}
}
}
Loading

0 comments on commit 7c9811a

Please sign in to comment.