vImageAlphaBlend 崩溃

Posted

技术标签:

【中文标题】vImageAlphaBlend 崩溃【英文标题】:vImageAlphaBlend crashes 【发布时间】:2015-01-31 20:20:35 【问题描述】:

我正在尝试在我的自定义NSViewdrawLayer(thisLayer: CALayer!, inContext ctx: CGContext!) 例程中混合一些layers: [CGImageRef]。到目前为止,我使用CGContextDrawImage() 将这些图层绘制到drawLayer 上下文中。在进行分析时,我注意到CGContextDrawImage() 需要 70% 的 CPU 时间,因此我决定尝试 Accelerate 框架。我更改了代码,但它只是崩溃了,我不知道可能是什么原因。

我正在像这样创建这些层:

func addLayer() 
    let colorSpace: CGColorSpaceRef = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
    let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedFirst.rawValue)

    var layerContext = CGBitmapContextCreate(nil, UInt(canvasSize.width), UInt(canvasSize.height), 8, UInt(canvasSize.width * 4), colorSpace, bitmapInfo)
    var newLayer = CGBitmapContextCreateImage(layerContext)

    layers.append( newLayer )

我的 drawLayers 例程如下所示:

override func drawLayer(thisLayer: CALayer!, inContext ctx: CGContext!)

    var ctxImageBuffer = vImage_Buffer(data:CGBitmapContextGetData(ctx),
        height:CGBitmapContextGetHeight(ctx),
        width:CGBitmapContextGetWidth(ctx),
        rowBytes:CGBitmapContextGetBytesPerRow(ctx))
    
    for imageLayer in layers
    
        //CGContextDrawImage(ctx, CGRect(origin: frameOffset, size: canvasSize), imageLayer)
        
        var inProvider:CGDataProviderRef = CGImageGetDataProvider(imageLayer)
        var inBitmapData:CFDataRef = CGDataProviderCopyData(inProvider)
        var buffer:vImage_Buffer = vImage_Buffer(data: &inBitmapData, height:
            CGImageGetHeight(imageLayer), width: CGImageGetWidth(imageLayer), rowBytes:
            CGImageGetBytesPerRow(imageLayer))
            
        vImageAlphaBlend_ARGB8888(&buffer, &ctxImageBuffer, &ctxImageBuffer, 0)
    

canvasSize 总是相同的,而且所有层的大小都相同,所以我不明白为什么最后一行会崩溃。

我也看不到如何使用新的便利函数直接从 CGLayerRefs 创建 vImageBuffers。这就是为什么我用复杂的方式来做。

任何帮助表示赞赏。

编辑

inBitmapData 确实拥有反映我设置的背景颜色的像素数据。但是调试器不能po &inBitmapData 并失败并显示此消息:

错误:对“CFData”的引用未用于初始化 inout 参数 &inBitmapData

所以我寻找一种方法来获取指向 inBitmapData 的指针。这就是我想出的:

var bitmapPtr: UnsafeMutablePointer<CFDataRef> = UnsafeMutablePointer<CFDataRef>.alloc(1)
bitmapPtr.initialize(inBitmapData)

我还必须更改指向我的数据的方式,用于 Alpha 混合输入所需的两个缓冲区。现在它不再崩溃了,幸运的是,可以使用分析器检查速度提升(vImageAlphaBlend 只需要 CGContextDrawImage 的三分之一左右),但不幸的是,图像会导致透明图像出现像素故障而不是白色图像背景。

到目前为止,我没有再遇到任何运行时错误,但由于结果不如预期,我担心我仍然没有正确使用 alpha blend 函数。

【问题讨论】:

【参考方案1】:

vImage_Buffer.data 应该指向 CFData 数据(像素数据),而不是 CFDataRef。

此外,并非所有图像都将其数据存储为四通道、每通道 8 位数据。如果结果是三通道或 RGBA 或单色,您可能会得到更多崩溃或有趣的颜色。此外,您假设原始图像数据未预乘,这可能不是一个安全的假设。

