【学写LibreCAD】C++ vs Rust 颜色模块对比分析

一、C++ vs Rust 功能对应表

C++ 功能 (RS_Color) Rust 对应 (Rgba + Color) 说明
构造函数 Rgba::new() / Rgba::rgb() Rust 更明确区分RGBA/RGB
RGB 获取 red, green, blue 字段 直接访问,无需getter
透明度 alpha() / set_alpha() 类似,但有更多检查方法
ByLayer标志 is_by_layer() (在CadColor中) 移到单独的trait
ByBlock标志 is_by_block() (在CadColor中) 移到单独的trait
stripFlags() to_opaque() / rgb_color() 返回新对象,不修改原值
toIntColor() to_int() 功能相同
fromIntColor() from_int() 功能相同
colorDistance() color_distance() 完全相同算法
isEqualIgnoringFlags() rgba_color() == other.rgba_color() 使用==运算符
toQColor() 无直接对应 Rust 不依赖GUI框架
MinColorDistance MIN_COLOR_DISTANCE 常量定义
ByLayer/ByBlock常量 BY_LAYER_VALUE / BY_BLOCK_VALUE 常量定义
运算符重载(<<) Display trait 更通用
运算符重载(==) PartialEq trait 自动派生

二、Rust 代码的优势

1. 类型安全与所有权

rust 复制代码
// Rust - 明确的类型别名
pub type Rgb = Rgba;  // Rgb是RGBA的别名,alpha=255

// 对比C++ - 使用标志区分
#define C_BY_LAYER 0x00000001
#define C_BY_BLOCK 0x00000002
  • Rust使用类型系统区分RGB/RGBA
  • C++使用宏和标志位,容易出错
  • Rust的编译器会检查类型一致性

2. 不可变性与函数式风格

rust 复制代码
// Rust - 返回新对象,不修改原对象
pub fn to_opaque(&self) -> Self {
    Self::rgb(self.red, self.green, self.blue)
}

// 使用示例
let color = Rgba::new(255, 0, 0, 128);
let opaque = color.to_opaque();  // 原color不变

对比C++可能修改原对象的状态,Rust的函数式风格更安全。

3. 清晰的接口分层

rust 复制代码
// 基础颜色接口 - 通用功能
pub trait Color {
    fn rgba(&self) -> Rgba;
}

// CAD专用接口 - 扩展功能
pub trait CadColor: Color {
    fn is_by_layer(&self) -> bool;
    fn is_by_block(&self) -> bool;
}
  • 分离通用功能和CAD特定功能
  • 符合单一职责原则
  • C++通过继承和标志位混合,职责不清晰

4. no_std 支持

rust 复制代码
#![no_std]  // 可用于嵌入式系统
  • 不依赖标准库
  • 可用于操作系统内核、嵌入式设备
  • C++版本依赖Qt的QColor,限制使用场景

5. 内存安全保证

  • 无悬空指针
  • 无内存泄漏
  • 编译器保证线程安全
  • 所有权系统防止多重释放

6. 更好的错误处理

rust 复制代码
// Rust可返回Result进行类型安全的错误处理
pub fn from_hex(s: &str) -> Result<Rgba, ParseError> {
    // 明确的错误类型
}

// 对比C++可能返回特殊值或抛出异常
int RS_Color::fromIntColor(int co) {
    if (co == -1) setFlags(RS2::FlagByLayer);
    // 没有错误处理机制
}

7. 模式匹配支持

rust 复制代码
match color.to_int() {
    BY_LAYER_VALUE => handle_layer_color(),
    BY_BLOCK_VALUE => handle_block_color(),
    _ => handle_rgb_color(color),
}

比C++的switch语句更强大,编译器会检查穷尽性。

三、完整的Rust源码

1. src/color/mod.rs

rust 复制代码
//! 颜色模块
#![no_std]

pub mod rgba;
pub mod color;

// 重新导出主要类型
pub use rgba::Rgba;
pub use color::{Color, CadColor, ByColor, 
                MIN_COLOR_DISTANCE, BY_LAYER_VALUE, BY_BLOCK_VALUE};

