primg 0.1.0

Reproducing images with geometric primitives. Ported from https://github.com/fogleman/primitive
Documentation
use super::SIZE;
use image::{Pixel, Rgba, RgbaImage};
use image::math::utils::clamp;
use std::fmt;
use std::marker::Sync;

use scanline::Scanline;
use util;

#[derive(Clone)]
pub struct Pixels {
    pub buf: [u8; Pixels::BUF_SIZE],
    pub w: usize,
    pub h: usize,
}

unsafe impl Sync for Pixels {}

impl Pixels {
    pub const BUF_SIZE: usize = SIZE * SIZE * 4;

    pub fn new(w: usize, h: usize) -> Pixels {
        Pixels { buf: [0; Pixels::BUF_SIZE], w, h }
    }

    pub fn from(img: RgbaImage) -> Pixels {
        let w = img.width() as usize;
        let h = img.height() as usize;
        assert!(w * h <= SIZE * SIZE);
        let mut img: Vec<u8> = img.into_raw();
        img.resize(Pixels::BUF_SIZE, 0);
        let mut buf = [0; Pixels::BUF_SIZE];
        buf.copy_from_slice(img.as_ref());
        Pixels { buf, w, h }
    }

    pub fn get(&self, x: usize, y: usize) -> Color {
        let i = self.index(x, y);
        Color::from(&self.buf[i..i + 4])
    }

    pub fn get_arr(&self, x: usize, y: usize) -> &[u8] {
        let i = self.index(x, y);
        &self.buf[i..i + 4]
    }

    pub fn put(&mut self, x: usize, y: usize, color: &Color) {
        let i = self.index(x, y);
        self.buf[i] = color.r();
        self.buf[i + 1] = color.g();
        self.buf[i + 2] = color.b();
        self.buf[i + 3] = color.a();
    }

    pub fn erase(&mut self, color: &Color) {
        util::erase(&mut self.buf, color);
    }

    pub fn average_color(&self) -> Color {
        let mut r = 0u64;
        let mut g = 0u64;
        let mut b = 0u64;
        for y in 0..self.h {
            for x in 0..self.w {
                let c = self.get(x, y);
                r += c.r() as u64;
                g += c.g() as u64;
                b += c.b() as u64;
            }
        }
        let area = self.w as u64 * self.h as u64;
        r /= area;
        g /= area;
        b /= area;
        Color::new(r as u8, g as u8, b as u8, 255)
    }

    pub fn compute_color(&self, target: &Pixels, lines: &[Scanline], alpha: u8) -> Color {
        let mut rsum = 0;
        let mut gsum = 0;
        let mut bsum = 0;
        let mut count = 0;
        let a = 0xffff / (alpha as i32);
        unsafe {
            for line in lines {
                let mut i = target.index(line.x1, line.y);
                for _ in line.x1..line.x2 + 1 {
                    let tr = *target.buf.get_unchecked(i + 0) as i32;
                    let tg = *target.buf.get_unchecked(i + 1) as i32;
                    let tb = *target.buf.get_unchecked(i + 2) as i32;
                    let cr = *self.buf.get_unchecked(i + 0) as i32;
                    let cg = *self.buf.get_unchecked(i + 1) as i32;
                    let cb = *self.buf.get_unchecked(i + 2) as i32;
                    rsum += (tr - cr) * a + cr * 0x101;
                    gsum += (tg - cg) * a + cg * 0x101;
                    bsum += (tb - cb) * a + cb * 0x101;
                    count += 1;
                    i += 4;
                }
            }
        }
        if count == 0 {
            return Color::new(0, 0, 0, 0);
        }
        let r = clamp((rsum / count) >> 8, 0, 255);
        let g = clamp((gsum / count) >> 8, 0, 255);
        let b = clamp((bsum / count) >> 8, 0, 255);
        return Color::new(r as u8, g as u8, b as u8, alpha);
    }

    pub fn copy_lines(&mut self, src: &Pixels, lines: &[Scanline]) {
        for line in lines {
            let a = self.index(line.x1, line.y);
            let b = a + (line.x2 - line.x1 + 1) * 4;
            let dst = &mut self.buf[a..b];
            dst.copy_from_slice(&src.buf[a..b])
        }
    }

