美文网首页
iOS开发 App图像使用及内存优化(wwdc2018)

iOS开发 App图像使用及内存优化(wwdc2018)

作者: 左方 | 来源:发表于2021-03-15 14:08 被阅读0次

    一、图像如何显示

    内存使用与图像的尺寸有关,而不是与文件大小有关。

    举个例子,我有一张非常漂亮的图片,我想用它作为iPad应用程序的墙纸。



    它的大小是2048✖️1536,磁盘上的文件是590KB。
    但是它到底用了多少内存呢?10MB。
    10MB,太大了!其原因是,将宽像素数*高像素数:2048✖️1536,再✖️每像素4个字节,得到大约10MB。

    那它为什么这么大呢?好吧,我们得谈谈图像在iOS上是如何工作的:

    1. 有加载-解码-渲染阶段:

    加载阶段将这个590KB的JPEG文件压缩后加载到内存中。
    解码器将该JPEG文件转换为GPU可以读取的格式。
    现在,这需要解压缩,这使得它有10MB。

    一旦被解码,就可以随意渲染。

    2. 图片格式:

    2.1 SRGB格式

    我们用SRGB格式得到了每像素4个字节。
    这通常是图形中最常见的图像格式。
    它是每像素8位,所以你有1个字节代表红色,1个字节代表绿色,1个字节代表蓝色,还有一个alpha分量。

    不过,我们可以做得更大一些。

    2.2 宽格式(wide format)

    iOS硬件可以呈现宽格式(wide format)。
    现在,宽格式为了得到有表现力的颜色,每像素需要2个字节,所以我们把图像的大小增加了一倍。

    iPhone7、8、X和iPhone上的摄像头,一些iPad专业人士非常适合捕捉这种高保真的内容。
    你也可以使用它的超级准确的颜色,如体育标志等。
    但是这些只有在宽格式显示上才真正有用,所以我们不想在不需要的时候使用它。

    2.3 luminance and alpha 8(AL8)

    另一方面,我们实际上可以缩小规模。
    现在,有一个亮度和alpha 8格式。

    此格式仅存储灰度和alpha值。
    这通常用于着色器,就像金属应用程序之类。
    在我们的使用中不太常见。

    2.4 alpha 8(AL8)

    实际上我们可以变得更小。
    我们可以继续使用alpha8格式。

    现在,alpha8只有一个通道,每像素一个字节。
    非常小。
    它比SRGB小75%。
    现在,这很适合蒙版或单色的文本,因为我们使用75%的内存。

    3. 选择正确格式

    如果我们看一下细分,我们可以从alpha8的每像素1字节一直到宽格式的每像素8字节。
    范围很大。
    所以我们真正需要做的是知道如何选择正确的格式。
    那么我们如何选择正确的格式呢?简单的回答是

    不要选择格式。

    让格式选择你。

    如果您从使用UIGraphics BeginImageContext with options API迁移到iOS,而改为使用UIGraphics ImageRenderer格式,则可以节省大量内存,因为UIGraphics BeginImageContext with options始终是每像素4字节的格式。
    它总是SRGB。
    所以如果你想要的话,你不能得到宽格式,如果你需要的话,你也不能得到每像素1字节的A8格式。
    相反,如果您使用iOS 10中的UIGraphics ImageRenderer API(从iOS 12开始),它将自动为您选择最佳的图形格式。
    假设我画了一个圆。
    现在,使用旧的API和突出显示的代码是我的绘图代码,我得到的是每像素4字节的格式,只是为了画一个黑色的圆。

    /**
    使用旧的API
    */
    let bounds = CGRect(x: 0, y: 0, width:300, height: 100)
    UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0)
    
    // Drawing Code
    UIColor.black.setFill()
    let path = UIBezierPath(roundedRect: bounds,
                              path.addClip()
                              UIRectFill(bounds)
                              byRoundingCorners: UIRectCorner.allCorners,
                              cornerRadii: CGSize(width: 20, height: 20))
    path.addClip()
    UIRectFill(bounds)
    
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    
    /**
    使用新的API
    */
    // Circle via UIGraphicsImageRenderer
    
    let bounds = CGRect(x: 0, y: 0, width:300, height: 100)
    let renderer = UIGraphicsImageRenderer(size: bounds.size)
    let image = renderer.image { context in
    // Drawing Code
    UIColor.black.setFill()
    let path = UIBezierPath(roundedRect: bounds,
                            path.addClip()
                            UIRectFill(bounds)
                            byRoundingCorners: UIRectCorner.allCorners,
                            cornerRadii: CGSize(width: 20, height: 20))
    }
    

    如果我改为使用新的API,我使用的是完全相同的绘图代码。
    仅仅使用新的API,我现在就得到了每像素1字节的图像。
    这意味着它的内存使用减少了75%。
    这是一个巨大的节约和同样的保真度。
    另外一个好处,如果我想再次使用这个蒙版,我可以改变UIimageView的tintColor,这用一个·就可以做到,这意味着我不必分配更多的内存。

    // Make circle render blue, but stay at 1 byte-per-pixel image
    let imageView = UIImageView(image: image)
    imageView.tintColor = .blue
    

    因此,我可以不仅把它作为一个黑色的圆圈,还可以作为一个蓝色的圆圈,红色的圆圈,绿色的圆圈,而没有额外的内存成本。

    参考资料:
    1.iOS图片内存优化
    2.WWDC2018 图像最佳实践
    3.iOS核心动画高级技术

    相关文章

      网友评论

          本文标题:iOS开发 App图像使用及内存优化(wwdc2018)

          本文链接:https://www.haomeiwen.com/subject/enqxqltx.html