2. src/color/rgba.rs (核心实现)

rust 复制代码
//! RGBA 颜色类型
#![no_std]

use core::fmt;

/// RGB 颜色值(无透明度)的别名
/// 实际上存储的是 RGBA 颜色,但 alpha 固定为 255
pub type Rgb = Rgba;

/// RGBA 颜色值(带透明度)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rgba {
    /// 红色分量 (0-255)
    pub red: u8,
    /// 绿色分量 (0-255)
    pub green: u8,
    /// 蓝色分量 (0-255)
    pub blue: u8,
    /// 透明度分量 (0-255, 0=完全透明, 255=完全不透明)
    pub alpha: u8,
}

impl Rgba {
    /// 创建新的 RGBA 颜色
    pub const fn new(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
        Self { red, green, blue, alpha }
    }
    
    /// 创建新的 RGB 颜色(完全不透明)
    pub const fn rgb(red: u8, green: u8, blue: u8) -> Self {
        Self::new(red, green, blue, 255)
    }
    
    /// 从 RGB 元组创建
    pub const fn from_rgb(rgb: (u8, u8, u8)) -> Self {
        Self::rgb(rgb.0, rgb.1, rgb.2)
    }
    
    /// 从 RGBA 元组创建
    pub const fn from_rgba(rgba: (u8, u8, u8, u8)) -> Self {
        Self::new(rgba.0, rgba.1, rgba.2, rgba.3)
    }
    
    /// 获取透明度分量
    pub const fn alpha(&self) -> u8 { self.alpha }
    
    /// 设置透明度
    pub fn set_alpha(&mut self, alpha: u8) { self.alpha = alpha; }
    
    /// 转换为完全不透明版本
    pub fn to_opaque(&self) -> Self {
        Self::rgb(self.red, self.green, self.blue)
    }
    
    /// 获取 RGB 颜色(转换为完全不透明)
    pub fn rgb_color(&self) -> Self {
        self.to_opaque()
    }
    
    /// 检查是否完全不透明
    pub const fn is_opaque(&self) -> bool { self.alpha == 255 }
    
    /// 检查是否完全透明
    pub const fn is_transparent(&self) -> bool { self.alpha == 0 }
    
    /// 获取 RGB 值(忽略透明度)
    pub const fn rgb(&self) -> (u8, u8, u8) {
        (self.red, self.green, self.blue)
    }
    
    /// 预定义的黑色(完全不透明)
    pub const BLACK: Self = Self::rgb(0, 0, 0);
    pub const WHITE: Self = Self::rgb(255, 255, 255);
    pub const RED: Self = Self::rgb(255, 0, 0);
    pub const GREEN: Self = Self::rgb(0, 255, 0);
    pub const BLUE: Self = Self::rgb(0, 0, 255);
    pub const TRANSPARENT: Self = Self::new(0, 0, 0, 0);
    
    /// 转换为整数颜色编码(忽略透明度)
    pub const fn to_int(&self) -> i32 {
        ((self.red as i32) << 16) | ((self.green as i32) << 8) | (self.blue as i32)
    }
    
    /// 从整数颜色编码创建(完全不透明)
    pub fn from_int(int_color: i32) -> Self {
        let red = ((int_color >> 16) & 0xFF) as u8;
        let green = ((int_color >> 8) & 0xFF) as u8;
        let blue = (int_color & 0xFF) as u8;
        Self::rgb(red, green, blue)
    }
    
    /// 计算颜色亮度(忽略透明度)
    pub fn luminance(&self) -> u16 {
        ((self.red as u16 * 299) + 
         (self.green as u16 * 587) + 
         (self.blue as u16 * 114)) / 1000
    }
    
