use crate::canvas::Canvas;
use crate::pixels::Pixel;
use std::cmp;
use std::collections::HashMap;
pub fn to_grey_lightness(p: &Pixel) -> Pixel {
let lightness = max(p.r, p.g, p.b) + min(p.r, p.g, p.b) / 2;
from_grey(lightness)
}
pub fn to_grey_average(p: &Pixel) -> Pixel {
let average = (p.r + p.g + p.b) / 3;
from_grey(average)
}
pub fn to_grey_lumiosity(p: &Pixel) -> Pixel {
to_grey_custom(p, 0.21, 0.72, 0.07)
}
pub fn to_grey_mean(p: &Pixel) -> Pixel {
to_grey_custom(p, 0.299, 0.587, 0.114)
}
pub fn to_grey_custom(p: &Pixel, v1: f32, v2: f32, v3: f32) -> Pixel {
let value = (p.r as f32 * v1 + p.g as f32 * v2 + p.b as f32 * v3) as u8;
from_grey(value)
}
pub fn from_grey(grey: u8) -> Pixel {
Pixel {
r: grey,
g: grey,
b: grey,
a: 255,
}
}
pub fn max<T>(v1: T, v2: T, v3: T) -> T
where
T: Ord,
{
cmp::max(v1, cmp::max(v2, v3))
}
pub fn min<T>(v1: T, v2: T, v3: T) -> T
where
T: Ord,
{
cmp::min(v1, cmp::min(v2, v3))
}
pub fn clamp<T: PartialOrd>(min: T, max: T, val: T) -> T {
assert!(min < max);
if val > max {
return max;
}
if val < min {
return min;
}
val
}
pub fn find_center_and_size(canvas: &Canvas) -> (Pixel, f32) {
let (center_r, max_r) = center(&canvas, |p| p.r);
let (center_g, max_g) = center(&canvas, |p| p.g);
let (center_b, max_b) = center(&canvas, |p| p.b);
let (center_a, max_a) = center(&canvas, |p| p.a);
let center = Pixel::new(center_r, center_g, center_b, center_a);
let corner = Pixel::new(max_r, max_g, max_b, max_a);
let distance = center.distance(&corner);
(center, distance)
}
fn center<F>(canvas: &Canvas, map_color: F) -> (u8, u8)
where F: Fn(Pixel) -> u8{
let min = canvas.iter().map(|p| map_color(p)).min().unwrap();
let max = canvas.iter().map(|p| map_color(p)).max().unwrap();
((max - min) / 2 + min, max)
}
pub fn diff_squared(p1: &Pixel, p2: &Pixel) -> (u32, u32, u32, u32) {
(
(p1.r - p2.r).pow(2).into(),
(p1.g - p2.g).pow(2).into(),
(p1.b - p2.b).pow(2).into(),
(p1.a - p2.a).pow(2).into(),
)
}
pub fn count_colors(c: &Canvas) -> HashMap<Pixel, usize> {
let mut map = HashMap::new();
for pixel in c.pixels() {
*map.entry(pixel.clone()).or_insert(0) += 1;
}
map
}
pub fn counted_colors_to_html(map: &HashMap<Pixel, usize>) -> String {
let mut pairs: Vec<(Pixel, usize)> = map.iter().map(|(x, y)| (x.clone(), y.clone())).collect();
pairs.sort_by_key(|x| x.1);
pairs.reverse();
let output: String = pairs.iter().map(|x| format!("<tr> <td> {} </td> <td bgcolor=\"RGB({}, {}, {})\"> ({}, {}, {}) </td> </tr>", x.1, x.0.r, x.0.g, x.0.b, x.0.r, x.0.g, x.0.b).to_string()).collect();
output
}
pub fn diff(c1: &Canvas, c2: &Canvas) -> Canvas {
let c = Canvas::new(1, 1);
c
}
pub fn diff_debug(c1: &Canvas, c2: &Canvas) -> Canvas {
let c1_dim = c1.dimensions();
let c2_dim = c2.dimensions();
let res = if c1_dim.width == c2_dim.width && c1_dim.height == c2_dim.height {
let pixels = c1
.pixels()
.zip(c2.pixels())
.map(|(p1, p2)| p1.diff(p2))
.collect();
Canvas::new_with_data(c1_dim.width, c1_dim.height, pixels)
} else {
c1.clone()
};
res
}
pub fn error(c1: &Canvas, c2: &Canvas) -> u128 {
let c1_dim = c1.dimensions();
let c2_dim = c2.dimensions();
let width = c1_dim.width.min(c2_dim.width);
let height = c1_dim.height.min(c2_dim.height);
let (mut r, mut g, mut b, mut a) = (0u128, 0u128, 0u128, 0u128);
for x in 0..width {
for y in 0..height {
let p1 = c1.get_pixel(x, y);
let p2 = c2.get_pixel(x, y);
let diff = p1.diff(&p2);
r += (diff.r as u128).pow(2) as u128;
g += (diff.g as u128).pow(2) as u128;
b += (diff.b as u128).pow(2) as u128;
a += (diff.a as u128).pow(2) as u128;
}
}
(r + g + b + a) / ((4 * width * height) as u128)
}
pub fn apply_alpha_color(
source_color: f32,
source_alpha: f32,
destination_color: f32,
_destination_alpha: f32,
) -> f32 {
source_color * source_alpha + destination_color * (1.0f32 - source_alpha)
}
pub fn overlap_colors(destination: &Pixel, source: &Pixel) -> Pixel {
let source_normalized = source.normalize();
let destination_normalized = destination.normalize();
let new_r = apply_alpha_color(
source_normalized.0,
source_normalized.3,
destination_normalized.0,
destination_normalized.3,
);
let new_g = apply_alpha_color(
source_normalized.1,
source_normalized.3,
destination_normalized.1,
destination_normalized.3,
);
let new_b = apply_alpha_color(
source_normalized.2,
source_normalized.3,
destination_normalized.2,
destination_normalized.3,
);
let new_a = apply_alpha_color(
source_normalized.3,
source_normalized.3,
destination_normalized.3,
destination_normalized.3,
);
Pixel::denormalize(new_r, new_g, new_b, new_a)
}
pub fn merge_chunks_vertically(chunks: &Vec<Canvas>) -> Canvas {
let width = chunks.iter().map(|x| x.dimensions().width).max().unwrap_or_default();
let height = chunks.iter().map(|x| x.dimensions().height).sum();
let mut vec = vec![0];
chunks.iter().map(|x| x.dimensions().height).fold(0, |acc, x| { vec.push(acc + x); acc + x });
let mut canvas = Canvas::new(width, height);
for (i, e) in chunks.iter().enumerate() {
canvas.set_subimage_mut(0, vec[i], e);
}
canvas
}
pub fn merge_chunks_horizontally(chunks: &Vec<Canvas>) -> Canvas {
let height = chunks.iter().map(|x| x.dimensions().height).max().unwrap_or_default();
let width = chunks.iter().map(|x| x.dimensions().width).sum();
let mut vec = vec![0];
chunks.iter().map(|x| x.dimensions().width).fold(0, |acc, x| { vec.push(acc + x); acc + x });
let mut canvas = Canvas::new(width, height);
for (i, e) in chunks.iter().enumerate() {
canvas.set_subimage_mut(vec[i], 0, e);
}
canvas
}