在透明背景上绘制时,CGContext 抗锯齿效果不如预期

CGContext antialiasing works not as expected when drawing on transparent background

示例取自 https://medium.com/@s1ddok/combine-the-power-of-coregraphics-and-metal-by-sharing-resource-memory-eabb4c1be615。刚刚将颜色 space 从 r8unorm 更改为 rgba8unorm。

// CGContext created with aligned data, which shared with MTLTexture
let cgContext = textureContext.cgContext
        let clearRect = CGRect(
                origin: .zero,
                size: CGSize(width: cgContext.width, height: cgContext.height)
        )
        cgContext.setAllowsAntialiasing(true)
        cgContext.setShouldAntialias(true)
        cgContext.clear(clearRect)

        cgContext.addPath(path)
        cgContext.setFillColor(fillColor ?? UIColor.clear.cgColor)
        cgContext.fillPath(using: fillRule.cgFillRuleValue())

共享数据块上的渲染路径然后用作在 MTKView 纹理上渲染的四元图元的纹理。但是有问题 - 抗锯齿会像这样在边缘混合路径填充颜色和清晰(黑色)颜色(希望如果这张图片之间的差异很明显) -

这是正常的重叠,当我用红色预填充上下文区域时

如果为上下文关闭抗锯齿 - 边缘会明显变白,但会变锐利

有没有办法在绘制路径之前不将适当的重叠纹理块渲染到重叠纹理的情况下重叠两个具有清晰平滑边缘的纹理?

更新: 如何创建上下文

func createCGMTLContext(withSize size: CGSize) -> CGMTLContext? {
        let width = Int(size.width)
        let height = Int(size.height)

        let pixelRowAlignment = device.minimumTextureBufferAlignment(for: .rgba8Unorm)
        let bytesPerRow = alignUp(size: width, align: pixelRowAlignment) * 4

        let pagesize = Int(getpagesize())
        let allocationSize = alignUp(size: bytesPerRow * height, align: pagesize)
        var data: UnsafeMutableRawPointer? = nil
        let result = posix_memalign(&data, pagesize, allocationSize)
        if result != noErr {
            fatalError("Error during memory allocation")
        }

        let context = CGContext(data: data,
                width: width,
                height: height,
                bitsPerComponent: 8,
                bytesPerRow: bytesPerRow,
                space: CGColorSpaceCreateDeviceRGB(),
                bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!

        context.scaleBy(x: 1.0, y: -1.0)
        context.translateBy(x: 0, y: -CGFloat(context.height))

        let buffer = device.makeBuffer(
                bytesNoCopy: context.data!,
                length: allocationSize,
                options: .storageModeShared,
                deallocator: { pointer, length in free(data) }
        )!

        let textureDescriptor = MTLTextureDescriptor()
        textureDescriptor.pixelFormat = .rgba8Unorm
        textureDescriptor.width = context.width
        textureDescriptor.height = context.height
        textureDescriptor.storageMode = buffer.storageMode
        textureDescriptor.usage = .shaderRead

        let texture = buffer.makeTexture(descriptor: textureDescriptor,
                offset: 0,
                bytesPerRow: context.bytesPerRow)

        guard let requiredTexture = texture else {
            return nil
        }

        let cgmtlContext = CGMTLContext(
                cgContext: context,
                buffer: buffer,
                texture: requiredTexture
        )

        return cgmtlContext
    }

金属混合是如何设置的

pipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add
            pipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add
            pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
            pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
            pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
            pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha

阅读 https://metalbyexample.com/translucency-and-transparency/ 后,我注意到 Warren Moore 的评论:

Since you’re loading your textures with CoreGraphics, you’re getting premultiplied alpha (i.e., the color channels are already multiplied by their respective alpha values). The consequence of using premultiplied alpha with the SourceAlpha, OneMinusSourceAlpha blend factors is that the source alpha value is redundantly multiplied, causing areas with a < 1.0 to become unnaturally dark. If instead you use the One, OneMinusSourceAlpha blend factors, you get a much more pleasing result.Incidentally, this is the main reason I used an opaque texture and translucent vertex colors when demonstrating blending in the article.

将 sourceAlpha 更改为 One 有帮助。不幸的是,无法关闭 CGContext 中的 alpha 预乘