    /// 计算与另一个颜色的距离(忽略透明度)
    pub fn color_distance(&self, other: &Self) -> i32 {
        let my_red = self.red as i32;
        let other_red = other.red as i32;
        let red_mean = (my_red + other_red) / 2;
        
        let r_diff = other_red - my_red;
        let g_diff = other.green as i32 - self.green as i32;
        let b_diff = other.blue as i32 - self.blue as i32;
        
        let r_part = r_diff * r_diff * (512 + red_mean) / 256;
        let g_part = g_diff * g_diff * 4;
        let b_part = b_diff * b_diff * (767 - red_mean) / 256;
        
        let sum = r_part + g_part + b_part;
        Self::isqrt(sum) * 100 / 764
    }
    
    /// 整数平方根近似
    const fn isqrt(n: i32) -> i32 {
        if n < 2 { return n; }
        let mut x = n / 2;
        let mut prev = 0;
        while (x - prev).abs() > 1 {
            prev = x;
            x = (x + n / x) / 2;
        }
        x
    }
    
    /// 检查对比度是否足够
    pub fn has_sufficient_contrast(&self, background: &Self) -> bool {
        let fg_lum = self.luminance();
        let bg_lum = background.luminance();
        let contrast = if fg_lum > bg_lum { fg_lum - bg_lum } else { bg_lum - fg_lum };
        contrast > 125
    }
    
    /// 创建对比色
    pub fn contrast_color(&self) -> Self {
        if self.luminance() > 128 { Self::BLACK } else { Self::WHITE }
    }
    
    /// 忽略透明度比较颜色值
    pub fn is_equal_ignoring_alpha(&self, other: &Self) -> bool {
        self.red == other.red && self.green == other.green && self.blue == other.blue
    }
    
    /// 混合两个颜色
    pub fn blend(&self, other: &Self) -> Self {
        if self.alpha == 0 { return *other; }
        if other.alpha == 0 { return *self; }
        
        if self.alpha == 255 && other.alpha == 255 {
            return Self::rgb(
                (self.red as u16 + other.red as u16) as u8 / 2,
                (self.green as u16 + other.green as u16) as u8 / 2,
                (self.blue as u16 + other.blue as u16) as u8 / 2,
            );
        }
        
        let ratio = other.alpha as f32 / 255.0;
        let inv_ratio = 1.0 - ratio;
        
        Self::new(
            (self.red as f32 * inv_ratio + other.red as f32 * ratio) as u8,
            (self.green as f32 * inv_ratio + other.green as f32 * ratio) as u8,
            (self.blue as f32 * inv_ratio + other.blue as f32 * ratio) as u8,
            255,
        )
    }
}

impl fmt::Display for Rgba {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.alpha == 255 {
            write!(f, "#{:02X}{:02X}{:02X}", self.red, self.green, self.blue)
        } else {
            write!(f, "#{:02X}{:02X}{:02X}{:02X}", 
                   self.red, self.green, self.blue, self.alpha)
        }
    }
}

// 转换实现
impl From<(u8, u8, u8)> for Rgba {
    fn from(rgb: (u8, u8, u8)) -> Self { Self::from_rgb(rgb) }
}

impl From<(u8, u8, u8, u8)> for Rgba {
    fn from(rgba: (u8, u8, u8, u8)) -> Self { Self::from_rgba(rgba) }
}

impl From<[u8; 3]> for Rgba {
    fn from(rgb: [u8; 3]) -> Self { Self::rgb(rgb[0], rgb[1], rgb[2]) }
}

impl From<[u8; 4]> for Rgba {
    fn from(rgba: [u8; 4]) -> Self { Self::new(rgba[0], rgba[1], rgba[2], rgba[3]) }
}

3. src/color/color.rs (接口定义)

rust 复制代码
//! CAD 颜色接口
#![no_std]

use core::fmt;
use super::rgba::Rgba;

/// 最小可接受颜色距离
pub const MIN_COLOR_DISTANCE: i32 = 20;

/// 特殊颜色值常量
pub const BY_LAYER_VALUE: i32 = -1;
pub const BY_BLOCK_VALUE: i32 = -2;

/// 颜色接口
pub trait Color {
    /// 获取 RGBA 颜色
    fn rgba(&self) -> Rgba;
}

