如何使用UnsafeMutableRawPointer填充数组?

12

我有一张金属质感的纹理,我想通过将其转换为float4数组来从Swift中访问其数据(以便我可以访问每个像素4个颜色组件)。

我发现了这个 MTLTexture 的方法:

getBytes(UnsafeMutableRawPointer, bytesPerRow: Int, bytesPerImage: Int, from: MTLRegion, mipmapLevel: Int, slice: Int)

我完全不知道如何使用UnsafeMutableRawPointer,它是如何工作的,以及如何将数据返回到简单的Swift数组中。我的第一次尝试是创建一个指针并分配足够的空间,但我甚至不知道这是否是我应该做的:

var pointer = UnsafeMutableRawPointer.allocate(bytes: myTextureSizeInBytes, alignedTo: 0)  

那么我完全不知道如何将这些数据重新转换为标准的Swift数组...

谢谢。

5个回答

28

首先,假设您有一个 UnsafeRawPointer 和一个长度:

let ptr: UnsafeRawPointer = ...
let length: Int = ...

现在您想将它转换为一个[float4]。首先,您可以通过将UnsafeRawPointer绑定到类型来将其转换为类型化指针:

let float4Ptr = ptr.bindMemory(to: float4.self, capacity: length)

现在你可以将它转换为类型化的缓冲指针:

let float4Buffer = UnsafeBufferPointer(start: float4Ptr, count: length)

由于缓冲区是一个集合,您可以使用它来初始化数组:

let output = Array(float4Buffer)

有关使用UnsafeRawPointer的更多信息,请参见SE-0138SE-0107UnsafeRawPointer迁移指南


非常感谢您的回答!一旦我获得了UInt8数组,如何将其转换为所需的类型?例如,如果我想要一个float4数组,我应该怎么做? - Pop Flamingo
抱歉,我错过了对 [float4] 的请求。我会更新的。 - Rob Napier
请务必查看马丁的答案;对于您特定的用例,它可能更好。 - Rob Napier
1
哪些(如果有的话)步骤会导致源数据被复制?Array初始化程序吗? - Rhythmic Fistman
是的,从集合初始化数组会复制集合。 - Rob Napier

6
另一种选择是创建一个适当大小的数组,并将地址传递给函数底层存储:
var pixelData = Array(repeating: float4(), count: myTextureSizeInFloat4)
pixelData.withUnsafeMutableBytes {
    texture.getBytes($0.baseAddress!, ...)
}

在闭包内部,$0 是一个 UnsafeMutableRawBufferPointer,表示以字节的形式呈现数组存储,并且 $0.baseAddress 是指向第一个字节的指针。


1
同意对于所描述的用例,这是更好的解决方案。我没有关注getBytes的调用方式。 - Rob Napier
谢谢您的回答!为什么我们可以在 getBytes 中使用 UnsafeMutableRawBufferPointer,而不是 UnsafeMutableRawPointer - Pop Flamingo
@TrevörAnneDenise: $0.baseAddress!UnsafeMutableRawPointer - Martin R
1
@TrevörAnneDenise: 缓冲指针描述了整个缓冲区,它基本上是指向第一个元素和长度的指针。它也是一个集合,因此您可以例如迭代for byte in myBufferPointer { ... } - Martin R
@MartinR 好的!感谢您的帮助。 - Pop Flamingo
显示剩余4条评论

4

详情

  • Xcode 11.2.1 (11B500),Swift 5.1

解决方案

extension UnsafeMutableRawPointer {
    func toArray<T>(to type: T.Type, capacity count: Int) -> [T]{
        let pointer = bindMemory(to: type, capacity: count)
        return Array(UnsafeBufferPointer(start: pointer, count: count))
    }
}

使用方法

var array = [1,2,3,4,5]
let ponter = UnsafeMutableRawPointer(mutating: array)
print(ponter.toArray(to: Int.self, capacity: array.count))

1
为了补充@VasilyBodnarchuk的回答:

extension UnsafeMutableRawPointer {
    func toArray<T>(to type: T.Type, capacity count: Int) -> [T] {
        return Array(UnsafeBufferPointer(start: bindMemory(to: type, capacity: count), count: count))
    }
}

0
这是一个Swift 4的示例,将字面量UInt8数组转换为UnsafeMutableRawPointer,然后再转换回UInt32数组。
static func unsafePointerTest() {
    //let a : [UInt8] = [0,0,0,4,0,0,0,8,0,0,0,12]
    let a : [UInt8] = [0x04, 0x00, 0x00, 0x00,
                       0x08, 0x00, 0x00, 0x00,
                       0x0C, 0x00, 0x00, 0x00] //little endian
    //0xFF, 0xF0, 0xF0, 0x12]  //317780223 = 12F0F0FF
    let b:UnsafeMutableRawPointer = UnsafeMutableRawPointer(mutating:a)
    let bTypedPtr = b.bindMemory(to: UInt32.self, capacity: a.count/4)
    let UInt32Buffer = UnsafeBufferPointer(start: bTypedPtr, count: a.count/4)
    let output = Array(UInt32Buffer)
    print(output)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接