<commit_before>//! Image Processing Functions
use std::cmp;

use image::{GenericImage, GenericImageView, SubImage};

use buffer::Pixel;

pub use self::sample::FilterType;

pub use self::sample::FilterType::{CatmullRom, Gaussian, Lanczos3, Nearest, Triangle};

/// Affine transformations
pub use self::affine::{flip_horizontal, flip_vertical, rotate180, rotate270, rotate90};

/// Image sampling
pub use self::sample::{blur, filter3x3, resize, thumbnail, unsharpen};

/// Color operations
pub use self::colorops::{brighten, contrast, dither, grayscale, huerotate, index_colors, invert,
                         BiLevel, ColorMap};

mod affine;
// Public only because of Rust bug:
// https://github.com/rust-lang/rust/issues/18241
pub mod colorops;
mod sample;

/// Return a mutable view into an image
pub fn crop<I: GenericImageView>(
    image: &mut I,
    x: u32,
    y: u32,
    width: u32,
    height: u32,
) -> SubImage<&mut I> {
    let (iwidth, iheight) = image.dimensions();

    let x = cmp::min(x, iwidth);
    let y = cmp::min(y, iheight);

    let height = cmp::min(height, iheight - y);
    let width = cmp::min(width, iwidth - x);

    SubImage::new(image, x, y, width, height)
}

/// Calculate the region that can be copied from top to bottom.
///
/// Given image size of bottom and top image, and a point at which we want to place the top image
/// onto the bottom image, how large can we be? Have to wary of the following issues:
/// * Top might be larger than bottom
/// * Overflows in the computation
/// * Coordinates could be completely out of bounds
///
/// The main idea is to make use of inequalities provided by the nature of `saturing_add` and
/// `saturating_sub`. These intrinsically validate that all resulting coordinates will be in bounds
/// for both images.
///
/// We want that all these coordinate accesses are safe:
/// 1. `bottom.get_pixel(x + [0..x_range), y + [0..y_range))`
/// 2. `top.get_pixel([0..x_range), [0..y_range))`
///
/// Proof that the function provides the necessary bounds for width. Note that all unaugmented math
/// operations are to be read in standard arithmetic, not integer arithmetic. Since no direct
/// integer arithmetic occurs in the implementation, this is unambiguous.
///
/// ```text
/// Three short notes/lemmata:
/// - Iff `(a - b) <= 0` then `a.saturating_sub(b) = 0`
/// - Iff `(a - b) >= 0` then `a.saturating_sub(b) = a - b`
/// - If  `a <= c` then `a.saturating_sub(b) <= c.saturating_sub(b)`
///
/// 1.1 We show that if `bottom_width <= x`, then `x_range = 0` therefore `x + [0..x_range)` is empty.
///
/// x_range
///  = (top_width.saturating_add(x).min(bottom_width)).saturating_sub(x)
/// <= bottom_width.saturating_sub(x)
///
/// bottom_width <= x
/// <==> bottom_width - x <= 0
/// <==> bottom_width.saturating_sub(x) = 0
///  ==> x_range <= 0
///  ==> x_range  = 0
///
/// 1.2 If `x < bottom_width` then `x + x_range < bottom_width`
///
/// x + x_range
/// <= x + bottom_width.saturating_sub(x)
///  = x + (bottom_width - x)
///  = bottom_width
///
/// 2. We show that `x_range <= top_width`
///
/// x_range
///  = (top_width.saturating_add(x).min(bottom_width)).saturating_sub(x)
/// <= top_width.saturating_add(x).saturating_sub(x)
/// <= (top_wdith + x).saturating_sub(x)
///  = top_width (due to `top_width >= 0` and `x >= 0`)
/// ```
///
/// Proof is the same for height.
pub fn overlay_bounds(
    (bottom_width, bottom_height): (u32, u32),
    (top_width, top_height): (u32, u32),
    x: u32,
    y: u32
)
    -> (u32, u32)
{
    let x_range = top_width.saturating_add(x) // Calculate max coordinate
        .min(bottom_width) // Restrict to lower width
        .saturating_sub(x); // Determinate length from start `x`
    let y_range = top_height.saturating_add(y)
        .min(bottom_height)
        .saturating_sub(y);
    (x_range, y_range)
}

/// Overlay an image at a given coordinate (x, y)
pub fn overlay<I: GenericImage>(bottom: &mut I, top: &I, x: u32, y: u32) {
    let bottom_dims = bottom.dimensions();
    let top_dims = top.dimensions();

    // Crop our top image if we're going out of bounds
    let (range_width, range_height) = overlay_bounds(bottom_dims, top_dims, x, y);

    for top_y in 0..range_height {
        for top_x in 0..range_width {
            let p = top.get_pixel(top_x, top_y);
            let mut bottom_pixel = bottom.get_pixel(x + top_x, y + top_y);
            bottom_pixel.blend(&p);

            bottom.put_pixel(x + top_x, y + top_y, bottom_pixel);
        }
    }
}

/// Replace the contents of an image at a given coordinate (x, y)
pub fn replace<I: GenericImage>(bottom: &mut I, top: &I, x: u32, y: u32) {
    let bottom_dims = bottom.dimensions();
    let top_dims = top.dimensions();

    // Crop our top image if we're going out of bounds
    let (range_width, range_height) = overlay_bounds(bottom_dims, top_dims, x, y);

    for top_y in 0..range_height {
        for top_x in 0..range_width {
            let p = top.get_pixel(top_x, top_y);
            bottom.put_pixel(x + top_x, y + top_y, p);
        }
    }
}

#[cfg(test)]
mod tests {

    use super::overlay;
    use buffer::ImageBuffer;
    use color::Rgb;

    #[test]
    /// Test that images written into other images works
    fn test_image_in_image() {
        let mut target = ImageBuffer::new(32, 32);
        let source = ImageBuffer::from_pixel(16, 16, Rgb([255u8, 0, 0]));
        overlay(&mut target, &source, 0, 0);
        assert!(*target.get_pixel(0, 0) == Rgb([255u8, 0, 0]));
        assert!(*target.get_pixel(15, 0) == Rgb([255u8, 0, 0]));
        assert!(*target.get_pixel(16, 0) == Rgb([0u8, 0, 0]));
        assert!(*target.get_pixel(0, 15) == Rgb([255u8, 0, 0]));
        assert!(*target.get_pixel(0, 16) == Rgb([0u8, 0, 0]));
    }

    #[test]
    /// Test that images written outside of a frame doesn't blow up
    fn test_image_in_image_outside_of_bounds() {
        let mut target = ImageBuffer::new(32, 32);
        let source = ImageBuffer::from_pixel(32, 32, Rgb([255u8, 0, 0]));
        overlay(&mut target, &source, 1, 1);
        assert!(*target.get_pixel(0, 0) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(1, 1) == Rgb([255u8, 0, 0]));
        assert!(*target.get_pixel(31, 31) == Rgb([255u8, 0, 0]));
    }

    #[test]
    /// Test that images written to coordinates out of the frame doesn't blow up
    /// (issue came up in #848)
    fn test_image_outside_image_no_wrap_around() {
        let mut target = ImageBuffer::new(32, 32);
        let source = ImageBuffer::from_pixel(32, 32, Rgb([255u8, 0, 0]));
        overlay(&mut target, &source, 33, 33);
        assert!(*target.get_pixel(0, 0) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(1, 1) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(31, 31) == Rgb([0, 0, 0]));
    }

    #[test]
    /// Test that images written to coordinates with overflow works
    fn test_image_coordinate_overflow() {
        let mut target = ImageBuffer::new(16, 16);
        let source = ImageBuffer::from_pixel(32, 32, Rgb([255u8, 0, 0]));
        // Overflows to 'sane' coordinates but top is larger than bot.
        overlay(&mut target, &source, u32::max_value() - 31, u32::max_value() - 31);
        assert!(*target.get_pixel(0, 0) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(1, 1) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(15, 15) == Rgb([0, 0, 0]));
    }
}
<commit_msg>allow different image types in overlay and replace<commit_after>//! Image Processing Functions
use std::cmp;

use image::{GenericImage, GenericImageView, SubImage};

use buffer::Pixel;

pub use self::sample::FilterType;

pub use self::sample::FilterType::{CatmullRom, Gaussian, Lanczos3, Nearest, Triangle};

/// Affine transformations
pub use self::affine::{flip_horizontal, flip_vertical, rotate180, rotate270, rotate90};

/// Image sampling
pub use self::sample::{blur, filter3x3, resize, thumbnail, unsharpen};

/// Color operations
pub use self::colorops::{brighten, contrast, dither, grayscale, huerotate, index_colors, invert,
                         BiLevel, ColorMap};

mod affine;
// Public only because of Rust bug:
// https://github.com/rust-lang/rust/issues/18241
pub mod colorops;
mod sample;

/// Return a mutable view into an image
pub fn crop<I: GenericImageView>(
    image: &mut I,
    x: u32,
    y: u32,
    width: u32,
    height: u32,
) -> SubImage<&mut I> {
    let (iwidth, iheight) = image.dimensions();

    let x = cmp::min(x, iwidth);
    let y = cmp::min(y, iheight);

    let height = cmp::min(height, iheight - y);
    let width = cmp::min(width, iwidth - x);

    SubImage::new(image, x, y, width, height)
}

/// Calculate the region that can be copied from top to bottom.
///
/// Given image size of bottom and top image, and a point at which we want to place the top image
/// onto the bottom image, how large can we be? Have to wary of the following issues:
/// * Top might be larger than bottom
/// * Overflows in the computation
/// * Coordinates could be completely out of bounds
///
/// The main idea is to make use of inequalities provided by the nature of `saturing_add` and
/// `saturating_sub`. These intrinsically validate that all resulting coordinates will be in bounds
/// for both images.
///
/// We want that all these coordinate accesses are safe:
/// 1. `bottom.get_pixel(x + [0..x_range), y + [0..y_range))`
/// 2. `top.get_pixel([0..x_range), [0..y_range))`
///
/// Proof that the function provides the necessary bounds for width. Note that all unaugmented math
/// operations are to be read in standard arithmetic, not integer arithmetic. Since no direct
/// integer arithmetic occurs in the implementation, this is unambiguous.
///
/// ```text
/// Three short notes/lemmata:
/// - Iff `(a - b) <= 0` then `a.saturating_sub(b) = 0`
/// - Iff `(a - b) >= 0` then `a.saturating_sub(b) = a - b`
/// - If  `a <= c` then `a.saturating_sub(b) <= c.saturating_sub(b)`
///
/// 1.1 We show that if `bottom_width <= x`, then `x_range = 0` therefore `x + [0..x_range)` is empty.
///
/// x_range
///  = (top_width.saturating_add(x).min(bottom_width)).saturating_sub(x)
/// <= bottom_width.saturating_sub(x)
///
/// bottom_width <= x
/// <==> bottom_width - x <= 0
/// <==> bottom_width.saturating_sub(x) = 0
///  ==> x_range <= 0
///  ==> x_range  = 0
///
/// 1.2 If `x < bottom_width` then `x + x_range < bottom_width`
///
/// x + x_range
/// <= x + bottom_width.saturating_sub(x)
///  = x + (bottom_width - x)
///  = bottom_width
///
/// 2. We show that `x_range <= top_width`
///
/// x_range
///  = (top_width.saturating_add(x).min(bottom_width)).saturating_sub(x)
/// <= top_width.saturating_add(x).saturating_sub(x)
/// <= (top_wdith + x).saturating_sub(x)
///  = top_width (due to `top_width >= 0` and `x >= 0`)
/// ```
///
/// Proof is the same for height.
pub fn overlay_bounds(
    (bottom_width, bottom_height): (u32, u32),
    (top_width, top_height): (u32, u32),
    x: u32,
    y: u32
)
    -> (u32, u32)
{
    let x_range = top_width.saturating_add(x) // Calculate max coordinate
        .min(bottom_width) // Restrict to lower width
        .saturating_sub(x); // Determinate length from start `x`
    let y_range = top_height.saturating_add(y)
        .min(bottom_height)
        .saturating_sub(y);
    (x_range, y_range)
}