    pub fn draw_lines(&mut self, a: &Color, lines: &[Scanline]) {
        util::draw_lines(&mut self.buf, self.w, self.h, a, lines);
    }

    pub fn difference_full(a: &Pixels, b: &Pixels) -> f32 {
        let w = a.w;
        let h = a.h;
        let mut total = 0i32;
        for y in 0..h {
            for x in 0..w {
                let pa = a.get_arr(x, y);
                let pb = b.get_arr(x, y);

                let dr = pa[0] as i32 - pb[0] as i32;
                let dg = pa[1] as i32 - pb[1] as i32;
                let db = pa[2] as i32 - pb[2] as i32;
                let da = pa[3] as i32 - pb[3] as i32;
                total += (dr * dr) + (dg * dg) + (db * db) + (da * da);
            }
        }
        (total as f32 / (w * h * 4) as f32).sqrt() / 255.0
    }

    pub fn difference_partial(target: &Pixels,
                              before: &Pixels,
                              after: &Pixels,
                              score: f32,
                              lines: &[Scanline]) -> f32 {
        let ni = target.w * target.h * 4;
        let mut total = ((score * 255.0).powi(2) * ni as f32) as i32;

        unsafe {
            for line in lines {
                let mut i = target.index(line.x1, line.y);
                for _ in line.x1..line.x2 + 1 {
                    let dr1 = *target.buf.get_unchecked(i + 0) as i32 - *before.buf.get_unchecked(i + 0) as i32;
                    let dg1 = *target.buf.get_unchecked(i + 1) as i32 - *before.buf.get_unchecked(i + 1) as i32;
                    let db1 = *target.buf.get_unchecked(i + 2) as i32 - *before.buf.get_unchecked(i + 2) as i32;
                    let da1 = *target.buf.get_unchecked(i + 3) as i32 - *before.buf.get_unchecked(i + 3) as i32;

                    let dr2 = *target.buf.get_unchecked(i + 0) as i32 - *after.buf.get_unchecked(i + 0) as i32;
                    let dg2 = *target.buf.get_unchecked(i + 1) as i32 - *after.buf.get_unchecked(i + 1) as i32;
                    let db2 = *target.buf.get_unchecked(i + 2) as i32 - *after.buf.get_unchecked(i + 2) as i32;
                    let da2 = *target.buf.get_unchecked(i + 3) as i32 - *after.buf.get_unchecked(i + 3) as i32;

                    total -= (dr1 * dr1) + (dg1 * dg1) + (db1 * db1) + (da1 * da1);
                    total += (dr2 * dr2) + (dg2 * dg2) + (db2 * db2) + (da2 * da2);

                    i += 4;
                }
            }
        }
        (total as f32 / ni as f32).sqrt() / 255.0
    }

    pub fn index(&self, x: usize, y: usize) -> usize {
        4 * (y * self.w + x)
    }
}

#[derive(Copy, Clone)]
pub struct Color(u32);

impl Color {
    pub fn new(r: u8, g: u8, b: u8, a: u8) -> Color {
        Color(((r as u32) << 24) | ((g as u32) << 16) | ((b as u32) << 8) | a as u32)
    }

    pub fn from(c: &[u8]) -> Color {
        Color::new(c[0], c[1], c[2], c[3])
    }

    pub fn to_rgba(&self) -> Rgba<u8> {
        Pixel::from_channels(self.r(), self.g(), self.b(), self.a())
    }

    #[cfg(target_os = "android")]
    pub fn to_argb_i32(&self) -> i32 {
        let a = self.0 & 0xff;
        ((a << 24) | (self.0 >> 8)) as i32
    }

    pub fn r(&self) -> u8 {
        (self.0 >> 24) as u8
    }

    pub fn g(&self) -> u8 {
        ((self.0 >> 16) & 0xff) as u8
    }

    pub fn b(&self) -> u8 {
        ((self.0 >> 8) & 0xff) as u8
    }

    pub fn a(&self) -> u8 {
        (self.0 & 0xff) as u8
    }
}

impl fmt::Display for Color {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}