use core::{
hash::{Hash, Hasher},
mem,
};
use super::{
resource::{Handle, LabelHandle},
FillShader, Rgba, Transform,
};
struct FxHasher(u64);
impl Hasher for FxHasher {
#[inline]
fn write_u8(&mut self, i: u8) {
self.write_u64(i as u64);
}
#[inline]
fn write_u16(&mut self, i: u16) {
self.write_u64(i as u64);
}
#[inline]
fn write_u32(&mut self, i: u32) {
self.write_u64(i as u64);
}
#[inline]
fn write_u64(&mut self, i: u64) {
self.0 = (self.0.rotate_left(5) ^ i).wrapping_mul(0x517cc1b727220a95);
}
#[inline]
fn write_u128(&mut self, i: u128) {
let [a, b]: [u64; 2] = bytemuck::cast(i);
self.write_u64(a);
self.write_u64(b);
}
#[inline]
fn write_usize(&mut self, i: usize) {
self.write(bytemuck::bytes_of(&i))
}
#[inline]
fn finish(&self) -> u64 {
self.0
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
let (_, chunks, rem) = bytemuck::pod_align_to::<_, [u8; 8]>(bytes);
for chunk in chunks {
self.write_u64(bytemuck::cast(*chunk));
}
let (_, chunks, rem) = bytemuck::pod_align_to::<_, [u8; 4]>(rem);
for chunk in chunks {
self.write_u32(bytemuck::cast(*chunk));
}
for byte in rem {
self.write_u8(*byte);
}
}
}
pub enum Entity<P, I, L> {
FillPath(Handle<P>, FillShader, Transform),
StrokePath(Handle<P>, f32, Rgba, Transform),
Image(Handle<I>, Transform),
Label(LabelHandle<L>, FillShader, Transform),
}
pub fn calculate_hash<P, I, L>(
background: &Option<FillShader>,
entities: &[Entity<P, I, L>],
) -> u64 {
let mut hasher = FxHasher(0x517cc1b727220a95);
mem::discriminant(background).hash(&mut hasher);
if let Some(background) = background {
hash_shader(background, &mut hasher);
}
entities.hash(&mut hasher);
hasher.finish()
}
#[inline]
fn hash_float(f: f32, state: &mut impl Hasher) {
u32::hash(&bytemuck::cast(f), state);
}
#[inline]
fn hash_transform(f: &Transform, state: &mut impl Hasher) {
const _: () = assert!(core::mem::size_of::<Transform>() == 16);
let [a, b]: [u64; 2] = bytemuck::cast(*f);
u64::hash(&a, state);
u64::hash(&b, state);
}
#[inline]
fn hash_floats(f: &[f32; 4], state: &mut impl Hasher) {
let [a, b]: [u64; 2] = bytemuck::cast(*f);
u64::hash(&a, state);
u64::hash(&b, state);
}
fn hash_shader(shader: &FillShader, state: &mut impl Hasher) {
mem::discriminant(shader).hash(state);
match shader {
FillShader::SolidColor(c) => hash_floats(c, state),
FillShader::VerticalGradient(t, b) => {
hash_floats(t, state);
hash_floats(b, state);
}
FillShader::HorizontalGradient(l, r) => {
hash_floats(l, state);
hash_floats(r, state);
}
}
}
impl<P, I, L> Hash for Entity<P, I, L> {
fn hash<H: Hasher>(&self, state: &mut H) {
mem::discriminant(self).hash(state);
match self {
Entity::FillPath(path, shader, transform) => {
path.hash(state);
hash_shader(shader, state);
hash_transform(transform, state);
}
Entity::StrokePath(path, stroke_width, color, transform) => {
path.hash(state);
hash_float(*stroke_width, state);
hash_floats(color, state);
hash_transform(transform, state);
}
Entity::Image(image, transform) => {
image.hash(state);
hash_transform(transform, state);
}
Entity::Label(label, shader, transform) => {
label.hash(state);
hash_shader(shader, state);
hash_transform(transform, state);
}
}
}
}