/// Overlay an image at a given coordinate (x, y)
pub fn overlay<I, J>(bottom: &mut I, top: &J, x: u32, y: u32)
where
    I: GenericImage,
    J: GenericImageView<Pixel = I::Pixel>,
{
    let bottom_dims = bottom.dimensions();
    let top_dims = top.dimensions();

    // Crop our top image if we're going out of bounds
    let (range_width, range_height) = overlay_bounds(bottom_dims, top_dims, x, y);

    for top_y in 0..range_height {
        for top_x in 0..range_width {
            let p = top.get_pixel(top_x, top_y);
            let mut bottom_pixel = bottom.get_pixel(x + top_x, y + top_y);
            bottom_pixel.blend(&p);

            bottom.put_pixel(x + top_x, y + top_y, bottom_pixel);
        }
    }
}

/// Replace the contents of an image at a given coordinate (x, y)
pub fn replace<I, J>(bottom: &mut I, top: &J, x: u32, y: u32)
where
    I: GenericImage,
    J: GenericImageView<Pixel = I::Pixel>,
{
    let bottom_dims = bottom.dimensions();
    let top_dims = top.dimensions();

    // Crop our top image if we're going out of bounds
    let (range_width, range_height) = overlay_bounds(bottom_dims, top_dims, x, y);

    for top_y in 0..range_height {
        for top_x in 0..range_width {
            let p = top.get_pixel(top_x, top_y);
            bottom.put_pixel(x + top_x, y + top_y, p);
        }
    }
}

#[cfg(test)]
mod tests {

    use super::overlay;
    use buffer::ImageBuffer;
    use color::Rgb;

    #[test]
    /// Test that images written into other images works
    fn test_image_in_image() {
        let mut target = ImageBuffer::new(32, 32);
        let source = ImageBuffer::from_pixel(16, 16, Rgb([255u8, 0, 0]));
        overlay(&mut target, &source, 0, 0);
        assert!(*target.get_pixel(0, 0) == Rgb([255u8, 0, 0]));
        assert!(*target.get_pixel(15, 0) == Rgb([255u8, 0, 0]));
        assert!(*target.get_pixel(16, 0) == Rgb([0u8, 0, 0]));
        assert!(*target.get_pixel(0, 15) == Rgb([255u8, 0, 0]));
        assert!(*target.get_pixel(0, 16) == Rgb([0u8, 0, 0]));
    }

    #[test]
    /// Test that images written outside of a frame doesn't blow up
    fn test_image_in_image_outside_of_bounds() {
        let mut target = ImageBuffer::new(32, 32);
        let source = ImageBuffer::from_pixel(32, 32, Rgb([255u8, 0, 0]));
        overlay(&mut target, &source, 1, 1);
        assert!(*target.get_pixel(0, 0) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(1, 1) == Rgb([255u8, 0, 0]));
        assert!(*target.get_pixel(31, 31) == Rgb([255u8, 0, 0]));
    }

    #[test]
    /// Test that images written to coordinates out of the frame doesn't blow up
    /// (issue came up in #848)
    fn test_image_outside_image_no_wrap_around() {
        let mut target = ImageBuffer::new(32, 32);
        let source = ImageBuffer::from_pixel(32, 32, Rgb([255u8, 0, 0]));
        overlay(&mut target, &source, 33, 33);
        assert!(*target.get_pixel(0, 0) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(1, 1) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(31, 31) == Rgb([0, 0, 0]));
    }

    #[test]
    /// Test that images written to coordinates with overflow works
    fn test_image_coordinate_overflow() {
        let mut target = ImageBuffer::new(16, 16);
        let source = ImageBuffer::from_pixel(32, 32, Rgb([255u8, 0, 0]));
        // Overflows to 'sane' coordinates but top is larger than bot.
        overlay(&mut target, &source, u32::max_value() - 31, u32::max_value() - 31);
        assert!(*target.get_pixel(0, 0) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(1, 1) == Rgb([0, 0, 0]));
        assert!(*target.get_pixel(15, 15) == Rgb([0, 0, 0]));
    }
}
<|endoftext|>


<commit_before>extern crate rayon;
use self::rayon::prelude::*;

pub mod gofloat;
pub mod demosaic;
pub mod level;
pub mod colorspaces;
pub mod curves;
pub mod gamma;
pub mod transform;

use decoders::RawImage;

extern crate time;
extern crate toml;
extern crate serde;
extern crate serde_yaml;
use self::serde::Serialize;

extern crate bincode;
extern crate sha2;
use self::sha2::{Sha256, Digest};

use std::fmt::Debug;

extern crate multicache;
use self::multicache::MultiCache;
use std::sync::Arc;

#[derive(Debug, Clone, PartialEq)]
pub struct OpBuffer {
  pub width: usize,
  pub height: usize,
  pub colors: usize,
  pub data: Vec<f32>,
}

impl OpBuffer {
  pub fn new(width: usize, height: usize, colors: usize) -> OpBuffer {
    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: vec![0.0; width*height*(colors as usize)],
    }
  }

  pub fn mutate_lines<F>(&mut self, closure: &F)
    where F : Fn(&mut [f32], usize)+Sync {

    self.data.par_chunks_mut(self.width*self.colors).enumerate().for_each(|(row, line)| {
      closure(line, row);
    });
  }

  pub fn process_into_new<F>(&self, colors: usize, closure: &F) -> OpBuffer
    where F : Fn(&mut [f32], &[f32])+Sync {

    let mut out = OpBuffer::new(self.width, self.height, colors);
    out.data.par_chunks_mut(out.width*out.colors).enumerate().for_each(|(row, line)| {
      closure(line, &self.data[self.width*self.colors*row..]);
    });
    out
  }

  /// Helper function to allow human readable creation of `OpBuffer` instances
  pub fn from_rgb_str_vec(data: Vec<&str>) -> OpBuffer {
    let width = data.first().expect("Invalid data for rgb helper function").len();
    let height = data.len();
    let colors = 3;

    let mut pixel_data: Vec<f32> = Vec::with_capacity(width * height * colors);
    for row in data {
      for col in row.chars() {
        let (r, g, b) = match col {
            'R' => (1.0, 0.0, 0.0),
            'G' => (0.0, 1.0, 0.0),
            'B' => (0.0, 0.0, 1.0),
            'O' => (1.0, 1.0, 1.0),
            ' ' => (0.0, 0.0, 0.0),
            c @ _ => panic!(format!(
              "Invalid color '{}' sent to rgb expected any of 'RGBO '", c)),
        };

        pixel_data.push(r);
        pixel_data.push(g);
        pixel_data.push(b);
      }
    }

    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: pixel_data,
    }
  }
}

fn do_timing<O, F: FnMut() -> O>(name: &str, mut closure: F) -> O {
  let from_time = time::precise_time_ns();
  let ret = closure();
  let to_time = time::precise_time_ns();
  println!("{} ms for '{}'", (to_time - from_time)/1000000, name);

  ret
}

pub trait ImageOp<'a>: Debug+Serialize {
  fn name(&self) -> &str;
  fn run(&self, pipeline: &mut PipelineGlobals, inid: BufHash, outid: BufHash);
  fn to_settings(&self) -> String {
    serde_yaml::to_string(self).unwrap()
  }
  fn hash(&self, hasher: &mut Sha256) {
    let encoded = self::bincode::serialize(self, self::bincode::Infinite).unwrap();
    hasher.input(&encoded);
  }
}

type BufHash = [u8;32];

#[derive(Debug)]
pub struct PipelineGlobals<'a> {
  cache: MultiCache<BufHash, OpBuffer>,
  maxwidth: usize,
  maxheight: usize,
  linear: bool,
  image: &'a RawImage,
}

#[derive(Debug)]
pub struct PipelineOps {
  gofloat: gofloat::OpGoFloat,
  demosaic: demosaic::OpDemosaic,
  level: level::OpLevel,
  tolab: colorspaces::OpToLab,
  basecurve: curves::OpBaseCurve,
  fromlab: colorspaces::OpFromLab,
  gamma: gamma::OpGamma,
  transform: transform::OpTransform,
}

macro_rules! for_vals {
  ([$($val:expr),*] |$x:pat, $i:ident| $body:expr) => {
    let mut pos = 0;
    $({
      let $x = $val;
      pos += 1;
      let $i = pos-1;
      $body
    })*
  }
}

macro_rules! all_ops {
  ($ops:expr, |$x:pat, $i:ident| $body:expr) => {
    for_vals!([
      $ops.gofloat,
      $ops.demosaic,
      $ops.level,
      $ops.tolab,
      $ops.basecurve,
      $ops.fromlab,
      $ops.gamma,
      $ops.transform
    ] |$x, $i| {
      $body
    });
  }
}

#[derive(Debug)]
pub struct Pipeline<'a> {
  globals: PipelineGlobals<'a>,
  ops: PipelineOps,
}