最好使用 vImageBuffer_initWithCGImage,这样可以保证原始图像数据的格式和色彩空间。有关该功能的更具体的问题可能会帮助我们解决您对此的困惑。

一些 CG 调用依赖于 vImage 来完成这项工作。在这种情况下,以这种方式重写代码可能无利可图。通常首先要做的正确的事情是仔细查看 CG 调用中的回溯,以尝试了解为什么要为此付出如此多的工作。通常答案是色彩空间转换。我会仔细查看绘图表面的 CGBitmapInfo 和色彩空间以及您的图像,看看是否有什么办法可以让它们更好地匹配。

IIRC、CALayerRefs 通常将其数据保存在不可缓存的存储中,以便更好地访问 GPU。这可能会导致 CPU 出现问题。 如果数据在 CALayerRef 中,我会使用 CA 进行合成。另外,我认为 CALayer 几乎总是 BGRA 8 位预乘。如果你不打算使用 CA 进行合成,那么正确的 vImage 函数可能是 vImagePremultipliedAlphaBlend_RGBA/BGRA8888。

【讨论】:

首先感谢您的帮助,不幸的是我无法解决我的问题。 CFDataRef 是 CFData 的一个别名,所以它不会导致崩溃,我还是把它改成了 CFData。我的所有图层都是使用 addLayer 函数创建的,因此具有相同的四个通道和预乘 alpha。在我发布我的问题之前,我使用了 RGBA (.PremultipliedLast),但是 swift 编译器无法识别 vImagePremultipliedAlphaBlend_RGBA8888 宏,所以我转而使用 .PremultipliedFirst 创建我的图层,还尝试了 vImagePremultipliedAlphaBlend_ARGB8888,但没有任何运气。 还有关于 vImageBuffer_initWithCGImage 函数,你能指出如何使用它吗?我已经在这里找到了一个关于此的问题:***.com/questions/26755978/… 最后一个解决方案建议使用 nil 作为其数据创建 vImageBuffer 结构。我不太确定 vImageBuffer_initWithCGImage 是否可以将 CGImage 数据写入 nil 指向的任何位置。当我尝试使用 vImageCreateCGImageFromBuffer 从该 vImageBuffer 创建一个 CGImage 时,程序在尝试访问错误时崩溃 err 我对 Swift 不满意,但是当您执行 &inBitmapData 时,它会返回指向 CFDataRef 的指针还是返回 CFDataGetBytePtr(inBitmapData)。如果是前者,您可能会崩溃,因为 vImage 需要一个指向像素的指针,而不是指向 CFDataRef 对象的指针。在任何情况下, vImageBuffer_initWithCGImage 和 vImageBuffer_Init 都采用已分配但未初始化的 vImage_Buffer 并填写内容。它们还为 vImage_Buffer.data 分配存储空间。完成后,您需要 free(buffer.data) 。 (仍然想知道为什么要在 CG 中合成 CA 层) 我的 CGImage 图层的内容不断被改变。据我了解,图层支持视图不是用于此目的的选项。所以我决定使用一个图层托管视图并将我所有的 CGImages 绘制到一个 CALayer 的上下文中。这是我现在想出的最快的解决方案,但我仍然想把所有东西都固定好。 &inBitmapData 上的“inout”运算符为我提供了一个 UnsafeMutablePointer,这是函数所要求的。 CFDataGetBytePtr 只返回一个 UnsafePointer,顺便说一下,当我尝试让 CFData 手动操作像素时,这也让我很难过。 使用调试器检查 &inBitmapData 指向的内存。它看起来像像素吗?通常,对于 8 位四通道格式,每第四个字节对于 alpha 为 0xff。

以上是关于vImageAlphaBlend 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

存档崩溃中的 iOS 崩溃似乎是块

VS2013 程序崩溃了 怎么定位到造成崩溃原因的语句

如何防止 Bootstrap 崩溃的子项在崩溃崩溃时调整大小

请教如何生成程序的崩溃日志

如何获得Android的崩溃日志

HarmonyOS崩溃服务能力全新上线,帮你高效解决崩溃问题!