You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于浮点数距离计算的绘图应用笔触渲染性能与精度问题

绘图应用浮点数计算性能与精度优化方案

针对你的两个技术问题,直接给出优化方案:

1. 浮点数距离计算下提升渲染性能

  • 砍掉图层创建开销:你当前每次绘制都新建CALayer,这是性能暴跌的核心原因——大量独立图层会导致GPU过度绘制、内存占用飙升。改用Core Graphics直接在画布批量绘制,或者预渲染brush到上下文,减少GPU指令调度成本。
  • 简化距离计算:isAdd函数里的类型转换可以提前完成,避免循环中重复转CGFloat;同时把空间阈值提前转为浮点数,减少函数调用时的运算开销。
  • 优化插值逻辑:当前interpolatePoints里的整数转换会丢失浮点数精度,还产生冗余点。直接基于浮点数插值,跳过重复坐标判断,减少数组元素数量。
  • 严格控制绘制范围:利用loopIndex只处理新增的点,不要每次遍历整个数组(你已经在做,但可以更严格地限制绘制区间)。

2. 不牺牲性能保证定位精准的替代方案

  • Core Graphics批量绘制:放弃逐个添加CALayer,改用CGContext一次性绘制所有brush点,GPU批量处理渲染指令,性能远高于多个独立图层。
  • 整数运算兼容方案:如果必须保留浮点数精度,可以将所有坐标乘以固定倍数(比如100)转为整数运算,最后再除以倍数还原,既避免浮点数精度误差,又降低运算开销。
  • 预计算缓存:提前缓存不同大小、角度、透明度的brush图像,绘制时直接取用,避免实时计算transform和opacity。
  • Metal加速(高阶方案):对性能要求极高时,用Metal直接操作GPU,自定义渲染管线,比Core Graphics和CALayer效率更高,但实现成本稍高。

核心代码优化示例

优化isAdd距离判断函数

// 提前把space转为CGFloat,避免每次函数调用时的类型转换
func isAdd(p: CGPoint, space: CGFloat) -> Bool {
    guard let previousPoint = previousPoint else { return true }
    let dx = abs(previousPoint.x - p.x)
    let dy = abs(previousPoint.y - p.y)
    return max(dx, dy) >= space
}

调用时直接传入CGFloat(getSpace()),减少类型转换开销。

优化插值逻辑,保留浮点数精度

func interpolatePoints(from array: [CGPoint]) -> [CGPoint] {
    var result: [CGPoint] = []
    guard array.count >= 2 else { return array }
    
    for i in 0..<array.count-1 {
        let prev = array[i]
        let curr = array[i+1]
        let dx = curr.x - prev.x
        let dy = curr.y - prev.y
        let distance = hypot(dx, dy)
        guard distance > 0 else { continue }
        
        // 根据间隔计算需要插入的点数,直接生成精准浮点数坐标
        let stepCount = max(1, Int(distance / CGFloat(getSpace())))
        let stepX = dx / CGFloat(stepCount)
        let stepY = dy / CGFloat(stepCount)
        
        for j in 0...stepCount {
            let x = prev.x + stepX * CGFloat(j)
            let y = prev.y + stepY * CGFloat(j)
            result.append(CGPoint(x: x, y: y))
        }
    }
    return result
}

改用Core Graphics批量绘制(替代CALayer方案)

override func draw(_ rect: CGRect) {
    super.draw(rect)
    guard let context = UIGraphicsGetCurrentContext(), pts2.count > 0 else { return }
    
    context.saveGState()
    context.setBlendMode(blendMode)
    
    let brushSize = shapeSize
    let halfWidth = brushSize.width / 2
    let halfHeight = brushSize.height / 2
    
    for idx in loopIndex..<pts2.count {
        let point = pts2[idx]
        guard isAdd(p: point, space: CGFloat(getSpace())) else { continue }
        
        previousPoint = point
        
        // 计算当前brush的缩放和透明度
        let maxScale: CGFloat = 1.0
        let s = 1.0 - CGFloat(dynamicScale)
        let scale = min(s + CGFloat(idx)/400.0, maxScale)
        
        let minOpacity = CGFloat(1.0 - dynamicOpacity)
        let opacity = min(minOpacity + CGFloat(idx)/300.0, brushAlpha)
        
        // 绘制brush
        context.saveGState()
        context.translateBy(x: point.x, y: point.y)
        context.scaleBy(x: scale, y: scale)
        // 转换角度为弧度
        let angle = self.angle ? angles.randomElement()! : CGFloat(degree)
        context.rotate(by: angle * .pi / 180)
        context.setAlpha(opacity)
        
        shape.draw(in: CGRect(x: -halfWidth, y: -halfHeight, width: brushSize.width, height: brushSize.height))
        
        context.restoreGState()
        
        loopIndex = idx
    }
    
    context.restoreGState()
}

内容的提问来源于stack exchange,提问作者Rezwan Khan chowdhury

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 23:22:11