impl<'a> Pipeline<'a> {
  pub fn new(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> Pipeline {
    // Check if the image's orientation results in a rotation that
    // swaps the maximum width with the maximum height
    let (transpose, ..) = img.orientation.to_flips();
    let (maxwidth, maxheight) = if transpose {
      (maxheight, maxwidth)
    } else {
      (maxwidth, maxheight)
    };

    Pipeline {
      globals: PipelineGlobals {
        cache: MultiCache::new(1),
        maxwidth,
        maxheight,
        linear,
        image: img,
      },
      ops: PipelineOps {
        gofloat: gofloat::OpGoFloat::new(img),
        demosaic: demosaic::OpDemosaic::new(img),
        level: level::OpLevel::new(img),
        tolab: colorspaces::OpToLab::new(img),
        basecurve: curves::OpBaseCurve::new(img),
        fromlab: colorspaces::OpFromLab::new(img),
        gamma: gamma::OpGamma::new(img),
        transform: transform::OpTransform::new(img),
      },
    }
  }

  pub fn run(&mut self) -> Arc<OpBuffer> {
    // Generate all the hashes for the operations
    let mut hasher = Sha256::default();
    let mut ophashes = Vec::new();
    all_ops!(self.ops, |ref op, _i| {
      // Hash the name first as a zero sized struct doesn't actually do any hashing
      hasher.input(op.name().as_bytes());
      op.hash(&mut hasher);
      let mut result = BufHash::default();
      for (i, byte) in hasher.result().into_iter().enumerate() {
        result[i] = byte;
      }
      ophashes.push(result);
    });

    // Do the operations, starting with a dummy buffer id as gofloat doesn't use it
    let mut bufin = BufHash::default();
    all_ops!(self.ops, |ref op, i| {
      let globals = &mut self.globals;
      let hash = ophashes[i];
      do_timing(op.name(), ||op.run(globals, bufin, hash));
      bufin = hash;
    });
    self.globals.cache.get(bufin).unwrap()
  }
}

fn simple_decode_full(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> OpBuffer {
  let buf = {
    let mut pipeline = Pipeline::new(img, maxwidth, maxheight, linear);
    pipeline.run()
  };

  // Since we've kept the pipeline to ourselves unwraping always works
  Arc::try_unwrap(buf).unwrap()
}

pub fn simple_decode(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  simple_decode_full(img, maxwidth, maxheight, false)
}

pub fn simple_decode_linear(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  simple_decode_full(img, maxwidth, maxheight, true)
}
<commit_msg>imageops: centralize the definition of hash to use<commit_after>extern crate rayon;
use self::rayon::prelude::*;

pub mod gofloat;
pub mod demosaic;
pub mod level;
pub mod colorspaces;
pub mod curves;
pub mod gamma;
pub mod transform;

use decoders::RawImage;

extern crate time;
extern crate toml;
extern crate serde;
extern crate serde_yaml;
use self::serde::Serialize;

extern crate bincode;
extern crate sha2;
use self::sha2::Digest;

use std::fmt::Debug;

extern crate multicache;
use self::multicache::MultiCache;
use std::sync::Arc;

#[derive(Debug, Clone, PartialEq)]
pub struct OpBuffer {
  pub width: usize,
  pub height: usize,
  pub colors: usize,
  pub data: Vec<f32>,
}

impl OpBuffer {
  pub fn new(width: usize, height: usize, colors: usize) -> OpBuffer {
    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: vec![0.0; width*height*(colors as usize)],
    }
  }

  pub fn mutate_lines<F>(&mut self, closure: &F)
    where F : Fn(&mut [f32], usize)+Sync {

    self.data.par_chunks_mut(self.width*self.colors).enumerate().for_each(|(row, line)| {
      closure(line, row);
    });
  }

  pub fn process_into_new<F>(&self, colors: usize, closure: &F) -> OpBuffer
    where F : Fn(&mut [f32], &[f32])+Sync {

    let mut out = OpBuffer::new(self.width, self.height, colors);
    out.data.par_chunks_mut(out.width*out.colors).enumerate().for_each(|(row, line)| {
      closure(line, &self.data[self.width*self.colors*row..]);
    });
    out
  }

  /// Helper function to allow human readable creation of `OpBuffer` instances
  pub fn from_rgb_str_vec(data: Vec<&str>) -> OpBuffer {
    let width = data.first().expect("Invalid data for rgb helper function").len();
    let height = data.len();
    let colors = 3;

    let mut pixel_data: Vec<f32> = Vec::with_capacity(width * height * colors);
    for row in data {
      for col in row.chars() {
        let (r, g, b) = match col {
            'R' => (1.0, 0.0, 0.0),
            'G' => (0.0, 1.0, 0.0),
            'B' => (0.0, 0.0, 1.0),
            'O' => (1.0, 1.0, 1.0),
            ' ' => (0.0, 0.0, 0.0),
            c @ _ => panic!(format!(
              "Invalid color '{}' sent to rgb expected any of 'RGBO '", c)),
        };

        pixel_data.push(r);
        pixel_data.push(g);
        pixel_data.push(b);
      }
    }

    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: pixel_data,
    }
  }
}

fn do_timing<O, F: FnMut() -> O>(name: &str, mut closure: F) -> O {
  let from_time = time::precise_time_ns();
  let ret = closure();
  let to_time = time::precise_time_ns();
  println!("{} ms for '{}'", (to_time - from_time)/1000000, name);

  ret
}

// Use SHA256 as the hash for buffers
type BufHasher = sha2::Sha256;
type BufHash = [u8;32];

pub trait ImageOp<'a>: Debug+Serialize {
  fn name(&self) -> &str;
  fn run(&self, pipeline: &mut PipelineGlobals, inid: BufHash, outid: BufHash);
  fn to_settings(&self) -> String {
    serde_yaml::to_string(self).unwrap()
  }
  fn hash(&self, hasher: &mut BufHasher) {
    let encoded = self::bincode::serialize(self, self::bincode::Infinite).unwrap();
    hasher.input(&encoded);
  }
}

#[derive(Debug)]
pub struct PipelineGlobals<'a> {
  cache: MultiCache<BufHash, OpBuffer>,
  maxwidth: usize,
  maxheight: usize,
  linear: bool,
  image: &'a RawImage,
}

#[derive(Debug)]
pub struct PipelineOps {
  gofloat: gofloat::OpGoFloat,
  demosaic: demosaic::OpDemosaic,
  level: level::OpLevel,
  tolab: colorspaces::OpToLab,
  basecurve: curves::OpBaseCurve,
  fromlab: colorspaces::OpFromLab,
  gamma: gamma::OpGamma,
  transform: transform::OpTransform,
}

macro_rules! for_vals {
  ([$($val:expr),*] |$x:pat, $i:ident| $body:expr) => {
    let mut pos = 0;
    $({
      let $x = $val;
      pos += 1;
      let $i = pos-1;
      $body
    })*
  }
}

macro_rules! all_ops {
  ($ops:expr, |$x:pat, $i:ident| $body:expr) => {
    for_vals!([
      $ops.gofloat,
      $ops.demosaic,
      $ops.level,
      $ops.tolab,
      $ops.basecurve,
      $ops.fromlab,
      $ops.gamma,
      $ops.transform
    ] |$x, $i| {
      $body
    });
  }
}

#[derive(Debug)]
pub struct Pipeline<'a> {
  globals: PipelineGlobals<'a>,
  ops: PipelineOps,
}

impl<'a> Pipeline<'a> {
  pub fn new(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> Pipeline {
    // Check if the image's orientation results in a rotation that
    // swaps the maximum width with the maximum height
    let (transpose, ..) = img.orientation.to_flips();
    let (maxwidth, maxheight) = if transpose {
      (maxheight, maxwidth)
    } else {
      (maxwidth, maxheight)
    };

    Pipeline {
      globals: PipelineGlobals {
        cache: MultiCache::new(1),
        maxwidth,
        maxheight,
        linear,
        image: img,
      },
      ops: PipelineOps {
        gofloat: gofloat::OpGoFloat::new(img),
        demosaic: demosaic::OpDemosaic::new(img),
        level: level::OpLevel::new(img),
        tolab: colorspaces::OpToLab::new(img),
        basecurve: curves::OpBaseCurve::new(img),
        fromlab: colorspaces::OpFromLab::new(img),
        gamma: gamma::OpGamma::new(img),
        transform: transform::OpTransform::new(img),
      },
    }
  }

  pub fn run(&mut self) -> Arc<OpBuffer> {
    // Generate all the hashes for the operations
    let mut hasher = BufHasher::default();
    let mut ophashes = Vec::new();
    all_ops!(self.ops, |ref op, _i| {
      // Hash the name first as a zero sized struct doesn't actually do any hashing
      hasher.input(op.name().as_bytes());
      op.hash(&mut hasher);
      let mut result = BufHash::default();
      for (i, byte) in hasher.result().into_iter().enumerate() {
        result[i] = byte;
      }
      ophashes.push(result);
    });

    // Do the operations, starting with a dummy buffer id as gofloat doesn't use it
    let mut bufin = BufHash::default();
    all_ops!(self.ops, |ref op, i| {
      let globals = &mut self.globals;
      let hash = ophashes[i];
      do_timing(op.name(), ||op.run(globals, bufin, hash));
      bufin = hash;
    });
    self.globals.cache.get(bufin).unwrap()
  }
}

fn simple_decode_full(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> OpBuffer {
  let buf = {
    let mut pipeline = Pipeline::new(img, maxwidth, maxheight, linear);
    pipeline.run()
  };

  // Since we've kept the pipeline to ourselves unwraping always works
  Arc::try_unwrap(buf).unwrap()
}

pub fn simple_decode(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  simple_decode_full(img, maxwidth, maxheight, false)
}

pub fn simple_decode_linear(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  simple_decode_full(img, maxwidth, maxheight, true)
}
<|endoftext|>


<commit_before>use crate::file::tempfile;
use std::fs::File;
use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};

/// A wrapper for the two states of a `SpooledTempFile`.
#[derive(Debug)]
pub enum SpooledInner {
    InMemory(Cursor<Vec<u8>>),
    OnDisk(File),
}

/// An object that behaves like a regular temporary file, but keeps data in
/// memory until it reaches a configured size, at which point the data is
/// written to a temporary file on disk, and further operations use the file
/// on disk.
#[derive(Debug)]
pub struct SpooledTempFile {
    max_size: usize,
    inner: SpooledInner,
}

/// Create a new spooled temporary file.
///
/// # Security
///
/// This variant is secure/reliable in the presence of a pathological temporary
/// file cleaner.
///
/// # Resource Leaking
///
/// The temporary file will be automatically removed by the OS when the last
/// handle to it is closed. This doesn't rely on Rust destructors being run, so
/// will (almost) never fail to clean up the temporary file.
///
/// # Examples
///
/// ```
/// use tempfile::spooled_tempfile;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let mut file = spooled_tempfile(15);
///
/// writeln!(file, "short line")?;
/// assert!(!file.is_rolled());
///
/// // as a result of this write call, the size of the data will exceed
/// // `max_size` (15), so it will be written to a temporary file on disk,
/// // and the in-memory buffer will be dropped
/// writeln!(file, "marvin gardens")?;
/// assert!(file.is_rolled());
///
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn spooled_tempfile(max_size: usize) -> SpooledTempFile {
    SpooledTempFile::new(max_size)
}

impl SpooledTempFile {
    pub fn new(max_size: usize) -> SpooledTempFile {
        SpooledTempFile {
            max_size: max_size,
            inner: SpooledInner::InMemory(Cursor::new(Vec::new())),
        }
    }

    /// Returns true if the file has been rolled over to disk.
    pub fn is_rolled(&self) -> bool {
        match self.inner {
            SpooledInner::InMemory(_) => false,
            SpooledInner::OnDisk(_) => true,
        }
    }

    /// Rolls over to a file on disk, regardless of current size. Does nothing
    /// if already rolled over.
    pub fn roll(&mut self) -> io::Result<()> {
        if !self.is_rolled() {
            let mut file = tempfile()?;
            if let SpooledInner::InMemory(ref mut cursor) = self.inner {
                file.write_all(cursor.get_ref())?;
                file.seek(SeekFrom::Start(cursor.position()))?;
            }
            self.inner = SpooledInner::OnDisk(file);
        }
        Ok(())
    }

    pub fn set_len(&mut self, size: u64) -> Result<(), io::Error> {
        if size as usize > self.max_size {
            self.roll()?; // does nothing if already rolled over
        }
        match self.inner {
            SpooledInner::InMemory(ref mut cursor) => {
                cursor.get_mut().resize(size as usize, 0);
                Ok(())
            }
            SpooledInner::OnDisk(ref mut file) => file.set_len(size),
        }
    }

    /// Consumes and returns the inner `SpooledInner` type.
    pub fn into_inner(self) -> SpooledInner {
        self.inner
    }
}

impl Read for SpooledTempFile {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.inner {
            SpooledInner::InMemory(ref mut cursor) => cursor.read(buf),
            SpooledInner::OnDisk(ref mut file) => file.read(buf),
        }
    }
}

impl Write for SpooledTempFile {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        // roll over to file if necessary
        let mut rolling = false;
        if let SpooledInner::InMemory(ref mut cursor) = self.inner {
            rolling = cursor.position() as usize + buf.len() > self.max_size;
        }
        if rolling {
            self.roll()?;
        }

