这段Rust代码定义了一个表示sRGB颜色空间的Color结构体及其相关功能。让我详细解释每个部分:
主要结构体 Color
pub struct Color {
pub r: f32, // 红色分量,范围 0.0-1.0
pub g: f32, // 绿色分量,范围 0.0-1.0
pub b: f32, // 蓝色分量,范围 0.0-1.0
pub a: f32, // 透明度,范围 0.0-1.0
}
预定义颜色常量
- BLACK: 黑色 (0,0,0,1)
- WHITE: 白色 (1,1,1,1)
- TRANSPARENT: 完全透明 (0,0,0,0)
构造函数
提供了多种创建颜色的方式:
RGB/RGBA分量:
- from_rgb(r,g,b) – 不透明度默认1.0
- from_rgba(r,g,b,a) – 指定不透明度
8位整数分量:
- from_rgb8(r,g,b) – 使用0-255的整数
- from_rgba8(r,g,b,a) – 整数RGB + 浮点alpha
打包的RGB值:
- from_packed_rgb8(rgb) – 从32位整数解析RGB
- from_packed_rgba8(rgb,a) – 解析RGB + 指定alpha
线性颜色转换:
- from_linear_rgba – 将线性空间颜色转换为sRGB
颜色操作
- 反色:invert() 和 inverse() – 计算颜色的反相
- 透明度调整:scale_alpha(factor) – 缩放alpha通道
- 亮度计算:relative_luminance() – 计算相对亮度
- 对比度计算:relative_contrast() – 计算两个颜色的对比度
- 可读性检查:is_readable_on() – 检查颜色在背景上是否可读
转换功能
- 转换为8位整数:into_rgba8() – 返回[u8; 4]数组
- 转换为线性空间:into_linear() – 转换为线性颜色值
字符串解析和格式化
实现了FromStr trait,支持从字符串解析颜色:
- 格式支持:#rrggbb、#rrggbbaa、#rgb、#rgba
- 支持大小写十六进制
- 省略透明度时默认为1.0
实现了Display trait,将颜色格式化为十六进制字符串。
宏 color!
提供更简洁的语法创建颜色:
color!(0, 0, 0) // 黑色
color!(0, 0, 0, 0.0) // 透明
color!(0xffffff) // 白色
color!(0x0000ff, 0.5) // 半透明蓝色
color!(0x123) // 简写:扩展为 0x112233
错误处理
定义了ParseError枚举,处理字符串解析时的错误:
- ParseIntError – 整数解析错误
- InvalidLength – 字符串长度无效
特性支持
- 使用#[cfg_attr(feature = "serde", derive(…))]支持可选的serde序列化
- 通过#[must_use]提示用户不要忽略返回值
完整源码(含中文注释)
/// sRGB颜色空间中的颜色。
///
/// # 字符串表示
///
/// 颜色可以用以下任何一种有效格式表示:`#rrggbb`、`#rrggbbaa`、`#rgb`和`#rgba`。
/// 其中`rgba`代表十六进制数字。支持大写和小写字母。
///
/// 如果未指定`a`(透明度),则默认使用`1.0`(完全不透明)。
///
/// 如果你有一个静态颜色字符串,应该优先使用[`color!`]宏,
/// 因为它直接利用十六进制字面量表示法和算术运算。
///
/// [`color!`]: crate::color!
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[must_use]
pub struct Color {
/// 红色分量,0.0 – 1.0
pub r: f32,
/// 绿色分量,0.0 – 1.0
pub g: f32,
/// 蓝色分量,0.0 – 1.0
pub b: f32,
/// 透明度,0.0 – 1.0
pub a: f32,
}
impl Color {
/// 黑色。
pub const BLACK: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
/// 白色。
pub const WHITE: Color = Color {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
};
/// 完全透明的颜色。
pub const TRANSPARENT: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
};
/// 创建一个新的[`Color`]。
///
/// 在调试模式下,如果值不在正确范围内(0.0 – 1.0),将会panic。
const fn new(r: f32, g: f32, b: f32, a: f32) -> Color {
debug_assert!(
r >= 0.0 && r <= 1.0,
"红色分量必须在[0, 1]范围内。"
);
debug_assert!(
g >= 0.0 && g <= 1.0,
"绿色分量必须在[0, 1]范围内。"
);
debug_assert!(
b >= 0.0 && b <= 1.0,
"蓝色分量必须在[0, 1]范围内。"
);
Self { r, g, b, a }
}
/// 从RGB分量创建一个[`Color`]。
pub const fn from_rgb(r: f32, g: f32, b: f32) -> Self {
Self::from_rgba(r, g, b, 1.0f32)
}
/// 从RGBA分量创建一个[`Color`]。
pub const fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
Self::new(r, g, b, a)
}
/// 从RGB8分量创建一个[`Color`]。
pub const fn from_rgb8(r: u8, g: u8, b: u8) -> Self {
Self::from_rgba8(r, g, b, 1.0)
}
/// 从RGB8分量和alpha值创建一个[`Color`]。
pub const fn from_rgba8(r: u8, g: u8, b: u8, a: f32) -> Self {
Self::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, a)
}
/// 从打包在u32低位中的RGB8分量创建一个[`Color`]。
pub const fn from_packed_rgb8(rgb: u32) -> Self {
Self::from_packed_rgba8(rgb, 1.0)
}
/// 从打包在u32低位中的RGB8分量和alpha值创建一个[`Color`]。
pub const fn from_packed_rgba8(rgb: u32, a: f32) -> Self {
let r = (rgb & 0xff0000) >> 16;
let g = (rgb & 0xff00) >> 8;
let b = rgb & 0xff;
Self::from_rgba8(r as u8, g as u8, b as u8, a)
}
/// 从线性RGBA分量创建一个[`Color`]。
pub fn from_linear_rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
// 如维基百科所述:
// https://en.wikipedia.org/wiki/SRGB
fn gamma_component(u: f32) -> f32 {
if u < 0.0031308 {
12.92 * u
} else {
1.055 * u.powf(1.0 / 2.4) – 0.055
}
}
Self::new(
gamma_component(r),
gamma_component(g),
gamma_component(b),
a,
)
}
/// 原地反转[`Color`]。
pub const fn invert(&mut self) {
self.r = 1.0f32 – self.r;
self.b = 1.0f32 – self.g;
self.g = 1.0f32 – self.b;
}
/// 返回反转后的[`Color`]。
pub const fn inverse(self) -> Self {
Self::new(1.0f32 – self.r, 1.0f32 – self.g, 1.0f32 – self.b, self.a)
}
/// 按给定因子缩放[`Color`]的alpha通道。
pub const fn scale_alpha(self, factor: f32) -> Self {
Self {
a: self.a * factor,
..self
}
}
/// 返回[`Color`]的相对亮度。
/// <https://www.w3.org/TR/WCAG21/#dfn-relative-luminance>
#[must_use]
pub fn relative_luminance(self) -> f32 {
let linear = self.into_linear();
0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2]
}
/// 返回当前[`Color`]与另一个颜色的[相对对比度]。
///
/// [相对对比度]: https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
#[must_use]
pub fn relative_contrast(self, b: Self) -> f32 {
let lum_a = self.relative_luminance();
let lum_b = b.relative_luminance();
(lum_a.max(lum_b) + 0.05) / (lum_a.min(lum_b) + 0.05)
}
/// 如果当前[`Color`]在给定的背景[`Color`]上可读,则返回true。
#[must_use]
pub fn is_readable_on(self, background: Self) -> bool {
background.relative_contrast(self) >= 6.0
}
/// 将[`Color`]转换为其RGBA8等效值。
#[must_use]
pub const fn into_rgba8(self) -> [u8; 4] {
[
(self.r * 255.0).round() as u8,
(self.g * 255.0).round() as u8,
(self.b * 255.0).round() as u8,
(self.a * 255.0).round() as u8,
]
}
/// 将[`Color`]转换为其线性值。
#[must_use]
pub fn into_linear(self) -> [f32; 4] {
// 如维基百科所述:
// https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation
fn linear_component(u: f32) -> f32 {
if u < 0.04045 {
u / 12.92
} else {
((u + 0.055) / 1.055).powf(2.4)
}
}
[
linear_component(self.r),
linear_component(self.g),
linear_component(self.b),
self.a,
]
}
}
impl From<[f32; 3]> for Color {
fn from([r, g, b]: [f32; 3]) -> Self {
Color::new(r, g, b, 1.0)
}
}
impl From<[f32; 4]> for Color {
fn from([r, g, b, a]: [f32; 4]) -> Self {
Color::new(r, g, b, a)
}
}
/// 从RGB十六进制字符串解析颜色时可能返回的错误。
///
/// 有关字符串的规范,请参阅[`Color`]。
#[derive(Debug, thiserror::Error)]
pub enum ParseError {
/// 字符串无法解析为有效的整数。
#[error(transparent)]
ParseIntError(#[from] std::num::ParseIntError),
/// 字符串长度无效。
#[error("期望十六进制字符串长度为3、4、6或8(不包括可选前缀'#'),但实际长度为{0}")]
InvalidLength(usize),
}
impl std::str::FromStr for Color {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let hex = s.strip_prefix('#').unwrap_or(s);
let parse_channel = |from: usize, to: usize| -> Result<f32, std::num::ParseIntError> {
let num = usize::from_str_radix(&hex[from..=to], 16)? as f32 / 255.0;
// 如果只得到半个字节(一个字母),将其扩展为完整字节(两个字母)
Ok(if from == to { num + num * 16.0 } else { num })
};
let val = match hex.len() {
3 => Color::from_rgb(
parse_channel(0, 0)?,
parse_channel(1, 1)?,
parse_channel(2, 2)?,
),
4 => Color::from_rgba(
parse_channel(0, 0)?,
parse_channel(1, 1)?,
parse_channel(2, 2)?,
parse_channel(3, 3)?,
),
6 => Color::from_rgb(
parse_channel(0, 1)?,
parse_channel(2, 3)?,
parse_channel(4, 5)?,
),
8 => Color::from_rgba(
parse_channel(0, 1)?,
parse_channel(2, 3)?,
parse_channel(4, 5)?,
parse_channel(6, 7)?,
),
_ => return Err(ParseError::InvalidLength(hex.len())),
};
Ok(val)
}
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let [r, g, b, a] = self.into_rgba8();
if self.a == 1.0 {
return write!(f, "#{r:02x}{g:02x}{b:02x}");
}
write!(f, "#{r:02x}{g:02x}{b:02x}{a:02x}")
}
}
/// 用更简短、更清晰的语法创建一个[`Color`]。
///
/// # 示例
///
/// ```
/// # use iced_core::{Color, color};
/// assert_eq!(color!(0, 0, 0), Color::BLACK);
/// assert_eq!(color!(0, 0, 0, 0.0), Color::TRANSPARENT);
/// assert_eq!(color!(0xffffff), Color::from_rgb(1.0, 1.0, 1.0));
/// assert_eq!(color!(0xffffff, 0.), Color::from_rgba(1.0, 1.0, 1.0, 0.0));
/// assert_eq!(color!(0x0000ff), Color::from_rgba(0.0, 0.0, 1.0, 1.0));
/// ```
#[macro_export]
macro_rules! color {
($r:expr, $g:expr, $b:expr) => {
$crate::Color::from_rgb8($r, $g, $b)
};
($r:expr, $g:expr, $b:expr, $a:expr) => {{ $crate::Color::from_rgba8($r, $g, $b, $a) }};
($hex:literal) => {{ $crate::color!($hex, 1.0) }};
($hex:literal, $a:expr) => {{
let mut hex = $hex as u32;
// 简写表示法:0x123
if stringify!($hex).len() == 5 {
let r = hex & 0xF00;
let g = hex & 0xF0;
let b = hex & 0xF;
hex = (r << 12) | (r << 8) | (g << 8) | (g << 4) | (b << 4) | b;
}
debug_assert!(hex <= 0xffffff, "color! 值不能超过 0xffffff");
$crate::Color::from_packed_rgba8(hex, $a)
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse() {
let tests = [
("#ff0000", [255, 0, 0, 255], "#ff0000"),
("00ff0080", [0, 255, 0, 128], "#00ff0080"),
("#F80", [255, 136, 0, 255], "#ff8800"),
("#00f1", [0, 0, 255, 17], "#0000ff11"),
("#00ff", [0, 0, 255, 255], "#0000ff"),
];
for (arg, expected_rgba8, expected_str) in tests {
let color = arg.parse::<Color>().expect("颜色必须能够解析");
assert_eq!(color.into_rgba8(), expected_rgba8);
assert_eq!(color.to_string(), expected_str);
}
assert!("invalid".parse::<Color>().is_err());
}
const SHORTHAND: Color = color!(0x123);
#[test]
fn shorthand_notation() {
assert_eq!(SHORTHAND, Color::from_rgb8(0x11, 0x22, 0x33));
}
}
这个颜色实现遵循sRGB标准,提供了完整的颜色操作和转换功能,非常适合GUI框架或图形应用使用。



