///|
/// Procedural noise utilities for texture, dithering, and synthetic fixtures.
pub(all) enum NoiseBlend {
AddNoise
MultiplyNoise
ReplaceLuma
} derive(Eq, Debug)
///|
fn hash_noise(x : Int, y : Int, seed : Int) -> Int {
let mut value = x * 1103515245 + y * 12345 + seed * 265443576
value = (value ^ (value >> 11)) * 1597334677
let result = value ^ (value >> 15)
let positive = if result < 0 { -result } else { result }
positive % 256
}
///|
fn interpolated_noise(x : Int, y : Int, scale : Int, seed : Int) -> Int {
let safe_scale = if scale < 1 { 1 } else { scale }
let grid_x = x / safe_scale
let grid_y = y / safe_scale
let next_x = grid_x + 1
let next_y = grid_y + 1
let fx = x % safe_scale
let fy = y % safe_scale
let top = (
hash_noise(grid_x, grid_y, seed) * (safe_scale - fx) +
hash_noise(next_x, grid_y, seed) * fx
) /
safe_scale
let bottom = (
hash_noise(grid_x, next_y, seed) * (safe_scale - fx) +
hash_noise(next_x, next_y, seed) * fx
) /
safe_scale
(top * (safe_scale - fy) + bottom * fy) / safe_scale
}
///|
/// Add deterministic noise with a per-channel strength.
pub fn Canvas::noise(
self : Canvas,
amount : Int,
seed : Int,
blend : NoiseBlend,
) -> Canvas {
let safe_amount = if amount < 0 {
0
} else if amount > 255 {
255
} else {
amount
}
let result = self.copy()
for y in 0.. 255 { 255 } else { red }) *
factor /
255).to_byte(),
((if green < 0 { 0 } else if green > 255 { 255 } else { green }) *
factor /
255).to_byte(),
((if blue < 0 { 0 } else if blue > 255 { 255 } else { blue }) *
factor /
255).to_byte(),
source.alpha(),
)
}
}
result
}
///|
/// Layer two scales of value noise for a smoother texture.
pub fn Canvas::fractal_noise(self : Canvas, amount : Int, seed : Int) -> Canvas {
let safe_amount = if amount < 0 {
0
} else if amount > 255 {
255
} else {
amount
}
let result = self.copy()
for y in 0.. 255 { 255 } else { red }).to_byte(),
(if green < 0 { 0 } else if green > 255 { 255 } else { green }).to_byte(),
(if blue < 0 { 0 } else if blue > 255 { 255 } else { blue }).to_byte(),
source.alpha(),
)
}
}
result
}
///|
/// Add blue-noise-like ordered variation before palette quantization.
pub fn Canvas::noise_dither(self : Canvas, amount : Int, seed : Int) -> Canvas {
let result = self.copy()
for y in 0.. 255 { 255 } else { value }
result.pixels[x + y * self.width] = rgba(
safe.to_byte(),
safe.to_byte(),
safe.to_byte(),
source.alpha(),
)
}
}
result
}