        // write the bytes
        match self.inner {
            SpooledInner::InMemory(ref mut cursor) => cursor.write(buf),
            SpooledInner::OnDisk(ref mut file) => file.write(buf),
        }
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        match self.inner {
            SpooledInner::InMemory(ref mut cursor) => cursor.flush(),
            SpooledInner::OnDisk(ref mut file) => file.flush(),
        }
    }
}

impl Seek for SpooledTempFile {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        match self.inner {
            SpooledInner::InMemory(ref mut cursor) => cursor.seek(pos),
            SpooledInner::OnDisk(ref mut file) => file.seek(pos),
        }
    }
}
<commit_msg>Rename `SpooledInner` to `SpooledData`.<commit_after>use crate::file::tempfile;
use std::fs::File;
use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};

/// A wrapper for the two states of a `SpooledTempFile`.
#[derive(Debug)]
pub enum SpooledData {
    InMemory(Cursor<Vec<u8>>),
    OnDisk(File),
}

/// An object that behaves like a regular temporary file, but keeps data in
/// memory until it reaches a configured size, at which point the data is
/// written to a temporary file on disk, and further operations use the file
/// on disk.
#[derive(Debug)]
pub struct SpooledTempFile {
    max_size: usize,
    inner: SpooledData,
}

/// Create a new spooled temporary file.
///
/// # Security
///
/// This variant is secure/reliable in the presence of a pathological temporary
/// file cleaner.
///
/// # Resource Leaking
///
/// The temporary file will be automatically removed by the OS when the last
/// handle to it is closed. This doesn't rely on Rust destructors being run, so
/// will (almost) never fail to clean up the temporary file.
///
/// # Examples
///
/// ```
/// use tempfile::spooled_tempfile;
/// use std::io::{self, Write};
///
/// # fn main() {
/// #     if let Err(_) = run() {
/// #         ::std::process::exit(1);
/// #     }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let mut file = spooled_tempfile(15);
///
/// writeln!(file, "short line")?;
/// assert!(!file.is_rolled());
///
/// // as a result of this write call, the size of the data will exceed
/// // `max_size` (15), so it will be written to a temporary file on disk,
/// // and the in-memory buffer will be dropped
/// writeln!(file, "marvin gardens")?;
/// assert!(file.is_rolled());
///
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn spooled_tempfile(max_size: usize) -> SpooledTempFile {
    SpooledTempFile::new(max_size)
}

impl SpooledTempFile {
    pub fn new(max_size: usize) -> SpooledTempFile {
        SpooledTempFile {
            max_size: max_size,
            inner: SpooledData::InMemory(Cursor::new(Vec::new())),
        }
    }

    /// Returns true if the file has been rolled over to disk.
    pub fn is_rolled(&self) -> bool {
        match self.inner {
            SpooledData::InMemory(_) => false,
            SpooledData::OnDisk(_) => true,
        }
    }

    /// Rolls over to a file on disk, regardless of current size. Does nothing
    /// if already rolled over.
    pub fn roll(&mut self) -> io::Result<()> {
        if !self.is_rolled() {
            let mut file = tempfile()?;
            if let SpooledData::InMemory(ref mut cursor) = self.inner {
                file.write_all(cursor.get_ref())?;
                file.seek(SeekFrom::Start(cursor.position()))?;
            }
            self.inner = SpooledData::OnDisk(file);
        }
        Ok(())
    }

    pub fn set_len(&mut self, size: u64) -> Result<(), io::Error> {
        if size as usize > self.max_size {
            self.roll()?; // does nothing if already rolled over
        }
        match self.inner {
            SpooledData::InMemory(ref mut cursor) => {
                cursor.get_mut().resize(size as usize, 0);
                Ok(())
            }
            SpooledData::OnDisk(ref mut file) => file.set_len(size),
        }
    }

    /// Consumes and returns the inner `SpooledData` type.
    pub fn into_inner(self) -> SpooledData {
        self.inner
    }
}

impl Read for SpooledTempFile {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.inner {
            SpooledData::InMemory(ref mut cursor) => cursor.read(buf),
            SpooledData::OnDisk(ref mut file) => file.read(buf),
        }
    }
}

impl Write for SpooledTempFile {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        // roll over to file if necessary
        let mut rolling = false;
        if let SpooledData::InMemory(ref mut cursor) = self.inner {
            rolling = cursor.position() as usize + buf.len() > self.max_size;
        }
        if rolling {
            self.roll()?;
        }

        // write the bytes
        match self.inner {
            SpooledData::InMemory(ref mut cursor) => cursor.write(buf),
            SpooledData::OnDisk(ref mut file) => file.write(buf),
        }
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        match self.inner {
            SpooledData::InMemory(ref mut cursor) => cursor.flush(),
            SpooledData::OnDisk(ref mut file) => file.flush(),
        }
    }
}

impl Seek for SpooledTempFile {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        match self.inner {
            SpooledData::InMemory(ref mut cursor) => cursor.seek(pos),
            SpooledData::OnDisk(ref mut file) => file.seek(pos),
        }
    }
}
<|endoftext|>


<commit_before>use rand;
use rand::Rng;

use std::collections::HashMap;

pub trait KeyValueStore: 'static + Send + Sync + Sized {
    fn insert(&mut self, value: String) -> String;
    fn get(&self, key: &str) -> Option<String>;
}

pub struct InMemoryKeyValueStore {
    hashmap: HashMap<String, String>,
}

impl InMemoryKeyValueStore {
    pub fn new() -> Self {
        InMemoryKeyValueStore { hashmap: HashMap::new() }
    }

    fn generate_unused_random_key(hashmap: &HashMap<String, String>) -> String {
        let mut url_key: Option<String> = None;
        while url_key.is_none() {
            let random_key: String = rand::thread_rng().gen_ascii_chars().take(7).collect();
            url_key = if hashmap.contains_key(&random_key) {
                None
            } else {
                Some(random_key)
            }
        }
        url_key.unwrap()
    }
}

impl KeyValueStore for InMemoryKeyValueStore {
    fn insert(&mut self, value: String) -> String {
        let key = Self::generate_unused_random_key(&self.hashmap);
        self.hashmap.insert(key.clone(), value);
        key
    }

    fn get(&self, key: &str) -> Option<String> {
        self.hashmap.get(key).map(|s| s.to_owned())
    }
}
<commit_msg>Implement a persisted key-value store<commit_after>use rand;
use rand::Rng;

use std::collections::HashMap;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Write;

pub trait KeyValueStore: 'static + Send + Sync + Sized {
    fn insert(&mut self, value: String) -> String;
    fn get(&self, key: &str) -> Option<String>;
}

pub struct InMemoryKeyValueStore {
    data: HashMap<String, String>,
}

impl InMemoryKeyValueStore {
    #[cfg(test)]
    pub fn new() -> Self {
        InMemoryKeyValueStore { data: HashMap::new() }
    }

    pub fn with_initial_data(data: HashMap<String, String>) -> Self {
        InMemoryKeyValueStore { data: data }
    }

    fn generate_unused_random_key(hashmap: &HashMap<String, String>) -> String {
        let mut url_key: Option<String> = None;
        while url_key.is_none() {
            let random_key: String = rand::thread_rng().gen_ascii_chars().take(7).collect();
            url_key = if hashmap.contains_key(&random_key) {
                None
            } else {
                Some(random_key)
            }
        }
        url_key.unwrap()
    }
}

impl KeyValueStore for InMemoryKeyValueStore {
    fn insert(&mut self, value: String) -> String {
        let key = Self::generate_unused_random_key(&self.data);
        self.data.insert(key.clone(), value);
        key
    }

    fn get(&self, key: &str) -> Option<String> {
        self.data.get(key).map(|s| s.to_owned())
    }
}

const KEY_VALUE_SEPARATOR: char = ' ';

pub struct PersistedKeyValueStore {
    base: InMemoryKeyValueStore,
    file: File,
}

impl PersistedKeyValueStore {
    pub fn new(filename: String) -> Self {
        let file = OpenOptions::new().read(true).write(true).create(true).open(filename).unwrap();

        let mut file_data: HashMap<String, String> = HashMap::new();
        {
            let buffered_reader = BufReader::new(&file);
            for line in buffered_reader.lines() {
                let line = line.unwrap();
                match line.find(KEY_VALUE_SEPARATOR) {
                    Some(separator_index) => {
                        let key = line[0..separator_index].to_owned();
                        let value = line[separator_index + 1..].to_owned();
                        file_data.insert(key, value);
                    }
                    None => println!("Ignoring line because of invalid format: '{}'", line),
                }
            }
        }

        PersistedKeyValueStore {
            base: InMemoryKeyValueStore::with_initial_data(file_data),
            file: file,
        }
    }
}

impl KeyValueStore for PersistedKeyValueStore {
    fn insert(&mut self, value: String) -> String {
        let key = self.base.insert(value.clone());
        self.file
            .write_all(format!("{}{}{}\n", &key, KEY_VALUE_SEPARATOR, &value).as_bytes())
            .unwrap();
        key
    }

    fn get(&self, key: &str) -> Option<String> {
        self.base.get(key)
    }
}
<|endoftext|>


<commit_before>// Copyright 2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::mem;
use libc::c_void;

use glib::translate::*;
use ffi;
use ffi::enums::{
    Status,
    SurfaceType,
};

#[allow(raw_pointer_derive)]
#[derive(Debug)]
pub struct Surface(*mut ffi::cairo_surface_t);

impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
    type Storage = &'a Surface;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
        Stash(self.0, self)
    }
}

impl FromGlibPtr<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        ffi::cairo_surface_reference(ptr);
        Surface(ptr)
    }

    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        Surface(ptr)
    }
}

impl AsRef<Surface> for Surface {
    fn as_ref(&self) -> &Surface {
        self
    }
}

impl Clone for Surface {
    fn clone(&self) -> Surface {
        unsafe { from_glib_none(self.to_glib_none().0) }
    }
}

impl Drop for Surface {
    fn drop(&mut self) {
        unsafe { ffi::cairo_surface_destroy(self.0); }
    }
}

pub trait SurfaceExt {
    fn flush(&self);
    fn finish(&self);
    fn get_type(&self) -> SurfaceType;
}

impl<O: AsRef<Surface>> SurfaceExt for O {
    fn flush(&self) {
        unsafe { ffi::cairo_surface_flush(self.as_ref().0); }
    }

    fn finish(&self) {
        unsafe { ffi::cairo_surface_finish(self.as_ref().0); }
    }

    fn get_type(&self) -> SurfaceType {
        unsafe { ffi::cairo_surface_get_type(self.as_ref().0) }
    }
}

pub trait SurfacePriv {
    unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status>;
}

impl<O: AsRef<Surface>> SurfacePriv for O {
    unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status> {
        let status = ffi::cairo_surface_set_user_data(self.as_ref().0, mem::transmute(key),
            mem::transmute(data), Some(unbox::<T>));
        match status {
            Status::Success => Ok(()),
            x => Err(x),
        }
    }
}

unsafe extern fn unbox<T>(data: *mut c_void) {
    let data: Box<T> = mem::transmute(data);
    drop(data);
}
<commit_msg>Sunset the raw_pointer_derive lint<commit_after>// Copyright 2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::mem;
use libc::c_void;

use glib::translate::*;
use ffi;
use ffi::enums::{
    Status,
    SurfaceType,
};

#[derive(Debug)]
pub struct Surface(*mut ffi::cairo_surface_t);

impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
    type Storage = &'a Surface;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
        Stash(self.0, self)
    }
}

