Search code examples
swiftcompressionmetalmetalkituiimagepngrepresentation

create transparent texture in swift


I just need to create a transparent texture.(pixels with 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!
    }

when I open temparyTexture using preview,it's appeared to be black. What is the missing here?

UPDATE

I just tried to create texture using transparent image. code.

 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!
    }

method is get called as follows

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

let imageData = UIImagePNGRepresentation(image)

self.layerTexture(imageData: imageData!)

where layer1.png is a transparent png. But even though it is crashing with message "Thread 1: EXC_BAD_ACCESS (code=1, address=0x107e8c000) " at the point I try to replace texture. I believe it's because image data is compressed and rawpointer should point to uncompressed data. How can I resolve this?

Am I in correct path or completely in wrong direction? Is there any other alternatives. What I just need is to create transparent texture.


Solution

  • Pre-edit: When you quick-look a transparent texture, it will appear black. I just double-checked with some code I have running stably in production - that is the expected result.

    Post-edit: You are correct, you should not be copying PNG or JPEG data to a MTLTexture's contents directly. I would recommend doing something like this:

        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.
    

    The issue you are running into is that it is actually pretty hard to fully grasp how bitmaps work and how they can be laid out differently. Graphics is a very closed field with lots of esoteric terminology, some of which refers to things that take years to grasp, some of which refers to things that are trivial but people just picked a weird word to call them by. My main pointers are:

    • Get out of UIImage land as early in your code as possible. The best way to avoiding overhead and delays when you go into Metal land is to get your images into a GPU-compatible representation as soon as you can.
    • Once you are outside of UIImage land, always know your channel order (RGBA, BGRA). At any point in code that you are editing, you should have a mental model of what pixel format each CVPixelBuffer / MTLTexture has.
    • Read up on premultiplied vs non-premultiplied alpha, you may not run into issues with this, but it threw me off repeatedly when I was first learning.
    • total byte size of a bitmap/pixelbuffer = bytesPerRow * height