use rand::{
distr::{uniform::SampleUniform, Distribution, Uniform},
prelude::*,
Fill,
};
use std::ops::Range;
use crate::{
colour::{BlendMode, Colour},
formats::{self, PixelFormatSpec, A8},
math::{PixelBox, PixelPoint, PixelSize},
prelude::*,
Bitmap,
};
use super::{Context, ContextValueTag};
pub struct Benchmark;
impl ContextValueTag for Benchmark {
type Value = bool;
}
const DEFAULT_SIZE_BOUNDS: Range<i32> = 4..8;
pub struct ImageSizeBounds;
impl ContextValueTag for ImageSizeBounds {
type Value = Range<i32>;
}
pub struct DstSize;
impl ContextValueTag for DstSize {
type Value = PixelSize;
}
pub struct SrcSize;
impl ContextValueTag for SrcSize {
type Value = PixelSize;
}
pub struct AlphamapSize;
impl ContextValueTag for AlphamapSize {
type Value = PixelSize;
}
impl ContextValueTag for BlendMode {
type Value = BlendMode;
}
fn point_in_size(rng: &mut dyn Rng, sz: &PixelSize) -> PixelPoint {
let xdist = Uniform::new(0, sz.width).unwrap();
let ydist = Uniform::new(0, sz.height).unwrap();
PixelPoint::new(xdist.sample(rng), ydist.sample(rng))
}
pub fn constant<V: Clone>(v: V) -> impl Fn(&mut Context) -> Option<V> {
move |_| Some(v.clone())
}
pub fn uniform<V: SampleUniform + Clone>(from: V, to: V) -> impl Fn(&mut Context) -> Option<V> {
move |ctx| {
let uniform = Uniform::new(from.clone(), to.clone()).unwrap();
Some(ctx.rng().sample(uniform))
}
}
pub fn random_colour(ctx: &mut Context) -> Option<Colour> {
let rng = ctx.rng();
Some(Colour {
r: rng.random(),
g: rng.random(),
b: rng.random(),
a: rng.random(),
})
}
pub fn blend_mode(ctx: &mut Context) -> Option<BlendMode> {
if let Some(v) = ctx.lookup::<BlendMode>() {
return Some(*v);
}
let rng = ctx.rng();
let dist = Uniform::new(0, 5).unwrap();
Some(match rng.sample(dist) {
0 => BlendMode::SourceCopy,
1 => BlendMode::Simple,
2 => BlendMode::SourceAlpha,
3 => BlendMode::BackdropAlpha,
4 => BlendMode::Multiply,
_ => unreachable!(),
})
}
pub fn dst_image_generator<Format: PixelFormatSpec>(ctx: &mut Context) -> Option<Bitmap<Format>> {
let size_bounds = ctx
.lookup::<ImageSizeBounds>()
.unwrap_or(&DEFAULT_SIZE_BOUNDS);
let size_dist = Uniform::new(size_bounds.start, size_bounds.end).unwrap();
let mut bitmap = Bitmap::new(
size_dist.sample(ctx.rng()) as usize,
size_dist.sample(ctx.rng()) as usize,
);
u8::fill_slice(bitmap.data_mut(), ctx.rng());
ctx.insert::<DstSize>(bitmap.size());
Some(bitmap)
}
pub fn src_image_generator<Format: PixelFormatSpec>(ctx: &mut Context) -> Option<Bitmap<Format>> {
let size_bounds = ctx
.lookup::<ImageSizeBounds>()
.unwrap_or(&DEFAULT_SIZE_BOUNDS);
let size_dist = Uniform::new(size_bounds.start, size_bounds.end).unwrap();
let mut bitmap = Bitmap::new(
size_dist.sample(ctx.rng()) as usize,
size_dist.sample(ctx.rng()) as usize,
);
u8::fill_slice(bitmap.data_mut(), ctx.rng());
ctx.insert::<SrcSize>(bitmap.size());
Some(bitmap)
}
pub fn alphamap<'l>(ctx: &mut Context) -> Option<&'l dyn BitmapAccess<formats::A8>> {
let size = if *ctx.lookup::<Benchmark>().unwrap_or(&false) {
*ctx.lookup::<DstSize>().unwrap()
} else {
let size_bounds = ctx
.lookup::<ImageSizeBounds>()
.unwrap_or(&DEFAULT_SIZE_BOUNDS);
let size_dist = Uniform::new(size_bounds.start, size_bounds.end).unwrap();
PixelSize::new(
size_dist.sample(ctx.rng()) as i32,
size_dist.sample(ctx.rng()) as i32,
)
};
let mut alphamap = Bitmap::<A8>::new(size.width as usize, size.height as usize);
u8::fill_slice(alphamap.data_mut(), ctx.rng());
ctx.insert::<AlphamapSize>(alphamap.size());
let alphamap = Box::leak(Box::new(alphamap));
Some(alphamap)
}
pub fn alphamap_area(ctx: &mut Context) -> Option<PixelBox> {
if *ctx.lookup::<Benchmark>().unwrap_or(&false) {
return Some(PixelBox::from_size(*ctx.lookup::<DstSize>().unwrap()));
}
let dst_size = *ctx.lookup::<DstSize>()?;
let p1 = point_in_size(ctx.rng(), &dst_size);
let p2 = point_in_size(ctx.rng(), &dst_size);
Some(PixelBox::new(p1.min(p2), p1.max(p2)))
}
pub fn dst_point(ctx: &mut Context) -> Option<PixelPoint> {
if *ctx.lookup::<Benchmark>().unwrap_or(&false) {
return Some(PixelPoint::zero());
}
let dstsize = *ctx.lookup::<DstSize>()?;
Some(point_in_size(ctx.rng(), &dstsize))
}
pub fn dst_area(ctx: &mut Context) -> Option<PixelBox> {
if *ctx.lookup::<Benchmark>().unwrap_or(&false) {
return Some(PixelBox::from_size(*ctx.lookup::<DstSize>().unwrap()));
}
let dst_size = *ctx.lookup::<DstSize>()?;
let p1 = point_in_size(ctx.rng(), &dst_size);
let p2 = point_in_size(ctx.rng(), &dst_size);
Some(PixelBox::new(p1.min(p2), p1.max(p2)))
}
pub fn src_area(ctx: &mut Context) -> Option<PixelBox> {
if *ctx.lookup::<Benchmark>().unwrap_or(&false) {
return Some(PixelBox::from_size(*ctx.lookup::<SrcSize>().unwrap()));
}
let src_size = *ctx.lookup::<SrcSize>()?;
let p1 = point_in_size(ctx.rng(), &src_size);
let p2 = point_in_size(ctx.rng(), &src_size);
Some(PixelBox::new(p1.min(p2), p1.max(p2)))
}