impl FromGlibPtr<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        ffi::cairo_surface_reference(ptr);
        Surface(ptr)
    }

    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        Surface(ptr)
    }
}

impl AsRef<Surface> for Surface {
    fn as_ref(&self) -> &Surface {
        self
    }
}

impl Clone for Surface {
    fn clone(&self) -> Surface {
        unsafe { from_glib_none(self.to_glib_none().0) }
    }
}

impl Drop for Surface {
    fn drop(&mut self) {
        unsafe { ffi::cairo_surface_destroy(self.0); }
    }
}

pub trait SurfaceExt {
    fn flush(&self);
    fn finish(&self);
    fn get_type(&self) -> SurfaceType;
}

impl<O: AsRef<Surface>> SurfaceExt for O {
    fn flush(&self) {
        unsafe { ffi::cairo_surface_flush(self.as_ref().0); }
    }

    fn finish(&self) {
        unsafe { ffi::cairo_surface_finish(self.as_ref().0); }
    }

    fn get_type(&self) -> SurfaceType {
        unsafe { ffi::cairo_surface_get_type(self.as_ref().0) }
    }
}

pub trait SurfacePriv {
    unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status>;
}

impl<O: AsRef<Surface>> SurfacePriv for O {
    unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status> {
        let status = ffi::cairo_surface_set_user_data(self.as_ref().0, mem::transmute(key),
            mem::transmute(data), Some(unbox::<T>));
        match status {
            Status::Success => Ok(()),
            x => Err(x),
        }
    }
}

unsafe extern fn unbox<T>(data: *mut c_void) {
    let data: Box<T> = mem::transmute(data);
    drop(data);
}
<|endoftext|>


<commit_before>use super::point::Point;

#[derive(Debug)]
pub struct Polygon {
   pub points: Vec<Point>
}

impl Polygon {
   pub fn new(points: Vec<Point>) -> Self {
      Polygon {
         points: points
      }
   }
}
<commit_msg>Experimental polygon Edge struct<commit_after>use std::cmp::Ordering;

use super::point::Point;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Polygon {
   pub points: Vec<Point>
}

impl Polygon {
   pub fn new(points: Vec<Point>) -> Self {
      Polygon {
         points: points
      }
   }
}

#[derive(Debug, PartialEq, Eq, Ord, Clone, Copy)]
pub struct Edge {
   pub p1: Point,
   pub p2: Point,
   pub dx: i64,
   pub dy: i64,
}

impl Edge {
   fn new(a1: &Point, a2: &Point) -> Self {
      let (p1, p2) = if a1 < a2 {
         (a1, a2)
      } else {
         (a2, a1)
      };

      Edge {
         p1: *p1,
         p2: *p2,
         dx: p2.x - p1.x,
         dy: p2.y - p1.y,
      }
   }
}

impl PartialOrd for Edge {
   fn partial_cmp(&self, other: &Edge) -> Option<Ordering> {
      Some(
         match self.p1.cmp(&other.p1) {
            Ordering::Greater => {
               Ordering::Greater
            },
            Ordering::Less => {
               Ordering::Less
            },
            Ordering::Equal => {
               if self.dy == 0 {
                  if other.dy == 0 {
                     self.dx.cmp(&other.dx)
                  } else {
                     if self.dx > 0 {
                        Ordering::Greater
                     } else {
                        Ordering::Less
                     }
                  }
               } else if other.dy == 0 {
                  if other.dx > 0 {
                     Ordering::Less
                  } else {
                     Ordering::Greater
                  }
               } else {
                  match (self.dx * other.dy).cmp(&(self.dy * other.dx)) {
                     Ordering::Greater => {
                        Ordering::Greater
                     },
                     Ordering::Less => {
                        Ordering::Less
                     },
                     Ordering::Equal => {
                        self.p2.cmp(&other.p2)
                     }
                  }
               }
            }
         }
      )
   }
}

fn cmp_edges_by_y(first: &Edge, second: &Edge) -> Ordering {
   first.p1.y.cmp(&second.p1.y)
}

pub fn to_scanline_edges(points: &Vec<Point>) -> Vec<Edge> {
   let mut edges = Vec::new();

   let mut p1 = points.last().unwrap();

   for p2 in points.iter() {
      let edge = Edge::new(p1, p2);

      if edge.dy != 0 {
         edges.push(edge);
      }

      p1 = p2
   }

   edges.sort_by(cmp_edges_by_y);

   edges
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;
use std::collections::vec_deque;
use std::cmp::min;
use std::hash::Hasher;
use std::io::Result;
pub use std::io::{Read, Write};

const BLOCK_SIZE: usize = 0x10000 - 0x100;

type Block = [u8; BLOCK_SIZE];

pub struct MemStream {
	size: usize,
	blocks: VecDeque<Block>,
}

pub struct Iter<'a> {
	size: usize,
	iter: vec_deque::Iter<'a, Block>,
}

pub struct MemReader<'a> {
	offset: usize,
	iter: Iter<'a>,
	last: Option<&'a [u8]>,
}

impl MemStream {
	pub fn new() -> MemStream {
		MemStream {
			size: 0,
			blocks: VecDeque::new(),
		}
	}

	pub fn size(&self) -> usize {
		return self.size;
	}

	pub fn reader(&self) -> MemReader {
		let mut iter = self.iter();
		let last = iter.next();
		MemReader {
			offset: 0,
			iter: iter,
			last: last,
		}
	}

	pub fn iter(&self) -> Iter {
		Iter {
			size: self.size,
			iter: self.blocks.iter(),
		}
	}

	pub fn copy<W: Write>(&self, writer: &mut W) -> Result<usize> {
		for block in self.iter() {
			try!(writer.write(block));
		}
		Ok(self.size)
	}

	pub fn hash<H: Hasher>(&self, hasher: &mut H) {
		hasher.write_usize(self.size);
		for block in self.iter() {
			hasher.write(block);
		}
	}
}

impl Write for MemStream {
	fn write(&mut self, buf: &[u8]) -> Result<usize> {
		let mut src_offset = 0;
		while src_offset < buf.len() {
			let dst_offset = self.size % BLOCK_SIZE;
			if dst_offset == 0 {
				self.blocks.push_back([0; BLOCK_SIZE]);
			};
			let mut block = self.blocks.back_mut().unwrap();
			let copy_size = min(buf.len() - src_offset, BLOCK_SIZE - dst_offset);
			for i in 0..copy_size {
				block[dst_offset + i] = buf[src_offset + i];
			}
			self.size += copy_size;
			src_offset += copy_size;
		}
		Ok(src_offset)
	}

	fn flush(&mut self) -> Result<()> {
		Ok(())
	}
}

impl<'a> Iterator for Iter<'a> {
	type Item = &'a [u8];

	fn next(&mut self) -> Option<Self::Item> {
		if self.size > 0 {
			let block = self.iter.next().unwrap();
			let size = min(self.size, block.len());
			self.size -= size;
			Some(&block[0..size])
		} else {
			None
		}
	}
}

impl<'a> Read for MemReader<'a> {
	fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
		let mut dst_offset = 0;
		while dst_offset < buf.len() {
			match self.last {
			    Some(block) => {
			    	if self.offset == block.len() {
			    		self.last = self.iter.next();
			    		self.offset = 0;
			    		continue;
			    	}
					let copy_size = min(buf.len() - dst_offset, block.len() - self.offset);
					for i in 0..copy_size {
						buf[dst_offset + i] = block[self.offset + i];
					}
					// add code here
					self.offset += copy_size;
					dst_offset += copy_size;
			    }
			    None => {
			    	break;
			    }
			}
		}
		Ok(dst_offset)
	}
}

#[cfg(test)]
mod test {
	extern crate rand;

	use super::{MemStream, BLOCK_SIZE};
	use std::io::{Read, Write};

	fn check_stream(write_size: usize, read_size: usize) {
		let mut expected: Vec<u8> = Vec::new();
		let mut writer = MemStream::new();
		while expected.len() < BLOCK_SIZE * 3 {
			let mut block = Vec::with_capacity(write_size);
			for _ in 0..write_size {
				block.push(rand::random::<u8>());
			}
			assert_eq!(writer.write(&block).unwrap(), write_size);
			assert_eq!(expected.write(&block).unwrap(), write_size);
		}
		{
			let mut actual = Vec::new();
			writer.copy(&mut actual);
			assert_eq!(expected.len(), actual.len());
			assert_eq!(expected, actual);
		}
		{
			let mut actual = Vec::new();
			let mut reader = writer.reader();
			let mut block = vec!(0; read_size);
			loop {
				let size = reader.read(&mut block).unwrap();
				if size == 0 {
					break;
				}
				actual.write(&block[0..size]).unwrap();
			}
			assert_eq!(expected.len(), actual.len());
			assert_eq!(expected, actual);
		}
	}

	#[test]
	fn test_simple() {
		check_stream(BLOCK_SIZE, BLOCK_SIZE);
	}

	#[test]
	fn test_simple_half() {
		check_stream(BLOCK_SIZE / 2, BLOCK_SIZE / 2);
	}

	#[test]
	fn test_simple_one_and_half() {
		check_stream(BLOCK_SIZE * 3 / 2, BLOCK_SIZE * 3 / 2);
	}

	#[test]
	fn test_simple_1() {
		check_stream(1, 1);
	}

	#[test]
	fn test_simple_7() {
		check_stream(7, 7);
	}
}
<commit_msg>Performance improvement<commit_after>use std::collections::VecDeque;
use std::collections::vec_deque;
use std::cmp::min;
use std::mem;
use std::ptr;
use std::hash::Hasher;
use std::io::Result;
pub use std::io::{Read, Write};

const BLOCK_SIZE: usize = 0x10000 - 0x100;

type Block = [u8; BLOCK_SIZE];

pub struct MemStream {
	size: usize,
	blocks: VecDeque<Block>,
}

pub struct Iter<'a> {
	size: usize,
	iter: vec_deque::Iter<'a, Block>,
}

pub struct MemReader<'a> {
	offset: usize,
	iter: Iter<'a>,
	last: Option<&'a [u8]>,
}

impl MemStream {
	pub fn new() -> MemStream {
		MemStream {
			size: 0,
			blocks: VecDeque::new(),
		}
	}

	pub fn size(&self) -> usize {
		return self.size;
	}

	pub fn reader(&self) -> MemReader {
		let mut iter = self.iter();
		let last = iter.next();
		MemReader {
			offset: 0,
			iter: iter,
			last: last,
		}
	}

	pub fn iter(&self) -> Iter {
		Iter {
			size: self.size,
			iter: self.blocks.iter(),
		}
	}

	pub fn copy<W: Write>(&self, writer: &mut W) -> Result<usize> {
		for block in self.iter() {
			try!(writer.write(block));
		}
		Ok(self.size)
	}

	pub fn hash<H: Hasher>(&self, hasher: &mut H) {
		hasher.write_usize(self.size);
		for block in self.iter() {
			hasher.write(block);
		}
	}
}

fn memcpy(src: &[u8], dst: &mut [u8]) {
	assert!(src.len() == dst.len());
	unsafe {
		ptr::copy_nonoverlapping(&src[0], &mut dst[0], src.len());
	}
}