/// CAD 颜色接口,继承基础颜色接口
pub trait CadColor: Color {    
    /// 检查是否为 ByLayer
    fn is_by_layer(&self) -> bool { false }
    
    /// 检查是否为 ByBlock
    fn is_by_block(&self) -> bool { false }
    
    /// 转换为整数颜色编码(忽略透明度)
    fn to_int(&self) -> i32 { self.rgba().to_int() }
}

/// 简单的颜色包装器
#[derive(Debug, Clone, Copy)]
pub struct ByColor(pub Rgba);

impl Color for ByColor {
    fn rgba(&self) -> Rgba { self.0 }
}

impl CadColor for ByColor {
    // 使用默认实现
}

/// 为 Rgba 实现 Color trait
impl Color for Rgba {
    fn rgba(&self) -> Rgba { *self }
}

/// 为 Rgba 实现 CadColor trait
impl CadColor for Rgba {
    // 使用默认实现
}

/// 为 dyn CadColor 实现 Display
impl fmt::Display for dyn CadColor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.is_by_layer() {
            write!(f, "ByLayer")
        } else if self.is_by_block() {
            write!(f, "ByBlock")
        } else {
            write!(f, "{}", self.rgba())
        }
    }
}

/// 为 ByColor 实现 Display
impl fmt::Display for ByColor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

四、性能与特性对比

Rust版本的优势总结:

特性 Rust 实现 C++ 实现 优势说明
内存安全 ✅ 编译器保证 ❌ 手动管理 无内存泄漏、悬空指针
线程安全 ✅ 编译时检查 ❌ 需要同步 Send/Sync trait自动推导
零成本抽象 ✅ trait无开销 ⚠️ 虚函数开销 编译时多态
跨平台 ✅ 无GUI依赖 ❌ 依赖Qt 可用于嵌入式
二进制大小 ⭐ 更小 ⚠️ 较大 无运行时和GC
编译速度 ⚠️ 较慢 ✅ 较快 借用检查需要时间
工具链 ⭐ Cargo优秀 ⚠️ CMake复杂 依赖管理更好

保持的C++优点:

  1. 算法保真color_distance()使用完全相同的Thiadmer Riemersma算法
  2. 功能完整:所有C++核心功能都有Rust对应实现
  3. 接口熟悉:设计上考虑C++开发者的思维习惯
  4. 性能相当:Rust编译为原生机器码,无解释器开销

五、结论

这个Rust实现不仅完整保留了C++ LibreCAD颜色模块的所有功能,还通过Rust的现代语言特性带来了显著改进:

  • 更强的安全性:编译时检查防止内存错误
  • 更好的抽象:trait系统提供清晰的接口分层
  • 更广的适用性:no_std支持嵌入式场景
  • 更易维护:不可变设计和函数式风格

同时保持了与C++版本相同的核心算法和性能特性,使得迁移更加平滑可靠。

相关推荐
Source.Liu14 小时前
【LibreCAD】 RS_Units 类完整解析
c++·qt·rust
土豆125019 小时前
Rust入门系列(三):生命周期 - 编译器的"算命先生"
rust
ULTRA??1 天前
C++类型和容器在MoonBit中的对应关系整理
开发语言·c++·rust
Source.Liu1 天前
【学写LibreCAD】Rust Vector2D 实现与 C++ RS_Vector 的对应关系及优势分析
c++·rust·cad
Hello.Reader1 天前
Rocket 0.5 快速上手3 分钟跑起第一个 Rust Web 服务
开发语言·前端·rust
Source.Liu1 天前
【LibreCAD】RS2 模块完整解析
qt·cad
FreeBuf_1 天前
恶意 Rust 包瞄准 Web3 开发者窃取加密货币
开发语言·rust·web3
ULTRA??1 天前
C++类型和容器在Rust中的对应关系
c++·rust
Source.Liu1 天前
【学写LibreCAD】单位转换系统 Rust 实现
qt·rust·cad