///|
/// Export-oriented canvas helpers for deterministic previews and fixtures.
pub(all) struct CanvasDigest {
hash : UInt
pixels : Int
width : Int
height : Int
alpha_pixels : Int
} derive(Debug)
///|
pub fn CanvasDigest::hash(self : CanvasDigest) -> UInt {
self.hash
}
///|
pub fn CanvasDigest::pixels(self : CanvasDigest) -> Int {
self.pixels
}
///|
pub fn CanvasDigest::width(self : CanvasDigest) -> Int {
self.width
}
///|
pub fn CanvasDigest::height(self : CanvasDigest) -> Int {
self.height
}
///|
pub fn CanvasDigest::alpha_pixels(self : CanvasDigest) -> Int {
self.alpha_pixels
}
///|
/// Compute a stable digest for cache keys and regression reports.
pub fn Canvas::digest(self : Canvas) -> CanvasDigest {
let mut value : UInt = 2166136261
let mut alpha_pixels = 0
for color in self.pixels {
value = (value ^ color.red().to_int().reinterpret_as_uint()) * 16777619
value = (value ^ color.green().to_int().reinterpret_as_uint()) * 16777619
value = (value ^ color.blue().to_int().reinterpret_as_uint()) * 16777619
value = (value ^ color.alpha().to_int().reinterpret_as_uint()) * 16777619
if color.alpha() > 0 {
alpha_pixels += 1
}
}
CanvasDigest::{
hash: value,
pixels: self.len(),
width: self.width,
height: self.height,
alpha_pixels,
}
}
///|
/// Return a one-pixel border sample in clockwise order.
pub fn Canvas::border_samples(self : Canvas) -> Array[Rgba] {
let result : Array[Rgba] = []
if self.width == 0 || self.height == 0 {
result
} else {
for x in 0.. 1 {
for y in 1.. 1 {
for x in 0..<(self.width - 1) {
result.push(
self.pixels[(self.height - 1) * self.width + self.width - 2 - x],
)
}
}
if self.width > 1 {
for y in 1..<(self.height - 1) {
result.push(self.pixels[(self.height - 1 - y) * self.width])
}
}
result
}
}
///|
/// Replace transparent RGB values with a canonical zero color.
pub fn Canvas::canonicalize_transparency(self : Canvas) -> Canvas {
let result = self.copy()
for i in 0.. Canvas {
let safe_columns = if columns < 1 { 1 } else { columns }
let safe_rows = if rows < 1 { 1 } else { rows }
let colors = self.color_frequencies(safe_columns * safe_rows)
let result = Canvas::new(
width=safe_columns,
height=safe_rows,
fill=rgba(0, 0, 0, 0),
)
for i, pair in colors {
result.pixels[i] = pair.0
}
result
}
///|
/// Return true when two canvases have equal dimensions and digest.
pub fn canvas_digest_equal(left : Canvas, right : Canvas) -> Bool {
let a = left.digest()
let b = right.digest()
a.width() == b.width() && a.height() == b.height() && a.hash() == b.hash()
}