impl Write for MemStream {
	fn write(&mut self, buf: &[u8]) -> Result<usize> {
		let mut src_offset = 0;
		while src_offset < buf.len() {
			let dst_offset = self.size % BLOCK_SIZE;
			if dst_offset == 0 {
				self.blocks.push_back(unsafe {mem::uninitialized()});
			};
			let mut block = self.blocks.back_mut().unwrap();
			let copy_size = min(buf.len() - src_offset, BLOCK_SIZE - dst_offset);
			memcpy(&buf[src_offset..src_offset + copy_size], &mut block[dst_offset..dst_offset + copy_size]);
			self.size += copy_size;
			src_offset += copy_size;
		}
		Ok(src_offset)
	}

	fn flush(&mut self) -> Result<()> {
		Ok(())
	}
}

impl<'a> Iterator for Iter<'a> {
	type Item = &'a [u8];

	fn next(&mut self) -> Option<Self::Item> {
		if self.size > 0 {
			let block = self.iter.next().unwrap();
			let size = min(self.size, block.len());
			self.size -= size;
			Some(&block[0..size])
		} else {
			None
		}
	}
}

impl<'a> Read for MemReader<'a> {
	fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
		let mut dst_offset = 0;
		while dst_offset < buf.len() {
			match self.last {
			    Some(block) => {
			    	if self.offset == block.len() {
			    		self.last = self.iter.next();
			    		self.offset = 0;
			    		continue;
			    	}
					let copy_size = min(buf.len() - dst_offset, block.len() - self.offset);
					memcpy(&block[self.offset..self.offset + copy_size], &mut buf[dst_offset..dst_offset + copy_size]);
					// add code here
					self.offset += copy_size;
					dst_offset += copy_size;
			    }
			    None => {
			    	break;
			    }
			}
		}
		Ok(dst_offset)
	}
}

#[cfg(test)]
mod test {
	extern crate rand;

	use super::{MemStream, BLOCK_SIZE};
	use std::io::{Read, Write};

	fn check_stream(write_size: usize, read_size: usize) {
		let mut expected: Vec<u8> = Vec::new();
		let mut writer = MemStream::new();
		while expected.len() < BLOCK_SIZE * 3 {
			let mut block = Vec::with_capacity(write_size);
			for _ in 0..write_size {
				block.push(rand::random::<u8>());
			}
			assert_eq!(writer.write(&block).unwrap(), write_size);
			assert_eq!(expected.write(&block).unwrap(), write_size);
		}
		{
			let mut actual = Vec::new();
			writer.copy(&mut actual).unwrap();
			assert_eq!(expected.len(), actual.len());
			assert_eq!(expected, actual);
		}
		{
			let mut actual = Vec::new();
			let mut reader = writer.reader();
			let mut block = vec!(0; read_size);
			loop {
				let size = reader.read(&mut block).unwrap();
				if size == 0 {
					break;
				}
				actual.write(&block[0..size]).unwrap();
			}
			assert_eq!(expected.len(), actual.len());
			assert_eq!(expected, actual);
		}
	}

	#[test]
	fn test_simple() {
		check_stream(BLOCK_SIZE, BLOCK_SIZE);
	}

	#[test]
	fn test_simple_half() {
		check_stream(BLOCK_SIZE / 2, BLOCK_SIZE / 2);
	}

	#[test]
	fn test_simple_one_and_half() {
		check_stream(BLOCK_SIZE * 3 / 2, BLOCK_SIZE * 3 / 2);
	}

	#[test]
	fn test_simple_1() {
		check_stream(1, 1);
	}

	#[test]
	fn test_simple_7() {
		check_stream(7, 7);
	}
}
<|endoftext|>


<commit_before>use gfx::handle::ShaderResourceView;
use gfx::{texture, Factory, Resources};
use gfx::format::Rgba8;
use cgmath;
use cgmath::{Angle, Deg, Matrix4, Point3, Vector3, Point2, Rad};
use game::constants::{TILE_WIDTH, RESOLUTION_X, RESOLUTION_Y, VIEW_DISTANCE};
use graphics::orientation::Orientation;
use gfx_app::mouse_controls::MouseInputState;
use image;
use shaders::Projection;
use std::io::Cursor;

pub mod camera;
pub mod orientation;

#[derive(Debug)]
pub struct DeltaTime(pub f64);

#[derive(Debug, Clone)]
pub struct Dimensions {
  width: u32,
  height: u32,
}

impl Dimensions {
  pub fn new(_window_width: u32, _window_height: u32) -> Dimensions {
    Dimensions {
      width: RESOLUTION_X,
      height: RESOLUTION_Y,
    }
  }

  pub fn get_view_matrix() -> Matrix4<f32> {
    Matrix4::look_at(
      Point3::new(0.0, 0.0, VIEW_DISTANCE),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    )
  }

  pub fn world_to_projection(&self, input: &camera::CameraInputState) -> Projection {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(0.0, 0.0, input.distance),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );
    let aspect_ratio = self.width as f32 / self.height as f32;
    Projection {
      model: view.into(),
      view: view.into(),
      proj: cgmath::perspective(cgmath::Deg(75.0f32), aspect_ratio, 0.1, 4000.0).into(),
    }
  }
}

pub fn flip_y_axel(point: Point2<f32>) -> Point2<f32> {
  Point2 {
    x: point.x,
    y: RESOLUTION_Y as f32 - point.y
  }
}

pub fn direction(start_point: Point2<f32>, end_point: Point2<f32>) -> i32 {
  let theta= cgmath::Angle::atan2(end_point.y - start_point.y, end_point.x - start_point.x);
  let angle = match theta {
    Deg(i) => i
  };
  let a = angle.floor() as i32;
  if a < 0 { 360 + a } else { a }
}

pub fn direction_movement(direction: u32) -> Point2<f32> {
  println!("Implement direction x,y calculation {}", direction);

  let f_direction = (direction as f32 - 360.0).abs() + 90.0;

  let foo = Point2 {
    x: Angle::sin(Rad(f_direction)),
    y: Angle::cos(Rad(f_direction)),
  };
  print!("movement {:?}", foo);
  foo
}

pub fn get_orientation(mouse_input: &MouseInputState) -> Orientation {
  if let Some(end_point_gl) = mouse_input.left_click_point {
    let start_point = Point2 {
      x: (RESOLUTION_X / 2) as f32,
      y: (RESOLUTION_Y / 2) as f32
    };
    let angle_in_degrees = direction(start_point, flip_y_axel(end_point_gl));

    match angle_in_degrees {
      345 ... 360 | 0 ... 22 => Orientation::Right,
      23 ... 68 => Orientation::UpRight,
      69 ... 114 => Orientation::Up,
      115 ... 160 => Orientation::UpLeft,
      161 ... 206 => Orientation::Left,
      207 ... 252 => Orientation::DownLeft,
      253 ... 298 => Orientation::Down,
      299 ... 344 => Orientation::DownRight,
      _ => unreachable!("Invalid orientation")
    }
  } else {
    Orientation::Right
  }
}

fn is_within_map_borders(point: Point2<f64>) -> bool {
  point.x > 0.0 && point.x < 63.0 && point.y > 0.0 && point.y < 63.0
}
pub fn can_move(screen_pos: [f32; 2]) -> bool {
  let x_coord = f64::from(screen_pos[0]);
  let y_coord = f64::from(screen_pos[1]);
  let point = Point2 {
    x: (x_coord / TILE_WIDTH + y_coord / TILE_WIDTH).round() + 31.0,
    y: (y_coord / TILE_WIDTH - x_coord / TILE_WIDTH).round() + 32.0
  };
  is_within_map_borders(point)
}

pub fn load_texture<R, F>(factory: &mut F, data: &[u8]) -> Result<ShaderResourceView<R, [f32; 4]>, String> where R: Resources, F: Factory<R> {
  let img = image::load(Cursor::new(data), image::PNG).unwrap().to_rgba();
  let (width, height) = img.dimensions();
  let kind = texture::Kind::D2(width as texture::Size, height as texture::Size, texture::AaMode::Single);
  let (_, view) = factory.create_texture_immutable_u8::<Rgba8>(kind, &[&img]).unwrap();
  Ok(view)
}
<commit_msg>Update direction and direction_movement functions<commit_after>use gfx::handle::ShaderResourceView;
use gfx::{texture, Factory, Resources};
use gfx::format::Rgba8;
use cgmath;
use cgmath::{Angle, Deg, Matrix4, Point3, Vector3, Point2};
use game::constants::{TILE_WIDTH, RESOLUTION_X, RESOLUTION_Y, VIEW_DISTANCE};
use graphics::orientation::Orientation;
use gfx_app::mouse_controls::MouseInputState;
use image;
use shaders::Projection;
use std::io::Cursor;

pub mod camera;
pub mod orientation;

#[derive(Debug)]
pub struct DeltaTime(pub f64);

#[derive(Debug, Clone)]
pub struct Dimensions {
  width: u32,
  height: u32,
}

impl Dimensions {
  pub fn new(_window_width: u32, _window_height: u32) -> Dimensions {
    Dimensions {
      width: RESOLUTION_X,
      height: RESOLUTION_Y,
    }
  }

  pub fn get_view_matrix() -> Matrix4<f32> {
    Matrix4::look_at(
      Point3::new(0.0, 0.0, VIEW_DISTANCE),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    )
  }

  pub fn world_to_projection(&self, input: &camera::CameraInputState) -> Projection {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(0.0, 0.0, input.distance),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );
    let aspect_ratio = self.width as f32 / self.height as f32;
    Projection {
      model: view.into(),
      view: view.into(),
      proj: cgmath::perspective(cgmath::Deg(75.0f32), aspect_ratio, 0.1, 4000.0).into(),
    }
  }
}

pub fn flip_y_axel(point: Point2<f32>) -> Point2<f32> {
  Point2 {
    x: point.x,
    y: RESOLUTION_Y as f32 - point.y
  }
}

pub fn direction(start_point: Point2<f32>, end_point: Point2<f32>) -> u32 {
  let theta= cgmath::Angle::atan2(end_point.y - start_point.y, end_point.x - start_point.x);
  let angle = match theta {
    Deg(i) => i
  };
  let a = angle.floor() as i32;
  if a < 0 { (360 + a) as u32 } else { a as u32 }
}

pub fn direction_movement(direction: u32) -> Point2<f32> {
  let f_direction = direction as f32;
  Point2 {
    x: (Angle::cos(Deg(f_direction)) * 100.0).round() / 100.0,
    y: (Angle::sin(Deg(f_direction)) * 100.0).round() / 100.0,
  }
}

pub fn get_orientation(mouse_input: &MouseInputState) -> Orientation {
  if let Some(end_point_gl) = mouse_input.left_click_point {
    let start_point = Point2 {
      x: (RESOLUTION_X / 2) as f32,
      y: (RESOLUTION_Y / 2) as f32
    };
    let angle_in_degrees = direction(start_point, flip_y_axel(end_point_gl));

    match angle_in_degrees {
      345 ... 360 | 0 ... 22 => Orientation::Right,
      23 ... 68 => Orientation::UpRight,
      69 ... 114 => Orientation::Up,
      115 ... 160 => Orientation::UpLeft,
      161 ... 206 => Orientation::Left,
      207 ... 252 => Orientation::DownLeft,
      253 ... 298 => Orientation::Down,
      299 ... 344 => Orientation::DownRight,
      _ => unreachable!("Invalid orientation")
    }
  } else {
    Orientation::Right
  }
}

