#![allow(clippy::inline_always)]
#![allow(clippy::cast_lossless)]
#[allow(unused_imports)]
use crate::extensions::*;
#[cfg(feature = "random")]
#[cfg(not(target_arch = "wasm32"))]
mod random;
#[cfg(feature = "random")]
#[cfg(not(target_arch = "wasm32"))]
pub use random::*;
pub mod colors;
mod interpolation;
pub use interpolation::*;
#[inline(always)]
#[must_use]
pub const fn rgb_u8_to_u32(r: u8, g: u8, b: u8) -> u32 {
(r as u32) << 16 | (g as u32) << 8 | (b as u32)
}
#[inline(always)]
#[must_use]
pub const fn rgb_to_u32(r: u32, g: u32, b: u32) -> u32 {
r << 16 | g << 8 | b
}
#[inline(always)]
#[must_use]
pub const fn rgba_u8_to_u32(r: u8, g: u8, b: u8, a: u8) -> u32 {
(a as u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | b as u32
}
#[inline(always)]
#[must_use]
pub const fn rgba_to_u32(r: u32, g: u32, b: u32, a: u32) -> u32 {
(a) << 24 | (r) << 16 | (g) << 8 | b
}
#[inline(always)]
#[must_use]
pub const fn u32_to_rgb_u8(color: u32) -> (u8, u8, u8) {
let red = ((color >> 16) & 0xFF) as u8;
let green = ((color >> 8) & 0xFF) as u8;
let blue = (color & 0xFF) as u8;
(red, green, blue)
}
#[inline(always)]
#[must_use]
pub const fn u32_to_rgb(color: u32) -> (u32, u32, u32) {
let red = (color >> 16) & 0xFF;
let green = (color >> 8) & 0xFF;
let blue = color & 0xFF;
(red, green, blue)
}
#[inline(always)]
#[must_use]
pub const fn u32_to_rgba_u8(color: u32) -> (u8, u8, u8, u8) {
let alpha = ((color >> 24) & 0xFF) as u8;
let red = ((color >> 16) & 0xFF) as u8;
let green = ((color >> 8) & 0xFF) as u8;
let blue = (color & 0xFF) as u8;
(red, green, blue, alpha)
}
#[inline(always)]
#[must_use]
pub const fn u32_to_rgba(color: u32) -> (u32, u32, u32, u32) {
let alpha = (color >> 24) & 0xFF;
let red = (color >> 16) & 0xFF;
let green = (color >> 8) & 0xFF;
let blue = color & 0xFF;
(red, green, blue, alpha)
}
#[inline(always)]
#[must_use]
pub const fn u32_to_argb_u8(color: u32) -> (u8, u8, u8, u8) {
let alpha = ((color >> 24) & 0xFF) as u8;
let red = ((color >> 16) & 0xFF) as u8;
let green = ((color >> 8) & 0xFF) as u8;
let blue = (color & 0xFF) as u8;
(alpha, red, green, blue)
}
#[inline(always)]
#[must_use]
pub const fn u32_to_argb(color: u32) -> (u32, u32, u32, u32) {
let alpha = (color >> 24) & 0xFF;
let red = (color >> 16) & 0xFF;
let green = (color >> 8) & 0xFF;
let blue = color & 0xFF;
(alpha, red, green, blue)
}
#[inline(always)]
#[must_use]
pub const fn get_alpha_of_u32_in_u8(color: u32) -> u8 {
((color >> 24) & 0xFF) as u8
}
#[inline(always)]
#[must_use]
pub const fn get_red_of_u32_in_u8(color: u32) -> u8 {
((color >> 16) & 0xFF) as u8
}
#[inline(always)]
#[must_use]
pub const fn get_green_of_u32_in_u8(color: u32) -> u8 {
((color >> 8) & 0xFF) as u8
}
#[inline(always)]
#[must_use]
pub const fn get_blue_of_u32_in_u8(color: u32) -> u8 {
(color & 0xFF) as u8
}
#[inline(always)]
#[must_use]
pub const fn get_alpha_of_u32(color: u32) -> u32 {
(color >> 24) & 0xFF
}
#[inline(always)]
#[must_use]
pub const fn get_red_of_u32(color: u32) -> u32 {
(color >> 16) & 0xFF
}
#[inline(always)]
#[must_use]
pub const fn get_green_of_u32(color: u32) -> u32 {
(color >> 8) & 0xFF
}
#[inline(always)]
#[must_use]
pub const fn get_blue_of_u32(color: u32) -> u32 {
color & 0xFF
}
#[cfg(feature = "imagery")]
#[cfg(feature = "std")]
pub mod imagery;
#[cfg(feature = "std")]
use std::collections::HashSet;
#[cfg(feature = "imagery")]
#[cfg(feature = "std")]
pub use imagery::*;
#[inline]
#[must_use]
#[allow(clippy::float_cmp)]
pub fn get_hue_of_rgb(r: f32, g: f32, b: f32) -> f32 {
let max = r.max(g).max(b);
let min = r.min(g).min(b);
if max == min {
0.0
} else if max == r {
((g - b) / core::f32::math::rem_euclid(max - min, 6.0)) * 60.0
} else if max == g {
((b - r) / (max - min) + 2.0) * 60.0
} else {
((r - g) / (max - min) + 4.0) * 60.0
}
}
#[inline]
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn adjust_brightness_hsl_of_rgb(color: u32, change: f32) -> u32 {
let alpha = (color >> 24) & 0xFF;
let red = (color >> 16) & 0xFF;
let green = (color >> 8) & 0xFF;
let blue = color & 0xFF;
let (h, s, l) = rgb_to_hsl(red as u8, green as u8, blue as u8);
let l_new = (l + change).clamp(0.0, 100.0);
let (r_new, g_new, b_new) = hsl_to_rgb_u32(h, s, l_new);
(alpha << 24) | (r_new << 16) | (g_new << 8) | b_new
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub const fn hsl_to_rgb_f32(
hue: f32,
saturation: f32,
lightness: f32,
) -> (f32, f32, f32) {
let c = (1.0 - (2.0 * lightness - 1.0).abs()) * saturation;
let x = c * (1.0 - ((hue / 60.0) % 2.0 - 1.0).abs());
let m = lightness - c / 2.0;
let (r1, g1, b1) = match hue as i32 {
0..=59 => (c, x, 0.0),
60..=119 => (x, c, 0.0),
120..=179 => (0.0, c, x),
180..=239 => (0.0, x, c),
240..=299 => (x, 0.0, c),
300..=359 => (c, 0.0, x),
_ => (0.0, 0.0, 0.0),
};
(r1 + m, g1 + m, b1 + m)
}
#[inline]
#[must_use]
#[allow(clippy::float_cmp)]
pub const fn rgb_to_hsl(r: u8, g: u8, b: u8) -> (f32, f32, f32) {
let r_norm = r as f32 / 255.0;
let g_norm = g as f32 / 255.0;
let b_norm = b as f32 / 255.0;
let max = r_norm.max(g_norm).max(b_norm);
let min = r_norm.min(g_norm).min(b_norm);
let delta = max - min;
let lightness = f32::midpoint(max, min);
let saturation = if delta < 0.0001 {
0.0 } else if lightness < 0.5 {
delta / (max + min)
} else {
delta / (2.0 - max - min)
};
let hue = if delta < 0.0001 {
0.0 } else if max == r_norm {
60.0 * (((g_norm - b_norm) / delta) % 6.0)
} else if max == g_norm {
60.0 * ((b_norm - r_norm) / delta + 2.0)
} else {
60.0 * ((r_norm - g_norm) / delta + 4.0)
};
(hue, saturation * 100.0, lightness * 100.0)
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
pub fn hsl_to_rgb_u32(
hue: f32,
saturation: f32,
lightness: f32,
) -> (u32, u32, u32) {
let h_norm = hue / 360.0;
let s_norm = saturation / 100.0;
let l_norm = lightness / 100.0;
if s_norm < 0.0001 {
let gray = core::f32::math::round(l_norm * 255.0) as u32;
return (gray, gray, gray);
}
let hue_to_rgb = |p: f32, q: f32, t: f32| -> f32 {
let t_adj = if t < 0.0 {
t + 1.0
} else if t > 1.0 {
t - 1.0
} else {
t
};
if t_adj < 1.0 / 6.0 {
core::f32::math::mul_add((q - p) * 6.0, t_adj, p)
} else if t_adj < 1.0 / 2.0 {
q
} else if t_adj < 2.0 / 3.0 {
core::f32::math::mul_add((q - p) * (2.0 / 3.0 - t_adj), 6.0, p)
} else {
p
}
};
let q = if l_norm < 0.5 {
l_norm * (1.0 + s_norm)
} else {
core::f32::math::mul_add(l_norm, -s_norm, l_norm + s_norm)
};
let p = core::f32::math::mul_add(2.0f32, l_norm, -q);
let red = hue_to_rgb(p, q, h_norm + 1.0 / 3.0);
let green = hue_to_rgb(p, q, h_norm);
let blue = hue_to_rgb(p, q, h_norm - 1.0 / 3.0);
let r_8bit = core::f32::math::round(red * 255.0) as u32;
let g_8bit = core::f32::math::round(green * 255.0) as u32;
let b_8bit = core::f32::math::round(blue * 255.0) as u32;
(r_8bit, g_8bit, b_8bit)
}
#[inline]
#[must_use]
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
pub fn shift_color_rgb(
red: u8,
green: u8,
blue: u8,
hue_shift: f32,
) -> (u32, u32, u32) {
let (hue, saturation, lightness) = rgb_to_hsl(red, green, blue);
let new_h = (hue + hue_shift) % 360.0;
let l_norm = lightness / 100.0;
let s_norm = saturation / 100.0;
let new_s = if l_norm > 0.5 {
s_norm * core::f32::math::mul_add(l_norm - 0.5, -2.0, 1.0) * 100.0
} else {
s_norm * core::f32::math::mul_add(0.5 - l_norm, -2.0, 1.0) * 100.0
};
let new_l = if l_norm > 0.5 {
l_norm * 0.95 * 100.0
} else {
(l_norm * 1.1).min(1.0) * 100.0
};
let new_s = new_s.clamp(0.0, 100.0);
let new_l = new_l.clamp(0.0, 100.0);
hsl_to_rgb_u32(new_h, new_s, new_l)
}
#[must_use]
pub fn shift_hue_rgb(
r: u8,
g: u8,
b: u8,
hue_shift_degrees: f32,
) -> (u32, u32, u32) {
let mut hsv = rgb_to_hsl(r, g, b);
hsv.0 = (hsv.0 + hue_shift_degrees) % 360.0;
let (r, g, b) = hsl_to_rgb_u32(hsv.0, hsv.1, hsv.2);
(r, g, b)
}
#[must_use]
pub fn shift_hue_u32(color: u32, hue_shift: f32) -> u32 {
let (r, g, b) = u32_to_rgb_u8(color);
let (r, g, b) = shift_hue_rgb(r, g, b, hue_shift);
rgb_to_u32(r, g, b)
}
#[inline]
#[must_use]
pub fn shift_color_u32(color: u32, hue_shift: f32) -> u32 {
let alpha = (color >> 24) & 0xFF;
let red = (color >> 16) & 0xFF;
let green = (color >> 8) & 0xFF;
let blue = color & 0xFF;
let (r_new, g_new, b_new) =
shift_color_rgb(red as u8, green as u8, blue as u8, hue_shift);
rgba_to_u32(r_new, g_new, b_new, alpha)
}
#[must_use]
#[inline]
#[allow(clippy::cast_sign_loss)]
pub fn adjust_brightness_fast(color: u32, x: i32) -> u32 {
let (red, green, blue): (i32, i32, i32) =
u32_to_rgb(color).try_tuple_into().unwrap_or((0, 0, 0));
let r_new = (red + x).clamp(0, 255) as u32;
let g_new = (green + x).clamp(0, 255) as u32;
let b_new = (blue + x).clamp(0, 255) as u32;
(r_new << 16) | (g_new << 8) | b_new
}
#[inline]
#[must_use]
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
pub fn desaturate_fast(color: u32, amount: f32) -> u32 {
let red = ((color >> 16) & 0xFF) as f32;
let green = ((color >> 8) & 0xFF) as f32;
let blue = (color & 0xFF) as f32;
let gray = core::f32::math::mul_add(
0.114f32,
blue,
core::f32::math::mul_add(0.299f32, red, 0.587 * green),
);
let r_new = core::f32::math::mul_add(red, 1.0 - amount, gray * amount)
.clamp(0.0, 255.0) as u32;
let g_new = core::f32::math::mul_add(green, 1.0 - amount, gray * amount)
.clamp(0.0, 255.0) as u32;
let b_new = core::f32::math::mul_add(blue, 1.0 - amount, gray * amount)
.clamp(0.0, 255.0) as u32;
(r_new << 16) | (g_new << 8) | b_new
}
#[allow(clippy::missing_errors_doc)]
#[cfg(feature = "svg")]
pub fn rasterize_svg(
svg_data: &[u8],
width: u32,
height: u32,
) -> Result<resvg::tiny_skia::Pixmap, Option<resvg::usvg::Error>> {
let opt = resvg::usvg::Options::default();
let tree = match resvg::usvg::Tree::from_data(svg_data, &opt) {
Ok(value) => value,
Err(error) => return Err(Some(error)),
};
let mut pixmap =
resvg::tiny_skia::Pixmap::new(width, height).ok_or(None)?;
resvg::render(
&tree,
resvg::usvg::Transform::default(),
&mut pixmap.as_mut(),
);
Ok(pixmap)
}
#[cfg(feature = "svg")]
#[cfg(feature = "std")]
#[allow(clippy::unwrap_used, clippy::missing_panics_doc)]
#[must_use]
pub fn pixmap_to_buffer(pixmap: &resvg::tiny_skia::Pixmap) -> Buffer {
let mut data = Vec::new();
for y in 0..pixmap.height() {
for x in 0..pixmap.width() {
let color = unsafe { pixmap.pixel(x, y).unwrap_unchecked() };
data.push(rgba_u8_to_u32(
color.red(),
color.green(),
color.blue(),
color.alpha(),
));
}
}
unsafe {
Buffer::new((pixmap.width() as usize, pixmap.height() as usize), data)
.unwrap_unchecked()
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "glfw")]
#[cfg(feature = "std")]
use glfw::PixelImage;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "glfw")]
#[inline(always)]
#[must_use]
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
#[cfg(feature = "std")]
pub fn buffer_to_pixel_image(buffer: &Buffer) -> glfw::PixelImage {
glfw::PixelImage {
width: buffer.width as u32,
height: buffer.height as u32,
pixels: switch_red_and_blue_list(&buffer.data),
}
}
#[cfg(feature = "glfw")]
#[cfg(not(target_arch = "wasm32"))]
#[inline(always)]
#[must_use]
#[cfg(feature = "std")]
#[allow(clippy::missing_panics_doc, clippy::unwrap_used)]
pub fn pixel_image_to_buffer(pixel_image: &glfw::PixelImage) -> Buffer {
Buffer::new(
(pixel_image.width as usize, pixel_image.height as usize),
rgba_list_to_argb_list(&pixel_image.pixels),
)
.unwrap()
}
mod pixel;
pub use pixel::*;
#[cfg(feature = "imagery")]
#[cfg(feature = "std")]
use crate::platform::file_system::file_system_traits::FileSystemTrait;
#[cfg(feature = "std")]
use crate::{prelude::Buffer, settings::MapType};
#[inline(always)]
#[must_use]
#[cfg(feature = "std")]
pub fn u32_to_hex_without_alpha(color: u32) -> String {
let (r, g, b) = u32_to_rgb_u8(color);
format!("{r:02x}{g:02x}{b:02x}")
}
#[inline(always)]
#[must_use]
#[cfg(feature = "std")]
pub fn u32_to_hex(color: u32) -> String {
let (r, g, b, a) = u32_to_rgba_u8(color);
format!("{r:02x}{g:02x}{b:02x}{a:02x}")
}
#[inline(always)]
pub fn hex_to_u32(hex: &str) -> Result<u32, core::num::ParseIntError> {
let alpha = u8::from_str_radix(&hex[0..2], 16)?;
let red = u8::from_str_radix(&hex[2..4], 16)?;
let green = u8::from_str_radix(&hex[4..6], 16)?;
let blue = u8::from_str_radix(&hex[6..8], 16)?;
Ok(rgba_u8_to_u32(red, green, blue, alpha))
}
#[inline(always)]
#[cfg(feature = "std")]
pub fn hex_to_u32_rgba(hex: &str) -> Result<u32, core::num::ParseIntError> {
let red = u8::from_str_radix(&hex[0..2], 16)?;
let green = u8::from_str_radix(&hex[2..4], 16)?;
let blue = u8::from_str_radix(&hex[4..6], 16)?;
let alpha = u8::from_str_radix(&hex[6..8], 16)?;
Ok(argb_to_rgba(rgba_u8_to_u32(red, green, blue, alpha)))
}
#[inline(always)]
#[cfg(feature = "std")]
pub fn hex_to_u32_rgb(hex: &str) -> Result<u32, core::num::ParseIntError> {
let red = u8::from_str_radix(&hex[2..4], 16)?;
let green = u8::from_str_radix(&hex[4..6], 16)?;
let blue = u8::from_str_radix(&hex[6..8], 16)?;
Ok(rgb_u8_to_u32(red, green, blue))
}
#[must_use]
#[inline(always)]
#[cfg(feature = "std")]
pub fn rgb_to_hex(r: u8, g: u8, b: u8) -> String {
format!("{r:02x}{g:02x}{b:02x}")
}
#[must_use]
pub const fn argb_to_rgba(color: u32) -> u32 {
let (a, r, g, b) = u32_to_argb_u8(color);
rgba_u8_to_u32(a, g, b, r)
}
#[must_use]
pub const fn switch_red_and_blue(color: u32) -> u32 {
let (a, r, g, b) = u32_to_argb_u8(color);
rgba_u8_to_u32(b, g, r, a)
}
#[must_use]
pub const fn switch_alpha_and_blue(color: u32) -> u32 {
let (a, r, g, b) = u32_to_argb_u8(color);
rgba_u8_to_u32(r, g, a, b)
}
#[must_use]
#[inline(always)]
#[cfg(feature = "std")]
pub fn switch_alpha_and_blue_list(input: &[u32]) -> Vec<u32> {
input.iter().map(|&argb| switch_alpha_and_blue(argb)).collect()
}
#[must_use]
#[inline(always)]
#[cfg(feature = "std")]
pub fn switch_red_and_blue_list(input: &[u32]) -> Vec<u32> {
input.iter().map(|&argb| switch_red_and_blue(argb)).collect()
}
#[must_use]
#[inline(always)]
#[cfg(feature = "std")]
pub fn argb_list_to_rgba_list(input: &[u32]) -> Vec<u32> {
input.iter().map(|&argb| argb_to_rgba(argb)).collect()
}
#[must_use]
#[cfg(feature = "std")]
pub fn rgba_list_to_argb_list(input: &[u32]) -> Vec<u32> {
argb_list_to_rgba_list(input)
}
#[cfg(feature = "std")]
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "glfw")]
impl From<Buffer> for glfw::PixelImage {
fn from(buffer: Buffer) -> Self {
buffer_to_pixel_image(&buffer)
}
}
#[cfg(feature = "std")]
#[cfg(feature = "glfw")]
#[cfg(not(target_arch = "wasm32"))]
impl From<glfw::PixelImage> for Buffer {
fn from(pixel_image: PixelImage) -> Self {
pixel_image_to_buffer(&pixel_image)
}
}
#[must_use]
#[inline]
pub const fn advance_color(red: u8, green: u8, blue: u8) -> (u8, u8, u8) {
if blue == 255 {
if green == 255 {
if red == 255 {
return (0, 0, 0);
}
return (red + 1, green, blue);
}
return (red, green + 1, blue);
}
(red, green, blue + 1)
}
#[must_use]
#[cfg(feature = "std")]
pub fn get_unused_color(
buffer: &[u8],
current_color: (u8, u8, u8),
) -> (u8, u8, u8) {
let mut current_color = current_color;
let mut unique_colors = HashSet::new();
for i in buffer.chunks_exact(4) {
if i[0] != 0 {
unique_colors.insert((i[1], i[2], i[3]));
}
}
while unique_colors.contains(¤t_color) {
current_color =
advance_color(current_color.0, current_color.1, current_color.2);
}
current_color
}
#[must_use]
#[cfg(feature = "std")]
pub fn get_unused_color_of_buffer(
buffer: &mut Buffer,
current_color: (u8, u8, u8),
) -> (u8, u8, u8) {
let mut current_color = current_color;
let mut unique_colors = HashSet::new();
for index in 0..buffer.total_size {
unsafe {
let i = u32_to_argb_u8(*buffer.pointer().add(index));
if i.0 != 0 {
unique_colors.insert((i.1, i.2, i.3));
}
}
}
while unique_colors.contains(¤t_color) {
current_color =
advance_color(current_color.0, current_color.1, current_color.2);
}
current_color
}
#[must_use]
pub const fn interpolate_color_rgb_u32(
from: u32,
to: u32,
progress: u32,
) -> u32 {
let inv_progress = 256 - progress;
let r1 = (from >> 24) & 0xFF;
let g1 = (from >> 16) & 0xFF;
let b1 = (from >> 8) & 0xFF;
let r2 = (to >> 24) & 0xFF;
let g2 = (to >> 16) & 0xFF;
let b2 = (to >> 8) & 0xFF;
let r = (r1 * inv_progress + r2 * progress) >> 8;
let g = (g1 * inv_progress + g2 * progress) >> 8;
let b = (b1 * inv_progress + b2 * progress) >> 8;
(r << 24) | (g << 16) | (b << 8) | 0xFF
}
macro_rules! interpolate_color_rgb_u32 {
($t:ty, $name:ident) => {
#[must_use]
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
pub const fn $name(from: u32, to: u32, progress: $t) -> u32 {
let (r1, g1, b1) = u32_to_rgb(from);
let (r2, g2, b2) = u32_to_rgb(to);
let red = crate::math::interpolate(r1 as $t, r2 as $t, progress);
let green = crate::math::interpolate(g1 as $t, g2 as $t, progress);
let blue = crate::math::interpolate(b1 as $t, b2 as $t, progress);
rgba_to_u32(red as u32, green as u32, blue as u32, 255)
}
};
}
interpolate_color_rgb_u32!(f32, interpolate_color_rgb_u32_f32);
interpolate_color_rgb_u32!(f64, interpolate_color_rgb_u32_f64);
interpolate_color_rgb_u32!(f16, interpolate_color_rgb_u32_f16);
interpolate_color_rgb_u32!(f128, interpolate_color_rgb_u32_f128);
#[must_use]
pub const fn invert_color(color: u32) -> u32 {
let (r, g, b, a) = u32_to_rgba_u8(color);
rgba_u8_to_u32(255 - r, 255 - g, 255 - b, a)
}
#[cfg_attr(all(feature = "std", not(feature = "ahash")), derive(Default))]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg(feature = "std")]
#[cfg_attr(feature = "c_compatible", repr(C))]
pub struct TextureManager {
pub textures: Vec<Option<Buffer>>,
pub lookup: MapType<String, usize>,
pub free_list: Vec<usize>,
#[cfg(feature = "imagery")]
pub texture_lookup: MapType<String, String>,
#[cfg(feature = "texture_manager_cleanup")]
pub last_used: Vec<u64>,
#[cfg(feature = "texture_manager_cleanup")]
pub current_frame: u64,
}
#[cfg(feature = "std")]
impl TextureManager {
#[must_use]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
use crate::settings::SettingsMapType;
Self {
textures: Vec::new(),
lookup: MapType::new_map(),
free_list: Vec::new(),
#[cfg(feature = "imagery")]
texture_lookup: MapType::new_map(),
#[cfg(feature = "texture_manager_cleanup")]
last_used: Vec::new(),
#[cfg(feature = "texture_manager_cleanup")]
current_frame: 0,
}
}
#[cfg(feature = "imagery")]
pub fn register_texture(&mut self, name: String, file_path: String) {
self.texture_lookup.insert(name, file_path);
}
#[must_use]
pub fn get_from_idx(&self, index: usize) -> Option<&Buffer> {
self.textures.get(index).and_then(|v| v.as_ref())
}
#[must_use]
pub fn get_idx(&self, name: &String) -> Option<usize> {
self.lookup.get(name).copied()
}
pub fn get(&mut self, name: &str) -> Option<&Buffer> {
#[cfg(feature = "texture_manager_cleanup")]
if let Some(&index) = self.lookup.get(name) {
if index < self.last_used.len() {
self.last_used[index] = self.current_frame;
}
return self.textures[index].as_ref();
}
if let Some(&index) = self.lookup.get(name) {
return self.textures[index].as_ref();
}
None
}
#[cfg(feature = "imagery")]
pub fn get_or_load<F: FileSystemTrait>(
&mut self,
name: &str,
file_system: &F,
remove_margins: bool,
) -> Result<Option<&Buffer>, Box<dyn std::error::Error>> {
#[cfg(feature = "texture_manager_cleanup")]
if let Some(&index) = self.lookup.get(name) {
if index < self.last_used.len() {
self.last_used[index] = self.current_frame;
}
return self.textures[index].as_ref();
}
if let Some(&index) = self.lookup.get(name) {
return Ok(self.textures[index].as_ref());
}
#[cfg(feature = "imagery")]
if let Some(file_path) = self.texture_lookup.get(name) {
match self.load_texture_from_file(file_path, file_system) {
Ok(mut buffer) => {
if remove_margins {
buffer.remove_margins();
}
self.insert_texture(name.to_string(), buffer);
if let Some(&index) = self.lookup.get(name) {
return Ok(self.textures[index].as_ref());
}
}
Err(e) => {
return Err(e);
}
}
}
Ok(None)
}
#[cfg(feature = "imagery")]
pub fn load_texture_from_file<F: FileSystemTrait>(
&self,
file_path: &str,
file_system: &F,
) -> Result<Buffer, Box<dyn core::error::Error>> {
let file = file_system.get_file_contents(file_path)?;
let img = file.to_image()?;
Ok(img.into())
}
pub fn insert_texture(&mut self, name: String, texture: Buffer) -> usize {
let index = if let Some(free) = self.free_list.pop() {
self.textures[free] = Some(texture);
free
} else {
self.textures.push(Some(texture));
self.textures.len() - 1
};
self.lookup.insert(name, index);
index
}
pub fn unload_texture(&mut self, name: &str) {
if let Some(&index) = self.lookup.get(name) {
use crate::settings::SettingsMapType;
self.textures[index] = None;
self.free_list.push(index);
self.lookup.remove_thingy(&name.to_string());
}
}
#[cfg(feature = "imagery")]
#[must_use]
pub fn is_texture_registered(&self, name: &str) -> bool {
self.texture_lookup.contains_key(name)
}
#[cfg(feature = "imagery")]
pub fn preload_texture<F: FileSystemTrait>(
&mut self,
name: &str,
file_system: &F,
) -> Result<(), Box<dyn core::error::Error>> {
if !self.lookup.contains_key(name) {
if let Some(file_path) = self.texture_lookup.get(name) {
let buffer =
self.load_texture_from_file(file_path, file_system)?;
self.insert_texture(name.to_string(), buffer);
}
}
Ok(())
}
#[cfg(feature = "texture_manager_cleanup")]
#[allow(arithmetic_overflow)]
pub fn cleanup_unused(&mut self, frames_unused: u64) {
let cutoff = self.current_frame.saturating_sub(frames_unused);
let to_remove: Vec<String> = self
.lookup
.iter()
.filter(|(_, &index)| {
index < self.last_used.len() && self.last_used[index] < cutoff
})
.map(|(name, _)| name.clone())
.collect();
for name in &to_remove {
self.unload_texture(name);
}
}
#[allow(arithmetic_overflow)]
#[cfg(feature = "texture_manager_cleanup")]
pub const fn tick(&mut self) {
self.current_frame += 1;
}
}
pub const trait ColorManipulation {
fn with_alpha(&self, alpha: u32) -> u32;
fn with_red(&self, red: u32) -> u32;
fn with_green(&self, green: u32) -> u32;
fn with_blue(&self, blue: u32) -> u32;
fn alpha(&self) -> u32;
fn red(&self) -> u32;
fn green(&self) -> u32;
fn blue(&self) -> u32;
fn add_blue(&self, other: u32) -> u32;
fn add_red(&self, other: u32) -> u32;
fn add_green(&self, other: u32) -> u32;
fn add_alpha(&self, other: u32) -> u32;
fn sub_blue(&self, other: u32) -> u32;
fn sub_red(&self, other: u32) -> u32;
fn sub_green(&self, other: u32) -> u32;
fn sub_alpha(&self, other: u32) -> u32;
fn add_all_color_channels(&self, other: u32) -> u32;
fn sub_all_color_channels(&self, other: u32) -> u32;
}
impl const ColorManipulation for u32 {
#[inline(always)]
fn with_alpha(&self, alpha: u32) -> u32 {
rgba_to_u32(
get_red_of_u32(*self),
get_green_of_u32(*self),
get_blue_of_u32(*self),
alpha,
)
}
#[inline(always)]
fn with_red(&self, red: u32) -> u32 {
rgba_to_u32(
red,
get_green_of_u32(*self),
get_blue_of_u32(*self),
get_alpha_of_u32(*self),
)
}
#[inline(always)]
fn with_green(&self, green: u32) -> u32 {
rgba_to_u32(
get_red_of_u32(*self),
green,
get_blue_of_u32(*self),
get_alpha_of_u32(*self),
)
}
#[inline(always)]
fn with_blue(&self, blue: u32) -> u32 {
rgba_to_u32(
get_red_of_u32(*self),
get_green_of_u32(*self),
blue,
get_alpha_of_u32(*self),
)
}
#[inline(always)]
fn alpha(&self) -> u32 {
get_alpha_of_u32(*self)
}
#[inline(always)]
fn red(&self) -> u32 {
get_red_of_u32(*self)
}
#[inline(always)]
fn green(&self) -> u32 {
get_green_of_u32(*self)
}
#[inline(always)]
fn blue(&self) -> u32 {
get_blue_of_u32(*self)
}
#[inline(always)]
fn add_alpha(&self, other: u32) -> u32 {
self.with_alpha(self.alpha() + other)
}
#[inline(always)]
fn add_red(&self, other: u32) -> u32 {
self.with_red(self.red() + other)
}
#[inline(always)]
fn add_green(&self, other: u32) -> u32 {
self.with_green(self.green() + other)
}
#[inline(always)]
fn add_blue(&self, other: u32) -> u32 {
self.with_blue(self.blue() + other)
}
#[inline(always)]
fn sub_alpha(&self, other: u32) -> u32 {
self.with_alpha(self.alpha() - other)
}
#[inline(always)]
fn sub_red(&self, other: u32) -> u32 {
self.with_red(self.red() - other)
}
#[inline(always)]
fn sub_green(&self, other: u32) -> u32 {
self.with_green(self.green() - other)
}
#[inline(always)]
fn sub_blue(&self, other: u32) -> u32 {
self.with_blue(self.blue() - other)
}
fn add_all_color_channels(&self, other: u32) -> u32 {
self.add_blue(other).add_red(other).add_green(other)
}
fn sub_all_color_channels(&self, other: u32) -> u32 {
self.sub_blue(other).sub_red(other).sub_green(other)
}
}
#[must_use]
#[cfg(feature = "std")]
pub fn create_bmp(image: &Buffer) -> Vec<u8> {
let mut bmp_buffer: Vec<u8> = Vec::new();
let width = image.width as u32;
let height = image.height as u32;
bmp_buffer.extend(&[0x42, 0x4D]);
let row_stride = (width * 32).div_ceil(32) * 4;
let pixel_array_size = row_stride * height;
let bmp_header_size = 40;
let file_header_size = 14;
let file_size = file_header_size + bmp_header_size + pixel_array_size;
let pixel_data_offset = file_header_size + bmp_header_size;
bmp_buffer.extend(&file_size.to_le_bytes()); bmp_buffer.extend(&[0x00, 0x00]); bmp_buffer.extend(&[0x00, 0x00]); bmp_buffer.extend(&pixel_data_offset.to_le_bytes());
bmp_buffer.extend(&40u32.to_le_bytes()); bmp_buffer.extend(&(width as i32).to_le_bytes()); bmp_buffer.extend(&(height as i32).to_le_bytes()); bmp_buffer.extend(&1u16.to_le_bytes()); bmp_buffer.extend(&32u16.to_le_bytes()); bmp_buffer.extend(&0u32.to_le_bytes()); bmp_buffer.extend(&pixel_array_size.to_le_bytes()); bmp_buffer.extend(&0u32.to_le_bytes()); bmp_buffer.extend(&0u32.to_le_bytes()); bmp_buffer.extend(&0u32.to_le_bytes()); bmp_buffer.extend(&0u32.to_le_bytes());
for pixel in &image.flip_vertically().data {
let (r, g, b, a) = u32_to_rgba_u8(*pixel);
#[allow(clippy::tuple_array_conversions)]
bmp_buffer.extend(&[b, g, r, a]);
}
bmp_buffer
}
#[must_use]
#[cfg(feature = "std")]
pub fn create_ico(image: &Buffer) -> Vec<u8> {
let mut ico_buffer: Vec<u8> = Vec::new();
let width = image.width as u8;
let height = image.height as u8;
ico_buffer.extend(&[0x00, 0x00]); ico_buffer.extend(&[0x01, 0x00]); ico_buffer.extend(&[0x01, 0x00]);
ico_buffer.push(width); ico_buffer.push(height); ico_buffer.push(0); ico_buffer.push(0); ico_buffer.extend(&[0x00, 0x00]); ico_buffer.extend(&[0x20, 0x00]);
let image_data_offset = 6 + 16;
let row_stride = (u32::from(width) * 32).div_ceil(32) * 4;
let pixel_array_size = row_stride * u32::from(height);
let bmp_header_size = 40;
let and_mask_size = u32::from(height) * (u32::from(width).div_ceil(32) * 4);
let size_in_bytes = bmp_header_size + pixel_array_size + and_mask_size;
ico_buffer.extend(&size_in_bytes.to_le_bytes()); ico_buffer.extend(&(image_data_offset as u32).to_le_bytes());
let mut bmp_data: Vec<u8> = Vec::with_capacity(size_in_bytes as usize);
bmp_data.extend(&40u32.to_le_bytes()); bmp_data.extend(&i32::from(width).to_le_bytes()); bmp_data.extend(&(2 * i32::from(height)).to_le_bytes()); bmp_data.extend(&1u16.to_le_bytes()); bmp_data.extend(&32u16.to_le_bytes()); bmp_data.extend(&0u32.to_le_bytes()); bmp_data.extend(&0u32.to_le_bytes()); bmp_data.extend(&0u32.to_le_bytes()); bmp_data.extend(&0u32.to_le_bytes()); bmp_data.extend(&0u32.to_le_bytes()); bmp_data.extend(&0u32.to_le_bytes());
for pixel in &image.flip_vertically().data {
let (r, g, b, a) = u32_to_rgba_u8(*pixel);
#[allow(clippy::tuple_array_conversions)]
bmp_data.extend(&[b, g, r, a]);
}
bmp_data.extend(vec![0u8; and_mask_size as usize]);
ico_buffer.extend(bmp_data);
ico_buffer
}
#[must_use]
pub const fn reorder_color_range(
color_range: ((u32, u32), (u32, u32), (u32, u32)),
) -> ((u32, u32, u32), (u32, u32, u32)) {
(
(color_range.0 .0, color_range.1 .0, color_range.2 .0),
(color_range.0 .1, color_range.1 .1, color_range.2 .1),
)
}