feat: use imported texture on iOS
This commit is contained in:
@@ -6,7 +6,7 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin {
|
||||
|
||||
var registrar : FlutterPluginRegistrar
|
||||
var registry: FlutterTextureRegistry
|
||||
var texture: ThermionFlutterTexture?
|
||||
var textures: [Int64: ThermionFlutterTexture] = [:]
|
||||
|
||||
var createdAt = Date()
|
||||
|
||||
@@ -116,13 +116,12 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin {
|
||||
instance.resources.removeObject(forKey:rbuf.id)
|
||||
}
|
||||
|
||||
var markTextureFrameAvailable : @convention(c) (UnsafeMutableRawPointer?) -> () = { instancePtr in
|
||||
let instance:SwiftThermionFlutterPlugin = Unmanaged<SwiftThermionFlutterPlugin>.fromOpaque(instancePtr!).takeUnretainedValue()
|
||||
if(instance.texture != nil) {
|
||||
instance.registry.textureFrameAvailable(instance.texture!.flutterTextureId)
|
||||
var markTextureFrameAvailable: @convention(c) (UnsafeMutableRawPointer?) -> () = { instancePtr in
|
||||
let instance: SwiftThermionFlutterPlugin = Unmanaged<SwiftThermionFlutterPlugin>.fromOpaque(instancePtr!).takeUnretainedValue()
|
||||
for (_, texture) in instance.textures {
|
||||
instance.registry.textureFrameAvailable(texture.flutterTextureId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let _messenger = registrar.messenger();
|
||||
@@ -151,22 +150,35 @@ public class SwiftThermionFlutterPlugin: NSObject, FlutterPlugin {
|
||||
result(nil)
|
||||
case "getSharedContext":
|
||||
result(nil)
|
||||
case "markTextureFrameAvailable":
|
||||
let flutterTextureId = call.arguments as! Int64
|
||||
registry.textureFrameAvailable(flutterTextureId)
|
||||
result(nil)
|
||||
case "createTexture":
|
||||
let args = call.arguments as! [Any]
|
||||
let width = args[0] as! Int64
|
||||
let height = args[1] as! Int64
|
||||
|
||||
self.texture = ThermionFlutterTexture(width: width, height: height, registry: registry)
|
||||
let pixelBufferPtr = unsafeBitCast(self.texture!.pixelBuffer, to:UnsafeRawPointer.self)
|
||||
let pixelBufferAddress = Int(bitPattern:pixelBufferPtr);
|
||||
|
||||
let texture = ThermionFlutterTexture(registry: registry, width: width, height: height)
|
||||
|
||||
result([self.texture!.flutterTextureId as Any, nil, pixelBufferAddress])
|
||||
if texture.texture.metalTextureAddress == -1 {
|
||||
result(nil)
|
||||
} else {
|
||||
textures[texture.flutterTextureId] = texture
|
||||
result([texture.flutterTextureId, texture.texture.metalTextureAddress, nil])
|
||||
}
|
||||
case "destroyTexture":
|
||||
let texture = self.texture
|
||||
self.texture = nil
|
||||
texture?.destroy()
|
||||
let args = call.arguments as! [Any]
|
||||
let flutterTextureId = args[0] as! Int64
|
||||
|
||||
result(true)
|
||||
if let texture = textures[flutterTextureId] {
|
||||
registry.unregisterTexture(flutterTextureId)
|
||||
texture.destroy()
|
||||
textures.removeValue(forKey: flutterTextureId)
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
|
||||
@@ -4,45 +4,31 @@ import Flutter
|
||||
|
||||
public class ThermionFlutterTexture : NSObject, FlutterTexture {
|
||||
|
||||
public var pixelBuffer: CVPixelBuffer?
|
||||
|
||||
var pixelBufferAttrs = [
|
||||
kCVPixelBufferPixelFormatTypeKey: NSNumber(value: kCVPixelFormatType_32BGRA),
|
||||
kCVPixelBufferOpenGLCompatibilityKey: kCFBooleanTrue,
|
||||
kCVPixelBufferOpenGLESCompatibilityKey: kCFBooleanTrue,
|
||||
kCVPixelBufferIOSurfacePropertiesKey: [:]
|
||||
] as CFDictionary
|
||||
|
||||
var flutterTextureId: Int64 = -1
|
||||
var registry: FlutterTextureRegistry?
|
||||
var registry: FlutterTextureRegistry
|
||||
var texture: ThermionTextureSwift
|
||||
|
||||
init(width:Int64, height:Int64, registry:FlutterTextureRegistry) {
|
||||
init(registry:FlutterTextureRegistry, width:Int64, height:Int64) {
|
||||
self.registry = registry
|
||||
|
||||
self.texture = ThermionTextureSwift(width:width, height: height)
|
||||
super.init()
|
||||
|
||||
if(CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height),
|
||||
kCVPixelFormatType_32BGRA, pixelBufferAttrs, &pixelBuffer) != kCVReturnSuccess) {
|
||||
print("Error allocating pixel buffer")
|
||||
} else {
|
||||
self.flutterTextureId = registry.register(self)
|
||||
}
|
||||
self.flutterTextureId = registry.register(self)
|
||||
}
|
||||
|
||||
|
||||
public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
|
||||
return Unmanaged.passRetained(pixelBuffer!);
|
||||
if(self.texture.pixelBuffer == nil) {
|
||||
return nil
|
||||
}
|
||||
return Unmanaged.passRetained(self.texture.pixelBuffer!);
|
||||
}
|
||||
|
||||
public func onTextureUnregistered(_ texture:FlutterTexture) {
|
||||
print("Texture unregistered")
|
||||
|
||||
}
|
||||
|
||||
public func destroy() {
|
||||
if(self.flutterTextureId != -1) {
|
||||
self.registry!.unregisterTexture(self.flutterTextureId)
|
||||
}
|
||||
|
||||
self.pixelBuffer = nil
|
||||
self.registry.unregisterTexture(self.flutterTextureId)
|
||||
self.texture.destroyTexture()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
import Foundation
|
||||
import GLKit
|
||||
|
||||
@objc public class ThermionTextureSwift : NSObject {
|
||||
|
||||
public var pixelBuffer: CVPixelBuffer?
|
||||
|
||||
var pixelBufferAttrs = [
|
||||
kCVPixelBufferPixelFormatTypeKey: NSNumber(value: kCVPixelFormatType_32ABGR ),
|
||||
kCVPixelBufferIOSurfacePropertiesKey: [:] as CFDictionary
|
||||
] as [CFString : Any] as CFDictionary
|
||||
|
||||
@objc public var cvMetalTextureCache:CVMetalTextureCache?
|
||||
@objc public var metalDevice:MTLDevice?
|
||||
|
||||
@objc public var cvMetalTexture:CVMetalTexture?
|
||||
@objc public var metalTexture:MTLTexture?
|
||||
@objc public var metalTextureAddress:Int = -1
|
||||
|
||||
@objc override public init() {
|
||||
|
||||
}
|
||||
|
||||
@objc public init(width:Int64, height:Int64) {
|
||||
if(self.metalDevice == nil) {
|
||||
self.metalDevice = MTLCreateSystemDefaultDevice()!
|
||||
}
|
||||
|
||||
// create pixel buffer
|
||||
if(CVPixelBufferCreate(kCFAllocatorDefault, Int(width), Int(height),
|
||||
kCVPixelFormatType_32BGRA, pixelBufferAttrs, &pixelBuffer) != kCVReturnSuccess) {
|
||||
print("Error allocating pixel buffer")
|
||||
metalTextureAddress = -1;
|
||||
return
|
||||
}
|
||||
if self.cvMetalTextureCache == nil {
|
||||
let cacheCreationResult = CVMetalTextureCacheCreate(
|
||||
kCFAllocatorDefault,
|
||||
nil,
|
||||
self.metalDevice!,
|
||||
nil,
|
||||
&self.cvMetalTextureCache)
|
||||
if(cacheCreationResult != kCVReturnSuccess) {
|
||||
print("Error creating Metal texture cache")
|
||||
metalTextureAddress = -1
|
||||
return
|
||||
}
|
||||
}
|
||||
let cvret = CVMetalTextureCacheCreateTextureFromImage(
|
||||
kCFAllocatorDefault,
|
||||
self.cvMetalTextureCache!,
|
||||
pixelBuffer!, nil,
|
||||
MTLPixelFormat.bgra8Unorm,
|
||||
Int(width), Int(height),
|
||||
0,
|
||||
&cvMetalTexture)
|
||||
if(cvret != kCVReturnSuccess) {
|
||||
print("Error creating texture from image")
|
||||
metalTextureAddress = -1
|
||||
return
|
||||
}
|
||||
metalTexture = CVMetalTextureGetTexture(cvMetalTexture!)
|
||||
let metalTexturePtr = Unmanaged.passRetained(metalTexture!).toOpaque()
|
||||
metalTextureAddress = Int(bitPattern:metalTexturePtr)
|
||||
}
|
||||
|
||||
@objc public func destroyTexture() {
|
||||
CVMetalTextureCacheFlush(self.cvMetalTextureCache!, 0)
|
||||
self.metalTexture = nil
|
||||
self.cvMetalTexture = nil
|
||||
self.pixelBuffer = nil
|
||||
self.metalDevice = nil
|
||||
self.cvMetalTextureCache = nil
|
||||
}
|
||||
|
||||
@objc public func fillColor() {
|
||||
CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
|
||||
let bufferWidth = Int(CVPixelBufferGetWidth(pixelBuffer!))
|
||||
let bufferHeight = Int(CVPixelBufferGetHeight(pixelBuffer!))
|
||||
let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer!)
|
||||
|
||||
guard let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer!) else {
|
||||
return
|
||||
}
|
||||
|
||||
for row in 0..<bufferHeight {
|
||||
var pixel = baseAddress + row * bytesPerRow
|
||||
for col in 0..<bufferWidth {
|
||||
let blue = pixel
|
||||
blue.storeBytes(of: 255, as: UInt8.self)
|
||||
|
||||
let red = pixel + 1
|
||||
red.storeBytes(of: 0, as: UInt8.self)
|
||||
|
||||
let green = pixel + 2
|
||||
green.storeBytes(of: 0, as: UInt8.self)
|
||||
|
||||
let alpha = pixel + 3
|
||||
alpha.storeBytes(of: 255, as: UInt8.self)
|
||||
|
||||
pixel += 4;
|
||||
}
|
||||
}
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
|
||||
}
|
||||
@objc public func getTextureBytes() -> NSData? {
|
||||
guard let texture = self.metalTexture else {
|
||||
print("Metal texture is not available")
|
||||
return nil
|
||||
}
|
||||
|
||||
let width = texture.width
|
||||
let height = texture.height
|
||||
let bytesPerPixel = 4 // RGBA
|
||||
let bytesPerRow = width * bytesPerPixel
|
||||
let byteCount = bytesPerRow * height
|
||||
|
||||
var bytes = [UInt8](repeating: 0, count: byteCount)
|
||||
let region = MTLRegionMake2D(0, 0, width, height)
|
||||
texture.getBytes(&bytes, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
|
||||
|
||||
// Swizzle bytes from BGRA to RGBA
|
||||
for i in stride(from: 0, to: byteCount, by: 4) {
|
||||
let blue = bytes[i]
|
||||
let green = bytes[i + 1]
|
||||
let red = bytes[i + 2]
|
||||
let alpha = bytes[i + 3]
|
||||
|
||||
bytes[i] = red
|
||||
bytes[i + 1] = green
|
||||
bytes[i + 2] = blue
|
||||
bytes[i + 3] = alpha
|
||||
}
|
||||
|
||||
// Convert Swift Data to Objective-C NSData
|
||||
let nsData = Data(bytes: &bytes, count: byteCount) as NSData
|
||||
return nsData
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -34,6 +34,7 @@ class ThermionWidget extends StatefulWidget {
|
||||
{Key? key, this.initial, required this.viewer, this.view, this.options})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<ThermionWidget> createState() => _ThermionWidgetState();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user