swift - 快速创建透明纹理

标签 swift compression metal metalkit uiimagepngrepresentation

我只需要创建一个透明纹理。(具有 alpha 0 的像素)。

func layerTexture()-> MTLTexture {

        let width = Int(self.drawableSize.width  )
        let height = Int(self.drawableSize.height )


        let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, width: width , height: height, mipmapped: false)
        let temparyTexture = self.device?.makeTexture(descriptor: texDescriptor)

        return temparyTexture!
    }

当我使用预览打开 temparyTexture 时,它​​看起来是黑色的。这里缺少什么?

更新

我只是尝试使用透明图像创建纹理。 代码。

 func layerTexture(imageData:Data)-> MTLTexture {

        let width = Int(self.drawableSize.width  )
        let height = Int(self.drawableSize.height )
        let bytesPerRow = width * 4


        let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm, width: width , height: height, mipmapped: false)

        let temparyTexture = self.device?.makeTexture(descriptor: texDescriptor)

         let region = MTLRegionMake2D(0, 0, width, height)

        imageData.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
            let rawPtr = UnsafeRawPointer(u8Ptr)

            temparyTexture?.replace(region: region, mipmapLevel: 0, withBytes: rawPtr, bytesPerRow: bytesPerRow)
        }


        return temparyTexture!
    }

方法调用如下

let image = UIImage(named: "layer1.png")!

let imageData = UIImagePNGRepresentation(image)

self.layerTexture(imageData: imageData!)

其中 layer1.png 是一个透明的 png。但即使它在我尝试替换纹理时崩溃并显示消息“Thread 1: EXC_BAD_ACCESS (code=1, address=0x107e8c000)”。我相信这是因为图像数据被压缩并且 rawpointer 应该指向未压缩的数据。我该如何解决这个问题?

我是走在正确的道路上还是完全走错了方向?有没有其他选择。我只需要创建透明纹理。

最佳答案

预编辑:当您快速查看透明纹理时,它会显示为黑色。我只是仔细检查了我在生产环境中稳定运行的一些代码——这是预期的结果。

后期编辑:你是对的,你不应该将 PNG 或 JPEG 数据直接复制到 MTLTexture 的内容中。我建议做这样的事情:

    var pixelBuffer: CVPixelBuffer?

    let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue,
                 kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue,
                 kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue]

    var status = CVPixelBufferCreate(nil, Int(image.size.width), Int(image.size.height),
                                     kCVPixelFormatType_32BGRA, attrs as CFDictionary,
                                     &pixelBuffer)
    assert(status == noErr)

    let coreImage = CIImage(image: image)!
    let context = CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!)
    context.render(coreImage, to: pixelBuffer!)

    var textureWrapper: CVMetalTexture?

    status = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                       GPUManager.shared.textureCache, pixelBuffer!, nil, .bgra8Unorm,
                                                       CVPixelBufferGetWidth(pixelBuffer!), CVPixelBufferGetHeight(pixelBuffer!), 0, &textureWrapper)

    let texture = CVMetalTextureGetTexture(textureWrapper!)!

    // use texture now for your Metal texture. the texture is now map-bound to the CVPixelBuffer's underlying memory.

您遇到的问题是,实际上很难完全掌握位图的工作原理以及如何以不同方式布置它们。图形是一个非常封闭的领域,有很多深奥的术语,其中一些指的是需要多年才能掌握的东西,其中一些指的是微不足道但人们只是用一个奇怪的词来调用它们的东西。我的主要观点是:

  • 在您的代码中尽早摆脱 UIImage 领域。当您进入 Metal 领域时,避免开销和延迟的最佳方法是尽快将您的图像转换为 GPU 兼容的表示形式。
  • 一旦您离开了 UIImage 领域,始终了解您的 channel 顺序(RGBA、BGRA)。在您正在编辑的代码中的任何一点,您都应该对每个 CVPixelBuffer/MTLTexture 具有的像素格式有一个心理模型。
  • 阅读有关预乘与非预乘 alpha 的文章,您可能不会遇到这方面的问题,但当我第一次学习时,它反复让我失望。
  • 位图/像素缓冲区的总字节大小 = bytesPerRow * height

关于swift - 快速创建透明纹理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52159426/

相关文章:

ios - GoogleMapSDK (GMSMapView) 与 Swift 崩溃

swift - 推送 View Controller 时,iOS 11 黑条出现在导航栏上

ffmpeg - 从根本上说,编解码器到底是什么?

algorithm - 简单来说,压缩通常是如何实现的?

c++ - 按内核线程增量

ios - seacrhDisplayController 中的搜索栏颜色不准确

ios - ContactBitMask 指示我的对象发生碰撞,但它们没有

c# - 如何解压缩 dotnet 核心中的存档?

swift - 为什么我启用了 Metal API 但我的 Coreml 自定义层仍在 CPU 上运行

ios - Metal Compute 管道不适用于 MacOS,但适用于 iOS