fn is_within_map_borders(point: Point2<f64>) -> bool {
  point.x > 0.0 && point.x < 63.0 && point.y > 0.0 && point.y < 63.0
}
pub fn can_move(screen_pos: [f32; 2]) -> bool {
  let x_coord = f64::from(screen_pos[0]);
  let y_coord = f64::from(screen_pos[1]);
  let point = Point2 {
    x: (x_coord / TILE_WIDTH + y_coord / TILE_WIDTH).round() + 31.0,
    y: (y_coord / TILE_WIDTH - x_coord / TILE_WIDTH).round() + 32.0
  };
  is_within_map_borders(point)
}

pub fn load_texture<R, F>(factory: &mut F, data: &[u8]) -> Result<ShaderResourceView<R, [f32; 4]>, String> where R: Resources, F: Factory<R> {
  let img = image::load(Cursor::new(data), image::PNG).unwrap().to_rgba();
  let (width, height) = img.dimensions();
  let kind = texture::Kind::D2(width as texture::Size, height as texture::Size, texture::AaMode::Single);
  let (_, view) = factory.create_texture_immutable_u8::<Rgba8>(kind, &[&img]).unwrap();
  Ok(view)
}
<|endoftext|>


<commit_before>pub struct Block<'a> {
  pub is_last: bool,
  pub length: u32,
  pub data: BlockData<'a>,
}

#[derive(Debug, PartialEq, Eq)]
pub enum BlockData<'a> {
  StreamInfo(StreamInfo<'a>),
  Padding(u32),
  Application(Application<'a>),
  SeekTable(Vec<SeekPoint>),
  VorbisComment(VorbisComment<'a>),
  CueSheet(CueSheet<'a>),
  Picture(Picture<'a>),
  Unknown(&'a [u8]),
}

/// Information regarding the entire audio stream
#[derive(Debug, PartialEq, Eq)]
pub struct StreamInfo<'a> {
  pub min_block_size: u16,
  pub max_block_size: u16,
  pub min_frame_size: u32,
  pub max_frame_size: u32,
  pub sample_rate: u32,
  pub channels: u8,
  pub bits_per_sample: u8,
  pub total_samples: u64,
  pub md5_sum: &'a [u8],
}

/// Data used by third-party applications
#[derive(Debug, PartialEq, Eq)]
pub struct Application<'a> {
  pub id: &'a str,
  pub data: &'a [u8],
}

/// Seek, or skip, to a point within the FLAC file
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  pub sample_number: u64,
  pub stream_offset: u64,
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment<'a> {
  pub vendor_string: &'a str,
  pub comments: Vec<&'a str>,
}

/// Stores cue information
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet<'a> {
  pub media_catalog_number: &'a str,
  pub lead_in: u64,
  pub is_cd: bool,
  pub tracks: Vec<CueSheetTrack<'a>>,
}

#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack<'a> {
  pub offset: u64,
  pub number: u8,
  pub isrc: &'a str,
  pub isnt_audio: bool,
  pub is_pre_emphasis: bool,
  pub indices: Vec<CueSheetTrackIndex>,
}

#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  pub offset: u64,
  pub number: u8,
}

/// Stores pictures associated with a FLAC file
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture<'a> {
  pub picture_type: PictureType,
  pub mime_type: &'a str,
  pub description: &'a str,
  pub width: u32,
  pub height: u32,
  pub depth: u32,
  pub colors: u32,
  pub data: &'a [u8],
}

#[derive(Debug, PartialEq, Eq)]
pub enum PictureType {
  Other,
  FileIconStandard,
  FileIcon,
  FrontCover,
  BackCover,
  LeafletPage,
  Media,
  LeadArtist,
  Artist,
  Conductor,
  Band,
  Composer,
  Lyricist,
  RecordingLocation,
  DuringRecording,
  DuringPerformace,
  VideoScreenCapture,
  Fish,
  Illustration,
  BandLogoType,
  PublisherLogoType,
}
<commit_msg>Get rid of lifetime annotation on types<commit_after>pub struct Block {
  pub is_last: bool,
  pub length: u32,
  pub data: BlockData,
}

#[derive(Debug, PartialEq, Eq)]
pub enum BlockData {
  StreamInfo(StreamInfo),
  Padding(u32),
  Application(Application),
  SeekTable(Vec<SeekPoint>),
  VorbisComment(VorbisComment),
  CueSheet(CueSheet),
  Picture(Picture),
  Unknown(Vec<u8>),
}

/// Information regarding the entire audio stream
#[derive(Debug, PartialEq, Eq)]
pub struct StreamInfo {
  pub min_block_size: u16,
  pub max_block_size: u16,
  pub min_frame_size: u32,
  pub max_frame_size: u32,
  pub sample_rate: u32,
  pub channels: u8,
  pub bits_per_sample: u8,
  pub total_samples: u64,
  pub md5_sum: Vec<u8>,
}

/// Data used by third-party applications
#[derive(Debug, PartialEq, Eq)]
pub struct Application {
  pub id: String,
  pub data: Vec<u8>,
}

/// Seek, or skip, to a point within the FLAC file
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  pub sample_number: u64,
  pub stream_offset: u64,
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment {
  pub vendor_string: String,
  pub comments: Vec<String>,
}

/// Stores cue information
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet {
  pub media_catalog_number: String,
  pub lead_in: u64,
  pub is_cd: bool,
  pub tracks: Vec<CueSheetTrack>,
}

#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack {
  pub offset: u64,
  pub number: u8,
  pub isrc: String,
  pub isnt_audio: bool,
  pub is_pre_emphasis: bool,
  pub indices: Vec<CueSheetTrackIndex>,
}

#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  pub offset: u64,
  pub number: u8,
}

/// Stores pictures associated with a FLAC file
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture {
  pub picture_type: PictureType,
  pub mime_type: String,
  pub description: String,
  pub width: u32,
  pub height: u32,
  pub depth: u32,
  pub colors: u32,
  pub data: Vec<u8>,
}

#[derive(Debug, PartialEq, Eq)]
pub enum PictureType {
  Other,
  FileIconStandard,
  FileIcon,
  FrontCover,
  BackCover,
  LeafletPage,
  Media,
  LeadArtist,
  Artist,
  Conductor,
  Band,
  Composer,
  Lyricist,
  RecordingLocation,
  DuringRecording,
  DuringPerformace,
  VideoScreenCapture,
  Fish,
  Illustration,
  BandLogoType,
  PublisherLogoType,
}
<|endoftext|>


<commit_before>use std::ptr;
use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use image_data::{Image2d, TextelFormat, ImageFormat};
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub unsafe fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        let mut id : GLuint =  0;

        gl::GenTextures(1, &mut id as *mut GLuint);
        dbg_gl_sanity_check! {
            GLError::InvalidValue => "`n` is negative",
            _ => "Unknown error"
        }

        Texture {
            gl_id: id,
            phantom: PhantomData
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tx2dImageTarget {
    Texture2d = gl::TEXTURE_2D as isize
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextureBindingTarget {
    Texture2d = gl::TEXTURE_2D as isize,
    TextureCubeMap = gl::TEXTURE_CUBE_MAP as isize
}

impl TextureBindingTarget {
    pub fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

// HACK: Done to allow glitter::TEXTURE_2D as both a
//       TextureBindingTarget and Tx2dImageTarget
pub struct VariantTexture2d;

impl From<VariantTexture2d> for TextureBindingTarget {
    fn from(_: VariantTexture2d) -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

impl From<VariantTexture2d> for Tx2dImageTarget {
    fn from(_: VariantTexture2d) -> Tx2dImageTarget {
        Tx2dImageTarget::Texture2d
    }
}

impl ImageTargetType for VariantTexture2d {
    fn gl_enum(&self) -> GLenum {
        gl::TEXTURE_2D
    }
}

pub const TEXTURE_CUBE_MAP : TextureBindingTarget =
    TextureBindingTarget::TextureCubeMap;
pub const TEXTURE_2D : VariantTexture2d = VariantTexture2d;

// TODO: Use type refinements someday...
#[derive(Debug, Clone, Copy)]
pub enum TextureFilter {
    Nearest,
    Linear
}

#[derive(Debug, Clone, Copy)]
pub enum TextureMipmapFilter {
    Filter(TextureFilter),
    MipmapFilter { criterion: TextureFilter, mipmap: TextureFilter }
}

pub const NEAREST : TextureFilter = TextureFilter::Nearest;
pub const LINEAR : TextureFilter = TextureFilter::Linear;
pub const NEAREST_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Nearest
    };
pub const LINEAR_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Nearest
    };
pub const NEAREST_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Linear
    };
pub const LINEAR_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Linear
    };

#[allow(dead_code)]
impl TextureFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(self::NEAREST) },
            gl::LINEAR => { Ok(self::LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            self::NEAREST => gl::NEAREST,
            self::LINEAR => gl::LINEAR
        }
    }
}

#[allow(dead_code)]
impl TextureMipmapFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(TextureMipmapFilter::Filter(self::NEAREST)) },
            gl::LINEAR => { Ok(TextureMipmapFilter::Filter(self::LINEAR)) },
            gl::NEAREST_MIPMAP_NEAREST => { Ok(self::NEAREST_MIPMAP_NEAREST) },
            gl::LINEAR_MIPMAP_NEAREST => { Ok(self::LINEAR_MIPMAP_NEAREST) },
            gl::NEAREST_MIPMAP_LINEAR => { Ok(self::NEAREST_MIPMAP_LINEAR) },
            gl::LINEAR_MIPMAP_LINEAR => { Ok(self::LINEAR_MIPMAP_LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            TextureMipmapFilter::Filter(self::LINEAR) => { gl::LINEAR },
            TextureMipmapFilter::Filter(self::NEAREST) => { gl::NEAREST },
            self::NEAREST_MIPMAP_NEAREST => { gl::NEAREST_MIPMAP_NEAREST },
            self::LINEAR_MIPMAP_NEAREST => { gl::LINEAR_MIPMAP_NEAREST },
            self::NEAREST_MIPMAP_LINEAR => { gl::NEAREST_MIPMAP_LINEAR },
            self::LINEAR_MIPMAP_LINEAR => { gl::LINEAR_MIPMAP_LINEAR }
        }
    }
}

impl From<TextureFilter> for TextureMipmapFilter {
    fn from(filter: TextureFilter) -> TextureMipmapFilter {
        TextureMipmapFilter::Filter(filter)
    }
}

gl_enum! {
    pub gl_enum TextureWrapMode {
        ClampToEdge as CLAMP_TO_EDGE = gl::CLAMP_TO_EDGE,
        MirroredRepeat as MIRRORED_REPEAT = gl::MIRRORED_REPEAT,
        Repeat as REPEAT = gl::REPEAT
    }
}

