use std::collections::hash_map::DefaultHasher;
use std::hash::{ Hash, Hasher };
use tiny_skia::{ Path, PathBuilder };
use crate::types::{ Corners, PathCmd };
#[ derive( Clone, Copy, PartialEq, Eq, Hash ) ]
pub( crate ) enum ImageId
{
Content( u64 ),
Keyed( u64 ),
}
pub( crate ) fn fingerprint_rgba( bytes: &[u8] ) -> u64
{
const FULL_HASH_THRESHOLD: usize = 16 * 1024;
const SAMPLE_CHUNKS: usize = 8;
const SAMPLE_CHUNK_BYTES: usize = 512;
let mut h = DefaultHasher::new();
let n = bytes.len();
n.hash( &mut h );
if n <= FULL_HASH_THRESHOLD
{
bytes.hash( &mut h );
} else {
let stride = n / SAMPLE_CHUNKS;
for i in 0..SAMPLE_CHUNKS
{
let pos = ( i * stride ).min( n - SAMPLE_CHUNK_BYTES );
bytes[ pos..pos + SAMPLE_CHUNK_BYTES ].hash( &mut h );
}
}
h.finish()
}
const KAPPA: f32 = 0.5523_f32;
pub ( crate ) fn build_ts_path( cmds: &[ PathCmd ] ) -> Option<Path>
{
let mut pb = PathBuilder::new();
for cmd in cmds
{
match *cmd
{
PathCmd::MoveTo( x, y ) => pb.move_to( x, y ),
PathCmd::LineTo( x, y ) => pb.line_to( x, y ),
PathCmd::QuadTo( x1, y1, x, y ) => pb.quad_to( x1, y1, x, y ),
PathCmd::CubicTo( x1, y1, x2, y2, x, y ) => pb.cubic_to( x1, y1, x2, y2, x, y ),
PathCmd::Close => pb.close(),
}
}
pb.finish()
}
pub ( crate ) fn validate_rgba_dims( backend: &str, data: &[u8], img_w: u32, img_h: u32 ) -> bool
{
let expected = ( img_w as usize ).saturating_mul( img_h as usize ).saturating_mul( 4 );
if img_w == 0 || img_h == 0 || data.len() != expected
{
eprintln!(
"[ltk] {}::draw_image_data: refusing draw — {}×{} declared, {} bytes provided, expected {}",
backend, img_w, img_h, data.len(), expected,
);
return false;
}
true
}
pub ( super ) fn build_rounded_rect( rect: tiny_skia::Rect, corners: Corners ) -> Option<Path>
{
let c = corners.clamp_to_size( rect.width(), rect.height() );
let tl = c.tl;
let tr = c.tr;
let br = c.br;
let bl = c.bl;
let x0 = rect.left();
let y0 = rect.top();
let x1 = rect.right();
let y1 = rect.bottom();
let mut pb = PathBuilder::new();
pb.move_to( x0 + tl, y0 );
pb.line_to( x1 - tr, y0 );
if tr > 0.0
{
let kk = tr * KAPPA;
pb.cubic_to( x1 - tr + kk, y0, x1, y0 + tr - kk, x1, y0 + tr );
}
pb.line_to( x1, y1 - br );
if br > 0.0
{
let kk = br * KAPPA;
pb.cubic_to( x1, y1 - br + kk, x1 - br + kk, y1, x1 - br, y1 );
}
pb.line_to( x0 + bl, y1 );
if bl > 0.0
{
let kk = bl * KAPPA;
pb.cubic_to( x0 + bl - kk, y1, x0, y1 - bl + kk, x0, y1 - bl );
}
pb.line_to( x0, y0 + tl );
if tl > 0.0
{
let kk = tl * KAPPA;
pb.cubic_to( x0, y0 + tl - kk, x0 + tl - kk, y0, x0 + tl, y0 );
}
pb.close();
pb.finish()
}