【学Rust写CAD】24 扫描渐变(sweep_gradient.rs)

源码

rust 复制代码
// src/color/sweep_gradient.rs
use crate::fixed::Fixed;

/// 用于处理扫描渐变的数据结构
pub struct SweepGradientSource {
    /// 固定点矩阵,用于图形变换
    pub matrix: Matrix2D<Fixed>,
    /// 时间/渐变参数的偏置调整
    pub t_bias: f32,
    /// 时间/渐变参数的缩放因子
    pub t_scale: f32,
    /// 颜色查找表(256个32位颜色值)
    pub lut: [u32; 256],
}

impl SweepGradientSource {
    // This implementation is taken from Skia
    pub fn eval<S:Spread>(&self, x: u16, y: u16, spread: Spread) -> u32 {
        let p = self.matrix.transform(x, y);
        // XXX: this is slow and bad
        // the derivation is from pixman radial_get_scanline_narrow
        // " Mathematically the gradient can be defined as the family of circles
        //
        //    ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
        //
        // excluding those circles whose radius would be < 0."
        // i.e. anywhere where r < 0 we return 0 (transparent black).
        let px = p.x as f32 / 65536.;
        let py = p.y as f32 / 65536.;

        let xabs = px.abs();
        let yabs = py.abs();

        let slope = xabs.min(yabs)/xabs.max(yabs);
        let s = slope * slope;

        // Use a 7th degree polynomial to approximate atan.
        // This was generated using sollya.gforge.inria.fr.
        // A float optimized polynomial was generated using the following command.
        // P1 = fpminimax((1/(2*Pi))*atan(x),[|1,3,5,7|],[|24...|],[2^(-40),1],relative);
        let mut phi = slope
                * (0.15912117063999176025390625     + s
                * (-5.185396969318389892578125e-2   + s
                * (2.476101927459239959716796875e-2 + s
                * (-7.0547382347285747528076171875e-3))));

        if xabs < yabs {
            phi = 1.0/4.0 - phi;
        }
        if px < 0.0 {
            phi = 1.0/2.0 - phi;
        }
        if py < 0.0 {
            phi = 1.0 - phi;
        }
        if phi != phi {  // Check for NaN
            phi = 0.0;
        }
        let r = phi;

        let t = r * self.t_scale - self.t_bias;

        let result = self.lut[S::spread.apply((t * 255.) as i32, spread) as usize];
        result
    }
}

代码分析

这段代码实现了一个扫描渐变(Sweep Gradient)的数据结构和计算方法。扫描渐变是一种颜色沿着圆周方向渐变的着色效果。

数据结构
rust 复制代码
pub struct SweepGradientSource {
    pub matrix: Matrix2D<Fixed>,  // 用于图形变换的固定点矩阵
    pub t_bias: f32,              // 时间/渐变参数的偏置调整
    pub t_scale: f32,             // 时间/渐变参数的缩放因子
    pub lut: [u32; 256],          // 颜色查找表(256个32位颜色值)
}
主要方法 eval

eval 方法计算在给定坐标 (x,y) 处的颜色值:

  1. 坐标变换:
  • 使用矩阵变换将输入坐标 (x,y) 转换为新的坐标 p
  1. 计算角度:
  • 将坐标转换为浮点数 (px, py)

  • 计算绝对值和斜率

  • 使用7次多项式近似计算反正切函数(atan),这是为了性能优化

  1. 角度调整:
  • 根据坐标所在象限调整角度值 phi

  • 处理 NaN 情况

  1. 渐变参数计算:
  • 计算渐变参数 t = r * t_scale - t_bias

  • 使用 spread 方法处理超出范围的 t 值

  • 从颜色查找表(LUT)中获取最终颜色

技术细节

  • 该实现参考了Skia图形库的实现

  • 使用多项式近似代替精确的三角函数计算以提高性能

  • 颜色查找表(LUT)有256个条目,对应256种可能的渐变位置

  • Spread 类型参数控制如何处理超出范围的渐变参数

数学原理

渐变可以定义为一系列圆的族 ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂),排除那些半径小于0的圆。

这段代码实现了扫描渐变效果,它通过计算每个像素相对于中心点的角度来确定其在渐变中的位置,然后从预计算的颜色查找表中获取对应的颜色值。

相关推荐
Asthenia04126 分钟前
如何设计实现一个定时任务执行器 - SpringBoot环境下的最佳实践
后端
兔子的洋葱圈25 分钟前
【django】1-2 django项目的请求处理流程(详细)
后端·python·django
Asthenia041233 分钟前
如何为这条sql语句建立索引:select * from table where x = 1 and y < 1 order by z;
后端
ihgry35 分钟前
SpringBoot+Mybatis实现Mysql分表
后端
Asthenia041236 分钟前
令牌桶算法与惰性机制的应用
后端
lamdaxu38 分钟前
02Tomcat 线程模型详解&性能调优
后端
lamdaxu41 分钟前
03Tomcat类加载机制&热加载和热部署
后端
程序猿chen1 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算
Asthenia04121 小时前
Pandas全面操作指南与电商销售数据分析
后端