unsafe fn _tex_parameter_iv(target: TextureBindingTarget,
                            pname: GLenum,
                            params: *const GLint)
{
    gl::TexParameteriv(target.gl_enum(), pname, params);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target` or `pname` is not an accepted defined value, or `params` should have defined a symbolic constant and does not",
        _ => "Unknown error"
    }
}

unsafe fn _tex_image_2d<T: ImageTargetType>(target: T,
                                            level: u32,
                                            internal_format: TextelFormat,
                                            width: u32,
                                            height: u32,
                                            border: u32,
                                            format: ImageFormat,
                                            image_ptr: *const u8) {
    debug_assert!(internal_format == format.textel_format);
    gl::TexImage2D(target.gl_enum(),
                   level as GLint,
                   internal_format.gl_enum() as GLint,
                   width as GLint,
                   height as GLint,
                   border as GLint,
                   format.textel_format.gl_enum(),
                   format.textel_type.gl_enum(),
                   image_ptr as *const GLvoid);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target`, `format`, or `type` is not an accepted value",
        GLError::InvalidValue => "`target`, `level`, `internalformat`, `width`, `height`, or `border` is an invalid value",
        GLError::InvalidOperation => "`format` conflicts with either `internalformat` or `type`",
        _ => "Unknown error"
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }

    fn set_min_filter<F: Into<TextureMipmapFilter>>(&mut self, filter: F) {
        let gl_int = filter.into().gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MIN_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_mag_filter(&mut self, filter: TextureFilter) {
        let gl_int = filter.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MAG_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_s(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_S,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_t(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_T,
                              &gl_int as *const GLint);
        }
    }

    fn image_2d<T, I: ?Sized>(&mut self,
                              target: T,
                              level: u32,
                              img: &I)
        where T: Into<<Self::TextureType as TextureType>::ImageTargetType>,
              I: Image2d
    {
        unsafe {
            _tex_image_2d(target.into(),
                          level,
                          img.format().textel_format,
                          img.width() as u32,
                          img.height() as u32,
                          0,
                          img.format(),
                          img.textel_bytes().as_ptr());
        }
    }

    fn image_2d_empty<T: ImageTargetType>(&mut self,
                                          target: T,
                                          level: u32,
                                          format: ImageFormat,
                                          width: u32,
                                          height: u32)
    {
        unsafe {
            _tex_image_2d(target,
                          level,
                          format.textel_format,
                          width,
                          height,
                          0,
                          format,
                          ptr::null());
        }
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind(&mut self, texture: &mut Texture2d) -> Texture2dBinding {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind(&mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<commit_msg>Add `Texture2dBuilder`<commit_after>use std::ptr;
use std::marker::PhantomData;
use gl;
use gl::types::*;
use prelude::*;
use context::Context;
use image_data::{Image2d, TextelFormat, ImageFormat};
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

pub struct Texture2dBuilder<'a> {
    gl: &'a mut Context,
    min_filter: Option<TextureMipmapFilter>,
    mag_filter: Option<TextureFilter>,
    wrap_s: Option<TextureWrapMode>,
    wrap_t: Option<TextureWrapMode>,
    image: Option<&'a Image2d>,
    empty_params: Option<(ImageFormat, u32, u32)>
}

impl<'a> Texture2dBuilder<'a> {
    fn new(gl: &'a mut Context) -> Self {
        Texture2dBuilder {
            gl: gl,
            min_filter: None,
            mag_filter: None,
            wrap_s: None,
            wrap_t: None,
            image: None,
            empty_params: None
        }
    }

    pub fn image_2d(mut self, image: &'a Image2d) -> Self {
        self.image = Some(image);
        self
    }

    pub fn empty(mut self, format: ImageFormat, width: u32, height: u32)
        -> Self
    {
        self.empty_params = Some((format, width, height));
        self
    }

    pub fn min_filter<F>(mut self, filter: F) -> Self
        where F: Into<TextureMipmapFilter>
    {
        self.min_filter = Some(filter.into());
        self
    }

    pub fn mag_filter(mut self, filter: TextureFilter) -> Self {
        self.mag_filter = Some(filter);
        self
    }

    pub fn wrap_s(mut self, wrap: TextureWrapMode) -> Self {
        self.wrap_s = Some(wrap);
        self
    }

    pub fn wrap_t(mut self, wrap: TextureWrapMode) -> Self {
        self.wrap_t = Some(wrap);
        self
    }

    pub fn try_unwrap(self) -> Result<Texture2d, GLError> {
        let mut texture = unsafe { self.gl.gen_texture() };

        // TODO: Use macros here
        let mut gl_tex_unit = self.gl.tex_units.0.active();
        let mut gl_tex = gl_tex_unit.texture_2d.bind(&mut texture);

        if let Some(min_filter) = self.min_filter {
            gl_tex.set_min_filter(min_filter);
        }
        if let Some(mag_filter) = self.mag_filter {
            gl_tex.set_mag_filter(mag_filter);
        }
        if let Some(wrap_s) = self.wrap_s {
            gl_tex.set_wrap_s(wrap_s);
        }
        if let Some(wrap_t) = self.wrap_t {
            gl_tex.set_wrap_t(wrap_t);
        }

        if let Some(image) = self.image {
            gl_tex.image_2d(Tx2dImageTarget::Texture2d, 0, image);

            Ok(texture)
        }
        else if let Some((format, width, height)) = self.empty_params {
            gl_tex.image_2d_empty(Tx2dImageTarget::Texture2d,
                                  0,
                                  format,
                                  width,
                                  height);

            Ok(texture)
        }
        else {
            let msg = "Error building texture: neither an image nor a format were provided";
            Err(GLError::Message(msg.to_owned()))
        }
    }

    pub fn unwrap(self) -> Texture2d {
        self.try_unwrap().unwrap()
    }
}

impl Context {
    pub unsafe fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        let mut id : GLuint =  0;

        gl::GenTextures(1, &mut id as *mut GLuint);
        dbg_gl_sanity_check! {
            GLError::InvalidValue => "`n` is negative",
            _ => "Unknown error"
        }

        Texture {
            gl_id: id,
            phantom: PhantomData
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tx2dImageTarget {
    Texture2d = gl::TEXTURE_2D as isize
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextureBindingTarget {
    Texture2d = gl::TEXTURE_2D as isize,
    TextureCubeMap = gl::TEXTURE_CUBE_MAP as isize
}

impl TextureBindingTarget {
    pub fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

// HACK: Done to allow glitter::TEXTURE_2D as both a
//       TextureBindingTarget and Tx2dImageTarget
pub struct VariantTexture2d;

impl From<VariantTexture2d> for TextureBindingTarget {
    fn from(_: VariantTexture2d) -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

impl From<VariantTexture2d> for Tx2dImageTarget {
    fn from(_: VariantTexture2d) -> Tx2dImageTarget {
        Tx2dImageTarget::Texture2d
    }
}

impl ImageTargetType for VariantTexture2d {
    fn gl_enum(&self) -> GLenum {
        gl::TEXTURE_2D
    }
}

pub const TEXTURE_CUBE_MAP : TextureBindingTarget =
    TextureBindingTarget::TextureCubeMap;
pub const TEXTURE_2D : VariantTexture2d = VariantTexture2d;

// TODO: Use type refinements someday...
#[derive(Debug, Clone, Copy)]
pub enum TextureFilter {
    Nearest,
    Linear
}

#[derive(Debug, Clone, Copy)]
pub enum TextureMipmapFilter {
    Filter(TextureFilter),
    MipmapFilter { criterion: TextureFilter, mipmap: TextureFilter }
}

pub const NEAREST : TextureFilter = TextureFilter::Nearest;
pub const LINEAR : TextureFilter = TextureFilter::Linear;
pub const NEAREST_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Nearest
    };
pub const LINEAR_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Nearest
    };
pub const NEAREST_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Linear
    };
pub const LINEAR_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Linear
    };

#[allow(dead_code)]
impl TextureFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(self::NEAREST) },
            gl::LINEAR => { Ok(self::LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            self::NEAREST => gl::NEAREST,
            self::LINEAR => gl::LINEAR
        }
    }
}

#[allow(dead_code)]
impl TextureMipmapFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(TextureMipmapFilter::Filter(self::NEAREST)) },
            gl::LINEAR => { Ok(TextureMipmapFilter::Filter(self::LINEAR)) },
            gl::NEAREST_MIPMAP_NEAREST => { Ok(self::NEAREST_MIPMAP_NEAREST) },
            gl::LINEAR_MIPMAP_NEAREST => { Ok(self::LINEAR_MIPMAP_NEAREST) },
            gl::NEAREST_MIPMAP_LINEAR => { Ok(self::NEAREST_MIPMAP_LINEAR) },
            gl::LINEAR_MIPMAP_LINEAR => { Ok(self::LINEAR_MIPMAP_LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            TextureMipmapFilter::Filter(self::LINEAR) => { gl::LINEAR },
            TextureMipmapFilter::Filter(self::NEAREST) => { gl::NEAREST },
            self::NEAREST_MIPMAP_NEAREST => { gl::NEAREST_MIPMAP_NEAREST },
            self::LINEAR_MIPMAP_NEAREST => { gl::LINEAR_MIPMAP_NEAREST },
            self::NEAREST_MIPMAP_LINEAR => { gl::NEAREST_MIPMAP_LINEAR },
            self::LINEAR_MIPMAP_LINEAR => { gl::LINEAR_MIPMAP_LINEAR }
        }
    }
}

impl From<TextureFilter> for TextureMipmapFilter {
    fn from(filter: TextureFilter) -> TextureMipmapFilter {
        TextureMipmapFilter::Filter(filter)
    }
}

gl_enum! {
    pub gl_enum TextureWrapMode {
        ClampToEdge as CLAMP_TO_EDGE = gl::CLAMP_TO_EDGE,
        MirroredRepeat as MIRRORED_REPEAT = gl::MIRRORED_REPEAT,
        Repeat as REPEAT = gl::REPEAT
    }
}

unsafe fn _tex_parameter_iv(target: TextureBindingTarget,
                            pname: GLenum,
                            params: *const GLint)
{
    gl::TexParameteriv(target.gl_enum(), pname, params);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target` or `pname` is not an accepted defined value, or `params` should have defined a symbolic constant and does not",
        _ => "Unknown error"
    }
}

unsafe fn _tex_image_2d<T: ImageTargetType>(target: T,
                                            level: u32,
                                            internal_format: TextelFormat,
                                            width: u32,
                                            height: u32,
                                            border: u32,
                                            format: ImageFormat,
                                            image_ptr: *const u8) {
    debug_assert!(internal_format == format.textel_format);
    gl::TexImage2D(target.gl_enum(),
                   level as GLint,
                   internal_format.gl_enum() as GLint,
                   width as GLint,
                   height as GLint,
                   border as GLint,
                   format.textel_format.gl_enum(),
                   format.textel_type.gl_enum(),
                   image_ptr as *const GLvoid);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target`, `format`, or `type` is not an accepted value",
        GLError::InvalidValue => "`target`, `level`, `internalformat`, `width`, `height`, or `border` is an invalid value",
        GLError::InvalidOperation => "`format` conflicts with either `internalformat` or `type`",
        _ => "Unknown error"
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }

    fn set_min_filter<F: Into<TextureMipmapFilter>>(&mut self, filter: F) {
        let gl_int = filter.into().gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MIN_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_mag_filter(&mut self, filter: TextureFilter) {
        let gl_int = filter.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MAG_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_s(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_S,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_t(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_T,
                              &gl_int as *const GLint);
        }
    }

    fn image_2d<T, I: ?Sized>(&mut self,
                              target: T,
                              level: u32,
                              img: &I)
        where T: Into<<Self::TextureType as TextureType>::ImageTargetType>,
              I: Image2d
    {
        unsafe {
            _tex_image_2d(target.into(),
                          level,
                          img.format().textel_format,
                          img.width() as u32,
                          img.height() as u32,
                          0,
                          img.format(),
                          img.textel_bytes().as_ptr());
        }
    }

    fn image_2d_empty<T: ImageTargetType>(&mut self,
                                          target: T,
                                          level: u32,
                                          format: ImageFormat,
                                          width: u32,
                                          height: u32)
    {
        unsafe {
            _tex_image_2d(target,
                          level,
                          format.textel_format,
                          width,
                          height,
                          0,
                          format,
                          ptr::null());
        }
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind(&mut self, texture: &mut Texture2d) -> Texture2dBinding {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind(&mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<|endoftext|>