mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 05:10:03 -04:00
Removes `egui_assert` etc and replaces it with normal `debug_assert` calls. Previously you could opt-in to more runtime checks using feature flags. Now these extra runtime checks are always enabled for debug builds. You are most likely to encounter them if you use negative sizes or NaNs or other similar bugs. These usually indicate bugs in user space.
278 lines
6.8 KiB
Rust
278 lines
6.8 KiB
Rust
use crate::{
|
|
gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_f32_from_linear_u8,
|
|
linear_u8_from_linear_f32,
|
|
};
|
|
|
|
/// 0-1 linear space `RGBA` color with premultiplied alpha.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
pub struct Rgba(pub(crate) [f32; 4]);
|
|
|
|
impl std::ops::Index<usize> for Rgba {
|
|
type Output = f32;
|
|
|
|
#[inline]
|
|
fn index(&self, index: usize) -> &f32 {
|
|
&self.0[index]
|
|
}
|
|
}
|
|
|
|
impl std::ops::IndexMut<usize> for Rgba {
|
|
#[inline]
|
|
fn index_mut(&mut self, index: usize) -> &mut f32 {
|
|
&mut self.0[index]
|
|
}
|
|
}
|
|
|
|
/// Deterministically hash an `f32`, treating all NANs as equal, and ignoring the sign of zero.
|
|
#[inline]
|
|
pub(crate) fn f32_hash<H: std::hash::Hasher>(state: &mut H, f: f32) {
|
|
if f == 0.0 {
|
|
state.write_u8(0);
|
|
} else if f.is_nan() {
|
|
state.write_u8(1);
|
|
} else {
|
|
use std::hash::Hash;
|
|
f.to_bits().hash(state);
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::derived_hash_with_manual_eq)]
|
|
impl std::hash::Hash for Rgba {
|
|
#[inline]
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
crate::f32_hash(state, self.0[0]);
|
|
crate::f32_hash(state, self.0[1]);
|
|
crate::f32_hash(state, self.0[2]);
|
|
crate::f32_hash(state, self.0[3]);
|
|
}
|
|
}
|
|
|
|
impl Rgba {
|
|
pub const TRANSPARENT: Self = Self::from_rgba_premultiplied(0.0, 0.0, 0.0, 0.0);
|
|
pub const BLACK: Self = Self::from_rgb(0.0, 0.0, 0.0);
|
|
pub const WHITE: Self = Self::from_rgb(1.0, 1.0, 1.0);
|
|
pub const RED: Self = Self::from_rgb(1.0, 0.0, 0.0);
|
|
pub const GREEN: Self = Self::from_rgb(0.0, 1.0, 0.0);
|
|
pub const BLUE: Self = Self::from_rgb(0.0, 0.0, 1.0);
|
|
|
|
#[inline]
|
|
pub const fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
|
|
Self([r, g, b, a])
|
|
}
|
|
|
|
#[inline]
|
|
pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
|
|
Self([r * a, g * a, b * a, a])
|
|
}
|
|
|
|
#[inline]
|
|
pub fn from_srgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
|
|
let r = linear_f32_from_gamma_u8(r);
|
|
let g = linear_f32_from_gamma_u8(g);
|
|
let b = linear_f32_from_gamma_u8(b);
|
|
let a = linear_f32_from_linear_u8(a);
|
|
Self::from_rgba_premultiplied(r, g, b, a)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn from_srgba_unmultiplied(r: u8, g: u8, b: u8, a: u8) -> Self {
|
|
let r = linear_f32_from_gamma_u8(r);
|
|
let g = linear_f32_from_gamma_u8(g);
|
|
let b = linear_f32_from_gamma_u8(b);
|
|
let a = linear_f32_from_linear_u8(a);
|
|
Self::from_rgba_premultiplied(r * a, g * a, b * a, a)
|
|
}
|
|
|
|
#[inline]
|
|
pub const fn from_rgb(r: f32, g: f32, b: f32) -> Self {
|
|
Self([r, g, b, 1.0])
|
|
}
|
|
|
|
#[inline]
|
|
pub const fn from_gray(l: f32) -> Self {
|
|
Self([l, l, l, 1.0])
|
|
}
|
|
|
|
#[inline]
|
|
pub fn from_luminance_alpha(l: f32, a: f32) -> Self {
|
|
debug_assert!(0.0 <= l && l <= 1.0);
|
|
debug_assert!(0.0 <= a && a <= 1.0);
|
|
Self([l * a, l * a, l * a, a])
|
|
}
|
|
|
|
/// Transparent black
|
|
#[inline]
|
|
pub fn from_black_alpha(a: f32) -> Self {
|
|
debug_assert!(0.0 <= a && a <= 1.0);
|
|
Self([0.0, 0.0, 0.0, a])
|
|
}
|
|
|
|
/// Transparent white
|
|
#[inline]
|
|
pub fn from_white_alpha(a: f32) -> Self {
|
|
debug_assert!(0.0 <= a && a <= 1.0, "a: {a}");
|
|
Self([a, a, a, a])
|
|
}
|
|
|
|
/// Return an additive version of this color (alpha = 0)
|
|
#[inline]
|
|
pub fn additive(self) -> Self {
|
|
let [r, g, b, _] = self.0;
|
|
Self([r, g, b, 0.0])
|
|
}
|
|
|
|
/// Is the alpha=0 ?
|
|
#[inline]
|
|
pub fn is_additive(self) -> bool {
|
|
self.a() == 0.0
|
|
}
|
|
|
|
/// Multiply with e.g. 0.5 to make us half transparent
|
|
#[inline]
|
|
pub fn multiply(self, alpha: f32) -> Self {
|
|
Self([
|
|
alpha * self[0],
|
|
alpha * self[1],
|
|
alpha * self[2],
|
|
alpha * self[3],
|
|
])
|
|
}
|
|
|
|
#[inline]
|
|
pub fn r(&self) -> f32 {
|
|
self.0[0]
|
|
}
|
|
|
|
#[inline]
|
|
pub fn g(&self) -> f32 {
|
|
self.0[1]
|
|
}
|
|
|
|
#[inline]
|
|
pub fn b(&self) -> f32 {
|
|
self.0[2]
|
|
}
|
|
|
|
#[inline]
|
|
pub fn a(&self) -> f32 {
|
|
self.0[3]
|
|
}
|
|
|
|
/// How perceptually intense (bright) is the color?
|
|
#[inline]
|
|
pub fn intensity(&self) -> f32 {
|
|
0.3 * self.r() + 0.59 * self.g() + 0.11 * self.b()
|
|
}
|
|
|
|
/// Returns an opaque version of self
|
|
#[inline]
|
|
pub fn to_opaque(&self) -> Self {
|
|
if self.a() == 0.0 {
|
|
// Additive or fully transparent black.
|
|
Self::from_rgb(self.r(), self.g(), self.b())
|
|
} else {
|
|
// un-multiply alpha:
|
|
Self::from_rgb(
|
|
self.r() / self.a(),
|
|
self.g() / self.a(),
|
|
self.b() / self.a(),
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Premultiplied RGBA
|
|
#[inline]
|
|
pub fn to_array(&self) -> [f32; 4] {
|
|
[self.r(), self.g(), self.b(), self.a()]
|
|
}
|
|
|
|
/// Premultiplied RGBA
|
|
#[inline]
|
|
pub fn to_tuple(&self) -> (f32, f32, f32, f32) {
|
|
(self.r(), self.g(), self.b(), self.a())
|
|
}
|
|
|
|
/// unmultiply the alpha
|
|
#[inline]
|
|
pub fn to_rgba_unmultiplied(&self) -> [f32; 4] {
|
|
let a = self.a();
|
|
if a == 0.0 {
|
|
// Additive, let's assume we are black
|
|
self.0
|
|
} else {
|
|
[self.r() / a, self.g() / a, self.b() / a, a]
|
|
}
|
|
}
|
|
|
|
/// unmultiply the alpha
|
|
#[inline]
|
|
pub fn to_srgba_unmultiplied(&self) -> [u8; 4] {
|
|
let [r, g, b, a] = self.to_rgba_unmultiplied();
|
|
[
|
|
gamma_u8_from_linear_f32(r),
|
|
gamma_u8_from_linear_f32(g),
|
|
gamma_u8_from_linear_f32(b),
|
|
linear_u8_from_linear_f32(a.abs()),
|
|
]
|
|
}
|
|
}
|
|
|
|
impl std::ops::Add for Rgba {
|
|
type Output = Self;
|
|
|
|
#[inline]
|
|
fn add(self, rhs: Self) -> Self {
|
|
Self([
|
|
self[0] + rhs[0],
|
|
self[1] + rhs[1],
|
|
self[2] + rhs[2],
|
|
self[3] + rhs[3],
|
|
])
|
|
}
|
|
}
|
|
|
|
impl std::ops::Mul for Rgba {
|
|
type Output = Self;
|
|
|
|
#[inline]
|
|
fn mul(self, other: Self) -> Self {
|
|
Self([
|
|
self[0] * other[0],
|
|
self[1] * other[1],
|
|
self[2] * other[2],
|
|
self[3] * other[3],
|
|
])
|
|
}
|
|
}
|
|
|
|
impl std::ops::Mul<f32> for Rgba {
|
|
type Output = Self;
|
|
|
|
#[inline]
|
|
fn mul(self, factor: f32) -> Self {
|
|
Self([
|
|
self[0] * factor,
|
|
self[1] * factor,
|
|
self[2] * factor,
|
|
self[3] * factor,
|
|
])
|
|
}
|
|
}
|
|
|
|
impl std::ops::Mul<Rgba> for f32 {
|
|
type Output = Rgba;
|
|
|
|
#[inline]
|
|
fn mul(self, rgba: Rgba) -> Rgba {
|
|
Rgba([
|
|
self * rgba[0],
|
|
self * rgba[1],
|
|
self * rgba[2],
|
|
self * rgba[3],
|
|
])
|
|
}
|
|
}
|