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

UIImage旋转后边缘出白边,多次旋转白边扩大问题求助

UIImage旋转后边缘白边及渲染性能问题

问题现象

开发图片编辑器时,点击旋转按钮调用rotateLeft函数旋转图片,多次旋转(需等待前一次完成)后,图片边缘的白边会逐渐变大。

旋转后白边效果

现有旋转实现代码

最初使用UIGraphicsBeginImageContext实现旋转:

func rotateLeft() -> UIImage? {
    let radians = CGFloat.rotateRadians
    
    // Calculate the size of the rotated image
    var rotatedSize = CGRect(origin: .zero, size: size)
        .applying(CGAffineTransform(rotationAngle: radians))
        .integral.size

    // Round the dimensions to the nearest whole number
    rotatedSize.width = round(rotatedSize.width)
    rotatedSize.height = round(rotatedSize.height)

    // Create a context for the rotated image
    UIGraphicsBeginImageContext(rotatedSize)
    defer { UIGraphicsEndImageContext() }

    if let context = UIGraphicsGetCurrentContext() {
        // Move the origin to the center of the image
        context.translateBy(x: rotatedSize.width / 2.0, y: rotatedSize.height / 2.0)
        
        // Rotate the context
        context.rotate(by: radians)
        
        // Draw the image
        draw(in: CGRect(
            x: -size.width / 2.0,
            y: -size.height / 2.0,
            width: size.width,
            height: size.height)
        )
        
        // Get the rotated image from the context
        if let rotatedImage = UIGraphicsGetImageFromCurrentImageContext() {
            return rotatedImage
        }
    }
    
    return nil
}

白边问题原因分析

  1. 误差累积:每次旋转都基于上一次旋转后的图片(已带有边缘误差)进行操作,导致白边逐次放大。
  2. 坐标与尺寸计算的浮点精度问题:使用applying(CGAffineTransform)计算旋转后的尺寸,会产生浮点数值,即使做了round处理,仍会导致绘制时的像素对齐偏差,边缘出现空白或模糊。
  3. 上下文设置不严谨:UIGraphicsBeginImageContext默认使用屏幕缩放因子,若原图片的缩放因子与屏幕不一致,绘制时会出现插值误差,多次叠加后形成明显白边。

优化方案

1. 基于原始图片旋转(核心解决累积问题)

不要每次用旋转后的图片作为下一次旋转的输入,始终保留原始图片的引用,旋转时直接从原始图片生成新的旋转结果。

2. 修正旋转尺寸计算与上下文设置

对于90度/270度这类正交旋转,直接互换宽高即可得到精确尺寸,无需通过变换计算。同时使用UIGraphicsBeginImageContextWithOptions指定正确的缩放因子和不透明属性:

func rotateLeft() -> UIImage? {
    let radians = CGFloat.rotateRadians // 应为-π/2(左旋转90度)
    // 90度旋转后尺寸直接互换宽高
    let rotatedSize = CGSize(width: size.height, height: size.width)
    
    // 使用带参数的上下文创建方法,指定原图片的缩放因子,opaque根据图片是否透明设置
    UIGraphicsBeginImageContextWithOptions(rotatedSize, true, self.scale)
    defer { UIGraphicsEndImageContext() }
    
    guard let context = UIGraphicsGetCurrentContext() else { return nil }
    
    // 平移到中心,旋转,绘制
    context.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
    context.rotate(by: radians)
    draw(in: CGRect(x: -size.width/2, y: -size.height/2, width: size.width, height: size.height))
    
    return UIGraphicsGetImageFromCurrentImageContext()
}

3. 优化UIGraphicsImageRenderer的性能

若要使用UIGraphicsImageRenderer,需指定正确的缩放因子和格式,避免不必要的高分辨率绘制:

func rotateLeft() -> UIImage? {
    let radians = CGFloat.rotateRadians
    let rotatedSize = CGSize(width: size.height, height: size.width)
    
    // 自定义渲染格式,指定缩放因子为原图片的scale,设置opaque
    let format = UIGraphicsImageRendererFormat()
    format.scale = self.scale // 或者设为1,根据需求调整
    format.opaque = true
    
    let renderer = UIGraphicsImageRenderer(size: rotatedSize, format: format)
    
    let rotatedImage = renderer.image { context in
        context.cgContext.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
        context.cgContext.rotate(by: radians)
        let origin = CGPoint(x: -size.width / 2, y: -size.height / 2)
        draw(at: origin)
    }
    
    return rotatedImage
}

指定scale为原图片的缩放因子,避免渲染出3倍尺寸的大图,能大幅提升渲染速度。

内容的提问来源于stack exchange,提问作者Petr Bones

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 14:04:53